feat(docs): update README with OpenWebUI support details

This commit is contained in:
2025-05-28 12:31:11 +02:00
parent 5da37f388f
commit 664a775724
8 changed files with 1705 additions and 717 deletions

View File

@ -1,297 +1,269 @@
/**
* Multi-provider example for Simple AI Provider
* Demonstrates how to use Claude, OpenAI, and Gemini providers
* Multi-Provider Example - Demonstrating all supported AI providers
*
* This example shows how to use Claude, OpenAI, Gemini, and OpenWebUI providers
* with consistent interfaces, showcasing the power of the unified API.
*/
import {
createClaudeProvider,
createOpenAIProvider,
createGeminiProvider,
import {
ClaudeProvider,
OpenAIProvider,
GeminiProvider,
OpenWebUIProvider,
createProvider,
ProviderRegistry,
AIProviderError,
AIErrorType
type ClaudeConfig,
type OpenAIConfig,
type GeminiConfig,
type OpenWebUIConfig,
type CompletionParams
} from '../src/index.js';
async function multiProviderExample() {
console.log('=== Multi-Provider AI Example ===\n');
// Provider configurations
const configs = {
claude: {
apiKey: process.env.ANTHROPIC_API_KEY || 'your-claude-api-key',
defaultModel: 'claude-3-5-sonnet-20241022'
} as ClaudeConfig,
// Get API keys from environment
const claudeApiKey = process.env.ANTHROPIC_API_KEY || 'your-claude-api-key';
const openaiApiKey = process.env.OPENAI_API_KEY || 'your-openai-api-key';
const geminiApiKey = process.env.GOOGLE_AI_API_KEY || 'your-gemini-api-key';
openai: {
apiKey: process.env.OPENAI_API_KEY || 'your-openai-api-key',
defaultModel: 'gpt-4o'
} as OpenAIConfig,
gemini: {
apiKey: process.env.GOOGLE_AI_API_KEY || 'your-gemini-api-key',
defaultModel: 'gemini-1.5-flash'
} as GeminiConfig,
openwebui: {
apiKey: process.env.OPENWEBUI_API_KEY || 'your-bearer-token', // Get from OpenWebUI Settings > Account
baseUrl: 'http://localhost:3000',
defaultModel: 'llama3.1',
useOllamaProxy: false // Set to true to use Ollama API proxy
} as OpenWebUIConfig
};
async function demonstrateProviders() {
console.log('🤖 Multi-Provider AI Demo\n');
// ===== 1. Direct Provider Creation =====
console.log('1⃣ Creating providers directly...\n');
const claude = new ClaudeProvider(configs.claude);
const openai = new OpenAIProvider(configs.openai);
const gemini = new GeminiProvider(configs.gemini);
const openwebui = new OpenWebUIProvider(configs.openwebui);
const providers = { claude, openai, gemini, openwebui };
// ===== 2. Factory Creation =====
console.log('2⃣ Creating providers via factory...\n');
const factoryProviders = {
claude: createProvider('claude', configs.claude),
openai: createProvider('openai', configs.openai),
gemini: createProvider('gemini', configs.gemini),
openwebui: createProvider('openwebui', configs.openwebui)
};
// ===== 3. Provider Information =====
console.log('3⃣ Provider Information:\n');
for (const [name, provider] of Object.entries(providers)) {
const info = provider.getInfo();
console.log(`${name.toUpperCase()}: ${info.name} v${info.version}`);
console.log(` • Context: ${info.maxContextLength.toLocaleString()} tokens`);
console.log(` • Streaming: ${info.supportsStreaming ? '✅' : '❌'}`);
console.log(` • Models: ${info.models.slice(0, 3).join(', ')}${info.models.length > 3 ? '...' : ''}`);
if (info.capabilities) {
console.log(` • Vision: ${info.capabilities.vision ? '✅' : '❌'}`);
console.log(` • Function Calling: ${info.capabilities.functionCalling ? '✅' : '❌'}`);
console.log(` • Local Execution: ${info.capabilities.localExecution ? '✅' : '❌'}`);
}
console.log();
}
// ===== 4. Common Completion Example =====
console.log('4⃣ Running completions across all providers...\n');
const messages = [
{ role: 'system' as const, content: 'You are a helpful assistant. Be concise.' },
{ role: 'user' as const, content: 'What is TypeScript? Answer in one sentence.' }
];
const params: CompletionParams = {
messages,
maxTokens: 50,
temperature: 0.7
};
for (const [name, provider] of Object.entries(providers)) {
try {
console.log(`${name.toUpperCase()} Response:`);
// Initialize provider (would be done once in real app)
await provider.initialize();
const response = await provider.complete(params);
console.log(`${response.content.trim()}`);
console.log(` 📊 Tokens: ${response.usage.totalTokens} (${response.usage.promptTokens}+${response.usage.completionTokens})\n`);
} catch (error) {
console.log(` ❌ Error: ${(error as Error).message}\n`);
}
}
// ===== 5. Streaming Example =====
console.log('5⃣ Streaming example (Claude)...\n');
try {
// Method 1: Using factory functions
console.log('1. Creating providers using factory functions...');
await claude.initialize();
const claude = createClaudeProvider(claudeApiKey, {
defaultModel: 'claude-3-5-haiku-20241022'
});
const openai = createOpenAIProvider(openaiApiKey, {
defaultModel: 'gpt-3.5-turbo'
});
const gemini = createGeminiProvider(geminiApiKey, {
defaultModel: 'gemini-1.5-flash'
});
console.log('✓ Providers created\n');
// Method 2: Using generic createProvider function
console.log('2. Creating providers using generic factory...');
const claude2 = createProvider('claude', {
apiKey: claudeApiKey,
defaultModel: 'claude-3-5-haiku-20241022'
});
const openai2 = createProvider('openai', {
apiKey: openaiApiKey,
defaultModel: 'gpt-3.5-turbo'
});
const gemini2 = createProvider('gemini', {
apiKey: geminiApiKey,
defaultModel: 'gemini-1.5-flash'
});
console.log('✓ Generic providers created\n');
// Method 3: Using Provider Registry
console.log('3. Using Provider Registry...');
console.log('Registered providers:', ProviderRegistry.getRegisteredProviders());
const claudeFromRegistry = ProviderRegistry.create('claude', {
apiKey: claudeApiKey,
defaultModel: 'claude-3-5-haiku-20241022'
});
console.log('✓ Provider created from registry\n');
// Initialize providers
console.log('4. Initializing providers...');
await Promise.all([
claude.initialize(),
openai.initialize(),
gemini.initialize()
]);
console.log('✓ All providers initialized\n');
// Compare provider information
console.log('5. Provider Information:');
console.log('Claude Info:', claude.getInfo());
console.log('OpenAI Info:', openai.getInfo());
console.log('Gemini Info:', gemini.getInfo());
console.log();
// Test the same prompt with all providers
const testPrompt = 'Explain the concept of recursion in programming in one sentence.';
console.log('6. Testing same prompt with all providers...');
console.log(`Prompt: "${testPrompt}"\n`);
// Claude response
console.log('--- Claude Response ---');
const claudeResponse = await claude.complete({
messages: [
{ role: 'system', content: 'You are a concise programming tutor.' },
{ role: 'user', content: testPrompt }
],
maxTokens: 100,
temperature: 0.7
});
console.log('Response:', claudeResponse.content);
console.log('Usage:', claudeResponse.usage);
console.log('Model:', claudeResponse.model);
console.log();
// OpenAI response
console.log('--- OpenAI Response ---');
const openaiResponse = await openai.complete({
messages: [
{ role: 'system', content: 'You are a concise programming tutor.' },
{ role: 'user', content: testPrompt }
],
maxTokens: 100,
temperature: 0.7
});
console.log('Response:', openaiResponse.content);
console.log('Usage:', openaiResponse.usage);
console.log('Model:', openaiResponse.model);
console.log();
// Gemini response
console.log('--- Gemini Response ---');
const geminiResponse = await gemini.complete({
messages: [
{ role: 'system', content: 'You are a concise programming tutor.' },
{ role: 'user', content: testPrompt }
],
maxTokens: 100,
temperature: 0.7
});
console.log('Response:', geminiResponse.content);
console.log('Usage:', geminiResponse.usage);
console.log('Model:', geminiResponse.model);
console.log();
// Streaming comparison
console.log('7. Streaming comparison...');
console.log('Streaming from Claude:');
console.log('Claude Streaming Response:');
process.stdout.write(' ');
for await (const chunk of claude.stream({
messages: [{ role: 'user', content: 'Count from 1 to 5.' }],
maxTokens: 50
messages: [{ role: 'user', content: 'Count from 1 to 5 with explanations.' }],
maxTokens: 150
})) {
if (!chunk.isComplete) {
process.stdout.write(chunk.content);
} else {
console.log('\n✓ Claude streaming complete\n');
} else if (chunk.usage) {
console.log(`\n 📊 Final tokens: ${chunk.usage.totalTokens}\n`);
}
}
console.log('Streaming from OpenAI:');
for await (const chunk of openai.stream({
messages: [{ role: 'user', content: 'Count from 1 to 5.' }],
maxTokens: 50
})) {
if (!chunk.isComplete) {
process.stdout.write(chunk.content);
} else {
console.log('\n✓ OpenAI streaming complete\n');
}
}
console.log('Streaming from Gemini:');
for await (const chunk of gemini.stream({
messages: [{ role: 'user', content: 'Count from 1 to 5.' }],
maxTokens: 50
})) {
if (!chunk.isComplete) {
process.stdout.write(chunk.content);
} else {
console.log('\n✓ Gemini streaming complete\n');
}
}
// Provider-specific features demo
console.log('8. Provider-specific features...');
// OpenAI with organization
const openaiWithOrg = createOpenAIProvider(openaiApiKey, {
defaultModel: 'gpt-3.5-turbo',
organization: 'org-example',
timeout: 60000
});
console.log('✓ Created OpenAI provider with organization settings');
// Claude with custom version
const claudeCustom = createClaudeProvider(claudeApiKey, {
defaultModel: 'claude-3-5-sonnet-20241022',
version: '2023-06-01',
maxRetries: 5
});
console.log('✓ Created Claude provider with custom settings');
// Gemini with safety settings
const geminiCustom = createGeminiProvider(geminiApiKey, {
defaultModel: 'gemini-1.5-pro',
safetySettings: [],
generationConfig: {
temperature: 0.9,
topP: 0.8,
topK: 40
}
});
console.log('✓ Created Gemini provider with safety and generation settings');
console.log('\n🎉 Multi-provider example completed successfully!');
} catch (error) {
console.error('❌ Error in multi-provider example:');
if (error instanceof AIProviderError) {
console.error(`AI Provider Error (${error.type}):`, error.message);
switch (error.type) {
case AIErrorType.AUTHENTICATION:
console.error('💡 Hint: Check your API keys in environment variables');
console.error(' Set ANTHROPIC_API_KEY, OPENAI_API_KEY, and GOOGLE_AI_API_KEY');
break;
case AIErrorType.RATE_LIMIT:
console.error('💡 Hint: You are being rate limited. Wait and try again.');
break;
case AIErrorType.INVALID_REQUEST:
console.error('💡 Hint: Check your request parameters.');
break;
default:
console.error('💡 Hint: An unexpected error occurred.');
}
} else {
console.error('Unexpected error:', error);
}
console.log(` ❌ Streaming error: ${(error as Error).message}\n`);
}
}
// Provider comparison utility
async function compareProviders() {
console.log('\n=== Provider Comparison ===');
const providers = [
{ name: 'Claude', factory: () => createClaudeProvider('dummy-key') },
{ name: 'OpenAI', factory: () => createOpenAIProvider('dummy-key') },
{ name: 'Gemini', factory: () => createGeminiProvider('dummy-key') }
// ===== 6. Provider-Specific Features =====
console.log('6⃣ Provider-specific features...\n');
// Claude - Advanced reasoning
try {
await claude.initialize();
console.log('Claude Advanced Reasoning:');
const claudeResponse = await claude.complete({
messages: [{
role: 'user',
content: 'Analyze the logical structure of this argument: "All humans are mortal. Socrates is human. Therefore, Socrates is mortal."'
}],
maxTokens: 100,
temperature: 0.1
});
console.log(`${claudeResponse.content.trim()}\n`);
} catch (error) {
console.log(` ❌ Claude error: ${(error as Error).message}\n`);
}
// OpenAI - Function calling (conceptual)
try {
await openai.initialize();
console.log('OpenAI Code Generation:');
const openaiResponse = await openai.complete({
messages: [{
role: 'user',
content: 'Write a simple TypeScript function to calculate factorial. Just the function, no explanation.'
}],
maxTokens: 100,
temperature: 0.3
});
console.log(`${openaiResponse.content.trim()}\n`);
} catch (error) {
console.log(` ❌ OpenAI error: ${(error as Error).message}\n`);
}
// Gemini - Large context
try {
await gemini.initialize();
console.log('Gemini Large Context Capability:');
const geminiResponse = await gemini.complete({
messages: [{
role: 'user',
content: 'Explain the benefits of having 1M token context length for AI applications.'
}],
maxTokens: 80,
temperature: 0.5
});
console.log(`${geminiResponse.content.trim()}\n`);
} catch (error) {
console.log(` ❌ Gemini error: ${(error as Error).message}\n`);
}
// OpenWebUI - Local model capabilities
try {
await openwebui.initialize();
console.log('OpenWebUI Local Model:');
const openwebuiResponse = await openwebui.complete({
messages: [{
role: 'user',
content: 'What are the advantages of running AI models locally?'
}],
maxTokens: 80,
temperature: 0.6
});
console.log(`${openwebuiResponse.content.trim()}\n`);
} catch (error) {
console.log(` ❌ OpenWebUI error: ${(error as Error).message}`);
console.log(` (This is expected if OpenWebUI is not running locally or API key is invalid)\n`);
}
// ===== 7. Error Handling Demonstration =====
console.log('7⃣ Error handling examples...\n');
try {
const invalidProvider = new ClaudeProvider({ apiKey: 'invalid-key' });
await invalidProvider.initialize();
await invalidProvider.complete({
messages: [{ role: 'user', content: 'Test' }]
});
} catch (error: any) {
console.log('Expected authentication error:');
console.log(` ❌ Type: ${error.type}`);
console.log(` ❌ Message: ${error.message}\n`);
}
// ===== 8. Performance Comparison =====
console.log('8⃣ Provider Comparison Summary:\n');
const comparison = [
{
Provider: 'Claude',
'Context Length': '200K tokens',
'Best For': 'Reasoning, Analysis, Code Review',
'Streaming': '✅',
'Cost': 'Mid-range'
},
{
Provider: 'OpenAI',
'Context Length': '128K tokens',
'Best For': 'General Purpose, Function Calling',
'Streaming': '✅',
'Cost': 'Variable'
},
{
Provider: 'Gemini',
'Context Length': '1M tokens',
'Best For': 'Large Documents, Multimodal',
'Streaming': '✅',
'Cost': 'Low-cost'
},
{
Provider: 'OpenWebUI',
'Context Length': '8K-32K tokens',
'Best For': 'Privacy, Local Inference, Custom Models, RAG',
'Streaming': '✅',
'Cost': 'Free (compute)'
}
];
console.log('\nProvider Capabilities:');
console.log('| Provider | Models | Context | Streaming | Vision | Functions | Multimodal |');
console.log('|----------|--------|---------|-----------|--------|-----------|------------|');
console.table(comparison);
for (const { name, factory } of providers) {
const provider = factory();
const info = provider.getInfo();
const contextStr = info.maxContextLength >= 1000000
? `${(info.maxContextLength / 1000000).toFixed(1)}M`
: `${(info.maxContextLength / 1000).toFixed(0)}K`;
console.log(`| ${name.padEnd(8)} | ${info.models.length.toString().padEnd(6)} | ${contextStr.padEnd(7)} | ${info.supportsStreaming ? '✓' : '✗'.padEnd(9)} | ${info.capabilities?.vision ? '✓' : '✗'.padEnd(6)} | ${info.capabilities?.functionCalling ? '✓' : '✗'.padEnd(9)} | ${info.capabilities?.multimodal ? '✓' : '✗'.padEnd(10)} |`);
}
console.log();
console.log('\n✨ Demo completed! All providers work with the same unified interface.\n');
}
// Feature comparison
async function featureComparison() {
console.log('\n=== Feature Comparison ===');
const features = [
['Provider', 'Context Window', 'Streaming', 'Vision', 'Function Calling', 'System Messages', 'Special Features'],
['Claude', '200K tokens', '✅', '✅', '✅', '✅ (separate)', 'Advanced reasoning'],
['OpenAI', '128K tokens', '✅', '✅', '✅', '✅ (inline)', 'JSON mode, plugins'],
['Gemini', '1M tokens', '✅', '✅', '✅', '✅ (separate)', 'Largest context, multimodal']
];
for (const row of features) {
console.log('| ' + row.map(cell => cell.padEnd(15)).join(' | ') + ' |');
if (row[0] === 'Provider') {
console.log('|' + ''.padEnd(row.length * 17 + row.length - 1, '-') + '|');
}
}
console.log();
}
// Run the examples
// Run the demonstration
if (import.meta.main) {
await multiProviderExample();
await compareProviders();
await featureComparison();
demonstrateProviders().catch(console.error);
}