Skip to main content

tourney

PyPI Python CI License

Local-first benchmarking runner for AI models. Run your prompts against the field.

tourney runs your own prompts and datasets against multiple model APIs (your keys, your machine) and measures quality, latency, cost, and token usage — with the statistical rigor researchers expect: bootstrap confidence intervals, pinned model IDs, seeds, config hashes, and full run metadata in a local SQLite database. No hosted service, no telemetry.

Install

pip install "tourney[dashboard]"      # or plain `pip install tourney` for CLI/library only

Five-minute quickstart

tourney init            # scaffolds benchmark.yaml + cases.jsonl
tourney run benchmark.yaml
tourney show latest --failures
tourney serve           # local dashboard at http://127.0.0.1:8355

benchmark.yaml:

name: math-word-problems
models:
  - provider: openai
    model: gpt-4o-2024-08-06          # pin dated IDs for reproducibility
  - provider: anthropic
    model: claude-sonnet-5
  - provider: gemini
    model: gemini-2.5-flash
  - provider: openai_compat           # anything OpenAI-compatible: Ollama, vLLM, OpenRouter...
    model: llama3.1:8b
    base_url: http://localhost:11434/v1
defaults: {temperature: 0, max_tokens: 256, seed: 42}
prompt:
  system: "Answer with only the final number."
  user: "{{ question }}"              # Jinja2 over each case's input
dataset: {path: cases.jsonl}
graders:
  - {type: numeric, tolerance: 0.001}
run: {concurrency: 8, retries: 3, timeout_s: 60}

API keys come from environment variables (OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, or a custom api_key_env) — never from config files.

math-word-problems · run 01J9X4… · $0.213
┌──────────────────────┬───────┬──────────────┬─────────┬─────────┬─────────┬────────┐
│ model                │ score │ 95% CI       │ p50 lat │ p99 lat │ cost    │ errors │
├──────────────────────┼───────┼──────────────┼─────────┼─────────┼─────────┼────────┤
│ claude-sonnet-5      │ 0.960 │ [0.89, 1.00] │   944ms │  2107ms │ $0.0920 │      0 │
│ gpt-4o-2024-08-06    │ 0.940 │ [0.86, 0.98] │   812ms │  2431ms │ $0.1210 │      0 │
└──────────────────────┴───────┴──────────────┴─────────┴─────────┴─────────┴────────┘

Why another eval tool?

  • Local-first. Results live in ./.tourney/results.db (SQLite). Copy it, query it, commit exports.
  • Reproducible. Config hash, tourney/Python versions, and git commit stored per run. Deterministic graders by default; stats are seeded.
  • Cheap to iterate. Responses are content-address cached — rerunning an unchanged benchmark costs $0.
  • CI-native. tourney run --json --fail-under 0.85 exits non-zero when a model regresses.
  • Honest statistics. Bootstrap CIs over cases; errors score 0 instead of being dropped. pass@k and pass^k when you sample more than once.

pass@k and pass^k

Sample each case more than once and report how often a model gets there at all versus how often it gets there reliably:

run: {repeats: 10}
report:
  pass_at_k: [1, 5]     # P(at least one of k attempts passes)
  pass_hat_k: [5]       # P(all k attempts pass)

pass@k uses the unbiased estimator from the Codex paper; pass^k is its hypergeometric counterpart. Both are computed per case and averaged over cases, and both need repeats >= k — configs that ask for more are rejected at load time rather than after a paid run.

A sample counts as passing only when every grader on it passed, so partial credit contributes to score but not to these columns. Repeats are cached independently, so distinct samples stay distinct; set temperature above 0 or every repeat will be the same answer and pass@k will equal pass@1.

Graders

type what it checks
exact_match normalized string equality (normalize: [strip, lowercase, collapse_whitespace])
contains substring presence
regex pattern match (pattern:)
numeric last number in the response vs expected, within tolerance
json_schema output is valid JSON, optionally conforming to schema:

Multiple graders per benchmark average into the case score. LLM-as-judge is planned as strictly opt-in.

Library

import tourney

result = tourney.run("benchmark.yaml")
result.summary()      # per-model dicts: score, CI, latency percentiles, cost
result.to_records()   # per-completion rows -> pd.DataFrame(result.to_records())

CLI reference

tourney init                     scaffold a new benchmark
tourney run <config>             run it (--model, --limit, --no-cache, --json, --fail-under)
tourney list                     recent runs
tourney show <run|latest>        summary table (--failures for the failing cases)
tourney export <run> --format    jsonl | csv | md
tourney serve                    local dashboard (requires tourney[dashboard])

Extending

Third-party providers and graders register via entry points (tourney.providers, tourney.graders) — publish a package with a Provider or Grader subclass and it becomes available by name in configs. A mock provider ships in the box for dry runs and tests.

Development

pip install -e ".[dev]"
pytest        # fully offline — network is disabled in the test suite

Apache-2.0.

Download files

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

Source Distribution

tourney-0.3.0.tar.gz (43.0 kB view details)

Uploaded Source

Built Distribution

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

tourney-0.3.0-py3-none-any.whl (44.6 kB view details)

Uploaded Python 3

File details

Details for the file tourney-0.3.0.tar.gz.

File metadata

  • Download URL: tourney-0.3.0.tar.gz
  • Upload date:
  • Size: 43.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for tourney-0.3.0.tar.gz
Algorithm Hash digest
SHA256 acf6ffa181675dcd28fd2a324cf79af77e39c3bfe545025c6c9e8e4b5adaecdd
MD5 7ebb289d9b5f5f6e751f8bcf9acd8866
BLAKE2b-256 2df3904afd98b389013bfdc70eea6f11b46f442d8fca0138b0a83453841ae8ed

See more details on using hashes here.

Provenance

The following attestation bundles were made for tourney-0.3.0.tar.gz:

Publisher: release.yml on k-rthik/tourney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tourney-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: tourney-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 44.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for tourney-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f2e9670b925fce6611490c027f19e7ff072a195597f2ea25677e11b55b6207c6
MD5 d8a3e3c7721cf7b23fea102e5ee4b390
BLAKE2b-256 8f3f28d120692ecdfb02c28637bc85575dff6548fd6f052e4645a84865cdc892

See more details on using hashes here.

Provenance

The following attestation bundles were made for tourney-0.3.0-py3-none-any.whl:

Publisher: release.yml on k-rthik/tourney

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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