Skip to content

fix: expose stable Langfuse trace IO for evaluators - #14089

Merged
erichare merged 3 commits into
release-1.11.0from
fix/release-1.11.0-langfuse-trace-io-jsonpath
Jul 15, 2026
Merged

fix: expose stable Langfuse trace IO for evaluators#14089
erichare merged 3 commits into
release-1.11.0from
fix/release-1.11.0-langfuse-trace-io-jsonpath

Conversation

@erichare

@erichare erichare commented Jul 15, 2026

Copy link
Copy Markdown
Member

Summary

  • cherry-pick the Langfuse trace IO fix from fix: expose stable Langfuse trace IO for evaluators #14087 onto release-1.11.0
  • expose evaluator-addressable input and output keys on Langfuse trace IO
  • derive trace values from marked graph boundaries without hardcoding chat or agent component names
  • preserve the complete component aggregates on the root observation and leave child observations unchanged

The cherry-picked patch is identical to 1ac2e19551f4e0e148eef0ec7ad7acaf861a6a65; the 1.11-only Langfuse __deepcopy__ safeguard remains intact.

Reported by Naveed Syed (Solis).

Validation

  • test_langfuse_v3_compatibility.py: 30 passed
  • test_tracing_service.py: 20 passed
  • test_langfuse_orphan_generation.py: 9 passed
  • targeted Ruff check and format check passed
  • git diff --check passed
  • git range-diff reports an identical patch to fix: expose stable Langfuse trace IO for evaluators #14087

Summary by CodeRabbit

  • Bug Fixes
    • Improved Langfuse trace inputs and outputs to reflect graph boundary components more accurately.
    • Preserved structured data, message content, and multi-output fields in trace results.
    • Added deterministic ordering for multiple boundary outputs.
    • Improved fallback handling when boundary markers are unavailable.
    • Preserved correct behavior for components serving as both input and output boundaries.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 265d68e1-9119-4f9b-899b-1245192dfbdc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Langfuse tracing now derives root trace input and output from graph boundary components, normalizes message values, supports deterministic selection and fallbacks, and exposes stable evaluator-oriented shapes. Compatibility tests cover marked, unmarked, multi-output, dual-role, and nested-message scenarios.

Changes

Langfuse trace IO

Layer / File(s) Summary
Boundary extraction and trace finalization
src/backend/base/langflow/services/tracing/langfuse.py
Boundary components are tracked during trace construction; serialized values are normalized and selected deterministically, with fallback and dual-role handling before updating the root trace.
Stable boundary IO validation
src/backend/tests/unit/services/tracing/test_langfuse_v3_compatibility.py
Tests verify evaluator-facing boundary values, complete aggregate serialization, fallback behavior, deterministic output ordering, dual-role preservation, and nested message normalization.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Flow
  participant LangFuseTracer
  participant Langfuse
  Flow->>LangFuseTracer: add traces with boundary vertices
  LangFuseTracer->>LangFuseTracer: serialize and select boundary input/output
  LangFuseTracer->>Langfuse: update root trace with wrapped IO
Loading

Possibly related PRs

Suggested reviewers: cristhianzl


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error New Langfuse unit tests cover main paths, but the bug-fix edge case (two marked boundaries where one yields {}) isn’t tested, so regression coverage is incomplete. Add a regression test in test_langfuse_v3_compatibility.py for multiple marked input/output components where one branch returns {} and verify scalar/list collapse stays stable.
Test Quality And Coverage ⚠️ Warning The new pytest tests verify real trace I/O behavior, but they miss the mixed-boundary empty-dict case, so the scalar/list contract isn’t fully covered. Add a regression test with two marked boundary components where one yields {} and assert the surviving value still collapses correctly to a scalar.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: exposing stable Langfuse trace input/output for evaluators.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test File Naming And Structure ✅ Passed The changed backend test file follows pytest conventions, is logically grouped with fixtures, and includes descriptive tests plus positive/negative cases.
Excessive Mock Usage Warning ✅ Passed Mocks are limited to the external Langfuse SDK/logger; boundary-serialization tests still use real Message objects, so the mock usage isn’t excessive.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/release-1.11.0-langfuse-trace-io-jsonpath

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@erichare erichare added the bug Something isn't working label Jul 15, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 15, 2026
@erichare
erichare marked this pull request as ready for review July 15, 2026 15:13
@github-actions

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 15, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/base/langflow/services/tracing/langfuse.py`:
- Around line 54-81: Update _trace_boundary_value to collapse the result based
on the number of serialized values actually collected: return the sole value
when len(values) == 1, otherwise return the values list. Preserve the existing
empty-result behavior, and add a regression test in
test_langfuse_v3_compatibility.py for two marked output components where one
produces an empty dict.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 271079a9-5331-473c-ab7e-e64aade2c41a

📥 Commits

Reviewing files that changed from the base of the PR and between a93c64a and b1e6ca6.

📒 Files selected for processing (2)
  • src/backend/base/langflow/services/tracing/langfuse.py
  • src/backend/tests/unit/services/tracing/test_langfuse_v3_compatibility.py

Comment on lines +54 to +81
def _trace_boundary_value(
component_values: dict[str, Any],
boundary_traces: dict[str, str],
*,
fallback_component_values: dict[str, Any] | None = None,
prefer_fallback_trace_ids: set[str] | None = None,
) -> tuple[bool, Any]:
"""Return marked graph-boundary outputs in deterministic component-id order."""
values = []
prefer_fallback_trace_ids = prefer_fallback_trace_ids or set()
for trace_id, trace_name in sorted(boundary_traces.items()):
sources = (
(fallback_component_values, component_values)
if trace_id in prefer_fallback_trace_ids
else (component_values, fallback_component_values)
)
for source in sources:
if source is None or trace_name not in source:
continue
component_value = source[trace_name]
if isinstance(component_value, dict) and not component_value:
continue
values.append(_serialize_component_boundary(component_value))
break

if not values:
return False, None
return True, values[0] if len(boundary_traces) == 1 else values

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Scalar/list collapse should be based on how many values were actually found, not how many components were marked.

values[0] if len(boundary_traces) == 1 else values (Line 81) uses the count of registered boundary trace names, not the count of values that actually survived the empty-dict skip at Line 74-75. If two components are marked on the same side (e.g. two output-marked components) but one of them produces {} for a given execution (common with conditional/router branches), only one value ends up in values, yet the function still returns it wrapped in a single-element list instead of collapsing to a scalar — because len(boundary_traces) == 2. This breaks the "stable evaluator-addressable scalar" contract the PR is meant to guarantee, since the same flow can non-deterministically return a scalar or a one-item list for output/input depending on which branch executed.

None of the added tests exercise this "mixed marked components, one contributes nothing" case, so it currently slips through.

🐛 Proposed fix
     if not values:
         return False, None
-    return True, values[0] if len(boundary_traces) == 1 else values
+    return True, values[0] if len(values) == 1 else values

Consider also adding a regression test in test_langfuse_v3_compatibility.py covering two marked output components where one yields {}.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _trace_boundary_value(
component_values: dict[str, Any],
boundary_traces: dict[str, str],
*,
fallback_component_values: dict[str, Any] | None = None,
prefer_fallback_trace_ids: set[str] | None = None,
) -> tuple[bool, Any]:
"""Return marked graph-boundary outputs in deterministic component-id order."""
values = []
prefer_fallback_trace_ids = prefer_fallback_trace_ids or set()
for trace_id, trace_name in sorted(boundary_traces.items()):
sources = (
(fallback_component_values, component_values)
if trace_id in prefer_fallback_trace_ids
else (component_values, fallback_component_values)
)
for source in sources:
if source is None or trace_name not in source:
continue
component_value = source[trace_name]
if isinstance(component_value, dict) and not component_value:
continue
values.append(_serialize_component_boundary(component_value))
break
if not values:
return False, None
return True, values[0] if len(boundary_traces) == 1 else values
def _trace_boundary_value(
component_values: dict[str, Any],
boundary_traces: dict[str, str],
*,
fallback_component_values: dict[str, Any] | None = None,
prefer_fallback_trace_ids: set[str] | None = None,
) -> tuple[bool, Any]:
"""Return marked graph-boundary outputs in deterministic component-id order."""
values = []
prefer_fallback_trace_ids = prefer_fallback_trace_ids or set()
for trace_id, trace_name in sorted(boundary_traces.items()):
sources = (
(fallback_component_values, component_values)
if trace_id in prefer_fallback_trace_ids
else (component_values, fallback_component_values)
)
for source in sources:
if source is None or trace_name not in source:
continue
component_value = source[trace_name]
if isinstance(component_value, dict) and not component_value:
continue
values.append(_serialize_component_boundary(component_value))
break
if not values:
return False, None
return True, values[0] if len(values) == 1 else values
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/base/langflow/services/tracing/langfuse.py` around lines 54 - 81,
Update _trace_boundary_value to collapse the result based on the number of
serialized values actually collected: return the sole value when len(values) ==
1, otherwise return the values list. Preserve the existing empty-result
behavior, and add a regression test in test_langfuse_v3_compatibility.py for two
marked output components where one produces an empty dict.

@erichare
erichare deleted the branch release-1.11.0 July 15, 2026 18:36
@erichare erichare closed this Jul 15, 2026
@erichare erichare reopened this Jul 15, 2026
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 15, 2026
@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.61905% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 61.19%. Comparing base (4a880f2) to head (a5e7386).
⚠️ Report is 3 commits behind head on release-1.11.0.

Files with missing lines Patch % Lines
...backend/base/langflow/services/tracing/langfuse.py 97.61% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                 @@
##           release-1.11.0   #14089      +/-   ##
==================================================
+ Coverage           60.87%   61.19%   +0.31%     
==================================================
  Files                2375     2426      +51     
  Lines              234025   236020    +1995     
  Branches            32939    35781    +2842     
==================================================
+ Hits               142472   144441    +1969     
- Misses              89842    89866      +24     
- Partials             1711     1713       +2     
Flag Coverage Δ
backend 67.84% <97.61%> (+0.05%) ⬆️
frontend 59.93% <ø> (+0.48%) ⬆️
lfx 59.28% <ø> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...backend/base/langflow/services/tracing/langfuse.py 88.70% <97.61%> (+1.90%) ⬆️

... and 177 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 46%
46.92% (66202/141080) 70.11% (9220/13149) 44.91% (1514/3371)

Unit Test Results

Tests Skipped Failures Errors Time
5391 0 💤 0 ❌ 0 🔥 20m 11s ⏱️

@erichare
erichare merged commit 43f52df into release-1.11.0 Jul 15, 2026
120 of 121 checks passed
@erichare
erichare deleted the fix/release-1.11.0-langfuse-trace-io-jsonpath branch July 15, 2026 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant