Deep Agents
AgentContextOrchestratorRetrievalText2SQLToolbox

Tracing

Ship AI SDK spans to OpenAI's Traces API with batching, retry, and per-span-type wire rules

@deepagents/context/tracing is a subpath export that provides a TelemetryIntegration for the AI SDK. Pass it to experimental_telemetry and every generateText / streamText call produces an OpenAI trace with nested spans for steps, tool calls, and handoffs — shipped to the OpenAI Traces ingest endpoint (or any compatible endpoint).

Assuming sandbox and context have already been constructed (see Sandbox and Agent):

import { groq } from '@ai-sdk/groq';
import { agent } from '@deepagents/context';
import { createOpenAITracesIntegration } from '@deepagents/context/tracing';

const telemetry = createOpenAITracesIntegration({
  apiKey: process.env.OPENAI_API_KEY,
  workflowName: 'support-agent',
});

const assistant = agent({
  name: 'support',
  sandbox,
  context,
  model: groq('gpt-oss-20b'),
  experimental_telemetry: { integrations: [telemetry] },
});

Spans are queued by a BatchTraceProcessor, shipped in batches via an OpenAITracesExporter, and retried with exponential backoff plus jitter on 5xx responses.

createOpenAITracesIntegration(options)

Returns a TelemetryIntegration compatible with the AI SDK's experimental_telemetry hook points. It listens for onStart, onStepStart, onStepFinish, onToolCallStart, onToolCallFinish, and onFinish events and emits one trace per run with nested spans.

OptionTypeDefaultDescription
apiKeystring | (() => string | Promise<string>)process.env.OPENAI_API_KEYAPI key or resolver. Resolver is called on each export — useful for rotated credentials. Required: the exporter throws on export if both the option and env var are empty
baseURLstringhttps://api.openai.comOverride the base URL (Azure, local proxy)
endpointstring<baseURL>/v1/traces/ingestFull override of the ingest endpoint
organizationstringSent as OpenAI-Organization header
projectstringSent as OpenAI-Project header
workflowNamestringevent.functionId | 'ai-sdk-workflow'Name that appears on the root trace
groupIdstringGroup traces together in the UI
metadataRecord<string, unknown>Extra metadata merged into every trace. Non-string values are JSON-stringified on export; null and undefined entries are dropped
exporterOpenAITracesExporterconstructed from the other optionsFully custom exporter (useful for tests or alternative sinks)
processorTracingProcessor | TracingProcessor[]BatchTraceProcessor(exporter)Replace the default batch processor or chain your own
batchBatchTraceProcessorOptionssee belowTune the default batch processor
includeSensitiveDatabooleanenv OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA !== '0'Include inputs/outputs on generation and function spans

Set the env var OPENAI_AGENTS_DISABLE_TRACING=1 to return an empty integration — handy for disabling tracing in tests without changing call sites.

Trace Shape

Each run produces one trace with one root agent span and zero or more nested spans:

EventSpan typeNotes
onStartagent (root)Workflow name, available tools, output type
onStepStartgenerationModel id, provider, tool choice, messages (if sensitive data is enabled)
onStepFinishcloses the generation spanUsage tokens, response messages
onToolCallStartfunctionTool name + JSON-stringified input
onToolCallFinishcloses the function spanJSON-stringified output, or error
onFinishcloses the root spanTotal usage, step count, finish reason

When multiple runs are open concurrently (nested agents, asAdvisor), events are routed to the correct run by matching response.id, toolCall.toolCallId, experimental_context, metadata, abortSignal, functionId, model, and an input fingerprint — in descending priority. When exactly one run is open, it gets the event directly. When no run is open, the event is dropped rather than attached to the wrong trace.

Per-Span Wire Contracts

The ingest endpoint is strict about input/output shapes. Before shipping, the exporter rewrites each span to match:

Span typeinput shapeoutput shape
generationArray of message recordsArray of message records
functionJSON stringJSON string
transcriptionJSON stringJSON string
everything elseUnchangedUnchanged

FunctionSpanData and TranscriptionSpanData get their input / output JSON.stringifyed on the way out — sending them as objects or arrays causes a 400 from the API. The GenerationSpanData path keeps arrays. All of this lives in OpenAITracesExporter so processors and handlers can work with native objects until export time.

Trace-level metadata is also normalized at export time. The wire payload sends metadata as Record<string, string>; non-string values are JSON-stringified and empty metadata objects are omitted.

OpenAITracesExporter

A standalone exporter you can instantiate if you want to replace the default pipeline:

import { OpenAITracesExporter } from '@deepagents/context/tracing';

const exporter = new OpenAITracesExporter({
  apiKey: process.env.OPENAI_API_KEY,
  organization: 'org-123',
  maxRetries: 5,
  baseDelayMs: 500,
  maxDelayMs: 15000,
});

await exporter.export(items, abortSignal);
OptionDefaultDescription
apiKeyprocess.env.OPENAI_API_KEYString or async resolver
baseURLhttps://api.openai.com
endpoint<baseURL>/v1/traces/ingest
organization, projectHeaders
maxRetries3Only 5xx and network errors retry; 4xx throws OpenAIExportError immediately
baseDelayMs1000Initial backoff
maxDelayMs30000Upper bound on backoff

Retries use exponential backoff with 10% jitter. OpenAIExportError carries both the HTTP status code and the raw response body for debugging.

BatchTraceProcessor

The default processor queues spans in memory, flushes on a timer or when the queue hits a threshold, and calls exporter.export(batch).

OptionDefaultDescription
maxQueueSize8192Total span + trace items buffered
maxBatchSize128Max items shipped per export
scheduleDelayMs5000Timer interval between flushes
exportTriggerRatio0.7Proportion of maxQueueSize that triggers an immediate flush
exportTimeoutMs30000Abort signal deadline for each export call

Pass custom batch options on the integration to tune it:

createOpenAITracesIntegration({
  batch: { maxBatchSize: 32, scheduleDelayMs: 2000 },
});

Custom Processors

TracingProcessor is a minimal interface — start, trace/span lifecycle callbacks, flush, shutdown. Swap the default batch processor for your own implementation (e.g. to mirror spans to stdout during local development):

import type { TracingProcessor } from '@deepagents/context/tracing';

const consoleProcessor: TracingProcessor = {
  onSpanEnd(span) {
    console.log('[trace]', span.span_data.type, span.id);
  },
};

createOpenAITracesIntegration({ processor: consoleProcessor });

Pass an array to run multiple processors — they're combined via CompositeTraceProcessor.

Usage Normalization

normalizeUsage flattens the AI SDK's LanguageModelUsage into the OpenAI trace schema:

  • input_tokens, output_tokens on the span usage.
  • details.input_token_details (no-cache, cache-read, cache-write).
  • details.output_token_details (text, reasoning).
  • details.reasoning_tokens, details.cached_input_tokens when present.
  • details.raw — the provider's raw usage payload.

The same function is used inside the root span's total_usage metadata on finish.

Disabling Sensitive Data

Set includeSensitiveData: false (or env OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA=0) to strip inputs/outputs from generation and function spans. Usage counts, model names, tool names, and errors are still shipped. The tracing shape is identical — only the payload bodies drop.

  • Agentexperimental_telemetry on the agent options
  • Chat Function — Streaming entry point that forwards telemetry