mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-31 06:56:59 +00:00
Merge 53fdddfc75 into a392a39627
This commit is contained in:
commit
23604ea472
4 changed files with 40 additions and 3 deletions
|
|
@ -1747,7 +1747,7 @@ class OpenTelemetry(OTELGenAISemconvMixin, CustomLogger):
|
|||
self._token_usage_histogram.record(usage.get("completion_tokens", 0), attributes=out_attrs)
|
||||
|
||||
cost: Final = kwargs.get("response_cost")
|
||||
if self._cost_histogram and cost:
|
||||
if self._cost_histogram and cost is not None:
|
||||
self._cost_histogram.record(cost, attributes=common_attrs)
|
||||
|
||||
# Record latency metrics (TTFT, TPOT, and Total Generation Time)
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ class GenAIMetricRecorder:
|
|||
self._record_token_usage(response_obj, common_attrs)
|
||||
|
||||
cost: Final = kwargs.get("response_cost")
|
||||
if cost:
|
||||
if cost is not None:
|
||||
self._metrics.token_cost.record(cost, attributes=common_attrs)
|
||||
|
||||
self._record_time_to_first_token(kwargs, common_attrs)
|
||||
|
|
|
|||
|
|
@ -193,6 +193,26 @@ def test_all_six_metrics_emitted_when_enabled():
|
|||
assert cost_points[0].sum == pytest.approx(RESPONSE_COST)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("response_cost", [0, 0.0123, None])
|
||||
def test_cost_metric_records_known_values_including_zero(response_cost: float | None):
|
||||
reader = InMemoryMetricReader()
|
||||
logger = _logger(reader, enable_metrics=True)
|
||||
kwargs, response_obj, start, end = _build_call()
|
||||
kwargs["response_cost"] = response_cost
|
||||
asyncio.run(logger.async_log_success_event(kwargs, response_obj, start, end))
|
||||
|
||||
metrics = _metrics_by_name(reader)
|
||||
cost_points = metrics.get(TOKEN_COST, [])
|
||||
if response_cost is None:
|
||||
assert cost_points == []
|
||||
return
|
||||
|
||||
assert len(cost_points) == 1
|
||||
assert cost_points[0].sum == pytest.approx(response_cost)
|
||||
assert cost_points[0].count == 1
|
||||
assert cost_points[0].attributes == metrics[OPERATION_DURATION][0].attributes
|
||||
|
||||
|
||||
def test_time_to_first_token_is_streaming_only():
|
||||
"""time_to_first_token is gated on streaming: a non-streaming call emits the
|
||||
other five metrics but never that one."""
|
||||
|
|
|
|||
|
|
@ -6255,7 +6255,7 @@ class TestOpenTelemetryMetricAttributeFiltering(unittest.TestCase):
|
|||
response_obj = json.load(f)
|
||||
return kwargs, response_obj
|
||||
|
||||
def _record(self, attributes):
|
||||
def _record(self, attributes, response_cost: float | None = None):
|
||||
"""Run a real success hook with metrics enabled and return the reader."""
|
||||
metric_reader = InMemoryMetricReader()
|
||||
meter_provider = MeterProvider(metric_readers=[metric_reader])
|
||||
|
|
@ -6271,11 +6271,28 @@ class TestOpenTelemetryMetricAttributeFiltering(unittest.TestCase):
|
|||
otel.tracer = tracer_provider.get_tracer(__name__)
|
||||
|
||||
kwargs, response_obj = self._load_fixtures()
|
||||
if response_cost is not None:
|
||||
kwargs["response_cost"] = response_cost
|
||||
start = datetime.utcnow()
|
||||
end = start + timedelta(seconds=1)
|
||||
otel._handle_success(kwargs, response_obj, start, end)
|
||||
return metric_reader
|
||||
|
||||
def test_zero_cost_is_recorded(self):
|
||||
reader = self._record(None, response_cost=0)
|
||||
data = reader.get_metrics_data()
|
||||
cost_metrics = tuple(
|
||||
metric
|
||||
for resource_metrics in data.resource_metrics
|
||||
for scope_metrics in resource_metrics.scope_metrics
|
||||
for metric in scope_metrics.metrics
|
||||
if metric.name == "gen_ai.usage.cost"
|
||||
)
|
||||
|
||||
self.assertEqual(len(cost_metrics), 1)
|
||||
self.assertEqual(cost_metrics[0].data.data_points[0].sum, 0)
|
||||
self.assertEqual(cost_metrics[0].data.data_points[0].count, 1)
|
||||
|
||||
def _keysets(self, reader, metric_name):
|
||||
"""Attribute-key sets, one per recorded data point of `metric_name`."""
|
||||
deadline = time.time() + self.POLL_TIMEOUT
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue