chore(ci): promote internal staging to main#31477
Merged
Merged
Conversation
…=true test reaches rpm path #29256 made auth=true pass-through routes deny-by-default unless the key/team has allowed_passthrough_routes configured, but this integration test was not updated. The test key had no allowlist, so the auth=true parametrizations (rpm_limit=0 -> expect 429, rpm_limit=2 -> expect 207) now hit the 403 gate in auth before reaching the rpm/forwarding logic they mean to exercise. Grant the test key allowed_passthrough_routes for /api/public/ingestion so it clears the gate. Also removes a latent order-dependency: the case only passed locally when an earlier (auth=false) parametrization registered the route first; under worker isolation (CI xdist) it failed with 403.
Adds the OCR 3 model (mistral-ocr-2512) released 2025-12-18 to both the root and bundled backup cost maps at $2 / 1000 pages and $3 / 1000 annotated pages, mirroring the existing Mistral OCR entries. Regresses the pricing in both maps and verifies completion_cost scales per page.
…ols and errors (adopted from #31286) (#31461) * fix(guardrails): stop Generic Guardrail API 500 on built-in tools Requests carrying built-in tools (code_interpreter, file_search, ...) crashed the Generic Guardrail with a 500. GenericGuardrailAPIRequest.tools validated each tool against ChatCompletionToolParam, whose base TypedDict requires a function block, so a tool like {"type": "code_interpreter"} raised a Pydantic ValidationError before the request was ever sent. Type the field with a permissive GuardrailToolParam model (type required, extra=allow) so built-in tools validate and their config is forwarded to the guardrail intact instead of being stripped. * feat(guardrails): add complete fail-open (fail_on_error) to Generic Guardrail The Generic Guardrail already honored unreachable_fallback, which fails open only on network-unreachable errors. This wires up the existing generic fail_on_error config (so far implemented only by Model Armor) so that fail_on_error=false degrades any guardrail error to a critical-log warning and lets the request proceed as if the guardrail were absent. Only a valid guardrail response can act: a parsed BLOCKED decision still raises, while endpoint errors, malformed responses, and internal serialization or validation errors all fall through when fail_on_error=false. To cover that last class, the request construction now runs inside the protected block, so the kind of validation error that previously surfaced as a 500 is caught here too. Defaults to true (fail closed), matching today's behavior; turning it off is an explicit availability-over-security choice and is logged at critical level on every bypass. * test(guardrails): cover fail_on_error on the response path The existing fail_on_error tests all drive the request path. Add response-path (input_type=response) coverage: an endpoint error proceeds unchanged under fail_on_error=false, and a valid BLOCKED decision still raises. Guards against a future regression that special-cases input_type in the error handling. * style(guardrails): black-format the fail-open guard expression CI runs black (line-length 88) over litellm/; the unreachable_fail_open assignment exceeded it. Wrap it to satisfy the formatter. * fix(guardrails): validate tools into GuardrailToolParam at the call site Changing the request field to List[GuardrailToolParam] left the construction passing List[ChatCompletionToolParam] (list is invariant), which tripped the basedpyright reportArgumentType budget gate. Validate each tool explicitly, which is what Pydantic did implicitly, so the types line up with no Any or suppression and the serialized payload is unchanged. * fix(guardrails): make fail-open log message accurate for non-network errors The fail-open path is now shared by fail_on_error, so it fires for any guardrail error, not just unreachability. The log said 'unreachable' even for an HTTP 400 or a malformed response; reword to 'error' (the status code and exception are already logged). Addresses the Greptile review's only finding. * fix(guardrails): align GenericGuardrailAPIResponse.tools with GuardrailToolParam Greptile flagged that the request side moved to GuardrailToolParam but the response side still annotated tools as List[ChatCompletionToolParam], which mandates a function block and contradicts the new built-in-tools support. Update the response annotation (and the now-unused import) so the two sides agree. Runtime is unchanged; from_dict stores the raw dicts and the only consumer assigns through to GenericGuardrailAPIInputs without inspecting the elements. --------- Co-authored-by: Itay Ovadia <[email protected]>
#31267 switched the build backend to maturin to bundle the Rust OCR bridge into the litellm wheel. On the release runner, maturin builds with `--compatibility off` and emits a `cp312-cp312-linux_x86_64` wheel. PyPI only accepts `manylinux*` Linux wheels, so the upload is rejected with `400 Bad Request - unsupported platform tag 'linux_x86_64'`, which blocked the 1.91.0.dev2 publish. Revert `[build-system]` to the pure-Python `uv_build` backend and restore `[tool.uv.build-backend]` so the release once again produces a universal `litellm-<ver>-py3-none-any.whl` that installs on every OS / arch / Python. The Rust bridge stays optional: `litellm/rust_bridge/loader.py` already falls back gracefully when `_native` is absent (`try/except ImportError`), so runtime behavior is unchanged - the native module is simply not bundled. Rust acceleration can ship as a separate platform-specific package later.
test(pass-through): fix langfuse auth=true test broken by allowed_passthrough_routes gate
…#31443) /team/member_add could not set budget_duration on an individual member budget. add_new_member created the budget row with only max_budget and allowed_models, and TeamMemberAddRequest had no budget_duration field, so a member added with an explicit per-member budget while the team ran a recurring member budget got a lifetime cap instead of a recurring allowance. Thread budget_duration from TeamMemberAddRequest through _process_team_members into add_new_member, and pull the member-budget resolution into a helper that writes budget_duration plus a computed budget_reset_at. When only a budget_duration is supplied and the team has a default member budget, the default is cloned and its reset window overridden so the member keeps the default's max_budget rather than becoming uncapped; a duration with no team default creates a window-only budget. Invalid durations are rejected with a 400 before any DB write, symmetric with /team/member_update. The available-team self-join bypass only grants the ability to join, so reject per-member budget and model controls (max_budget_in_team, budget_duration, allowed_models) for non-admin self-join callers in _validate_team_member_add_permissions, before any DB write. Otherwise a self-joining non-admin could set their own cap, reset window, or model scope past the team default; admins, team admins, and org admins are unaffected and a clean self-join still inherits the team default budget. Resolves LIT-4052
…te COUNT(*) (#31423) The spend-logs UI list endpoint (/spend/logs/ui, /spend/logs/v2) ran a standalone SELECT COUNT(*) before the page query to compute total_pages. On sharded engines like YugabyteDB a COUNT(*) is a distributed RPC that contacts every tablet leader and aggregates partial results regardless of row count, so it hits the distributed RPC timeout and the logs tab 500s even on a one-minute window with a couple of rows. The startTime range cannot prune tablets because rows hash to tablets on request_id, not startTime. Fold the count into the same scan as the page data with COUNT(*) OVER () and read total off the returned rows, dropping the helper column before serialisation. One distributed scan per page load instead of two; the response shape is unchanged. An empty page carries no count row, in which case the total is zero. Resolves LIT-4027
…ken (#31412) When STS rejects a web identity token with InvalidIdentityToken (the "Incorrect token audience" case), litellm propagated the raw botocore error, which never names the aud LiteLLM actually sent. Diagnosing an audience mismatch then required enabling LITELLM_LOG=DEBUG on the prod instance, which degrades performance. _auth_with_web_identity_token now catches InvalidIdentityTokenException, decodes the public aud/iss claims of the resolved JWT without verifying its signature (no secret is read), and raises an AwsAuthError that preserves the STS reason and names the token audience and issuer, so the mismatch is visible from the error alone. Resolves LIT-4026
…cy (#31414) litellm_guardrail_latency_seconds was only emitted for pre-call guardrails. during_call_hook and post_call_success_hook ran guardrails without recording any latency, so during-call and post-call guardrail time was invisible in the metric and leaked into litellm_overhead_latency_metric, making the documented "subtract guardrail latency from overhead" workaround under-report total guardrail time. Extract the find-the-PrometheusLogger-and-record step into _emit_guardrail_metrics and add _run_guardrail_with_metrics, a single wrapper that times a guardrail coroutine, classifies its outcome (success / intervened / error), enriches any raised HTTPException, and records the latency under the given hook_type. Route the pre-call emit, during_call_hook, and post_call_success_hook through it so every guardrail phase contributes to the metric the same way. Resolves LIT-3999
…31418) The auth builder cached the team object under the raw `valid_token.team_id`, while `get_team_object`, `_cache_team_object`, and `_update_team_cache` all read and write under `team_id:{id}`. The raw-key write was therefore never served back, and on a non-team (personal) key, whose team_id is None, the original unguarded version passed a None key straight to the cache layer; the in-memory cache tolerates None keys but Redis rejects them with a NoneType key error, so with `enable_redis_auth_cache: true` the team object never reached the L2 cache and every request fell back to Postgres. Write under the canonical `team_id:{id}` key, keeping the existing guard that skips the write when team_id is None. Add a regression test that drives the real auth builder for a team-scoped key against an in-memory cache and asserts the team object is served back under `team_id:{id}` and never under the raw team_id or a None key. Resolves LIT-4000
…_wheel fix(build): restore pure-Python uv_build backend to unblock PyPI publish
Contributor
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
|
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
| _PROXY_SensitiveDataRoutingHandler, | ||
| ) | ||
| from litellm.integrations.custom_logger import CustomLogger | ||
| from litellm.integrations.prometheus import PrometheusLogger |
ryan-crabbe-berri
approved these changes
Jun 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewDelays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
Type
🆕 New Feature
🐛 Bug Fix
🧹 Refactoring
📖 Documentation
🚄 Infrastructure
✅ Test
Changes