mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-26 01:12:21 +00:00
fix(langfuse): keep trace_public true when a guardrail span is exported
Langfuse folds langfuse.trace.public across every observation in the trace and reads a missing attribute as false, so a guardrail child span without the flag turned a trace_public: true request private on Langfuse Cloud. Child spans now repeat the generation's value Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
5192f92eb5
commit
2dbbea5b6b
2 changed files with 36 additions and 1 deletions
|
|
@ -178,12 +178,23 @@ def start_child_span(
|
|||
The server derives the trace's name and I/O from every observation marked
|
||||
root, last start time wins, so only the generation may claim root. Nesting
|
||||
the rest under it keeps a post-call guardrail from rewriting the trace.
|
||||
|
||||
The trace's ``public`` flag is folded the same way, with a missing attribute
|
||||
read as ``False``, so the child repeats the generation's value.
|
||||
"""
|
||||
parent_span: Final = parent._otel_span # pyright: ignore[reportPrivateUsage] # the wrapper exposes no public span handle
|
||||
otel_span: Final = client._otel_tracer.start_span( # pyright: ignore[reportPrivateUsage] # only route to a historical start time
|
||||
name=name,
|
||||
context=otel_trace.set_span_in_context(parent._otel_span), # pyright: ignore[reportPrivateUsage] # the wrapper exposes no public span handle
|
||||
context=otel_trace.set_span_in_context(parent_span),
|
||||
start_time=to_unix_nanos(start_time),
|
||||
)
|
||||
public: Final = (
|
||||
parent_span.attributes.get(PUBLIC_ATTRIBUTE)
|
||||
if isinstance(parent_span, ReadableSpan) and parent_span.attributes is not None
|
||||
else None
|
||||
)
|
||||
if public is not None:
|
||||
otel_span.set_attribute(PUBLIC_ATTRIBUTE, public)
|
||||
return LangfuseSpan(otel_span=otel_span, langfuse_client=client, **attributes) # pyright: ignore[reportArgumentType] # kwargs-ok: callback-built params, v2 accepted the same shapes
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -227,6 +227,30 @@ def test_trace_public_flag_is_absent_when_not_requested(client):
|
|||
assert PUBLIC_ATTRIBUTE not in _only_span(exporter, "gen").attributes
|
||||
|
||||
|
||||
@pytest.mark.parametrize("public", [True, False, None], ids=["public", "private", "unset"])
|
||||
def test_child_span_repeats_the_generation_public_flag(client, public):
|
||||
"""The server folds ``public`` across observations and reads a missing value as False.
|
||||
|
||||
A guardrail span without the flag turned a ``trace_public: true`` request private on Langfuse Cloud.
|
||||
"""
|
||||
lf, exporter = client
|
||||
context, claim_root = open_trace_context(client=lf, trace_id="c" * 32, parent_observation_id=None)
|
||||
generation = start_generation(
|
||||
client=lf,
|
||||
context=context,
|
||||
name="gen",
|
||||
start_time=CALL_START,
|
||||
claim_trace_root=claim_root,
|
||||
public=public,
|
||||
attributes={},
|
||||
)
|
||||
start_child_span(client=lf, parent=generation, name="guardrail", start_time=CALL_END, attributes={}).end()
|
||||
generation.end(end_time=to_unix_nanos(CALL_END))
|
||||
lf.flush()
|
||||
|
||||
assert _only_span(exporter, "guardrail").attributes.get(PUBLIC_ATTRIBUTE) is public
|
||||
|
||||
|
||||
def test_request_release_beats_the_client_wide_release(monkeypatch):
|
||||
"""A client configured with its own release must not overwrite trace_release."""
|
||||
monkeypatch.setenv("LANGFUSE_RELEASE", "client-wide-release")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue