Skip to content

fix(langchain): emit invoke_agent for create_agent, including nested agents - #391

Open
AgentGymLeader wants to merge 18 commits into
open-telemetry:mainfrom
AgentGymLeader:langchain-nested-agent-classification
Open

fix(langchain): emit invoke_agent for create_agent, including nested agents#391
AgentGymLeader wants to merge 18 commits into
open-telemetry:mainfrom
AgentGymLeader:langchain-nested-agent-classification

Conversation

@AgentGymLeader

@AgentGymLeader AgentGymLeader commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What

Emits invoke_agent spans for LangChain create_agent graphs, including nested agents, which now get their own spans.

Callback metadata cannot distinguish a nested agent from its caller: LangChain merges the forwarded config over the inner agent's own, so the inherited fields describe the outer agent. create_agent root classification therefore no longer relies on callback metadata alone. Pregel.stream / astream are patched so a compiled graph announces itself on a context stack, and the callback handler claims the top unclaimed announcement when a root run starts. An inherited agent_name matching an enclosing agent is treated as inherited config, so deeply nested agents keep their own declared names. Deprecated create_react_agent and create_tool_calling_executor compile their own graphs without the marker, so they are tagged at the factory.

Tests

Classification tests drive real invocations through instrument() against a real tracer provider and assert on exported spans: names and parent/child nesting. Sync nested, three-level and renamed cases pass. The async nested case is a strict xfail exposing the pre-existing context-propagation gap above. create_agent tests run on the latest supported LangChain; the oldest supported version predates create_agent, so those tests skip there.

Runtime testing exposed that nested agents could be missed when LangGraph propagated the parent node name into langgraph_node.

Because agent run-name resolution falls back to langgraph_node, restrict the lc_agent_name comparison to the explicit kwargs name. This keeps inherited node metadata from contaminating the comparison while allowing nested agents without an explicit run name.
Copilot AI lite review requested due to automatic review settings August 15, 2026 13:53
@AgentGymLeader
AgentGymLeader requested a review from a team as a code owner August 15, 2026 13:53
@opentelemetry-pr-dashboard

opentelemetry-pr-dashboard Bot commented Aug 15, 2026

Copy link
Copy Markdown

Pull request dashboard status

Waiting on maintainers · refreshed 2026-09-02 14:52 UTC

Merge when ready.

Status above doesn't look right?
  • Just replied or pushed? Anything around or after the refresh time above may not be picked up yet — give it a few minutes.
  • Anything look wrong? Report it with what you expected; it helps us improve the dashboard.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR refines agent span classification for LangChain/LangGraph runs by using LangChain’s lc_agent_name signal while avoiding false positives for internal LangGraph nodes.

Changes:

  • Add lc_agent_name-aware agent detection logic in classify_chain_run via _has_agent_signals.
  • Avoid misclassifying internal LangGraph nodes as agents by comparing against the callback’s explicit run name.
  • Add unit tests covering top-level agents, nested agents, internal nodes, and suppression behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/operation_mapping.py Updates agent-signal detection to incorporate lc_agent_name with LangGraph-aware heuristics.
instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_operation_mapping.py Adds tests validating new classification behavior and suppression rules.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Distinguish confirmed agent ancestors from confirmed non-agent ancestors and unknown ancestry when classifying nested runs. Trim the classification corpus to focused three-state coverage.

Assisted-by: ChatGPT 5.2
@AgentGymLeader

AgentGymLeader commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

@lmolkova Pushed 42aaaf4.

  • Conformance: updated the existing scenario. It uses create_agent now and expects invoke_agent, green under weaver 0.25.1.
  • ls_integration: agreed, that's the marker. A run is a root when it carries it and no ancestor does, so the default form emits invoke_agent now.
  • run_name override: dropped the name comparison. It compares against the nearest marked ancestor now, using the parent runs the handler already tracks.
  • resolve_agent_name: checks lc_agent_name before the run name now.

Not fixed: agents nested inside another agent I couldn't do from callback metadata; the measurement and three options are in the PR body.

I resolved your four threads and the two Copilot ones. Reopen anything I closed too early.

Compiled create_agent graphs carry their own unshadowed marker in
graph.config, so patching Pregel.stream/astream lets a graph announce
itself on a context stack that the callback handler claims when the root
run starts. Nested agents then layer correctly regardless of what the
enclosing agent's config merged over their callback metadata.

Replaces the metadata/ancestry inference: create_agent_ancestry, the
has_create_agent_marker plumbing, and the ls_integration / lc_agent_name
branches in _has_agent_signals and resolve_agent_name are removed.

Also covers langgraph's deprecated create_react_agent, which compiles its
own graph and carries no marker, by tagging what the factory returns.

Verified: three-level nesting, same-named nested agents, async, streaming,
unnamed agents, agents invoked without config forwarding, and nested
create_react_agent. No false positives for a named RunnableLambda or a
plain subgraph invoked from a tool. User overrides (agent_name, otel_trace,
otel_agent_span, run_name) behave as they do without this change.

Prototype only - the existing tests for the removed inference still need to
be dropped and the corpus tests reworked to drive real invocations.

Assisted-by: Claude Opus 5
@AgentGymLeader

Copy link
Copy Markdown
Contributor Author

@lmolkova Folding your prototype in, and reworking the corpus tests through instrument() against exported spans.

Hadn't clocked that the mock made those tests pass with no span at all.

Ruff and the formatter over the modules added by the previous two commits.
Applied here rather than by amending them so authorship stays intact.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

@lmolkova lmolkova left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! a few more minor comments. Please update PR description to describe what PR does, it does not need to describe a history of changes it went through

…ain; announced roots always open their layer

Resolve inherited agent metadata against every enclosing agent while preserving genuine metadata renames. Announced create_agent roots now bypass name-derived noise suppression and always emit their own layer, with nested and async regression coverage.
…compatibly

- Re-add _find_nearest_agent as a thin accessor over _find_agent_context so existing callback tests pass
- Import Self from typing_extensions so Python 3.10 test collection no longer fails
- Apply ruff formatting
Place typing_extensions after the other third-party imports so the ruff isort hook is satisfied.
@AgentGymLeader

Copy link
Copy Markdown
Contributor Author

@lmolkova Updated the description to just describe what the PR does. The other comments are addressed and resolved too.

…on, unset agent name, runtime ReAct detection, uninstrument ordering
… via public callbacks

- add direct .stream()/.astream() tests covering announcement push/pop edge paths
- remove unused _find_nearest_agent; re-express its coverage through on_chain_start behavior
- update stale comments referencing the removed helper
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants