Architecture and extension contract

September 20, 2026 · View on GitHub

The public entry point is EvaluationPipeline. It composes an Evaluator, a backend adapter, metric definitions, and an optional CalibrationBundle.

flowchart TD
    A[Validated samples] --> B[Metric evidence checks]
    B --> C[One Jev request per sample]
    C --> D[Validated raw metric signals]
    D --> E{Calibration enabled?}
    E -->|No| F[Raw scores]
    E -->|Yes| G[Apply compatible fitted curves]
    F --> H[Thresholds and report]
    G --> H
    L[Human labeled examples] --> S[Independent train and holdout split]
    S --> T[Fit on training signals]
    S --> V[Measure on holdout signals]
    T --> V
    T --> G

Modules

ModuleResponsibility
data/models.pyValidated samples, observed tool calls, labels, metric/sample results, summaries
data/datasets.pyValidated JSON and JSONL interchange
metrics/base.pyCustom metric model, validation, signal extraction, and reload handling
metrics/rag.py, metrics/agents.pyBuilt-in response, RAG, agent, and policy rubrics
metrics/registry.py, metrics/presets.pyCLI metric registry and fixed evaluation panels
backends/jev.pyOfficial SDK adapter and backend/session protocols
evaluation/evaluator.pyPreflight, fixed worker pool, per-sample batching, error policy, aggregation
evaluation/pipeline.pyOpt-in lifecycle, independent split, fitting, save/load, automated run
evaluation/decorators.pySync/async decorators preserving native outputs
calibration/core.pyIsotonic fitting, portable prediction, diagnostics, validated artifacts
runtime/guards.pyNamed execution checkpoints, decision policies, tool dispatch gates, response metadata
runtime/agents.pyFramework-independent agent decorators, entrypoint discovery, and instance proxies
runtime/tools.pyShared tool-argument snapshots, evidence mapping, and guarded execution
adapters/LangChain, CrewAI, and Microsoft Agent Framework tool registration and runtime mapping
cli.pyDataset evaluation, calibration fitting, and CI gate exit codes

These paths are relative to typed_evals/. Each subpackage exposes its public objects through __init__.py; the root package continues to export the existing public API. For example, typed_evals.metrics.Metric and typed_evals.evaluation.Evaluator are available alongside their root imports. Direct implementation imports moved with the files: use typed_evals.data.models instead of typed_evals.models, typed_evals.backends instead of typed_evals.backend, and typed_evals.evaluation.evaluator instead of typed_evals.evaluator.

Importing the core package or an adapter module does not import an agent framework. The CrewAI decorator loads CrewAI when creating a native tool. The LangChain and Microsoft adapters operate on the runtime objects injected by their frameworks. See the adapter guide for the integration contracts.

Backend interface

Custom backends implement model: str and session(), an asynchronous context manager yielding a JudgeSession. The session implements:

async def judge(state: dict, questions: Mapping[str, Question]) -> JudgeResponse: ...

Question is an official SDK Noul, Choice, or Score object. JudgeResponse contains the actual model ID, typed-answer dictionaries, and optional usage. The fake backend in examples/offline_demo.py demonstrates the contract.

JevBackend(client=existing_async_client) allows custom transports, endpoints, or caller-owned SDK configuration. The caller closes that client. Its timeout and retry settings supersede JevBackend.timeout / max_retries; the adapter still passes its explicit model on each request. Use a supplied async client inside its owning event loop.

Without an injected client, each evaluation batch creates and closes a pooled client, including on exceptions or cancellation. Sync entry points use asyncio.run. When the calling thread already has a running event loop (for example, in a notebook), they use a worker thread with a separate loop and a copy of the caller's context variables. The worker and its loop are closed after each call, including when evaluation raises. These calls still block the caller; await the async APIs to keep notebook/server event loops responsive. Supplied async clients and custom backends with resources tied to a loop must use the async APIs in their owning loop.

Behavior under failures

  • All sample input checks occur before the first request in an evaluation batch.
  • A missing required evidence field raises by default. Explicit skips have no score.
  • The official SDK owns retries for transient errors; permanent bad requests and authentication errors are not retried by the framework.
  • API failures raise by default. errors="record" records the exception type without copying provider response bodies into exported reports.
  • Partial response omissions are validated metric by metric. Invalid/missing answers never become zero, an empty success, or a synthetic confidence value.
  • If a worker raises, the remaining workers are cancelled and awaited before closing the client. Already-sent requests may still have been billed.
  • Calibration mismatch is always fatal, including in record-errors mode.
  • A failed refit leaves the last successfully fitted calibration bundle intact.

Performance and intentional boundaries

Question construction happens once per batch. Questions sharing a sample share one request, one state, and one usage record. Fixed workers bound in-flight requests and task overhead. Report memory is O(number of samples × number of metrics); the API returns a materialized report. Process very large datasets in batches with evaluate, reusing the saved calibration bundle.

Only the union of fields required by active metrics is sent to Jev. Unrelated metadata and labels are excluded. Raw responses are supplied to TypeSafe for evaluation; they are not redacted automatically. No execution logs, cache files, or raw response text are persisted by default. Saved calibration artifacts contain knots, metrics, model IDs, diagnostics, and hashes, not raw training text.

The metric registry supports all three Jev primitives. It does not generate claims, explanations, or chain-of-thought with another LLM. It does not reimplement framework callbacks or orchestrate an agent. RuntimeGuard can invoke an explicitly supplied operation/tool once after its checkpoint permits it, and gate materialized output before delivery. It never automatically retries application actions. Proposals are separate from observed tool events. See the runtime contract for policy, failure, concurrency, and framework-adapter semantics. It does not claim numeric equality with existing Ragas/DeepEval metrics. Integrate native outputs through explicit sample mappings.