From 856dfd20ceaeba8393bc36dc52559a6ce28694a0 Mon Sep 17 00:00:00 2001 From: Leopold Jacquot Date: Mon, 9 Mar 2026 09:58:37 +0100 Subject: [PATCH] fix: streaming reasoning content --- .../Chat/CreateStreamedResponseDelta.php | 7 +++++-- tests/Fixtures/Chat.php | 19 +++++++++++++++++++ .../Chat/CreateStreamedResponseDelta.php | 16 ++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Responses/Chat/CreateStreamedResponseDelta.php b/src/Responses/Chat/CreateStreamedResponseDelta.php index 6439122e..817ee40a 100644 --- a/src/Responses/Chat/CreateStreamedResponseDelta.php +++ b/src/Responses/Chat/CreateStreamedResponseDelta.php @@ -12,12 +12,13 @@ final class CreateStreamedResponseDelta private function __construct( public readonly ?string $role, public readonly ?string $content, + public readonly ?string $reasoningContent, public readonly array $toolCalls, public readonly ?CreateStreamedResponseFunctionCall $functionCall, ) {} /** - * @param array{role?: string, content?: string, function_call?: array{name?: ?string, arguments?: ?string}, tool_calls?: array} $attributes + * @param array{role?: string, content?: string, reasoning?: string, function_call?: array{name?: ?string, arguments?: ?string}, tool_calls?: array} $attributes */ public static function from(array $attributes): self { @@ -28,19 +29,21 @@ public static function from(array $attributes): self return new self( $attributes['role'] ?? null, $attributes['content'] ?? null, + $attributes['reasoning'] ?? null, $toolCalls, isset($attributes['function_call']) ? CreateStreamedResponseFunctionCall::from($attributes['function_call']) : null, ); } /** - * @return array{role?: string, content?: string}|array{role?: string, content: null, function_call: array{name?: string, arguments?: string}} + * @return array{role?: string, content?: string, reasoning?: string}|array{role?: string, content: null, function_call: array{name?: string, arguments?: string}} */ public function toArray(): array { $data = array_filter([ 'role' => $this->role, 'content' => $this->content, + 'reasoning' => $this->reasoningContent, ], fn (?string $value): bool => ! is_null($value)); if ($this->functionCall instanceof CreateStreamedResponseFunctionCall) { diff --git a/tests/Fixtures/Chat.php b/tests/Fixtures/Chat.php index 8fd93440..f1cc56b9 100644 --- a/tests/Fixtures/Chat.php +++ b/tests/Fixtures/Chat.php @@ -722,6 +722,25 @@ function chatCompletionStreamVisionContentChunk(): array ]; } +function chatCompletionStreamReasoningChunk(): array +{ + return [ + 'id' => 'chatcmpl-123', + 'object' => 'chat.completion.chunk', + 'created' => 1677652288, + 'model' => 'deepseek-reasoner', + 'choices' => [ + [ + 'index' => 0, + 'delta' => [ + 'reasoning' => 'Hello world', + ], + 'finish_reason' => null, + ], + ], + ]; +} + /** * @return array */ diff --git a/tests/Responses/Chat/CreateStreamedResponseDelta.php b/tests/Responses/Chat/CreateStreamedResponseDelta.php index 7b8bb021..595e6c92 100644 --- a/tests/Responses/Chat/CreateStreamedResponseDelta.php +++ b/tests/Responses/Chat/CreateStreamedResponseDelta.php @@ -88,3 +88,19 @@ expect($result->toArray()) ->toBe($data); }); + +test('from reasoning chunk', function () { + $result = CreateStreamedResponseDelta::from(chatCompletionStreamReasoningChunk()['choices'][0]['delta']); + + expect($result) + ->role->toBeNull() + ->content->toBeNull() + ->reasoningContent->toBe('Hello world'); +}); + +test('to array for a reasoning chunk', function () { + $result = CreateStreamedResponseDelta::from(chatCompletionStreamReasoningChunk()['choices'][0]['delta']); + + expect($result->toArray()) + ->toBe(chatCompletionStreamReasoningChunk()['choices'][0]['delta']); +});