mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-23 05:34:41 +00:00
fix(otel): retain operation metadata when message logging is off
This commit is contained in:
parent
33d908e0ae
commit
6de7816b6a
2 changed files with 78 additions and 14 deletions
|
|
@ -2462,6 +2462,21 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger):
|
|||
value=standard_logging_payload["call_type"],
|
||||
)
|
||||
|
||||
operation_name: Final = (
|
||||
self._gen_ai_operation_name(kwargs)
|
||||
if self._gen_ai_semconv_latest_experimental
|
||||
else (
|
||||
"chat"
|
||||
if standard_logging_payload.get("call_type") == "completion"
|
||||
else standard_logging_payload.get("call_type") or "chat"
|
||||
)
|
||||
)
|
||||
self.safe_set_attribute(
|
||||
span=span,
|
||||
key=SpanAttributes.GEN_AI_OPERATION_NAME.value,
|
||||
value=operation_name,
|
||||
)
|
||||
|
||||
# The Generative AI Provider: Azure, OpenAI, etc.
|
||||
provider_name: Final = litellm_params.get("custom_llm_provider", "Unknown")
|
||||
# Latest-experimental semconv replaced gen_ai.system with
|
||||
|
|
@ -2631,20 +2646,6 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger):
|
|||
value=safe_dumps(transformed_system_instructions),
|
||||
)
|
||||
|
||||
if self._gen_ai_semconv_latest_experimental:
|
||||
operation_name = self._gen_ai_operation_name(kwargs)
|
||||
else:
|
||||
operation_name = (
|
||||
"chat"
|
||||
if standard_logging_payload.get("call_type") == "completion"
|
||||
else standard_logging_payload.get("call_type") or "chat"
|
||||
)
|
||||
self.safe_set_attribute(
|
||||
span=span,
|
||||
key=SpanAttributes.GEN_AI_OPERATION_NAME.value,
|
||||
value=operation_name,
|
||||
)
|
||||
|
||||
if standard_logging_payload.get("request_id"):
|
||||
self.safe_set_attribute(
|
||||
span=span,
|
||||
|
|
|
|||
|
|
@ -884,6 +884,69 @@ class TestOpenTelemetryCaptureMessageContent(unittest.TestCase):
|
|||
self.assertTrue(kept._capture_in_event())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("experimental", [False, True])
|
||||
@pytest.mark.parametrize(
|
||||
("capture_mode", "global_redact"),
|
||||
[("SPAN_AND_EVENT", True), ("NO_CONTENT", False), ("EVENT_ONLY", False), ("SPAN_AND_EVENT", False)],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
("call_type", "legacy_operation", "experimental_operation"),
|
||||
[("completion", "chat", "chat"), ("acompletion", "acompletion", "chat"), ("embedding", "embedding", "embeddings")],
|
||||
)
|
||||
def test_operation_name_is_metadata_independent_of_message_capture(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
experimental: bool,
|
||||
capture_mode: str,
|
||||
global_redact: bool,
|
||||
call_type: str,
|
||||
legacy_operation: str,
|
||||
experimental_operation: str,
|
||||
) -> None:
|
||||
monkeypatch.setattr(litellm, "turn_off_message_logging", global_redact)
|
||||
monkeypatch.setattr(litellm, "service_callback", [])
|
||||
monkeypatch.setattr(proxy_server, "open_telemetry_logger", None)
|
||||
monkeypatch.setenv("OTEL_SEMCONV_STABILITY_OPT_IN", "")
|
||||
span_exporter: Final = InMemorySpanExporter()
|
||||
provider: Final = TracerProvider()
|
||||
provider.add_span_processor(SimpleSpanProcessor(span_exporter))
|
||||
handler: Final = OpenTelemetry(
|
||||
config=OpenTelemetryConfig(
|
||||
exporter=span_exporter,
|
||||
capture_message_content=capture_mode,
|
||||
semconv_stability_opt_in={OTELSemconvCategory.GEN_AI_LATEST_EXPERIMENTAL} if experimental else set(),
|
||||
),
|
||||
tracer_provider=provider,
|
||||
)
|
||||
kwargs: Final = {
|
||||
"model": "test-model",
|
||||
"call_type": call_type,
|
||||
"messages": [{"role": "user", "content": "private prompt"}],
|
||||
"litellm_params": {"custom_llm_provider": "openai"},
|
||||
"standard_logging_object": {"call_type": call_type, "metadata": {}},
|
||||
}
|
||||
response: Final = {
|
||||
"model": "test-model",
|
||||
"choices": [{"message": {"role": "assistant", "content": "private response"}, "finish_reason": "stop"}],
|
||||
"usage": {"prompt_tokens": 3, "completion_tokens": 2, "total_tokens": 5},
|
||||
}
|
||||
with handler.tracer.start_as_current_span("test") as span:
|
||||
handler.set_attributes(span, kwargs, response)
|
||||
attributes: Final = span_exporter.get_finished_spans()[0].attributes
|
||||
provider.shutdown()
|
||||
assert attributes is not None
|
||||
assert attributes[SpanAttributes.GEN_AI_OPERATION_NAME.value] == (
|
||||
experimental_operation if experimental else legacy_operation
|
||||
)
|
||||
assert attributes[SpanAttributes.GEN_AI_USAGE_INPUT_TOKENS.value] == 3
|
||||
assert attributes[SpanAttributes.GEN_AI_USAGE_OUTPUT_TOKENS.value] == 2
|
||||
assert (SpanAttributes.GEN_AI_INPUT_MESSAGES.value in attributes) == (
|
||||
capture_mode == "SPAN_AND_EVENT" and not global_redact
|
||||
)
|
||||
assert (SpanAttributes.GEN_AI_OUTPUT_MESSAGES.value in attributes) == (
|
||||
capture_mode == "SPAN_AND_EVENT" and not global_redact
|
||||
)
|
||||
|
||||
|
||||
class TestOpenTelemetrySemconvStability(unittest.TestCase):
|
||||
"""OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental opts into
|
||||
semconv-conformant span shape (name, kind, no raw_gen_ai_request child)."""
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue