Commit graph

18690 commits

Author SHA1 Message Date
Ishaan
fe7485c837
fix(ci): unblock dependency checks (#48055)
* fix(deps): upgrade Python dependencies

* fix(deps): lock patched Python dependency versions

* fix(otel): preserve JSON export with the updated SDK

* test(otel): cover JSON export with the updated SDK

* test(otel): update SDK log records and worker discovery

* fix(ui): align Lens peer dependency with patched Next.js

* fix(otel): hand the AgentOps exporter a session it owns for the JWT header

OpenTelemetry 1.45 moved the OTLP HTTP exporter's requests.Session behind a
transport, so writing the minted JWT to the exporter's private _session raised,
was swallowed, and every AgentOps batch went out unauthenticated. The exporter
now creates the session, passes it through the public session= argument and
sets the Authorization header on it

The tests now drive a real export, serving the token endpoint with respx and
capturing the OTLP POST with requests_mock, instead of reading SDK internals

* fix(otel): skip FastAPI instrumentation once the app has started

opentelemetry-instrumentation-fastapi 0.66b1 wraps build_middleware_stack instead
of calling add_middleware, so instrumenting an app whose middleware stack is
already built no longer raises. The call silently did nothing while we still
registered the global response propagator. Check the built stack up front and
skip both, which is what the existing test expects

* test(otel): assert exported requests and span flags instead of SDK internals

OpenTelemetry 1.45 dropped the exporter's private _headers and sets the W3C
random-trace-id flag on new traces, so traceresponse ends in -03. The Langtrace
and Langfuse tests now export through requests_mock and check the URL and headers
the collector receives, and the traceresponse tests expect the server span's own
trace flags

* fix(deps): bump dashboard zod to 4.6.6 and make the vendored Lens use it

zod 4.6.5 is flagged by the dependency scan and blocks osv-scan on every PR. A
root bump alone leaves the vendored Lens tarball, which pins zod 4.6.5 exactly,
on its own nested copy, so a top-level zod override points every consumer at the
root version and the lock carries a single zod 4.6.6

* test(ui): stub the tag summary fetch the client panel retry refreshes

Retrying the model breakdown refetches /tag/summary, which this integration test
left on the real network. When that request failed before the assertion, the
panel switched to its summary error and unmounted the model row, so the test
passed or failed by timing

* fix(deps): drop the duplicated Lens override from the dashboard package.json

* style(ui): format the client panel fetch stub

---------

Dependency files (pyproject.toml, uv.lock, ui/litellm-dashboard/package.json and package-lock.json) were left out of this pick and are re-derived on the 1.103 line in the following commits
ClientUsagePanel.integration.test.tsx does not exist on this line, so its hunk was dropped
The traceresponse hunks in test_otel_v2_mount.py target tests this line does not have, so that file is unchanged
The Langtrace export test the squash edits in test_litellm_logging.py is not on this line, so that file is unchanged
OTLPJsonSpanExporter on this line never took certificate_file or session, so the fix keeps that signature and adds only the header merge, max_request_size and the export override
The new JSON exporter tests intercept the collector with requests_mock.Mocker instead of injecting a session, and the components test imports Final, requests and requests_mock for them
The TLS export tests and their log record import hunks are not on this line, so those hunks were dropped
test_opentelemetry.py imports Final for the worker thread lookup and keeps its trace import without SpanLimits, LogRecord, Resource or the TLS helpers

Co-authored-by: Yuneng Jiang <yuneng@berri.ai>
(cherry picked from commit 8f9894fbd6)
2026-10-23 15:08:56 -07:00
devin-ai-integration[bot]
3ba758322d
fix(otel): record the GenAI exception event through the Logs API on both OpenTelemetry lines (#42431)
* fix(otel): record the GenAI exception event without the removed Events API

OpenTelemetry removed opentelemetry._events in 1.44.0, so the three imports of
it broke 7 modules under litellm.integrations.otel, including the entry point.
Two things then failed quietly: with LITELLM_OTEL_V2 set the otel callback
resolved to None and nothing was exported, and with it unset the newrelic
callback was dropped as well, because that branch imports the v2 logger
ungated

Build and emit the event through the Logs API, which both lines carry. The
event name keeps riding the event.name attribute: the event_name log record
field that replaces it only exists from 1.44.0, and this package pins 1.28.0,
so the attribute is the only form both can write. It is also what the Events
API wrote, so exported events keep their shape

Emitting a plain record drops the default the Events SDK applied, so the
timestamp now falls back to time_ns() here

* style(otel): trim the event name key and regression test prose

Keep only the constraint a reader cannot infer from the code, that the
event_name record field does not exist on the pinned OpenTelemetry line

* fix(otel): export the GenAI exception event on both OpenTelemetry 1.28 and 1.44 lines

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

* refactor(otel): drop the record selection comment

Co-authored-by: Pawan-Shahane <shahanepawan511@gmail.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

* style(otel): collapse the record selection conditional for ruff format

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

* style(otel): build the record fields with a dict literal

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

* fix(otel): set the native event_name on the 1.44 log record

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

* style(otel): spell out the record kwargs so the type gate sees each call

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

* style(otel): suppress the version-gated kwargs for the type gate

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

* refactor(otel): drop the version-window prose and correct the event_name suppression reason

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

* style(otel): wrap the compat test docstring to the line limit

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

---------

test_otel_v2_components.py on this line has no TLS export tests, so the span context helper and the two new log event tests land at the end of the file instead of ahead of the TLS helpers

Co-authored-by: Pawan-Shahane <shahanepawan511@gmail.com>
Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(cherry picked from commit 238f434153)
2026-10-23 15:08:25 -07:00
ryan-crabbe-berri
1ca58f46ad
fix(security): stop trusting the compromised eMudhra root CA (#47670)
Update certifi, Rust trust roots, and the Docker CA bundle to remove emSign ECC Root CA - G3

Require the fixed certifi release in litellm-core and retain libssl3 in Docker runtimes for Prisma compatibility

Co-authored-by: shin-berri <shin-laptop@berri.ai>
(cherry picked from commit f2118fc767)
2026-10-23 11:58:09 -07:00
Yuneng Jiang
d7b606ac91
Merge stable/1.103.x (#47743, #47763) into litellm_backport_p0_batch_translation_cost_stable_1_103_x
# Conflicts:
#	litellm/model_prices_and_context_window_backup.json
#	litellm/proxy/hooks/proxy_track_cost_callback.py
#	litellm/responses/litellm_completion_transformation/streaming_iterator.py
#	model_prices_and_context_window.json
#	tests/test_litellm/proxy/hooks/test_proxy_track_cost_callback.py
#	tests/test_litellm/test_utils.py
#	uv.lock
2026-10-21 00:55:49 -07:00
yuneng-jiang
a3c62f9f48
Merge pull request #47763 from BerriAI/litellm_backport_lit145_stable_1_103_x
fix(proxy): backport the LIT-145 spend tracking fixes to stable/1.103.x and bump to 1.103.7
2026-10-21 00:54:25 -07:00
devin-ai-integration[bot]
8948ab4d0c fix(logging): spilled over PTU requests without base_model log the served model's rates (#47655)
Backport of #47655 to stable/1.103.x

Cherry-picked from 6b1f4ed46f (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
bd68d04aae fix(proxy): price a 499 spend row only for the proxy's own disconnect, and never after the call already completed (#47652)
Backport of #47652 to stable/1.103.x

Cherry-picked from ffb7133d32 (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
db8f45dc87 fix(proxy): price earlier timed-out retry attempts at their own deployment's model rate (#47648)
Backport of #47648 to stable/1.103.x

Cherry-picked from 27efc387f7 (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
7e630b07d4 fix(proxy): price a billed failure row at the deployment's long-context tier like the success path (#47647)
Backport of #47647 to stable/1.103.x

Cherry-picked from 70ece3501f (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
8147f8d68f fix(proxy): scope the search free-tier counter to the request's provider (#47646)
Backport of #47646 to stable/1.103.x

Cherry-picked from 640f2d865b (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
6dd7b489de fix(proxy): strip caller base_model from spend pricing and reject lone surrogates in multipart metadata (#47639)
Backport of #47639 to stable/1.103.x

Cherry-picked from 2a268951c4 (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
aa838f931b fix(router): price cost-based routing through model_info.base_model (#47617)
Backport of #47617 to stable/1.103.x

Cherry-picked from 0c6731eb5c (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
be085c9028 fix(proxy): flush the spend-log queue only for Responses turns the bridge reads back (#47614)
Backport of #47614 to stable/1.103.x

Cherry-picked from 423362bab2 (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
bb2ec9a422 fix(proxy): bill partial Responses API streams when the client disconnects (#47611)
Backport of #47611 to stable/1.103.x

Cherry-picked from f0ae445cc4 (main)
2026-10-21 03:37:25 +00:00
devin-ai-integration[bot]
d7e344440c fix(proxy): requeue spend logs when a Postgres restart tears down the prisma engine mid-write (#47571)
Backport of #47571 to stable/1.103.x

Cherry-picked from ccd0d4ea49 (main)
2026-10-21 01:55:14 +00:00
devin-ai-integration[bot]
6120ddf786 fix(litellm_proxy): forward tool_config cache_control_injection_points to the downstream proxy (#47570)
Backport of #47570 to stable/1.103.x

Cherry-picked from 04e9ed4c52 (main)
2026-10-21 01:55:14 +00:00
devin-ai-integration[bot]
20c0e82370 fix(logging): report the pay-as-you-go entry billing used on Azure PTU spillover (#47567)
Backport of #47567 to stable/1.103.x

Cherry-picked from 4222523261 (main)
2026-10-21 01:55:14 +00:00
devin-ai-integration[bot]
c6ee8e0d54 feat(proxy): write one spend row per billed timed-out retry attempt (#47416)
Backport of #47416 to stable/1.103.x

Cherry-picked from ad8ca533ca (main)
2026-10-21 01:55:14 +00:00
devin-ai-integration[bot]
c23f17dcff feat(proxy): opt-in free-tier allowance for Google Search grounding spend (#47494)
Backport of #47494 to stable/1.103.x

Cherry-picked from e63aec029a (main)
2026-10-21 01:55:14 +00:00
mrinal-berri
de896efc8e fix(count_tokens): fall back to Mantle on Bedrock 404 (Bedrock part of #46430)
(cherry picked from commit 08a24d391a)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-21 01:51:08 +00:00
devin-ai-integration[bot]
71118e893e fix(bedrock): count tokens on bedrock-mantle when bedrock-runtime cannot count a Claude model (#45317)
(cherry picked from commit 577d74c1ce)
2026-10-21 01:50:30 +00:00
Mrinal Chanshetty
f419a596a7 test: retarget cost-map tests to the rows mirrored from main
Test-only. Takes main's version of test_shipped_tool_search_rule_fills_mapped_claude_entries_without_flag, test_cache_control_injection_tool_config_honors_ttl_for_regional_model_lacking_own_pricing and test_get_model_info_gemini; retargets three tests whose rows main changed or removed (gemini-2.0-flash-001 -> gemini-2.5-flash, gpt-5.1-codex-max -> gpt-5.2 for xhigh, gpt-5.1-chat-latest -> gpt-5.1); registers this line's unsupported providers in PROVIDER_LABELS_WITHOUT_A_MODEL_SET; adds extraction mode, main's enum lists and every key main's map uses to test_utils' inline schema, plus main's seedance cost exceptions.
2026-10-20 18:20:07 -07:00
devin-ai-integration[bot]
81b9ab7e51 feat(proxy): lift every earlier billed timed-out retry attempt onto the failed request (#47490)
Backport of #47490 to stable/1.103.x

Cherry-picked from 7a4a255d1d (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
e7e1aa8984 fix(vertex_ai): bill per-character text embeddings on Google's billableCharacterCount (#47460)
Backport of #47460 to stable/1.103.x

Cherry-picked from d29200fa05 (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
183e1c8a49 fix(logging): do not stamp a provider handoff when pre_call runs for a proxy-only failure (#47456)
Backport of #47456 to stable/1.103.x

Cherry-picked from 3fa1587f43 (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
c793111f40 fix(proxy): price a billed failure row at the deployment's own rate like the success path (#47437)
Backport of #47437 to stable/1.103.x

Cherry-picked from fb555b00dc (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
6a65634599 fix(proxy): price the estimated input tokens on a client-cancelled (499) request's spend row (#47436)
Backport of #47436 to stable/1.103.x

Cherry-picked from 0e7c4908bb (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
543f1899d4 fix(proxy): price the estimated input tokens on a read-timed-out request's spend row (#47435)
Backport of #47435 to stable/1.103.x

Cherry-picked from 7b76b2b15f (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
37b7c14830 feat(router): let a deployment set a timeout per service_tier (#47406)
Backport of #47406 to stable/1.103.x

Cherry-picked from 62ce515d15 (main)
2026-10-21 00:45:27 +00:00
devin-ai-integration[bot]
8147fae4d6 fix(anthropic): apply tool_config cache-control points on chat and /v1/messages (#47394)
Backport of #47394 to stable/1.103.x

Cherry-picked from 93b79d2587 (main)
2026-10-21 00:45:27 +00:00
joshua-berri
56b0f9c3c2 fix(batches): retry confirmed pre-accounting failures (#47625)
Backport of the router charge-once part of #47625 to stable/1.103.x, from main 6564a490f4 via the stable/1.104.x port 91cb4a55b5.
The batch accounting retry already landed in the earlier #47625 commit; this adds the charge-once helpers moved into router_utils.batch_utils and RouterBudgetLimiting's per-batch marker using the line's duration_in_seconds window. The shared-worker test waits for this line's background Redis push before reading spend.
2026-10-20 17:36:04 -07:00
daqiangganjun
823e533f12 fix(router): count provider budget spend on every API surface (#38172)
Backport of #38172 to stable/1.103.x, from main 8327cd6d47 via the stable/1.104.x port 844e02f602.
Prerequisite for #47625: RouterBudgetLimiting reads the provider from the logging payload, so the batch poller's aretrieve_batch events reach router budgets at all. Test file placed at tests/test_litellm/router_strategy/ on this line.
2026-10-20 17:36:04 -07:00
mateo-berri
150b843cc2 fix(proxy): charge a finished batch once against per-model budgets
Prerequisite for the #47625 backport to stable/1.103.x, from main 8a33b37c39.
Adds the per-counter batch charge-once marker that #47625 moves into router_utils.batch_utils.
2026-10-20 17:36:04 -07:00
joshua-berri
a83924f8c8 fix(bedrock): resolve profile pricing before applying custom rates (#47419)
Backport of #47419 to stable/1.103.x.
Cherry-picked from 3124797399 (main), via the stable/1.104.x port c88cf13cd3.
Adapted to the line: the cost-calc threshold changes are applied to this line's loop without the 1.104 base's batch-tier helpers; ModelNotMappedError from #42497 is hand-ported (exception class plus the get_model_info raise and re-raise) so registration lookups can tell an unmapped model from a transient failure; tests whose catalog rows (gpt-6-sol, gpt-6-luna from #42515) are not on this line are retargeted to amazon.nova-micro-v1:0 and gpt-5.6-sol profiles; the test conftest also snapshots _runtime_registered_fields. Billing checked unchanged on this line for a cross-provider alias (keeps cache prices) and for unpriced openai/ft: and bedrock/ap-northeast-1 deployments.
2026-10-20 17:35:56 -07:00
joshua-berri
32bfdeaf31 fix(batches): retry confirmed pre-accounting failures (#47625)
Backport of #47625 to stable/1.103.x.
Cherry-picked from 6564a490f4 (main).
Adapted to the line: the batch accounting retry (track_batch_accounting in proxy_track_cost_callback and its use in check_batch_cost) is ported, while this PR's router-budget charge-once changes in router_utils/batch_utils, budget_limiter and model_max_budget_limiter are left out, since they refactor charge-once code from main (8a33b37c39, 9acf09f60d) that is not on this line. The write-started mark keys on a budget reservation only, as the search free tier is not on this line. Tests use the line's _ProxyDBLogger, and the router-budget and search-allowance assertions are dropped.
2026-10-20 17:35:56 -07:00
devin-ai-integration[bot]
1f47a4b5e7 fix(cost): bill Gemini audio/image/video prompt tokens at the resolved input rate when no modality rate exists (#47373)
Backport of #47373 to stable/1.103.x

Cherry-picked from 09e9193514 (main)
2026-10-20 23:56:56 +00:00
joshua-berri
2f3d44b469 fix(batch): preserve recoverable costs and prevent poll starvation (#47190)
Backport of #47190 to stable/1.103.x.
Cherry-picked from 0ede0e10e7 (main).
Ported from the stable/1.104.x hand-port (3b7b5ab319). Carries the small typed table helpers that port relies on (_ManagedObjectRow protocol and the _managed_object_table/_user_table/_token_table/_team_table accessors, from the stable/1.104.x base). The poll-starvation integration test is dropped since its base and harness (#42622, integration/_support/prisma_clients) are not on this line. The 404-recovery unit test resets the litellm callback lists so a callback left by an earlier test cannot shadow it.
2026-10-20 16:43:54 -07:00
joshua-berri
8bbd2c0601 fix(batches): preserve spend across malformed output and metadata failures (#47184)
Backport of #47184 to stable/1.103.x.
Cherry-picked from 3fb95c8fe3 (main).
Ported from the stable/1.104.x hand-port (f7b0b09e75) onto this line's poller, which keeps LiteLLM_ManagedObjectTable and direct prisma table access. Brings the managed-files postgres_storable_json sanitizer from #45703 that this fix needs. Test files keep the line's content plus this PR's own cases.
2026-10-20 16:43:54 -07:00
Paul El Khoury
ab69d8489c fix(openai): drop unsupported reasoning effort for gpt-6.1-sol (#43935)
Backport of #43935 to stable/1.103.x.
Cherry-picked from d7620c77a1 (main).
Ported from the stable/1.104.x adaptation (54f9259b40): the bridge check lives in litellm/main.py and the effort helpers keep the line's private names. Cost-map edits replay only this PR's reasoning-effort flags (gpt-6-astra and gpt-5.3-codex rows), plus supports_none_reasoning_effort=false on the azure gpt-6.1-sol rows from #46377, which the fix's tests key on. Tests built on gpt-6-sol rows (#42515), the Foundry tools-to-Responses bridge, or a full azure/eu/gpt-6-astra row are dropped since none of those are on this line.
2026-10-20 16:43:54 -07:00
berriai-litellm-provider-info-sync[bot]
e2072b7aa8 chore(cost-map): add openai gpt-6.1-sol from the pricing page (#43738)
Backport of #43738 to stable/1.103.x.
Cherry-picked from a3552c451b (main).
Prerequisite for #43935: adds the gpt-6.1-sol entry that fix corrects. Cost-map conflict resolved by appending the new entry.
The cost-map schema and its schema test also allow the *_above_272k_tokens_batches keys this row carries, which main already allows.
2026-10-20 16:43:54 -07:00
Kerry Lu
396b817738 fix(logging): report the cost-map entry billing used in model_map_information (#47182)
Backport of #47182 to stable/1.103.x.
Cherry-picked from 1212260ab1 (main).
Test-file conflict resolved by keeping the line's file and appending this PR's tests.
Calls the line's _select_model_name_for_cost_calc, which is the private name on this line.
2026-10-20 16:23:41 -07:00
joshua-berri
b10a4e6e1a fix(batches): handle empty models when logging batch cost (#47155)
Backport of #47155 to stable/1.103.x.
Cherry-picked from 4143e0869e (main).
2026-10-20 16:23:41 -07:00
Mrinal Chanshetty
5a3cfacab2 test(integration): add the vertex service-account helper the safeguards wire test imports
Backport prerequisite for #43955 on stable/1.103.x.
Copied from tests/integration/_support/vertex.py on main (added by #43900).
2026-10-20 16:23:41 -07:00
devin-ai-integration[bot]
29ea13d508 fix(anthropic): add the dangerous-tool-use beta when a /v1/messages request carries safeguards (#43955)
Backport of #43955 to stable/1.103.x.
Cherry-picked from eb5295b20c (main).
Adapted to the line: the transformation lives under anthropic/experimental_pass_through, the integration tests live under tests/integration/providers, and the CLI-beta wire assertion change is dropped since that test is not on the line.
The github_copilot safeguards test uses the line's authenticator mock, since per-user Copilot sessions are not on this line.
2026-10-20 16:23:41 -07:00
mrinal-berri
2369b3ce52 fix(databricks): send a single plain text block as a string for non-Claude models (#45686)
Backport of #45686 to stable/1.103.x.
Cherry-picked from 1aee1a21ed (main).
Adapted to the line: unit test ported into tests/test_litellm; the databricks wire-test additions and the bridge-flag test edit are dropped since neither base test is on this line.
2026-10-20 16:15:54 -07:00
mrinal-berri
88ecaa08d9 fix(databricks): keep #/$defs refs in json_schema response_format (#45659)
Backport of #45659 to stable/1.103.x.
Cherry-picked from fa7ad80a62 (main).
Adapted to the line: unit tests ported into tests/test_litellm; the databricks chat wire-test additions are dropped since that file is not on this line.
2026-10-20 16:15:54 -07:00
devin-ai-integration[bot]
f02a05fb32 fix(model_prices): consolidate claude-haiku-5-5 over-100k pricing and capability flags (#45151)
Backport of #45151 to stable/1.103.x.
Cherry-picked from 46d440ae96 (main).
Adapted to the line: the model-catalog Rust crate is not on this line so its two files are dropped; tests live under tests/test_litellm; conflicts resolved by keeping the line's fields plus the over-100k fields this PR adds.
Prerequisite from #39861 ported minimally: the standard above-threshold loop skips *_batches keys, so a non-batch request over 100k is not billed at the batch tier. The batch long-context tier tests are dropped since this line's batch cost path has no long-context tiers, and the runtime profile test calls the line's private _is_adaptive_thinking_model.
2026-10-20 16:15:54 -07:00
Krrish Dholakia
5944ba05f7 feat(model_prices): add claude-haiku-5-5 model pricing (#45108)
Backport of #45108 to stable/1.103.x.
Cherry-picked from 5cd1126870 (main).
Prerequisite for #45151: adds the claude-haiku-5-5 entries that fix corrects. Cost-map conflicts resolved by appending the new entries; schema test lives under tests/test_litellm on this line.
The cost-map schema and its schema test also allow cache_creation_input_token_cost_batches, cache_read_input_token_cost_batches and supports_anthropic_compaction, which these entries carry and main already allows.
2026-10-20 16:15:54 -07:00
devin-ai-integration[bot]
a963fc135f fix(responses): report truncated bridged output as incomplete and echo request params (#44460)
Backport of #44460 to stable/1.103.x.
Cherry-picked from f81a3f5243 (main).
Conflict: took main's terminal_event declaration over the line's completed_event comment form.
2026-10-20 15:50:12 -07:00
Hektor Jacynycz Garcia
cd788ce1aa
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-20 01:06:21 -07:00