Skip to content

feat(langchain): backport Langchain root metadata flag to v3-stable#1605

Merged
hassiebp merged 1 commit intov3-stablefrom
codex/v3-langchain-root-metadata
Apr 1, 2026
Merged

feat(langchain): backport Langchain root metadata flag to v3-stable#1605
hassiebp merged 1 commit intov3-stablefrom
codex/v3-langchain-root-metadata

Conversation

@hassiebp
Copy link
Copy Markdown
Contributor

@hassiebp hassiebp commented Apr 1, 2026

Summary

  • backport the LangChain callback handler change that marks root LangChain observations with is_langchain_root: true
  • apply the flag consistently at the root callback boundary for root chains, standalone root LLM runs, tools, and retrievers
  • add stable-branch LangChain test assertions that verify the root flag on a standalone LangChain generation and on the root chain in an LCEL run

Why

The v3 callback handler also creates a LangChain-root observation but did not explicitly mark it in observation metadata. Backporting the flag keeps the last major aligned with the newer branch and makes it easier for downstream consumers to identify the LangChain-created root observation reliably.

Impact

Consumers on v3-stable can detect the LangChain-created root observation via metadata.is_langchain_root without changing child observation behavior.

Validation

  • uv run ruff check langfuse/langchain/CallbackHandler.py tests/test_langchain.py
  • uv run ruff format --check langfuse/langchain/CallbackHandler.py tests/test_langchain.py
  • Attempted: uv run pytest -q tests/test_langchain.py -k "callback_generated_from_trace_chat or callback_generated_from_lcel_chain"
    • blocked in this environment because only Python 3.14 is installed here and v3-stable fails test collection with a pydantic.v1 compatibility error under 3.14

Disclaimer: Experimental PR review

Greptile Summary

This PR backports the is_langchain_root metadata flag to the v3-stable branch of the LangChain callback handler, ensuring that root-level LangChain observations (chains, tools, retrievers, and standalone LLM calls) can be reliably identified by downstream consumers.

Key changes:

  • Introduces _get_langchain_observation_metadata() helper that delegates to __join_tags_and_metadata and appends is_langchain_root: True when parent_run_id is None
  • Applies the helper consistently across all four event-start paths: on_chain_start, on_tool_start, on_retriever_start, and __on_llm_action (covering both on_chat_model_start and on_llm_start)
  • Intentionally keeps is_langchain_root out of the trace-level metadata in on_chain_start's update_trace call — the flag only lives on the observation
  • Adds two test assertions: one for a standalone root generation and one confirming exactly one CHAIN-type observation carries the root flag in an LCEL run

The implementation is correct: __join_tags_and_metadata always constructs a fresh dict, so the observation_metadata.copy() in the new helper is safe, and no shared-state mutation can occur. The keep_langfuse_trace_attributes forwarding is preserved for the LLM path as before.

Confidence Score: 5/5

  • Safe to merge — changes are additive, well-scoped, and do not alter existing behavior for non-root observations or for downstream trace metadata.
  • All remaining findings are P2 or lower (a minor double-computation of metadata in on_chain_start when update_trace is enabled, and a stylistic difference between single-underscore _get_langchain_observation_metadata vs double-underscore __join_tags_and_metadata naming). No correctness, data-integrity, or reliability concerns were found.
  • No files require special attention.

Important Files Changed

Filename Overview
langfuse/langchain/CallbackHandler.py Adds _get_langchain_observation_metadata helper that injects is_langchain_root: True when parent_run_id is None, and applies it consistently across on_chain_start, on_tool_start, on_retriever_start, and __on_llm_action; trace-level metadata update in on_chain_start is correctly left unchanged.
tests/test_langchain.py Adds two targeted assertions: one checking is_langchain_root on a standalone root generation, and one verifying exactly one CHAIN-type observation is marked as root in an LCEL run.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["LangChain Event\n(on_chain_start / on_tool_start /\non_retriever_start / __on_llm_action)"] --> B{"parent_run_id is None?"}

    B -- "Yes (root call)" --> C["_get_langchain_observation_metadata()\n→ __join_tags_and_metadata()\n→ copy + add is_langchain_root: True"]
    B -- "No (child call)" --> D["_get_langchain_observation_metadata()\n→ __join_tags_and_metadata()\n→ return as-is (no flag)"]

    C --> E["start_observation(metadata={...is_langchain_root: True...})"]
    D --> F["start_observation(metadata={...})"]

    E --> G{"on_chain_start\nupdate_trace?"}
    G -- "Yes" --> H["update_trace(metadata=__join_tags_and_metadata())\nNO is_langchain_root in trace"]
    G -- "No" --> I["Done"]
    F --> I
Loading

Reviews (1): Last reviewed commit: "mark LangChain roots in metadata" | Re-trigger Greptile

@hassiebp hassiebp marked this pull request as ready for review April 1, 2026 16:58
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 1, 2026

@claude review

@hassiebp hassiebp changed the title [codex] Backport LangChain root metadata flag to v3-stable feat(langchain): backport Langchain root metadata flag to v3-stable Apr 1, 2026
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

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

LGTM — clean, additive backport with well-scoped tests.

Extended reasoning...

Overview

This PR adds a _get_langchain_observation_metadata helper to CallbackHandler.py that wraps the existing __join_tags_and_metadata and injects is_langchain_root: True whenever parent_run_id is None (i.e., the observation is the LangChain-level root). The helper is applied consistently across on_chain_start, on_tool_start, on_retriever_start, and __on_llm_action. Two test assertions are added to verify the flag on a standalone generation and on the root chain of an LCEL run.

Security risks

None. The change only adds a metadata flag to observation payloads; it does not touch auth, permissions, crypto, or any external API surface.

Level of scrutiny

Low. This is a purely additive change with no behavioral impact on existing functionality — existing consumers who do not check is_langchain_root are completely unaffected. The logic is simple and deterministic: flag is present iff parent_run_id is None. The one intentional asymmetry (trace-update path in on_chain_start still uses raw __join_tags_and_metadata without the root flag) is correct, since that path updates the trace metadata rather than the observation metadata.

Other factors

Tests are updated to assert the new flag, pattern is consistent with how the newer branch already handles this, and linting/format checks were run. The PR is focused and does not touch any other logic.

@hassiebp hassiebp merged commit 41fadbf into v3-stable Apr 1, 2026
7 of 13 checks passed
@hassiebp hassiebp deleted the codex/v3-langchain-root-metadata branch April 1, 2026 17:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant