Bonus: Top up now and we'll double your first deposit — get x2 credits instantly.
LLM Tips

How to Benchmark Multiple LLMs Without Rewriting Code

Aug 10, 2026

New LLMs show up with the confidence of a startup founder on launch day.

This one is faster.
That one is cheaper.
Another one claims better reasoning.
Another one suddenly has a context window large enough to swallow your entire documentation folder and still ask for dessert.

So naturally, the team asks:

Should we switch models?

Reasonable question. Slightly cursed execution.

Because if every model has a different SDK, response format, auth pattern, pricing unit, timeout behavior, JSON reliability level, and “special little way” of doing things, benchmarking quickly becomes a mess. You end up rewriting the same test script over and over just to compare models that should have been easy to swap.

That is exactly what we want to avoid.

In this guide, we’ll walk through how to benchmark multiple LLMs without rewriting your code every time a new model shows up acting shiny and important. We’ll build a reusable benchmark workflow, use LLMAPI as a model access layer, define stable test cases, track quality, latency, cost, and reliability, and make model comparisons useful for real product decisions.

The real goal of LLM benchmarking

The goal is not to find “the best model.”

That sounds nice, but it is too vague.

The better goal is:

Which model works best for this task, at this cost, with this latency, inside our app?

That framing is way more useful.

A model can be excellent for coding and mediocre for customer support tone. Another can be cheap and fast for classification but weak for long-context reasoning. Another can produce beautiful answers and then randomly ignore your JSON schema like it has personal boundaries.

So the benchmark should compare models by task.

Examples:

TaskWhat we care about
Support ticket summarizationAccuracy, helpfulness, concise output
Resume parsingSchema validity, field accuracy, hallucination rate
RAG answersGroundedness, citation quality, refusal when sources are missing
Content rewritingStyle fit, readability, preservation of meaning
ClassificationAccuracy, consistency, cost, speed
Code generationCorrectness, tests passed, explanation quality
Agent/tool routingCorrect tool choice, argument validity, safety
Long-document analysisCoverage, faithfulness, context handling

That is the first rule:

Benchmark the workflow, not the model hype.

Why we can write this guide

We’ve spent around 6 years working with AI APIs, LLM integrations, model routing, RAG workflows, structured outputs, evaluation pipelines, and developer tutorials. We also checked current research and documentation from Stanford HELM, LMSYS Chatbot Arena, OpenAI Evals, MLflow, and LLMAPI while preparing this guide.

The research side has been very clear about one thing: LLM evaluation needs more than one score. Stanford’s Holistic Evaluation of Language Models project evaluates models across scenarios and metrics rather than pretending accuracy alone explains everything. The HELM paper also emphasizes transparency through standardized prompts, completions, scenarios, and metrics. Chatbot Arena, described in the ICML paper Chatbot Arena: An Open Platform for Evaluating LLMs by Human Preference, evaluates models through pairwise human preference battles, which is useful because many real LLM tasks are open-ended and hard to score with exact-match metrics.

Translation for product teams: do not trust one leaderboard, one demo prompt, or one viral screenshot. Build a small benchmark that matches your app.

The no-rewrite benchmark architecture

The clean way to benchmark multiple LLMs is to separate five things:

LayerJob
Test setThe prompts or tasks you want to evaluate
Model adapterHow your code calls each model
Benchmark runnerRuns every test against every model
EvaluatorScores or reviews the outputs
ReportCompares quality, cost, latency, and reliability

With LLMAPI, the model adapter layer becomes much cleaner because you can call multiple models through a more unified, OpenAI-compatible interface. The LLMAPI quick-start docs show chat completion examples using the /v1/chat/completions pattern, which makes it easier to test different models without rewriting the entire client layer each time.

The architecture looks like this in practice:

test cases
→ same benchmark runner
→ LLMAPI model calls
→ normalized outputs
→ scoring/evaluation
→ comparison report

The whole point is that adding a new model should mean changing a config list, not rewriting the app.

Step 1: Define the benchmark question

Before writing code, define what you are trying to learn.

Weak benchmark question:

Which LLM is best?

Better benchmark questions:

Which model gives the best support ticket summaries under 2 seconds?
Which model produces the most valid JSON for resume parsing at the lowest cost?
Which model answers policy questions with the fewest unsupported claims?
Which model is good enough for free-plan users, and which model should power enterprise workflows?

A benchmark question should include:

  1. Task.
  2. Quality expectation.
  3. Latency expectation.
  4. Cost sensitivity.
  5. Failure tolerance.
  6. Output format.
  7. Risk level.

This keeps the benchmark from becoming a random model beauty contest.

Step 2: Build a realistic test set

A benchmark is only as useful as its test cases.

Do not use only cute examples.

If your app handles messy support tickets, test messy support tickets. If your app parses resumes, test real resume-like text with weird formatting. If your app answers from documentation, include questions where the answer is missing so you can see whether the model guesses.

A good test set includes:

Test typeWhy it matters
Easy casesChecks baseline behavior
Normal casesRepresents everyday use
Messy casesTests real-world input
Edge casesReveals failure modes
Long inputsTests context handling
Ambiguous promptsTests uncertainty handling
Missing-info casesTests refusal behavior
Adversarial casesTests safety and instruction following
Format-heavy casesTests schema reliability
Domain-specific casesTests actual product fit

Example test case format:

{
  "id": "support_001",
  "task": "support_summary",
  "input": "Customer says they were charged twice for Pro and support has not replied in 3 days.",
  "expected_traits": {
    "must_include": ["charged twice", "Pro", "support has not replied"],
    "must_not_include": ["refund already issued"],
    "format": "json"
  },
  "risk_level": "medium"
}

Do not make the expected output too rigid for open-ended tasks. Use traits, rubrics, and constraints.

Step 3: Create a model config file

Instead of hardcoding models in the benchmark script, keep them in config.

Example models.json:

[
  {
    "name": "fast_model",
    "model": "gpt-4o-mini",
    "provider": "llmapi",
    "role": "fast",
    "enabled": true
  },
  {
    "name": "balanced_model",
    "model": "gpt-4o",
    "provider": "llmapi",
    "role": "balanced",
    "enabled": true
  },
  {
    "name": "reasoning_model",
    "model": "reasoning-model-name",
    "provider": "llmapi",
    "role": "reasoning",
    "enabled": false
  }
]

Now adding a model is a config change.

That is the dream.

Well, a small developer dream. Still valid.

Step 4: Create the shared LLMAPI client

Install packages:

pip install openai python-dotenv pydantic pandas

Create .env:

LLMAPI_API_KEY=your_api_key_here
LLMAPI_BASE_URL=https://api.llmapi.ai/v1

Create llm_client.py:

import os
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()

client = OpenAI(
    api_key=os.environ["LLMAPI_API_KEY"],
    base_url=os.environ.get("LLMAPI_BASE_URL", "https://api.llmapi.ai/v1")
)

Every model call will go through this client.

That is how we avoid rewriting provider-specific code for every benchmark.

Step 5: Create one benchmark call function

Create model_runner.py:

import time
from llm_client import client


def run_model_on_prompt(model_name: str, system_prompt: str, user_prompt: str) -> dict:
    started_at = time.perf_counter()

    response = client.chat.completions.create(
        model=model_name,
        messages=[
            {
                "role": "system",
                "content": system_prompt
            },
            {
                "role": "user",
                "content": user_prompt
            }
        ],
        temperature=0
    )

    latency_ms = round((time.perf_counter() - started_at) * 1000, 2)

    content = response.choices[0].message.content

    usage = getattr(response, "usage", None)

    return {
        "output": content,
        "latency_ms": latency_ms,
        "usage": usage.model_dump() if usage else None
    }

Now every benchmark test uses the same call function.

If you add a model, you do not rewrite this.

If you change provider routing through LLMAPI, your benchmark runner still stays clean.

Step 6: Store test cases as data

Create test_cases.json:

[
  {
    "id": "support_001",
    "task": "support_summary",
    "input": "The customer says they were charged twice for the Pro plan. They contacted support three times and have not received a reply.",
    "expected_traits": {
      "must_include": ["charged twice", "Pro plan", "not received a reply"],
      "must_not_include": ["refund was issued"],
      "format": "json"
    }
  },
  {
    "id": "support_002",
    "task": "support_summary",
    "input": "User says the dashboard is faster after the update, but exports still fail for large CSV files.",
    "expected_traits": {
      "must_include": ["dashboard is faster", "exports fail", "large CSV files"],
      "must_not_include": ["billing issue"],
      "format": "json"
    }
  }
]

For real benchmarks, use more than two cases.

Start with 30 to 50 examples if you are early. Grow toward 100 to 300 examples for important workflows. For high-risk production features, you may need much larger and more carefully labeled sets.

Step 7: Write task-specific prompts

Create prompts.py:

PROMPTS = {
    "support_summary": {
        "system": """
You summarize customer support messages.

Return only valid JSON:
{
  "summary": "string",
  "issue_type": "billing | bug | account | feature_request | other",
  "urgency": "low | medium | high",
  "missing_information": ["string"]
}

Rules:
- Do not invent facts.
- Use missing_information for details that are not provided.
- Keep the summary to one sentence.
"""
    }
}

This matters because the same prompt should be used across every model in the benchmark.

If Model A gets a better prompt than Model B, the benchmark is already biased.

Step 8: Build the benchmark runner

Create benchmark.py:

import json
from pathlib import Path
from model_runner import run_model_on_prompt
from prompts import PROMPTS


def load_json(path: str):
    return json.loads(Path(path).read_text(encoding="utf-8"))


def run_benchmark():
    models = [
        model for model in load_json("models.json")
        if model.get("enabled", True)
    ]

    test_cases = load_json("test_cases.json")
    results = []

    for model in models:
        for case in test_cases:
            task = case["task"]
            system_prompt = PROMPTS[task]["system"]

            try:
                result = run_model_on_prompt(
                    model_name=model["model"],
                    system_prompt=system_prompt,
                    user_prompt=case["input"]
                )

                results.append({
                    "test_id": case["id"],
                    "task": task,
                    "model_alias": model["name"],
                    "model": model["model"],
                    "status": "success",
                    "output": result["output"],
                    "latency_ms": result["latency_ms"],
                    "usage": result["usage"]
                })

            except Exception as error:
                results.append({
                    "test_id": case["id"],
                    "task": task,
                    "model_alias": model["name"],
                    "model": model["model"],
                    "status": "error",
                    "error": str(error),
                    "output": None,
                    "latency_ms": None,
                    "usage": None
                })

    Path("benchmark_results.json").write_text(
        json.dumps(results, indent=2),
        encoding="utf-8"
    )

    print(f"Saved {len(results)} benchmark results to benchmark_results.json")


if __name__ == "__main__":
    run_benchmark()

Run it:

python benchmark.py

Now you can benchmark multiple models with one runner.

Adding a model means editing models.json, not rewriting the benchmark.

Step 9: Add basic automatic scoring

For structured outputs, we can score simple things automatically.

Create scoring.py:

import json


def score_output(output: str, expected_traits: dict) -> dict:
    score = 0
    checks = []

    try:
        parsed = json.loads(output)
        checks.append({
            "name": "valid_json",
            "passed": True
        })
        score += 1
    except Exception:
        parsed = None
        checks.append({
            "name": "valid_json",
            "passed": False
        })

    lower_output = output.lower() if output else ""

    for phrase in expected_traits.get("must_include", []):
        passed = phrase.lower() in lower_output
        checks.append({
            "name": f"must_include:{phrase}",
            "passed": passed
        })
        if passed:
            score += 1

    for phrase in expected_traits.get("must_not_include", []):
        passed = phrase.lower() not in lower_output
        checks.append({
            "name": f"must_not_include:{phrase}",
            "passed": passed
        })
        if passed:
            score += 1

    total = len(checks)

    return {
        "score": score,
        "total": total,
        "score_percent": round((score / total) * 100, 2) if total else 0,
        "checks": checks,
        "parsed_json": parsed
    }

This is simple, but it catches important issues:

  1. Invalid JSON.
  2. Missing required facts.
  3. Invented forbidden facts.

For open-ended outputs, automatic checks are only part of the story. We still need human or judge-model evaluation.

Step 10: Add a judge model for open-ended tasks

Some tasks cannot be scored with exact matching.

Examples:

  1. Writing quality.
  2. Helpfulness.
  3. Tone.
  4. Reasoning quality.
  5. Faithfulness.
  6. User preference.
  7. Completeness.

This is where LLM-as-a-judge can help, carefully.

The paper Judging LLM-as-a-Judge with MT-Bench and Chatbot Arena explored using strong LLMs as judges and compared agreement with human preferences. It introduced MT-Bench and Chatbot Arena-style evaluations for open-ended responses. The key lesson for us: judge models can be useful, but they should be treated as evaluation tools with limitations, not holy truth machines.

Create judge.py:

import json
from llm_client import client


def judge_output(input_text: str, output_text: str, rubric: str) -> dict:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """
You are evaluating an LLM output.

Return only valid JSON:
{
  "score": 1,
  "reasoning": "short explanation",
  "major_issues": ["string"]
}

Score from 1 to 5:
1 = poor
2 = weak
3 = acceptable
4 = good
5 = excellent

Be strict. Penalize unsupported claims, missing key facts, and format failures.
"""
            },
            {
                "role": "user",
                "content": json.dumps({
                    "input": input_text,
                    "output": output_text,
                    "rubric": rubric
                })
            }
        ],
        temperature=0
    )

    return json.loads(response.choices[0].message.content)

Example rubric:

The summary should accurately capture the customer issue, avoid invented facts, be concise, and include urgency if clear.

Use judge models for:

  1. First-pass evaluation.
  2. Regression checks.
  3. Ranking outputs.
  4. Flagging bad generations.
  5. Reducing manual review volume.

Keep humans involved for final evaluation of important product workflows.

Step 11: Track latency and cost

A model that gives slightly better answers but takes 12 seconds may be bad for chat.

A model that is cheap but fails JSON 30% of the time may be expensive after retries.

Benchmark reports should include:

MetricWhy it matters
Quality scoreDid the model answer well?
JSON validityCan your app use the output?
LatencyIs the user waiting too long?
Input tokensPrompt/context cost
Output tokensGeneration cost
Estimated costMargin planning
Error rateReliability
Retry rateHidden cost
Fallback rateRoute health
Human review rateOperational cost

Create report.py:

import json
import pandas as pd
from pathlib import Path


def make_report():
    results = json.loads(
        Path("benchmark_results.json").read_text(encoding="utf-8")
    )

    rows = []

    for item in results:
        usage = item.get("usage") or {}

        rows.append({
            "test_id": item["test_id"],
            "task": item["task"],
            "model_alias": item["model_alias"],
            "model": item["model"],
            "status": item["status"],
            "latency_ms": item.get("latency_ms"),
            "prompt_tokens": usage.get("prompt_tokens"),
            "completion_tokens": usage.get("completion_tokens"),
            "total_tokens": usage.get("total_tokens")
        })

    df = pd.DataFrame(rows)

    summary = df.groupby("model_alias").agg(
        total_tests=("test_id", "count"),
        success_count=("status", lambda x: (x == "success").sum()),
        avg_latency_ms=("latency_ms", "mean"),
        avg_total_tokens=("total_tokens", "mean")
    ).reset_index()

    df.to_csv("benchmark_results.csv", index=False)
    summary.to_csv("benchmark_summary.csv", index=False)

    print(summary)


if __name__ == "__main__":
    make_report()

Run:

python report.py

Now you have a simple benchmark report.

Step 12: Compare models with a decision table

Once we have scores, latency, and cost, we can make a decision table.

Example:

ModelQualityJSON validityAvg latencyCostBest use
Fast model82%91%700 msLowFree-plan summaries
Balanced model91%98%1.5 secMediumDefault production route
Reasoning model95%97%4.8 secHighHigh-risk review
Long-context model89%94%5.5 secHighLarge documents

The winner depends on the workflow.

For example:

  • Use the fast model for simple classifications.
  • Use the balanced model for support summaries.
  • Use the reasoning model for legal-style risk checks.
  • Use the long-context model for large documents.
  • Use fallback when validation fails.

This is how benchmarking turns into routing.

Step 13: Add regression tests before switching models

Benchmarking is not only for choosing a new model.

It is also for preventing quality regressions.

Every time you change:

  1. Model.
  2. Prompt.
  3. Retrieval settings.
  4. Output schema.
  5. Temperature.
  6. Chunking strategy.
  7. Routing rule.

Run the benchmark again.

Save historical results.

Example folder structure:

benchmarks/
  test_cases/
    support_summary_v1.json
  results/
    2026-08-24_gpt-4o-mini.json
    2026-08-24_gpt-4o.json
  reports/
    support_summary_august.csv

Regression testing helps you avoid the classic disaster:

“We upgraded the model and everything got worse, but in a different font.”

Step 14: Add pairwise comparisons

Sometimes it is easier to compare two outputs directly.

Pairwise evaluation asks:

Which answer is better?

