From Zero to AI

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 Google 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

  1. 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;
    }
    
  2. 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;
    }
    
  3. 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.';
    
  4. 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

  1. No One-Size-Fits-All: Each provider has strengths; choose based on your specific needs
  2. Cost Matters: Pricing varies 10-100x between models; optimize for your volume
  3. Build for Portability: Use abstractions to avoid vendor lock-in
  4. Multi-Provider Strategy: Combine providers for reliability and optimization
  5. Test and Measure: Quality varies by task; benchmark for your use case
  6. Plan for Change: The AI landscape evolves rapidly; stay flexible
  7. 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:

  1. OpenAI: Market leader with GPT-4, excellent ecosystem
  2. Anthropic: Claude models with focus on safety and helpfulness
  3. Google: Gemini with massive context and multimodal capabilities
  4. 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.

Continue to Module 5: API Practice