Skip to content

Commit 1d4002d

Browse files
feat(OpenAI): Add 'Code Interpreter' support to Responses (#610)
* feat(OpenAI): Add 'Code Interpreter' support to Responses * feat(OpenAI): Add 'Code Interpreter' support to Responses * - `CreateResponse.php` - Added code interpreter to response types - `CreateStreamedResponse.php` - Added streaming support for code interpreter - `OutputCodeInterpreterToolCall.php` - New file for code interpreter output handling - `OutputMessageContentOutputTextAnnotationsContainerFile.php` - New file for container file annotations - `OutputMessageContentOutputText.php` - Updated to support container file annotations - `CodeInterpreterCall.php` - New streaming event handler - `OutputTextAnnotationAdded.php` - New streaming event for text annotations - `CodeInterpreterCodeDone.php` - New streaming event for code completion - `CodeInterpreterCodeDelta.php` - New streaming event for code deltas - `OutputItem.php` - Updated to handle code interpreter outputs - `CodeInterpreterTool.php` - New tool type definition * refactor: improve type safety and add static analysis annotations for code interpreter responses composer test now reports: • 896 PHPUnit tests PASS • Laravel Pint style PASS • php-stan level passes with zero errors. * feat(OpenAI): Add 'Code Interpreter' support to Responses * feat(OpenAI): Add 'Code Interpreter' support to Responses * feat(OpenAI): Add 'Code Interpreter' support to Responses * test(OpenAI): add test for output format * test(OpenAI): add test for output format tool call * test(OpenAI): add test for output format tool call * fix(OpenAI): correct streaming/response for code interpreter * chore(OpenAI) fix: move type to end * chore(OpenAI) fix: shorthand for function on toArray --------- Co-authored-by: Michael Sitarzewski <[email protected]>
1 parent 3ad0aaf commit 1d4002d

20 files changed

+884
-23
lines changed

src/Responses/Responses/CreateResponse.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use OpenAI\Responses\Concerns\ArrayAccessible;
1010
use OpenAI\Responses\Concerns\HasMetaInformation;
1111
use OpenAI\Responses\Meta\MetaInformation;
12+
use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall;
1213
use OpenAI\Responses\Responses\Output\OutputComputerToolCall;
1314
use OpenAI\Responses\Responses\Output\OutputFileSearchToolCall;
1415
use OpenAI\Responses\Responses\Output\OutputFunctionToolCall;
@@ -20,6 +21,7 @@
2021
use OpenAI\Responses\Responses\Output\OutputMessageContentOutputText;
2122
use OpenAI\Responses\Responses\Output\OutputReasoning;
2223
use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall;
24+
use OpenAI\Responses\Responses\Tool\CodeInterpreterTool;
2325
use OpenAI\Responses\Responses\Tool\ComputerUseTool;
2426
use OpenAI\Responses\Responses\Tool\FileSearchTool;
2527
use OpenAI\Responses\Responses\Tool\FunctionTool;
@@ -42,12 +44,14 @@
4244
* @phpstan-import-type OutputMcpApprovalRequestType from OutputMcpApprovalRequest
4345
* @phpstan-import-type OutputMcpCallType from OutputMcpCall
4446
* @phpstan-import-type OutputImageGenerationToolCallType from OutputImageGenerationToolCall
47+
* @phpstan-import-type OutputCodeInterpreterToolCallType from OutputCodeInterpreterToolCall
4548
* @phpstan-import-type ComputerUseToolType from ComputerUseTool
4649
* @phpstan-import-type FileSearchToolType from FileSearchTool
4750
* @phpstan-import-type ImageGenerationToolType from ImageGenerationTool
4851
* @phpstan-import-type RemoteMcpToolType from RemoteMcpTool
4952
* @phpstan-import-type FunctionToolType from FunctionTool
5053
* @phpstan-import-type WebSearchToolType from WebSearchTool
54+
* @phpstan-import-type CodeInterpreterToolType from CodeInterpreterTool
5155
* @phpstan-import-type ErrorType from CreateResponseError
5256
* @phpstan-import-type IncompleteDetailsType from CreateResponseIncompleteDetails
5357
* @phpstan-import-type UsageType from CreateResponseUsage
@@ -56,8 +60,8 @@
5660
* @phpstan-import-type ReasoningType from CreateResponseReasoning
5761
*
5862
* @phpstan-type ToolChoiceType 'none'|'auto'|'required'|FunctionToolChoiceType|HostedToolChoiceType
59-
* @phpstan-type ToolsType array<int, ComputerUseToolType|FileSearchToolType|FunctionToolType|WebSearchToolType|ImageGenerationToolType|RemoteMcpToolType|ImageGenerationToolType>
60-
* @phpstan-type OutputType array<int, OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType>
63+
* @phpstan-type ToolsType array<int, ComputerUseToolType|FileSearchToolType|FunctionToolType|WebSearchToolType|ImageGenerationToolType|RemoteMcpToolType|CodeInterpreterToolType>
64+
* @phpstan-type OutputType array<int, OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCodeInterpreterToolCallType>
6165
* @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}
6266
*
6367
* @implements ResponseContract<CreateResponseType>
@@ -75,8 +79,8 @@ final class CreateResponse implements ResponseContract, ResponseHasMetaInformati
7579
/**
7680
* @param 'response' $object
7781
* @param 'completed'|'failed'|'in_progress'|'incomplete' $status
78-
* @param array<int, OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall> $output
79-
* @param array<int, ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|ImageGenerationTool> $tools
82+
* @param array<int, OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall> $output
83+
* @param array<int, ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|ImageGenerationTool|CodeInterpreterTool> $tools
8084
* @param 'auto'|'disabled'|null $truncation
8185
* @param array<string, string> $metadata
8286
*/
@@ -114,7 +118,7 @@ private function __construct(
114118
public static function from(array $attributes, MetaInformation $meta): self
115119
{
116120
$output = array_map(
117-
fn (array $output): OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall => match ($output['type']) {
121+
fn (array $output): OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall => match ($output['type']) {
118122
'message' => OutputMessage::from($output),
119123
'file_search_call' => OutputFileSearchToolCall::from($output),
120124
'function_call' => OutputFunctionToolCall::from($output),
@@ -125,6 +129,7 @@ public static function from(array $attributes, MetaInformation $meta): self
125129
'mcp_approval_request' => OutputMcpApprovalRequest::from($output),
126130
'mcp_call' => OutputMcpCall::from($output),
127131
'image_generation_call' => OutputImageGenerationToolCall::from($output),
132+
'code_interpreter_call' => OutputCodeInterpreterToolCall::from($output),
128133
},
129134
$attributes['output'],
130135
);
@@ -137,13 +142,14 @@ public static function from(array $attributes, MetaInformation $meta): self
137142
: $attributes['tool_choice'];
138143

139144
$tools = array_map(
140-
fn (array $tool): ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool => match ($tool['type']) {
145+
fn (array $tool): ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool => match ($tool['type']) {
141146
'file_search' => FileSearchTool::from($tool),
142147
'web_search_preview', 'web_search_preview_2025_03_11' => WebSearchTool::from($tool),
143148
'function' => FunctionTool::from($tool),
144149
'computer_use_preview' => ComputerUseTool::from($tool),
145150
'image_generation' => ImageGenerationTool::from($tool),
146151
'mcp' => RemoteMcpTool::from($tool),
152+
'code_interpreter' => CodeInterpreterTool::from($tool),
147153
},
148154
$attributes['tools'],
149155
);
@@ -216,7 +222,7 @@ public function toArray(): array
216222
'metadata' => $this->metadata ?? [],
217223
'model' => $this->model,
218224
'output' => array_map(
219-
fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall $output): array => $output->toArray(),
225+
fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall $output): array => $output->toArray(),
220226
$this->output
221227
),
222228
'parallel_tool_calls' => $this->parallelToolCalls,
@@ -229,7 +235,7 @@ public function toArray(): array
229235
? $this->toolChoice
230236
: $this->toolChoice->toArray(),
231237
'tools' => array_map(
232-
fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool $tool): array => $tool->toArray(),
238+
fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool $tool): array => $tool->toArray(),
233239
$this->tools
234240
),
235241
'top_p' => $this->topP,

