Repository navigation
fix(azure): keep cached and reasoning token counts in usage - #9211
Pranav Tomar (Mystery01092000) wants to merge 1 commit into
Conversation
Strix Security ReviewWarning This pull request has 27 commits after the last Strix review ( ✅ No open security findings. PR summary: Fixes Azure token usage reporting so cached-prompt and reasoning token counts are preserved in both streaming and non-streaming paths, adding a `_usage_detail` helper that reads nested usage details as attributes or mapping items, plus tests using real `azure-ai-inference` objects. Review summaryThis PR fixes Azure token accounting so cached-prompt and reasoning token counts survive streaming: both streaming handlers now call I read the full diff and traced the new data flow end to end: usage chunk → The lint triage returned no item for any code this PR adds or changes; the single item in the changed file is 1,100 lines away in Ruled out (1)
Review 1 on this PR · Reviewed by Strix |
Christopher Krah (chrikrah)
left a comment
There was a problem hiding this comment.
Pranav Tomar (@Mystery01092000) nothing here blocks a merge from my side at 8a5ff09. With completion.py reverted and your tests kept, all three new tests fail:
# uv sync --frozen --package crewai --extra azure-ai-inference --dev, azure-ai-inference 1.0.0b9, Python 3.12
$ python -m pytest lib/crewai/tests/llms/azure -q -p no:cacheprovider -n 4
# head 8a5ff09
118 passed, 102 warnings in 14.55s
# head with completion.py reverted to 73f4832, tests kept
FAILED test_azure_streaming_usage.py::test_non_streaming_usage_reads_details_from_the_sdk_model
FAILED test_azure_streaming_usage.py::test_async_streaming_usage_keeps_cached_and_reasoning_tokens
FAILED test_azure_streaming_usage.py::test_streaming_usage_keeps_cached_and_reasoning_tokens
3 failed, 115 passed, 102 warnings in 14.26s
# probe at head: real ChatCompletions, usage with no *_details, then with prompt_tokens_details=None
$ python -I probe9211.py
{'prompt_tokens': 5, 'completion_tokens': 7, 'total_tokens': 12, 'cached_prompt_tokens': 0, 'reasoning_tokens': 0}
{'prompt_tokens': 5, 'completion_tokens': 7, 'total_tokens': 12, 'cached_prompt_tokens': 0, 'reasoning_tokens': 0}
# not run: a live Azure deployment
The detail reads came with 0341e5a (#4447) and fc6792d (#5389), and their tests build usage as a MagicMock, which answers any attribute. Your non-streaming test is the first against a real ChatCompletions.
non-blocking: test_azure_no_detail_fields pins the no-details case only on a MagicMock. If you want it on the real model, the two probe objects drop into the new test file unchanged.
|
Hi João Moura (@joaomdmoura), could you review this one? It keeps cached-prompt and reasoning token counts in Azure usage, for both streaming and non-streaming. Christopher Krah (@chrikrah) reproduced it above: with |
The streaming paths built the usage dict from the three totals only, so cached_prompt_tokens and reasoning_tokens were always 0. The non-streaming path tried to read them, but azure-ai-inference's CompletionsUsage does not declare prompt_tokens_details or completion_tokens_details, so they are only reachable as mapping items and the attribute lookup also returned 0 for real SDK responses. Read the details as an attribute or a mapping item, and use the same extraction for streaming usage chunks (sync and async). Fixes crewAIInc#9186 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8a5ff09 to
38514c1
Compare
Related issue
Fixes #9186
Summary
Azure usage lost
cached_prompt_tokensandreasoning_tokensin two ways:_handle_streaming_completionand_ahandle_streaming_completionbuilt the usage dict fromprompt_tokens,completion_tokensandtotal_tokensonly, so cached and reasoning counts were always 0. Both now call_extract_azure_token_usageon the usage chunk, the same extraction the non-streaming path uses._extract_azure_token_usagereadusage.prompt_tokens_detailsandusage.completion_tokens_detailsas attributes.azure-ai-inference'sCompletionsUsage(1.0.0b9) does not declare those fields, so on a real SDK object they exist only as mapping items and the attribute lookup returnsNone. The existing test passed because it builds the response withMagicMock. A new_usage_detailhelper reads the detail as an attribute or a mapping item, so both mocked and real objects work.Verification
lib/crewai/tests/llms/azure/test_azure_streaming_usage.py: a sync and an async streaming call fed realStreamingChatCompletionsUpdatechunks (content, then a usage chunk withprompt_tokens_details.cached_tokens=40andcompletion_tokens_details.reasoning_tokens=60) record both counts inget_token_usage_summary(); and_extract_azure_token_usagereads both from a realChatCompletionsobject. All three fail onmainand pass with this change.uv run pytest lib/crewai/tests/llms/azure --allowed-hosts=127.0.0.1: 91 passeduv run pytest lib/crewai/tests/llms --allowed-hosts=127.0.0.1: 1068 passed, 20 skipped, 2 failed. The failures are intest_missing_api_key.pyand the Anthropic/Bedrock PDF multimodal tests; they fail the same way with the unmodifiedcompletion.pyand do not touch Azure.uv run ruff check/uv run ruff format --checkon the changed files: cleanuv run mypy --config-file pyproject.toml lib/crewai/src/crewai/llms/providers/azure/completion.py: no issuesAdditional context
--allowed-hosts=127.0.0.1locally, because the Windows Proactor event loop opens a loopback socketpair that--block-networkrejects. Not needed on Linux CI.llm-generated; I can't set labels on this repo).🤖 Generated with Claude Code
Note
Low Risk
Changes are limited to Azure usage extraction and metrics; no auth, request routing, or response content logic is affected.
Overview
Azure token usage reporting now preserves
cached_prompt_tokensandreasoning_tokensfor streaming calls and when the Inference SDK exposes detail fields only as dict keys.Sync and async streaming handlers no longer build usage from the three totals alone; they route usage chunks through
_extract_azure_token_usage, matching the non-streaming path. A new_usage_detailhelper reads nestedprompt_tokens_details/completion_tokens_detailsvia attributes orMappinglookup, because realCompletionsUsageobjects omit those fields as attributes. Tests cover sync/async streaming and non-streaming extraction with SDK-shaped usage payloads.Reviewed by Cursor Bugbot for commit 38514c1. Bugbot is set up for automated code reviews on this repo. Configure here.