EvaltDocs

Evalt Python SDK · v0.10.20

Python SDK reference.

Install Evalt, define durable routes, validate suites, control provider spend, inspect promotion decisions, and enforce measured quality in CI.

Installpython3 -m pip install --upgrade https://evalt.onrender.com/python-sdk/dist/evalt-0.10.20-py3-none-any.whl
Five-minute integration

Run one durable route

Set OPENROUTER_API_KEY in your environment or a .env file in the directory where you run Python. Evalt() finds it automatically; an explicit api_key= takes precedence. The key is never written to Evalt’s route database or result files.

Python
from evalt import Evalt

ticket = "Please help—the website won't load."
evalt = Evalt()  # interactive terminals show design, tournament, route, and cost

answer = evalt.run(
    "Classify this request. Return exactly one lowercase label: billing, account, or technical.",
    ticket,
    task="Route recurring support tickets to billing, account, or technical.",
    route="support-routing",
    target_accuracy=0.95,
    test_budget_usd="auto",
)

print(answer.content)

Connect local routes to the hosted workspace

The SDK remains the production runner. The optional workspace gives its routes a visual history and live progress view without moving customer content into the dashboard.

Connect this computer once
python3 -m evalt connect

The command creates one private workspace token in your user-level Evalt config, opens evalt.dev/app, and publishes existing sanitized route summaries from the current .evalt/evalt.db without making a model call. That connection follows scripts across project folders; pass --state path/to/evalt.db only when one project needs a separate workspace. The CLI and browser show a safe ws_... ID; those IDs must match. Future Evalt.run(...) calls publish bounded route progress and final operational summaries, and the terminal explicitly reports whether each sync succeeded. Every invocation has an opaque run ID and an explicit running, completed, or failed state, so two tournaments on the same route never blend together. Prompts, inputs, outputs, cases, provider keys, request bodies, and raw responses stay local. Dashboard outages never fail a production call.

python3 -m evalt dashboardOpen the workspace connected to this Python installation.
python3 -m evalt --statusShow the safe workspace ID without opening a browser; the longer dashboard --status form also works.
python3 -m evalt doctorCompare the imported package, Python executable, PATH console shim, local routes, hosted routes and workspace reachability without provider calls.
python3 -m evalt dashboard --sync-existingRecover current sanitized route summaries without rerunning a tournament.
python3 -m evalt disconnectRemove the local connection. This does not change a serving route or delete remote metadata.
EVALT_WORKSPACE_TOKENUse an environment variable instead of the local config file in CI or a deployed service.
Interactive terminal progress (stderr)
Evalt · support-routing · TEST DESIGN STARTED · 25 cases · current catalog-selected designer · deadline 120s · one workflow cap $1.00
Evalt · support-routing · DESIGNING TESTS · openai/gpt-5.4-mini · 10s elapsed · still working
Evalt · support-routing · TEST DRAFT READY · 25 AI-authored cases · $0.94 remains for the tournament
Evalt · support-routing · BROAD SCREEN · 8 model configuration(s) · up to 8 in parallel
Evalt · support-routing · SCREENED · qwen/qwen3.5-9b#reasoning=low · 100% validation · p90 920 ms · $0.041200 spent
Evalt · support-routing · ROUTE SELECTED · qwen/qwen3.5-9b#reasoning=low · 100% observed final test · $0.31 test spend
Evalt · support-routing · PROVISIONAL EVIDENCE · 74.1% one-sided 95% lower bound · 10 distinct scenarios
What happens on the first call?Evalt uses the real input only as an unlabeled example of workload shape, creates and calibrates a separate 25-case AI test, searches prompts, training-only few-shot packages, models, providers, and supported reasoning efforts, promotes only a final-test passing package, then answers that real input through it. You do not call accept() or correct() to start this tournament; those methods optionally label later production outcomes. The route remains labeled AI_GENERATED_AI_JUDGED until production feedback strengthens the contract. Use first_run="bootstrap" only for an explicitly untested single call.
Optional review boundary

Inspect the AI-built test before it runs.

Evalt selects a capable test designer from the current intelligence-and-price catalog, then drafts 25 varied routine, ambiguous, adversarial, boundary, and realistic-domain scenarios in five parallel batches before the split. It recommends exact, semantic, or explicit numeric-tolerance judging. A 0–100 subjective rating can therefore accept a nearby approved score without asking another model to pretend one arbitrary number is uniquely correct. The designer fallback chain is a separate live-catalog shortlist and never borrows the judge. The draft spends from the same workflow cap as the later tournament. Evalt names the active model and attempt; if a provider returns malformed structured output, it rejects that draft and makes one visible budget-bounded recovery attempt before falling back.

AI drafts, you approve
from evalt import Evalt

evalt = Evalt()
draft = evalt.optimize_task(
    task="Route recurring support tickets to billing, account, or technical.",
    prompt="Return exactly one lowercase label: billing, account, or technical.",
    route="support-routing",
    case_control="review",
    workflow_budget_usd=1.00,
)

draft.save("support-routing-draft.json")
for case in draft.examples:
    print(case.id, case.conversation())
if input("Type APPROVE after reviewing every expected output: ").strip() != "APPROVE":
    raise SystemExit("Draft saved; no tournament ran.")

# Explicit trust boundary; pass edited examples to approve(...) when needed.
suite = draft.approve()
result = evalt.run(suite)
case_control="review"

AI drafts, you approve

Use for high-stakes or tightly specified work. AI proposes cases and expected behavior; none counts until approve().

Evalt.run(...)

Automatic first route

The default creates, calibrates, judges, and runs the suite immediately, with permanent AI_GENERATED_AI_JUDGED provenance.

After approval, the tournament tries current model candidates and supported reasoning levels in parallel. Prompt rewrites and approved training-only few-shot packages compete too. With the default balanced 25-case suite, ten cases train prompt packages, five choose among them, and ten unique unseen cases confirm the frozen winner. Each final case runs twice, for twenty final executions; prompt tuning never sees them.

Python API

Route lifecycle

A route is one stable production task with its own prompt, examples, budget, selected configuration, and maintenance history.

Evalt.run(..., route=name)On a new route, design and test a package before answering; afterward, reuse the promoted package and return a feedback-capable answer.
answer.contentRead the production output before recording a verdict.
answer.accept()Store this input and output as approved evidence for this route only.
answer.correct(expected)Store the corrected expected behavior without changing another route.
evalt.route_status(name)Inspect the promoted model, prompt version, evidence count, budget, and maintenance reasons.
Inspect a route
status = evalt.route_status("support-routing")

print(status["selected_model"])
print(status["selected_prompt_version"])
print(status["target_accuracy"])
print(status["test_budget_usd"])
print(status["maintenance_due"])
Run independent tasks
support = evalt.run(support_prompt, ticket, route="support-routing")
summary = evalt.run(summary_prompt, transcript, route="call-summary")
fraud = evalt.run(risk_prompt, transaction, route="fraud-review")

Each route keeps separate prompts, evidence, winners, budgets, and retest history. Feedback never crosses route names.

OpenRouter compatibility

Test the request you will actually serve.

Put Chat Completions settings in request_options. Evalt sends the same canonical envelope to every target-model training, validation, and final-test call, stores its SHA-256 fingerprint with the winning route, and reuses it when later calls omit the argument. Test design, prompt optimization, and judging keep independent settings.

Structured output, tools, sampling, and provider routing
answer = evalt.run(
    prompt,
    ticket,
    route="support-routing",
    max_tokens=2048,
    request_options={
        "temperature": 0.2,
        "top_p": 0.95,
        "response_format": {
            "type": "json_schema",
            "json_schema": {
                "name": "support_route",
                "strict": True,
                "schema": route_schema,
            },
        },
        "tools": tools,
        "tool_choice": "auto",
        "parallel_tool_calls": False,
        "provider": {
            "require_parameters": True,
            "data_collection": "deny",
            "zdr": True,
        },
        "plugins": [{"id": "response-healing"}],
    },
)

print(answer.content)
print(answer.tool_calls)
print(answer.request_envelope_validated)
request_options={...}Accepts current and future JSON-serializable Chat Completions fields, including structured output, sampling controls, penalties, stop sequences, tools, prediction, provider routing, plugins, web search, verbosity, and transforms.
max_tokens=...Evalt owns the output ceiling and tests it as part of the route contract. Omit it later to reuse the tested value.
answer.tool_callsReturns normalized function calls without treating a content-free tool response as an empty completion.
strict_request_options=TrueReject a production override before provider spend. Without strict mode, Evalt allows it but emits RequestEnvelopeDriftWarning and marks the answer unvalidated.
route_status(name)Shows the persisted envelope, output ceiling, fingerprint, and every recorded drift event.
Evalt-owned fieldsmodel, fallback models, messages, prompt, usage, streaming lifecycle, output-token fields, and reasoning effort cannot be hidden inside request_options. Evalt varies or accounts for those directly. Zero Data Retention, denied provider data collection, and required-parameter routing remain enforced safety defaults. The pass-through covers the Chat Completions runner, not separate image, embeddings, rerank, speech, or video endpoints.
Multimodal evaluation

Compare models on images you actually approved.

Evalt accepts local PNG, JPEG, WebP, and GIF files or public HTTPS image URLs whose path ends in one of those extensions through the Chat Completions image-understanding path. It filters out text-only candidates before spend, keeps the image modality in the tested request contract, and never sends raw images, filenames, paths, source URLs, or thumbnails to the hosted dashboard.

You do not need AI to make the examples: choose images whose answers you already know, write the expected label yourself, and review the set before running it. AI-generated or automatically inferred image labels do not count as approved ground truth.

Human-approved image suite
from evalt import Evalt, Example, ImageInput, Suite, multimodal_input

examples = tuple(
    Example(
        multimodal_input("Return exactly one label: damaged or intact.", ImageInput.from_path(path)),
        approved_output=label,
        id=f"package-{index}",
    )
    for index, (path, label) in enumerate(approved_images, start=1)
)

suite = Suite(
    name="package-condition",
    prompt="Inspect the package image. Return exactly one label: damaged or intact.",
    examples=examples,
    models=("google/gemini-2.5-flash-lite", "openai/gpt-5.4-nano"),
    evaluator={"type": "exact_text"},
    optimize_prompt=False,
    max_optimization_cost_usd=0.25,
)

result = Evalt().run(suite)
print(result.winner.model, result.winner.holdout_pass_rate)
ImageInput.from_path(path)Validates the file signature and size locally, then creates the provider data URL in memory. The original file is never changed.
ImageInput.from_url(url)Accepts public HTTPS URLs without embedded username/password credentials when the URL path ends in .png, .jpg, .jpeg, .webp, or .gif. URL query data is never written to route call metadata.
multimodal_input(text, *images)Builds the ordered text-first multipart content used in every target-model case.
optimize_prompt=FalseRequired for image suites in this release. Evalt compares models against the frozen prompt and labeled images instead of pretending an automatic text-only prompt writer saw the evidence.
Exact boundaryThis is image understanding in Chat Completions, not image generation. Automatic first-route case design does not invent image labels. Image-bearing feedback is recorded as non-replayable unless the customer supplies an explicit approved suite; raw media never enters hosted route metadata.
Route control plane

The SDK serves. The workspace explains why.

The free workspace is organized around durable named routes, with the latest local invocation scoped inside each route. Open one to see whether the current run is active, completed, or failed; its progress list excludes older invocations while the serving configuration and route history remain durable.

RoutesThe task portfolio: current quality, production cost, serving configuration, and last check.
EvidenceThe exact validation and final-test contract that a challenger must clear.
HistoryPromotions, rejections, prompt changes, price snapshots, and explicit approvals.
IntegrateThe stable route name production code calls instead of hard-coding today’s model.
Early-access boundaryThe current workspace supports BYOK route creation and an explicitly labeled recorded example. Team roles, scheduled Model Watch, alerts, and retained shared history are planned hosted capabilities—not implied current features.
Production learning

Accept, correct, or leave unjudged

Feedback is explicit. Evalt does not silently infer that a delivered answer was correct. Accept an outcome only when it meets the real task; correct it with the expected behavior when it does not.

answer.accept()Adds the input and returned output as approved evidence.
answer.correct(expected)Adds the input with the corrected expected output.
answer.contentThe production response; usable before feedback is recorded.
evalt.route_status(name)Returns the promoted route, evidence counts, price snapshot, and maintenance state.
Explicit optimization

Freeze a suite before provider spend

Use a suite when you already have approved cases, need an auditable tournament, or want a CI artifact. Validation is offline; optimization is the only command below that calls a provider.

