|
17 | 17 | use Symfony\AI\Platform\Exception\ContentFilterException; |
18 | 18 | use Symfony\AI\Platform\Exception\RateLimitExceededException; |
19 | 19 | use Symfony\AI\Platform\Exception\RuntimeException; |
| 20 | +use Symfony\AI\Platform\Metadata\TokenUsage; |
20 | 21 | use Symfony\AI\Platform\Model; |
21 | 22 | use Symfony\AI\Platform\Result\ChoiceResult; |
22 | 23 | use Symfony\AI\Platform\Result\RawHttpResult; |
23 | 24 | use Symfony\AI\Platform\Result\RawResultInterface; |
24 | 25 | use Symfony\AI\Platform\Result\ResultInterface; |
| 26 | +use Symfony\AI\Platform\Result\StreamChunk; |
25 | 27 | use Symfony\AI\Platform\Result\StreamResult; |
26 | 28 | use Symfony\AI\Platform\Result\TextResult; |
27 | 29 | use Symfony\AI\Platform\Result\ToolCall; |
@@ -88,20 +90,49 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options |
88 | 90 | private function convertStream(RawResultInterface|RawHttpResult $result): \Generator |
89 | 91 | { |
90 | 92 | $toolCalls = []; |
| 93 | + /** @var ToolCallResult|null $toolCallResult */ |
| 94 | + $toolCallResult = null; |
91 | 95 | foreach ($result->getDataStream() as $data) { |
92 | 96 | if ($this->streamIsToolCall($data)) { |
93 | 97 | $toolCalls = $this->convertStreamToToolCalls($toolCalls, $data); |
94 | 98 | } |
95 | 99 |
|
96 | 100 | if ([] !== $toolCalls && $this->isToolCallsStreamFinished($data)) { |
97 | | - yield new ToolCallResult(...array_map($this->convertToolCall(...), $toolCalls)); |
| 101 | + $toolCallResult = new ToolCallResult(...array_map($this->convertToolCall(...), $toolCalls)); |
| 102 | + // postpone yielding the tool call result until the usage is available |
| 103 | + continue; |
| 104 | + } |
| 105 | + |
| 106 | + // Usage arrives after the tool calls are finished. |
| 107 | + if ($usage = $data['usage'] ?? null) { |
| 108 | + if ($toolCallResult) { |
| 109 | + $toolCallResult->getMetadata()->add('usage', $usage); |
| 110 | + yield $toolCallResult; |
| 111 | + $toolCallResult = null; |
| 112 | + } else { |
| 113 | + yield new TokenUsage( |
| 114 | + promptTokens: $usage['prompt_tokens'] ?? null, |
| 115 | + completionTokens: $usage['completion_tokens'] ?? null, |
| 116 | + thinkingTokens: $usage['completion_tokens_details']['reasoning_tokens'] ?? null, |
| 117 | + cachedTokens: $usage['prompt_tokens_details']['cached_tokens'] ?? null, |
| 118 | + totalTokens: $usage['total_tokens'] ?? null, |
| 119 | + ); |
| 120 | + } |
98 | 121 | } |
99 | 122 |
|
100 | 123 | if (!isset($data['choices'][0]['delta']['content'])) { |
101 | 124 | continue; |
102 | 125 | } |
103 | 126 |
|
104 | | - yield $data['choices'][0]['delta']['content']; |
| 127 | + $chunk = new StreamChunk($data['choices'][0]['delta']['content']); |
| 128 | + $chunk->setRawResult($result); |
| 129 | + |
| 130 | + yield $chunk; |
| 131 | + } |
| 132 | + |
| 133 | + // Yield the last tool call result if any. |
| 134 | + if ($toolCallResult) { |
| 135 | + yield $toolCallResult; |
105 | 136 | } |
106 | 137 | } |
107 | 138 |
|
|
0 commit comments