src/Responses/Responses/CreateStreamedResponse.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
use OpenAI\Contracts\ResponseContract;
88
use OpenAI\Exceptions\UnknownEventException;
99
use OpenAI\Responses\Concerns\ArrayAccessible;
10+
use OpenAI\Responses\Responses\Streaming\CodeInterpreterCall;
11+
use OpenAI\Responses\Responses\Streaming\CodeInterpreterCodeDelta;
12+
use OpenAI\Responses\Responses\Streaming\CodeInterpreterCodeDone;
1013
use OpenAI\Responses\Responses\Streaming\ContentPart;
1114
use OpenAI\Responses\Responses\Streaming\Error;
1215
use OpenAI\Responses\Responses\Streaming\FileSearchCall;
@@ -47,7 +50,7 @@ final class CreateStreamedResponse implements ResponseContract
4750

4851
private function __construct(
4952
public readonly string $event,
50-
public readonly CreateResponse|OutputItem|ContentPart|OutputTextDelta|OutputTextAnnotationAdded|OutputTextDone|RefusalDelta|RefusalDone|FunctionCallArgumentsDelta|FunctionCallArgumentsDone|FileSearchCall|WebSearchCall|ReasoningSummaryPart|ReasoningSummaryTextDelta|ReasoningSummaryTextDone|McpListTools|McpListToolsInProgress|McpCall|McpCallArgumentsDelta|McpCallArgumentsDone|ImageGenerationPart|ImageGenerationPartialImage|Error $response,
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,
5154
) {}
5255