python3 -m evalt init evalt.jsonCreate a documented starter suite.
python3 -m evalt validate evalt.jsonValidate schema and contracts offline.
python3 -m evalt optimize evalt.json --output evalt-result.jsonRun the bounded search and save JSON.
Minimal suite JSON
evalt.json
{
  "schema": "evalt-suite-v2",
  "name": "support-routing",
  "prompt": "Return billing, account, or technical.",
  "examples": [
    {"id": "billing-1", "input": "Charged twice", "approved_output": "billing"},
    {"id": "account-1", "input": "Reset link expired", "approved_output": "account"},
    {"id": "technical-1", "input": "App freezes", "approved_output": "technical"}
  ],
  "evaluator": {"type": "exact_text"},
  "quality_threshold": 0.95,
  "max_optimization_cost_usd": 2.0,
  "rounds": 3,
  "max_parallel_models": 16,
  "max_parallel_scenarios": 32,
  "request_timeout_seconds": 600
}
Production-shaped evidence

Model the bell curve without hiding the hard tail

An overall 95% can be misleading when routine requests dominate traffic. Use group to stratify related cases across training, validation, and final test; weight to approximate production frequency; and difficulty_thresholds to require separate routine, complex, and adversarial floors.

Stratified suite excerpt
{
  "examples": [
    {"id":"routine-01", "group":"ordinary-refund", "difficulty":"routine", "weight":6, "input":"...", "approved_output":"approve"},
    {"id":"hard-01", "group":"policy-conflict", "difficulty":"adversarial", "weight":1, "critical":true, "input":"...", "approved_output":"manual_review"}
  ],
  "quality_threshold": 0.95,
  "difficulty_thresholds": {
    "routine": 0.95,
    "complex": 0.90,
    "adversarial": 0.85
  }
}

This is an excerpt. When grouping is enabled, every case needs a group and every group needs at least five cases. Promotion requires the overall weighted target and every named difficulty floor. Put rare catastrophic rules in a deterministic veto too.

Judging

Use the cheapest valid evaluator

Prefer deterministic evaluators whenever the contract allows it. Semantic judging is useful for nuanced outputs, but it adds provider cost and must be calibrated against route-specific human decisions before promotion.

exact_text

Strict normalized labels or strings. No judge-model call.

exact_json

Required keys, optional additional-property vetoes, and rational normalization. No judge-model call.

semantic

Meaning-based verdicts for open outputs. Use an explicit evaluator model and approved rubric.

rules + semantic

Hard rules veto unsafe structure; semantic judging scores the remaining behavior.

Conversations

Keep a conversation in one split

A multi-turn scenario uses a turns array. Evalt keeps the full conversation together when creating training, validation, and final-test splits, and replays prior assistant context in order.

Multi-turn case
{
  "id": "refund-followup",
  "turns": [
    {"role": "user", "content": "I was charged after cancelling."},
    {"role": "assistant", "content": "When did you cancel?"},
    {"role": "user", "content": "Three days before renewal."}
  ],
  "approved_output": "billing"
}

Turns inside one scenario remain ordered. Independent scenarios and repeated executions may run concurrently.

Hard limits

One budget covers the whole search

max_optimization_cost_usd covers prompt improvement, candidate calls, judging, retries, and final testing. Evalt reserves estimated in-flight cost before dispatch; work that cannot fit does not start.

16default parallel model lanes
32default parallel scenario executions
600sdefault per-response deadline
1budget-checked retry for an empty or truncated response
131Kretry ceiling for hidden reasoning tokens

Raise concurrency to reduce wall time, not to expand spend. Model lanes are configurable up to 32 and independent scenario executions up to 128; every request still competes for the same reserved budget. The terminal reports the broad parallel screen, then prints each settled configuration's validation rate, p90 latency, spend, and total elapsed time. Automatic first-route tests generate five coverage batches concurrently and use a 120-second deadline for both designer and candidate requests. Set designer_request_timeout_seconds or test_request_timeout_seconds when the job needs different limits. A candidate that times out cannot earn a higher reasoning rung. Automatic routes try one prompt rewrite by default; set optimization_rounds from 1 to 8 for a deeper search. Explicit suites keep a configurable 600-second default and can raise request_timeout_seconds as high as 7200 seconds.

When the cap ends too soonThe result includes continuation_recommendation only when budget-limited configurations remain unfinished. It lists those configurations and a bounded suggested next cap. automatic_spend is always false; a rerun still requires explicit approval.
Native GitHub integration

