litellm/litellm
Hektor Jacynycz Garcia 2287994de4
fix(logging): log spend once for large non-streaming requests on the chat to Responses bridge (#44508)
* fix(logging): claim the async success dedup flag before the first await

Nested @client wrappers on the chat -> Responses bridge schedule two async
success tasks on one logging object. The dedup flag was checked at the top
of _async_success_handler_body but only set after several awaits; the
worker-thread base64 offload for messages >= 256 KiB yields there, so the
second task passed the same check and the request was logged twice
(two SpendLogs rows, key/team/daily spend counted twice).

Fixes #44500

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

* fix(logging): serialise async success handlers per logging object instead of claiming early

Claiming the dedup flag before the first await (previous commit) let a first
handler that is cancelled before its callbacks run (e.g. by the logging
worker's per-coroutine timeout) suppress the second one, losing the request's
success accounting. Serialise non-streaming handlers on the same Logging
object with an asyncio.Lock kept in a module-level WeakKeyDictionary: the
second handler waits, then skips if the first logged, or logs itself if the
first raised or was cancelled first. Streaming chunk logging is not locked.

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

* test(integration): pin one spend row and one key charge for large bridged chat completions

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

* test(integration): wait for the large request's spend row before asserting

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

* test(logging): use fixed timestamps in async success dedup tests

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

* style(logging): trim the async success dedup lock comment

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

* fix(logging): reset background interaction dedupe under the success dedup lock

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

* test(logging): use a fixed start time in the interactions logging helper

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

* fix: claim the async success log at schedule time instead of locking the handler

Nested @client wrappers (chat over the Responses bridge, Anthropic Messages over the chat adapter) exit with one shared Logging object and each queued their own async success handler. The handler checked has_logged_async_success before its awaited work and set it after, so both could pass the check and the request was logged and billed twice.

_schedule_async_success_logging now claims the log on the Logging object synchronously through claim_async_success_log, and a later wrapper returns without enqueuing. There is no await between the check and the decision, so no lock is needed. The per-object asyncio.Lock, the WeakKeyDictionary that held it and the locked block in async_log_background_interaction_completion are gone.

Tests: the regression test moves to tests/unit/test_utils.py and drives two _dispatch_success_logging exits with one Logging object through the logging worker, asserting one log with the inner result. It fails without the claim. The cancelled-handler test is dropped since a second wrapper no longer retries. The background interaction completion test stays.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* test(logging): pin the background completion event order and name the innermost wrapper in the claim docstrings

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

* test(integration): send one bridged request and assert the inner Responses result is the logged one

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

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: kerry <kerry@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 157d96dcd0)
2026-10-07 17:39:15 -07:00
..
a2a_protocol fix(bedrock): sign on a dedicated executor instead of the shared default one 2026-09-09 18:52:00 -07:00
anthropic_interface fix(anthropic_endpoints): return Anthropic type:error envelope for /v1/messages errors 2026-08-31 16:23:36 -07:00
assistants refactor(typing): replace Any with proven types in 89 more backend files 2026-09-02 23:08:42 +00:00
batch_completion feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
batches Merge pull request #39626 from BerriAI/litellm_batch_ui_logs 2026-09-08 15:19:25 -07:00
caching fix(logging): track spend for streams a deployment hook converted to non-streaming 2026-09-18 11:10:31 -07:00
completion_extras fix(router): classify encrypted delegated tasks with native Responses 2026-09-10 12:32:09 -07:00
compression refactor(typing): replace Any with proven types in 89 more backend files 2026-09-02 23:08:42 +00:00
containers Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_decrease_anys_opus5_r4 2026-09-02 23:32:10 +00:00
endpoints/speech/speech_to_completion_bridge refactor(speech): freeze httpx response header dicts (LIT002) 2026-08-31 21:12:53 -07:00
evals chore(typing): remove 914 basedpyright Any errors across 16 hotspot files 2026-08-10 01:24:40 -07:00
experimental_mcp_client fix(mcp): bind discovery caching to credentials and active callers 2026-09-11 14:49:00 -07:00
files test(batches): use immutable expectations with explicit test doubles 2026-09-07 16:36:27 -07:00
fine_tuning refactor(typing): replace Any with proven types in 89 more backend files 2026-09-02 23:08:42 +00:00
google_genai refactor(types): replace Any with precise types across 73 modules 2026-09-01 11:05:02 +00:00
images refactor(typing): replace Any with proven types in 89 more backend files 2026-09-02 23:08:42 +00:00
integrations Merge pull request #41462 from BerriAI/litellm_otel_promote_nested_request_metadata_keys 2026-09-22 23:02:58 +00:00
interactions refactor(typing): replace Any with proven types in 42 more backend files 2026-09-02 15:35:01 +00:00
litellm_core_utils fix(logging): log spend once for large non-streaming requests on the chat to Responses bridge (#44508) 2026-10-07 17:39:15 -07:00
llms fix(realtime): surface an upstream handshake refusal as an error event and policy close (#42388) 2026-09-22 23:02:53 +00:00
models feat(auto-router): show routed model and savings in Claude Code and Codex (#40330) 2026-09-11 12:52:42 -07:00
ocr Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_ocr_custom_pricing 2026-09-12 15:42:59 -07:00
passthrough fix(passthrough): parse Bedrock stream spend incrementally instead of buffering the whole response (#40724) 2026-09-11 09:53:37 -07:00
proxy chore(lint): scope a TRY004 suppression to the bearer-token salt key check 2026-09-29 15:34:34 -07:00
proxy_auth feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
rag chore: merge origin/litellm_internal_staging into litellm_decrease_anys_opus5_r4 2026-09-04 18:59:35 -07:00
realtime_api refactor(realtime): move Meta Muse Voice onto BaseRealtimeConfig 2026-09-11 20:12:15 -07:00
repositories feat(auto-router): allow opted-in team members to manage their routers 2026-09-22 22:49:10 +00:00
rerank_api fix(rerank): adopt declared authenticating providers in arerank instead of resolving them 2026-09-01 14:47:59 -07:00
responses fix(logging): track spend for streams a deployment hook converted to non-streaming 2026-09-18 11:10:31 -07:00
router_strategy feat(openrouter): price typesafe/jev-1.13 and add an openrouter decisions pass-through (#42301) 2026-09-22 22:49:10 +00:00
router_utils feat(auto-router): add JEV classifier alongside LLM classifier 2026-09-22 22:49:10 +00:00
rust_bridge refactor(ocr): complete native lifecycle and preserve Azure auth (#40734) 2026-09-12 11:56:49 -07:00
sandbox feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
search fix(search): forward search-tool params through the router, complete Parallel AI v1 param mapping (#37883) 2026-09-01 21:46:46 -07:00
secret_managers feat(secret_managers): support customer-managed KMS key for virtual keys stored in AWS Secrets Manager (#40475) 2026-09-11 09:54:08 -07:00
skills feat(skills): semantic search over the LiteLLM-hosted skill registry (#39401) 2026-09-07 12:28:38 -07:00
types Merge pull request #42595 from BerriAI/litellm_cherrypick_1_102_x 2026-09-22 20:01:25 -07:00
vector_store_files refactor(typing): replace Any with proven types in 42 more backend files 2026-09-02 15:35:01 +00:00
vector_stores Merge remote-tracking branch 'origin/litellm_internal_staging' into litellm_python_version_ci 2026-09-03 00:16:19 -07:00
videos refactor(videos): make video upload param keyword-only on public edit fns 2026-08-24 15:55:07 -07:00
__init__.py Merge pull request #40770 from BerriAI/litellm_lit6996_agent_skills_wellknown 2026-09-12 15:20:20 -07:00
_internal_context.py fix(proxy): price one cost estimate at one moment 2026-09-07 18:38:12 -07:00
_lazy_imports.py fix(lazy_imports): type import_map as Mapping to stay under the LIT001 budget 2026-09-05 16:12:45 -07:00
_lazy_imports_registry.py Revert "perf: lazy-load SDK symbols so import litellm stays under 60 MB RSS (…" 2026-09-05 16:07:09 -07:00
_logging.py fix(proxy): stop leaking internal exception details to clients (#39380) 2026-09-02 17:32:00 -07:00
_redis.py fix(redis): accept every truthy flag and sign serverless ElastiCache caches 2026-09-10 10:13:03 -04:00
_redis_credential_provider.py fix(redis): accept every truthy flag and sign serverless ElastiCache caches 2026-09-10 10:13:03 -04:00
_service_logger.py chore: merge origin/litellm_internal_staging into litellm_decrease_anys_opus5_r4 2026-09-04 18:59:35 -07:00
_uuid.py chore(lint): strip inert type: ignore comments and zero LIT009, LIT010, LIT011 headroom 2026-08-05 02:37:24 -07:00
_version.py
anthropic_beta_headers_config.json fix(anthropic): forward Claude Code safeguards and dangerous-tool-use beta to Bedrock Invoke and Vertex on /v1/messages 2026-09-22 10:45:57 -07:00
anthropic_beta_headers_manager.py feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
blog_posts.json
budget_manager.py feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
constants.py fix(proxy): keep call_type and request start time on failed-request spend logs (#40558) 2026-09-11 12:22:18 -07:00
cost.json
cost_calculator.py chore(cost): drop the section header comment above custom_model_info 2026-09-10 18:10:34 -07:00
exceptions.py Merge pull request #39516 from BerriAI/litellm_vector_store_surface_retrieval_failure 2026-09-11 11:46:42 -07:00
main.py Merge pull request #41141 from BerriAI/litellm_lit7694_forwarded_headers_body_leak 2026-09-15 17:40:38 -07:00
model_prices_and_context_window_backup.json feat(openrouter): price typesafe/jev-1.13 and add an openrouter decisions pass-through (#42301) 2026-09-22 22:49:10 +00:00
policy_templates_backup.json
provider_endpoints_support_backup.json fix(ocr): send each provider a health-check document it accepts 2026-09-04 22:52:12 -07:00
py.typed
router.py fix(router): bind per-request routing_strategy override selectors to the request's callbacks 2026-09-18 11:10:28 -07:00
scheduler.py feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
setup_wizard.py feat(models): add Claude Fable 5.1 across Anthropic, Bedrock, Vertex AI, and Azure AI 2026-09-01 18:07:06 +00:00
timeout.py feat(lint): enforce Final on locals and freeze function parameters (LIT010, LIT011) 2026-08-04 12:54:39 -07:00
utils.py fix(logging): log spend once for large non-streaming requests on the chat to Responses bridge (#44508) 2026-10-07 17:39:15 -07:00