example-remote-client/docs/inference_provider_interface.md
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

338 lines
No EOL
9.2 KiB
Markdown

# InferenceProvider Interface Design
This document outlines the design for the InferenceProvider interface and its implementations, which abstracts LLM inference capabilities for the MCP client.
## Goals
1. **Provider Agnostic**: Support multiple inference providers (OpenRouter, Anthropic, OpenAI, Google, etc.)
2. **Tool Calling**: Native support for function/tool calling with MCP tools
3. **Authentication**: Flexible auth patterns (API keys, OAuth, etc.)
4. **Model Selection**: Dynamic model listing and selection
5. **Error Handling**: Consistent error handling across providers
## Core Interface
```typescript
interface InferenceProvider {
// Provider identification
readonly name: string;
readonly id: string;
// Authentication state
readonly isAuthenticated: boolean;
readonly authError?: string;
// Available models
readonly models: Model[];
readonly selectedModel?: Model;
// Core inference method
generateResponse(request: InferenceRequest): Promise<InferenceResponse>;
// Model management
loadModels(): Promise<Model[]>;
selectModel(modelId: string): void;
// Authentication
authenticate(config: AuthConfig): Promise<void>;
logout(): void;
// Provider-specific capabilities
getCapabilities(): ProviderCapabilities;
}
```
## Supporting Types
### Model
```typescript
interface Model {
id: string;
name: string;
description?: string;
contextLength: number;
inputCost?: number; // per token
outputCost?: number; // per token
provider: string;
capabilities: ModelCapabilities;
}
interface ModelCapabilities {
supportsTools: boolean;
supportsVision: boolean;
maxTokens: number;
}
```
### Inference Request/Response
```typescript
interface InferenceRequest {
messages: ChatMessage[];
tools?: Tool[]; // toolChoice defaults to 'auto' when tools provided
maxTokens?: number;
temperature?: number;
stopSequences?: string[];
}
interface InferenceResponse {
message: ChatMessage;
usage: TokenUsage;
stopReason: 'stop' | 'max_tokens' | 'tool_calls' | 'error';
error?: string;
}
interface ChatMessage {
role: 'user' | 'assistant' | 'system' | 'tool';
content: string | ContentBlock[];
toolCalls?: ToolCall[];
toolCallId?: string; // for tool response messages
}
interface ToolCall {
id: string;
type: 'function';
function: {
name: string;
arguments: string; // JSON string
};
}
```
### Authentication
```typescript
interface AuthConfig {
type: 'api_key' | 'oauth';
apiKey?: string;
oauthConfig?: {
clientId?: string;
redirectUri?: string;
scopes?: string[];
};
}
interface ProviderCapabilities {
authMethods: ('api_key' | 'oauth')[];
supportsModelListing: boolean;
supportsToolCalling: boolean;
requiresAuth: boolean;
}
```
## Provider Implementations
### OpenRouter Providers
For simplicity, we implement two separate providers using composition:
**OpenRouterApiProvider:**
- API key authentication only
- Simpler auth flow and error handling
- Direct API access
**OpenRouterOAuthProvider:**
- OAuth PKCE authentication only
- Browser-based auth popup flow
- Token refresh handling
**Shared Configuration:**
```typescript
interface OpenRouterBaseConfig {
baseUrl?: string; // defaults to https://openrouter.ai/api/v1
defaultModel?: string;
httpReferrer?: string;
appName?: string;
}
interface OpenRouterApiConfig extends OpenRouterBaseConfig {
apiKey: string;
}
interface OpenRouterOAuthConfig extends OpenRouterBaseConfig {
clientId?: string; // for custom OAuth apps
redirectUri?: string;
}
```
**Shared Logic:**
Both providers use composition with shared utilities:
- `OpenRouterClient` - HTTP client and request formatting
- `OpenRouterModelParser` - Model list parsing and capabilities
- `OpenRouterToolFormatter` - Tool call formatting
### Future Providers
**AnthropicProvider:**
- OAuth integration with Anthropic Console
- Claude model family support
- Message batching optimization
**OpenAIProvider:**
- OpenAI API key or OAuth
- GPT model family support
- Function calling optimization
**GoogleProvider:**
- Google Cloud OAuth
- Gemini model family support
- Tool calling with Google AI format
## React Integration
### Provider Context
```typescript
interface InferenceContextValue {
provider: InferenceProvider | null;
isLoading: boolean;
error: string | null;
// Actions
setProvider: (provider: InferenceProvider) => void;
clearProvider: () => void;
generateResponse: (request: InferenceRequest) => Promise<InferenceResponse>;
selectModel: (modelId: string) => void;
loadModels: () => Promise<Model[]>;
// Convenience getters
models: Model[];
selectedModel: Model | undefined;
isAuthenticated: boolean;
}
const InferenceContext = createContext<InferenceContextValue | null>(null);
```
### Hook Usage
```typescript
const useInference = () => {
const context = useContext(InferenceContext);
if (!context) {
throw new Error('useInference must be used within InferenceProvider');
}
return context;
};
```
## Error Handling
### Error Types
```typescript
interface InferenceError {
type: 'auth' | 'network' | 'rate_limit' | 'invalid_request' | 'provider_error';
message: string;
details?: any;
retryable: boolean;
}
```
### Error Scenarios
- **Authentication failures**: Invalid API key, expired tokens
- **Rate limiting**: Provider-specific rate limits
- **Network errors**: Connection issues, timeouts
- **Validation errors**: Invalid tool schemas, malformed requests
- **Provider errors**: Service unavailable, model not found
## Implementation Strategy
### Phase 1: Basic OpenRouter Support ✅
1. Implement core InferenceProvider interface
2. Create OpenRouterApiProvider with API key auth
3. Basic model listing and selection (filtered to tool-capable models only)
4. Simple tool calling support (toolChoice=auto)
5. React context with proper state management for UI reactivity
### Phase 2: OAuth and Enhanced Features ✅
1. Create OpenRouterOAuthProvider with PKCE auth
2. Implement error recovery and retries
3. Add usage tracking and cost estimation
4. Improve tool call validation
### Current Status
- ✅ Two OpenRouter providers (API key & OAuth) implemented
- ✅ Provider-level model filtering (only tool-capable models)
- ✅ React context with proper UI state synchronization
- ✅ Interactive test UI with tool calling demonstration
- ✅ OAuth callback routing for future MCP server integration
### Phase 3: Multi-Provider Support
1. Add AnthropicProvider
2. Add OpenAIProvider
3. Provider switching UI
4. Unified configuration management
## Testing Strategy
### Unit Tests
- Mock provider implementations for testing
- Tool call serialization/deserialization
- Error handling scenarios
- Authentication flows
### Integration Tests
- Real provider API integration (with test keys)
- End-to-end tool calling workflows
- Provider switching scenarios
- Authentication persistence
## Security Considerations
1. **API Key Storage**: Secure storage in browser (encrypted localStorage)
2. **OAuth Flow**: Secure popup-based OAuth with PKCE
3. **Token Refresh**: Automatic token refresh handling
4. **Rate Limiting**: Client-side rate limiting to prevent abuse
5. **Input Validation**: Strict validation of tool calls and responses
## Key Design Decisions
### 1. Provider-Level Model Filtering
All providers filter models to only return those that support tool calling. This simplifies consumers and prevents tool-calling errors:
```typescript
// In OpenRouterClient.fetchModels()
return response.data
.filter(model => model.supported_parameters?.includes('tools'))
.map(this.parseModel);
```
**Benefits:**
- No tool capability checking needed in UI components
- Eliminates "model doesn't support tools" errors
- Cleaner separation of concerns
### 2. React State Management for UI Reactivity
The React context tracks model selection in React state to ensure UI updates when models are selected:
```typescript
const [selectedModelId, setSelectedModelId] = useState<string | undefined>();
// In selectModel callback
provider.selectModel(modelId); // Update provider internal state
setSelectedModelId(modelId); // Update React state → triggers re-render
// In context value - compute selectedModel from React-tracked ID
selectedModel: selectedModelId ? provider?.models.find(m => m.id === selectedModelId) : undefined
```
**Why this pattern:**
- Provider internal state changes don't trigger React re-renders automatically
- React needs to track state changes to update the UI
- Keeps provider logic decoupled from React specifics
### 3. Composition Over Inheritance
Two separate OpenRouter providers (API & OAuth) use shared utilities rather than complex inheritance:
- `OpenRouterClient` - HTTP client and request formatting
- Shared model parsing and tool formatting logic
- Clean separation of authentication concerns
**Benefits:**
- Simpler code paths (no branching auth logic)
- Easier testing and maintenance
- Clear separation of API vs OAuth concerns
## Future Enhancements
1. **Caching**: Response caching for repeated requests
2. **Load Balancing**: Multi-provider load balancing
3. **Analytics**: Usage analytics and monitoring
4. **Custom Models**: Support for custom/fine-tuned models
5. **Advanced Tool Choice**: Support for specific tool selection (beyond auto)
6. **Streaming Support**: Real-time response streaming (if needed later)