Skip to main content

Measure cost, quality, and performance of LLM API interactions

Project description

llm-bench

Stop guessing which AI model to use. Start measuring.

Most teams pick AI providers based on marketing claims or gut feeling. llm-bench gives you evidence — real numbers on cost, quality, and speed — so you can make decisions grounded in data.

from llm_bench import Suite, Scenario
from llm_bench.quality import contains, max_length
from llm_bench.providers import OpenAIProvider, AnthropicProvider

suite = Suite(
    scenarios=[
        Scenario(
            prompt="Summarize this support ticket in one sentence.",
            providers=["openai/gpt-4o-mini", "anthropic/claude-haiku-4-5"],
            assertions=[contains("customer"), max_length(200)],
        )
    ],
    providers={
        "openai": OpenAIProvider(api_key="..."),
        "anthropic": AnthropicProvider(api_key="..."),
    },
)

suite.run().save("results")
openai/gpt-4o-mini      cost=$0.000089  quality=2/2  latency=743ms
anthropic/claude-haiku  cost=$0.000104  quality=2/2  latency=921ms

Why this exists

AI APIs cost real money. Response quality varies across models, prompts, and providers. Latency affects user experience. Yet most teams have no systematic way to compare any of this — they rely on vibes.

llm-bench treats AI evaluation as an engineering problem: define what "good" means, run the benchmark, read the numbers.


What it measures

Cost

Token-level cost tracking with a bundled pricing table (GPT-4o, GPT-4o-mini, Claude Haiku, Sonnet, Opus, and more). Every run reports input_usd, output_usd, and total_usd. No surprises on your bill.

Quality

Two complementary layers:

Deterministic assertions — fast, free, exact. Did the response contain the right word? Match a pattern? Stay within a length limit? Return valid JSON? These run on every call with zero extra cost.

from llm_bench.assertions import contains, not_contains, matches, max_length, valid_json, json_schema

assertions=[
    contains("Paris"),
    not_contains("I don't know"),
    matches(r"\d{4}-\d{2}-\d{2}"),   # date format
    max_length(500),
    valid_json(),
]

LLM-as-judge (opt-in) — a second model scores the response against a rubric you define, on a scale you choose. Judge cost is tracked separately and never blended into deterministic scores.

from llm_bench import Judge

judge=Judge(
    provider="openai",
    model="gpt-4o-mini",
    rubric="Is this explanation clear and accurate for a non-technical audience?",
    scale=10,
)

Performance

  • Latency (total_ms) — full round-trip time
  • Throughput (tokens_per_sec) — output tokens per second
  • Reliability — retry count and final error message, with automatic exponential backoff on rate limits and transient errors (up to 3 retries: 1s, 2s, 4s)

Output

Results export to JSON (full detail) and CSV (flat summary), both from one call:

suite.run().save("results")          # writes results.json + results.csv
suite.run().save("results.json")     # JSON only
suite.run().save("results.csv")      # CSV only

JSON includes assertion reasons, raw provider payloads, and per-call metadata. CSV is ready for Excel, Pandas, or CI artifact dashboards.


Installation

# OpenAI only
pip install llm-bench[openai]

# Anthropic only
pip install llm-bench[anthropic]

# Both
pip install llm-bench[all]

Python 3.11+. MIT license.


Extending

New providers implement one method:

from llm_bench.providers.base import Provider
from llm_bench.scenario import CallConfig, LLMResponse

class MyProvider(Provider):
    def call(self, prompt: str, config: CallConfig) -> LLMResponse:
        response = my_sdk.complete(prompt, model=config.model)
        return LLMResponse(
            text=response.text,
            input_tokens=response.usage.input,
            output_tokens=response.usage.output,
            model=config.model,
            raw=response.raw_dict,
        )

New assertions implement one callable:

def my_assertion(text: str) -> AssertionResult:
    passed = "expected phrase" in text
    return AssertionResult(passed=passed, reason="Found" if passed else "Missing")

No registration, no subclassing beyond these — just pass instances in.


Architecture

Seven focused modules, no circular imports, each with one responsibility:

llm_bench/
├── scenario.py      # All core dataclasses (Scenario, Result, Cost, Performance, ...)
├── suite.py         # Orchestration — runs the scenario × provider matrix
├── cost.py          # Token counting + USD calculation from pricing table
├── quality.py       # Deterministic assertions + QualityChecker
├── performance.py   # Latency and throughput tracking
├── report.py        # Aggregation + JSON/CSV serialization
├── assertions.py    # Re-export of quality assertion factories
└── providers/
    ├── base.py      # Abstract Provider interface
    ├── openai.py    # OpenAI adapter
    └── anthropic.py # Anthropic adapter

Key design decision: QualityChecker never makes API calls. All provider calls — including judge calls — are centralized in Suite. This keeps dependencies explicit and modules independently testable.


Testing

51 unit tests, zero API calls required:

pip install llm-bench[dev]
pytest tests/unit/ -v

Integration tests hit real APIs and are opt-in:

RUN_INTEGRATION=1 OPENAI_API_KEY=... ANTHROPIC_API_KEY=... pytest tests/integration/

CI runs unit tests on every pull request (Python 3.11 and 3.12). Integration tests run on a weekly schedule.


Examples

# Side-by-side provider comparison
OPENAI_API_KEY=... ANTHROPIC_API_KEY=... python examples/basic_comparison.py

# With LLM-as-judge scoring
OPENAI_API_KEY=... python examples/with_judge.py

Roadmap

  • More providers (Gemini, Cohere, Mistral, local models via Ollama)
  • Parallel execution (v2)
  • Streaming support + TTFT metric
  • CLI interface

Contributions welcome. Open an issue before starting large changes.


MIT License · Built with evidence, not vibes.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ai_prompt_benchmark-0.1.0.tar.gz (9.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ai_prompt_benchmark-0.1.0-py3-none-any.whl (12.9 kB view details)

Uploaded Python 3

File details

Details for the file ai_prompt_benchmark-0.1.0.tar.gz.

File metadata

  • Download URL: ai_prompt_benchmark-0.1.0.tar.gz
  • Upload date:
  • Size: 9.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for ai_prompt_benchmark-0.1.0.tar.gz
Algorithm Hash digest
SHA256 98679f4910862e6cd59f635df63ad9ca765cba9563cb12e9ad73bab44b2b2ab9
MD5 b958c63f6b8898989763e8f683395ff7
BLAKE2b-256 2c28f5be8cd2fe7c2a07f66de15823caa52685fe6c6ca7cfe1aaa09aec8faf4f

See more details on using hashes here.

File details

Details for the file ai_prompt_benchmark-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ai_prompt_benchmark-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6a81d55e60c48ea2a60391f0ff716dd4441b76d50d956125eb859aa0e9916717
MD5 da4dc7d918877613679ed6386353726b
BLAKE2b-256 e5c810b498212dbad32d48c6aaddeb1715476e84afd376493d58fda87fc4bba7

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page