Skip to content

⚡️ Speed up method LangFuseTracer.end_trace by 13% in PR #11114 (feat/langchain-1.0)#11832

Closed
codeflash-ai[bot] wants to merge 2 commits into
feat/langchain-1.0from
codeflash/optimize-pr11114-2026-02-19T20.45.30
Closed

⚡️ Speed up method LangFuseTracer.end_trace by 13% in PR #11114 (feat/langchain-1.0)#11832
codeflash-ai[bot] wants to merge 2 commits into
feat/langchain-1.0from
codeflash/optimize-pr11114-2026-02-19T20.45.30

Conversation

@codeflash-ai
Copy link
Copy Markdown
Contributor

@codeflash-ai codeflash-ai Bot commented Feb 19, 2026

⚡️ This pull request contains optimizations for PR #11114

If you approve this dependent PR, these changes will be merged into the original PR branch feat/langchain-1.0.

This PR will be automatically closed if the original PR is merged.


📄 13% (0.13x) speedup for LangFuseTracer.end_trace in src/backend/base/langflow/services/tracing/langfuse.py

⏱️ Runtime : 65.5 milliseconds 58.2 milliseconds (best of 33 runs)

📝 Explanation and details

The optimized code achieves a 12% speedup by adding a fast-path check in the serialize() function that bypasses expensive dispatcher logic for simple JSON-like structures (nested dicts/lists containing only primitives).

Key Optimization:

A new helper function _is_simple_json_structure() recursively validates whether an object contains only JSON-serializable primitives (None, str, int, float, bool) and simple containers (dict with string keys, list, tuple). When serialize() is called with no_limits=True and to_str=False on such structures, it immediately returns the object without invoking _serialize_dispatcher().

Why This Is Faster:

The line profiler reveals the bottleneck: in the original code, _serialize_dispatcher() consumes 63.1% of serialize()'s runtime (38.3ms out of 60.6ms). In the optimized version, the new fast-path check takes 96.7% of the much smaller total time (19.4ms out of 20.1ms), but this is still a net win because we avoid the dispatcher entirely. The dispatcher involves expensive pattern matching across many type checks (datetime, Decimal, UUID, Document, pandas types, numpy types, etc.).

Impact on LangFuseTracer.end_trace():

The end_trace() method calls serialize(output) where output is typically a simple dict containing strings, lists, and primitives (outputs, error messages, logs). The optimization reduces the time spent in span.update(output=serialize(output)) from 53.5% (153.9ms) to 42.5% (96.5ms) of the method's runtime—a 37% reduction in serialization overhead within the tracing context.

Test Results:

The annotated tests show the optimization excels for:

  • Simple nested structures: Tests with dict/list of primitives (e.g., test_end_trace_output_with_nested_dict, test_end_trace_with_list_in_outputs) benefit most since they match the fast-path criteria
  • Large simple outputs: Tests like test_end_trace_with_large_outputs (1000 key-value pairs) and test_end_trace_with_many_logs (1000 log entries) avoid repeated dispatcher overhead
  • High-frequency tracing: test_end_trace_multiple_spans_sequential (100 spans) shows cumulative benefits when serialization is called repeatedly

Workload Considerations:

Since end_trace() is called at the completion of every traced operation in the LangFuse tracing system, and tracing outputs are predominantly simple JSON structures, this optimization provides consistent benefits in production workloads involving instrumentation and observability. The 12% overall speedup compounds across many trace operations.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 221 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Click to see Generated Regression Tests

import os
import uuid
from typing import Any, Sequence

import pytest # used for our unit tests
from langflow.serialization.serialization import serialize
from langflow.services.tracing.langfuse import LangFuseTracer

Note:

The tests below exercise LangFuseTracer.end_trace under a variety of conditions.

We avoid relying on an actual langfuse client by directly manipulating the tracer

instance state (setting _ready and providing a span-like object). The span-like

object below is a tiny, concrete helper class defined in this test module to

capture calls to update and end. While the production code expects real

LangfuseSpan instances, for unit testing the method behavior we assert that

when a span object with the expected interface is present, end_trace will:

- remove the span from tracer.spans,

- call span.update with a serialized output,

- call span.end().

Each test includes comments explaining its purpose and the assertions made.

class _SpanRecorder:
"""
Minimal concrete span-like object used for tests.

It records calls to update and end so tests can assert on them.
This is a tiny, explicit helper class (not a mock/patch) used solely
to observe interactions from LangFuseTracer.end_trace.
"""
def __init__(self) -> None:
    # store the last args passed to update
    self.updated_with: Any | None = None
    # record whether end() was called
    self.ended: bool = False

