Skip to content

fix(responses): unwrap completed response for cost tracking on bridge path - #38188

Closed
willcai1984 wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
willcai1984:fix/responses-bridge-cost-tracking-standard-logging
Closed

willcai1984 wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
willcai1984:fix/responses-bridge-cost-tracking-standard-logging

Conversation

@willcai1984

Copy link
Copy Markdown

What does this PR do?

Fixes a Cost tracking failed: standard_logging_object not found error that occurs when a non-streaming caller drains a Responses API streaming iterator — most notably the Anthropic /v1/messages to Responses API bridge path against force-streaming providers like chatgpt/*.

The problem

When Claude Code sends a streaming /v1/messages request to a ChatGPT model, the bridge calls litellm.aresponses(stream=True). The inner Responses API streaming iterator completes and calls _log_completed_response, which passes self.completed_response (a ResponseCompletedEvent wrapper) to dispatch_success_handlers.

The success handlers only unwrap ResponseCompletedEvent to ResponsesAPIResponse in their assembled-stream branch, which a non-streaming caller draining the iterator never reaches. So _success_handler_helper_fn sees result=None (for the proxy deferred-logging path) or the unrecognised wrapper event, never builds standard_logging_object, and _PROXY_track_cost_callback raises:

Cost tracking failed for model=gpt-5.6-luna. Debug info - standard_logging_object not found

This produced ~550 error log lines per 6 hours in production (51% of all successful /v1/messages requests). Verified harmless: LiteLLM_SpendLogs and LiteLLM_DailyUserSpend reconcile exactly — the SpendLog row is written later by the iterator's own async success callback.

The fix

Add _response_for_success_logging() that copies self.completed_response via model_dump/model_validate (preserving the existing fix for #17192) and unwraps .response to the inner ResponsesAPIResponse before passing it to success handlers. This lets _is_recognized_call_type_for_logging match in the non-streaming branch so standard_logging_object and response_cost are built correctly.

Related

Test plan

  • Added test_completed_response_unwrapped_for_success_logging (async) and test_sync_completed_response_unwrapped_for_success_logging (sync)
  • Existing tests in test_streaming_iterator.py still pass (TTFT, transport-error regressions)
  • CI: make format and make lint and make test-unit

@CLAassistant

CLAassistant commented Aug 25, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR unwraps completed Responses API events before success logging so force-streamed responses drained by non-streaming bridge callers can produce standard cost records. It also includes broader provider, health-check, authentication, stream-reconstruction, passthrough-header, and Anthropic token-counting changes.

  • Adds completed-response copying and unwrapping for synchronous and asynchronous success handlers.
  • Reconstructs omitted Responses output items from terminal stream events.
  • Adds per-deployment ChatGPT authentication-file selection and request normalization.
  • Updates health-check mode selection, Anthropic count-token endpoint routing, and passthrough compression-header handling.

Confidence Score: 4/5

The PR appears safe to merge from a behavioral perspective, with only non-blocking repository-style issues in the newly added source.

The completed-response logging fix and surrounding stream changes have focused regression coverage, and the accepted findings concern added comments and annotation syntax rather than runtime correctness.

Files Needing Attention: litellm/llms/chatgpt/responses/transformation.py, litellm/responses/streaming_iterator.py

Important Files Changed

Filename Overview
litellm/responses/streaming_iterator.py Adds response-output reconstruction and unwraps completed events for success logging; no concrete blocking failure was established.
litellm/llms/chatgpt/responses/transformation.py Adds per-auth-file credential selection, request normalization, and SSE output cleanup, with repository-style violations in the new implementation.
litellm/main.py Allows provider configurations to select a default health-check mode when callers omit one.
litellm/llms/anthropic/count_tokens/transformation.py Resolves the count-token endpoint from a custom Anthropic API base while preserving the default endpoint.
litellm/passthrough/utils.py Prevents client Accept-Encoding negotiation from being forwarded to upstream passthrough targets.
tests/test_litellm/responses/test_streaming_iterator.py Adds sync and async regression coverage confirming success handlers receive an unwrapped ResponsesAPIResponse.

Reviews (1): Last reviewed commit: "fix(responses): unwrap completed respons..." | Re-trigger Greptile

Comment on lines +71 to +75
# v3: v1.94.0 的 anthropic->responses 调用链会丢失 deployment 的
# chatgpt_auth_file 参数(get_litellm_params 白名单过滤 + 中途 kwargs 重建),
# 导致多席位全部回退默认 auth.json。此处按 model_info.id 反查 auth 文件兜底。
# v7: /v1/messages 桥接路径在 config 模式下同样丢参(terra 实为命中本映射才正常),
# 新增 chatgpt/gpt-5.6-luna 席位(2026-08-19 上线,与 terra 同批 seat01+seat03)。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 New source comments violate policy

The newly added implementation-history comment conflicts with the repository's no-new-comments convention and increases the maintenance burden; the same pattern occurs in the new logging and token-counting code.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

self._authenticators: Dict[str, Authenticator] = {}
self.authenticator = self._get_authenticator(None)

def _get_authenticator(self, auth_file: Optional[str]) -> Authenticator:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Legacy optional annotation syntax

The new authentication helpers use Optional[str] and Optional[GenericLiteLLMParams] instead of the repository-standard T | None syntax, adding an unnecessary Optional import and diverging from the established typing convention.

Rule Used: In this repo, prefer str | None (PEP 604 union s... (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread litellm/litellm_core_utils/get_litellm_params.py Outdated
@veria-ai

veria-ai Bot commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

… path

When a non-streaming caller drains a Responses API streaming iterator
(e.g. the Anthropic /v1/messages -> Responses API bridge against a
force-streaming provider like chatgpt/*), _log_completed_response
passed the ResponseCompletedEvent wrapper to dispatch_success_handlers.
The success handlers only unwrap it in their assembled-stream branch,
which a non-streaming caller never reaches — so standard_logging_object
was never built, and _PROXY_track_cost_callback raised
"Cost tracking failed: standard_logging_object not found".

Fix: add _response_for_success_logging() that copies and unwraps
.completed_response to the inner ResponsesAPIResponse before passing
it to the success handlers. This lets _is_recognized_call_type_for_logging
match in the non-streaming branch so standard_logging_object is built.

Related: BerriAI#36426, BerriAI#36450
@willcai1984

Copy link
Copy Markdown
Author

Rebuilt this branch from the latest litellm_internal_staging with a single commit containing only the root-cause fix (_response_for_success_logging unwrap in responses/streaming_iterator.py) plus its focused test.

The previous head accidentally carried a batch of unrelated local ops patches (health-check, count-tokens, seat mapping, transformation snapshots), which is what made the diff 14 files and triggered the conflicting state and the earlier review threads — those all applied to now-removed commits.

New diff: 2 files, +83/−7. Happy to re-request review once CI is green.

@willcai1984
willcai1984 force-pushed the fix/responses-bridge-cost-tracking-standard-logging branch from 2466d14 to 6a4e558 Compare August 28, 2026 07:43
@codecov

codecov Bot commented Aug 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.30769% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
litellm/responses/streaming_iterator.py 92.30% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing willcai1984:fix/responses-bridge-cost-tracking-standard-logging (6a4e558) with litellm_internal_staging (ca0b951)

Open in CodSpeed

@willcai1984

Copy link
Copy Markdown
Author

Closing after rebasing onto current staging revealed the original bridge cost-logging failure is already fixed upstream: Logging._handle_anthropic_messages_response_logging now unwraps ResponseCompletedEvent/ResponseIncompleteEvent/ResponseFailedEvent before translating the inner ResponsesAPIResponse (present in the PR base at ca0b951, lines 3787-3793). The focused tests on this PR failed because their mock fixture emits a mocked event rather than the real Pydantic event, while the integration bridge behavior is covered by the native logging path. No additional iterator-layer unwrap is needed on current staging. The local v1.97.0 hotpatch remains necessary only until production upgrades to a version containing that upstream handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants