Skip to content

Commit 38e6493

Browse files
feat(OpenAI): add Response retrieve streamed (#713)
* Retrieve streamed response * Ensure the stream parameter is a string for retrieval * Allow setting stream parameter to different value * Added interface methods * Added docs * Added test * Fix docblocks * Stream parameter is string * Fixed mock
1 parent 0a0925a commit 38e6493

File tree

6 files changed

+121
-4
lines changed

6 files changed

+121
-4
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,20 @@ $response->truncation; // 'disabled'
303303
$response->toArray(); // ['id' => 'resp_67ccd2bed1ec8190b14f964abc054267', ...]
304304
```
305305

306+
#### `retrieve streamed`
307+
308+
When you retrieve a Response with stream set to true, the server will emit server-sent events to the client as the Response is generated. All events and their payloads can be found in [OpenAI docs](https://platform.openai.com/docs/api-reference/responses-streaming).
309+
310+
```php
311+
$stream = $client->responses()->retrieveStreamed('resp_67ccd2bed1ec8190b14f964abc054267', [
312+
'starting_after' => '2',
313+
]);
314+
315+
foreach ($stream as $response) {
316+
$response->event; // 'response.created'
317+
}
318+
```
319+
306320
#### `cancel`
307321

308322
Cancel a model response (background request) with the given ID.

src/Contracts/Resources/ResponsesContract.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ public function createStreamed(array $parameters): StreamResponse;
4242
*/
4343
public function retrieve(string $id): RetrieveResponse;
4444

45+
/**
46+
* Retrieves a streamed response.
47+
*
48+
* @see https://platform.openai.com/docs/api-reference/responses/retrieve
49+
*
50+
* @param array<string, mixed> $parameters
51+
* @return StreamResponse<CreateStreamedResponse>
52+
*/
53+
public function retrieveStreamed(string $id, array $parameters = []): StreamResponse;
54+
4555
/**
4656
* Cancels a model response with the given ID. Must be marked as 'background' to be cancellable.
4757
*

src/Resources/Concerns/Streamable.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ trait Streamable
99
/**
1010
* @param array<string, mixed> $parameters
1111
*/
12-
private function ensureNotStreamed(array $parameters, string $fallbackFunction = 'createStreamed'): void
12+
private function ensureNotStreamed(array $parameters, string $fallbackFunction = 'createStreamed', mixed $streamValue = true): void
1313
{
1414
if (! isset($parameters['stream'])) {
1515
return;
1616
}
1717

18-
if ($parameters['stream'] !== true) {
18+
if ($parameters['stream'] !== $streamValue) {
1919
return;
2020
}
2121

@@ -28,9 +28,9 @@ private function ensureNotStreamed(array $parameters, string $fallbackFunction =
2828
* @param array<string, mixed> $parameters
2929
* @return array<string, mixed>
3030
*/
31-
private function setStreamParameter(array $parameters): array
31+
private function setStreamParameter(array $parameters, mixed $streamValue = true): array
3232
{
33-
$parameters['stream'] = true;
33+
$parameters['stream'] = $streamValue;
3434

3535
return $parameters;
3636
}

src/Resources/Responses.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,26 @@ public function retrieve(string $id): RetrieveResponse
8080
return RetrieveResponse::from($response->data(), $response->meta());
8181
}
8282

83+
/**
84+
* When you retrieve a Response with stream set to true,
85+
* the server will emit server-sent events to the client as the Response is resumed.
86+
*
87+
* @see https://platform.openai.com/docs/api-reference/responses-streaming
88+
*
89+
* @param array<string, mixed> $parameters
90+
* @return StreamResponse<CreateStreamedResponse>
91+
*/
92+
public function retrieveStreamed(string $id, array $parameters = []): StreamResponse
93+
{
94+
$parameters = $this->setStreamParameter($parameters, 'true'); // Ensure the parameter is a string for retrieval
95+
96+
$payload = Payload::retrieve('responses', $id, parameters: $parameters);
97+
98+
$response = $this->transporter->requestStream($payload);
99+
100+
return new StreamResponse(CreateStreamedResponse::class, $response);
101+
}
102+
83103
/**
84104
* Cancels a model response with the given ID. Must be marked as 'background' to be cancellable.
85105
*

src/Testing/Resources/ResponsesTestResource.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ public function retrieve(string $id): RetrieveResponse
4040
return $this->record(__FUNCTION__, func_get_args());
4141
}
4242

43+
public function retrieveStreamed(string $id, array $parameters = []): StreamResponse
44+
{
45+
return $this->record(__FUNCTION__, func_get_args());
46+
}
47+
4348
public function list(string $id, array $parameters = []): ListInputItems
4449
{
4550
return $this->record(__FUNCTION__, func_get_args());

tests/Resources/Responses.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,74 @@
316316
->toBeInstanceOf(MetaInformation::class);
317317
});
318318

319+
test('retrieve streamed', function () {
320+
$response = new Response(
321+
headers: metaHeaders(),
322+
body: new Stream(responseCompletionStream()),
323+
);
324+
325+
$client = mockStreamClient('GET', 'responses/resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c', [
326+
'starting_after' => '2',
327+
'stream' => 'true',
328+
], $response);
329+
330+
$result = $client->responses()->retrieveStreamed('resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c', [
331+
'starting_after' => '2',
332+
]);
333+
334+
expect($result)
335+
->toBeInstanceOf(StreamResponse::class)
336+
->toBeInstanceOf(IteratorAggregate::class);
337+
338+
expect($result->getIterator())
339+
->toBeInstanceOf(Iterator::class);
340+
341+
$current = $result->getIterator()->current();
342+
expect($current)
343+
->toBeInstanceOf(CreateStreamedResponse::class);
344+
expect($current->event)
345+
->toBe('response.created');
346+
expect($current->response)
347+
->toBeInstanceOf(StreamedResponse::class);
348+
expect($current->response->response)
349+
->toBeInstanceOf(CreateResponse::class);
350+
expect($current->response->response->id)
351+
->toBe('resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c');
352+
expect($current->response->response->object)
353+
->toBe('response');
354+
expect($current->response->response->createdAt)
355+
->toBe(1741484430);
356+
expect($current->response->response->status)
357+
->toBe('in_progress');
358+
expect($current->response->response->error)
359+
->toBeNull();
360+
expect($current->response->response->incompleteDetails)
361+
->toBeNull();
362+
expect($current->response->response->instructions)
363+
->toBeNull();
364+
expect($current->response->response->maxOutputTokens)
365+
->toBeNull();
366+
expect($current->response->response->model)
367+
->toBe('gpt-4o-2024-08-06');
368+
expect($current->response->response->output)
369+
->toBeArray();
370+
expect($current->response->response->output)
371+
->toHaveCount(0);
372+
expect($current->response->response->parallelToolCalls)
373+
->toBeTrue();
374+
expect($current->response->response->previousResponseId)
375+
->toBeNull();
376+
expect($current->response->response->temperature)
377+
->toBe(1.0);
378+
expect($current->response->response->toolChoice)
379+
->toBe('auto');
380+
expect($current->response->response->topP)
381+
->toBe(1.0);
382+
383+
expect($result->meta())
384+
->toBeInstanceOf(MetaInformation::class);
385+
});
386+
319387
test('cancel', function () {
320388
$client = mockClient('POST', 'responses/resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c/cancel', [
321389
], \OpenAI\ValueObjects\Transporter\Response::from(retrieveResponseResource(), metaHeaders()));

0 commit comments

Comments
 (0)