Commit Graph
151 Commits
Author SHA1 Message Date
userandClaude Opus 4.7 5bafa8b3a2 Drop dep bumps + black-26 reformat to clear fork CI policy
PR was blocked by .github/workflows/guard-fork-dependencies.yml: fork PRs
cannot modify uv.lock. Reverting:

- uv.lock + pyproject.toml black bump (24.10.0 -> 26.3.1) and the 295
  files of mechanical Black 26 reformat coupled to it
- pyproject.toml diskcache extra change (kept the runtime mitigation in
  litellm/caching/disk_cache.py via JSONDisk)

Kept:
- Dockerfile cache narrowing (drops ~660 MB of uv build cache that
  surfaced cached setuptools as CVE findings)
- litellm/caching/disk_cache.py: dc.JSONDisk to neutralize CVE-2025-69872
- ui/litellm-dashboard/package-lock.json + litellm-js/spend-logs/package-lock.json:
  next/postcss/hono/uuid CVE bumps (these are not blocked by the fork guard)
- tests/test_litellm/caching/test_disk_cache.py
- tests/code_coverage_tests/liccheck.ini: harmless black authorization

Black + gitpython + langchain dep upgrades will need a follow-up from a
maintainer pushing a branch in the canonical BerriAI/litellm repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 23:04:52 +00:00
user 0d790d87ef Merge remote-tracking branch 'upstream/litellm_internal_staging' into cve-sweep-2026-05
# Conflicts:
#	tests/llm_responses_api_testing/conftest.py
#	tests/llm_translation/conftest.py
#	tests/logging_callback_tests/conftest.py
2026-05-05 22:43:13 +00:00
7e13256fee test: add 24hr Redis-backed VCR cache to additional test suites (#27159)
* test: add 24hr Redis-backed VCR cache to additional test suites

Extracts the existing llm_translation VCR plumbing into a reusable helper
(tests/_vcr_conftest_common.py) and wires it into the conftest.py files
of the test directories listed in LIT-2787:

  audio_tests, batches_tests, guardrails_tests, image_gen_tests,
  litellm_utils_tests, local_testing, logging_callback_tests,
  pass_through_unit_tests, router_unit_tests, unified_google_tests

The same helper is also adopted by the pre-existing llm_translation and
llm_responses_api_testing conftests to remove the copy-pasted VCR setup.

Each consuming conftest:
- registers the Redis persister via pytest_recording_configure
- auto-marks collected tests with pytest.mark.vcr (skipping respx-using
  files where applicable, since respx and vcrpy both patch httpx)
- gates cassette writes on test success via _vcr_outcome_gate

The cache is opt-in via CASSETTE_REDIS_URL; when unset, VCR is disabled
and tests hit live providers as before. LITELLM_VCR_DISABLE=1 still
forces a bypass for ad-hoc local runs.

Test directories that run LiteLLM proxy in Docker (build_and_test,
proxy_logging_guardrails_model_info_tests, proxy_store_model_in_db_tests)
are intentionally not included: VCR.py patches the in-process httpx
transport and cannot intercept calls made from inside a Docker container.
The installing_litellm_on_python* jobs make no LLM calls and don't
benefit from caching.

https://linear.app/litellm-ai/issue/LIT-2787/add-24hr-caching-to-additional-test-suites

* test(vcr): add safe-body matcher to handle JSONL and binary request bodies

vcrpy's stock body matcher inspects Content-Type and unconditionally
runs json.loads on application/json bodies. JSON Lines payloads (used
by the Bedrock batch S3 PUT and other upload paths) crash that with
json.JSONDecodeError: Extra data, before the matcher can return
'not a match'.

This was the root cause of the batches_testing CI job failing on
test_async_create_file once VCR auto-marking was applied to the
batches_tests directory.

Add a conservative byte-equality body matcher and use it in place of
'body' in the shared match_on tuple. The matcher is strictly more
conservative than vcrpy's default — the only thing it gives up is
'different JSON key order is treated as the same body', which doesn't
apply to deterministic litellm-built request payloads. It can never
produce a false positive that the default would have rejected, so
there is no cross-contamination risk.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): exclude tests that VCR replay actively breaks

A few tests are incompatible with cassette replay and were failing on
the latest CI run after VCR auto-marking was extended to local_testing
and logging_callback_tests:

- test_amazing_s3_logs.py (logging_callback_tests): the test asserts on
  a per-run response_id that should round-trip through a real S3
  PUT/LIST. vcrpy's boto3 stub intercepts the PUT and the LIST replays
  stale keys, so the freshly-generated id is never found.
- test_async_embedding_azure (logging_callback_tests) and
  test_amazing_sync_embedding (local_testing): the failure branches
  deliberately pass api_key='my-bad-key' to assert that the failure
  callback fires. We scrub auth headers from cassettes (so the bad-key
  request matches the prior good-key request), and vcrpy replays the
  recorded 200 — the failure callback never fires.
