[opentelemetry-instrumentation-genai-langchain] Propagate gen_ai.conversation.id across the LangChain invocation tree - #474
Conversation
gen_ai.conversation.id reached invoke_agent spans only, leaving it blank on the model-call, tool and workflow rows where users look for it first. It was also applied when the agent span finished, too late for a child span to read it off its parent at start. util-genai now accepts conversation_id on the three invocation types semconv defines the attribute for -- inference, agent and workflow -- and sets it in the span's creation attributes, so it is available for sampling decisions and readable while the span is open. WorkflowInvocation gains the field, which it did not have at all. The LangChain callback handler resolves the id once per run from the thread_id, session_id or conversation_id metadata keys, and _InvocationManager carries it down the invocation tree. Tool, retrieval and span-less chain runs track it for their descendants even though their own spans do not carry it, since semconv does not define the attribute for those operations.
Pull request dashboard statusWaiting on reviewers · refreshed 2026-08-25 21:19 UTC Review the latest changes. Status above doesn't look right?
|
There was a problem hiding this comment.
Pull request overview
This pull request fixes propagation of gen_ai.conversation.id through LangChain callback-driven invocation trees by (a) adding conversation_id to the GenAI util invocation types that semconv defines it for (inference/agent/workflow) and setting it at span creation time, and (b) resolving/inheriting the id per LangChain run via _InvocationManager so descendants receive it even when intermediate nodes do not emit it on their own spans.
Changes:
- Add
conversation_idsupport toInferenceInvocation,AgentInvocation, andWorkflowInvocation, and thread it throughTelemetryHandlerfactory methods so it is present in sampling attributes at span start. - Extend LangChain
_InvocationManagerto track/inherit conversation id across run nodes (including span-less nodes), and add callback handler resolution precedence (thread_id > session_id > conversation_id). - Add/extend unit and integration tests in both
opentelemetry-util-genaiand the LangChain instrumentation to assert correct resolution, inheritance, and omission on spans where semconv does not define the attribute.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| util/opentelemetry-util-genai/src/opentelemetry/util/genai/handler.py | Threads conversation_id through inference/agent/workflow factory methods so it’s applied at span creation time. |
| util/opentelemetry-util-genai/src/opentelemetry/util/genai/_inference_invocation.py | Stores conversation_id and includes it in start-time sampling attributes and finish attributes. |
| util/opentelemetry-util-genai/src/opentelemetry/util/genai/_agent_invocation.py | Stores conversation_id from constructor and includes it in start-time sampling attributes (and request attributes). |
| util/opentelemetry-util-genai/src/opentelemetry/util/genai/_workflow_invocation.py | Adds conversation_id to the workflow invocation and sets it at span start (and on finish). |
| util/opentelemetry-util-genai/tests/test_utils.py | Adds assertions that inference spans include conversation id when provided and omit it when not supplied. |
| util/opentelemetry-util-genai/tests/test_handler_agent.py | Adds coverage that agent conversation id is set at construction time and omitted when not provided. |
| util/opentelemetry-util-genai/tests/test_handler_workflow.py | Adds coverage that workflow conversation id is present at construction time and omitted when not provided. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/invocation_manager.py | Extends invocation state to track/inherit conversation id across parent/child run ids. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/callback_handler.py | Resolves conversation id once per run from metadata with precedence and propagates it through _InvocationManager; passes it only to semconv-eligible spans. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_invocation_manager.py | Adds unit tests for storing, inheriting, overriding, and querying conversation id. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py | Adds unit tests asserting conversation id resolution/inheritance and correct wiring into telemetry handler calls. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_tools.py | Adds tests verifying tool spans omit the attribute while still tracking it for descendant inheritance. |
| instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_conversation_id.py | New integration-style tests building multi-span trees and asserting correct attribute inheritance/omission behavior. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
opentelemetry-instrumentation-genai-langchain] Propagate gen_ai.conversation.id across the LangChain invocation tree
Description
gen_ai.conversation.idonly ever reachedinvoke_agentspans. Everychatand
invoke_workflowspan was missing it, andWorkflowInvocationhad noconversation_idfield at all, so a workflow root could not carry one even inprinciple.
It was also applied in
AgentInvocation._get_request_attributes, which runswhen the span finishes. The attribute was therefore absent for the entire life
of the span: no
SpanProcessor.on_startcould see it, and a child span couldnot read it from its parent.
semconv defines
gen_ai.conversation.idon three spans — inference,invoke_agent, and invoke_workflow. All three now accept a
conversation_idand set it in the span's creation attributes.
WorkflowInvocationgains thefield.
On the LangChain side, the callback handler resolves the id once per run from
the
thread_id,session_id, orconversation_idmetadata keys, and_InvocationManagercarries it down the invocation tree. Tool, retrieval, andunclassified chain runs record it for their descendants without putting it on
their own spans, since semconv does not define the attribute for
execute_toolor
retrieval. A model call nested under a tool still gets it.Fixes #475
Type of change
How has this been tested?
uv run tox -e py314-test-util-genaiuv run tox -e py314-test-instrumentation-genai-langchain-latestuv run tox -e typecheckuv run pre-commit run ruff --all-filesloops, workflows, retrieval, streaming, and error paths. All 31 eligible
spans (21
chat, 8invoke_agent, 2invoke_workflow) carriedgen_ai.conversation.id; the 8execute_tooland 1retrievalspansdid not, as intended.
New tests: conversation id on local agent, remote agent, and workflow spans in
util-genai, and its absence when none is supplied.langchain/tests/test_conversation_id.pybuilds multi-span trees and assertsinheritance, including through parents that do not carry the attribute
themselves. Resolution and precedence are covered in
test_callback_handler.py,test_invocation_manager.py, andtest_tools.py.Checklist
See CONTRIBUTING.md
for the style guide, changelog guidance, and more.