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 inDevelopmentstatus (not stable). Attribute names have changed between versions of the spec before (for examplegen_ai.systembecamegen_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
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
FlowExecutor.execute) produces:
parentSpanId
(withSpan hands the span to its callback); the flow span then becomes its
child.
Attributes
Constants for every name live insrc/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 thelousho.flow.* namespace. The run span uses the spec’s workflow convention.
Errors
A failed span getserror.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.
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 thegen_ai.* content attributes are never recorded by default. Opt in
per run:
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 namesagent.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.