Issue #3586522: Keep token usage in the chat() Fiber branch
Problem / Motivation
Non-streamed chat calls executed inside a PHP Fiber lose all token usage data. Since core 11.x wraps every web request in a Fiber (Renderer::executeInRenderContext()), this affects virtually every non-streamed chat call made in a web context, against any provider extending OpenAiBasedProviderClientBase.
The Fiber branch of chat() consumes the stream and reconstructs the final output, but only keeps getNormalized() — the token usage that reconstructChatOutput() had correctly captured is thrown away, and the returned ChatOutput carries an empty TokenUsageDto.
Closes #3586522
Changes
src/Base/OpenAiBasedProviderClientBase.php: the Fiber branch now keeps the reconstructedChatOutputand passes itsTokenUsageDtoto the finalChatOutput(4th constructor argument). The raw response object kept in the output is unchanged.tests/src/Unit/Base/OpenAiBasedProviderClientBaseTest.php: regression testtestFiberBranchKeepsTokenUsage()— runschat()inside a real\Fiberagainst a stubbed SDK client that streams two content chunks plus a trailing usage chunk (the OpenAIstream_options.include_usageshape), and asserts the returnedChatOutputcarries the usage.
Testing
- Before the fix:
testFiberBranchKeepsTokenUsagefails withFailed asserting that null is identical to 10. - After the fix: all 10 tests in the file pass.
- PHPCS (Drupal, DrupalPractice): 0 errors. PHPStan (module config): 0 errors.
Manual check: a non-streamed chat call from a web request (e.g. AI Chat Explorer, streaming disabled) now returns populated getTokenUsage(), matching what the same call returns via Drush.
AI-Generated: Yes (Claude, fix implementation and regression test scaffolding; bug diagnosed, code reviewed and validated manually).