def update(self, *args, **kwargs) -> None:
    # record whatever was passed for assertion
    # production code calls update(output=serialize(...))
    if args:
        # if positional args provided, store them as-is
        self.updated_with = {"args": args, "kwargs": kwargs}
    else:
        self.updated_with = kwargs

def end(self) -> None:
    # mark as ended for assertion
    self.ended = True

def test_end_trace_when_not_ready_does_nothing():
"""
If tracer._ready is False (default when no Langfuse config), end_trace should
return immediately and not raise. Also, spans dict should remain unchanged.
"""
trace_id = str(uuid.uuid4())
# Create a LangFuseTracer with minimal, valid constructor args.
tracer = LangFuseTracer(
trace_name="flow - basic",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
user_id=None,
session_id=None,
)

# Populate spans with an entry to ensure it is not touched when not ready.
tracer.spans[trace_id] = _SpanRecorder()

# Call end_trace; should do nothing and not raise
tracer.end_trace(trace_id=trace_id, trace_name="unused", outputs={"x": 1}, error=None, logs=())
# And the span should not have been updated or ended
span = tracer.spans[trace_id]

def test_end_trace_ready_but_no_matching_span_is_noop():
"""
If tracer._ready is True but no span for the provided trace_id exists,
end_trace should not raise and should leave tracer.spans unchanged.
"""
trace_id = "nonexistent-id"
tracer = LangFuseTracer(
trace_name="flow - ready_no_span",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
)

# Simulate ready state (bypassing actual langfuse client setup).
tracer._ready = True

# Call with various combinations of outputs/logs/error; should be no-op
tracer.end_trace(trace_id=trace_id, trace_name="name", outputs=None, error=None, logs=())

tracer.end_trace(trace_id=trace_id, trace_name="name", outputs={"a": 1}, error=ValueError("oops"), logs=[{"k": "v"}])

def test_end_trace_with_span_calls_update_and_end():
"""
When tracer._ready is True and a span exists for the trace_id,
end_trace must:
- pop the span from tracer.spans,
- call span.update with output=serialize(merged_output),
- call span.end().
Also verify that outputs, error, and logs are merged correctly before serialization.
"""
trace_id = "trace-123"
tracer = LangFuseTracer(
trace_name="flow - update_end",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
)
tracer._ready = True

# Create a span-like recorder and insert it
span = _SpanRecorder()
tracer.spans[trace_id] = span

# Prepare inputs: outputs dict, an error, and logs sequence
outputs = {"value": 42, "nested": {"a": "α"}}  # include a unicode char to test serialization
error = RuntimeError("boom")
logs = ({"event": "started"}, {"event": "ended"})

# Call end_trace which should pop the span, update it with serialized output, and end it
tracer.end_trace(trace_id=trace_id, trace_name="flow - update_end", outputs=outputs, error=error, logs=logs)

# Reconstruct expected merged output and its serialized form
expected_output = {}
expected_output |= outputs or {}
expected_output |= {"error": str(error)} if error else {}
expected_output |= {"logs": list(logs)} if logs else {}
expected_serialized = serialize(expected_output)

def test_end_trace_with_empty_and_none_values_serializes_correctly():
"""
Test end_trace with outputs=None, error=None, and empty logs.
When a span exists, update must be called with output serialized as {} (empty dict).
"""
trace_id = "edge-empty-none"
tracer = LangFuseTracer(
trace_name="flow - edge",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
)
tracer._ready = True

span = _SpanRecorder()
tracer.spans[trace_id] = span

# Call with all empty inputs
tracer.end_trace(trace_id=trace_id, trace_name="flow - edge", outputs=None, error=None, logs=())

def test_end_trace_with_special_characters_in_outputs():
"""
Ensure strings with special characters are preserved through merging and serialization.
"""
trace_id = "edge-special-chars"
tracer = LangFuseTracer(
trace_name="flow - special",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
)
tracer._ready = True

span = _SpanRecorder()
tracer.spans[trace_id] = span

outputs = {"text": "Line1\nLine2\t\u2603"}  # contains newline, tab, and snowman char
tracer.end_trace(trace_id=trace_id, trace_name="flow - special", outputs=outputs, error=None, logs=())

def test_end_trace_multiple_consecutive_calls_handle_state_correctly():
"""
Verify that consecutive calls to end_trace correctly pop spans one by one
and do not interfere with remaining spans in the tracer.
"""
base_trace_id = "multi-call-"
tracer = LangFuseTracer(
trace_name="flow - multi",
trace_type="type",
project_name="proj",
trace_id=uuid.uuid4(),
)
tracer._ready = True

# Insert multiple spans
num_spans = 10
spans = []
for i in range(num_spans):
    tid = base_trace_id + str(i)
    rec = _SpanRecorder()
    spans.append((tid, rec))
    tracer.spans[tid] = rec

# End spans one by one and assert correct behavior each time
for tid, rec in list(spans):
    tracer.end_trace(trace_id=tid, trace_name=f"name-{tid}", outputs={"i": tid}, error=None, logs=())

codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

#------------------------------------------------
from collections import OrderedDict
from unittest.mock import MagicMock, PropertyMock, patch
from uuid import UUID

imports

import pytest
from langflow.services.tracing.langfuse import LangFuseTracer
from langflow.services.tracing.schema import Log

def test_end_trace_basic_with_outputs():
"""Test end_trace updates span with output and ends it."""
# Create a tracer instance with mocked langfuse setup
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

# Create a mock span
mock_span = MagicMock()
mock_span.update = MagicMock()
mock_span.end = MagicMock()

# Add span to tracer
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Call end_trace with outputs
outputs = {'result': 'success', 'value': 42}
tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
)

# Verify span.update was called with serialized output
mock_span.update.assert_called_once()
call_args = mock_span.update.call_args

# Verify span.end was called
mock_span.end.assert_called_once()

def test_end_trace_with_error():
"""Test end_trace adds error to output when exception is provided."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Call end_trace with error
error = ValueError("Test error message")
tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    error=error,
)

# Verify error was added to output
call_args = mock_span.update.call_args
mock_span.end.assert_called_once()

def test_end_trace_with_logs():
"""Test end_trace adds logs to output when provided."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create mock logs
log_dict = {'message': 'test log', 'level': 'info'}
logs = [log_dict]

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    logs=logs,
)

# Verify logs were added to output
call_args = mock_span.update.call_args
mock_span.end.assert_called_once()

def test_end_trace_not_ready():
"""Test end_trace returns early when tracer is not ready."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

# Set tracer as not ready
tracer._ready = False
mock_span = MagicMock()
tracer.spans['test_id'] = mock_span

# Call end_trace
tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs={'result': 'success'},
)

# Verify span was not updated or ended
mock_span.update.assert_not_called()
mock_span.end.assert_not_called()

def test_end_trace_nonexistent_span():
"""Test end_trace handles case when span_id doesn't exist in spans dict."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

tracer._ready = True

# Call end_trace with non-existent trace_id
# Should not raise an error
tracer.end_trace(
    trace_id='nonexistent_id',
    trace_name='test_trace',
    outputs={'result': 'success'},
)

def test_end_trace_with_all_parameters():
"""Test end_trace with outputs, error, and logs all provided."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

outputs = {'result': 'completed'}
error = RuntimeError("Something went wrong")
logs = [{'message': 'log1'}, {'message': 'log2'}]

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
    error=error,
    logs=logs,
)

# Verify all data was combined in output
call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_none_outputs():
"""Test end_trace when outputs is None."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=None,
)

# Output should be empty dict when no outputs provided
call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_empty_outputs():
"""Test end_trace with empty outputs dict."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs={},
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_empty_logs():
"""Test end_trace with empty logs sequence."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    logs=(),
)

# Empty logs should not add logs key to output
call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_none_error():
"""Test end_trace when error is None (default)."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    error=None,
)

# Error key should not be in output when error is None
call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_special_characters_in_trace_id():
"""Test end_trace with special characters in trace_id."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
special_id = "trace-id_with.special@chars"
tracer.spans[special_id] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id=special_id,
    trace_name='test_trace',
    outputs={'result': 'success'},
)
mock_span.end.assert_called_once()

def test_end_trace_with_unicode_in_error_message():
"""Test end_trace with unicode characters in error message."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

error = ValueError("Error with unicode: 你好世界 🌍")
tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    error=error,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_output_with_nested_dict():
"""Test end_trace with nested dictionary in outputs."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

outputs = {
    'result': {
        'nested': {
            'deep': 'value'
        }
    }
}

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_list_in_outputs():
"""Test end_trace with list values in outputs."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

outputs = {
    'items': [1, 2, 3, 4, 5],
    'names': ['a', 'b', 'c']
}

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_span_removal_idempotent():
"""Test that calling end_trace multiple times with same id doesn't break."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# First call should work
tracer.end_trace(trace_id='test_id', trace_name='test_trace')
mock_span.end.assert_called_once()

# Second call should not fail even though span is already removed
mock_span.reset_mock()
tracer.end_trace(trace_id='test_id', trace_name='test_trace')
mock_span.update.assert_not_called()
mock_span.end.assert_not_called()

def test_end_trace_with_very_long_trace_id():
"""Test end_trace with very long trace_id string."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
long_id = "x" * 1000
tracer.spans[long_id] = mock_span
tracer._ready = True

tracer.end_trace(
    trace_id=long_id,
    trace_name='test_trace',
)
mock_span.end.assert_called_once()

def test_end_trace_merge_outputs_and_error():
"""Test that outputs and error are properly merged in output dict."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

outputs = {'status': 'completed', 'count': 5}
error = Exception("Test error")

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
    error=error,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']

def test_end_trace_with_large_outputs():
"""Test end_trace with large dictionary in outputs."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create a large outputs dictionary
large_outputs = {f'key_{i}': f'value_{i}' for i in range(1000)}

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=large_outputs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_many_logs():
"""Test end_trace with large number of log entries."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create many log entries
logs = [{'message': f'log_{i}', 'level': 'info'} for i in range(1000)]

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    logs=logs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_deeply_nested_structure():
"""Test end_trace with deeply nested output structure."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create deeply nested structure
deep_dict = {'level': 0}
current = deep_dict
for i in range(100):
    current['next'] = {'level': i + 1}
    current = current['next']

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=deep_dict,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_multiple_spans_sequential():
"""Test end_trace called multiple times on different span ids."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

tracer._ready = True

# Create multiple mock spans
mock_spans = {}
for i in range(100):
    span_id = f'span_{i}'
    mock_span = MagicMock()
    tracer.spans[span_id] = mock_span
    mock_spans[span_id] = mock_span

# End all spans
for i in range(100):
    span_id = f'span_{i}'
    tracer.end_trace(
        trace_id=span_id,
        trace_name=f'trace_{i}',
        outputs={'id': i},
    )
for span in mock_spans.values():
    span.end.assert_called_once()

