Commit graph

726 commits

Author SHA1 Message Date
Mateo Wang
b3898dfd85
Merge pull request #36815 from BerriAI/litellm_otel_gen_ai_system_none
fix(otel): drop None metric and event attributes before OTLP export
2026-09-16 13:56:05 -07:00
Yassin Kortam
4bf04b22b0
Merge pull request #41472 from BerriAI/litellm_customer_budget_prometheus_metrics
feat(prometheus): add customer (end_user) budget gauges
2026-09-16 13:46:57 -07:00
mateo-berri
4c79abfa0c Merge commit '79fc5153d3' into litellm_otel_gen_ai_system_none
# Conflicts:
#	litellm/integrations/opentelemetry.py
2026-09-16 13:38:40 -07:00
yassin
81d57cbad7 fix(prometheus): skip customer budget cache read when end_user tracking is off
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 20:23:59 +00:00
yassin
e7d537442d fix(prometheus): read the cached customer row for request-time budget gauges
The request path used get_end_user_object, which falls back to a database
lookup on a cache miss. Read the LiteLLM_EndUserTable row auth already cached
instead, with the default budget already attached, and leave misses to the
periodic refresh

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 20:13:52 +00:00
mateo-berri
a2724e7f15 fix(otel): drop None attributes before they reach the OTLP encoder
The metric attribute filter now removes every attribute whose value is
None, and the content and inference-details events pass their attributes
through drop_none before emitting, so a call with no provider label or no
model name never hands the OTLP exporter a NoneType attribute. This closes
the gen_ai.request.model report on #36759 the same way the gen_ai.system
one was closed, and the regression tests cover both keys.
2026-09-16 12:52:14 -07:00
yassin
8de51dfaab test(otel): describe which request metadata the pre-call seed reads
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:25:40 +00:00
yassin
c22916377f test(prometheus): cover customer budget series expiry by end_user ttl
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:22:30 +00:00
yassin
30f02aa6da fix(otel): read only the caller's requester_metadata snapshot in the v2 pre-call hook
The pre-call hook passed the proxy's whole per-request metadata dict into the
request identity, so proxy-owned siblings such as requester_ip_address were
promoted alongside the caller's keys. Only the requester_metadata mapping is
read now, keyed under its wrapper, which keeps the default allowlist behaviour
unchanged

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:11:25 +00:00
yassin
2821ba9667 fix(prometheus): refresh default-budget customers and honor independent customer gauges
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 19:08:42 +00:00
yassin
8c89cff0e0 feat(prometheus): add customer (end_user) budget gauges
Mirror the key, team, user and org budget gauges for customer objects with
litellm_remaining_customer_budget_metric, litellm_customer_max_budget_metric
and litellm_customer_budget_remaining_hours_metric. The gauges carry only the
end_user label, are emitted after each request and from the startup budget
refresh for every customer with a budget attached, and reuse the
enable_end_user_cost_tracking_prometheus_only opt-in and end_user series caps

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:39:56 +00:00
yassin
8a059cd4b4 fix(otel): promote nested metadata keys under the caller's dotted path
Strip only the proxy's requester_metadata. wrapper from an allowlisted key so
requester_metadata.trace_id lands as litellm.metadata.trace_id while other
dotted keys keep their full path and cannot collide on a shared leaf name

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:39:55 +00:00
yassin
17059564a8 feat(otel): promote nested request metadata keys to litellm.metadata.* span attributes
baggage_metadata_keys entries such as requester_metadata.trace_id now resolve the caller's nested metadata.trace_id and stamp it on the LLM-call span as litellm.metadata.trace_id, in both the OTEL v2 logger and the legacy OpenTelemetry callback. Nested metadata mappings are flattened to dotted paths, only allowlisted leaves are promoted, and the requester_metadata blob itself is never promoted

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-16 18:08:14 +00:00
mateo-berri
ba971d69d6 test(otel): type the providerless-call helpers and inject the logger provider 2026-09-15 17:51:28 -07:00
mateo-berri
8b0d28c219 Merge remote-tracking branch 'origin/main' into litellm_otel_gen_ai_system_none
# Conflicts:
#	litellm/integrations/opentelemetry.py
#	litellm/integrations/opentelemetry_utils/gen_ai_semconv.py
#	tests/test_litellm/integrations/test_opentelemetry.py
2026-09-15 17:46:41 -07:00
Yassin Kortam
226b1e1bb9
Merge pull request #41288 from BerriAI/litellm_langsmith_preserve_events_during_flush
fix(langsmith): keep events appended during an in-flight flush instead of clearing them
2026-09-15 15:06:36 -07:00
mrinal
4c179f2f59 test(langsmith): type the flush race test and cancel its periodic task
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 21:17:36 +00:00
mrinal
4a8986dd72 fix(langsmith): keep events appended during an in-flight flush instead of clearing them
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 20:38:35 +00:00
Yassin Kortam
cdb4eaac59
Merge pull request #40262 from BerriAI/litellm_lit_7190_inline_token_counts
fix(proxy): run the remaining inline token counts off the event loop
2026-09-15 13:38:00 -07:00
yassin
b9dd397746 fix(prometheus): count 401 auth failures in litellm_proxy_failed_requests_metric
Invalid or unknown virtual keys were filtered out of the proxy failed and total
request counters entirely. Count them with hashed_api_key unset so caller-chosen
key strings cannot create unbounded label series, and normalize the request
route on the auth failure path so dynamic path ids do not leak into the route
label either.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-15 02:09:00 +00:00
yucheng-berri
b67137b67f
Merge pull request #40702 from BerriAI/litellm_model_armor_logging_only_streaming
feat(model_armor): logging_only mode scans completed streams after delivery
2026-09-14 17:23:32 -07:00
yassin
845596063d fix(prometheus): label pre-call rate limit failures with the resolved api_provider
Pre-call limiters reject before a deployment is attached to request_data, so
the failure hook could not resolve api_provider for router aliases and emitted
api_provider="None" on litellm_proxy_failed_requests_metric_total and
litellm_proxy_total_requests_metric_total. Fall back to the provider the
limiter already resolved onto RateLimitError.llm_provider, keeping request
data as the first source and ignoring the proxy placeholder.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-14 19:50:21 +00:00
yucheng-berri
cae4a65545
Merge pull request #40562 from BerriAI/litellm_otel_openinference_message_cap
fix(otel): cap per-index OpenInference message attributes span-wide
2026-09-14 11:55:37 -07:00
yucheng
34e702c571 test(otel): build the clipped-blob conversation without mutating the payload
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-14 17:46:18 +00:00
joshua-berri
49b2d71057 test(guardrails): type the native lifecycle logging_only test double
Some checks failed
ai-gateway image / ai-gateway release image (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Terraform Provider / gofmt, vet, build, test (push) Has been cancelled
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Has been cancelled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-13 08:47:47 +00:00
yucheng
c65ace9164 Merge remote-tracking branch 'origin/main' into litellm_model_armor_logging_only_streaming 2026-09-13 04:24:44 +00:00
Mateo Wang
e1bff56f0b
Merge pull request #40772 from BerriAI/litellm_lit6982_bucket_config_includes
fix(proxy): resolve config include directives for bucket-hosted configs
2026-09-12 15:21:56 -07:00
Mateo Wang
d72ae3b9a2
Merge pull request #39895 from BerriAI/litellm_deflake_20260905
test: deflake redis semantic cache sys.modules leak, LangSmith init loop patch, wall-clock stagger assertion, and zombie grandchild check in the fake prisma cli
2026-09-12 15:20:26 -07:00
devin-ai-integration[bot]
5b36de4646
fix(guardrails): log mask when a guardrail adds request keys (#40882)
* fix(guardrails): log mask when a guardrail adds request keys

_inputs_were_modified only compared keys present in the pre-hook baseline, so a
guardrail that injected a new key such as tools was logged as allow. Compare over
the union of both key sets, and narrow the pre_call return value to the same
prompt-bearing keys the baseline holds so passthrough stays allow.

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(guardrails): snapshot apply_guardrail inputs before the hook mutates them

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-12 14:39:40 -07:00
devin-ai-integration[bot]
1c61c2606e
perf(proxy): one MGET and one pipeline for post-call spend counters, no team/user/org refetch on the response path (#40841)
* perf(auth): prefetch user, team, membership, org and project in one MGET, one query and one pipeline

Auth read each object with its own Redis GET and, on a miss, its own DB
query, then the admission spend counters with one GET each. The prefetch
warms every entry the checks read with one MGET, one raw query for the
Redis misses and one pipeline write, and a per-request batch serves the
spend counter reads from one MGET. The per-object getters stay the
readers and the fallback, so enforcement does not depend on the prefetch

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(auth): keep prefetch and spend batch collections immutable

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* perf(auth): let the cold spend-counter reseed reuse the admission MGET instead of one GET per counter

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* perf(auth): prefetch referenced auth objects only after the key's model access check passes

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(auth): give the prefetch-ordering test's patches their test-quality reasons

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(auth): move the real-Postgres prefetch join test to the proxy_behavior shard

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(auth): read NULL nested permission and budget lists as [] in the prefetch join

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* perf(proxy): batch post-call spend counter reads and carry budget state through the request

Post-call warm checks, reservation reads and reconcile reads for one request now go through a task-local spend counter batch: one MGET answers every counter, successful increments write their result back into the batch so no second Redis read follows, and invalidation forgets the key. RedisCache.async_increment sends INCRBYFLOAT and its TTL command in one pipeline round trip.

Auth pins frozen team, user and org budget snapshots on UserAPIKeyAuth, the pre-call setup writes them into the request metadata, and Prometheus reads them back instead of calling get_key_object, get_team_object, get_user_object and get_org_object on the response path. The getters stay as the fallback for requests that carried nothing (custom auth, unauthenticated routes, skipped checks).

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* perf(proxy): reconcile the budget reservation and the post-call warm checks from one MGET and one pipeline

A scope opened inside an open spend counter batch binds into it instead of starting its own, so the reservation reconcile and the post-call warm checks share the request's single MGET. The reconcile reads every reserved counter concurrently, sends the consistent adjustments in one INCRBYFLOAT+EXPIRE pipeline and settles a flushed or reseeded counter on its own afterwards, keeping the pre-call resize fail-closed. PendingSpendIncrement moves to spend_counter_batch so budget_reservation can build a pipeline without importing a private name

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore(proxy): drop the dataclass import left behind by the PendingSpendIncrement move

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(types): import Self from typing_extensions so the proxy imports on Python 3.10

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): use a neutral organization alias in the carried budget state tests

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(proxy): cover recorded and forgotten spend counter values in the request batch

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(caching): assert async_set_cache_pipeline_with_ttls keeps per-entry TTLs

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* refactor(proxy): type the reservation entry carried through reconcile adjustments

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(auth): map the model table's aliases column to model_aliases in the prefetch join and read user memberships the way get_user_object does

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-12 16:05:50 +00:00
mateo
441f863abb Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_deflake_20260905
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

# Conflicts:
#	tests/test_litellm/proxy/db/test_pgbouncer.py
2026-09-12 09:18:42 +00:00
devin-ai-integration[bot]
1fde15c1ec
fix(shadow-eval): skip hosted web search samples (#40827)
(cherry picked from commit a78cd2fe02)

Co-authored-by: Tin Chi Lo <tin@berri.ai>
2026-09-12 02:11:48 +00:00
devin-ai-integration[bot]
f84f986b4e
fix(guardrails): keep post_call guardrail info on streamed chat completions (#40806)
* fix(guardrails): sync logging_obj guardrail info on every record so post_call entries survive streamed chat completions

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(guardrails): hoist regression test imports to module scope

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 18:16:33 -07:00
ryan-crabbe-berri
50cd26cd9c
Merge pull request #40396 from jon-walton/litellm_user_budget_webhook_alerts
fix(proxy): emit internal user budget webhook alerts
2026-09-11 18:03:35 -07:00
devin-ai-integration[bot]
bf146e2cac
fix(otel v2): name Langfuse traces from the langfuse_trace_name header or metadata.trace_name (#40793)
* fix(otel v2): name Langfuse traces from the langfuse_trace_name header or metadata.trace_name

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(otel v2): type the named-request helper in the Langfuse logger tests

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 17:37:35 -07:00
yucheng
7a083353a8 Merge branch 'litellm_internal_staging' into litellm_model_armor_logging_only_streaming
Some checks are pending
ai-gateway image / ai-gateway release image (push) Waiting to run
LiteLLM Rust / rust-lint (push) Waiting to run
LiteLLM Rust / rust-test (push) Waiting to run
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 23:48:35 +00:00
devin-ai-integration[bot]
d78861bb29
fix(guardrails): stop logging the request payload as guardrail_response on pre_call hooks (#39699)
* fix(guardrails): stop logging the request payload as guardrail_response on pre_call hooks

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(guardrails): snapshot pre_call request before the hook so in-place edits log as mask

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(guardrails): log non-mapping pre_call hook results as mask instead of raising

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(guardrails): treat legacy functions and tool_choice edits as mask in pre_call logging

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(guardrails): log a pre_call rejection string as is instead of "mask"

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: shivam <shivam@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: yucheng <yucheng@berri.ai>
2026-09-11 16:44:56 -07:00
yucheng
a15309dfe8 refactor(otel): trim the message cap docstrings to one line each
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 23:32:44 +00:00
yucheng
a067557dae fix(otel): index the opener and the latest prompt turns, not the oldest
A value length limit clips the input.value blob, so the per-index keys are the
only untruncated copy of a message. Indexing the leading prompt messages dropped
the live user turn from every span attribute on long conversations. Keep message
0 and the most recent turns under the same span-wide budget, original indices
preserved, reply reservation unchanged

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 23:16:53 +00:00
devin-ai-integration[bot]
09b694894d
fix(datadog_llm_obs): keep tool call and result structure under redaction and emit tool output tokens (#40666)
* fix(datadog_llm_obs): keep tool call and result structure under redaction and emit tool output tokens

Under datadog_llm_observability_params.turn_off_message_logging the span kept only one role plus "redacted-by-litellm" per message, so Datadog showed Tool Call 0, Tool Result 0 and no tool output token data. The shared CustomLogger hook collapsed the messages before the callback ran, and the Datadog redaction then dropped tool_calls and tool_results.

The Datadog callback now opts out of the shared message collapse (redacts_messages_itself) and redacts its own normalized messages, keeping roles, tool names, ids and types while replacing content, arguments and results. Tool result tokens are counted with litellm.token_counter before redaction and shipped as the tool_output_tokens metric. Other callbacks keep the inherited behavior.

Resolves LIT-7545

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* fix(datadog_llm_obs): drop explanatory docstrings from the redaction change

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* chore(ui): regenerate schema.d.ts for the classifier descriptions changed in #40655

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

---------

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 13:12:14 -07:00
Mateo Wang
5c821828ea
Merge pull request #39516 from BerriAI/litellm_vector_store_surface_retrieval_failure
fix(vector-stores): surface retrieval failures to the API caller
2026-09-11 11:46:42 -07:00
joshua-berri
2ca29a9a91 fix(model_armor): gate apply_guardrail raise to non-logging_only and require native guardrails to declare logging_only
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 09:33:57 +00:00
mateo
0199cbcc52 Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_deflake_20260905 2026-09-11 09:18:30 +00:00
joshua-berri
135ec00b27 feat(model_armor): logging_only mode scans completed streams after delivery
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-11 08:06:09 +00:00
Jon Walton
f358ebbc08
Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_user_budget_webhook_alerts 2026-09-11 09:49:16 +08:00
devin-ai-integration[bot]
960fc4b114
feat(newrelic): export team max and remaining budget gauges to the Metric API (#40542)
Co-authored-by: yassin <yassin@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-09-10 17:16:15 -07:00
Yinon Kahta
6b27a16e67 feat(pointfive): add batching logger callback 2026-09-10 14:02:35 +03:00
Yinon Kahta
85d10c8558 feat(pointfive): add presigned upload client 2026-09-10 14:02:35 +03:00
Yinon Kahta
6d7b3a82ff feat(pointfive): add gzipped ndjson batch encoding 2026-09-10 14:02:35 +03:00
mateo
c956c24ade Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_deflake_20260905
Some checks are pending
Terraform Provider / gofmt, vet, build, test (push) Waiting to run
Terraform Provider / Provider endpoints vs proxy OpenAPI schema (push) Waiting to run
2026-09-10 09:19:52 +00:00