feat(prometheus): add litellm_total_overhead_latency_metric (SDK overhead + guardrails) - #31454
Conversation
…head + guardrails) litellm_overhead_latency_metric only covers the SDK wrapper window and excludes proxy guardrails. Add a histogram that sums SDK overhead plus pre/post-call guardrail durations (during-call excluded since it runs concurrently with the LLM call), recorded next to the existing overhead metric. Same labels and buckets, no existing metric changes.
Greptile SummaryThis PR adds
Confidence Score: 5/5Safe to merge — the change is additive (new metric only), no existing metric values or logging paths are altered, and all previously identified edge cases are handled correctly. The three previously flagged issues (walrus-operator zero-overhead gate, GuardrailMode dict TypeError, and list-mode during_call exclusion) are all resolved in this revision. The _set_total_overhead_metric helper is now called outside the SDK-overhead gate, the mode resolution strips non-string values before set membership tests, and only the two unambiguously additive modes (pre_call, post_call) are counted. No production behavior is changed for existing metrics. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/integrations/prometheus.py | Adds litellm_total_overhead_latency_metric histogram and supporting helpers; addresses all previously flagged edge cases (zero SDK overhead gate, GuardrailMode dict TypeError, list modes with during_call, logging_only/MCP exclusions). |
| litellm/types/integrations/prometheus.py | Registers the new metric name in DEFINED_PROMETHEUS_METRICS and PrometheusMetricLabels with the same label set as litellm_overhead_latency_metric; straightforward and correct. |
| tests/test_litellm/integrations/test_prometheus_total_overhead.py | 12 unit tests covering helper correctness, edge cases (dict mode, list mode, zero overhead, no guardrails), and histogram registration; all use mocks with no real network calls. |
Reviews (3): Last reviewed commit: "fix(prometheus): avoid TypeError on Guar..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…ead metric Address review feedback on litellm_total_overhead_latency_metric. Switch _get_guardrail_overhead_seconds to an allowlist (pre_call and post_call only) so a list-typed guardrail_mode such as [pre_call, during_call] is excluded, and logging_only plus MCP-specific modes are not counted since they do not block the user-facing response. Record the metric via _set_total_overhead_metric outside the SDK-overhead walrus gate, so guardrail-only overhead is still captured when litellm_overhead_time_ms is 0 or absent. Add unit tests for list modes, logging_only and MCP exclusion, and the zero or absent SDK-overhead gate.
|
Thanks for the review. Addressed all three points in
The existing |
…lper guardrail_mode can be a GuardrailMode TypedDict (a plain dict at runtime) for enterprise Mode-based guardrails that do not pass an explicit event_type. The set comprehension in _get_guardrail_overhead_seconds put that dict into a set, raising TypeError unhashable type dict, which aborted async_log_success_event and skipped later metric updates. Resolve each mode to its string value and keep only strings, so dict and None modes are ignored safely. Add regression tests for a dict-typed mode and a dict nested in a list mode.
|
Addressed the
Added regression tests (both would raise under the previous code):
All 12 tests in |
…ead helper Some guardrails (e.g. xecguard) assign a single dict to guardrail_information instead of a list. Iterating it yielded dict keys (strings) and raised AttributeError on info.get(...), aborting the success-metrics path. Normalize a dict to a one-item list and skip non-dict entries. Add a regression test for the dict-shaped payload.
Relevant issues
Surfaces LiteLLM's own per-request internal overhead, including guardrails, as a single Prometheus metric, so dashboards can read it directly instead of subtracting two unrelated percentiles of separate metrics.
Pre-Submission checklist
make test-unit@greptileaiand received a Confidence Score of at least 4/5Type
New Feature
Changes
LiteLLM already exposes
litellm_overhead_latency_metric, but it only measures the SDK wrapper window: it readslitellm_overhead_time_ms = (end_time - start_time) - llm_api_duration, andend_timeis captured the instant the provider response returns into the wrapper (litellm/utils.py, right afterresult = await original_function(...)). Proxy guardrails run outside that window (pre-call before the wrapper starts, post-call afterend_time, during-call concurrently with the LLM call), so no existing metric reflects guardrail latency as part of LiteLLM's overhead. There was no single number for "how much latency does LiteLLM itself add around the call, including guardrails".This PR adds a new histogram,
litellm_total_overhead_latency_metric, recorded once per successful request alongside the existing overhead metric inlitellm/integrations/prometheus.py:A new
_get_guardrail_overhead_secondshelper sums the guardrail durations already recorded on theStandardLoggingPayloadand excludesduring_call(moderation) guardrails, because those run concurrently with the LLM API call (asyncio.gather); their wall-clock time overlaps the provider call and is not additive overhead, so counting it would over-report.The metric reuses the exact label set and latency buckets of
litellm_overhead_latency_metric, and is registered inlitellm/types/integrations/prometheus.py(DEFINED_PROMETHEUS_METRICSandPrometheusMetricLabels) so the label machinery and metric-name validation accept it. No existing metric's value or math is changed.Key additions:
Because it is a histogram (same buckets as the other latency metrics), p95/p99 are available server side and aggregate correctly across replicas:
Tests and tooling
Added
tests/test_litellm/integrations/test_prometheus_total_overhead.py(4 tests): the helper sums pre and post and excludesduring_call; tolerates plain-stringguardrail_mode(post serialization) and missing durations; returns0.0with no guardrails; and the histogram registers in the Prometheus registry. The existingtest_prometheus_metric_name_consistency.pyandtest_prometheus_missing_metrics.pysuites (which enumerate every defined metric) pass with the new metric, confirming no name or label collision.Proof
Computed value and live
/metricsscrape (250 ms SDK overhead plus pre 100 ms plus post 50 ms guardrails; during-call 500 ms correctly excluded, total 0.4s):Unit tests: