Skip to content

Commit 5d0f9c8

Browse files
authored
fix(OpenAI): support reused prompts / instructions in Responses (#616)
* fix(OpenAI): support reused prompts / instructions in Responses * fix(OpenAI): add reused prompts to retrieve call
1 parent 76bebfd commit 5d0f9c8

File tree

6 files changed

+167
-4
lines changed

6 files changed

+167
-4
lines changed

src/Responses/Responses/CreateResponse.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@
5858
* @phpstan-import-type FunctionToolChoiceType from FunctionToolChoice
5959
* @phpstan-import-type HostedToolChoiceType from HostedToolChoice
6060
* @phpstan-import-type ReasoningType from CreateResponseReasoning
61+
* @phpstan-import-type ReferencePromptObjectType from ReferencePromptObject
6162
*
63+
* @phpstan-type InstructionsType array<int, mixed>|string|null
6264
* @phpstan-type ToolChoiceType 'none'|'auto'|'required'|FunctionToolChoiceType|HostedToolChoiceType
6365
* @phpstan-type ToolsType array<int, ComputerUseToolType|FileSearchToolType|FunctionToolType|WebSearchToolType|ImageGenerationToolType|RemoteMcpToolType|CodeInterpreterToolType>
6466
* @phpstan-type OutputType array<int, OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCodeInterpreterToolCallType>
65-
* @phpstan-type CreateResponseType array{id: string, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details: IncompleteDetailsType|null, instructions: string|null, max_output_tokens: int|null, model: string, output: OutputType, output_text: string|null, parallel_tool_calls: bool, previous_response_id: string|null, reasoning: ReasoningType|null, store: bool, temperature: float|null, text: ResponseFormatType, tool_choice: ToolChoiceType, tools: ToolsType, top_p: float|null, truncation: 'auto'|'disabled'|null, usage: UsageType|null, user: string|null, metadata: array<string, string>|null}
67+
* @phpstan-type CreateResponseType array{id: string, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details: IncompleteDetailsType|null, instructions: InstructionsType, max_output_tokens: int|null, model: string, output: OutputType, output_text: string|null, parallel_tool_calls: bool, previous_response_id: string|null, prompt: ReferencePromptObjectType|null, reasoning: ReasoningType|null, store: bool, temperature: float|null, text: ResponseFormatType, tool_choice: ToolChoiceType, tools: ToolsType, top_p: float|null, truncation: 'auto'|'disabled'|null, usage: UsageType|null, user: string|null, metadata: array<string, string>|null}
6668
*
6769
* @implements ResponseContract<CreateResponseType>
6870
*/
@@ -79,6 +81,7 @@ final class CreateResponse implements ResponseContract, ResponseHasMetaInformati
7981
/**
8082
* @param 'response' $object
8183
* @param 'completed'|'failed'|'in_progress'|'incomplete' $status
84+
* @param array<int, mixed>|string|null $instructions
8285
* @param array<int, OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall> $output
8386
* @param array<int, ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|ImageGenerationTool|CodeInterpreterTool> $tools
8487
* @param 'auto'|'disabled'|null $truncation
@@ -91,13 +94,14 @@ private function __construct(
9194
public readonly string $status,
9295
public readonly ?CreateResponseError $error,
9396
public readonly ?CreateResponseIncompleteDetails $incompleteDetails,
94-
public readonly ?string $instructions,
97+
public readonly array|string|null $instructions,
9598
public readonly ?int $maxOutputTokens,
9699
public readonly string $model,
97100
public readonly array $output,
98101
public readonly ?string $outputText,
99102
public readonly bool $parallelToolCalls,
100103
public readonly ?string $previousResponseId,
104+
public readonly ?ReferencePromptObject $prompt,
101105
public readonly ?CreateResponseReasoning $reasoning,
102106
public readonly bool $store,
103107
public readonly ?float $temperature,
@@ -184,6 +188,9 @@ public static function from(array $attributes, MetaInformation $meta): self
184188
outputText: empty($texts) ? null : implode(' ', $texts),
185189
parallelToolCalls: $attributes['parallel_tool_calls'],
186190
previousResponseId: $attributes['previous_response_id'],
191+
prompt: isset($attributes['prompt'])
192+
? ReferencePromptObject::from($attributes['prompt'])
193+
: null,
187194
reasoning: isset($attributes['reasoning'])
188195
? CreateResponseReasoning::from($attributes['reasoning'])
189196
: null,
@@ -227,6 +234,7 @@ public function toArray(): array
227234
),
228235
'parallel_tool_calls' => $this->parallelToolCalls,
229236
'previous_response_id' => $this->previousResponseId,
237+
'prompt' => $this->prompt?->toArray(),
230238
'reasoning' => $this->reasoning?->toArray(),
231239
'store' => $this->store,
232240
'temperature' => $this->temperature,
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Responses\Concerns\ArrayAccessible;
9+
use OpenAI\Testing\Responses\Concerns\Fakeable;
10+
11+
/**
12+
* @phpstan-type ReferencePromptObjectType array{id: string, variables?: array<string, string>|null, version?: string|null}
13+
*
14+
* @implements ResponseContract<ReferencePromptObjectType>
15+
*/
16+
final class ReferencePromptObject implements ResponseContract
17+
{
18+
/**
19+
* @use ArrayAccessible<ReferencePromptObjectType>
20+
*/
21+
use ArrayAccessible;
22+
23+
use Fakeable;
24+
25+
/**
26+
* @param array<string, string>|null $variables
27+
*/
28+
private function __construct(
29+
public readonly string $id,
30+
public readonly ?array $variables = null,
31+
public readonly ?string $version = null,
32+
) {}
33+
34+
/**
35+
* @param ReferencePromptObjectType $attributes
36+
*/
37+
public static function from(array $attributes): self
38+
{
39+
return new self(
40+
id: $attributes['id'],
41+
variables: $attributes['variables'] ?? null,
42+
version: $attributes['version'] ?? null,
43+
);
44+
}
45+
46+
/**
47+
* {@inheritDoc}
48+
*/
49+
public function toArray(): array
50+
{
51+
return [
52+
'id' => $this->id,
53+
'variables' => $this->variables,
54+
'version' => $this->version,
55+
];
56+
}
57+
}

src/Responses/Responses/RetrieveResponse.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@
5858
* @phpstan-import-type FunctionToolChoiceType from FunctionToolChoice
5959
* @phpstan-import-type HostedToolChoiceType from HostedToolChoice
6060
* @phpstan-import-type ReasoningType from CreateResponseReasoning
61+
* @phpstan-import-type ReferencePromptObjectType from ReferencePromptObject
6162
*
63+
* @phpstan-type InstructionsType array<int, mixed>|string|null
6264
* @phpstan-type ToolChoiceType 'none'|'auto'|'required'|FunctionToolChoiceType|HostedToolChoiceType
6365
* @phpstan-type ToolsType array<int, ComputerUseToolType|FileSearchToolType|FunctionToolType|WebSearchToolType|ImageGenerationToolType|RemoteMcpToolType|CodeInterpreterToolType>
6466
* @phpstan-type OutputType array<int, OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType>
65-
* @phpstan-type RetrieveResponseType array{id: string, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details: IncompleteDetailsType|null, instructions: string|null, max_output_tokens: int|null, model: string, output: OutputType, parallel_tool_calls: bool, previous_response_id: string|null, reasoning: ReasoningType|null, store: bool, temperature: float|null, text: ResponseFormatType, tool_choice: ToolChoiceType, tools: ToolsType, top_p: float|null, truncation: 'auto'|'disabled'|null, usage: UsageType|null, user: string|null, metadata: array<string, string>|null}
67+
* @phpstan-type RetrieveResponseType array{id: string, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details: IncompleteDetailsType|null, instructions: InstructionsType, max_output_tokens: int|null, model: string, output: OutputType, parallel_tool_calls: bool, previous_response_id: string|null, prompt: ReferencePromptObjectType|null, reasoning: ReasoningType|null, store: bool, temperature: float|null, text: ResponseFormatType, tool_choice: ToolChoiceType, tools: ToolsType, top_p: float|null, truncation: 'auto'|'disabled'|null, usage: UsageType|null, user: string|null, metadata: array<string, string>|null}
6668
*
6769
* @implements ResponseContract<RetrieveResponseType>
6870
*/
@@ -79,6 +81,7 @@ final class RetrieveResponse implements ResponseContract, ResponseHasMetaInforma
7981
/**
8082
* @param 'response' $object
8183
* @param 'completed'|'failed'|'in_progress'|'incomplete' $status
84+
* @param array<int, mixed>|string|null $instructions
8285
* @param array<int, OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall> $output
8386
* @param array<int, ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool> $tools
8487
* @param 'auto'|'disabled'|null $truncation
@@ -91,12 +94,13 @@ private function __construct(
9194
public readonly string $status,
9295
public readonly ?CreateResponseError $error,
9396
public readonly ?CreateResponseIncompleteDetails $incompleteDetails,
94-
public readonly ?string $instructions,
97+
public readonly array|string|null $instructions,
9598
public readonly ?int $maxOutputTokens,
9699
public readonly string $model,
97100
public readonly array $output,
98101
public readonly bool $parallelToolCalls,
99102
public readonly ?string $previousResponseId,
103+
public readonly ?ReferencePromptObject $prompt,
100104
public readonly ?CreateResponseReasoning $reasoning,
101105
public readonly bool $store,
102106
public readonly ?float $temperature,
@@ -169,6 +173,9 @@ public static function from(array $attributes, MetaInformation $meta): self
169173
output: $output,
170174
parallelToolCalls: $attributes['parallel_tool_calls'],
171175
previousResponseId: $attributes['previous_response_id'],
176+
prompt: isset($attributes['prompt'])
177+
? ReferencePromptObject::from($attributes['prompt'])
178+
: null,
172179
reasoning: isset($attributes['reasoning'])
173180
? CreateResponseReasoning::from($attributes['reasoning'])
174181
: null,
@@ -212,6 +219,7 @@ public function toArray(): array
212219
),
213220
'parallel_tool_calls' => $this->parallelToolCalls,
214221
'previous_response_id' => $this->previousResponseId,
222+
'prompt' => $this->prompt?->toArray(),
215223
'reasoning' => $this->reasoning?->toArray(),
216224
'store' => $this->store,
217225
'temperature' => $this->temperature,

tests/Fixtures/Responses.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ function createResponseResource(): array
2626
],
2727
'parallel_tool_calls' => true,
2828
'previous_response_id' => null,
29+
'prompt' => null,
2930
'reasoning' => [
3031
'effort' => null,
3132
'generate_summary' => null,
@@ -61,6 +62,75 @@ function createResponseResource(): array
6162
];
6263
}
6364

65+
function createResponseStoredPromptResource(): array
66+
{
67+
return [
68+
'id' => 'resp_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c',
69+
'object' => 'response',
70+
'created_at' => 1741484430,
71+
'status' => 'completed',
72+
'error' => null,
73+
'incomplete_details' => null,
74+
'instructions' => [
75+
[
76+
'type' => 'message',
77+
'content' => [
78+
[
79+
'type' => 'input_text',
80+
'text' => 'What is the weather in Tampa?',
81+
],
82+
],
83+
'role' => 'system',
84+
],
85+
],
86+
'max_output_tokens' => null,
87+
'model' => 'gpt-4.1-nano-2025-04-14',
88+
'output' => [
89+
outputBasicMessage(),
90+
],
91+
'outputText' => 'The weather in Tampa is sunny with a high of 85°F.',
92+
'prompt' => [
93+
'id' => 'prompt_67ccf18ef5fc8190b16dbee19bc54e5f087bb177ab789d5c',
94+
'variables' => [
95+
'city' => [
96+
'type' => 'input_text',
97+
'text' => 'Tampa',
98+
],
99+
],
100+
'version' => '1',
101+
],
102+
'parallel_tool_calls' => true,
103+
'previous_response_id' => null,
104+
'reasoning' => [
105+
'effort' => null,
106+
'generate_summary' => null,
107+
],
108+
'store' => true,
109+
'temperature' => 1.0,
110+
'text' => [
111+
'format' => [
112+
'type' => 'text',
113+
],
114+
],
115+
'tool_choice' => 'auto',
116+
'tools' => [],
117+
'top_p' => 1.0,
118+
'truncation' => 'disabled',
119+
'usage' => [
120+
'input_tokens' => 328,
121+
'input_tokens_details' => [
122+
'cached_tokens' => 0,
123+
],
124+
'output_tokens' => 356,
125+
'output_tokens_details' => [
126+
'reasoning_tokens' => 0,
127+
],
128+
'total_tokens' => 684,
129+
],
130+
'user' => null,
131+
];
132+
}
133+
64134
/**
65135
* @return array<string, mixed>
66136
*/
@@ -83,6 +153,7 @@ function retrieveResponseResource(): array
83153
],
84154
'parallel_tool_calls' => true,
85155
'previous_response_id' => null,
156+
'prompt' => null,
86157
'reasoning' => [
87158
'effort' => null,
88159
'generate_summary' => null,

tests/Resources/Responses.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use OpenAI\Responses\Responses\CreateStreamedResponse;
88
use OpenAI\Responses\Responses\DeleteResponse;
99
use OpenAI\Responses\Responses\ListInputItems;
10+
use OpenAI\Responses\Responses\ReferencePromptObject;
1011
use OpenAI\Responses\Responses\RetrieveResponse;
1112
use OpenAI\Responses\StreamResponse;
1213

@@ -73,6 +74,23 @@
7374
->toBeInstanceOf(MetaInformation::class);
7475
});
7576

77+
test('create with stored prompt.', function () {
78+
$client = mockClient('POST', 'responses', [
79+
'model' => 'gpt-4o',
80+
'input' => 'what was a positive news story from today?',
81+
], \OpenAI\ValueObjects\Transporter\Response::from(createResponseStoredPromptResource(), metaHeaders()));
82+
83+
$result = $client->responses()->create([
84+
'model' => 'gpt-4o',
85+
'input' => 'what was a positive news story from today?',
86+
]);
87+
88+
expect($result)
89+
->toBeInstanceOf(CreateResponse::class)
90+
->instructions->toBeArray()
91+
->prompt->toBeInstanceOf(ReferencePromptObject::class);
92+
});
93+
7694
test('create throws an exception if stream option is true', function () {
7795
OpenAI::client('foo')->responses()->create([
7896
'model' => 'gpt-3.5-turbo',

tests/Responses/Responses/RetrieveResponse.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
->output->toHaveCount(2)
2525
->parallelToolCalls->toBeTrue()
2626
->previousResponseId->toBeNull()
27+
->prompt->toBeNull()
2728
->reasoning->toBeInstanceOf(CreateResponseReasoning::class)
2829
->store->toBeTrue()
2930
->temperature->toBe(1.0)

0 commit comments

Comments
 (0)