Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 166ab20

Browse filesBrowse files
authored
fix(OpenAI): Add support for reasoning text streaming events (#673)
* Add support for `response.reasoning_text.delta` and `response.reasoning_text.done` events. * Remove `type` attribute from `ReasoningTextDelta` and `ReasoningTextDone` responses.
1 parent a00d775 commit 166ab20
Copy full SHA for 166ab20

File tree

Expand file treeCollapse file tree

7 files changed

+179
-1
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+179
-1
lines changed
Open diff view settings
Collapse file

‎src/Responses/Responses/CreateStreamedResponse.php‎

Copy file name to clipboardExpand all lines: src/Responses/Responses/CreateStreamedResponse.php
+5-1Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
use OpenAI\Responses\Responses\Streaming\ReasoningSummaryPart;
3030
use OpenAI\Responses\Responses\Streaming\ReasoningSummaryTextDelta;
3131
use OpenAI\Responses\Responses\Streaming\ReasoningSummaryTextDone;
32+
use OpenAI\Responses\Responses\Streaming\ReasoningTextDelta;
33+
use OpenAI\Responses\Responses\Streaming\ReasoningTextDone;
3234
use OpenAI\Responses\Responses\Streaming\RefusalDelta;
3335
use OpenAI\Responses\Responses\Streaming\RefusalDone;
3436
use OpenAI\Responses\Responses\Streaming\WebSearchCall;
@@ -50,7 +52,7 @@ final class CreateStreamedResponse implements ResponseContract
5052

5153
private function __construct(
5254
public readonly string $event,
53-
public readonly CreateResponse|OutputItem|ContentPart|OutputTextDelta|OutputTextAnnotationAdded|OutputTextDone|RefusalDelta|RefusalDone|FunctionCallArgumentsDelta|FunctionCallArgumentsDone|FileSearchCall|WebSearchCall|CodeInterpreterCall|CodeInterpreterCodeDelta|CodeInterpreterCodeDone|ReasoningSummaryPart|ReasoningSummaryTextDelta|ReasoningSummaryTextDone|McpListTools|McpListToolsInProgress|McpCall|McpCallArgumentsDelta|McpCallArgumentsDone|ImageGenerationPart|ImageGenerationPartialImage|Error $response,
55+
public readonly CreateResponse|OutputItem|ContentPart|OutputTextDelta|OutputTextAnnotationAdded|OutputTextDone|RefusalDelta|RefusalDone|FunctionCallArgumentsDelta|FunctionCallArgumentsDone|FileSearchCall|WebSearchCall|CodeInterpreterCall|CodeInterpreterCodeDelta|CodeInterpreterCodeDone|ReasoningSummaryPart|ReasoningSummaryTextDelta|ReasoningSummaryTextDone|ReasoningTextDelta|ReasoningTextDone|McpListTools|McpListToolsInProgress|McpCall|McpCallArgumentsDelta|McpCallArgumentsDone|ImageGenerationPart|ImageGenerationPartialImage|Error $response,
5456
) {}
5557

5658
/**
@@ -95,6 +97,8 @@ public static function from(array $attributes): self
9597
'response.reasoning_summary_part.done' => ReasoningSummaryPart::from($attributes, $meta), // @phpstan-ignore-line
9698
'response.reasoning_summary_text.delta' => ReasoningSummaryTextDelta::from($attributes, $meta), // @phpstan-ignore-line
9799
'response.reasoning_summary_text.done' => ReasoningSummaryTextDone::from($attributes, $meta), // @phpstan-ignore-line
100+
'response.reasoning_text.delta' => ReasoningTextDelta::from($attributes, $meta), // @phpstan-ignore-line
101+
'response.reasoning_text.done' => ReasoningTextDone::from($attributes, $meta), // @phpstan-ignore-line
98102
'response.mcp_list_tools.in_progress' => McpListToolsInProgress::from($attributes, $meta), // @phpstan-ignore-line
99103
'response.mcp_list_tools.failed',
100104
'response.mcp_list_tools.completed' => McpListTools::from($attributes, $meta), // @phpstan-ignore-line
Collapse file
+66Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Streaming;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Contracts\ResponseHasMetaInformationContract;
9+
use OpenAI\Responses\Concerns\ArrayAccessible;
10+
use OpenAI\Responses\Concerns\HasMetaInformation;
11+
use OpenAI\Responses\Meta\MetaInformation;
12+
use OpenAI\Testing\Responses\Concerns\Fakeable;
13+
14+
/**
15+
* @phpstan-type ReasoningTextDeltaType array{content_index: int, delta: string, item_id: string, output_index: int, sequence_number: int}
16+
*
17+
* @implements ResponseContract<ReasoningTextDeltaType>
18+
*/
19+
final class ReasoningTextDelta implements ResponseContract, ResponseHasMetaInformationContract
20+
{
21+
/**
22+
* @use ArrayAccessible<ReasoningTextDeltaType>
23+
*/
24+
use ArrayAccessible;
25+
26+
use Fakeable;
27+
use HasMetaInformation;
28+
29+
private function __construct(
30+
public readonly int $contentIndex,
31+
public readonly string $delta,
32+
public readonly string $itemId,
33+
public readonly int $outputIndex,
34+
public readonly int $sequenceNumber,
35+
private readonly MetaInformation $meta,
36+
) {}
37+
38+
/**
39+
* @param ReasoningTextDeltaType $attributes
40+
*/
41+
public static function from(array $attributes, MetaInformation $meta): self
42+
{
43+
return new self(
44+
contentIndex: $attributes['content_index'],
45+
delta: $attributes['delta'],
46+
itemId: $attributes['item_id'],
47+
outputIndex: $attributes['output_index'],
48+
sequenceNumber: $attributes['sequence_number'],
49+
meta: $meta,
50+
);
51+
}
52+
53+
/**
54+
* {@inheritDoc}
55+
*/
56+
public function toArray(): array
57+
{
58+
return [
59+
'content_index' => $this->contentIndex,
60+
'delta' => $this->delta,
61+
'item_id' => $this->itemId,
62+
'output_index' => $this->outputIndex,
63+
'sequence_number' => $this->sequenceNumber,
64+
];
65+
}
66+
}
Collapse file
+66Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Streaming;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Contracts\ResponseHasMetaInformationContract;
9+
use OpenAI\Responses\Concerns\ArrayAccessible;
10+
use OpenAI\Responses\Concerns\HasMetaInformation;
11+
use OpenAI\Responses\Meta\MetaInformation;
12+
use OpenAI\Testing\Responses\Concerns\Fakeable;
13+
14+
/**
15+
* @phpstan-type ReasoningTextDoneType array{content_index: int, item_id: string, output_index: int, sequence_number: int, text: string}
16+
*
17+
* @implements ResponseContract<ReasoningTextDoneType>
18+
*/
19+
final class ReasoningTextDone implements ResponseContract, ResponseHasMetaInformationContract
20+
{
21+
/**
22+
* @use ArrayAccessible<ReasoningTextDoneType>
23+
*/
24+
use ArrayAccessible;
25+
26+
use Fakeable;
27+
use HasMetaInformation;
28+
29+
private function __construct(
30+
public readonly int $contentIndex,
31+
public readonly string $itemId,
32+
public readonly int $outputIndex,
33+
public readonly int $sequenceNumber,
34+
public readonly string $text,
35+
private readonly MetaInformation $meta,
36+
) {}
37+
38+
/**
39+
* @param ReasoningTextDoneType $attributes
40+
*/
41+
public static function from(array $attributes, MetaInformation $meta): self
42+
{
43+
return new self(
44+
contentIndex: $attributes['content_index'],
45+
itemId: $attributes['item_id'],
46+
outputIndex: $attributes['output_index'],
47+
sequenceNumber: $attributes['sequence_number'],
48+
text: $attributes['text'],
49+
meta: $meta,
50+
);
51+
}
52+
53+
/**
54+
* {@inheritDoc}
55+
*/
56+
public function toArray(): array
57+
{
58+
return [
59+
'content_index' => $this->contentIndex,
60+
'item_id' => $this->itemId,
61+
'output_index' => $this->outputIndex,
62+
'sequence_number' => $this->sequenceNumber,
63+
'text' => $this->text,
64+
];
65+
}
66+
}
Collapse file

‎tests/Fixtures/Responses.php‎

Copy file name to clipboardExpand all lines: tests/Fixtures/Responses.php
+10Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,3 +760,13 @@ function responseCompletionSteamCreatedEvent()
760760
{
761761
return fopen(__DIR__.'/Streams/ResponseCreatedResponse.txt', 'r');
762762
}
763+
764+
function responseReasoningTextDeltaEvent()
765+
{
766+
return fopen(__DIR__.'/Streams/ResponseReasoningTextDelta.txt', 'r');
767+
}
768+
769+
function responseReasoningTextDoneEvent()
770+
{
771+
return fopen(__DIR__.'/Streams/ResponseReasoningTextDone.txt', 'r');
772+
}
Collapse file
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data: {"type":"response.reasoning_text.delta","content_index":0,"delta":"Let me analyze","item_id":"item_123","output_index":0,"sequence_number":5}
Collapse file
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
data: {"type":"response.reasoning_text.done","content_index":0,"item_id":"item_123","output_index":0,"sequence_number":10,"text":"Let me analyze this problem step by step to provide the best solution."}
Collapse file

‎tests/Responses/Responses/CreateStreamedResponse.php‎

Copy file name to clipboardExpand all lines: tests/Responses/Responses/CreateStreamedResponse.php
+30Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
use OpenAI\Responses\Responses\CreateResponse;
44
use OpenAI\Responses\Responses\CreateStreamedResponse;
5+
use OpenAI\Responses\Responses\Streaming\ReasoningTextDelta;
6+
use OpenAI\Responses\Responses\Streaming\ReasoningTextDone;
57

68
test('fake', function () {
79
$response = CreateStreamedResponse::fake();
@@ -20,3 +22,31 @@
2022
->response->toBeInstanceOf(CreateResponse::class)
2123
->response->id->toBe('resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c');
2224
});
25+
26+
test('reasoning text delta event', function () {
27+
$response = CreateStreamedResponse::fake(responseReasoningTextDeltaEvent());
28+
29+
expect($response->getIterator()->current())
30+
->toBeInstanceOf(CreateStreamedResponse::class)
31+
->event->toBe('response.reasoning_text.delta')
32+
->response->toBeInstanceOf(ReasoningTextDelta::class)
33+
->response->delta->toBe('Let me analyze')
34+
->response->itemId->toBe('item_123')
35+
->response->outputIndex->toBe(0)
36+
->response->contentIndex->toBe(0)
37+
->response->sequenceNumber->toBe(5);
38+
});
39+
40+
test('reasoning text done event', function () {
41+
$response = CreateStreamedResponse::fake(responseReasoningTextDoneEvent());
42+
43+
expect($response->getIterator()->current())
44+
->toBeInstanceOf(CreateStreamedResponse::class)
45+
->event->toBe('response.reasoning_text.done')
46+
->response->toBeInstanceOf(ReasoningTextDone::class)
47+
->response->text->toBe('Let me analyze this problem step by step to provide the best solution.')
48+
->response->itemId->toBe('item_123')
49+
->response->outputIndex->toBe(0)
50+
->response->contentIndex->toBe(0)
51+
->response->sequenceNumber->toBe(10);
52+
});

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.