Skip to main content
AgentExecutor.execute() and FlowExecutor.execute() emit spans through a TraceExporter. Span names and attributes follow the OpenTelemetry GenAI semantic conventions, so GenAI-aware observability backends can recognise them without custom mapping.
Stability. The GenAI conventions are in Development status (not stable). Attribute names have changed between versions of the spec before (for example gen_ai.system became gen_ai.provider.name) and may change again. This SDK follows the spec as retrieved on 2026-10-01 from the semantic-conventions-genai repository (where the conventions now live). Pin your SDK version if dashboards depend on these names.

Quick start

For real OpenTelemetry spans, import createOtelTraceExporter() from @lousho/build-ai-agent/otel (needs the optional peer dependency @opentelemetry/api and a registered TracerProvider). It carries span kind and error status over to OpenTelemetry. See examples/tracing (npm run example:tracing:console, npm run example:tracing:otel, which also prints metrics).

Span tree

A flow run (FlowExecutor.execute) produces:
To nest a flow under an agent span, pass that span’s id as parentSpanId (withSpan hands the span to its callback); the flow span then becomes its child.

Attributes

Constants for every name live in src/execution/semconv.ts (exported as GenAiAttr, GenAiOperation, GenAiMetric, ErrorAttr, FlowAttr, SdkAttr, LegacyAttr).

Agent run: invoke_agent {agent name}

Model call: chat {model} (CLIENT)

Tool call: execute_tool {tool}

Flows

The GenAI spec defines no convention for flow nodes, so node spans use the lousho.flow.* namespace. The run span uses the spec’s workflow convention.

Errors

A failed span gets error.type and an error span status (OpenTelemetry ERROR, message = the error message). error.type is the thrown error’s name (_OTHER when a non-Error is thrown). A tool call that returns an error to the model (isError) is marked error.type = tool_error.

Metrics

createOtelTraceExporter() also records the OpenTelemetry GenAI client metrics through @opentelemetry/api’s metrics API, using the global MeterProvider (metrics.getMeter(...)), or the meter option: Both use the spec’s recommended bucket boundaries. They follow the gen-ai-metrics page of semantic-conventions v1.40 (Development stability): the newest conventions split the token histogram into per-type counters, which this SDK does not emit yet. Agent and flow spans are not counted in the duration metric, only the model and tool calls inside them.
Without a registered MeterProvider, OpenTelemetry’s no-op meter discards the records. The metrics API lives in @opentelemetry/api, so nothing is recorded (and nothing is loaded) when that optional peer is not installed. The cost is an estimate from the price table (see registerModel), not a bill.

Message and argument content is opt-in

Prompts, model output and tool arguments/results are sensitive and large, so the gen_ai.* content attributes are never recorded by default. Opt in per run:
Flows take the same flag on their context (captureContent). When captureContent is not set, the OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true environment variable (the name the spec uses as its example) turns it on. An explicit captureContent: false always wins. With it on, content is recorded as JSON strings (the spec’s fallback for spans) in the spec’s message schema:

Deprecated attribute names

Before the GenAI conventions, spans used ad-hoc names and the span names agent.run, llm.generate and tool.call. The old attribute names are still emitted next to the new ones so existing dashboards keep working, but they are deprecated and will be removed in a future major version. Span names changed (to what the spec requires); update queries that matched on them. The deprecated content attributes (input, prompt, args, result) keep their old behavior: they are recorded unless redactContent: true. Set redactContent: true if you want no content on spans unless you opt in with captureContent.

Backends

Any backend that ingests OpenTelemetry traces (OTLP) receives these spans. Backends and tools that understand the GenAI semantic conventions can render them as LLM, agent and tool calls with token usage; others show them as ordinary spans with attributes. Which conventions a given backend supports, and which version of them, varies; check its documentation.