mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-15 09:12:18 +00:00
Merge 0041a3708f into 527d882508
This commit is contained in:
commit
9db8e855b4
6 changed files with 78 additions and 2 deletions
|
|
@ -58,6 +58,7 @@ from litellm.integrations.otel.model.semconv import (
|
|||
Metric,
|
||||
Network,
|
||||
NetworkTransport,
|
||||
OpenAI,
|
||||
RpcSystem,
|
||||
Server,
|
||||
resolve_operation,
|
||||
|
|
@ -102,6 +103,7 @@ __all__ = [
|
|||
"Metric",
|
||||
"Network",
|
||||
"NetworkTransport",
|
||||
"OpenAI",
|
||||
"OpenTelemetryV2Config",
|
||||
"ProxyRequestSpanData",
|
||||
"RequestContext",
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ from litellm.integrations.otel.model.semconv import (
|
|||
GenAI,
|
||||
JsonRpc,
|
||||
LiteLLM,
|
||||
OpenAI,
|
||||
RpcSystem,
|
||||
Server,
|
||||
)
|
||||
|
|
@ -63,6 +64,8 @@ class GenAIMapper:
|
|||
GenAI.RESPONSE_ID: lambda d: d.response_id,
|
||||
GenAI.RESPONSE_FINISH_REASONS: lambda d: list(d.finish_reasons) if d.finish_reasons else None,
|
||||
GenAI.RESPONSE_TIME_TO_FIRST_CHUNK: lambda d: d.time_to_first_chunk_seconds,
|
||||
OpenAI.REQUEST_SERVICE_TIER: lambda d: d.requested_service_tier,
|
||||
OpenAI.RESPONSE_SERVICE_TIER: lambda d: d.served_service_tier,
|
||||
GenAI.USAGE_INPUT_TOKENS: lambda d: d.usage.input_tokens,
|
||||
GenAI.USAGE_OUTPUT_TOKENS: lambda d: d.usage.output_tokens,
|
||||
GenAI.USAGE_CACHE_CREATION_INPUT_TOKENS: lambda d: d.usage.cache_creation_input_tokens,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ from litellm.integrations.otel.model.utils import (
|
|||
as_str_tuple,
|
||||
)
|
||||
from litellm.integrations.otel.routing import RoutingAttributeValue, routing_decision_attributes
|
||||
from litellm.litellm_core_utils.service_tier_utils import get_requested_service_tier, get_served_service_tier
|
||||
|
||||
# ``RequestIdentity`` and the request-metadata translation now live in
|
||||
# :mod:`metadata`; re-exported here so existing ``model.payloads`` imports keep
|
||||
|
|
@ -450,6 +451,8 @@ class LLMCallSpanData:
|
|||
routing_attributes: Mapping[str, RoutingAttributeValue] = field(default_factory=lambda: MappingProxyType({}))
|
||||
cache_hit: bool | None = None
|
||||
saved_cache_cost: float | None = None
|
||||
requested_service_tier: str | None = None
|
||||
served_service_tier: str | None = None
|
||||
|
||||
@classmethod
|
||||
def from_standard_logging_payload(
|
||||
|
|
@ -531,6 +534,8 @@ class LLMCallSpanData:
|
|||
routing_attributes=routing_decision_attributes(decision),
|
||||
cache_hit=cache_hit,
|
||||
saved_cache_cost=as_float(payload.get("saved_cache_cost")) if cache_hit else None, # pyright: ignore[reportUnknownMemberType] # TypedDict .get on a str key is partially unknown
|
||||
requested_service_tier=get_requested_service_tier(payload),
|
||||
served_service_tier=get_served_service_tier(payload),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,14 @@ class GenAI:
|
|||
PROMPT_NAME: Final = "gen_ai.prompt.name"
|
||||
|
||||
|
||||
class OpenAI:
|
||||
"""OpenAI-namespace keys from the GenAI semconv registry. Carries the service tier for every
|
||||
provider, not only OpenAI."""
|
||||
|
||||
REQUEST_SERVICE_TIER: Final = "openai.request.service_tier"
|
||||
RESPONSE_SERVICE_TIER: Final = "openai.response.service_tier"
|
||||
|
||||
|
||||
class MCP:
|
||||
"""OTel GenAI MCP (Model Context Protocol) span-attribute keys.
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,64 @@ def test_llm_call_span_golden():
|
|||
assert span.status.status_code is StatusCode.UNSET
|
||||
|
||||
|
||||
_REQUEST_TIER: Final = "openai.request.service_tier"
|
||||
_RESPONSE_TIER: Final = "openai.response.service_tier"
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("overrides", "expected_request_tier", "expected_response_tier"),
|
||||
[
|
||||
pytest.param(
|
||||
{
|
||||
"model_parameters": {"service_tier": "flex"},
|
||||
"response": {"id": "resp_1", "service_tier": "flex", "choices": [{"finish_reason": "stop"}]},
|
||||
},
|
||||
"flex",
|
||||
"flex",
|
||||
id="requested-and-served",
|
||||
),
|
||||
pytest.param(
|
||||
{
|
||||
"model_parameters": {"service_tier": "auto"},
|
||||
"response": {"id": "resp_1", "service_tier": "priority", "choices": [{"finish_reason": "stop"}]},
|
||||
},
|
||||
"auto",
|
||||
"priority",
|
||||
id="provider-picks-the-tier",
|
||||
),
|
||||
pytest.param(
|
||||
{"metadata": {"usage_object": {"service_tier": "standard"}}},
|
||||
None,
|
||||
"standard",
|
||||
id="served-tier-on-usage",
|
||||
),
|
||||
pytest.param(
|
||||
{"model_parameters": {"service_tier": "priority"}},
|
||||
"priority",
|
||||
None,
|
||||
id="requested-only",
|
||||
),
|
||||
pytest.param(
|
||||
{"model_parameters": {"service_tier": "not-a-tier"}},
|
||||
None,
|
||||
None,
|
||||
id="unknown-requested-tier",
|
||||
),
|
||||
pytest.param({}, None, None, id="no-tier"),
|
||||
],
|
||||
)
|
||||
def test_llm_call_span_service_tier(
|
||||
overrides: Mapping[str, object], expected_request_tier: str | None, expected_response_tier: str | None
|
||||
) -> None:
|
||||
engine, exporter = _engine()
|
||||
|
||||
engine.emit(SpanRole.LLM_CALL, LLMCallSpanData.from_standard_logging_payload(_payload(**overrides)))
|
||||
|
||||
(span,) = exporter.get_finished_spans()
|
||||
assert span.attributes.get(_REQUEST_TIER) == expected_request_tier
|
||||
assert span.attributes.get(_RESPONSE_TIER) == expected_response_tier
|
||||
|
||||
|
||||
def test_legacy_dual_emit_on():
|
||||
engine, exporter = _engine(legacy_compat=True)
|
||||
engine.emit(SpanRole.LLM_CALL, LLMCallSpanData.from_standard_logging_payload(_payload()))
|
||||
|
|
|
|||
|
|
@ -159,11 +159,11 @@ def _all_constants(cls):
|
|||
|
||||
|
||||
def test_attribute_keys_are_unique_across_namespaces():
|
||||
from litellm.integrations.otel import MCP, Client, JsonRpc, LiteLLMError, Network
|
||||
from litellm.integrations.otel import MCP, Client, JsonRpc, LiteLLMError, Network, OpenAI
|
||||
|
||||
# prefixes are allowed to be substrings; exact keys must not collide.
|
||||
exact = set()
|
||||
for cls in (GenAI, Error, LiteLLMError, Server, HTTP, DB, MCP, JsonRpc, Network, Client):
|
||||
for cls in (GenAI, Error, LiteLLMError, Server, HTTP, DB, MCP, JsonRpc, Network, Client, OpenAI):
|
||||
for key in _all_constants(cls):
|
||||
assert key not in exact, f"duplicate attribute key {key}"
|
||||
exact.add(key)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue