Span content: input and output
Every span’scontent object carries two keys — input and output — both stringified JSON. These represent the raw payload going into the operation and the raw result coming back out.
While input and output accept any valid stringified JSON, you get the most value by sending the exact request payload you send to your AI provider as input and the full response as output. When you use a supported provider — OpenAI, Anthropic, Google, and others — Adaline automatically parses these raw objects to:
- Calculate cost based on token counts and the model’s pricing
- Extract token usage (prompt tokens, completion tokens, total)
- Surface model metadata such as stop reason, tool calls, and function invocations
- Power continuous evaluations with structured input/output pairs
input:
- TypeScript
- Python
input and output values are the raw, unmodified payloads.
You can also set the input and output fields to use Adaline’s own content schema, although this is more advanced and requires maintaining custom transformations to convert provider payloads into the Adaline format.
Group spans into a single trace
When your workflow makes multiple LLM calls (e.g., a RAG pipeline with an embedding call followed by a chat completion), group them under a single trace to see the full picture.With the Proxy
Use theadaline-trace-reference-id header with the same value across all requests:
With the SDK
The SDK groups spans naturally through the trace object:Session tracking
Group related traces by session to follow a user’s full conversation or multi-request workflow:With the Proxy
With the SDK
Tool calling agent pattern
For agents that use tool calling, create nested spans that capture the decision -> tool execution -> response cycle:Error handling pattern
Capture errors at both the span and trace level for effective debugging:Multi-provider pattern
When your workflow uses multiple AI providers (e.g., OpenAI for generation and Anthropic for review), capture each in separate spans within the same trace:Passing variables for evaluation
When using continuous evaluations, include variable values so they can be used to build datasets:With the Proxy
With the SDK
Variables are set on the span’s content object (specifically onModel or ModelStream content types), not on logSpan() directly:
Override continuous evaluation sample rate
Continuous evaluations run on a configurable sample rate — not every span is evaluated. When you need to guarantee that a specific span is evaluated regardless of the sample rate, setrunEvaluation: true on the span.
This is useful when you want to force evaluation on specific requests — high-value customers, flagged conversations, edge cases you’re debugging, or canary deployments where every response matters.
At span creation
- TypeScript
- Python
Via span update
You can also set it after creation — for example, based on a condition you only know after the LLM responds:- TypeScript
- Python
With the Proxy
Set theadaline-span-run-evaluation header to "true":
Setting
runEvaluation: true guarantees the span will be evaluated. It does not affect spans where the flag is omitted or false — those still follow the configured sample rate.Parallel workflows
When your workflow runs operations concurrently — multiple tool calls at once, parallel retrieval from several sources, or fan-out to multiple LLM providers — create sibling spans that overlap in time. Adaline renders them correctly based on theirstartedAt and endedAt timestamps.
- TypeScript
- Python
Promise.all / asyncio.gather. Each span records its own timing independently.
Distributed logging
When a single user request flows through multiple services — an API gateway, a worker queue, a retrieval service — you can attach all spans to one trace using the REST API. The main service creates the trace and passes itsreferenceId to downstream services. Those services don’t create a new trace; they attach spans to the existing one using traceReferenceId, or update the trace using referenceId.
Step 1: Main service creates the trace
The orchestrator creates the trace, logs its own span, and passes thereferenceId to downstream workers:
traceId. But downstream services don’t need it — they can reference the trace by traceReferenceId instead.
Step 2: Worker attaches a span
A downstream worker picks up the job, reads the sharedreferenceId, and attaches its span to the same trace:
Step 3: Another service attaches more spans
A second worker handles the LLM call, again using the sametraceReferenceId:
Step 4: Any service can update the trace
Once the full pipeline completes, any service can update the trace status or add metadata using the samereferenceId:
Best practices
- One trace per user request — Each user interaction (API call, chat message, workflow trigger) should be a single trace.
- Descriptive span names — Use names that indicate the operation:
"query-embedding","vector-search","response-generation"— not"step-1","step-2". - Always set final status — Update trace and span status to
"success"or"failure"before ending. - Use tags for filtering — Add tags like
["production", "v1.3"]to make it easy to filter in the Monitor. - Include variables — Pass variable values on spans so they can be captured into datasets for evaluation.
Next steps
Log User Feedback
Attach user feedback signals to traces.
Analyze Log Traces
View your traces in Monitor.