Skip to content

fix: request streaming usage in Anthropic translation - #826

Open
EleanorWho wants to merge 3 commits into
praxis-proxy:mainfrom
EleanorWho:fix-803-streaming-usage
Open

fix: request streaming usage in Anthropic translation#826
EleanorWho wants to merge 3 commits into
praxis-proxy:mainfrom
EleanorWho:fix-803-streaming-usage

Conversation

@EleanorWho

Copy link
Copy Markdown

Summary

  • Set stream_options.include_usage = true on the translated Chat Completions request when an Anthropic streaming request is forwarded, so the backend returns token usage in its final chunk.
  • Extract prompt_tokens, completion_tokens, and prompt_tokens_details.cached_tokens from the upstream usage chunk and map them into the Anthropic message_delta usage fields.
  • Apply the same cache subtraction logic as the non-streaming path (input_tokens = prompt_tokens - cached_tokens).

Closes #803

Test plan

  • streaming_request_includes_usage_option — verifies stream_options.include_usage is injected for stream: true
  • non_streaming_request_omits_stream_options — verifies no stream_options without streaming
  • stream_false_omits_stream_options — verifies no stream_options for stream: false
  • message_delta_usage_matches_anthropic_schema — verifies output_tokens and input_tokens are mapped
  • message_delta_usage_with_cached_tokens — verifies input_tokens = prompt_tokens - cached_tokens and cache_read_input_tokens is set
  • message_delta_usage_without_usage_chunk — verifies graceful default when no usage chunk is present
  • all_inference_fixtures_replay — updated basic-stream fixtures for openai and vllm providers
  • make test passing (all unit + integration tests)
  • make lint passing (clippy + nightly fmt + machete)

🤖 Generated with Claude Code

@EleanorWho
EleanorWho requested review from a team and jland-redhat August 26, 2026 15:10
@EleanorWho
EleanorWho force-pushed the fix-803-streaming-usage branch from 935db50 to 9663554 Compare August 26, 2026 15:13

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.

"basic-stream recordings for openai and vllm now include stream_options.include_usage on the upstream request and the replay helper mirrors that. However, upstream SSE recordings still end at the finish_reason chunk with usage:null and no trailing usage-only chunk. Expected client message_delta in the openai fixture still shows output_tokens:0 and input_tokens:null, so fixture replay does not demonstrate restored billing counters end-to-end."

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for pointing out the issue. I'll re-record the OpenAI fixture.
For vLLM, is there a shared environment I can use, or should the vLLM fixture stay as-is for now? It still passes and covers the fallback when the backend doesn't return usage.

@praxis-bot praxis-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR Review

Summary: Injects stream_options.include_usage on translated streaming requests and maps the upstream usage chunk into Anthropic message_delta usage fields.

Overall: Well-implemented with thorough test coverage. The streaming usage mapping is correct, including the cache subtraction logic using saturating_sub. One doc comment was inadvertently split.

Severity Count
Critical 0
Large 0
Medium 1

Findings without inline placement

(none)


/// Map Anthropic parameters to Chat Completions-compatible equivalents.
///
/// Copy `stream` and request streaming usage when enabled.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Medium] The convert_stream function was inserted between lines 1 and 3 of map_parameters' existing doc comment, splitting it. As a result, convert_stream now has map_parameters' summary ("Map Anthropic parameters to Chat Completions-compatible equivalents.") as its first doc line, which is inaccurate, and map_parameters lost its summary (now starting with the top_k detail paragraph).

Replace convert_stream's doc with an accurate single-line summary and restore map_parameters' summary:

/// Copy `stream` and inject `stream_options.include_usage` when enabled.
fn convert_stream(...)

/// Map Anthropic parameters to Chat Completions-compatible equivalents.
///
/// `top_k` has no standard Chat Completions equivalent but is preserved
/// as an extra body parameter for backends that support it
/// (e.g. vLLM).
fn map_parameters(...)

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.

@EleanorWho is this addressed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

It's addressed in the latest commit.

@praxis-bot-app

Copy link
Copy Markdown

Unsigned commits: 3fd7ce6, 9663554. Please sign your commits.

@praxis-bot-app

Copy link
Copy Markdown

AI tool authorship detected:

  • 3fd7ce6: Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
  • 9663554: Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Sorry, this project does not accept commits authored by tools as valid.
Commits need to be authored by and signed-off by the human(s) responsible for the PR, with their name and contact.

@EleanorWho
EleanorWho force-pushed the fix-803-streaming-usage branch 2 times, most recently from c8ed0f3 to c5780c4 Compare August 27, 2026 10:50
@shaneutt
shaneutt self-requested a review as a code owner August 28, 2026 17:11
@leseb

leseb commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

@EleanorWho please rebase

…ation

The Anthropic→OpenAI request translator now sets
stream_options.include_usage=true for streaming requests so the
Chat Completions backend returns token usage in its final chunk.

The stream event transformer extracts prompt_tokens and
prompt_tokens_details.cached_tokens from the usage chunk and maps
them into the Anthropic message_delta usage fields, applying the
same cache subtraction logic as the non-streaming path.

Closes praxis-proxy#803

Signed-off-by: Eleanor Hu <ehu@redhat.com>
- Extract convert_stream() and collect_delta_usage() to satisfy
  clippy::too_many_lines.
- Consolidate MessageDeltaUsage constructors into a single new().
- Add stream_options.include_usage to inference fixture recordings
  and the replay test helper.
- Apply nightly rustfmt formatting.

Signed-off-by: Eleanor Hu <ehu@redhat.com>
@EleanorWho
EleanorWho force-pushed the fix-803-streaming-usage branch from c5780c4 to 1b42f3d Compare August 31, 2026 10:48
…ream fixture

Restore the map_parameters doc comment summary that was split by the
convert_stream insertion. Re-record the OpenAI basic-stream fixture
against a live provider with include_usage enabled, so the upstream
response contains real token counts and the client message_delta
demonstrates restored billing counters end-to-end.

Signed-off-by: Eleanor Hu <ehu@redhat.com>
@EleanorWho

EleanorWho commented Aug 31, 2026

Copy link
Copy Markdown
Author

@leseb Hi Seb, can you please take a look at the recording fixture in the latest commit? The previous OpenAI recording had "kind": "imported" with a source_id pointing to a Python test, but my re-recording using cargo xtask record-inference produced "kind": "live" with source_id: null.
I'm not sure if this provenance change is acceptable or if I should re-record through the import path to preserve the original format.

@leseb leseb left a comment

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.

Two actionable fixture-governance issues remain:

The vLLM live fixture’s request was manually updated without re-recording its response. It still lacks the trailing usage chunk, although vLLM emits one for include_usage: true. Re-record it truthfully. vLLM documentation

coverage.yaml needs explicit feature inventory for the new streaming usage request/response transformation, per AGENTS.md, followed by README regeneration.

@leseb

leseb commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

@leseb Hi Seb, can you please take a look at the recording fixture in the latest commit? The previous OpenAI recording had "kind": "imported" with a source_id pointing to a Python test, but my re-recording using cargo xtask record-inference produced "kind": "live" with source_id: null. I'm not sure if this provenance change is acceptable or if I should re-record through the import path to preserve the original format.

The provenance change is correct. Since this fixture was captured using cargo.

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.

Streaming translation does not request usage, causing zero token counts

4 participants