litellm/tests/unit/proxy/common_utils
Etienne Chabert 523b87ec71
fix(proxy): charge budget windows when the cached key or team expired before the request ended (#47608)
* fix(proxy): charge budget windows when the cached key or team expired before the request ended

The post-call spend update reads a key's or team's budget_limits from
user_api_key_cache and returned early on a miss, skipping both the window
counter increment and the LiteLLM_BudgetWindowSpend row. The cached object
expires after user_api_key_cache_ttl (60s by default), so any request that ran
longer than that without another request refreshing the entry was counted in
the key's lifetime spend but never in its budget windows. With budget
reservation disabled nothing else charges the windows, so keys kept running
past their window caps

On a miss the update now reads budget_limits straight from the key or team row.
It does not re-cache the object, since a write from the spend path could race a
/key/delete and put a just-deleted key back into the auth cache

* test(proxy): assert the budget window fallback never re-caches the key or team

* fix(proxy): read a missing key or team row once per cache TTL in the budget window fallback

The master key and UI session tokens are logged under names no key row
carries, so every one of their requests missed the auth cache and read the
database. A missing row is now remembered for the cache TTL in this process
only. A failed read is not remembered, and a row that exists is read fresh on
every miss so a key update is never served stale.

* fix(proxy): skip the budget window row read for a key or team with no windows

The fallback remembered only a missing row, so a key or team that exists but
holds no budget_limits was read from the database on every request that
outlived the key cache. It now remembers both cases under one cache key built
in user_api_key_cache.py, for the same TTL as the auth cache

* fix(proxy): keep no-window markers in their own cache and clear them when an update adds windows

Markers for keys and teams with no budget windows lived in the shared 200-entry auth cache, so a burst of late costs could evict cached teams and users. They now live in a dedicated in-memory cache on UserApiKeyCache, reached by local and broadcast evictions. /key/update and /team/update now evict the marker on every worker, so a late cost after an update charges the windows the update added.

* test(proxy): give the spend counter tests the proxy's real key cache and type the window spend helper

* fix(proxy): keep a row read that an update outran from restoring the no-window marker

* fix(proxy): clear finished no-window reads and evict a marker-shaped user id from both caches

* fix(proxy): apply other spend counters before a window row read and let no-window notes follow the key cache settings

---------

Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
2026-11-02 14:41:59 -08:00
..
html_forms test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
__init__.py ci: move tests/proxy_unit_tests to tests/unit/proxy and run the proxy-db shards from litellm-tests (#42903) 2026-09-24 22:59:11 +00:00
test_admin_ui_utils.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_auth_cache_invalidation_pubsub.py fix(proxy): charge budget windows when the cached key or team expired before the request ended (#47608) 2026-11-02 14:41:59 -08:00
test_cache_aware_routing.py refactor(router): route the router family through RoutingContext and close the outside surface (#49118) 2026-10-30 19:18:54 -07:00
test_cache_codec.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_callback_config_validation.py feat(otel): let a team or key excluded_services override the global setting for its OTel v2 destination (#45880) 2026-10-12 19:25:52 -04:00
test_callback_utils.py feat(router): opt in to non-chat deployment filtering (#46257) 2026-10-29 08:33:36 -07:00
test_check_batch_cost.py fix(batches): cost an unmanaged gs:// batch through the deployment that created it (#49301) 2026-10-30 15:25:47 -07:00
test_check_responses_cost.py refactor(router): move router callers onto the entry points and invert the proxy imports (#48847) 2026-10-29 17:26:33 +00:00
test_codex_model_catalog.py feat(proxy)!: honor model_info.max_input_tokens in the Codex catalog for stock models (#49701) 2026-11-02 13:27:58 -08:00
test_config_sync_pubsub.py fix(proxy): apply new model rows on every worker as soon as they are written (#49034) 2026-10-30 03:19:25 -07:00
test_credential_hydration.py fix(secret_managers)!: resolve oidc/file and oidc/env refs in admin-configured api_key per request (#47341) 2026-10-20 01:43:59 +00:00
test_custom_openapi_spec.py docs(proxy): describe /v1/decisions and /v1/systemone in Swagger UI (#45838) 2026-10-10 21:04:59 +00:00
test_debug_utils.py fix(router): keep the prompt caching affinity pin alive as long as the prefix's cache_control ttl (#46084) 2026-10-14 21:10:09 -07:00
test_discoverable_model_filter.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_encrypt_decrypt_utils.py refactor(proxy): expose public names for private proxy helpers (#45170) 2026-10-07 18:59:27 -07:00
test_error_body_call_id.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_expired_ui_session_key_cleanup_manager.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_get_routes.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_http_parsing_utils.py fix(proxy): abort the request with a 499 when the client disconnects during the body upload (#48640) 2026-10-27 16:49:56 -07:00
test_json_merge_patch.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_key_rotation_e2e.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_key_rotation_integration.py fix(security): remove the publicly known master key from the repo (#44718) 2026-10-06 10:55:24 -07:00
test_key_rotation_lock.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_key_rotation_manager.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_load_config_utils.py fix(security): remove the publicly known master key from the repo (#44718) 2026-10-06 10:55:24 -07:00
test_model_deprecation.py refactor(router): move router callers onto the entry points and invert the proxy imports (#48847) 2026-10-29 17:26:33 +00:00
test_model_listing_utils.py fix(proxy): resolve model aliases to their target deployment for listing metadata (#41041) 2026-10-15 00:38:50 -07:00
test_openai_endpoint_utils.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_openai_error_payload.py fix(proxy)!: label shared-handler errors the way the route catch-alls do (#39555) 2026-10-30 06:58:05 -07:00
test_openapi_route.py fix(proxy): build the OpenAPI schema off the event loop so a cold /openapi.json no longer stalls the worker (#47352) 2026-10-20 04:31:37 +00:00
test_openapi_schema_compat.py fix(proxy): build the OpenAPI schema off the event loop so a cold /openapi.json no longer stalls the worker (#47352) 2026-10-20 04:31:37 +00:00
test_periodic_reload_schedule.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_proxy_encrypt_decrypt.py fix(security): remove the publicly known master key from the repo (#44718) 2026-10-06 10:55:24 -07:00
test_rbac_utils.py refactor(proxy): expose public names for private proxy helpers (#45170) 2026-10-07 18:59:27 -07:00
test_realtime_cache.py fix(proxy): check the model in every OpenAI websocket passthrough frame (#48979) 2026-11-02 09:48:59 -08:00
test_registry_read_through.py fix(proxy): apply new model rows on every worker as soon as they are written (#49034) 2026-10-30 03:19:25 -07:00
test_request_tag_mappings.py feat(proxy): map baggage values to budget tags (#47358) 2026-10-20 09:09:42 +00:00
test_reset_budget_job.py fix(proxy): reconcile team member total_spend with current cycle spend (#48260) 2026-10-24 17:57:09 -07:00
test_resource_ownership.py refactor(core_utils): move resource_ownership out of the proxy (#45805) 2026-10-10 21:02:53 +00:00
test_scheduled_job_stagger.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_semantic_text_index.py fix(mcp): leave out tool texts too long to embed instead of failing tool search (#43554) 2026-10-14 14:53:00 -07:00
test_slow_gc_logger.py test(e2e): event_loop_stalls perf case fails on a gateway event-loop hold over 100 ms that is not on its allowlist (#46974) 2026-10-17 06:55:53 +00:00
test_sse_keepalive.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_static_asset_utils.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_swagger_utils.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_timezone_utils.py test(proxy): move middleware, spend_tracking, pass_through, common_utils and root proxy tests into tests/unit/proxy (#44015) 2026-10-01 18:23:31 +00:00
test_token_count_log.py feat(proxy): log one line per token count call (#47657) 2026-10-28 11:23:52 -07:00
test_token_count_settings.py fix(count_tokens): count grok-4-1, grok-4.3 and DeepSeek V4.1 tool requests the way their hosts bill them (#49074) 2026-11-02 12:37:35 -08:00
test_upsert_budget_membership.py fix(team): keep the team reset schedule when a temp budget increase is set on a member inheriting the team default (#48495) 2026-10-26 17:13:56 -07:00
test_user_api_key_cache.py fix(proxy): charge budget windows when the cached key or team expired before the request ended (#47608) 2026-11-02 14:41:59 -08:00
test_validation_error_body.py fix(proxy): keep the submitted body out of 422 validation errors (#43231) 2026-09-25 17:05:53 -07:00