Implement complete InferenceProvider system with OpenRouter integration
## Features - 🔧 InferenceProvider interface with pluggable architecture - 🔑 OpenRouter API key and OAuth PKCE authentication providers - 🛠️ Tool calling support with automatic agent loop execution - 📱 Interactive test UI with model selection and chat interface - 🎯 Provider-level filtering to tool-capable models only - ⚛️ React context with proper state management for UI reactivity ## Components - Core types and interfaces in src/types/inference.ts - OpenRouter client with shared utilities for both auth methods - Two separate providers (composition over inheritance): - OpenRouterApiProvider for API key authentication - OpenRouterOAuthProvider for OAuth PKCE flow - React InferenceContext with useInference hook - Interactive test UI with tool calling demonstration - OAuth callback routing with proper namespacing ## Tool Calling - 3 test tools: weather, calculator, time - Automatic tool execution and agent loop - Visual tool call display with arguments and results - Error handling for malformed tool calls ## Bug Fixes - Fixed model selection UI reactivity issue - Fixed OAuth popup race condition causing "cancelled" errors - Proper cleanup of event listeners and intervals ## Documentation - Comprehensive interface design document - Implementation strategy and key design decisions - Testing approach and security considerations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aa97d655aa
commit
438ceb333c
1 changed files with 8564 additions and 0 deletions
109
src/types/inference.ts
Normal file
109
src/types/inference.ts
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
// Core inference types and interfaces
|
||||
|
||||
export interface Model {
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
contextLength: number;
|
||||
inputCost?: number; // per token
|
||||
outputCost?: number; // per token
|
||||
provider: string;
|
||||
capabilities: ModelCapabilities;
|
||||
}
|
||||
|
||||
export interface ModelCapabilities {
|
||||
supportsVision: boolean;
|
||||
maxTokens: number;
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system' | 'tool';
|
||||
content: string | ContentBlock[];
|
||||
toolCalls?: ToolCall[];
|
||||
toolCallId?: string; // for tool response messages
|
||||
}
|
||||
|
||||
export interface ContentBlock {
|
||||
type: 'text' | 'image';
|
||||
text?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
export interface ToolCall {
|
||||
id: string;
|
||||
type: 'function';
|
||||
function: {
|
||||
name: string;
|
||||
arguments: Record<string, any>; // Parsed JSON object
|
||||
};
|
||||
}
|
||||
|
||||
export interface Tool {
|
||||
type: 'function';
|
||||
function: {
|
||||
name: string;
|
||||
description?: string;
|
||||
parameters: Record<string, any>; // JSON schema
|
||||
};
|
||||
}
|
||||
|
||||
export interface InferenceRequest {
|
||||
messages: ChatMessage[];
|
||||
tools?: Tool[]; // toolChoice defaults to 'auto' when tools provided
|
||||
maxTokens?: number;
|
||||
temperature?: number;
|
||||
stopSequences?: string[];
|
||||
}
|
||||
|
||||
export interface InferenceResponse {
|
||||
message: ChatMessage;
|
||||
usage: TokenUsage;
|
||||
stopReason: 'stop' | 'max_tokens' | 'tool_calls' | 'error';
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface TokenUsage {
|
||||
promptTokens: number;
|
||||
completionTokens: number;
|
||||
totalTokens: number;
|
||||
cost?: number;
|
||||
}
|
||||
|
||||
export interface AuthConfig {
|
||||
type: 'api_key' | 'oauth';
|
||||
apiKey?: string;
|
||||
oauthConfig?: {
|
||||
clientId?: string;
|
||||
redirectUri?: string;
|
||||
scopes?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ProviderCapabilities {
|
||||
authMethods: ('api_key' | 'oauth')[];
|
||||
supportsModelListing: boolean;
|
||||
requiresAuth: boolean;
|
||||
}
|
||||
|
||||
export interface InferenceError {
|
||||
type: 'auth' | 'network' | 'rate_limit' | 'invalid_request' | 'provider_error';
|
||||
message: string;
|
||||
details?: any;
|
||||
retryable: boolean;
|
||||
}
|
||||
|
||||
export abstract class InferenceProvider {
|
||||
abstract readonly name: string;
|
||||
abstract readonly id: string;
|
||||
abstract readonly isAuthenticated: boolean;
|
||||
abstract readonly authError?: string;
|
||||
abstract readonly models: Model[];
|
||||
abstract readonly selectedModel?: Model;
|
||||
|
||||
abstract generateResponse(request: InferenceRequest): Promise<InferenceResponse>;
|
||||
abstract loadModels(): Promise<Model[]>;
|
||||
abstract selectModel(modelId: string): void;
|
||||
abstract authenticate(config: AuthConfig): Promise<void>;
|
||||
abstract logout(): void;
|
||||
abstract getCapabilities(): ProviderCapabilities;
|
||||
}
|
||||
Loading…
Reference in a new issue