Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions docs/providers/openai.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,19 @@ $response = Prism::structured()

### Previous Responses

Prism supports OpenAI's [conversation state](https://platform.openai.com/docs/guides/conversation-state#openai-apis-for-conversation-state) with the `previous_response_id` parameter.
Prism supports OpenAI's [conversation state](https://platform.openai.com/docs/guides/conversation-state#openai-apis-for-conversation-state) with either the `conversation` or the `previous_response_id` parameter.

```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'previous_response_id' => 'response_id' // [!code focus]
'conversation' => 'conv_abc123' // [!code focus]
]) // [!code focus]
```

```php
$response = Prism::structured()
->withProviderOptions([ // [!code focus]
'previous_response_id' => 'resp_abc123' // [!code focus]
]) // [!code focus]
```

Expand Down
2 changes: 1 addition & 1 deletion src/Providers/Gemini/Maps/SchemaMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function toArray(): array
? (new self($this->schema->items))->toArray()
: null,
'properties' => $this->schema instanceof ObjectSchema && property_exists($this->schema, 'properties')
? array_reduce($this->schema->properties, function (array $carry, Schema $property) {
? array_reduce($this->schema->properties, function (array $carry, Schema $property): array {
// Use property name as the key, but do NOT include "name" inside the value
$carry[$property->name()] = (new self($property))->toArray();

Expand Down
2 changes: 1 addition & 1 deletion src/Providers/Gemini/Maps/ToolMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static function map(array $tools): array
*/
public static function mapProperties(array $properties): array
{
return Arr::mapWithKeys($properties, fn (Schema $schema, string $name) => [
return Arr::mapWithKeys($properties, fn (Schema $schema, string $name): array => [
$name => (new SchemaMap($schema))->toArray(),
]);
}
Expand Down
1 change: 1 addition & 0 deletions src/Providers/OpenAI/Handlers/Stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ protected function sendRequest(Request $request): Response
'metadata' => $request->providerOptions('metadata'),
'tools' => $this->buildTools($request),
'tool_choice' => ToolChoiceMap::map($request->toolChoice()),
'conversation' => $request->providerOptions('conversation'),
'previous_response_id' => $request->providerOptions('previous_response_id'),
'truncation' => $request->providerOptions('truncation'),
'reasoning' => $request->providerOptions('reasoning'),
Expand Down
1 change: 1 addition & 0 deletions src/Providers/OpenAI/Handlers/Structured.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ protected function sendRequest(Request $request, array $responseFormat): ClientR
'temperature' => $request->temperature(),
'top_p' => $request->topP(),
'metadata' => $request->providerOptions('metadata'),
'conversation' => $request->providerOptions('conversation'),
'previous_response_id' => $request->providerOptions('previous_response_id'),
'truncation' => $request->providerOptions('truncation'),
'reasoning' => $request->providerOptions('reasoning'),
Expand Down
1 change: 1 addition & 0 deletions src/Providers/OpenAI/Handlers/Text.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ protected function sendRequest(Request $request): ClientResponse
'metadata' => $request->providerOptions('metadata'),
'tools' => $this->buildTools($request),
'tool_choice' => ToolChoiceMap::map($request->toolChoice()),
'conversation' => $request->providerOptions('conversation'),
'previous_response_id' => $request->providerOptions('previous_response_id'),
'truncation' => $request->providerOptions('truncation'),
'reasoning' => $request->providerOptions('reasoning'),
Expand Down
177 changes: 177 additions & 0 deletions tests/Fixtures/openai/stream-conversation-1.json

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions tests/Fixtures/openai/structured-conversation-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"id": "resp_68ba1008a17481959a76d0d17c72e06d051fbc5e5c8d680a",
"object": "response",
"created_at": 1757024264,
"status": "completed",
"background": false,
"conversation": {
"id": "conv_68ba1008458c8195a272b1a5e303283d051fbc5e5c8d680a"
},
"error": null,
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
"model": "gpt-4.1-2025-04-14",
"output": [
{
"id": "msg_68ba10097b2481958998143f9956a003051fbc5e5c8d680a",
"type": "message",
"status": "completed",
"content": [
{
"type": "output_text",
"annotations": [],
"logprobs": [],
"text": "{\"steps\":[\"Once upon a time, three clever pigs built cozy houses in a peaceful forest, each choosing a different style: straw, sticks, and sturdy bricks.\",\"One blustery night, a curious wolf politely knocked on their doors, just wanting to join in a friendly game of cards.\",\"By the next morning, everyone was laughing together, discovering that friendship was much stronger than fear.\"]}"
}
],
"role": "assistant"
}
],
"parallel_tool_calls": true,
"previous_response_id": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"store": true,
"temperature": 1.0,
"text": {
"format": {
"type": "json_schema",
"description": null,
"name": "story",
"schema": {
"type": "object",
"properties": {
"steps": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"steps"
],
"additionalProperties": false
},
"strict": true
},
"verbosity": "medium"
},
"tool_choice": "auto",
"tools": [],
"top_logprobs": 0,
"top_p": 1.0,
"truncation": "disabled",
"usage": {
"input_tokens": 57,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 81,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 138
},
"user": null,
"metadata": {}
}
67 changes: 67 additions & 0 deletions tests/Fixtures/openai/text-conversation-1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"id": "resp_68ba0b88a6948190b7b902be922272040ea504bb5513dcc4",
"object": "response",
"created_at": 1757023112,
"status": "completed",
"background": false,
"conversation": {
"id": "conv_68ba0b8846108190996008e4964f3cb70ea504bb5513dcc4"
},
"error": null,
"incomplete_details": null,
"instructions": null,
"max_output_tokens": null,
"max_tool_calls": null,
"model": "gpt-4.1-2025-04-14",
"output": [
{
"id": "msg_68ba0b897d248190b09c394da519519d0ea504bb5513dcc4",
"type": "message",
"status": "completed",
"content": [
{
"type": "output_text",
"annotations": [],
"logprobs": [],
"text": "Once, three clever pigs built cozy homes in a peaceful meadow, each using straw, sticks, and bricks. When a gentle but lonely wolf wandered by, he invited the pigs to a moonlit picnic rather than blowing their houses down. Under the twinkling stars, they became the best of friends and shared sweet dreams together."
}
],
"role": "assistant"
}
],
"parallel_tool_calls": true,
"previous_response_id": null,
"prompt_cache_key": null,
"reasoning": {
"effort": null,
"summary": null
},
"safety_identifier": null,
"service_tier": "default",
"store": true,
"temperature": 1.0,
"text": {
"format": {
"type": "text"
},
"verbosity": "medium"
},
"tool_choice": "auto",
"tools": [],
"top_logprobs": 0,
"top_p": 1.0,
"truncation": "disabled",
"usage": {
"input_tokens": 29,
"input_tokens_details": {
"cached_tokens": 0
},
"output_tokens": 67,
"output_tokens_details": {
"reasoning_tokens": 0
},
"total_tokens": 96
},
"user": null,
"metadata": {}
}
2 changes: 1 addition & 1 deletion tests/Providers/ElevenLabs/AudioTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@

Http::assertSent(function (Request $request): bool {
$data = collect($request->data())
->flatMap(fn ($content) => [$content['name'] => $content['contents']])
->flatMap(fn ($content): array => [$content['name'] => $content['contents']])
->toArray();

return $data['language_code'] === 'es'
Expand Down
17 changes: 17 additions & 0 deletions tests/Providers/OpenAI/StreamTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,20 @@

Http::assertSent(fn (Request $request): bool => $request->data()['reasoning']['effort'] === 'low');
});

it('sends conversation when defined', function (): void {
FixtureResponse::fakeResponseSequence('v1/responses', 'openai/stream-conversation');

$response = Prism::text()
->using('openai', 'gpt-5')
->withPrompt('Who are you?')
->withProviderOptions([
'conversation' => 'conv_abc123',
])
->asStream();

// process stream
collect($response);

Http::assertSent(fn (Request $request): bool => $request->data()['conversation'] === 'conv_abc123');
});
26 changes: 26 additions & 0 deletions tests/Providers/OpenAI/StructuredTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -413,3 +413,29 @@

Http::assertSent(fn (Request $request): bool => $request->data()['reasoning']['effort'] === 'low');
});

it('sends conversation when defined', function (): void {
FixtureResponse::fakeResponseSequence('v1/responses', 'openai/structured-conversation');

$schema = new ObjectSchema(
'output',
'the output object',
[
new StringSchema('weather', 'The weather forecast'),
new StringSchema('game_time', 'The tigers game time'),
new BooleanSchema('coat_required', 'whether a coat is required'),
],
['weather', 'game_time', 'coat_required']
);

Prism::structured()
->using('openai', 'gpt-5')
->withPrompt('Who are you?')
->withProviderOptions([
'conversation' => 'conv_abc123',
])
->withSchema($schema)
->asStructured();

Http::assertSent(fn (Request $request): bool => $request->data()['conversation'] === 'conv_abc123');
});
14 changes: 14 additions & 0 deletions tests/Providers/OpenAI/TextTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,20 @@
Http::assertSent(fn (Request $request): bool => $request->data()['reasoning']['effort'] === 'low');
});

it('sends conversation when defined', function (): void {
FixtureResponse::fakeResponseSequence('v1/responses', 'openai/text-conversation');

Prism::text()
->using('openai', 'gpt-5')
->withPrompt('Tell me a 3-sentence bedtime story.')
->withProviderOptions([
'conversation' => 'conv_abc123',
])
->asText();

Http::assertSent(fn (Request $request): bool => $request->data()['conversation'] === 'conv_abc123');
});

describe('citations', function (): void {
it('adds citations to additionalContent on response steps and assistant message for the web search tool', function (): void {
FixtureResponse::fakeResponseSequence('v1/responses', 'openai/generate-text-with-web-search-citations');
Expand Down