Skip to main content
Every framework below works the same way with Flow Observe:
  1. Set up the exporter as on Send traces from any agent.
  2. Wrap each turn in your own agent.turn span with the flow.* attributes, as in step 2. Set flow.agent_reply from the text you actually send, not from the framework’s output.
  3. Get the framework’s spans into that trace, so Observe shows the model calls, tools and errors under the turn. This is the only part that differs per framework.
Step 3 is optional: your agent.turn span alone is enough for conversations and grading. If the framework’s spans end up in traces of their own, they are kept but not shown under any conversation. Do not copy the flow.* attributes onto those traces: Observe reads every trace with a flow.conversation_id as a turn of its own. Frameworks record prompts and outputs in their spans by default. Observe stores them encrypted and does not show them today. If you would rather not send them, each section says how to turn recording off.

OpenAI Agents SDK

The SDK has its own tracing, which by default sends traces to OpenAI’s traces dashboard. It does not export OpenTelemetry itself. The OpenInference instrumentation turns its traces into OpenTelemetry spans, nested under your current span, and by default replaces the export to OpenAI.
  • OpenInference names spans its own way, so Observe shows these spans with their names, timings and errors, but leaves the model and token columns empty for them today.
  • Do not turn the SDK’s tracing off (OPENAI_AGENTS_DISABLE_TRACING, set_tracing_disabled): that stops the instrumentation too. To keep sending to OpenAI as well, pass exclusive_processor=False (Python) or manuallyInstrument(agents, { exclusiveProcessor: false }) (TypeScript).
  • To leave prompts and outputs out of the spans: RunConfig(trace_include_sensitive_data=False) (Python) or traceIncludeSensitiveData: false (TypeScript).

LangChain and LangGraph

Use the OpenInference instrumentation. It sends LangChain’s and LangGraph’s runs as OpenTelemetry spans through your provider, nested under your current span, and never contacts LangSmith as long as LANGSMITH_TRACING (and the older LANGCHAIN_TRACING_V2) stay unset.
As with the OpenAI Agents SDK, Observe shows OpenInference spans with their names, timings and errors, with the model and token columns empty today. Already on LangSmith? Its OpenTelemetry mode can send to Observe instead: pip install "langsmith[otel]", then set LANGSMITH_TRACING=true, LANGSMITH_OTEL_ENABLED=true and LANGSMITH_OTEL_ONLY=true (without the last one, traces go to LangSmith too), with the provider from step 1 set before LangChain starts.

Vercel AI SDK

The AI SDK’s spans use the global OpenTelemetry provider and nest under your current span. Telemetry must be turned on, and the option’s name depends on your version:
TypeScript
In Next.js, register the exporter in instrumentation.ts instead of step 1:
instrumentation.ts
  • With streamText, end the agent.turn span only after the stream has been read to the end and sent.
  • Do not also set OTEL_EXPORTER_OTLP_ENDPOINT when you pass a traceExporter: @vercel/otel would export through both and send every span twice.
  • The AI SDK records prompts and outputs by default. To leave them out: recordInputs: false, recordOutputs: false in the telemetry options.
  • On AI SDK 5 and 6 you can also pass the ids as telemetry metadata (metadata: { "flow.conversation_id": threadId }); Observe reads them there. AI SDK 7 removed metadata, so the agent.turn span is the way that works on every version.

Claude Agent SDK

The SDK runs Claude Code, which exports its own spans over OpenTelemetry when you turn it on through the SDK’s env option (a beta feature). Run query() inside your agent.turn span and the SDK passes your trace context on, so its spans join your trace. Needs claude-agent-sdk 0.1.60 or later installed as claude-agent-sdk[otel] (Python), or @anthropic-ai/claude-agent-sdk 0.2.113 or later (TypeScript).
  • Its spans are named claude_code.interaction, claude_code.llm_request and claude_code.tool. Observe shows them with their timings and errors; they use Claude Code’s own attribute names, so model and token columns stay empty for them today.
  • Prompts and responses are left out of these spans unless you set OTEL_LOG_USER_PROMPTS=1 or OTEL_LOG_ASSISTANT_RESPONSES=1. You do not need them: flow.user_message and flow.agent_reply carry the text.
  • Claude Code batches spans and fails quietly. Set CLAUDE_CODE_OTEL_DIAG_STDERR=1 to see export errors. Never use the console exporter: standard output is the SDK’s message channel.

Mastra

Mastra’s tracing has its own OpenTelemetry exporter, @mastra/otel-exporter. It does not join your active span by itself: pass your turn span’s trace id and span id in tracingOptions, and Mastra’s spans nest under it. Your agent.turn span is still exported by your own setup from step 1.
TypeScript
  • Put the flow.* attributes on your agent.turn span. Observe also reads them from Mastra’s metadata (exported as mastra.metadata.flow.*), but Mastra exports its spans apart from yours, so keep the ids on the turn span with the texts.
  • signals: { logs: false } keeps the exporter from also posting logs.
  • In a serverless function, await mastra.observability.flush() before returning, besides flushing your own provider.
  • To leave prompts and outputs out of Mastra’s spans: tracingOptions: { hideInput: true, hideOutput: true }.

Another framework

Any framework that emits OpenTelemetry spans through the global provider works like the Vercel AI SDK above: run it inside your agent.turn span. One that has its own tracing system needs a bridge to OpenTelemetry, as the OpenAI Agents SDK and LangChain do. If you cannot get its spans into your trace, your agent.turn span is still enough for conversations and grading.