Skip to main content
Every agent run is a trace: a record of inputs, outputs, timing, cost, and the steps in between.
1

Wrap the run

RunAsync starts a trace, runs your agent, then ships the trace to Veval.
2

Track each step

Your agent calls ctx.TrackStepAsync for each LLM call or sub-operation.
3

Open the dashboard

The trace appears in your dashboard with full step detail.

RunAsync

Wraps a complete agent invocation. Sends a trace on success or error.

Synchronous Python agents

If your Python agent is plain synchronous code, use run and track_step. They take ordinary functions and need no event loop. run sends the trace on the calling thread, so in async code, such as an async def web handler, use run_async instead. C# and Node agents use the async API.
Python
Tests can stay synchronous too: get_trace, replay and run_scenario mirror their _async counterparts. Call them from synchronous code; inside a running event loop, await the _async versions instead. The async methods also accept a plain function as the agent.

Delivery failures

Tracing never breaks your app. If Veval can’t store a trace (wrong API key, quota used up, network down), the SDK logs a warning and your agent’s result comes back as usual. The warning names the HTTP status and, when the API gives one, the reason:
Each SDK logs it in that language’s usual place: Posting a scenario run or a snapshot comparison to the dashboard fails the same way: a warning, and no exception unless strict mode is on. This only covers sending results. Calls that return something you asked for throw when they fail, whether or not strict mode is on. That includes SaveSnapshotAsync, GetSnapshotAsync, JudgeAsync and GetTraceAsync. It also includes RunScenarioAsync when it loads a scenario’s items or a saved trace to replay. Loading a trace or a scenario’s items throws VevalRequestException (C#) or VevalRequestError (Python, Node). Like the send error, it carries the HTTP status, or null when there was no response. GetTraceAsync returns null only when the trace doesn’t exist. A scenario whose items can’t be loaded throws instead of running zero items and passing.

Throw in tests and CI

In a test suite, a run that never reached the dashboard should fail the build. Turn on strict mode and a failed send throws VevalSendException (C#) or VevalSendError (Python, Node). The exception carries the HTTP status (StatusCode in C#, status in Python and Node). The status is null when the request got no response, and on the single error a scenario throws for lost item traces (see below).
If your agent throws, you get your agent’s exception, not the send failure. The send failure is still logged. With strict mode on, CompareSnapshotAsync throws if it can’t post the comparison, so you don’t get the diff back. The comparison itself ran; only reporting it failed. In a scenario, a trace that can’t be stored doesn’t stop the remaining items. Every item runs, then the scenario run is posted. If posting the run fails, strict mode throws that failure. Otherwise, if any item traces were lost, it throws once for all of them: Veval: 3 trace(s) from scenario 'my-scenario' could not be stored. That error has no status; the warnings logged for each item have the details.

VevalExecutionContext

The context object passed into your agent. Do not instantiate directly — Veval creates it for you.

TrackStepAsync

Records a single step within a trace.

StepHandle metadata keys

handle.SetMeta(key, value) accepts these well-known keys, plus any custom string:

Nested steps

Steps can be nested by passing the StepHandle to a child TrackStepAsync call.
Nested steps appear as a tree in the dashboard, letting you see exactly where time and cost are spent.

Trace-level metadata

Attach arbitrary key/value pairs to the whole trace (not a step):

What’s next

Test with replay

Run your agent against recorded traces with no live LLM calls.

Run a scenario

Run your agent against multiple inputs and assert on every result.