1212class LLMResponse
1313{
1414 private mixed $ value = null ;
15+ private array $ partialResponses = [];
16+ private bool $ isPartial = false ;
1517
1618 public function __construct (
1719 private string $ content = '' ,
@@ -32,8 +34,16 @@ public function __construct(
3234 * @param PartialLLMResponse[] $partialResponses
3335 * @return LLMResponse
3436 */
35- public static function fromPartialResponses (array $ partialResponses ) : LLMResponse {
36- return (new self )->makeFromPartialResponses ($ partialResponses );
37+ public static function fromPartialResponses (array $ partialResponses = []) : self {
38+ $ newResponse = new self ();
39+ foreach ($ partialResponses as $ partialResponse ) {
40+ if ($ partialResponse === null ) {
41+ continue ;
42+ }
43+ $ newResponse ->applyPartialResponse ($ partialResponse );
44+ }
45+ $ newResponse ->toolCalls = ToolCalls::fromArray (self ::makeTools ($ partialResponses ));
46+ return $ newResponse ;
3747 }
3848
3949 // PUBLIC ////////////////////////////////////////////////
@@ -150,6 +160,10 @@ public function finishReason() : LLMFinishReason {
150160 return LLMFinishReason::fromText ($ this ->finishReason );
151161 }
152162
163+ public function hasFinishReason () : bool {
164+ return $ this ->finishReason !== '' ;
165+ }
166+
153167 public function responseData () : array {
154168 return $ this ->responseData ;
155169 }
@@ -161,61 +175,98 @@ public function toArray() : array {
161175 'finishReason ' => $ this ->finishReason ,
162176 'toolCalls ' => $ this ->toolCalls ?->toArray() ?? [],
163177 'usage ' => $ this ->usage ->toArray (),
178+ // raw response data
164179 'responseData ' => $ this ->responseData ,
165180 ];
166181 }
167182
183+ public function clone () : self {
184+ return new self (
185+ content: $ this ->content ,
186+ finishReason: $ this ->finishReason ,
187+ toolCalls: $ this ->toolCalls ?->clone(),
188+ reasoningContent: $ this ->reasoningContent ,
189+ usage: $ this ->usage ?->clone(),
190+ responseData: $ this ->responseData ,
191+ );
192+ }
193+
168194 // INTERNAL //////////////////////////////////////////////
169195
170196 /**
171- * @param PartialLLMResponse[] $partialResponses
172- * @return LLMResponse
197+ * Apply a partial response to the current response.
198+ * This will accumulate content, reasoning content, usage,
199+ * and response data.
200+ *
201+ * @param PartialLLMResponse $partialResponse
173202 */
174- private function makeFromPartialResponses (array $ partialResponses = []) : self {
175- if (empty ($ partialResponses )) {
176- return $ this ;
177- }
178-
179- $ content = '' ;
180- $ reasoningContent = '' ;
181- foreach ($ partialResponses as $ partialResponse ) {
182- if ($ partialResponse === null ) {
183- continue ;
184- }
185- $ content .= $ partialResponse ->contentDelta ;
186- $ reasoningContent .= $ partialResponse ->reasoningContentDelta ;
203+ private function applyPartialResponse (PartialLLMResponse $ partialResponse ) : void {
204+ $ this ->content .= $ partialResponse ->contentDelta ?? '' ;
205+ $ this ->reasoningContent .= $ partialResponse ->reasoningContentDelta ?? '' ;
206+ $ this ->finishReason = $ partialResponse ->finishReason ?? $ this ->finishReason ;
207+ $ this ->usage ()->accumulate ($ partialResponse ->usage );
208+ if (!empty ($ partialResponse ->responseData )) {
187209 $ this ->responseData [] = $ partialResponse ->responseData ;
188- $ this ->usage ()->accumulate ($ partialResponse ->usage );
189- $ this ->finishReason = $ partialResponse ->finishReason ;
190- }
191- $ this ->content = $ content ;
192- $ this ->reasoningContent = $ reasoningContent ;
193-
194- $ tools = $ this ->makeTools ($ partialResponses );
195- if (!empty ($ tools )) {
196- $ this ->toolCalls = ToolCalls::fromArray ($ tools );
197210 }
198- return $ this ;
199211 }
200212
201- private function makeTools (array $ partialResponses ): array {
213+ /**
214+ * Make a list of tool calls from the partial responses.
215+ *
216+ * @param PartialLLMResponse[] $partialResponses
217+ * @return array
218+ */
219+ private static function makeTools (array $ partialResponses ): array {
202220 $ tools = [];
203221 $ currentTool = '' ;
204222 foreach ($ partialResponses as $ partialResponse ) {
205223 if ($ partialResponse === null ) {
206224 continue ;
207225 }
208- if (('' !== ($ partialResponse ->toolName ?? '' ))
209- && ($ currentTool !== ($ partialResponse ->toolName ?? '' ))) {
210- $ currentTool = $ partialResponse ->toolName ?? '' ;
226+ // if the tool name changes, start a new tool call
227+ if ($ partialResponse ->hasToolName ()
228+ && ($ currentTool !== ($ partialResponse ->toolName ()))) {
229+ $ currentTool = $ partialResponse ->toolName ();
211230 $ tools [$ currentTool ] = '' ;
212231 }
232+ // append the tool arguments to it
213233 if ('' !== $ currentTool ) {
214- if (( '' !== ( $ partialResponse ->toolArgs ?? '' ) )) {
215- $ tools [$ currentTool ] .= $ partialResponse ->toolArgs ?? '' ;
234+ if ($ partialResponse ->hasToolArgs ( )) {
235+ $ tools [$ currentTool ] .= $ partialResponse ->toolArgs () ;
216236 }
217237 }
218238 }
219239 return $ tools ;
220240 }
221241}
242+
243+ // /**
244+ // * @param PartialLLMResponse[] $partialResponses
245+ // * @return LLMResponse
246+ // */
247+ // private function makeFromPartialResponses(array $partialResponses = []) : self {
248+ // if (empty($partialResponses)) {
249+ // return $this;
250+ // }
251+ //
252+ // $content = '';
253+ // $reasoningContent = '';
254+ // foreach($partialResponses as $partialResponse) {
255+ // if ($partialResponse === null) {
256+ // continue;
257+ // }
258+ // $content .= $partialResponse->contentDelta;
259+ // $reasoningContent .= $partialResponse->reasoningContentDelta;
260+ // $this->responseData[] = $partialResponse->responseData;
261+ // $this->usage()->accumulate($partialResponse->usage);
262+ // $this->finishReason = $partialResponse->finishReason;
263+ // }
264+ // $this->content = $content;
265+ // $this->reasoningContent = $reasoningContent;
266+ //
267+ // $tools = self::makeTools($partialResponses);
268+ // if (!empty($tools)) {
269+ // $this->toolCalls = ToolCalls::fromArray($tools);
270+ // }
271+ // return $this;
272+ // }
0 commit comments