Lesson 4.5: Comparing and Choosing Providers
Duration: 50 minutes
Learning Objectives
By the end of this lesson, you will be able to:
- Compare AI providers across key dimensions
- Apply a decision framework for choosing providers
- Understand pricing models and optimize costs
- Implement multi-provider strategies
- Make informed decisions based on your project requirements
- Plan for provider changes and migrations
Introduction
You have learned about OpenAI, Anthropic, Google, and open-source options. But how do you choose the right one for your project? In this lesson, you will learn a systematic approach to evaluating and selecting AI providers based on your specific needs, constraints, and goals.
Provider Comparison Overview
┌───────────────────────────────────────────────────────────────────────────┐
│ Provider Comparison Matrix │
├─────────────┬──────────────┬──────────────┬──────────────┬────────────────┤
│ Dimension │ OpenAI │ Anthropic │ Google │ Open Source │
├─────────────┼──────────────┼──────────────┼──────────────┼────────────────┤
│ Quality │ Excellent │ Excellent │ Very Good │ Good │
│ Speed │ Fast │ Fast │ Very Fast │ Varies │
│ Cost │ Medium │ Medium │ Low │ Infrastructure│
│ Context │ 128K │ 200K │ 2M │ Varies │
│ Vision │ Yes │ Yes │ Yes │ Limited │
│ Privacy │ Cloud │ Cloud │ Cloud │ Full Control │
│ Ecosystem │ Largest │ Growing │ Google Svc │ Community │
└─────────────┴──────────────┴──────────────┴──────────────┴────────────────┘
Decision Framework
Use this framework to evaluate providers for your specific use case:
Step 1: Define Your Requirements
Start by answering these questions:
interface ProjectRequirements {
// Quality requirements
taskComplexity: 'simple' | 'moderate' | 'complex';
accuracyNeeded: 'best-effort' | 'high' | 'critical';
// Technical requirements
contextLength: number; // tokens needed
needsVision: boolean;
needsStreaming: boolean;
needsFunctionCalling: boolean;
// Operational requirements
expectedVolume: 'low' | 'medium' | 'high'; // requests per day
latencyRequirement: 'relaxed' | 'moderate' | 'strict';
uptimeRequirement: number; // percentage, e.g., 99.9
// Business requirements
budget: 'minimal' | 'moderate' | 'flexible';
dataPrivacy: 'standard' | 'sensitive' | 'regulated';
vendorLockIn: 'acceptable' | 'minimize' | 'avoid';
}
Step 2: Score Providers
interface ProviderScore {
provider: string;
scores: {
qualityFit: number; // 1-10
costEfficiency: number; // 1-10
featureMatch: number; // 1-10
operationalFit: number; // 1-10
riskLevel: number; // 1-10 (lower is better)
};
totalScore: number;
recommendation: string;
}
function evaluateProvider(requirements: ProjectRequirements, provider: string): ProviderScore {
// This would be your evaluation logic
// Example scoring for demonstration
const scores = {
qualityFit: 0,
costEfficiency: 0,
featureMatch: 0,
operationalFit: 0,
riskLevel: 0,
};
// Score based on requirements vs. provider capabilities
// ... evaluation logic ...
return {
provider,
scores,
totalScore: Object.values(scores).reduce((a, b) => a + b, 0),
recommendation: '',
};
}
Step 3: Decision Matrix
| Requirement | OpenAI | Anthropic | Open Source | |
|---|---|---|---|---|
| Complex reasoning | ★★★★★ | ★★★★★ | ★★★★ | ★★★ |
| Code generation | ★★★★★ | ★★★★★ | ★★★★ | ★★★★ |
| Long documents | ★★★★ | ★★★★★ | ★★★★★ | ★★★ |
| Vision tasks | ★★★★★ | ★★★★ | ★★★★★ | ★★ |
| Low latency | ★★★★ | ★★★★ | ★★★★★ | ★★★★★ |
| Low cost | ★★★ | ★★★ | ★★★★★ | ★★★★ |
| Data privacy | ★★★ | ★★★ | ★★★ | ★★★★★ |
| Ecosystem | ★★★★★ | ★★★★ | ★★★★ | ★★★★ |
Pricing Comparison
Understanding pricing is crucial for production applications:
Cost Per Million Tokens (2024)
┌─────────────────────────────────────────────────────────────────┐
│ Cost Comparison (USD per 1M tokens) │
├─────────────────┬───────────────┬───────────────────────────────┤
│ Model │ Input │ Output │
├─────────────────┼───────────────┼───────────────────────────────┤
│ GPT-4o-mini │ $0.15 │ $0.60 │
│ GPT-4o │ $2.50 │ $10.00 │
│ Claude Sonnet │ $3.00 │ $15.00 │
│ Claude Haiku │ $0.25 │ $1.25 │
│ Gemini Flash │ $0.075 │ $0.30 │
│ Gemini Pro │ $1.25 │ $5.00 │
│ Llama (Groq) │ $0.05 │ $0.08 │
│ Llama (local) │ Infrastructure cost only │
└─────────────────┴───────────────┴───────────────────────────────┘
Cost Calculator
interface UsageEstimate {
requestsPerDay: number;
avgInputTokens: number;
avgOutputTokens: number;
}
interface PricingTier {
inputPer1M: number;
outputPer1M: number;
}
const PRICING: Record<string, PricingTier> = {
'gpt-4o-mini': { inputPer1M: 0.15, outputPer1M: 0.6 },
'gpt-4o': { inputPer1M: 2.5, outputPer1M: 10.0 },
'claude-sonnet': { inputPer1M: 3.0, outputPer1M: 15.0 },
'claude-haiku': { inputPer1M: 0.25, outputPer1M: 1.25 },
'gemini-flash': { inputPer1M: 0.075, outputPer1M: 0.3 },
'gemini-pro': { inputPer1M: 1.25, outputPer1M: 5.0 },
};
function calculateMonthlyCost(
model: string,
usage: UsageEstimate
): { daily: number; monthly: number; yearly: number } {
const pricing = PRICING[model];
if (!pricing) {
throw new Error(`Unknown model: ${model}`);
}
const dailyInputTokens = usage.requestsPerDay * usage.avgInputTokens;
const dailyOutputTokens = usage.requestsPerDay * usage.avgOutputTokens;
const dailyInputCost = (dailyInputTokens / 1_000_000) * pricing.inputPer1M;
const dailyOutputCost = (dailyOutputTokens / 1_000_000) * pricing.outputPer1M;
const dailyCost = dailyInputCost + dailyOutputCost;
return {
daily: dailyCost,
monthly: dailyCost * 30,
yearly: dailyCost * 365,
};
}
function compareAllProviders(usage: UsageEstimate): void {
console.log('Monthly Cost Comparison:');
console.log('========================');
for (const [model, _] of Object.entries(PRICING)) {
const cost = calculateMonthlyCost(model, usage);
console.log(`${model}: $${cost.monthly.toFixed(2)}/month`);
}
}
// Example usage
compareAllProviders({
requestsPerDay: 1000,
avgInputTokens: 500,
avgOutputTokens: 200,
});
Cost Optimization Strategies
-
Choose the right model size
// Use smaller models for simple tasks const simpleTaskModel = 'gpt-4o-mini'; // $0.15/1M input const complexTaskModel = 'gpt-4o'; // $2.50/1M input function selectModel(taskComplexity: 'simple' | 'complex'): string { return taskComplexity === 'simple' ? simpleTaskModel : complexTaskModel; } -
Implement caching
const cache = new Map<string, string>(); async function cachedChat(prompt: string): Promise<string> { const cacheKey = prompt.toLowerCase().trim(); if (cache.has(cacheKey)) { return cache.get(cacheKey)!; } const response = await chat(prompt); cache.set(cacheKey, response); return response; } -
Optimize prompts
// Bad: Verbose, wasteful tokens const verbosePrompt = ` I would really appreciate it if you could help me with something. I'm trying to understand TypeScript generics and I was wondering if you could explain them to me in detail with examples. `; // Good: Concise, same result const concisePrompt = 'Explain TypeScript generics with examples.'; -
Batch requests when possible
// Instead of multiple calls const items = ['apple', 'banana', 'orange']; // Bad: 3 API calls for (const item of items) { await classifyItem(item); } // Good: 1 API call const results = await classifyItems(items.join(', '));
Multi-Provider Strategies
Strategy 1: Route by Task Type
type TaskType = 'chat' | 'code' | 'analysis' | 'creative';
interface ProviderConfig {
provider: string;
model: string;
client: any;
}
const TASK_ROUTING: Record<TaskType, ProviderConfig> = {
chat: {
provider: 'anthropic',
model: 'claude-3-5-haiku-20241022',
client: anthropicClient,
},
code: {
provider: 'openai',
model: 'gpt-4o',
client: openaiClient,
},
analysis: {
provider: 'google',
model: 'gemini-1.5-pro',
client: googleClient,
},
creative: {
provider: 'anthropic',
model: 'claude-sonnet-4-20250514',
client: anthropicClient,
},
};
async function routedRequest(taskType: TaskType, prompt: string): Promise<string> {
const config = TASK_ROUTING[taskType];
// Use the appropriate provider
return executeRequest(config, prompt);
}
Strategy 2: Fallback Chain
interface ProviderWithFallback {
primary: ProviderConfig;
fallbacks: ProviderConfig[];
}
async function requestWithFallback(
config: ProviderWithFallback,
prompt: string
): Promise<{ response: string; usedProvider: string }> {
// Try primary
try {
const response = await executeRequest(config.primary, prompt);
return { response, usedProvider: config.primary.provider };
} catch (error) {
console.log(`Primary failed: ${config.primary.provider}`);
}
// Try fallbacks in order
for (const fallback of config.fallbacks) {
try {
const response = await executeRequest(fallback, prompt);
return { response, usedProvider: fallback.provider };
} catch (error) {
console.log(`Fallback failed: ${fallback.provider}`);
}
}
throw new Error('All providers failed');
}
Strategy 3: Load Balancing
class LoadBalancer {
private providers: ProviderConfig[];
private currentIndex: number = 0;
private requestCounts: Map<string, number> = new Map();
constructor(providers: ProviderConfig[]) {
this.providers = providers;
providers.forEach((p) => this.requestCounts.set(p.provider, 0));
}
// Round-robin selection
getNextProvider(): ProviderConfig {
const provider = this.providers[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.providers.length;
this.requestCounts.set(provider.provider, (this.requestCounts.get(provider.provider) || 0) + 1);
return provider;
}
// Weighted selection based on cost
getCheapestAvailable(): ProviderConfig {
// Implementation would check rate limits and costs
return this.providers[0];
}
getStats(): Map<string, number> {
return new Map(this.requestCounts);
}
}
Strategy 4: Quality-Based Routing
interface QualityMetrics {
provider: string;
avgLatency: number;
errorRate: number;
userSatisfaction: number;
}
class QualityRouter {
private metrics: Map<string, QualityMetrics> = new Map();
private providers: ProviderConfig[];
constructor(providers: ProviderConfig[]) {
this.providers = providers;
}
recordMetric(provider: string, latency: number, success: boolean, satisfaction?: number): void {
const current = this.metrics.get(provider) || {
provider,
avgLatency: 0,
errorRate: 0,
userSatisfaction: 0,
};
// Update rolling averages
// ... implementation
}
getBestProvider(): ProviderConfig {
let bestScore = -Infinity;
let bestProvider = this.providers[0];
for (const provider of this.providers) {
const metrics = this.metrics.get(provider.provider);
if (!metrics) continue;
// Score based on quality metrics
const score = this.calculateScore(metrics);
if (score > bestScore) {
bestScore = score;
bestProvider = provider;
}
}
return bestProvider;
}
private calculateScore(metrics: QualityMetrics): number {
return (
(1 - metrics.errorRate) * 40 + (1 / metrics.avgLatency) * 30 + metrics.userSatisfaction * 30
);
}
}
Common Use Case Recommendations
Use Case 1: Customer Support Chatbot
Recommended: Claude (Anthropic)
- Natural conversation style
- Good at following guidelines
- Handles edge cases gracefully
Alternative: GPT-4o-mini (OpenAI)
- Lower cost at high volume
- Fast response times
Configuration:
- Model: claude-3-5-haiku-20241022 or gpt-4o-mini
- Temperature: 0.3-0.5
- Max tokens: 500-1000
Use Case 2: Code Generation Tool
Recommended: GPT-4o (OpenAI) or Claude Sonnet (Anthropic)
- Both excel at code generation
- Good understanding of context
Alternative: CodeLlama (Open Source)
- Free, self-hosted option
- Good for specific languages
Configuration:
- Model: gpt-4o or claude-sonnet-4-20250514
- Temperature: 0.2
- Max tokens: 2000-4000
Use Case 3: Document Analysis
Recommended: Gemini 1.5 Pro (Google)
- 2M token context window
- Good at summarization
Alternative: Claude (Anthropic)
- 200K context window
- Excellent comprehension
Configuration:
- Model: gemini-1.5-pro or claude-sonnet-4-20250514
- Temperature: 0.3
- Max tokens: varies by task
Use Case 4: Privacy-Sensitive Application
Recommended: Open Source (Llama, Mistral)
- Data never leaves your infrastructure
- Full control over processing
Configuration:
- Host with Ollama or similar
- Model: llama3.1:8b or mistral
- Deploy in secure environment
Use Case 5: High-Volume, Low-Cost
Recommended: Gemini Flash (Google) or Groq (Llama)
- Lowest cost per token
- Fast inference
Configuration:
- Model: gemini-1.5-flash or llama-3.1-70b (Groq)
- Implement caching
- Batch requests when possible
Migration and Portability
Building Provider-Agnostic Code
// Define a common interface
interface AIMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
interface AIResponse {
content: string;
usage: {
inputTokens: number;
outputTokens: number;
};
}
interface AIProvider {
chat(messages: AIMessage[], options?: ChatOptions): Promise<AIResponse>;
}
// Implement for each provider
class OpenAIProvider implements AIProvider {
async chat(messages: AIMessage[]): Promise<AIResponse> {
// OpenAI-specific implementation
}
}
class AnthropicProvider implements AIProvider {
async chat(messages: AIMessage[]): Promise<AIResponse> {
// Anthropic-specific implementation
}
}
// Use dependency injection
class AIService {
constructor(private provider: AIProvider) {}
async chat(messages: AIMessage[]): Promise<AIResponse> {
return this.provider.chat(messages);
}
}
// Easy to switch providers
const service = new AIService(new OpenAIProvider());
// Later: const service = new AIService(new AnthropicProvider());
Migration Checklist
When migrating between providers:
## Pre-Migration
- [ ] Document current prompts and their expected outputs
- [ ] Identify provider-specific features being used
- [ ] Estimate cost differences
- [ ] Plan for testing period
## During Migration
- [ ] Update SDK dependencies
- [ ] Adapt prompts for new provider
- [ ] Adjust parameters (temperature, max_tokens)
- [ ] Update error handling
- [ ] Implement parallel testing
## Post-Migration
- [ ] Monitor quality metrics
- [ ] Compare costs
- [ ] Gather user feedback
- [ ] Document differences
- [ ] Update documentation
Exercises
Exercise 1: Cost Estimator
Build a tool that estimates costs across providers:
// Your implementation here
interface UsageProfile {
description: string;
dailyRequests: number;
avgInputTokens: number;
avgOutputTokens: number;
}
function estimateCosts(profile: UsageProfile): void {
// TODO: Calculate and display monthly costs for all providers
}
Solution
interface UsageProfile {
description: string;
dailyRequests: number;
avgInputTokens: number;
avgOutputTokens: number;
}
interface ProviderCost {
provider: string;
model: string;
monthlyInputCost: number;
monthlyOutputCost: number;
totalMonthlyCost: number;
}
const MODELS = [
{ provider: 'OpenAI', model: 'gpt-4o-mini', input: 0.15, output: 0.6 },
{ provider: 'OpenAI', model: 'gpt-4o', input: 2.5, output: 10.0 },
{ provider: 'Anthropic', model: 'claude-haiku', input: 0.25, output: 1.25 },
{ provider: 'Anthropic', model: 'claude-sonnet', input: 3.0, output: 15.0 },
{ provider: 'Google', model: 'gemini-flash', input: 0.075, output: 0.3 },
{ provider: 'Google', model: 'gemini-pro', input: 1.25, output: 5.0 },
{ provider: 'Groq', model: 'llama-3.1-70b', input: 0.59, output: 0.79 },
];
function estimateCosts(profile: UsageProfile): ProviderCost[] {
const monthlyRequests = profile.dailyRequests * 30;
const monthlyInputTokens = monthlyRequests * profile.avgInputTokens;
const monthlyOutputTokens = monthlyRequests * profile.avgOutputTokens;
const costs: ProviderCost[] = MODELS.map((model) => {
const inputCost = (monthlyInputTokens / 1_000_000) * model.input;
const outputCost = (monthlyOutputTokens / 1_000_000) * model.output;
return {
provider: model.provider,
model: model.model,
monthlyInputCost: inputCost,
monthlyOutputCost: outputCost,
totalMonthlyCost: inputCost + outputCost,
};
});
// Sort by cost
costs.sort((a, b) => a.totalMonthlyCost - b.totalMonthlyCost);
// Display results
console.log(`\nCost Estimate: ${profile.description}`);
console.log(`Daily requests: ${profile.dailyRequests.toLocaleString()}`);
console.log(`Avg tokens: ${profile.avgInputTokens} in, ${profile.avgOutputTokens} out`);
console.log('\nMonthly Costs (sorted by price):');
console.log('='.repeat(60));
for (const cost of costs) {
console.log(`${cost.provider} ${cost.model}: $${cost.totalMonthlyCost.toFixed(2)}/month`);
}
return costs;
}
// Test with different profiles
estimateCosts({
description: 'Customer Support Bot',
dailyRequests: 5000,
avgInputTokens: 200,
avgOutputTokens: 300,
});
estimateCosts({
description: 'Code Analysis Tool',
dailyRequests: 500,
avgInputTokens: 2000,
avgOutputTokens: 1000,
});
Exercise 2: Provider Selector
Create a recommendation system that suggests providers based on requirements:
// Your implementation here
interface Requirements {
taskType: 'chat' | 'code' | 'analysis' | 'creative';
budget: 'low' | 'medium' | 'high';
privacyLevel: 'standard' | 'high';
volumeLevel: 'low' | 'medium' | 'high';
}
function recommendProvider(requirements: Requirements): string[] {
// TODO: Return ranked list of recommended providers
}
Solution
interface Requirements {
taskType: 'chat' | 'code' | 'analysis' | 'creative';
budget: 'low' | 'medium' | 'high';
privacyLevel: 'standard' | 'high';
volumeLevel: 'low' | 'medium' | 'high';
}
interface ProviderRecommendation {
provider: string;
model: string;
score: number;
reasons: string[];
}
function recommendProvider(requirements: Requirements): ProviderRecommendation[] {
const recommendations: ProviderRecommendation[] = [];
// OpenAI GPT-4o
const gpt4o: ProviderRecommendation = {
provider: 'OpenAI',
model: 'gpt-4o',
score: 0,
reasons: [],
};
if (requirements.taskType === 'code') {
gpt4o.score += 30;
gpt4o.reasons.push('Excellent code generation');
}
if (requirements.budget !== 'low') {
gpt4o.score += 20;
gpt4o.reasons.push('Best-in-class quality');
}
recommendations.push(gpt4o);
// OpenAI GPT-4o-mini
const gpt4oMini: ProviderRecommendation = {
provider: 'OpenAI',
model: 'gpt-4o-mini',
score: 0,
reasons: [],
};
if (requirements.budget === 'low' || requirements.volumeLevel === 'high') {
gpt4oMini.score += 30;
gpt4oMini.reasons.push('Cost-effective for high volume');
}
if (requirements.taskType === 'chat') {
gpt4oMini.score += 20;
gpt4oMini.reasons.push('Good for conversational tasks');
}
recommendations.push(gpt4oMini);
// Claude
const claude: ProviderRecommendation = {
provider: 'Anthropic',
model: 'claude-sonnet-4-20250514',
score: 0,
reasons: [],
};
if (requirements.taskType === 'analysis') {
claude.score += 30;
claude.reasons.push('Excellent at document analysis');
}
if (requirements.taskType === 'creative') {
claude.score += 25;
claude.reasons.push('Natural, nuanced writing');
}
recommendations.push(claude);
// Gemini
const gemini: ProviderRecommendation = {
provider: 'Google',
model: 'gemini-1.5-flash',
score: 0,
reasons: [],
};
if (requirements.budget === 'low') {
gemini.score += 35;
gemini.reasons.push('Lowest cost option');
}
if (requirements.volumeLevel === 'high') {
gemini.score += 25;
gemini.reasons.push('Great for high-volume applications');
}
recommendations.push(gemini);
// Open Source
const openSource: ProviderRecommendation = {
provider: 'Self-Hosted',
model: 'llama3.1 (Ollama)',
score: 0,
reasons: [],
};
if (requirements.privacyLevel === 'high') {
openSource.score += 50;
openSource.reasons.push('Full data privacy - data never leaves your infrastructure');
}
if (requirements.budget === 'low' && requirements.volumeLevel === 'high') {
openSource.score += 30;
openSource.reasons.push('No per-token costs');
}
recommendations.push(openSource);
// Sort by score
recommendations.sort((a, b) => b.score - a.score);
// Display
console.log('\nProvider Recommendations:');
console.log('='.repeat(50));
for (const rec of recommendations.filter((r) => r.score > 0)) {
console.log(`\n${rec.provider} (${rec.model}) - Score: ${rec.score}`);
for (const reason of rec.reasons) {
console.log(` + ${reason}`);
}
}
return recommendations;
}
// Test
recommendProvider({
taskType: 'chat',
budget: 'low',
privacyLevel: 'standard',
volumeLevel: 'high',
});
recommendProvider({
taskType: 'analysis',
budget: 'medium',
privacyLevel: 'high',
volumeLevel: 'low',
});
Exercise 3: Multi-Provider Client
Build a unified client that can use any provider:
// Your implementation here
class UnifiedAIClient {
// TODO: Implement a client that:
// - Works with OpenAI, Anthropic, and Gemini
// - Has a consistent interface
// - Tracks usage and costs
}
Solution
import Anthropic from '@anthropic-ai/sdk';
import { GoogleGenerativeAI } from '@google/generative-ai';
import OpenAI from 'openai';
interface Message {
role: 'system' | 'user' | 'assistant';
content: string;
}
interface AIResponse {
content: string;
provider: string;
model: string;
usage: {
inputTokens: number;
outputTokens: number;
estimatedCost: number;
};
}
type ProviderName = 'openai' | 'anthropic' | 'google';
class UnifiedAIClient {
private openai?: OpenAI;
private anthropic?: Anthropic;
private google?: GoogleGenerativeAI;
private totalCost: number = 0;
private requestCount: number = 0;
constructor(config: { openaiKey?: string; anthropicKey?: string; googleKey?: string }) {
if (config.openaiKey) {
this.openai = new OpenAI({ apiKey: config.openaiKey });
}
if (config.anthropicKey) {
this.anthropic = new Anthropic({ apiKey: config.anthropicKey });
}
if (config.googleKey) {
this.google = new GoogleGenerativeAI(config.googleKey);
}
}
async chat(provider: ProviderName, model: string, messages: Message[]): Promise<AIResponse> {
this.requestCount++;
switch (provider) {
case 'openai':
return this.chatOpenAI(model, messages);
case 'anthropic':
return this.chatAnthropic(model, messages);
case 'google':
return this.chatGoogle(model, messages);
default:
throw new Error(`Unknown provider: ${provider}`);
}
}
private async chatOpenAI(model: string, messages: Message[]): Promise<AIResponse> {
if (!this.openai) throw new Error('OpenAI not configured');
const response = await this.openai.chat.completions.create({
model,
messages,
});
const usage = response.usage!;
const cost = this.calculateCost('openai', model, usage.prompt_tokens, usage.completion_tokens);
this.totalCost += cost;
return {
content: response.choices[0].message.content || '',
provider: 'openai',
model,
usage: {
inputTokens: usage.prompt_tokens,
outputTokens: usage.completion_tokens,
estimatedCost: cost,
},
};
}
private async chatAnthropic(model: string, messages: Message[]): Promise<AIResponse> {
if (!this.anthropic) throw new Error('Anthropic not configured');
const systemMessage = messages.find((m) => m.role === 'system');
const otherMessages = messages.filter((m) => m.role !== 'system');
const response = await this.anthropic.messages.create({
model,
max_tokens: 2048,
system: systemMessage?.content,
messages: otherMessages.map((m) => ({
role: m.role as 'user' | 'assistant',
content: m.content,
})),
});
const cost = this.calculateCost(
'anthropic',
model,
response.usage.input_tokens,
response.usage.output_tokens
);
this.totalCost += cost;
const textBlock = response.content[0];
return {
content: textBlock.type === 'text' ? textBlock.text : '',
provider: 'anthropic',
model,
usage: {
inputTokens: response.usage.input_tokens,
outputTokens: response.usage.output_tokens,
estimatedCost: cost,
},
};
}
private async chatGoogle(model: string, messages: Message[]): Promise<AIResponse> {
if (!this.google) throw new Error('Google not configured');
const geminiModel = this.google.getGenerativeModel({ model });
const prompt = messages.map((m) => `${m.role}: ${m.content}`).join('\n');
const result = await geminiModel.generateContent(prompt);
const response = result.response;
const usage = response.usageMetadata;
const cost = this.calculateCost(
'google',
model,
usage?.promptTokenCount || 0,
usage?.candidatesTokenCount || 0
);
this.totalCost += cost;
return {
content: response.text(),
provider: 'google',
model,
usage: {
inputTokens: usage?.promptTokenCount || 0,
outputTokens: usage?.candidatesTokenCount || 0,
estimatedCost: cost,
},
};
}
private calculateCost(
provider: string,
model: string,
inputTokens: number,
outputTokens: number
): number {
const pricing: Record<string, { input: number; output: number }> = {
'gpt-4o-mini': { input: 0.15, output: 0.6 },
'gpt-4o': { input: 2.5, output: 10.0 },
'claude-sonnet-4-20250514': { input: 3.0, output: 15.0 },
'gemini-1.5-flash': { input: 0.075, output: 0.3 },
};
const price = pricing[model] || { input: 1, output: 1 };
return (inputTokens / 1_000_000) * price.input + (outputTokens / 1_000_000) * price.output;
}
getStats(): { totalCost: number; requestCount: number } {
return {
totalCost: this.totalCost,
requestCount: this.requestCount,
};
}
}
// Usage
const client = new UnifiedAIClient({
openaiKey: process.env.OPENAI_API_KEY,
anthropicKey: process.env.ANTHROPIC_API_KEY,
googleKey: process.env.GOOGLE_API_KEY,
});
const response1 = await client.chat('openai', 'gpt-4o-mini', [{ role: 'user', content: 'Hello!' }]);
const response2 = await client.chat('anthropic', 'claude-sonnet-4-20250514', [
{ role: 'user', content: 'Hello!' },
]);
console.log('Stats:', client.getStats());
Key Takeaways
- No One-Size-Fits-All: Each provider has strengths; choose based on your specific needs
- Cost Matters: Pricing varies 10-100x between models; optimize for your volume
- Build for Portability: Use abstractions to avoid vendor lock-in
- Multi-Provider Strategy: Combine providers for reliability and optimization
- Test and Measure: Quality varies by task; benchmark for your use case
- Plan for Change: The AI landscape evolves rapidly; stay flexible
- Privacy First: For sensitive data, consider self-hosted options
Resources
| Resource | Type | Description |
|---|---|---|
| OpenAI Pricing | Reference | Current OpenAI prices |
| Anthropic Pricing | Reference | Current Anthropic prices |
| Google AI Pricing | Reference | Current Google AI prices |
| Artificial Analysis | Tool | Provider comparison benchmarks |
| LLM Leaderboard | Tool | Model quality rankings |
Module Summary
In this module, you learned about the major AI providers:
- OpenAI: Market leader with GPT-4, excellent ecosystem
- Anthropic: Claude models with focus on safety and helpfulness
- Google: Gemini with massive context and multimodal capabilities
- Open Source: Llama, Mistral for privacy and control
You also learned how to:
- Evaluate providers based on your requirements
- Optimize costs across providers
- Build multi-provider strategies
- Create portable, provider-agnostic code
Next Module
You are now ready to put your knowledge into practice. In the next module, you will build real applications that integrate with AI APIs, handling streaming, errors, and production concerns.