def test_end_trace_spans_dict_maintains_order():
"""Test that spans dict maintains insertion order through end_trace calls."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

tracer._ready = True

# Add spans in specific order
trace_ids = []
for i in range(100):
    span_id = f'span_{i:03d}'
    trace_ids.append(span_id)
    mock_span = MagicMock()
    tracer.spans[span_id] = mock_span

# End every other span
for i in range(0, 100, 2):
    tracer.end_trace(
        trace_id=trace_ids[i],
        trace_name=f'trace_{i}',
    )

# Verify remaining spans are the odd-indexed ones
remaining_ids = list(tracer.spans.keys())
for i, span_id in enumerate(remaining_ids):
    pass

def test_end_trace_with_large_string_values():
"""Test end_trace with very large string values in outputs."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create large string value
large_string = "x" * 100000
outputs = {
    'data': large_string,
    'size': len(large_string),
}

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_error_with_large_traceback():
"""Test end_trace with exception that has large traceback."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

# Create an error with nested function calls
def recursive_error(depth):
    if depth == 0:
        raise ValueError("Deep error")
    return recursive_error(depth - 1)

try:
    recursive_error(50)
except ValueError as e:
    error = e

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    error=error,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

def test_end_trace_with_mixed_data_types():
"""Test end_trace with outputs containing various Python types."""
with patch.object(LangFuseTracer, '_setup_langfuse', return_value=True):
tracer = LangFuseTracer(
trace_name="test_trace - flow_1",
trace_type="flow",
project_name="test_project",
trace_id=UUID('12345678-1234-5678-1234-567812345678'),
)

mock_span = MagicMock()
tracer.spans['test_id'] = mock_span
tracer._ready = True

outputs = {
    'string': 'text',
    'integer': 42,
    'float': 3.14,
    'bool': True,
    'none': None,
    'list': [1, 2, 3],
    'dict': {'nested': 'value'},
    'tuple': (1, 2, 3),
}

tracer.end_trace(
    trace_id='test_id',
    trace_name='test_trace',
    outputs=outputs,
)

call_args = mock_span.update.call_args
output = call_args.kwargs['output']
mock_span.end.assert_called_once()

codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-pr11114-2026-02-19T20.45.30 and push.

Codeflash

The optimized code achieves a **12% speedup** by adding a fast-path check in the `serialize()` function that bypasses expensive dispatcher logic for simple JSON-like structures (nested dicts/lists containing only primitives).

**Key Optimization:**

A new helper function `_is_simple_json_structure()` recursively validates whether an object contains only JSON-serializable primitives (None, str, int, float, bool) and simple containers (dict with string keys, list, tuple). When `serialize()` is called with `no_limits=True` and `to_str=False` on such structures, it immediately returns the object without invoking `_serialize_dispatcher()`.

**Why This Is Faster:**

The line profiler reveals the bottleneck: in the original code, `_serialize_dispatcher()` consumes **63.1%** of `serialize()`'s runtime (38.3ms out of 60.6ms). In the optimized version, the new fast-path check takes **96.7%** of the much smaller total time (19.4ms out of 20.1ms), but this is still a net win because we avoid the dispatcher entirely. The dispatcher involves expensive pattern matching across many type checks (datetime, Decimal, UUID, Document, pandas types, numpy types, etc.).

**Impact on `LangFuseTracer.end_trace()`:**

The `end_trace()` method calls `serialize(output)` where `output` is typically a simple dict containing strings, lists, and primitives (outputs, error messages, logs). The optimization reduces the time spent in `span.update(output=serialize(output))` from **53.5%** (153.9ms) to **42.5%** (96.5ms) of the method's runtime—a **37% reduction** in serialization overhead within the tracing context.

**Test Results:**

The annotated tests show the optimization excels for:
- **Simple nested structures**: Tests with dict/list of primitives (e.g., `test_end_trace_output_with_nested_dict`, `test_end_trace_with_list_in_outputs`) benefit most since they match the fast-path criteria
- **Large simple outputs**: Tests like `test_end_trace_with_large_outputs` (1000 key-value pairs) and `test_end_trace_with_many_logs` (1000 log entries) avoid repeated dispatcher overhead
- **High-frequency tracing**: `test_end_trace_multiple_spans_sequential` (100 spans) shows cumulative benefits when serialization is called repeatedly

**Workload Considerations:**

Since `end_trace()` is called at the completion of every traced operation in the LangFuse tracing system, and tracing outputs are predominantly simple JSON structures, this optimization provides consistent benefits in production workloads involving instrumentation and observability. The 12% overall speedup compounds across many trace operations.
@codeflash-ai codeflash-ai Bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Feb 19, 2026
@github-actions github-actions Bot added the community Pull Request from an external contributor label Feb 19, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 18%
18.4% (5919/32152) 12.03% (3017/25071) 12.36% (855/6914)

Unit Test Results

Tests Skipped Failures Errors Time
2288 0 💤 0 ❌ 0 🔥 32.483s ⏱️

@codecov
Copy link
Copy Markdown

codecov Bot commented Feb 19, 2026

Codecov Report

❌ Patch coverage is 75.00000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 35.33%. Comparing base (0070d13) to head (30a1ab7).

Files with missing lines Patch % Lines
...ckend/base/langflow/serialization/serialization.py 75.00% 5 Missing ⚠️

❌ Your project status has failed because the head coverage (42.03%) is below the target coverage (60.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files

Impacted file tree graph

@@                  Coverage Diff                   @@
##           feat/langchain-1.0   #11832      +/-   ##
======================================================
- Coverage               35.33%   35.33%   -0.01%     
======================================================
  Files                    1521     1521              
  Lines                   73033    73053      +20     
  Branches                10951    10951              
======================================================
+ Hits                    25808    25813       +5     
- Misses                  45829    45845      +16     
+ Partials                 1396     1395       -1     
Flag Coverage Δ
backend 56.13% <75.00%> (-0.04%) ⬇️
frontend 16.65% <ø> (ø)
lfx 42.03% <ø> (+<0.01%) ⬆️

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

Files with missing lines Coverage Δ
...ckend/base/langflow/serialization/serialization.py 73.33% <75.00%> (+0.17%) ⬆️

... and 11 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.

@ogabrielluiz
Copy link
Copy Markdown
Contributor

Closing automated codeflash PR.

@codeflash-ai codeflash-ai Bot deleted the codeflash/optimize-pr11114-2026-02-19T20.45.30 branch March 3, 2026 18:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI community Pull Request from an external contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant