Skip to content

fix(responses): log completed response, not the event, for non-streaming callers - #36450

Closed
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
litellm_fix_responses_bridge_spendlog_36426
Closed

devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
litellm_fix_responses_bridge_spendlog_36426

Conversation

@devin-ai-integration

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Non-streaming chat calls through the responses bridge wrote no SpendLogs row
  • Every such call logged standard_logging_object not found instead
  • Only hits providers whose responses sub-call always returns SSE

How it solves it:

  • Responses stream iterator now logs the completed response, not the wrapper event
  • Only when the logging object is not in stream mode
  • Streaming callers keep getting the event, so their path is unchanged

User Flow

Before: a developer calling a mode: responses model over /v1/chat/completions without streaming gets a good answer that is never billed

  1. They send POST https://litellm-domain/v1/chat/completions for a chatgpt/* model, no stream field
  2. They get HTTP 200 with the assistant text and a correct usage block
  3. They open https://litellm-domain/ui/?page=logs and their request is missing
  4. The same request sent with "stream": true does show up, so spend silently depends on the request shape

After: the same non-streaming request is billed like the streaming one

  1. They send the same POST https://litellm-domain/v1/chat/completions with no stream field
  2. They get the same HTTP 200, text and usage
  3. https://litellm-domain/ui/?page=logs now shows the request with its tokens and spend
  4. Streaming requests still show up exactly as before, one row each

Relevant issues

Fixes #36426

Linear ticket

Pre-Submission checklist

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Reproduced against a local proxy backed by postgres, with a mode: responses model whose provider always answers /responses with SSE, which is what forces the bridge to drain a streaming iterator for a non-streaming caller. Not a paid provider call: the failure needs a provider that unconditionally sets stream = True on the responses request, so the backend here is a local SSE server returning a fixed response.completed payload with input_tokens: 11 / output_tokens: 7, and the model is priced in the config so a real cost is computed.

Before, at 457be8f (parent of this branch):

$ curl -s -X POST localhost:4000/v1/chat/completions -H "Authorization: Bearer $KEY" \
    -H 'Content-Type: application/json' \
    -d '{"model":"chatgpt/gpt-5.6-terra","messages":[{"role":"user","content":"baseline"}]}'
{"id":"chatcmpl-...","model":"chatgpt/gpt-5.6-terra","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"hello from fake backend","role":"assistant"}}],"usage":{"completion_tokens":7,"prompt_tokens":11,"total_tokens":18}}

$ psql -c 'select count(*) from "LiteLLM_SpendLogs";'   # unchanged, no row for the call above
 count
-------
     3

$ grep -c 'Cost tracking failed' proxy.log
1
# Error in tracking cost callback - Cost tracking failed for model=gpt-5.6-terra.
# Debug info - standard_logging_object not found

After, at ca767b4:

$ curl -s -X POST localhost:4000/v1/chat/completions -H "Authorization: Bearer $KEY" \
    -H 'Content-Type: application/json' \
    -d '{"model":"chatgpt/gpt-5.6-terra","messages":[{"role":"user","content":"final check"}]}'
{"id":"chatcmpl-ff76b496-21ff-44db-8db6-5c3fe25a4688","model":"chatgpt/gpt-5.6-terra","object":"chat.completion","choices":[{"finish_reason":"stop","index":0,"message":{"content":"hello from fake backend","role":"assistant"}}],"usage":{"completion_tokens":7,"prompt_tokens":11,"total_tokens":18}}

$ psql -c 'select call_type,status,spend,prompt_tokens,completion_tokens,"endTime" from "LiteLLM_SpendLogs" order by "endTime" desc limit 1;'
 call_type | status  |  spend  | prompt_tokens | completion_tokens |        endTime
-----------+---------+---------+---------------+-------------------+------------------------
 responses | success | 2.5e-05 |            11 |                 7 | 2026-08-10 19:12:56.77

$ grep -c 'Cost tracking failed' proxy.log
0

Streaming on the same build still bills exactly one row per request, with the same tokens and spend, so the fix does not double count.

Type

🐛 Bug Fix

Caveats (if any)

  • Untested against a paid provider that force-streams responses

Link to Devin session: https://app.devin.ai/sessions/5efe36ef464e4140aa82e37d6cf2680d

@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@greptile-apps

greptile-apps Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR fixes spend logging when a non-streaming caller drains an SSE Responses API iterator by passing the completed response, rather than its wrapper event, to success handlers

  • Preserves wrapper events for streaming logging paths
  • Adds focused regression coverage for streaming and non-streaming callers
  • Ratchets down lint and type-discipline budgets

Confidence Score: 5/5

The PR appears safe to merge, with the non-streaming logging fix isolated from the existing streaming path

The changed branch passes the completed Responses API payload to non-streaming success handlers while preserving the completion event for streaming handlers, and focused tests cover both outcomes

Important Files Changed

Filename Overview
litellm/responses/streaming_iterator.py Selectively unwraps completed response events for non-streaming success logging while retaining copied wrapper events for streaming callers
tests/test_litellm/responses/test_streaming_iterator.py Adds focused asynchronous coverage proving the logged object shape for both stream modes
ruff-strict-budget.json Ratchets the S110 allowance down by one after removing the broad exception suppression
type-discipline-budget.json Ratchets the LIT010 allowance down by two for the newly annotated final variables

Reviews (1): Last reviewed commit: "fix(responses): log completed response, ..." | Re-trigger Greptile

@codecov

codecov Bot commented Aug 10, 2026 •

Copy link
Copy Markdown

Codecov Report

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

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

📢 Thoughts on this report? Let us know!

@codspeed

codspeed Bot commented Aug 10, 2026 •

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_fix_responses_bridge_spendlog_36426 (6bf2156) with litellm_internal_staging (ade805e)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (c408285) during the generation of this report, so ade805e was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

…ing callers

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration
devin-ai-integration Bot force-pushed the litellm_fix_responses_bridge_spendlog_36426 branch from ca767b4 to 6bf2156 Compare August 10, 2026 21:52
willcai1984 added a commit to willcai1984/litellm that referenced this pull request Aug 28, 2026
… 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
@dalzio

dalzio commented Sep 7, 2026 •

Copy link
Copy Markdown

Could you please close this PR in favor of #40139? The replacement is now conflict-free.

Posted for GP by his AI assistant

@yuneng-berri
yuneng-berri deleted the branch main September 13, 2026 04:40
@yuneng-berri yuneng-berri reopened this Sep 13, 2026
@devin-ai-integration
devin-ai-integration Bot changed the base branch from litellm_internal_staging to main September 23, 2026 15:11
@devin-ai-integration
devin-ai-integration Bot requested a review from a team September 23, 2026 15:11
@mateo-berri

Copy link
Copy Markdown
Contributor

Superseded by #41235, which keeps non-streaming callers off the streaming iterator entirely and lands one spend row per call

@mateo-berri mateo-berri closed this Oct 6, 2026
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.

[Bug]: Responses-API bridge drops the SpendLogs row for non-streaming /v1/chat/completions (standard_logging_object not found)

4 participants