- test_assistants.py (local_testing): the OpenAI Assistants polling
  APIs mint fresh thread/run IDs every recording session and then poll
  until status=='completed'. Replays of those polled GETs can never
  match a freshly-generated run id, so every CI run effectively
  re-records and the suite blows past the 15m no_output_timeout.

Skip these from VCR auto-marking so they continue to hit live providers
as they did before this change. The remaining tests in each directory
still get cached.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): expand skip lists for second batch of incompatible tests

Followup to the previous commit. After re-running CI on the rebuilt
branch, three more tests surfaced as VCR-replay-incompatible:

- litellm_utils_testing :: test_get_valid_models_from_dynamic_api_key
  Calls GET /v1/models with api_key='123' to assert the result is empty.
  We scrub auth headers, so the bad-key request matches the prior
  good-key cassette and replays the recorded model list.
- litellm_utils_testing :: test_litellm_overhead.py
  Measures litellm_overhead_time_ms as a percentage of total wall-clock
  time. With cached responses the upstream 'network' time collapses to
  microseconds, blowing past the 40%% threshold the test asserts on.
  Skip the whole file (every parametrization is at risk).
- local_testing_part1 :: test_async_custom_handler_completion and
  test_async_custom_handler_embedding
  Same bad-key failure-callback pattern as the already-skipped
  test_amazing_sync_embedding.
- litellm_router_testing :: test_router_caching.py
  Asserts on litellm's own router-level response cache by comparing
  response1.id to response2.id across repeat upstream calls (test
  bypasses litellm cache via ttl=0 and expects upstream to return a
  *new* id). With VCR replay both upstream calls return the same
  cassette body, so the ids are identical. Skip the whole file.
- logging_callback_tests :: test_async_chat_azure (preemptive)
  Same shape as already-skipped test_async_embedding_azure; was masked
  by upstream OpenAI rate-limit failures on baseline.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): use item.path and tighten matcher docstring

- Replace pytest's deprecated item.fspath with item.path in
  apply_vcr_auto_marker_to_items so we don't emit deprecation
  warnings under pytest 8.
- Clarify _safe_body_matcher docstring to reflect actual behavior
  (direct == first, then UTF-8 bytes comparison, no repr fallback).

Addresses Greptile review feedback on PR #27159.

* test(vcr): swallow all RedisError on cassette save/load

Cassette persistence is strictly best-effort: any Redis-side failure
(connection blip, timeout, OutOfMemoryError when the maxmemory cap is
hit, READONLY replicas, etc.) should degrade to 'test passed but
cassette not cached' rather than fail the test on teardown.

Previously the persister only caught ConnectionError and TimeoutError,
so OutOfMemoryError — which Redis Cloud raises when the cassette cache
hits its memory cap and there are no evictable keys — propagated out of
vcrpy's autouse fixture and ERRORed otherwise-passing tests on
teardown. This caused the litellm_utils_testing CircleCI job to fail on
the latest commit's run, even though the underlying test was a unit
test that used mock_response and produced no real upstream traffic
(the cassette was dirtied by a background langfuse callback). The
rerun only succeeded because Redis evictions happened to free enough
room before the SET — i.e. it was timing-dependent flakiness.

Catch redis.exceptions.RedisError (the common base of all server- and
client-side Redis exceptions) on both save and load, and parametrize
the regression tests across ConnectionError, TimeoutError, and
OutOfMemoryError to pin the new behavior.

* test(vcr): surface cassette-cache failures with warnings + session banner

When the persister silently swallows a Redis OOM (or any RedisError) on
save/load there is otherwise no visible signal that the cache is
degraded — tests pass, the cassette just isn't persisted, and the next
session still hits the same Redis at the same near-cap memory.

Add three layers of observability so that failure mode is loud:

1. Per-process health counters ("save_failures", "load_failures", and
   the last error string for each), exposed via cassette_cache_health()
   and reset via reset_cassette_cache_health(). The persister
   increments these in addition to logging.

2. VCRCassetteCacheWarning (UserWarning subclass) emitted via
   warnings.warn() inside the persister's except block. Pytest's
   built-in warnings summary at session end automatically lists every
   such warning, so the failure is visible in CI logs without any
   conftest-level wiring.

3. Session-end banner via emit_cassette_cache_session_banner() and a
   stderr-fallback atexit handler registered from
   register_persister_if_enabled(). Two states:
     - red "VCR CASSETTE CACHE DEGRADED" when save_failures or
       load_failures > 0
     - yellow "VCR CASSETTE CACHE NEAR CAPACITY" (no failures, but
       used_memory >= 85% of maxmemory) so the next session knows
       the Redis is approaching OOM before any SET actually fails

Capacity comes from a best-effort INFO memory probe
(cassette_cache_capacity_snapshot) that returns None on any failure or
when maxmemory is uncapped. The atexit handler skips xdist workers so
only the controller emits.

Tests: parametrize the existing save/load swallow-error tests across
ConnectionError/TimeoutError/OutOfMemoryError, add direct tests for
the health counters and warning emission, and a new
test_vcr_conftest_common_banner.py covering banner output for every
state (silent/red/yellow/disabled/xdist-worker).