Run the same performance gate on every trusted change

The versioned Evalt Action can run a budget-capped tournament or gate an existing result without provider calls. It validates the suite before spending, writes JSON, HTML, and JUnit evidence, and exposes stable outputs for later deployment steps.

.github/workflows/evalt.yml
name: Evalt

on:
  pull_request:
  workflow_dispatch:

permissions:
  contents: read

jobs:
  performance-gate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Find the cheapest route that passes
        id: evalt
        uses: JarJarBeatyourattitude/evalt-action@v1
        env:
          OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
        with:
          suite: evalt.json
          min-pass-rate: "0.95"
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: evalt-evidence
          path: |
            evalt-result.json
            evalt-report.html
            evalt-junit.xml
The spend cap lives in the suitemax_optimization_cost_usd covers target calls, prompt improvement, judging, and final testing. The Action does not silently increase it. Use optimize: "false" to apply the same gate to a saved result fully offline.

Store the OpenRouter key as a repository Actions secret. Ordinary secrets are not passed to workflows from forks; do not switch untrusted pull requests to pull_request_target merely to expose a provider key. See the Action repository for every input and output.

Regression gate

Make measured quality a deploy check

Shell
python3 -m evalt check evalt-result.json \
  --min-pass-rate 0.95 \
  --max-cost-per-success 0.002 \
  --require-complete-coverage
0The result clears every requested gate.
1The measured result is valid but fails a quality, cost, or coverage gate.
2The input is invalid or the check could not run.

python3 -m evalt check is offline. It does not call OpenRouter or mutate the saved result.

Portable reports and run comparison

Render saved results or compare two frozen runs without provider access. A comparison is promotion-safe only when both results pin the same suite hash.

Shell
evalt report evalt-result.json \
  --html evalt-report.html \
  --junit evalt-report.xml

evalt compare baseline.json candidate.json \
  --output comparison.json \
  --html comparison.html

evalt compare shows case-level improvements and regressions plus quality, cost, latency, model, and prompt deltas. If suite hashes differ or are absent, the report labels the comparison descriptive and refuses to imply a shared promotion gate.

Provider boundary

Know what leaves your machine

Sent to the selected provider

The prompt package and the specific inputs needed for a production or evaluation request.

Kept local by the SDK

API key, route database, approved/corrected evidence, suite files, result files, and promotion history.

  • Every OpenRouter request asks for Zero Data Retention and denies provider data collection.
  • Endpoint eligibility still depends on the current provider route; Evalt fails closed when the required privacy route is unavailable.
  • Current pricing is refreshed at least hourly by default. An unpriced or unbounded route is ineligible.
  • The open-source SDK adds no platform fee. Provider charges remain visible in the result.
Versioning

Stable contracts, explicit deprecations

Evalt follows semantic versioning for the evalt Python API, CLI command names, suite schema, result schema, and exit-code contract. Deprecations remain for at least one minor release and are documented before removal.

The historical modelsieve, last_good_prompt, and lgp names are compatibility shims. New integrations should use the evalt import and command.

Troubleshooting

Common failure states

No eligible model route

Confirm the provider exposes a current ZDR endpoint for at least one candidate and that its price, context, output, and required parameters fit the suite. Evalt does not silently downgrade the data contract.

Optimization stops before every candidate runs

The hard budget or provider-response deadline was reached. Inspect the partial-coverage result, then raise the explicit test budget, raise the task-appropriate request_timeout_seconds, or narrow the candidate set. Partial coverage is never labeled globally best.

A response is empty or truncated

Evalt starts with generous reasoning-aware completion headroom: 32,768 tokens without reasoning, 65,536 at low, 98,304 at medium, and up to 131,072 at high, clamped to the route's natural output limit and remaining context. An empty or explicitly truncated response is retried only when a genuinely larger valid ceiling exists. A second cutoff remains an honest unavailable configuration rather than an infinite paid retry loop.

Too few final-test cases

With the default 20% final-test split, provide at least 25 distinct approved scenarios to obtain five final-test scenarios. Repeats measure consistency but do not inflate the distinct case count.

Need a reproducible issue report

Run offline validation, preserve the sanitized suite and result JSON, include evalt --version, and open a GitHub issue without API keys or private production inputs.

Ready to test a real repeated task?

Start with the route. Keep the evidence.

Open the Evalt workspaceInstall from PyPI