5356
/**
@@ -82,6 +85,12 @@ public static function from(array $attributes): self
8285
'response.web_search_call.in_progress',
8386
'response.web_search_call.searching',
8487
'response.web_search_call.completed' => WebSearchCall::from($attributes, $meta), // @phpstan-ignore-line
88+
'response.code_interpreter_call.in_progress',
89+
'response.code_interpreter_call.running',
90+
'response.code_interpreter_call.interpreting',
91+
'response.code_interpreter_call.completed' => CodeInterpreterCall::from($attributes, $meta), // @phpstan-ignore-line
92+
'response.code_interpreter_call_code.delta' => CodeInterpreterCodeDelta::from($attributes, $meta), // @phpstan-ignore-line
93+
'response.code_interpreter_call_code.done' => CodeInterpreterCodeDone::from($attributes, $meta), // @phpstan-ignore-line
8594
'response.reasoning_summary_part.added',
8695
'response.reasoning_summary_part.done' => ReasoningSummaryPart::from($attributes, $meta), // @phpstan-ignore-line
8796
'response.reasoning_summary_text.delta' => ReasoningSummaryTextDelta::from($attributes, $meta), // @phpstan-ignore-line
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Output\CodeInterpreter;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Responses\Concerns\ArrayAccessible;
9+
use OpenAI\Testing\Responses\Concerns\Fakeable;
10+
11+
/**
12+
* @phpstan-type CodeFileObjectType array{file_id: string, mime_type: string}
13+
*
14+
* @implements ResponseContract<CodeFileObjectType>
15+
*/
16+
final class CodeFileObject implements ResponseContract
17+
{
18+
/**
19+
* @use ArrayAccessible<CodeFileObjectType>
20+
*/
21+
use ArrayAccessible;
22+
23+
use Fakeable;
24+
25+
private function __construct(
26+
public readonly string $fileId,
27+
public readonly string $mimeType,
28+
) {}
29+
30+
/**
31+
* @param CodeFileObjectType $attributes
32+
*/
33+
public static function from(array $attributes): self
34+
{
35+
return new self(
36+
fileId: $attributes['file_id'],
37+
mimeType: $attributes['mime_type'],
38+
);
39+
}
40+
41+
/**
42+
* {@inheritDoc}
43+
*/
44+
public function toArray(): array
45+
{
46+
return [
47+
'file_id' => $this->fileId,
48+
'mime_type' => $this->mimeType,
49+
];
50+
}
51+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Output\CodeInterpreter;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Responses\Concerns\ArrayAccessible;
9+
use OpenAI\Testing\Responses\Concerns\Fakeable;
10+
11+
/**
12+
* @phpstan-import-type CodeFileObjectType from CodeFileObject
13+
*
14+
* @phpstan-type CodeFileOutputType array{files: array<int, CodeFileObjectType>, type: 'files'}
15+
*
16+
* @implements ResponseContract<CodeFileOutputType>
17+
*/
18+
final class CodeFileOutput implements ResponseContract
19+
{
20+
/**
21+
* @use ArrayAccessible<CodeFileOutputType>
22+
*/
23+
use ArrayAccessible;
24+
25+
use Fakeable;
26+
27+
/**
28+
* @param array<int, CodeFileObject> $files
29+
* @param 'files' $type
30+
*/
31+
private function __construct(
32+
public readonly array $files,
33+
public readonly string $type,
34+
) {}
35+
36+
/**
37+
* @param CodeFileOutputType $attributes
38+
*/
39+
public static function from(array $attributes): self
40+
{
41+
$files = array_map(
42+
static fn (array $file): CodeFileObject => CodeFileObject::from($file),
43+
$attributes['files']
44+
);
45+
46+
return new self(
47+
files: $files,
48+
type: $attributes['type'],
49+
);
50+
}
51+
52+
/**
53+
* {@inheritDoc}
54+
*/
55+
public function toArray(): array
56+
{
57+
return [
58+
'type' => $this->type,
59+
'files' => array_map(
60+
static fn (CodeFileObject $file): array => $file->toArray(),
61+
$this->files
62+
),
63+
];
64+
}
65+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Output\CodeInterpreter;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Responses\Concerns\ArrayAccessible;
9+
use OpenAI\Testing\Responses\Concerns\Fakeable;
10+
11+
/**
12+
* @phpstan-type CodeTextOutputType array{logs: string, type: 'logs'}
13+
*
14+
* @implements ResponseContract<CodeTextOutputType>
15+
*/
16+
final class CodeTextOutput implements ResponseContract
17+
{
18+
/**
19+
* @use ArrayAccessible<CodeTextOutputType>
20+
*/
21+
use ArrayAccessible;
22+
23+
use Fakeable;
24+
25+
/**
26+
* @param 'logs' $type
27+
*/
28+
private function __construct(
29+
public readonly string $logs,
30+
public readonly string $type,
31+
) {}
32+
33+
/**
34+
* @param CodeTextOutputType $attributes
35+
*/
36+
public static function from(array $attributes): self
37+
{
38+
return new self(
39+
logs: $attributes['logs'],
40+
type: $attributes['type'],
41+
);
42+
}
43+
44+
/**
45+
* {@inheritDoc}
46+
*/
47+
public function toArray(): array
48+
{
49+
return [
50+
'type' => $this->type,
51+
'logs' => $this->logs,
52+
];
53+
}
54+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OpenAI\Responses\Responses\Output;
6+
7+
use OpenAI\Contracts\ResponseContract;
8+
use OpenAI\Responses\Concerns\ArrayAccessible;
9+
use OpenAI\Responses\Responses\Output\CodeInterpreter\CodeFileOutput;
10+
use OpenAI\Responses\Responses\Output\CodeInterpreter\CodeTextOutput;
11+
use OpenAI\Testing\Responses\Concerns\Fakeable;
12+
13+
/**
14+
* @phpstan-import-type CodeFileOutputType from CodeFileOutput
15+
* @phpstan-import-type CodeTextOutputType from CodeTextOutput
16+
*
17+
* @phpstan-type OutputType array<int, CodeFileOutputType|CodeTextOutputType>|null
18+
* @phpstan-type OutputCodeInterpreterToolCallType array{code: string, id: string, outputs: OutputType, status: string, type: 'code_interpreter_call', container_id: string}
19+
*
20+
* @implements ResponseContract<OutputCodeInterpreterToolCallType>
21+
*/
22+
final class OutputCodeInterpreterToolCall implements ResponseContract
23+
{
24+
/**
25+
* @use ArrayAccessible<OutputCodeInterpreterToolCallType>
26+
*/
27+
use ArrayAccessible;
28+
29+
use Fakeable;
30+
31+
/**
32+
* @param array<int, CodeFileOutput|CodeTextOutput>|null $outputs
33+
* @param 'code_interpreter_call' $type
34+
*/
35+
private function __construct(
36+
public readonly string $code,
37+
public readonly string $id,
38+
public readonly ?array $outputs,
39+
public readonly string $status,
40+
public readonly string $type,
41+
public readonly string $containerId,
42+
) {}
43+
44+
/**
45+
* @param OutputCodeInterpreterToolCallType $attributes
46+
*/
47+
public static function from(array $attributes): self
48+
{
49+
$outputs = null;
50+
51+
if (is_array($attributes['outputs'])) {
52+
$outputs = array_map(
53+
static fn (array $output): CodeFileOutput|CodeTextOutput => match ($output['type']) {
54+
'files' => CodeFileOutput::from($output),
55+
'logs' => CodeTextOutput::from($output),
56+
},
57+
$attributes['outputs']
58+
);
59+
}
60+
61+
return new self(
62+
code: $attributes['code'],
63+
id: $attributes['id'],
64+
outputs: $outputs,
65+
status: $attributes['status'],
66+
type: $attributes['type'],
67+
containerId: $attributes['container_id'],
68+
);
69+
}
70+
71+
/**
72+
* {@inheritDoc}
73+
*/
74+
public function toArray(): array
75+
{
76+
return [
77+
'code' => $this->code,
78+
'id' => $this->id,
79+
'outputs' => $this->outputs
80+
? array_map(static fn (CodeFileOutput|CodeTextOutput $output): array => $output->toArray(), $this->outputs)
81+
: null,
82+
'status' => $this->status,
83+
'type' => $this->type,
84+
'container_id' => $this->containerId,
85+
];
86+
}
87+
}

0 commit comments

Comments
 (0)