Skip to main content

How tracing works

Every agent run is a trace — a record of inputs, outputs, timing, cost, and the steps in between.
  1. VevalSdk.RunAsync starts a trace, runs your agent, then ships the payload to Veval.
  2. Your agent calls ctx.TrackStepAsync for each LLM call or sub-operation.
  3. The trace appears in your dashboard with full step detail.

RunAsync

Wraps a complete agent invocation. Sends a trace on success or error.
var result = await veval.RunAsync(
    agentName: "my-agent",
    callback:  ctx => myService.ExecuteAsync(ctx),
    input:     userMessage   // optional — stored on the trace
);
ParameterTypeDescription
agentNamestringIdentifies the agent in the dashboard
callbackFunc<VevalExecutionContext, Task<T>>Your agent logic
inputobject?Arbitrary input stored on the trace

VevalExecutionContext

The context object passed into your agent. Do not instantiate directly — Veval creates it for you.
MemberDescription
TraceIdUnique ID for this run (tr_...)
InputThe input passed to RunAsync
TrackStepAsync(...)Records a step
SetMetadata(key, value)Attaches trace-level metadata

TrackStepAsync

Records a single step within a trace.
// Simple overload — no handle needed
var summary = await ctx.TrackStepAsync("summarize", input: text, async () =>
{
    return await llm.Summarize(text);
});

// Handle overload — attach LLM metadata
var answer = await ctx.TrackStepAsync("answer", input: question, async handle =>
{
    var response = await claude.Messages.CreateAsync(...);

    handle.SetMeta("model",      response.Model);
    handle.SetMeta("tokens_in",  response.Usage.InputTokens);
    handle.SetMeta("tokens_out", response.Usage.OutputTokens);
    handle.SetMeta("cost_usd",   0.0012m);

    return response.Content[0].Text;
});

StepHandle metadata keys

handle.SetMeta(key, value) accepts these well-known keys, plus any custom string:
KeyTypeDescription
modelstringModel name (e.g. claude-sonnet-4-6)
tokens_inintInput token count
tokens_outintOutput token count
cost_usddecimalCost in USD
typestringStep type — use "tool" for tool calls
(custom)objectAny other key stored in step metadata

Nested steps

Steps can be nested by passing the StepHandle to a child TrackStepAsync call.
var result = await ctx.TrackStepAsync("pipeline", input: query, async handle =>
{
    var classified = await ctx.TrackStepAsync("classify", input: query, async () =>
        await llm.Classify(query));

    var answered = await ctx.TrackStepAsync("answer", input: classified, async () =>
        await llm.Answer(classified));

    return answered;
});
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):
ctx.SetMetadata("user_id",  userId);
ctx.SetMetadata("session",  sessionId);
ctx.SetMetadata("region",   "us-east-1");