The Model Landscape (2024–2025)

The number of capable LLMs has exploded. Choosing the right one is now a core engineering decision — it affects cost, latency, quality, and vendor risk. Here are the major families:

Major Model Families

  • GPT (OpenAI) — GPT-4o, GPT-4o-mini, o1. Best all-rounder, strong at code and reasoning. Closed source.
  • Claude (Anthropic) — Claude 3.5 Sonnet, Claude 3 Opus/Haiku. Excellent at long-context, nuanced writing, and following complex instructions. Closed source.
  • Llama (Meta) — Llama 3 8B/70B/405B. Top open-source model. Self-hostable. Great cost efficiency at scale.
  • Mistral — Mistral 7B, Mixtral 8x7B, Mistral Large. European, open-weight (small models), strong coding performance.
  • Gemini (Google) — Gemini 1.5 Pro/Flash. Massive context window (1M+), strong multimodal, tight Google Cloud integration.

Model Comparison

Model Context Input $/1M tok Output $/1M tok Strengths
GPT-4o 128K $2.50 $10.00 Best all-rounder, multimodal, fast
GPT-4o-mini 128K $0.15 $0.60 Incredible value, good for most tasks
Claude 3.5 Sonnet 200K $3.00 $15.00 Long-context, coding, instruction-following
Claude 3 Haiku 200K $0.25 $1.25 Fast + cheap, good for classification
Llama 3 70B 128K ~$0.60* ~$0.80* Open source, self-hostable, no vendor lock-in
Mistral Large 128K $2.00 $6.00 Strong coding, multilingual, EU-based
Gemini 1.5 Pro 1M+ $1.25 $5.00 Massive context, multimodal, Google ecosystem

* Llama pricing varies by hosting provider (Together, Fireworks, Groq, self-hosted).

Cost Per 1M Tokens (Input)

Input cost per 1M tokens — lower is better for high-volume applications
The 80/20 rule of model selection

For 80% of production tasks, GPT-4o-mini or Claude 3 Haiku is sufficient. Reserve frontier models (GPT-4o, Claude 3.5 Sonnet) for complex reasoning, nuanced generation, or when quality directly impacts revenue. The cost difference can be 10-20x.

Understanding Benchmarks

Benchmarks give directional signal but are not the final word. Models are increasingly optimized for benchmarks specifically, reducing their predictive value for real-world tasks.

Key Benchmarks

Benchmark Measures Useful For
MMLU Broad knowledge (57 subjects) General capability comparison
HumanEval Python code generation (pass@1) Coding assistant quality
GSM8K Grade-school math reasoning Multi-step logical reasoning
MT-Bench Multi-turn conversation quality Chatbot/assistant use cases
LMSYS Chatbot Arena Human preference (ELO rating) Real-world user satisfaction
Benchmarks lie (sometimes)

A model scoring 90% on HumanEval might still fail on your specific coding tasks. Benchmark contamination (training on test data) is a known issue. Always run your own evals on YOUR data before committing to a model.

Open Source vs Closed Source

Factor Open Source (Llama, Mistral) Closed Source (GPT-4, Claude)
Cost at scale Lower (self-host, no per-token fees) Higher (pay per token always)
Quality (2024) Good — 90% of frontier for most tasks Best — still leads on complex reasoning
Data privacy Full control — data never leaves your infra Trust the provider's policies
Customization Full fine-tuning, custom training Limited fine-tuning via API
Ops complexity High — GPUs, serving infra, monitoring Low — just API calls
Vendor risk None — you own the weights API deprecation, price changes, policy shifts

When to Go Open Source

  • You process >1M tokens/day and cost matters
  • Data must stay on-premise (healthcare, finance, gov)
  • You need custom fine-tuning for a domain-specific task
  • You can't accept vendor lock-in risk

Otherwise, start with closed-source APIs. The simplicity and quality are hard to beat for early-stage products.

Decision Framework: How to Choose a Model

🌳 Decision tree

Follow this flowchart to narrow your choice:

  1. What's your budget? If ultra-low → GPT-4o-mini or Claude Haiku. If cost isn't primary → continue.
  2. Do you need the absolute best quality? Yes → GPT-4o or Claude 3.5 Sonnet. No → continue.
  3. Is data privacy critical? Yes → Llama 3 (self-hosted) or Azure OpenAI (enterprise agreement). No → continue.
  4. Do you need massive context (>200K)? Yes → Gemini 1.5 Pro. No → continue.
  5. Is latency critical (<500ms)? Yes → GPT-4o-mini, Groq (Llama), or Claude Haiku. No → use the best within budget.
  6. Default answer: Start with GPT-4o-mini. Upgrade to GPT-4o or Claude 3.5 Sonnet only where quality gaps appear in your evals.

Latency Tradeoffs

Model size directly correlates with latency. Here are typical time-to-first-token (TTFT) values:

Typical time-to-first-token (ms) — lower is better for interactive applications

For interactive applications (chat, copilots), target <1 second TTFT. For batch processing (classification, extraction), latency matters less — optimize for cost instead.

Mini-Project: Model Comparison Benchmark

🛠️ Build a Model Benchmark Script

Compare models on your own test cases — measuring quality, latency, and cost. This is how you make data-driven model decisions.

model_benchmark.py
"""
Model Comparison Benchmark
Compare multiple models on the same prompts, measuring quality, latency, and cost.
"""
import os
import time
import json
from dataclasses import dataclass, asdict
from openai import OpenAI
import anthropic


@dataclass
class BenchmarkResult:
    model: str
    prompt_name: str
    response: str
    latency_ms: float
    input_tokens: int
    output_tokens: int
    estimated_cost: float


# ─── Configuration ───────────────────────────────────────────

OPENAI_MODELS = ["gpt-4o", "gpt-4o-mini"]
ANTHROPIC_MODELS = ["claude-sonnet-4-20250514", "claude-3-haiku-20240307"]

# Pricing per 1M tokens
PRICING = {
    "gpt-4o": {"input": 2.50, "output": 10.00},
    "gpt-4o-mini": {"input": 0.15, "output": 0.60},
    "claude-sonnet-4-20250514": {"input": 3.00, "output": 15.00},
    "claude-3-haiku-20240307": {"input": 0.25, "output": 1.25},
}

# Test prompts — adjust these to match YOUR use case
TEST_PROMPTS = {
    "code_generation": {
        "system": "You are a Python expert. Write clean, well-commented code.",
        "user": "Write a function that finds the longest palindromic substring in a string. Include type hints and a docstring.",
    },
    "summarization": {
        "system": "You summarize text concisely.",
        "user": "Summarize in 2-3 sentences: Machine learning operations (MLOps) is a set of practices that aims to deploy and maintain machine learning models in production reliably and efficiently. The word is a compound of machine learning and the continuous development practice of DevOps. MLOps involves automating and monitoring all steps of ML system construction, including integration, testing, releasing, deployment, and infrastructure management.",
    },
    "reasoning": {
        "system": "You solve logic puzzles step by step.",
        "user": "If all roses are flowers, and some flowers fade quickly, can we conclude that some roses fade quickly? Explain your reasoning.",
    },
    "classification": {
        "system": "Classify the sentiment as positive, negative, or neutral. Reply with ONE word only.",
        "user": "The product works fine but the customer service was absolutely terrible and I waited 3 hours on hold.",
    },
}


# ─── Benchmark Functions ─────────────────────────────────────

def benchmark_openai(model: str, system: str, user: str) -> BenchmarkResult:
    """Benchmark a single OpenAI model call."""
    client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

    start = time.perf_counter()
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system},
            {"role": "user", "content": user},
        ],
        temperature=0,
        max_tokens=500,
    )
    latency = (time.perf_counter() - start) * 1000

    usage = response.usage
    cost = (
        (usage.prompt_tokens / 1_000_000) * PRICING[model]["input"]
        + (usage.completion_tokens / 1_000_000) * PRICING[model]["output"]
    )

    return BenchmarkResult(
        model=model,
        prompt_name="",
        response=response.choices[0].message.content,
        latency_ms=round(latency, 1),
        input_tokens=usage.prompt_tokens,
        output_tokens=usage.completion_tokens,
        estimated_cost=round(cost, 6),
    )


def benchmark_anthropic(model: str, system: str, user: str) -> BenchmarkResult:
    """Benchmark a single Anthropic model call."""
    client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

    start = time.perf_counter()
    response = client.messages.create(
        model=model,
        max_tokens=500,
        system=system,
        messages=[{"role": "user", "content": user}],
        temperature=0,
    )
    latency = (time.perf_counter() - start) * 1000

    usage = response.usage
    cost = (
        (usage.input_tokens / 1_000_000) * PRICING[model]["input"]
        + (usage.output_tokens / 1_000_000) * PRICING[model]["output"]
    )

    return BenchmarkResult(
        model=model,
        prompt_name="",
        response=response.content[0].text,
        latency_ms=round(latency, 1),
        input_tokens=usage.input_tokens,
        output_tokens=usage.output_tokens,
        estimated_cost=round(cost, 6),
    )


def run_benchmark() -> list[BenchmarkResult]:
    """Run all benchmarks and return results."""
    results = []

    for prompt_name, prompt in TEST_PROMPTS.items():
        print(f"\n{'='*60}")
        print(f"  Testing: {prompt_name}")
        print(f"{'='*60}")

        for model in OPENAI_MODELS:
            print(f"  Running {model}...", end=" ", flush=True)
            result = benchmark_openai(model, prompt["system"], prompt["user"])
            result.prompt_name = prompt_name
            results.append(result)
            print(f"{result.latency_ms}ms, ${result.estimated_cost:.6f}")

        for model in ANTHROPIC_MODELS:
            print(f"  Running {model}...", end=" ", flush=True)
            result = benchmark_anthropic(model, prompt["system"], prompt["user"])
            result.prompt_name = prompt_name
            results.append(result)
            print(f"{result.latency_ms}ms, ${result.estimated_cost:.6f}")

    return results


def print_summary(results: list[BenchmarkResult]):
    """Print a summary table of results."""
    print(f"\n\n{'='*80}")
    print("  BENCHMARK SUMMARY")
    print(f"{'='*80}\n")

    # Group by prompt
    prompts = list(TEST_PROMPTS.keys())
    models = OPENAI_MODELS + ANTHROPIC_MODELS

    # Latency summary
    print(f"{'Model':<30} {'Avg Latency':<15} {'Avg Cost':<15} {'Avg Tokens Out'}")
    print("-" * 75)

    for model in models:
        model_results = [r for r in results if r.model == model]
        if not model_results:
            continue
        avg_latency = sum(r.latency_ms for r in model_results) / len(model_results)
        avg_cost = sum(r.estimated_cost for r in model_results) / len(model_results)
        avg_tokens = sum(r.output_tokens for r in model_results) / len(model_results)
        print(f"{model:<30} {avg_latency:>8.0f}ms     ${avg_cost:<12.6f} {avg_tokens:.0f}")

    # Save full results
    output_file = "benchmark_results.json"
    with open(output_file, "w") as f:
        json.dump([asdict(r) for r in results], f, indent=2)
    print(f"\nFull results saved to {output_file}")


if __name__ == "__main__":
    results = run_benchmark()
    print_summary(results)

Running the Benchmark

terminal
# Install dependencies
pip install openai anthropic

# Set API keys
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."

# Run benchmark
python model_benchmark.py

Exercises

  1. Add your own test prompts that match your actual use case
  2. Add a quality scoring mechanism (e.g., check if code compiles, if classification is correct)
  3. Run the benchmark 3 times and compute standard deviation for latency
  4. Add Gemini or Mistral models via their respective SDKs
  5. Create a cost projection: "If I make 10K calls/day with this prompt, what's my monthly bill per model?"

Practical Recommendations

The Multi-Model Strategy

Production systems often use multiple models:

  • Routing — Simple classifier (GPT-4o-mini) decides which model handles each request
  • Tiered quality — Use cheap models for easy tasks, frontier models for hard ones
  • Fallback chains — Primary model → fallback model → cached response
  • A/B testing — Compare models in production with real user feedback
Start cheap, upgrade where needed

Deploy with GPT-4o-mini everywhere first. Monitor quality. Upgrade specific prompts/tasks to frontier models only where you see quality gaps. This approach often cuts costs by 80% compared to using GPT-4o for everything.

Key Takeaways

  • Model choice is an engineering decision — evaluate on YOUR data, not just benchmarks
  • Cost varies 10-100x between models; match model capability to task difficulty
  • Open source gives control and cost savings; closed source gives simplicity and quality
  • Benchmarks are directional signals, not guarantees — always run your own evals
  • Use a multi-model strategy: route easy tasks to cheap models, hard tasks to frontier models
  • Default starting point: GPT-4o-mini for most tasks, upgrade based on eval results