Install
dotnet add package Veval.Sdk
pip install veval-sdk
npm install @veval/sdk
Get your API key
Go to dashboard.veval.dev → Settings → API Keys.Part 1: Minimal trace (no LLM required)
The simplest possible integration — wrap any async operation and it appears in your dashboard.using Veval.Sdk;
var veval = new VevalSdk(new VevalOptions
{
ApiKey = "YOUR_API_KEY",
Endpoint = "https://api.veval.dev",
});
var result = await veval.RunAsync("my-agent", async ctx =>
{
var output = await ctx.TrackStepAsync("summarize", input: "hello world", async () =>
{
// replace with your real LLM call
return "Hello, world!";
});
return output;
});
veval.Dispose();
import asyncio
from veval import VevalSdk, VevalOptions
veval = VevalSdk(VevalOptions(api_key="YOUR_API_KEY"))
async def main():
async def my_agent(ctx):
async def summarize():
# replace with your real LLM call
return "Hello, world!"
return await ctx.track_step_async("summarize", "hello world", summarize)
result = await veval.run_async("my-agent", my_agent)
asyncio.run(main())
import { VevalSdk } from "@veval/sdk";
const veval = new VevalSdk({ apiKey: "YOUR_API_KEY" });
const result = await veval.runAsync("my-agent", async (ctx) => {
const output = await ctx.trackStepAsync("summarize", "hello world", async () => {
// replace with your real LLM call
return "Hello, world!";
});
return output;
});
Part 2: Service pattern (recommended for production)
For real agents, pass the execution context into your service class so every LLM call is tracked automatically.// Your agent service accepts VevalExecutionContext
public class MyAgentService(IVevalSdk veval, AnthropicClient claude)
{
public async Task<string> ExecuteAsync(VevalExecutionContext ctx)
{
var answer = await ctx.TrackStepAsync("answer", ctx.Input, async handle =>
{
var response = await claude.Messages.CreateAsync(...);
// attach LLM metadata to the step
handle.SetMeta("model", response.Model);
handle.SetMeta("tokens_in", response.Usage.InputTokens);
handle.SetMeta("tokens_out", response.Usage.OutputTokens);
handle.SetMeta("cost_usd", ComputeCost(response.Usage));
return response.Content[0].Text;
});
return answer;
}
}
// Wire it up
var veval = new VevalSdk(new VevalOptions { ApiKey = "YOUR_API_KEY" });
var service = new MyAgentService(veval, claude);
var result = await veval.RunAsync("my-agent", service.ExecuteAsync, input: userMessage);
# Your agent service accepts VevalExecutionContext
class MyAgentService:
def __init__(self, anthropic):
self._anthropic = anthropic
async def execute_async(self, ctx):
async def step(handle):
response = await self._anthropic.messages.create(...)
# attach LLM metadata to the step
handle.set_meta("model", response.model)
handle.set_meta("tokens_in", response.usage.input_tokens)
handle.set_meta("tokens_out", response.usage.output_tokens)
handle.set_meta("cost_usd", compute_cost(response.usage))
return response.content[0].text
return await ctx.track_step_async("answer", ctx.input, step)
# Wire it up
veval = VevalSdk(VevalOptions(api_key="YOUR_API_KEY"))
service = MyAgentService(anthropic)
result = await veval.run_async("my-agent", service.execute_async, input=user_message)
// Your agent service accepts VevalExecutionContext
class MyAgentService {
constructor(anthropic) {
this.anthropic = anthropic;
}
async execute(ctx) {
return await ctx.trackStepAsync("answer", ctx.input, async (handle) => {
const response = await this.anthropic.messages.create({ ... });
// attach LLM metadata to the step
handle.setMeta("model", response.model);
handle.setMeta("tokens_in", response.usage.input_tokens);
handle.setMeta("tokens_out", response.usage.output_tokens);
handle.setMeta("cost_usd", computeCost(response.usage));
return response.content[0].text;
});
}
}
// Wire it up
const veval = new VevalSdk({ apiKey: "YOUR_API_KEY" });
const service = new MyAgentService(anthropic);
const result = await veval.runAsync("my-agent", (ctx) => service.execute(ctx), userMessage);
Injecting
IVevalSdk (the interface) lets you swap in VevalTestSdk in tests — no live LLM calls, no API cost. See Replay.