|
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 | +use Symfony\AI\Platform\Metadata\Metadata; |
21 | 21 | use Symfony\AI\Platform\Model; |
22 | 22 | use Symfony\AI\Platform\Result\ChoiceResult; |
23 | 23 | use Symfony\AI\Platform\Result\RawHttpResult; |
24 | 24 | use Symfony\AI\Platform\Result\RawResultInterface; |
25 | 25 | use Symfony\AI\Platform\Result\ResultInterface; |
26 | | -use Symfony\AI\Platform\Result\StreamChunk; |
| 26 | +use Symfony\AI\Platform\Result\TextChunk; |
27 | 27 | use Symfony\AI\Platform\Result\StreamResult; |
28 | 28 | use Symfony\AI\Platform\Result\TextResult; |
29 | 29 | use Symfony\AI\Platform\Result\ToolCall; |
@@ -90,50 +90,43 @@ public function convert(RawResultInterface|RawHttpResult $result, array $options |
90 | 90 | private function convertStream(RawResultInterface|RawHttpResult $result): \Generator |
91 | 91 | { |
92 | 92 | $toolCalls = []; |
93 | | - /** @var ToolCallResult|null $toolCallResult */ |
94 | | - $toolCallResult = null; |
| 93 | + $metadata = []; |
95 | 94 | foreach ($result->getDataStream() as $data) { |
| 95 | + if (!$metadata) { |
| 96 | + $metadata['id'] = $data['id']; |
| 97 | + } |
| 98 | + |
| 99 | + if (isset($data['usage'])) { |
| 100 | + $metadata['usage'] = $data['usage']; |
| 101 | + } |
| 102 | + |
| 103 | + if (isset($data['choices'][0]['finish_reason'])) { |
| 104 | + $metadata['finish_reason'] = $data['choices'][0]['finish_reason']; |
| 105 | + } |
| 106 | + |
96 | 107 | if ($this->streamIsToolCall($data)) { |
97 | 108 | $toolCalls = $this->convertStreamToToolCalls($toolCalls, $data); |
98 | 109 | } |
99 | 110 |
|
100 | 111 | if ([] !== $toolCalls && $this->isToolCallsStreamFinished($data)) { |
101 | 112 | $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 | | - } |
| 113 | + $metadata['tool_calls'] = $toolCalls; |
| 114 | + $toolCallResult->getMetadata()->set($metadata); |
| 115 | + yield $toolCallResult; |
121 | 116 | } |
122 | 117 |
|
123 | 118 | if (!isset($data['choices'][0]['delta']['content'])) { |
124 | 119 | continue; |
125 | 120 | } |
126 | 121 |
|
127 | | - $chunk = new StreamChunk($data['choices'][0]['delta']['content']); |
128 | | - $chunk->setRawResult($result); |
| 122 | + $textChunk = new TextChunk($data['choices'][0]['delta']['content']); |
| 123 | + $textChunk->getMetadata()->set($metadata); |
| 124 | + $textChunk->setRawResult($result); |
129 | 125 |
|
130 | | - yield $chunk; |
| 126 | + yield $textChunk; |
131 | 127 | } |
132 | 128 |
|
133 | | - // Yield the last tool call result if any. |
134 | | - if ($toolCallResult) { |
135 | | - yield $toolCallResult; |
136 | | - } |
| 129 | + yield new Metadata($metadata); |
137 | 130 | } |
138 | 131 |
|
139 | 132 | /** |
|
0 commit comments