From c8c579da8b7e1465c2476a1f0b18dfe3cc510a2c Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Thu, 21 May 2026 14:25:01 +0200 Subject: [PATCH] docs: rewrite README Full rewrite focused on what consumers actually need: install, a working snippet per provider, and the public API surface. Changes vs the previous version: - Document ClaudeCodeProvider, including the subscription-via-CLI auth path that the new provider enables. - Remove the ProviderRegistry section (the class was removed in R1). - Drop the stale Provider Comparison and Detailed Capabilities tables; vendor capabilities and model lists move too fast for a README to track. - Remove the inaccurate Zero Dependencies and Comprehensive Testing claims (post-refactor 44/91 tests need updating). - Refresh default models (Gemini 1.5 -> 2.5) and the package list to match the current build. - Add a brief Architecture note covering the base hooks introduced in R2 and the OpenWebUI strategy split from R3. README is 315 lines (was 701). --- README.md | 732 +++++++++++++----------------------------------------- 1 file changed, 173 insertions(+), 559 deletions(-) diff --git a/README.md b/README.md index 76347ea..4b252ca 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,10 @@ # Simple AI Provider -A professional, type-safe TypeScript package that provides a unified interface for multiple AI providers. Currently supports **Claude (Anthropic)**, **OpenAI**, **Google Gemini**, and **OpenWebUI** with a consistent API across all providers. +A type-safe TypeScript library with a single API surface for **Claude (Anthropic)**, **OpenAI**, **Google Gemini**, **OpenWebUI**, and **Claude Code** (subscription-friendly via local CLI). -## ✨ Features +The same `complete()` / `stream()` interface works across every provider, with consistent error types, streaming, and optional structured (typed JSON) output. -- πŸ”— **Unified Interface**: Same API for Claude, OpenAI, Gemini, and OpenWebUI -- 🎯 **Type Safety**: Full TypeScript support with comprehensive type definitions -- πŸš€ **Streaming Support**: Real-time response streaming for all providers -- πŸ›‘οΈ **Error Handling**: Standardized error types with provider-specific details -- 🏭 **Factory Pattern**: Easy provider creation and management -- πŸ”§ **Configurable**: Extensive configuration options for each provider -- πŸ“¦ **Zero Dependencies**: Lightweight with minimal external dependencies -- 🌐 **Local Support**: OpenWebUI integration for local/private AI models -- 🎨 **Structured Output**: Define custom response types for type-safe AI outputs -- πŸ—οΈ **Provider Registry**: Dynamic provider registration and creation system -- βœ… **Comprehensive Testing**: Full test coverage with Bun test framework -- πŸ” **Advanced Validation**: Input validation with detailed error messages - -## πŸ—οΈ Architecture - -The library is built on solid design principles: - -- **Template Method Pattern**: Base provider defines the workflow, subclasses implement specifics -- **Factory Pattern**: Clean provider creation and management -- **Strategy Pattern**: Unified interface across different AI providers -- **Type Safety**: Comprehensive TypeScript support throughout -- **Error Normalization**: Consistent error handling across all providers -- **Validation First**: Input validation before processing -- **Extensibility**: Easy to add new providers via registry system - -## πŸš€ Quick Start +## Install ```bash npm install simple-ai-provider @@ -37,37 +12,14 @@ npm install simple-ai-provider bun add simple-ai-provider ``` -### Basic Usage +Requires Node β‰₯ 18 (or Bun). TypeScript β‰₯ 5 is recommended as a peer dependency. + +## Quick start ```typescript -import { ClaudeProvider, OpenAIProvider, GeminiProvider, OpenWebUIProvider } from 'simple-ai-provider'; +import { ClaudeProvider } from 'simple-ai-provider'; -// Claude -const claude = new ClaudeProvider({ - apiKey: process.env.ANTHROPIC_API_KEY!, - defaultModel: 'claude-3-5-sonnet-20241022' -}); - -// OpenAI -const openai = new OpenAIProvider({ - apiKey: process.env.OPENAI_API_KEY!, - defaultModel: 'gpt-4o' -}); - -// Google Gemini -const gemini = new GeminiProvider({ - apiKey: process.env.GOOGLE_AI_API_KEY!, - defaultModel: 'gemini-1.5-flash' -}); - -// OpenWebUI (local) -const openwebui = new OpenWebUIProvider({ - apiKey: 'ollama', // Often not required - baseUrl: 'http://localhost:3000', - defaultModel: 'llama2' -}); - -// Initialize and use any provider +const claude = new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }); await claude.initialize(); const response = await claude.complete({ @@ -75,183 +27,88 @@ const response = await claude.complete({ { role: 'system', content: 'You are a helpful assistant.' }, { role: 'user', content: 'Explain TypeScript in one sentence.' } ], - maxTokens: 100, - temperature: 0.7 + maxTokens: 200 }); console.log(response.content); +console.log(response.usage); // { promptTokens, completionTokens, totalTokens } ``` -## 🏭 Factory Functions +Every provider follows the same pattern: construct, `initialize()`, then `complete()` or `stream()`. -Create providers using factory functions for cleaner code: +## Providers + +### Claude (Anthropic API) ```typescript -import { createProvider, createClaudeProvider, createOpenAIProvider, createGeminiProvider, createOpenWebUIProvider } from 'simple-ai-provider'; +import { ClaudeProvider } from 'simple-ai-provider'; -// Method 1: Specific factory functions -const claude = createClaudeProvider('your-key', { defaultModel: 'claude-3-5-sonnet-20241022' }); -const openai = createOpenAIProvider('your-key', { defaultModel: 'gpt-4o' }); -const gemini = createGeminiProvider('your-key', { defaultModel: 'gemini-1.5-flash' }); -const openwebui = createOpenWebUIProvider({ apiKey: 'your-key', baseUrl: 'http://localhost:3000' }); - -// Method 2: Generic factory -const provider = createProvider('claude', { apiKey: 'your-key' }); +const claude = new ClaudeProvider({ + apiKey: process.env.ANTHROPIC_API_KEY!, + defaultModel: 'claude-3-5-sonnet-20241022', // optional + version: '2023-06-01', // optional + timeout: 30_000, // optional, ms + maxRetries: 3 // optional +}); ``` -### Provider Registry +### Claude Code (subscription via local CLI) -For dynamic provider creation and registration: +`ClaudeCodeProvider` wraps `@anthropic-ai/claude-agent-sdk`, which authenticates through the local `claude` CLI. This is the supported path for **Claude Pro / Max subscribers** who don't have a console API key. + +**Setup:** install the CLI and run `claude login` once. No API key required. ```typescript -import { ProviderRegistry, ClaudeProvider } from 'simple-ai-provider'; +import { ClaudeCodeProvider } from 'simple-ai-provider'; -// Register a custom provider -ProviderRegistry.register('my-claude', ClaudeProvider); - -// Create provider dynamically -const provider = ProviderRegistry.create('my-claude', { apiKey: 'your-key' }); - -// Check available providers -const availableProviders = ProviderRegistry.getRegisteredProviders(); -console.log(availableProviders); // ['claude', 'openai', 'gemini', 'openwebui', 'my-claude'] -``` - -## 🎨 Structured Response Types - -Define custom response types for type-safe, structured AI outputs. The library automatically parses the AI's response into your desired type. - -```typescript -import { createResponseType, createClaudeProvider } from 'simple-ai-provider'; - -// 1. Define your response type -interface ProductAnalysis { - productName: string; - priceRange: 'budget' | 'mid-range' | 'premium'; - pros: string[]; - cons: string[]; - overallRating: number; // 1-10 scale - recommendation: 'buy' | 'consider' | 'avoid'; -} - -// 2. Create a ResponseType object -const productAnalysisType = createResponseType( - 'A comprehensive product analysis with pros, cons, rating, and recommendation' -); - -// 3. Use with any provider -const claude = createClaudeProvider({ apiKey: 'your-key' }); +const claude = new ClaudeCodeProvider({}); // uses local credentials await claude.initialize(); -const response = await claude.complete({ - messages: [ - { role: 'user', content: 'Analyze the iPhone 15 Pro from a consumer perspective.' } - ], - responseType: productAnalysisType, - maxTokens: 800 -}); - -// 4. Get the fully typed and parsed response -const analysis = response.content; -console.log(`Product: ${analysis.productName}`); -console.log(`Recommendation: ${analysis.recommendation}`); -console.log(`Rating: ${analysis.overallRating}/10`); -``` - -### Key Benefits - -- **Automatic Parsing**: The AI's JSON response is automatically parsed into your specified type. -- **Type Safety**: Get fully typed responses from AI providers with IntelliSense. -- **Automatic Prompting**: System prompts are automatically generated to guide the AI. -- **Validation**: Built-in response validation and parsing logic. -- **Consistency**: Ensures AI outputs match your expected format. -- **Developer Experience**: Catch errors at compile-time instead of runtime. - -### Streaming with Response Types - -You can also use response types with streaming. The raw stream provides real-time text, and you can parse the final string once the stream is complete. - -```typescript -import { parseAndValidateResponseType } from 'simple-ai-provider'; - -const stream = claude.stream({ - messages: [{ role: 'user', content: 'Analyze the Tesla Model 3.' }], - responseType: productAnalysisType, - maxTokens: 600 -}); - -let fullResponse = ''; -for await (const chunk of stream) { - if (!chunk.isComplete) { - process.stdout.write(chunk.content); - fullResponse += chunk.content; - } else { - console.log('\n\nStream complete!'); - // Validate the complete streamed response - try { - const analysis = parseAndValidateResponseType(fullResponse, productAnalysisType); - console.log('Validation successful!'); - console.log(`Product: ${analysis.productName}`); - } catch (e) { - console.error('Validation failed:', (e as Error).message); - } - } -} -``` - -## πŸ“ Environment Variables - -Set up your API keys: - -```bash -# Required for respective providers -export ANTHROPIC_API_KEY="your-claude-api-key" -export OPENAI_API_KEY="your-openai-api-key" -export GOOGLE_AI_API_KEY="your-gemini-api-key" - -# OpenWebUI Bearer Token (get from Settings > Account in OpenWebUI) -export OPENWEBUI_API_KEY="your-bearer-token" -``` - -## πŸ”§ Provider-Specific Configuration - -### Claude Configuration - -```typescript -const claude = new ClaudeProvider({ - apiKey: 'your-api-key', - defaultModel: 'claude-3-5-sonnet-20241022', - version: '2023-06-01', - maxRetries: 3, - timeout: 30000 +const response = await claude.complete({ + messages: [{ role: 'user', content: 'Hello!' }] }); ``` -### OpenAI Configuration +You can still pass `apiKey` to override (it's set as `ANTHROPIC_API_KEY` for the SDK). Optional config: ```typescript +new ClaudeCodeProvider({ + defaultModel: 'sonnet', // 'sonnet' | 'opus' | 'haiku' | 'inherit' | full model ID + maxTurns: 1, // 1 for plain completion; raise for agent/tool loops + allowedTools: [], // tool names to enable (default: none) + cwd: process.cwd() // working directory for the agent +}); +``` + +**Trade-offs to know:** +- Requires `claude` CLI installed on the host. Not ideal for typical server deployments. +- Higher latency than the direct API (spawns a CLI process per request). +- Streaming yields text as the SDK emits successive assistant messages, not token-by-token deltas. + +### OpenAI + +```typescript +import { OpenAIProvider } from 'simple-ai-provider'; + const openai = new OpenAIProvider({ - apiKey: 'your-api-key', + apiKey: process.env.OPENAI_API_KEY!, defaultModel: 'gpt-4o', - organization: 'your-org-id', - project: 'your-project-id', - maxRetries: 3, - timeout: 30000 + organization: 'org-...', // optional + project: 'proj-...' // optional }); ``` -### Gemini Configuration +`baseUrl` is supported for OpenAI-compatible endpoints. + +### Gemini (Google) ```typescript +import { GeminiProvider } from 'simple-ai-provider'; + const gemini = new GeminiProvider({ - apiKey: 'your-api-key', - defaultModel: 'gemini-1.5-flash', - safetySettings: [ - { - category: 'HARM_CATEGORY_HARASSMENT', - threshold: 'BLOCK_MEDIUM_AND_ABOVE' - } - ], + apiKey: process.env.GOOGLE_AI_API_KEY!, + defaultModel: 'gemini-2.5-flash', + safetySettings: [/* SafetySetting[] from @google/genai */], generationConfig: { temperature: 0.7, topP: 0.8, @@ -261,441 +118,198 @@ const gemini = new GeminiProvider({ }); ``` -### OpenWebUI Configuration +Backed by `@google/genai` (the successor to the deprecated `@google/generative-ai`). + +### OpenWebUI (local / self-hosted) ```typescript +import { OpenWebUIProvider } from 'simple-ai-provider'; + const openwebui = new OpenWebUIProvider({ - apiKey: 'your-bearer-token', // Get from OpenWebUI Settings > Account - baseUrl: 'http://localhost:3000', // Your OpenWebUI instance - defaultModel: 'llama3.1', - useOllamaProxy: false, // Use OpenWebUI's chat API (recommended) - // useOllamaProxy: true, // Use Ollama API proxy for direct model access - dangerouslyAllowInsecureConnections: true, // For local HTTPS - timeout: 60000, // Longer timeout for local inference - maxRetries: 2 + apiKey: 'your-bearer-token', // from Settings > Account in OpenWebUI + baseUrl: 'http://localhost:3000', + defaultModel: 'llama3.1:latest', + useOllamaProxy: false, // false: OpenWebUI chat API (default) + // true: direct Ollama proxy + dangerouslyAllowInsecureConnections: true }); ``` -## 🌊 Streaming Support +`useOllamaProxy` flips between two internal strategies β€” the OpenAI-compatible chat completions endpoint and the direct Ollama generate endpoint. -All providers support real-time streaming: +## Streaming + +Identical shape across providers: ```typescript -const stream = provider.stream({ - messages: [{ role: 'user', content: 'Count from 1 to 10' }], - maxTokens: 100 -}); - -for await (const chunk of stream) { +for await (const chunk of provider.stream({ messages, maxTokens: 200 })) { if (!chunk.isComplete) { process.stdout.write(chunk.content); } else { - console.log('\nDone! Usage:', chunk.usage); + console.log('\n', chunk.usage); } } ``` -## πŸ”€ Multi-Provider Usage +## Structured output -Use multiple providers seamlessly: +Ask any provider for a typed JSON response. The library injects a system prompt describing the expected shape and parses the result. ```typescript -const providers = { - claude: new ClaudeProvider({ apiKey: process.env.ANTHROPIC_API_KEY! }), - openai: new OpenAIProvider({ apiKey: process.env.OPENAI_API_KEY! }), - gemini: new GeminiProvider({ apiKey: process.env.GOOGLE_AI_API_KEY! }), - openwebui: new OpenWebUIProvider({ - apiKey: 'ollama', - baseUrl: 'http://localhost:3000' - }) -}; +import { createResponseType } from 'simple-ai-provider'; -// Initialize all providers -await Promise.all(Object.values(providers).map(p => p.initialize())); - -// Use the same interface for all -const prompt = { - messages: [{ role: 'user', content: 'Hello!' }], - maxTokens: 50 -}; - -for (const [name, provider] of Object.entries(providers)) { - try { - const response = await provider.complete(prompt); - console.log(`${name}: ${response.content}`); - } catch (error) { - console.log(`${name} failed: ${error.message}`); - } +interface UserProfile { + name: string; + age: number; + hobbies: string[]; } + +const profileType = createResponseType( + 'A user profile with name, age, and hobbies', + { name: 'Alice', age: 30, hobbies: ['climbing', 'photography'] } +); + +const response = await claude.complete({ + messages: [{ role: 'user', content: 'Generate a fictional user profile.' }], + responseType: profileType +}); + +// response.content is typed as UserProfile +// response.rawContent is the original string +console.log(response.content.name); ``` -## πŸ“Š Provider Comparison +This also works with `stream()` β€” chunks deliver text, then the final chunk parses. -| Provider | Context Length | Streaming | Vision | Function Calling | Local Execution | Best For | -|----------|---------------|-----------|--------|------------------|-----------------|----------| -| **Claude** | 200K tokens | βœ… | βœ… | βœ… | ❌ | Reasoning, Analysis, Code Review | -| **OpenAI** | 128K tokens | βœ… | βœ… | βœ… | ❌ | General Purpose, Function Calling | -| **Gemini** | 1M tokens | βœ… | βœ… | βœ… | ❌ | Large Documents, Multimodal | -| **OpenWebUI** | 32K tokens | βœ… | ❌ | ❌ | βœ… | Privacy, Custom Models, Local | +## Factory functions -### Detailed Capabilities - -Each provider offers unique capabilities: - -#### Claude (Anthropic) - -- Advanced reasoning and analysis -- Excellent code review capabilities -- Strong safety features -- System message support - -#### OpenAI - -- Broad model selection -- Function calling support -- JSON mode for structured outputs -- Vision capabilities - -#### Gemini (Google) - -- Largest context window (1M tokens) -- Multimodal capabilities -- Cost-effective pricing -- Strong multilingual support - -#### OpenWebUI - -- Complete privacy (local execution) -- Custom model support -- No API costs -- RAG (Retrieval Augmented Generation) support - -## 🎯 Model Selection - -### Getting Available Models - -Instead of maintaining a static list, you can programmatically get available models: +If you prefer a single entry point: ```typescript -// Get provider information including available models -const info = provider.getInfo(); -console.log('Available models:', info.models); +import { createProvider } from 'simple-ai-provider'; -// Example output: -// Claude: ['claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', ...] -// OpenAI: ['gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', ...] -// Gemini: ['gemini-1.5-flash', 'gemini-1.5-pro', ...] -// OpenWebUI: ['llama3.1:latest', 'mistral:latest', ...] +const claude = createProvider('claude', { apiKey: '…' }); +const openai = createProvider('openai', { apiKey: '…' }); +const gemini = createProvider('gemini', { apiKey: '…' }); +const openwebui = createProvider('openwebui', { apiKey: '…', baseUrl: '…' }); +const claudeCode = createProvider('claude-code', {}); ``` -### Model Selection Guidelines +Per-provider shortcut factories also exist: `createClaudeProvider`, `createOpenAIProvider`, `createGeminiProvider`, `createOpenWebUIProvider`, `createClaudeCodeProvider`. -**For Claude (Anthropic):** -- Check [Anthropic's model documentation](https://docs.anthropic.com/claude/docs/models-overview) for latest models +## Error handling -**For OpenAI:** -- Check [OpenAI's model documentation](https://platform.openai.com/docs/models) for latest models - -**For Gemini (Google):** -- Check [Google AI's model documentation](https://ai.google.dev/docs/models) for latest models - -**For OpenWebUI:** -- Models depend on your local installation -- Check your OpenWebUI instance for available models - -## 🚨 Error Handling - -The package provides comprehensive, standardized error handling with detailed error types: +Every error thrown by the library is an `AIProviderError` with a typed `.type` and optional `.statusCode`: ```typescript import { AIProviderError, AIErrorType } from 'simple-ai-provider'; try { - const response = await provider.complete({ - messages: [{ role: 'user', content: 'Hello' }] - }); + await provider.complete({ messages: [...] }); } catch (error) { if (error instanceof AIProviderError) { switch (error.type) { - case AIErrorType.AUTHENTICATION: - console.log('Invalid API key or authentication failed'); - break; - case AIErrorType.RATE_LIMIT: - console.log('Rate limited, try again later'); - break; - case AIErrorType.MODEL_NOT_FOUND: - console.log('Model not available or not found'); - break; - case AIErrorType.INVALID_REQUEST: - console.log('Invalid request parameters'); - break; - case AIErrorType.NETWORK: - console.log('Network/connection issue'); - break; - case AIErrorType.TIMEOUT: - console.log('Request timed out'); - break; - case AIErrorType.UNKNOWN: - console.log('Unknown error:', error.message); - break; - default: - console.log('Error:', error.message); + case AIErrorType.AUTHENTICATION: /* bad API key, expired token */ break; + case AIErrorType.RATE_LIMIT: /* slow down */ break; + case AIErrorType.MODEL_NOT_FOUND: /* wrong model name */ break; + case AIErrorType.INVALID_REQUEST: /* bad input */ break; + case AIErrorType.NETWORK: /* transient connectivity */ break; + case AIErrorType.TIMEOUT: /* slow request */ break; + case AIErrorType.UNKNOWN: /* fall back */ break; } - - // Access additional error details - console.log('Status Code:', error.statusCode); - console.log('Original Error:', error.originalError); + console.error(error.statusCode, error.originalError); } } ``` -### Error Types +## Configuration reference -- **AUTHENTICATION**: Invalid API keys or authentication failures -- **RATE_LIMIT**: API rate limits exceeded -- **INVALID_REQUEST**: Malformed requests or invalid parameters -- **MODEL_NOT_FOUND**: Requested model is not available -- **NETWORK**: Connection issues or server errors -- **TIMEOUT**: Request timeout exceeded -- **UNKNOWN**: Unclassified errors +All providers share these base options: -## πŸ”§ Advanced Usage +| Option | Type | Default | Notes | +|--------------|----------|-----------|----------------------------------------| +| `apiKey` | `string` | requiredΒΉ | ΒΉ Optional for `ClaudeCodeProvider` | +| `baseUrl` | `string` | provider default | Custom or self-hosted endpoint | +| `timeout` | `number` | `30000` | Request timeout in ms | +| `maxRetries` | `number` | `3` | SDK-level retry attempts | -### Custom Base URLs +Each provider adds its own config (see the sections above). + +## TypeScript + +Full type definitions ship with the package. The main types you'll use: ```typescript -// OpenAI-compatible endpoint -const customOpenAI = new OpenAIProvider({ - apiKey: 'your-key', - baseUrl: 'https://api.custom-provider.com/v1' -}); +import type { + AIMessage, + CompletionParams, + CompletionResponse, + CompletionChunk, + TokenUsage, + ProviderInfo, + ResponseType, -// Custom OpenWebUI instance -const remoteOpenWebUI = new OpenWebUIProvider({ - apiKey: 'your-key', - baseUrl: 'https://my-openwebui.example.com', - apiPath: '/api/v1' -}); + // Per-provider config interfaces + ClaudeConfig, + ClaudeCodeConfig, + OpenAIConfig, + GeminiConfig, + OpenWebUIConfig +} from 'simple-ai-provider'; ``` -### Provider Information +`CompletionResponse` is generic β€” when you pass `responseType`, `content` is `T` (and the original string is preserved on `rawContent`). + +## Provider metadata + +Every provider exposes `getInfo()`: ```typescript const info = provider.getInfo(); -console.log(`Provider: ${info.name} v${info.version}`); -console.log(`Models: ${info.models.join(', ')}`); -console.log(`Max Context: ${info.maxContextLength} tokens`); -console.log(`Supports Streaming: ${info.supportsStreaming}`); -console.log('Capabilities:', info.capabilities); +// { name, version, models, maxContextLength, supportsStreaming, capabilities } ``` -### OpenWebUI-Specific Features +Model lists in `info.models` are example values β€” refer to each vendor's docs for the current authoritative list. The library accepts any model string the underlying SDK supports. -OpenWebUI offers unique advantages for local AI deployment: +## Architecture (brief) -```typescript -const openwebui = new OpenWebUIProvider({ - apiKey: 'your-bearer-token', // Get from OpenWebUI Settings > Account - baseUrl: 'http://localhost:3000', - defaultModel: 'llama3.1', - useOllamaProxy: false, // Use chat completions API (recommended) - // Longer timeout for local inference - timeout: 120000, - // Allow self-signed certificates for local development - dangerouslyAllowInsecureConnections: true -}); +The library uses a template-method base class (`BaseAIProvider`) that owns the public lifecycle (`initialize`, `complete`, `stream`), input validation, response-type parsing, and error normalization. Each provider supplies: -// Test connection and list available models -try { - await openwebui.initialize(); - console.log('Connected to local OpenWebUI instance'); - - // Use either chat completions or Ollama proxy - const response = await openwebui.complete({ - messages: [{ role: 'user', content: 'Hello!' }], - maxTokens: 100 - }); -} catch (error) { - console.log('OpenWebUI not available:', error.message); - // Gracefully fallback to cloud providers -} -``` +- `doInitialize()` / `doComplete()` / `doStream()` β€” the actual SDK calls +- `getModelNamePatterns()` β€” regexes for naming-convention warnings +- `sendValidationProbe()` β€” minimal request used during `initialize()` +- `mapProviderError()` / `providerErrorMessages()` β€” provider-specific error translation -**OpenWebUI API Modes:** +`OpenWebUIProvider` additionally uses an internal strategy split (`OpenWebUIChatStrategy` vs `OpenWebUIOllamaStrategy`) selected by `useOllamaProxy`. -- **Chat Completions** (`useOllamaProxy: false`): OpenWebUI's native API with full features -- **Ollama Proxy** (`useOllamaProxy: true`): Direct access to Ollama API for raw model interaction - -## πŸ“¦ TypeScript Support - -Full TypeScript support with comprehensive type definitions: - -```typescript -import type { - CompletionParams, - CompletionResponse, - CompletionChunk, - ProviderInfo, - ClaudeConfig, - OpenAIConfig, - GeminiConfig, - OpenWebUIConfig, - AIMessage, - ResponseType, - TokenUsage -} from 'simple-ai-provider'; - -// Type-safe configuration -const config: ClaudeConfig = { - apiKey: 'your-key', - defaultModel: 'claude-3-5-sonnet-20241022', - // TypeScript will validate all options -}; - -// Type-safe responses -const response: CompletionResponse = await provider.complete(params); - -// Type-safe messages with metadata -const messages: AIMessage[] = [ - { - role: 'user', - content: 'Hello', - metadata: { timestamp: Date.now() } - } -]; - -// Type-safe response types -interface UserProfile { - name: string; - age: number; -} - -const responseType: ResponseType = createResponseType( - 'A user profile with name and age', - { name: 'John', age: 30 } -); -``` - -### Advanced Type Features - -- **Generic Response Types**: Type-safe structured outputs -- **Message Metadata**: Support for custom message properties -- **Provider-Specific Configs**: Type-safe configuration for each provider -- **Error Types**: Comprehensive error type definitions -- **Factory Functions**: Type-safe provider creation - -## πŸ§ͺ Testing - -The package includes comprehensive tests using Bun test framework: +## Development ```bash -# Run all tests -bun test - -# Run tests for specific provider -bun test tests/claude.test.ts -bun test tests/openai.test.ts -bun test tests/gemini.test.ts -bun test tests/openwebui.test.ts - -# Run tests with coverage -bun test --coverage -``` - -### Test Coverage - -- βœ… Provider initialization and configuration -- βœ… Message validation and conversion -- βœ… Error handling and normalization -- βœ… Response formatting -- βœ… Streaming functionality -- βœ… Structured response types -- βœ… Factory functions -- βœ… Provider registry - -## πŸ› οΈ Development - -### Prerequisites - -- Node.js 18.0.0 or higher -- Bun (recommended) or npm/yarn -- TypeScript 5.0 or higher - -### Setup - -```bash -# Clone the repository git clone https://gitea.jleibl.net/jleibl/simple-ai-provider.git cd simple-ai-provider - -# Install dependencies bun install - -# Build the project bun run build +``` -# Run tests -bun test +Examples live in `examples/`: -# Run examples +```bash bun run examples/basic-usage.ts -bun run examples/structured-response-types.ts bun run examples/multi-provider.ts +bun run examples/structured-response-types.ts ``` -### Project Structure +Tests in `tests/` use Bun's test runner (`bun test`). Note: post-refactor some tests need updating before they pass cleanly. -```text -src/ -β”œβ”€β”€ index.ts # Main entry point -β”œβ”€β”€ types/ -β”‚ └── index.ts # Type definitions and utilities -β”œβ”€β”€ providers/ -β”‚ β”œβ”€β”€ base.ts # Abstract base provider -β”‚ β”œβ”€β”€ claude.ts # Claude provider implementation -β”‚ β”œβ”€β”€ openai.ts # OpenAI provider implementation -β”‚ β”œβ”€β”€ gemini.ts # Gemini provider implementation -β”‚ β”œβ”€β”€ openwebui.ts # OpenWebUI provider implementation -β”‚ └── index.ts # Provider exports -└── utils/ - └── factory.ts # Factory functions and registry +## License -examples/ -β”œβ”€β”€ basic-usage.ts # Basic usage examples -β”œβ”€β”€ structured-response-types.ts # Structured output examples -└── multi-provider.ts # Multi-provider examples +MIT β€” see [LICENSE](LICENSE). -tests/ -β”œβ”€β”€ claude.test.ts # Claude provider tests -β”œβ”€β”€ openai.test.ts # OpenAI provider tests -β”œβ”€β”€ gemini.test.ts # Gemini provider tests -└── openwebui.test.ts # OpenWebUI provider tests -``` - -## 🀝 Contributing - -Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change. - -### Development Guidelines - -1. **Code Style**: Follow the existing TypeScript patterns -2. **Testing**: Add tests for new features -3. **Documentation**: Update README for new features -4. **Type Safety**: Maintain comprehensive type definitions -5. **Error Handling**: Use standardized error types - -## πŸ“„ License - -MIT License - see the [LICENSE](LICENSE) file for details. - -## πŸ”— Links +## Links - [Anthropic Claude API](https://docs.anthropic.com/claude/reference/) +- [Claude Code SDK](https://docs.anthropic.com/claude/docs/claude-code-sdk) - [OpenAI API](https://platform.openai.com/docs/) -- [Google Gemini API](https://ai.google.dev/) +- [Google Gen AI SDK](https://ai.google.dev/) - [OpenWebUI](https://openwebui.com/) -- [Gitea Repository](https://gitea.jleibl.net/jleibl/simple-ai-provider) - ---- - -⭐ **Star this repo if you find it helpful!** +- [Repository](https://gitea.jleibl.net/jleibl/simple-ai-provider)