example-remote-client/src/types/inference.ts
Jerome 438ceb333c
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>
2025-06-10 17:37:11 +01:00

109 lines
No EOL
2.5 KiB
TypeScript

// 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;
}