One agent execution = one trace. Use
lemma.trace() as the boundary around the run.The root trace
- TypeScript
- Python
Trace handles and span handles
Use the callback form when one function owns the whole run. In TypeScript, use a trace handle when work is coordinated across several helpers and you want to pass IDs around explicitly. In Python, keep the root trace inlemma.trace() / lemma.async_trace() and use span, tool, or generation handles inside the callback.
- TypeScript
- Python
trace.id. Call trace.end({ output, durationMs }) once when the run is finished to send the complete trace. Callback traces in both SDKs measure total trace duration automatically; pass durationMs / duration_ms only when you already measured it.
Root latency vs generation duration
Analytics treats these as distinct quantities:
Keep generation timing as measured wall-clock (or provider-reported response time when that is what the integration records). Do not substitute root latency for generation speed, or sum child durations into the root when the root already has an accurate wall-clock measurement.
Framework integrations already set both. For manual instrumentation, pass
durationMs on each generation and let the root measure from lemma.trace() start to end() / callback return.
Record by ID
Helpers can attach work to a trace when they only have IDs from the caller. This detached helper API is TypeScript-specific.traceId. If a detached span, generation, or tool belongs under a parent span, pass parentSpanId; calls that cannot attach safely warn and no-op.
Send a trace you built yourself
lemma.trace() assumes the client owns the trace lifecycle within a single process. When the producer lives elsewhere — a cross-process buffer, a queue worker, a batch backfill — build a TraceContext yourself and deliver it with ingest(). Give it a stable id so retries address the same trace.
- TypeScript
- Python
ingest() POSTs one payload. Deliver one complete trace when the execution (agent turn) finishes: root input/output, thread/user, and all child spans in one call. This is required — patching a trace over time is not currently supported.
ingest() is not an incremental merge API: omitted root fields do not preserve prior values, and after Lemma processes the trace once, a later re-delivery does not re-run issue extraction (occasional late child spans may still append to the tree for display). Retries of the same complete payload are safe — already-stored span IDs are skipped — so a failed send can be retried as-is. It raises on a non-2xx response and never mutates the trace’s status.
Override output or record errors
Return values are captured automatically. Usetrace.output() only when the recorded output should differ from the return value.
- TypeScript
- Python
Pass trace context to helpers
Pass the trace context into nested helper functions that need to record child work:- TypeScript
- Python
Add the work inside
- Generations for LLM calls (model, prompt, completion).
- Tool calls for tool invocations (name, args, result).
- Spans for everything else (retrieval, ranking, app logic).
Next steps
Generations
Capture LLM calls with model, prompt, completion, and timing.
Tool calls
Record tool arguments and results.
Threads & context
Group conversations and attach users.
Trace contract
The exact shape Lemma reads.