What Veval does
Veval wraps your agent runs and LLM calls to record traces: inputs, outputs, step timing, token counts, and cost. Traces appear in the dashboard. You run scenarios and assertions against them in CI to catch regressions before they reach production. Three SDKs, same concept: C# (Veval.Sdk), Python (veval), Node (@veval/sdk).
Install
Initialize
C#
Python
Node
Options
Delivery failures
Sending results (traces, scenario runs, snapshot comparisons) never throws unless strict mode is on. A rejected or failed send (401 wrong API key, 429 quota used up, network error) logs a warning that names the status and, when the API gives one, the reason:- C#: standard error
- Python: the
vevallogger atWARNING - Node:
console.warn
WARNING or silences library loggers, the warning is hidden.
Calls that return data (SaveSnapshotAsync, GetSnapshotAsync, JudgeAsync, GetTraceAsync, LoadSnapshotAsync, and RunScenarioAsync loading items or saved traces from the dashboard) are not sends: they throw on failure regardless of strict mode. Loading a trace or a scenario’s items throws VevalRequestException (C#, StatusCode) or VevalRequestError (Python and Node, status); the status is null when there was no response. GetTraceAsync returns null only for a trace that doesn’t exist (404). A scenario whose items can’t be loaded throws rather than running zero items.
With ThrowOnSendFailure / raise_on_send_failure / throwOnSendFailure on, the send throws VevalSendException (C#, StatusCode) or VevalSendError (Python and Node, status). The status is null when there was no response. If the agent itself threw, its exception wins and the send failure is only logged. CompareSnapshotAsync throws without returning the diff if posting the comparison fails. In a scenario, every item runs, then the run is posted. If that post fails, strict mode throws that failure. Otherwise it throws one error for any item traces that couldn’t be stored (N trace(s) from scenario '<name>' could not be stored). That error’s status is always null; the per-item warnings have the statuses.
If traces don’t appear in the dashboard, look for these warnings first.
Core API
RunAsync — wrap a complete agent run
C#
Python
Node
ctx object is a VevalExecutionContext.
Python also has synchronous run, track_step, get_trace, replay and run_scenario for agents written as plain functions. Call them from synchronous code; inside a running event loop, use the _async versions.
TrackStepAsync — record one LLM call or sub-operation
Simple overload (no metadata):C#
Python
Node
C#
Python
Node
Nested steps
Pass the parent handle’s context to nest steps in the dashboard tree:C#
Python
Node
Trace-level metadata
C#
Python
Node
Replay / Test SDK
VevalTestSdk is a drop-in test double. It mocks LLM step outputs from a recorded trace so no real API calls are made. Throws if a step name isn’t found in the trace (strict mode). Each call gets the recorded step with the same name and an identical input, else the next unused one in recorded order (reported as diverged in ReplayResult.Fidelity). A replay fails if a recorded step is never called.
C#
Python
Node
ReplayAsync (lower-level)
C#
Python
Node
ctx.IsReplay (is_replay, isReplay) is true while a recorded trace is replayed (any mode, scenario trace-backed items, WithReplay). Only steps are served from the recording; check it before side effects outside a step, such as sending an email.
Assertions
All assertions implementITraceAssertion. They return null on pass or a failure string.
Built-in factory — all three languages:
Custom assertion:
C#
Python
Node
Scenarios
Run your agent against multiple inputs and post pass/fail to the dashboard.C#
Python
Node
ScenarioItem fields: name (string), input (any), trace_id (string) — provide either input (live LLM) or trace_id (live replay: steps recorded as llm run live, all other steps return their recorded output), not both. assertions is always an array (can be empty). MockModelSteps / mock_model_steps (bool) makes a trace-backed item a fully mocked replay: free, but blind to prompt changes. RunScenarioAsync(..., mockModelSteps: true) (mock_model_steps=True; Node: 5th argument { mock_model_steps: true }) does it for every item, including dashboard-defined ones. VevalTestSdk always replays trace-backed items mocked. In ReplayOptions, LiveModelSteps / live_model_steps selects live replay; it fails if no step ran live or the agent calls a step the trace never recorded.
A trace-backed item’s trace follows the plan’s retention window, which restarts each time the trace is replayed. Pinning the trace in the dashboard (Use as fixture or Pin on the trace page) keeps it indefinitely, up to the plan’s pinned-trace limit (Free 25, Starter 100, Growth 500, Pro 2,000). If the trace no longer exists, that item fails with trace '<id>' not found and the other items still run.
Pass items: null / omit items to fetch them from the dashboard by scenarioName.
Snapshots
A snapshot is a stored known-good run: every step in order with its input and output. Comparing a run against it reports added, removed, and moved steps (including an extra call of a known step) and changed step inputs (prompts, tool arguments), each with a line diff. Saving by trace ID pins the trace, so baselines survive retention.C#
Python
Node
SnapshotOptions: CompareInputs (default true), CompareOutputs (default false), IgnoreSteps, IgnoreFields (JSON keys dropped at any depth), Normalize ((stepName, value) => value). Python snake_case, Node camelCase.
SnapshotDiff: has_changes, changes (kind Added/Removed/Moved/InputChanged/OutputChanged, step_name, occurrence, positions, detail line diff), alignment, summary(), plus legacy added_steps, removed_steps, order_changes. C# uses PascalCase (HasChanges, Changes, Summary()).
LoadSnapshotAsync(traceId) still builds a snapshot from a trace on the fly, but it isn’t pinned — prefer SaveSnapshotAsync.