litellm/litellm/router_utils
Mateo Wang 820725661e
fix(router): log each dropped /v1/messages attempt through its own logging copy and open the retry without its usage (#45194)
* fix(proxy): keep a retried /v1/messages stream's spend log by logging the superseded attempt as a failure

A /v1/messages stream the router retries before any content shares one logging object across both attempts. The superseded attempt's end-of-stream logging ran as a success (a usage-only or placeholder result) and claimed the request's one-shot success log, so the recovered stream's spend row was either missing or written under the superseded attempt's message id. The passthrough logging path now logs an Anthropic stream whose frames carry an `event: error` as a failure with whatever usage the provider reported, the way a dropped stream already does, and the router closes the superseded stream before it opens the retry so that logging is queued first.

* fix(proxy): log a provider error frame through an attempt-scoped logging copy

The error-frame failure stashed the superseded attempt's usage on the logging
object the retry shares, so the retry's success log carried the failed
attempt's token counts. The failure now logs through a copy of the logging
object with its own model_call_details, and the helper moves into the
Anthropic passthrough logging handler so the proxy streaming handler only
routes by endpoint type.

* fix(proxy): scope the error-frame failure log to streams closed at the frame

A stream that ran to its end at the provider's error frame kept logging
through the success route before this PR, and the attempt-scoped failure
log is now taken only for a stream the router closed at that frame, with
the copy made at the close so the retry's deployment never reaches it

* fix(router): log each dropped /v1/messages attempt through its own logging copy and open the retry without its usage

A /v1/messages stream dropped after message_start and retried in its model group shares one Logging object
across attempts, so the retry's success log inherited the dropped attempt's stashed usage and exception,
and a second drop never reached the failure handlers because the first had already marked the object as
logged. The dropped attempt now dispatches its failure handlers through an attempt-scoped copy, and the
router discards the stashed usage right before it opens the next attempt, on same-group retries and on
every fallback hop, so a request that runs out of attempts still bills the last attempt's partial usage.

* fix(router): dispatch chat stream failures through the attempt-scoped logging copy

The router now clears a dropped attempt's stashed usage before every fallback hop, so the chat stream wrapper's queued failure log must read that usage from its own copy of the logging object, like the /v1/messages path already does. The stash test asserts through a real success log, the cooldown test reads a real Router's cooldown list, and the integration helpers carry full types.

* test(vertex): give the stream wrapper's logging stub an attempt_scoped_copy

* test: route the pass-through logging mock's attempt copy to itself and read the callback sink through a reader

The pass-through failure test stubbed dispatch_failure_handlers on a spec'd MagicMock, which the attempt-scoped copy no longer reached, so the logging worker never finished. The integration tests kept earlier sink batches in a caller-owned list the helper mutated; a small reader keeps them instead.

* test(router): use real Logging in fallback cooldown test

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

* fix(router): preserve superseded retry usage for billing

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

* fix(proxy): bill retry input after prior stream usage

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

* fix(proxy): track retry dispatch for terminal billing

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

* test(router): use deterministic retry timestamp

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

* test(router): avoid real clock in fallback tests

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

* fix(tests): remove duplicate streaming logger import

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

* fix(proxy): retain error-frame usage for exhausted retries

* test(proxy): fix retry billing expectations and helper annotations

* fix(proxy): preserve failure estimate without carried usage

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

* refactor(proxy): audit retry helpers and fixtures

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

* test(proxy): fix retry fixture timestamps

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

* test(proxy): observe retry stash at attempt boundary

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

* fix(logging): keep terminal failure attribution when dropped attempts log on copies

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

* fix(logging): publish a dropped attempt's payload only while it is still the current attempt

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

* test(proxy): cover spend-log encoding error propagation

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

* fix(anthropic): skip adapter failure dispatch when the wrapped stream already logged it

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

* fix(router): cool down the final fallback attempt when its failure callbacks were blocked

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

* test(anthropic): import Final in adapter regression

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

* test(proxy): drop spend-log encoding expectation superseded by main's poison isolation

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

* fix(anthropic): finish the wrapped stream's failure log before surfacing the error

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

* fix(anthropic): wait for the wrapped stream's failure log to finish like the inline dispatch did

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

* test(anthropic): wait for the executor-run sync failure callback separately from the async caller boundary

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

* test(proxy): pin pass-through stream fixture start times

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

* fix(logging): publish failed attempt payload before awaiting failure callbacks

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

* fix(logging): publish failed attempt payload from the failure handler after baseline invalidation

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

* refactor(logging): drop unused discard_partial_usage_for_failure

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-20 21:46:45 -07:00
..
pre_call_checks fix(router): key prompt caching affinity on tools and the /v1/messages system prompt (#46411) 2026-10-19 19:59:09 -07:00
router_callbacks fix(router): count TPM/RPM usage before building rate-limit headers 2026-09-16 18:45:01 +00:00
access_windows.py feat(router): time-windowed team reservation of deployments via model_info.access_windows (#42398) 2026-09-22 13:22:15 -05:00
add_retry_fallback_headers.py refactor: expose public hidden_params accessors (#44668) 2026-10-07 00:01:33 -07:00
auto_router_model_naming.py feat(ui): streamline auto-router setup with deployed decision models (#45913) 2026-10-10 20:49:59 -07:00
auto_router_tuning_baseline.py fix(auto-router): separate tuning and select chained heuristic (#44928) 2026-10-06 13:43:53 -07:00
baseline_request.py fix(router): preserve native baseline identity and accounting (#44960) 2026-10-07 11:00:31 -07:00
batch_utils.py fix(batches): retry confirmed pre-accounting failures (#47625) 2026-10-20 14:12:26 -07:00
client_initalization_utils.py feat(router): reject with 429 when a deployment's max_parallel_requests slots are all in use 2026-09-17 18:14:09 +00:00
clientside_credential_handler.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
common_utils.py feat(router): route batch creates to mode batch deployments and expose supported_endpoints on /model_group/info (#47376) 2026-10-20 07:01:12 +00:00
cooldown_cache.py refactor(types): replace Any with proven types in 24 files (#46663) 2026-10-15 06:10:12 -07:00
cooldown_callbacks.py refactor: expose core private helpers under public names (#44871) 2026-10-07 00:05:27 +00:00
cooldown_handlers.py fix(videos)!: keep model_id in proxy video ids so per-model keys resolve on status/content (#44614) 2026-10-20 05:42:59 +00:00
deployment_pricing.py fix(router): price cost-based routing through model_info.base_model (#47617) 2026-10-20 22:20:16 +00:00
embedding_batch_size_check.py feat(embeddings): enforce per-request input batch limit (#44574) 2026-10-20 11:35:18 +00:00
fallback_event_handlers.py fix(router): log each dropped /v1/messages attempt through its own logging copy and open the retry without its usage (#45194) 2026-10-20 21:46:45 -07:00
get_retry_from_policy.py fix(router): retry a /v1/messages stream the provider drops before the first content chunk (#44276) 2026-10-05 22:53:39 +00:00
handle_error.py fix(router): record cooled deployments on the all-cooled failure row (#47120) 2026-10-19 13:18:20 -07:00
health_state_cache.py refactor(typing): clear 1143 basedpyright Any errors across 273 files (#46574) 2026-10-15 21:08:47 +00:00
kubernetes_pod_discovery.py feat(ui): show kubernetes pods on the model page (#44666) 2026-10-19 09:58:20 -07:00
pattern_match_deployments.py fix(videos)!: keep model_id in proxy video ids so per-model keys resolve on status/content (#44614) 2026-10-20 05:42:59 +00:00
prompt_caching_cache.py fix(router): skip non-dict tool entries in the prompt caching affinity key instead of raising (#47565) 2026-10-20 08:03:31 -07:00
ptu_shares.py feat(proxy): split Azure PTU deployments across teams with per-team PTU ceilings and PTU-hour reporting (#43043) 2026-10-15 18:44:30 +00:00
reasoning_effort_capability.py feat(router)!: apply each deployment's reasoning_effort_levels on every provider and route (#46601) 2026-10-20 04:34:55 +00:00
request_priority.py fix(router)!: schedule default_priority requests once and reject a malformed priority (#47498) 2026-10-20 08:00:57 +00:00
response_headers.py LiteLLM Minor Fixes & Improvements (11/26/2024) (#6913) 2024-11-28 00:01:38 +05:30
routing_groups.py feat(router): add group-scoped priority routing strategy (#42378) 2026-09-22 13:09:38 -07:00
routing_read_batch.py fix(otel): nest cache spans under their operation and name service spans by purpose (#44150) 2026-10-03 09:20:27 -07:00
search_api_router.py fix(types): drop restating docstrings, import search tool types at runtime, close the spend-table match 2026-09-21 13:44:58 -07:00
stream_usage_injection.py fix(streaming)!: send the include_usage chunk with an empty choices array (#47109) 2026-10-21 02:26:05 +00:00