* test(vcr): bucket cassettes by API key fingerprint, drop bad-key skips

Tests that deliberately call an LLM API with a bad key (e.g. to assert
that the failure callback fires, or that check_valid_key returns False)
were being silently served the prior good-key cassette: we scrub the
real Authorization / x-api-key header from the cassette before storing
it, so a follow-up bad-key call is byte-identical to the good-key call
under the existing match_on tuple.

Add a 'key_fingerprint' custom matcher that distinguishes requests by
the SHA-256 of their API-key headers. The fingerprint is stamped into
a synthetic 'x-litellm-key-fp' header by a new before_record_request
hook, which then strips the real auth headers (we have to do the
scrubbing here instead of via vcrpy's filter_headers knob, because
filter_headers runs *first* and would erase the value we want to hash).

Bad-key requests now get a different cassette bucket than good-key
requests, so vcrpy will not replay a recorded 200 in place of the
expected 401. The fingerprint is a one-way hash of the secret, so
cassettes never contain the key.

This permanently removes the 'bad-key' category of skips:

- tests/local_testing: dropped ::test_amazing_sync_embedding,
  ::test_async_custom_handler_completion,
  ::test_async_custom_handler_embedding
- tests/logging_callback_tests: dropped ::test_async_chat_azure,
  ::test_async_embedding_azure
- tests/litellm_utils_tests: dropped
  ::test_get_valid_models_from_dynamic_api_key

Coverage: 7 new unit tests in tests/test_litellm/test_vcr_safe_body_matcher.py
covering header stripping, fingerprint determinism, no-auth bucketing,
good-vs-bad key discrimination, x-api-key (Anthropic/Azure) discrimination,
and idempotence under replay.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): drop redundant comments and docstrings

Trim narration of code that is already self-evident from function and
variable names. Keep the two genuinely non-obvious bits:

- ordering constraint between filter_headers and before_record_request,
  which would invite a maintainer to re-introduce the bug if removed
- the per-directory _VCR_INCOMPATIBLE_FILES rationale, since 'why
  exactly is this skipped' is not knowable from the test name alone

Also drop the 40-line commented-out drop-in conftest snippet at the
bottom of _vcr_conftest_common.py — the consuming conftests are the
canonical reference.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): make _before_record_request idempotent

vcrpy invokes before_record_request more than once per request:
can_play_response_for calls it, then __contains__ /
_responses (reached via play_response) call it again on the
result. The second invocation sees a request whose auth headers we
already stripped, so a naive recompute yields "no-key" and
overwrites the real fingerprint stored in the header.

This makes can_play_response_for and play_response disagree on
matchability — the former says "yes, we have a stored response for
this" (matching no-key to no-key) and the latter throws
UnhandledHTTPRequestError because it computes a fresh real
fingerprint that doesn't match the stored no-key.

In CI this manifested as ~30 failing tests across guardrails_testing,
audio_testing, batches_testing, image_gen_testing, llm_responses_api,
litellm_router_unit_testing, etc. Skip the recompute when the header
is already set, so re-applying the hook is a no-op.

Adds a regression test that fires the hook twice on the same dict and
asserts the fingerprint stays put.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

* test(vcr): drop more redundant docstrings and headers

* test(vcr): enable 24hr cache for ocr_tests and search_tests

These two directories were the only non-dockerized test suites in the
build_and_test workflow that make live LLM/provider API calls but were
not VCR-enabled by this PR. Together they account for 96 tests:

- tests/ocr_tests/ (31): Mistral OCR, Azure AI OCR, Azure Document
  Intelligence, Vertex AI OCR. Pure-unit tests inside the same files
  (e.g. TestAzureDocumentIntelligencePagesParam) make no HTTP calls
  and become benign VCR NOOPs.
- tests/search_tests/ (65): Brave, DataForSEO, DuckDuckGo, Exa,
  Firecrawl, Google PSE, Linkup, Parallel.ai, Perplexity, SearchAPI,
  Searxng, Serper, Tavily.

Both directories use the canonical minimal conftest pattern from
tests/audio_tests/conftest.py with no skip lists. None of the test
files use respx, none assert on per-call upstream non-determinism
(no response1.id != response2.id, no overhead-as-fraction-of-total,
no live polling), so the default match_on tuple should cache cleanly.
If a flake surfaces during the first cassette-recording CI run, we
can add a targeted skip the same way we did for the other dirs.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
2026-05-05 15:13:31 -07:00
userandClaude Opus 4.7 4af58e1f97 [Security] Clear AWS Inspector CVE findings on Docker image
- Narrow /root/.cache COPY in Dockerfile to /root/.cache/prisma{,-python}
  only — drops ~660MB of uv build cache including a setuptools wheel
  that surfaced as CVE-2024-6345 / CVE-2025-47273 even though it was
  never on the runtime sys.path.
- DiskCache: switch to dc.JSONDisk to neutralize the pickle code path
  (CVE-2025-69872, no upstream fix). Values must be JSON-serializable;
  cleanup get_cache to skip the now-dead json.loads(dict) branch by
  guarding on isinstance(str).
- pyproject.toml: drop diskcache pin from [caching] extra (no fixed
  version exists). Stub kept so `pip install litellm[caching]` doesn't
  warn; users who want disk caching install diskcache themselves.
- Bump black 24.10.0 → 26.3.1 (CVE-2026-32274) + apply 296-file mechanical
  reformat. Black is dev-only (not in the runtime image), but bumping
  clears the manifest-scan finding.
- Refresh ui/litellm-dashboard/package-lock.json to pick up next 16.2.4
  (was 16.1.7, GHSA-q4gf-8mx6-v5v3), uuid 14.0.0, postcss 8.5.13.
- Refresh litellm-js/spend-logs/package-lock.json to pick up
  hono 4.12.16 (GHSA-458j-xx4x-4375).
- uv lock: gitpython 3.1.46 → 3.1.49 (clears two High GHSAs),
  langchain-text-splitters 1.1.1 → 1.1.2.
- Add tests/test_litellm/caching/test_disk_cache.py covering JSONDisk
  enforcement, dict/string round-trip, TTL, increment, delete/flush.

Net delta on combined trivy + grype scans: 17 findings → 4 (all
remaining 4 are Wolfi system python-3.13 CVEs marked WONTFIX upstream
in CPython 3.14; CVE-2026-3298 is Windows-unreachable on Linux).

Existing on-disk caches written by the previous pickle-format Disk
will silently miss after upgrade — diskcache is intended to be
ephemeral so impact is recreate-on-next-write.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 20:21:15 +00:00
Yuneng Jiang e3917c9d08 [Test] Anthropic: Replace Legacy Claude-4-Sonnet Alias With Haiku 4.5
Three live-API tests pinned to claude-4-sonnet-20250514, which is a
non-canonical alias of claude-sonnet-4-20250514. Anthropic's main API
no longer resolves the legacy form under freshly issued keys, so the
tests fail with not_found_error. The token counter test pinned to
claude-sonnet-4-20250514 itself (deprecation_date 2026-05-14, two weeks
out) was on borrowed time too.

Bump all four to claude-haiku-4-5-20251001 — capability superset for what
these tests exercise (streaming, parallel tool calling, extended thinking,
token counting), no upcoming deprecation, cheaper per-token.
2026-05-01 19:10:27 -07:00
Yuneng Jiang 8eca93470c [Fix] Tests: Align Bedrock count-tokens endpoint assertions with URL-encoded model id
The endpoint builder in BedrockCountTokensConfig.get_bedrock_count_tokens_endpoint
percent-encodes the model id as a single path segment (d4dd865b1a, path-traversal
hardening). Update the four endpoint-URL assertions in TestBedrockCountTokensEndpoint
to expect `amazon.nova-lite-v1%3A0` instead of the literal `:0`, matching production
behavior already covered by test_count_tokens_endpoint_encodes_model_id.
2026-05-01 15:22:40 -07:00
user 7497674661 fix(proxy): sanitize redaction controls at ingress 2026-04-29 22:52:31 -07:00
user 842eea0131 chore(proxy): harden request control fields 2026-04-29 22:35:17 -07:00
Ryan Crabbe dc46467235 fix(tests): replace deprecated Bedrock Claude 3.7 Sonnet model ID
AWS Bedrock has reached end-of-life for `claude-3-7-sonnet-20250219-v1:0`,
returning 404s with "This model version has reached the end of its life."
Update test references to `claude-sonnet-4-5-20250929-v1:0` (same capability
surface: thinking, tools, prompt caching, PDF input, vision, computer use).

The bedrock/invoke pass-through tests stay on Sonnet 3.5 since Sonnet 4.5
is converse-only on Bedrock.
2026-04-28 14:24:19 -07:00
milan-berriandGitHub 10aed9e981 feat(logging): add retry settings for generic API logger (#26645)
* Add retry settings for generic API logger

Made-with: Cursor

* Refine generic API retry behavior

Made-with: Cursor
2026-04-28 08:38:17 -07:00
Ishaan Jaffer e8461b5b97 style: run black formatter on files from main merge 2026-04-17 13:02:59 -07:00
Yuneng Jiang 06a0d4498a fix: tighten handling of environment references in request parameters
- Reject os.environ/ references supplied via /health/test_connection
  request params instead of resolving them; config-sourced values are
  already resolved before reaching the endpoint.
- Skip os.environ/ references in dynamic callback params loaded from
  per-request metadata.
- Constrain oidc/file/ to an allowed credential directory allowlist
  (defaults to /var/run/secrets and /run/secrets, overridable via
  LITELLM_OIDC_ALLOWED_CREDENTIAL_DIRS).
2026-04-11 21:41:41 -07:00
51876292a0 Litellm ishaan april4 2 (#25150)
* feat(router): integrate allowed_fails_policy into health check failures (#24988)

* feat(router): integrate allowed_fails_policy into health check failures

Health check failures now increment the same per-deployment failure
counters used by allowed_fails_policy, so users can control how many
health check failures of each error type are required before a
deployment enters cooldown.

- ahealth_check() preserves the original exception in its return dict
- run_with_timeout() returns a litellm.Timeout on health check timeout
- _perform_health_check() propagates exceptions to unhealthy endpoints
- _write_health_state_to_router_cache() calls _set_cooldown_deployments
  for each unhealthy endpoint that has an exception
- When allowed_fails_policy is set, the binary health check filter is
  bypassed so cooldown is the sole routing exclusion mechanism
- Safety net: if all deployments are in cooldown with
  enable_health_check_routing=True, the cooldown filter is bypassed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(router): add health_check_ignore_transient_errors flag

When enabled, health check failures with 429 (rate limit) or 408 (timeout)
status codes are skipped from the cooldown pipeline. These are transient
load issues, not broken deployments. Auth errors (401), 404, and 5xx errors
still increment counters and trigger cooldown as before.

Config (general_settings):
  health_check_ignore_transient_errors: true

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(router): also exclude 429/408 from health state cache when ignore_transient_errors set

The previous fix only skipped cooldown counter increments. The health state
cache was still marking 429/408 endpoints as is_healthy=False, causing the
binary health check filter to exclude them from routing.

Now, when health_check_ignore_transient_errors=True, 429/408 endpoints are
also excluded from the unhealthy list passed to build_deployment_health_states(),
so the binary filter treats them as unaffected (not unhealthy).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(router): add health check driven routing guide

New standalone page covering the full health check routing feature:
allowed_fails_policy integration, health_check_ignore_transient_errors,
architecture SVG, step-by-step setup, and gotchas (TTL, AllowedFails semantics).

Replaces the inline section in health.md with a link to the new page.
Added to the Routing & Load Balancing sidebar.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(health-check-routing): fix three CI failures

- Add "exception" to ILLEGAL_DISPLAY_PARAMS in health_check.py so the
  exception object is stripped before the health endpoint serializes
  results to JSON (fixes TypeError: 'URL' object is not iterable)
- Add allowed_fails_policy = None to FakeRouter stubs in
  test_router_health_check_routing.py (fixes AttributeError)
- Add health_check_ignore_transient_errors to config_settings.md router
  settings reference table (fixes documentation test)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix litellm/tests/proxy_unit_tests/test_proxy_server.py

* fix(router): address greptile review comments

- Narrow cooldown safety-net bypass: only fires when allowed_fails_policy
  is set (cooldown is health-check driven). Without a policy, cooldowns
  are from real request failures and must not be bypassed.
- Restore cooldown deployments DEBUG log that was accidentally removed.
- Fix test_health TypeError: move exception extraction to a separate
  exceptions_by_model_id dict returned alongside endpoints, so exception
  objects never appear in the endpoint dicts that get JSON-serialized
  by the /health response.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(health-check-routing): properly isolate exceptions from health response

Return exceptions_by_model_id as a separate third value from
_perform_health_check / perform_health_check so exception objects
(which contain non-JSON-serializable httpx URL types) never appear
in the endpoint dicts that get serialized by the /health response.

Callers updated: _health_endpoints.py, shared_health_check_manager.py,
proxy_server.py background loop. All use the exceptions dict only for
cooldown integration, not for display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(shared-health-check): fix remaining 2-value return sites and update type annotation

* fix(health-check-routing): fix P0 cooldown integration never firing

The cooldown loop was reading endpoint.get("exception") which is always
None because exceptions are now returned via exceptions_by_model_id, not
stored in endpoint dicts. Fixed to use _exceptions.get(model_id).

Also fixes the transient-error filter to use _exceptions instead of
endpoint.get("exception"), and fixes all remaining 2-value return sites
in shared_health_check_manager.py. Tests updated to pass exceptions via
exceptions_by_model_id parameter instead of endpoint dicts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(health-check-routing): fix P1 transient-error filter broken on cache hits

When SharedHealthCheckManager returns cached results, exceptions_by_model_id
is always {} so the transient-error filter defaulted to status 500 for all
endpoints, incorrectly marking 429/408 endpoints as unhealthy.

Fix: store integer exception_status on each unhealthy endpoint dict in
_perform_health_check. _get_endpoint_exception_status() uses the live
exception object when available (direct path) and falls back to the stored
integer (cache-hit path). The integer is JSON-serializable and survives
the shared cache round-trip.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(health-check-routing): gate cooldown loop behind allowed_fails_policy

Without the policy, cooldown is not the routing exclusion mechanism.
Firing _set_cooldown_deployments for all enable_health_check_routing users
was a backwards-incompatible change — 401s would immediately cooldown
deployments that the binary filter would have recovered on the next cycle.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* revert: undo allowed_fails_policy gate on cooldown loop

Cooldown integration via health checks is intentional for all
enable_health_check_routing users, not just those with allowed_fails_policy.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(docs+tests): fix health_check_ignore_transient_errors doc section and test coverage

- Move health_check_ignore_transient_errors from router_settings to
  general_settings in config_settings.md (code reads it from general_settings)
- Remove duplicate enable_health_check_routing / health_check_staleness_threshold
  entries that were incorrectly listed under router_settings
- Replace TestHealthCheckEndpointExceptionPropagation tests with ones that
  exercise the real _perform_health_check code path via mocked ahealth_check,
  verifying exceptions appear in exceptions_by_model_id and NOT in endpoint dicts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(tests+docs): fix tuple unpacking and docs test failures

- Update test mocks that return (healthy, unhealthy) to return
  (healthy, unhealthy, {}) to match the new 3-value signature
- Update test unpackings of perform_shared_health_check to use
  healthy, unhealthy, _ = ...
- Add health_check_ignore_transient_errors to router_settings section
  in config_settings.md (it is a Router constructor param, so the doc
  test requires it there; it also lives in general_settings for proxy use)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix CodeQL errors

* fix(tests): fix 2-value unpackings of _perform_health_check in test_health_check.py

* fix(tests): fix mock _perform_health_check returning 2-tuple instead of 3

* fix team routing

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add distributed lock for key rotation job (#23364)

* fix: add distributed lock for key rotation job

* fix: address Greptile review feedback on key rotation lock (#23834)

* fix: address Greptile review feedback on key rotation lock

* fix req changes greptile

* feat(proxy): Optional on_error for guardrail pipeline (API / technical failures) (#24831)

* guardrails fallback

* docs

* docs: add LITELLM_KEY_ROTATION_LOCK_TTL_SECONDS to environment variables reference

* fix(mypy): accept Union[Dict, Any] in _get_deployment_order and use typed list to fix min() type error

* fix(mypy): use Optional[str] for api_base in PydanticAI provider to match superclass signature

---------

Co-authored-by: Sameer Kankute <sameer@berri.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Harshit Jain <48647625+Harshit28j@users.noreply.github.com>
Co-authored-by: Shivam Rawat <shivam@berri.ai>
Co-authored-by: yuneng-jiang <yuneng@berri.ai>
2026-04-04 23:09:42 +00:00
David ChenandGitHub d1df4e838b Litellm fix update bedrock models (#24947)
* update bedrock models in tests

* updated more tests and model_prices_and_context_window

* fix model id and pricing

* replace more sonnet models

* update tests

* git push

* update pricing

* flaky total cost

* monkey patch

* relax the cost change

* fix and revert some changes

* revert the pricing

* chore: move cost/pricing changes to bedrock-cost-fixes branch

* chore: split Bedrock file-api beta stripping to separate branch

Removes strip_unsupported_file_api_betas_for_bedrock_invoke from this branch;
see litellm_bedrock_invoke_strip_file_api_betas for that fix.

Made-with: Cursor
2026-04-01 19:22:54 -07:00
e4442a4d98 test fix us.anthropic.claude-haiku-4-5-20251001-v1:0 (#24931)
* test fix us.anthropic.claude-haiku-4-5-20251001-v1:0

* ignore mypy cache files

---------

Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
Co-authored-by: David Chen <clfhhc@gmail.com>
2026-04-01 11:01:03 -07:00
Krrish Dholakia e17acd6b69 test: make tests smarter 2026-03-30 18:14:44 -07:00
Ishaan Jaffer facb230fee test_create_vertex_fine_tune_jobs_mocked 2026-03-30 18:01:23 -07:00
Krrish Dholakia c7e2bfc577 fix: cleanup tests 2026-03-30 16:24:35 -07:00
Krrish Dholakia 4c00a14ce0 fix: fix ci/cd + handle oidc jwt tokens 2026-03-30 16:12:58 -07:00
Ishaan Jaffer 87debe6254 test_litellm_overhead_non_streaming 2026-03-30 15:20:53 -07:00
Krrish Dholakia 1fb677702d test: update to new vertex ai keys 2026-03-28 20:19:05 -07:00
Krrish Dholakia cdcab8a243 refactor: cleanup deprecated models 2026-03-28 19:39:11 -07:00
Krrish Dholakia bc829d51f2 test: test 2026-03-28 19:17:38 -07:00
Seokjun YangGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
d3afaf613d Update tests/litellm_utils_tests/test_bedrock_token_counter.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-20 22:21:22 +09:00
Seokjun YangGitHubgreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
eb733702fc Update tests/litellm_utils_tests/test_bedrock_token_counter.py
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-20 22:21:15 +09:00
stias bc4608e718 fix(bedrock): respect api_base and aws_bedrock_runtime_endpoint in count_tokens endpoint
The /v1/messages/count_tokens endpoint was hardcoding the Bedrock runtime
URL, ignoring api_base and aws_bedrock_runtime_endpoint settings. This
aligns it with invoke/converse handlers by using the existing
get_runtime_endpoint() method for consistent endpoint resolution.

Signed-off-by: stias <seokjun.yang@mycraft.kr>
2026-03-20 17:58:20 +09:00
yuneng-jiangandClaude Opus 4.6 27d0ffea44 Fix flaky AWS secret manager tests by skipping on ThrottlingException
ThrottlingException is a transient AWS rate-limit error unrelated to code
correctness. Skip the test instead of failing the CI pipeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 18:53:18 -07:00
yuneng-jiangandClaude Opus 4.6 3d45ba3edf Fix flaky vertex_ai overhead test by mocking auth and HTTP calls
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 18:01:54 -07:00
yuneng-jiangandClaude Opus 4.6 0fbfb8e772 fix(tests): remove test_oidc_circle_v1_with_amazon_fips that depends on external infra
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:33:52 -07:00
yuneng-jiangandClaude Opus 4.6 b98ecd8c1d fix(tests): quarantine flaky test_oidc_circle_v1_with_amazon
The test fails with InvalidIdentityToken because the OIDC provider is
no longer configured in the third-party AWS account (ai.moda). This
matches the existing quarantine on test_oidc_circleci_with_azure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 09:04:42 -07:00
Cursor Agentandyuneng-jiang 177edb06ae fix: stabilize 5 CI test failures
- Vertex AI batch cost tests: replace removed gemini-1.5-flash-001 model
  with gemini-2.0-flash-001 in pricing lookups
- MCP test_executes_tool_when_allowed: add server_id and auth_type attrs
  to StubServer to match new _resolve_allowed_mcp_servers_with_ip_filter
- MCP M2M tests: infer oauth2_flow='client_credentials' in
  _execute_with_mcp_client when client_id/client_secret/token_url present
  (NewMCPServerRequest lacks oauth2_flow field)
- Team list test: update mock find_many to filter by team_id per the
  current per-team query pattern in list_team
- Azure DALL-E 3 health check: skip test due to 410 ModelDeprecated

Co-authored-by: yuneng-jiang <yuneng-jiang@users.noreply.github.com>
2026-03-13 01:03:35 +00:00
yuneng-jiangandClaude Opus 4.6 c9f7075690 Replace additional deprecated models across test files
- tests/local_testing/test_completion_cost.py:
  - claude-3-5-sonnet-20240620 -> claude-sonnet-4-6
  - gemini/gemini-1.5-flash-001 -> gemini/gemini-2.5-flash

- tests/test_litellm/test_utils.py:
  - claude-3-5-sonnet-20240620 -> claude-sonnet-4-6 (VertexAI config test, proxy tests)
  - gemini-1.5-pro -> gemini-2.5-pro (pre_process_non_default_params)
  - gemini/gemini-1.5-pro -> gemini/gemini-2.5-pro (proxy tests)

- tests/litellm_utils_tests/test_utils.py:
  - claude-3-opus-20240229 -> claude-sonnet-4-6 (trimming, vision tests)
  - gemini-pro -> gemini-2.5-pro (function calling test)
  - gemini-pro-vision -> gemini-2.5-flash (vision test)
  - gemini-1.5-pro -> gemini-2.5-pro (response schema test)
  - gemini/gemini-1.5-flash -> gemini/gemini-2.5-flash (function calling test)
  - gemini-1.5-pro -> gemini-2.5-pro (vision gemini test)
  - gpt-4-vision-preview -> gpt-4o (vision test)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:03:54 -07:00
yuneng-jiangandGitHub b314e8d20a Merge pull request #20688 from BerriAI/litellm_budget_tier_enforcement_for_keys
[Fix] Budget-linked keys never had spend reset
2026-03-06 20:44:58 -08:00
Sameer Kankute f68cbc4c95 Fix test_perform_health_check_filters_by_model_id 2026-02-26 10:43:05 +05:30
Alejandro Tapia 80c09295ad Merge upstream/main - resolve health check conflicts 2026-02-24 17:09:05 -08:00
Sean Marsh GloverGitHubSean Glovergreptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
4652c73259 feat(proxy): limit concurrent health checks with health_check_concurrency (#20584)
* staged first pass

* black

* Update litellm/proxy/health_check.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* simpler

* restore cached logo

* fix tests for perform_health_check max_concurrency arg

* implement pr suggestion

* and the helm chart

* add configureable resources and probes to the deployment in the helm chart

* more helm chart unittests

* move some background healthcheck loggin to debug

---------

Co-authored-by: Sean Glover <sglover@athenahealth.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-24 08:16:59 -08:00
Ryan Crabbe ea32ad72c6 Merge origin/main into perf/callback-registration-routing
Resolve conflicts:
- logging_callback_manager.py: keep PR's MAX_CALLBACKS, _is_async_callable, Callable type
- test_utils.py: keep both TestCallbackAsyncSyncSeparation and TestMetadataNoneHandling
2026-02-21 12:40:23 -08:00
Ishaan JaffandGitHub afdf70be73 fix(test): update claude model name in test_get_valid_models_from_dynamic_api_key (#21771) 2026-02-21 10:15:23 -08:00
Shivam RawatandGitHub c45a17bad4 Merge branch 'main' into litellm_budget_tier_enforcement_for_keys 2026-02-17 16:00:04 -08:00
Tomu Hirata 43ba7d0a07 Add test case for Databricks Meta LLaMA 3.1 70B instruct model in content parsing tests
Signed-off-by: Tomu Hirata <tomu.hirata@gmail.com>
2026-02-17 15:36:00 +09:00
shivam 9f7f190670 resolved greptile comment 2026-02-16 10:24:06 -08:00
shivam 0117b35a6b added more tests, fixed tests 2026-02-16 09:59:19 -08:00
Alejandro Tapia 82f6d0fe43 healthcheck-model_id-fix: There was quite a bit of code that needed to be changed since health checks were entirely keyed by model name. This includes, proxy logic, the dashboard, and even networking, because model name was the identifier everywhere. All changed files had tests added to them, which are passing with no regressions. 2026-02-12 14:47:09 -08:00
Ryan Crabbe aaaf7f3b6c perf: move async/sync callback separation from per-request to registration time
The three loops in function_setup that called is_async_callable() on every
callback each request were redundant after the first request. Move the
async/sync routing into LoggingCallbackManager.add_litellm_*_callback()
so it happens once at registration time instead of on every request.
2026-02-07 12:10:38 -08:00
Yuta Saito 695fbf4ec5 feat: hashicorp vault rotate support 2026-01-23 17:32:55 +09:00
Yuta Saito 898cc3ff4f test: update langfuse trace_id tests to use litellm_trace_id 2026-01-22 06:19:43 +09:00
Sameer Kankute e315e4cd7c fix litellm/tests/litellm_utils_tests/test_utils.py 2026-01-21 18:50:02 +05:30
Ishaan JaffandGitHub ddebdd47bc [Feat] Add Support for Claude Code Max/OAuth 2 on LiteLLM AI Gateway (#19453)
* fix count_tokens_with_anthropic_api

* remove outdated file

* fix ANTHROPIC_TOKEN_COUNTING_BETA_VERSION

* refactor: get_token_counter

* init test suite for token counter

* init token counters

* fix: fix pyrightI

* fix Code QA issues

* feat: add OAUTH handling ant

* feat: Oauth handling Ant

* test anthopic common utils

* fix code QA

* docs
2026-01-20 17:21:17 -08:00
Alexsander HamirandGitHub 15c3bc219b [Refactor] Add CI enforcement for O(1) operations in _get_model_cost_key to prevent performance regressions (#19052)
* Optimize _get_model_cost_key to avoid expensive scans

- Remove expensive O(n) scan fallback that was causing 42.87% CPU overhead
- Only scan when size mismatch detected (O(1) check)
- Add warning in docstring: Only O(1) lookup operations are acceptable
- Clean up comments to be more concise
- Keep stale entry rebuild for pop() case (only triggers when stale entry found)

This fixes the performance issue where the scan was being triggered on every
failed lookup, causing severe CPU overhead during router operations.

* Add code quality check to enforce O(1) operations in _get_model_cost_key

- Add check_get_model_cost_key_performance.py to statically analyze _get_model_cost_key
- Detects O(n) operations (loops, comprehensions, problematic function calls)
- Recursively checks called functions to find nested O(n) operations
- Allows conditional O(n) rebuilds in helper functions (_rebuild_model_cost_lowercase_map, _handle_stale_map_entry_rebuild, _handle_new_key_with_scan)

* Integrate _get_model_cost_key performance check into CI pipeline

- Add check_get_model_cost_key_performance.py to check_code_and_doc_quality job
- Ensures O(1) requirement is enforced in CI to prevent performance regressions

* Remove unused performance test and clean up utils.py

- Remove test_get_model_info_performance.py (no longer needed)
- Remove extra blank line in utils.py

* Document allowed helper functions and exception process in _get_model_cost_key

- Add documentation listing allowed helper functions with O(n) operations
- Explain why these are acceptable (conditionally called)
- Add instructions for adding new exceptions to check_get_model_cost_key_performance.py

* Fix docstring detection and type checker error in performance check

- Add proper docstring tracking to skip docstring content (fixes false positive for 'map' in docstring)
- Add None check for docstring_quote to fix type checker error
- Restore _handle_new_key_with_scan to allowed_helpers list

* Remove check_get_model_cost_key_performance from CI pipeline

- Temporarily remove the performance check from CI to avoid blocking builds

* Restore performance check and remove memory leak tests from CI

- Add back check_get_model_cost_key_performance.py to CI pipeline
- Remove memory_leak_tests job that was causing port conflicts

* Remove extra blank line in CI config
2026-01-13 17:08:03 -08:00
Alexsander HamirandGitHub a1dd3ead4d [Perf] Remove bottleneck causing high CPU usage & overhead under heavy load (#19049) 2026-01-13 15:22:09 -08:00