This is how Chatbot Arena-style comparisons work. The Chatbot Arena paper describes an open platform for evaluating LLMs through human preference, using pairwise comparisons to rank models.

Pairwise comparison works well for:

  1. Writing quality.
  2. Helpfulness.
  3. Tone.
  4. Reasoning.
  5. Summaries.
  6. RAG answers.
  7. Chatbot responses.

Example judge prompt idea:

Given the same user input, compare Answer A and Answer B.
Choose the better answer based on accuracy, completeness, concision, and usefulness.
Return JSON with winner: A, B, or tie.

Store pairwise results:

{
  "test_id": "support_001",
  "model_a": "fast_model",
  "model_b": "balanced_model",
  "winner": "model_b",
  "reason": "Model B included the missing support follow-up and avoided unsupported claims."
}

This is useful when numerical scoring feels too artificial.

Step 15: Add task-specific metrics

Different tasks need different metrics.

TaskUseful metrics
ClassificationAccuracy, precision, recall, F1
JSON extractionSchema validity, field accuracy, missing fields
SummarizationFaithfulness, coverage, concision, human rating
RAGCitation accuracy, groundedness, answer correctness
Code generationUnit tests passed, syntax validity, security issues
Tool useCorrect tool, valid arguments, safe execution
TranslationHuman review, BLEU/COMET-style metrics
SentimentLabel accuracy, confusion matrix
Resume parsingField accuracy, skill precision/recall
Agent workflowsTask completion, tool errors, cost, safety

The model with the highest “general quality” score may still be the wrong choice for your actual task.

A resume parser needs schema validity.

A support chatbot needs helpfulness and factuality.

A RAG assistant needs source-grounded answers.

A code assistant needs tests to pass.

So choose metrics that match the workflow.

Step 16: Benchmark prompts too

Model benchmarking and prompt benchmarking are connected.

If one prompt performs badly across all models, the model may not be the problem.

Test prompt variants:

Prompt variantWhat changes
v1Basic instructions
v2Adds JSON schema
v3Adds examples
v4Adds “do not invent facts”
v5Adds evidence requirement
v6Shorter prompt for lower latency

Benchmark matrix:

ModelPrompt v1Prompt v2Prompt v3
Fast model72%84%86%
Balanced model81%91%93%
Reasoning model86%94%94%

Sometimes a better prompt improves all models.

Sometimes a stronger model only helps after the prompt is clear.

Do not use benchmarking to compensate for sloppy prompts.

Step 17: Benchmark RAG separately

RAG benchmarks need extra care because failures can come from retrieval or generation.

If a RAG answer is bad, the model may not be at fault.

Maybe retrieval found the wrong chunk.

Track:

RAG metricWhy
Retrieval hit rateDid the correct source appear in top results?
Context precisionWere retrieved chunks relevant?
Citation accuracyDid the answer cite the right source?
Answer correctnessDid the final response answer correctly?
GroundednessWas the answer supported by context?
Refusal accuracyDid the model refuse when sources were missing?

Stanford HELM evaluates language models across scenarios and metrics, including robustness and efficiency, which is a good reminder that one-dimensional scores are weak for complex workflows. RAG evaluation also needs multiple layers: retrieval, generation, citation, and refusal behavior.

A practical RAG test case:

{
  "id": "policy_001",
  "question": "Can users export invoices on the Starter plan?",
  "expected_source_id": "billing_policy_v3",
  "expected_answer": "No, invoice export is available on Pro and Business plans.",
  "should_refuse": false
}

A missing-answer case:

{
  "id": "policy_009",
  "question": "Does the company support wire transfers in Brazil?",
  "expected_source_id": null,
  "expected_answer": null,
  "should_refuse": true
}

The refusal cases are important because they catch models that guess.

Step 18: Use MLflow, OpenAI Evals, or HELM when needed

You do not have to build everything yourself.

Useful tools:

ToolGood for
OpenAI EvalsCustom evals and model behavior checks
Stanford HELMHolistic benchmark framework and transparency ideas
MLflow LLM evaluationExperiment tracking and evaluation workflows
promptfooPrompt/model regression testing
RagasRAG evaluation
DeepEvalLLM app evaluation
LangSmithLangChain workflow tracing/evals
Human review sheetsPractical early-stage evaluation

OpenAI’s Evals repository provides a framework for evaluating LLMs and LLM systems. Stanford’s HELM framework is an open-source Python framework for holistic, reproducible, transparent evaluation of foundation models. MLflow’s LLM evaluation docs describe tools for evaluating models, prompts, and providers with built-in and custom metrics.

For early teams, a simple spreadsheet plus benchmark script may be enough.

For serious production workflows, use proper evaluation tooling.

Step 19: Watch for benchmark traps

LLM benchmarking has traps everywhere.

TrapWhy it hurts
Testing only easy promptsHides real failures
Using one metricMisses tradeoffs
Ignoring latencyUsers may hate the “best” model
Ignoring costFinance may hate the “best” model
No repeat runsMisses output variability
Overusing judge modelsReplaces one model bias with another
No human reviewMisses practical usefulness
Prompt differencesMakes model comparison unfair
No versioningResults become impossible to reproduce
Benchmark leakageModel may already know public benchmarks
No production dataBenchmark does not match real app
Comparing raw providers onlyIgnores workflow/routing effects

The biggest trap is believing a leaderboard answers your product question.

Leaderboards are useful context.

Your benchmark should reflect your users.

Step 20: Turn benchmark results into routing rules

The best benchmark output is not a trophy.

It is a routing decision.

Example:

FindingProduct decision
Fast model passes 95% of simple classification testsUse for low-risk classification
Fast model fails JSON extraction oftenDo not use for structured parsing
Balanced model is best cost/quality mixMake it default
Reasoning model improves legal review accuracyUse only for high-risk workflows
Long-context model is slow but handles big docsUse only when input exceeds chunk limit
Model A has low latency but weak citationsAvoid for RAG answers
Model B is expensive but reliableReserve for enterprise or fallback

A strong routing policy might be:

Simple classification → fast model
Structured extraction → balanced model
Failed validation → stronger fallback
Policy/RAG answers → balanced model with citations
High-risk review → reasoning model + human review
Large documents → long-context model or chunking route

This is how benchmarking pays off.

Where LLMAPI fits

LLMAPI helps because benchmarking multiple models becomes much easier when the model access layer is centralized.

Instead of rewriting code for every provider or model, you can keep your benchmark runner stable and change model names or routes through config. The LLMAPI quick-start docs show an OpenAI-compatible API shape, which means your benchmark scripts can use one familiar client pattern while testing different models.

Use LLMAPI for:

NeedHow it helps
Multi-model callsTest several models through one integration layer
Model swapsChange config instead of rewriting code
Routing testsCompare fast, balanced, reasoning, and fallback routes
Cost trackingCentralize model usage
Latency trackingCompare model speed in the same runner
Output normalizationKeep app-facing results consistent
Production migrationMove winning benchmark routes into real workflows
Fallback testingMeasure backup behavior before launch

A useful setup:

models.json
→ benchmark runner
→ LLMAPI model calls
→ evaluator
→ benchmark report
→ routing rules

That is the no-rewrite loop.

A practical benchmark checklist

Before you trust results, check this:

  • The benchmark question is specific.
  • Test cases match real product inputs.
  • Easy, normal, messy, and edge cases are included.
  • Every model gets the same prompt.
  • Prompt versions are tracked.
  • Model versions are tracked.
  • Output schemas are validated.
  • Latency is measured.
  • Token usage or cost is tracked.
  • Errors are counted.
  • Judge model scoring is used carefully.
  • Human review is included for important workflows.
  • RAG tests separate retrieval from generation.
  • Results are saved and comparable over time.
  • Benchmarks run before model swaps.
  • Final decisions become routing rules.

If you skip most of this, you are not benchmarking.

You are sampling vibes with extra steps.

The practical takeaway

You can benchmark multiple LLMs without rewriting code by separating the benchmark system from the model provider details.

Create a stable test set. Define task-specific prompts. Store models in config. Use one shared LLMAPI client. Run every model through the same benchmark runner. Normalize outputs. Score JSON validity, quality, latency, cost, and errors. Use judge models carefully for open-ended tasks. Add human review where the product actually matters. Save results over time so model swaps do not become guesswork.

A clean benchmark loop looks like this:

test cases
→ model config
→ LLMAPI calls
→ scoring
→ report
→ routing decision

That way, when a new model arrives looking shiny and important, you do not rewrite your whole codebase.

You add it to the config, run the benchmark, compare the results, and let the data humble everyone politely.

Deploy in minutes