From bbda5b0ccd941b7229263b5fb5d386d99116d43d Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Mon, 6 Jul 2026 14:57:36 +0400 Subject: [PATCH 1/4] Support streaming compaction output --- .../Responses/Streaming/OutputItem.php | 7 +++++-- tests/Fixtures/Responses.php | 5 +++++ .../ResponseOutputItemCompactionDone.txt | 1 + .../Responses/CreateStreamedResponse.php | 17 +++++++++++++++++ 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 tests/Fixtures/Streams/ResponseOutputItemCompactionDone.txt diff --git a/src/Responses/Responses/Streaming/OutputItem.php b/src/Responses/Responses/Streaming/OutputItem.php index 1223c0e3b..68e037452 100644 --- a/src/Responses/Responses/Streaming/OutputItem.php +++ b/src/Responses/Responses/Streaming/OutputItem.php @@ -10,6 +10,7 @@ use OpenAI\Responses\Concerns\HasMetaInformation; use OpenAI\Responses\Meta\MetaInformation; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; +use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; use OpenAI\Responses\Responses\Output\OutputFileSearchToolCall; use OpenAI\Responses\Responses\Output\OutputFunctionToolCall; @@ -34,8 +35,9 @@ * @phpstan-import-type OutputMcpApprovalRequestType from OutputMcpApprovalRequest * @phpstan-import-type OutputMcpCallType from OutputMcpCall * @phpstan-import-type OutputCodeInterpreterToolCallType from OutputCodeInterpreterToolCall + * @phpstan-import-type OutputCompactionType from OutputCompaction * - * @phpstan-type OutputItemType array{type: string, output_index: int, sequence_number: int, item: OutputCodeInterpreterToolCallType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType} + * @phpstan-type OutputItemType array{type: string, output_index: int, sequence_number: int, item: OutputCodeInterpreterToolCallType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCompactionType} * * @implements ResponseContract */ @@ -53,7 +55,7 @@ private function __construct( public readonly string $type, public readonly int $outputIndex, public readonly int $sequenceNumber, - public readonly OutputMessage|OutputCodeInterpreterToolCall|OutputFileSearchToolCall|OutputFunctionToolCall|OutputWebSearchToolCall|OutputComputerToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall $item, + public readonly OutputMessage|OutputCodeInterpreterToolCall|OutputFileSearchToolCall|OutputFunctionToolCall|OutputWebSearchToolCall|OutputComputerToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCompaction $item, private readonly MetaInformation $meta, ) {} @@ -74,6 +76,7 @@ public static function from(array $attributes, MetaInformation $meta): self 'mcp_approval_request' => OutputMcpApprovalRequest::from($attributes['item']), 'mcp_call' => OutputMcpCall::from($attributes['item']), 'code_interpreter_call' => OutputCodeInterpreterToolCall::from($attributes['item']), + 'compaction' => OutputCompaction::from($attributes['item']), }; return new self( diff --git a/tests/Fixtures/Responses.php b/tests/Fixtures/Responses.php index 36fd6a2a2..4da44a0f7 100644 --- a/tests/Fixtures/Responses.php +++ b/tests/Fixtures/Responses.php @@ -1042,3 +1042,8 @@ function responseRateLimitsUpdatedEvent() { return fopen(__DIR__.'/Streams/ResponseRateLimitsUpdated.txt', 'r'); } + +function responseOutputItemCompactionDoneEvent() +{ + return fopen(__DIR__.'/Streams/ResponseOutputItemCompactionDone.txt', 'r'); +} diff --git a/tests/Fixtures/Streams/ResponseOutputItemCompactionDone.txt b/tests/Fixtures/Streams/ResponseOutputItemCompactionDone.txt new file mode 100644 index 000000000..c8d409118 --- /dev/null +++ b/tests/Fixtures/Streams/ResponseOutputItemCompactionDone.txt @@ -0,0 +1 @@ +data: {"type":"response.output_item.done","output_index":0,"sequence_number":11,"item":{"id":"cmp_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c","encrypted_content":"encrypted_string_value","type":"compaction","created_by":"user_123"}} diff --git a/tests/Responses/Responses/CreateStreamedResponse.php b/tests/Responses/Responses/CreateStreamedResponse.php index e303d37f7..907243fbd 100644 --- a/tests/Responses/Responses/CreateStreamedResponse.php +++ b/tests/Responses/Responses/CreateStreamedResponse.php @@ -2,6 +2,8 @@ use OpenAI\Responses\Responses\CreateResponse; use OpenAI\Responses\Responses\CreateStreamedResponse; +use OpenAI\Responses\Responses\Output\OutputCompaction; +use OpenAI\Responses\Responses\Streaming\OutputItem; use OpenAI\Responses\Responses\Streaming\RateLimits; use OpenAI\Responses\Responses\Streaming\ReasoningTextDelta; use OpenAI\Responses\Responses\Streaming\ReasoningTextDone; @@ -68,3 +70,18 @@ 'type' => 'response.rate_limits.updated', ]); }); + +test('output item done event with compaction item', function () { + $response = CreateStreamedResponse::fake(responseOutputItemCompactionDoneEvent()); + + expect($response->getIterator()->current()) + ->toBeInstanceOf(CreateStreamedResponse::class) + ->event->toBe('response.output_item.done') + ->response->toBeInstanceOf(OutputItem::class) + ->response->outputIndex->toBe(0) + ->response->sequenceNumber->toBe(11) + ->response->item->toBeInstanceOf(OutputCompaction::class) + ->response->item->id->toBe('cmp_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->response->item->encryptedContent->toBe('encrypted_string_value') + ->response->item->createdBy->toBe('user_123'); +}); From 681754e1caf403716dc739444fbd2bbceab890f9 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Mon, 27 Jul 2026 23:46:42 +0400 Subject: [PATCH 2/4] Add apply_patch tool support --- src/Actions/Conversations/ItemObjects.php | 12 ++- src/Actions/Responses/ItemObjects.php | 12 ++- src/Actions/Responses/OutputObjects.php | 9 +- src/Actions/Responses/ToolChoiceObjects.php | 2 +- src/Actions/Responses/ToolObjects.php | 9 +- .../Conversations/ConversationItem.php | 4 +- src/Responses/Responses/CreateResponse.php | 6 +- .../Input/ApplyPatchToolCallOutput.php | 72 ++++++++++++++++ src/Responses/Responses/ListInputItems.php | 6 +- .../OutputApplyPatchOperationCreateFile.php | 57 +++++++++++++ .../OutputApplyPatchOperationDeleteFile.php | 54 ++++++++++++ .../OutputApplyPatchOperationUpdateFile.php | 57 +++++++++++++ .../Output/OutputApplyPatchToolCall.php | 85 +++++++++++++++++++ .../Output/OutputToolSearchOutput.php | 3 +- src/Responses/Responses/RetrieveResponse.php | 6 +- .../Responses/Streaming/OutputItem.php | 9 +- .../Responses/Tool/ApplyPatchTool.php | 55 ++++++++++++ .../Responses/ToolChoice/HostedToolChoice.php | 4 +- tests/Fixtures/Responses.php | 64 ++++++++++++++ .../ResponseOutputItemApplyPatchCallDone.txt | 1 + tests/Resources/Responses.php | 7 +- .../Conversations/ConversationItem.php | 18 ++++ tests/Responses/Responses/CreateResponse.php | 11 +++ .../Responses/CreateStreamedResponse.php | 17 ++++ .../Input/ApplyPatchToolCallOutput.php | 48 +++++++++++ tests/Responses/Responses/ListInputItems.php | 8 +- .../Output/OutputApplyPatchToolCall.php | 72 ++++++++++++++++ .../Responses/Tool/ApplyPatchTool.php | 29 +++++++ 28 files changed, 708 insertions(+), 29 deletions(-) create mode 100644 src/Responses/Responses/Input/ApplyPatchToolCallOutput.php create mode 100644 src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFile.php create mode 100644 src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFile.php create mode 100644 src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFile.php create mode 100644 src/Responses/Responses/Output/OutputApplyPatchToolCall.php create mode 100644 src/Responses/Responses/Tool/ApplyPatchTool.php create mode 100644 tests/Fixtures/Streams/ResponseOutputItemApplyPatchCallDone.txt create mode 100644 tests/Responses/Responses/Input/ApplyPatchToolCallOutput.php create mode 100644 tests/Responses/Responses/Output/OutputApplyPatchToolCall.php create mode 100644 tests/Responses/Responses/Tool/ApplyPatchTool.php diff --git a/src/Actions/Conversations/ItemObjects.php b/src/Actions/Conversations/ItemObjects.php index 3c0dd0a1d..d91bd37b7 100644 --- a/src/Actions/Conversations/ItemObjects.php +++ b/src/Actions/Conversations/ItemObjects.php @@ -5,11 +5,13 @@ namespace OpenAI\Actions\Conversations; use OpenAI\Responses\Conversations\Objects\Message; +use OpenAI\Responses\Responses\Input\ApplyPatchToolCallOutput; use OpenAI\Responses\Responses\Input\ComputerToolCallOutput; use OpenAI\Responses\Responses\Input\CustomToolCallOutput; use OpenAI\Responses\Responses\Input\FunctionToolCallOutput; use OpenAI\Responses\Responses\Input\LocalShellCallOutput; use OpenAI\Responses\Responses\Input\McpApprovalResponse; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; use OpenAI\Responses\Responses\Output\OutputCustomToolCall; @@ -26,6 +28,7 @@ use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; /** + * @phpstan-import-type ApplyPatchToolCallOutputType from ApplyPatchToolCallOutput * @phpstan-import-type MessageType from Message * @phpstan-import-type ComputerToolCallOutputType from ComputerToolCallOutput * @phpstan-import-type FunctionToolCallOutputType from FunctionToolCallOutput @@ -33,6 +36,7 @@ * @phpstan-import-type McpApprovalResponseType from McpApprovalResponse * @phpstan-import-type CustomToolCallOutputType from CustomToolCallOutput * @phpstan-import-type OutputComputerToolCallType from OutputComputerToolCall + * @phpstan-import-type OutputApplyPatchToolCallType from OutputApplyPatchToolCall * @phpstan-import-type OutputFileSearchToolCallType from OutputFileSearchToolCall * @phpstan-import-type OutputFunctionToolCallType from OutputFunctionToolCall * @phpstan-import-type OutputReasoningType from OutputReasoning @@ -47,9 +51,9 @@ * @phpstan-import-type OutputProgramType from OutputProgram * @phpstan-import-type OutputProgramOutputType from OutputProgramOutput * - * @phpstan-type ItemObjectTypes MessageType|ComputerToolCallOutputType|FunctionToolCallOutputType|LocalShellCallOutputType|McpApprovalResponseType|CustomToolCallOutputType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCodeInterpreterToolCallType|OutputLocalShellCallType|OutputCustomToolCallType|OutputProgramType|OutputProgramOutputType + * @phpstan-type ItemObjectTypes ApplyPatchToolCallOutputType|MessageType|ComputerToolCallOutputType|FunctionToolCallOutputType|LocalShellCallOutputType|McpApprovalResponseType|CustomToolCallOutputType|OutputApplyPatchToolCallType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCodeInterpreterToolCallType|OutputLocalShellCallType|OutputCustomToolCallType|OutputProgramType|OutputProgramOutputType * @phpstan-type ConversationItemObjectTypes array - * @phpstan-type ConversationItemObjectReturnType array + * @phpstan-type ConversationItemObjectReturnType array */ final class ItemObjects { @@ -60,7 +64,7 @@ final class ItemObjects public static function parse(array $outputItems): array { return array_map( - fn (array $item): Message|ComputerToolCallOutput|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|CustomToolCallOutput|OutputProgram|OutputProgramOutput => match ($item['type']) { + fn (array $item): ApplyPatchToolCallOutput|Message|ComputerToolCallOutput|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|OutputApplyPatchToolCall|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|CustomToolCallOutput|OutputProgram|OutputProgramOutput => match ($item['type']) { 'message' => Message::from($item), 'file_search_call' => OutputFileSearchToolCall::from($item), 'function_call' => OutputFunctionToolCall::from($item), @@ -81,6 +85,8 @@ public static function parse(array $outputItems): array 'mcp_approval_response' => McpApprovalResponse::from($item), 'program' => OutputProgram::from($item), 'program_output' => OutputProgramOutput::from($item), + 'apply_patch_call' => OutputApplyPatchToolCall::from($item), + 'apply_patch_call_output' => ApplyPatchToolCallOutput::from($item), }, $outputItems, ); diff --git a/src/Actions/Responses/ItemObjects.php b/src/Actions/Responses/ItemObjects.php index e7bdb74ff..b658b8878 100644 --- a/src/Actions/Responses/ItemObjects.php +++ b/src/Actions/Responses/ItemObjects.php @@ -4,12 +4,14 @@ namespace OpenAI\Actions\Responses; +use OpenAI\Responses\Responses\Input\ApplyPatchToolCallOutput; use OpenAI\Responses\Responses\Input\ComputerToolCallOutput; use OpenAI\Responses\Responses\Input\CustomToolCallOutput; use OpenAI\Responses\Responses\Input\FunctionToolCallOutput; use OpenAI\Responses\Responses\Input\InputMessage; use OpenAI\Responses\Responses\Input\LocalShellCallOutput; use OpenAI\Responses\Responses\Input\McpApprovalResponse; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; use OpenAI\Responses\Responses\Output\OutputCustomToolCall; @@ -27,6 +29,7 @@ use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; /** + * @phpstan-import-type ApplyPatchToolCallOutputType from ApplyPatchToolCallOutput * @phpstan-import-type InputMessageType from InputMessage * @phpstan-import-type ComputerToolCallOutputType from ComputerToolCallOutput * @phpstan-import-type FunctionToolCallOutputType from FunctionToolCallOutput @@ -34,6 +37,7 @@ * @phpstan-import-type McpApprovalResponseType from McpApprovalResponse * @phpstan-import-type CustomToolCallOutputType from CustomToolCallOutput * @phpstan-import-type OutputComputerToolCallType from OutputComputerToolCall + * @phpstan-import-type OutputApplyPatchToolCallType from OutputApplyPatchToolCall * @phpstan-import-type OutputFileSearchToolCallType from OutputFileSearchToolCall * @phpstan-import-type OutputFunctionToolCallType from OutputFunctionToolCall * @phpstan-import-type OutputMessageType from OutputMessage @@ -49,8 +53,8 @@ * @phpstan-import-type OutputProgramType from OutputProgram * @phpstan-import-type OutputProgramOutputType from OutputProgramOutput * - * @phpstan-type ResponseItemObjectTypes array - * @phpstan-type ResponseItemObjectReturnType array + * @phpstan-type ResponseItemObjectTypes array + * @phpstan-type ResponseItemObjectReturnType array */ final class ItemObjects { @@ -61,7 +65,7 @@ final class ItemObjects public static function parse(array $outputItems): array { return array_map( - fn (array $item): InputMessage|ComputerToolCallOutput|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput => match ($item['type']) { + fn (array $item): ApplyPatchToolCallOutput|InputMessage|ComputerToolCallOutput|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputApplyPatchToolCall|OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput => match ($item['type']) { 'message' => $item['role'] === 'assistant' ? OutputMessage::from($item) : InputMessage::from($item), 'file_search_call' => OutputFileSearchToolCall::from($item), 'function_call' => OutputFunctionToolCall::from($item), @@ -82,6 +86,8 @@ public static function parse(array $outputItems): array 'mcp_approval_response' => McpApprovalResponse::from($item), 'program' => OutputProgram::from($item), 'program_output' => OutputProgramOutput::from($item), + 'apply_patch_call' => OutputApplyPatchToolCall::from($item), + 'apply_patch_call_output' => ApplyPatchToolCallOutput::from($item), }, $outputItems, ); diff --git a/src/Actions/Responses/OutputObjects.php b/src/Actions/Responses/OutputObjects.php index 0e4477462..6a9e51aa0 100644 --- a/src/Actions/Responses/OutputObjects.php +++ b/src/Actions/Responses/OutputObjects.php @@ -4,6 +4,7 @@ namespace OpenAI\Actions\Responses; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; @@ -24,6 +25,7 @@ use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; /** + * @phpstan-import-type OutputApplyPatchToolCallType from OutputApplyPatchToolCall * @phpstan-import-type OutputComputerToolCallType from OutputComputerToolCall * @phpstan-import-type OutputFileSearchToolCallType from OutputFileSearchToolCall * @phpstan-import-type OutputFunctionToolCallType from OutputFunctionToolCall @@ -43,8 +45,8 @@ * @phpstan-import-type OutputToolSearchCallType from OutputToolSearchCall * @phpstan-import-type OutputToolSearchOutputType from OutputToolSearchOutput * - * @phpstan-type ResponseOutputObjectTypes array - * @phpstan-type ResponseOutputObjectReturnType array + * @phpstan-type ResponseOutputObjectTypes array + * @phpstan-type ResponseOutputObjectReturnType array */ final class OutputObjects { @@ -55,7 +57,7 @@ final class OutputObjects public static function parse(array $outputItems): array { return array_map( - fn (array $item): OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction => match ($item['type']) { + fn (array $item): OutputApplyPatchToolCall|OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction => match ($item['type']) { 'message' => OutputMessage::from($item), 'file_search_call' => OutputFileSearchToolCall::from($item), 'function_call' => OutputFunctionToolCall::from($item), @@ -74,6 +76,7 @@ public static function parse(array $outputItems): array 'tool_search_call' => OutputToolSearchCall::from($item), 'tool_search_output' => OutputToolSearchOutput::from($item), 'compaction' => OutputCompaction::from($item), + 'apply_patch_call' => OutputApplyPatchToolCall::from($item), default => throw new \UnexpectedValueException('Uh oh! We do not recognize this type. Please submit a bug to openai-php/client on GitHub!'), }, $outputItems, diff --git a/src/Actions/Responses/ToolChoiceObjects.php b/src/Actions/Responses/ToolChoiceObjects.php index 553e97aa7..48ddb22dd 100644 --- a/src/Actions/Responses/ToolChoiceObjects.php +++ b/src/Actions/Responses/ToolChoiceObjects.php @@ -24,7 +24,7 @@ public static function parse(array|string $toolChoice): string|FunctionToolChoic { return is_array($toolChoice) ? match ($toolChoice['type']) { - 'file_search', 'web_search', 'web_search_preview', 'computer_use_preview', 'programmatic_tool_calling' => HostedToolChoice::from($toolChoice), + 'apply_patch', 'file_search', 'web_search', 'web_search_preview', 'computer_use_preview', 'programmatic_tool_calling' => HostedToolChoice::from($toolChoice), 'function' => FunctionToolChoice::from($toolChoice), } : $toolChoice; diff --git a/src/Actions/Responses/ToolObjects.php b/src/Actions/Responses/ToolObjects.php index bb6c8035d..dabfb4fbe 100644 --- a/src/Actions/Responses/ToolObjects.php +++ b/src/Actions/Responses/ToolObjects.php @@ -4,6 +4,7 @@ namespace OpenAI\Actions\Responses; +use OpenAI\Responses\Responses\Tool\ApplyPatchTool; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; use OpenAI\Responses\Responses\Tool\CustomTool; @@ -17,6 +18,7 @@ use OpenAI\Responses\Responses\Tool\WebSearchTool; /** + * @phpstan-import-type ApplyPatchToolType from ApplyPatchTool * @phpstan-import-type CodeInterpreterToolType from CodeInterpreterTool * @phpstan-import-type ComputerUseToolType from ComputerUseTool * @phpstan-import-type CustomToolType from CustomTool @@ -29,8 +31,8 @@ * @phpstan-import-type ToolSearchToolType from ToolSearchTool * @phpstan-import-type WebSearchToolType from WebSearchTool * - * @phpstan-type ResponseToolObjectTypes array - * @phpstan-type ResponseToolObjectReturnType array + * @phpstan-type ResponseToolObjectTypes array + * @phpstan-type ResponseToolObjectReturnType array */ final class ToolObjects { @@ -41,7 +43,7 @@ final class ToolObjects public static function parse(array $toolItems): array { return array_map( - fn (array $tool): CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|ProgrammaticToolCallingTool|RemoteMcpTool|ToolSearchTool|WebSearchTool => match ($tool['type']) { + fn (array $tool): ApplyPatchTool|CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|ProgrammaticToolCallingTool|RemoteMcpTool|ToolSearchTool|WebSearchTool => match ($tool['type']) { 'file_search' => FileSearchTool::from($tool), 'web_search', 'web_search_preview', 'web_search_preview_2025_03_11' => WebSearchTool::from($tool), 'function' => FunctionTool::from($tool), @@ -53,6 +55,7 @@ public static function parse(array $toolItems): array 'namespace' => NamespaceTool::from($tool), 'custom' => CustomTool::from($tool), 'programmatic_tool_calling' => ProgrammaticToolCallingTool::from($tool), + 'apply_patch' => ApplyPatchTool::from($tool), }, $toolItems, ); diff --git a/src/Responses/Conversations/ConversationItem.php b/src/Responses/Conversations/ConversationItem.php index 140b7c4f1..d1e9d4437 100644 --- a/src/Responses/Conversations/ConversationItem.php +++ b/src/Responses/Conversations/ConversationItem.php @@ -8,11 +8,13 @@ use OpenAI\Contracts\ResponseContract; use OpenAI\Responses\Concerns\ArrayAccessible; use OpenAI\Responses\Conversations\Objects\Message; +use OpenAI\Responses\Responses\Input\ApplyPatchToolCallOutput; use OpenAI\Responses\Responses\Input\ComputerToolCallOutput; use OpenAI\Responses\Responses\Input\CustomToolCallOutput; use OpenAI\Responses\Responses\Input\FunctionToolCallOutput; use OpenAI\Responses\Responses\Input\LocalShellCallOutput; use OpenAI\Responses\Responses\Input\McpApprovalResponse; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; use OpenAI\Responses\Responses\Output\OutputCustomToolCall; @@ -46,7 +48,7 @@ final class ConversationItem implements ResponseContract use Fakeable; private function __construct( - public readonly Message|OutputFileSearchToolCall|OutputFunctionToolCall|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputWebSearchToolCall|OutputComputerToolCall|ComputerToolCallOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput $item + public readonly ApplyPatchToolCallOutput|Message|OutputApplyPatchToolCall|OutputFileSearchToolCall|OutputFunctionToolCall|FunctionToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputWebSearchToolCall|OutputComputerToolCall|ComputerToolCallOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput $item ) {} /** diff --git a/src/Responses/Responses/CreateResponse.php b/src/Responses/Responses/CreateResponse.php index e3ac63acf..cfd2ac34b 100644 --- a/src/Responses/Responses/CreateResponse.php +++ b/src/Responses/Responses/CreateResponse.php @@ -13,6 +13,7 @@ use OpenAI\Responses\Concerns\ArrayAccessible; use OpenAI\Responses\Concerns\HasMetaInformation; use OpenAI\Responses\Meta\MetaInformation; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; @@ -31,6 +32,7 @@ use OpenAI\Responses\Responses\Output\OutputToolSearchCall; use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; +use OpenAI\Responses\Responses\Tool\ApplyPatchTool; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; use OpenAI\Responses\Responses\Tool\CustomTool; @@ -199,7 +201,7 @@ public function toArray(): array 'metadata' => $this->metadata ?? [], 'model' => $this->model, 'output' => array_map( - fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction $output): array => $output->toArray(), + fn (OutputApplyPatchToolCall|OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction $output): array => $output->toArray(), $this->output ), 'parallel_tool_calls' => $this->parallelToolCalls, @@ -216,7 +218,7 @@ public function toArray(): array ? $this->toolChoice : $this->toolChoice->toArray(), 'tools' => array_map( - fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool|ProgrammaticToolCallingTool $tool): array => $tool->toArray(), + fn (ApplyPatchTool|ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool|ProgrammaticToolCallingTool $tool): array => $tool->toArray(), $this->tools ), 'top_logprobs' => $this->topLogProbs, diff --git a/src/Responses/Responses/Input/ApplyPatchToolCallOutput.php b/src/Responses/Responses/Input/ApplyPatchToolCallOutput.php new file mode 100644 index 000000000..92bc255c1 --- /dev/null +++ b/src/Responses/Responses/Input/ApplyPatchToolCallOutput.php @@ -0,0 +1,72 @@ + + */ +final class ApplyPatchToolCallOutput implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'completed'|'failed' $status + * @param 'apply_patch_call_output' $type + * @param ApplyPatchToolCallOutputCallerType|null $caller + */ + private function __construct( + public readonly string $id, + public readonly string $callId, + public readonly string $status, + public readonly string $type, + public readonly ?array $caller, + public readonly ?string $createdBy, + public readonly ?string $output, + ) {} + + /** + * @param ApplyPatchToolCallOutputType $attributes + */ + public static function from(array $attributes): self + { + return new self( + id: $attributes['id'], + callId: $attributes['call_id'], + status: $attributes['status'], + type: $attributes['type'], + caller: $attributes['caller'] ?? null, + createdBy: $attributes['created_by'] ?? null, + output: $attributes['output'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'call_id' => $this->callId, + 'status' => $this->status, + 'type' => $this->type, + 'caller' => $this->caller, + 'created_by' => $this->createdBy, + 'output' => $this->output, + ]; + } +} diff --git a/src/Responses/Responses/ListInputItems.php b/src/Responses/Responses/ListInputItems.php index ed7682d4a..18e006f46 100644 --- a/src/Responses/Responses/ListInputItems.php +++ b/src/Responses/Responses/ListInputItems.php @@ -10,12 +10,14 @@ use OpenAI\Responses\Concerns\ArrayAccessible; use OpenAI\Responses\Concerns\HasMetaInformation; use OpenAI\Responses\Meta\MetaInformation; +use OpenAI\Responses\Responses\Input\ApplyPatchToolCallOutput; use OpenAI\Responses\Responses\Input\ComputerToolCallOutput; use OpenAI\Responses\Responses\Input\CustomToolCallOutput; use OpenAI\Responses\Responses\Input\FunctionToolCallOutput; use OpenAI\Responses\Responses\Input\InputMessage; use OpenAI\Responses\Responses\Input\LocalShellCallOutput; use OpenAI\Responses\Responses\Input\McpApprovalResponse; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; use OpenAI\Responses\Responses\Output\OutputCustomToolCall; @@ -49,7 +51,7 @@ final class ListInputItems implements ResponseContract, ResponseHasMetaInformati use HasMetaInformation; /** - * @param array $data + * @param array $data * @param 'list' $object */ private function __construct( @@ -86,7 +88,7 @@ public function toArray(): array return [ 'object' => $this->object, 'data' => array_map( - fn (InputMessage|OutputMessage|OutputFileSearchToolCall|OutputFunctionToolCall|FunctionToolCallOutput|OutputWebSearchToolCall|OutputComputerToolCall|ComputerToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput $item): array => $item->toArray(), + fn (ApplyPatchToolCallOutput|InputMessage|OutputApplyPatchToolCall|OutputMessage|OutputFileSearchToolCall|OutputFunctionToolCall|FunctionToolCallOutput|OutputWebSearchToolCall|OutputComputerToolCall|ComputerToolCallOutput|LocalShellCallOutput|McpApprovalResponse|CustomToolCallOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputProgram|OutputProgramOutput $item): array => $item->toArray(), $this->data, ), 'first_id' => $this->firstId, diff --git a/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFile.php b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFile.php new file mode 100644 index 000000000..c2f137e8a --- /dev/null +++ b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFile.php @@ -0,0 +1,57 @@ + + */ +final class OutputApplyPatchOperationCreateFile implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'create_file' $type + */ + private function __construct( + public readonly string $type, + public readonly string $diff, + public readonly string $path, + ) {} + + /** + * @param OutputApplyPatchOperationCreateFileType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + diff: $attributes['diff'], + path: $attributes['path'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'diff' => $this->diff, + 'path' => $this->path, + ]; + } +} diff --git a/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFile.php b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFile.php new file mode 100644 index 000000000..edb32642d --- /dev/null +++ b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFile.php @@ -0,0 +1,54 @@ + + */ +final class OutputApplyPatchOperationDeleteFile implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'delete_file' $type + */ + private function __construct( + public readonly string $type, + public readonly string $path, + ) {} + + /** + * @param OutputApplyPatchOperationDeleteFileType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + path: $attributes['path'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'path' => $this->path, + ]; + } +} diff --git a/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFile.php b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFile.php new file mode 100644 index 000000000..4c6898fda --- /dev/null +++ b/src/Responses/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFile.php @@ -0,0 +1,57 @@ + + */ +final class OutputApplyPatchOperationUpdateFile implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'update_file' $type + */ + private function __construct( + public readonly string $type, + public readonly string $diff, + public readonly string $path, + ) {} + + /** + * @param OutputApplyPatchOperationUpdateFileType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + diff: $attributes['diff'], + path: $attributes['path'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'diff' => $this->diff, + 'path' => $this->path, + ]; + } +} diff --git a/src/Responses/Responses/Output/OutputApplyPatchToolCall.php b/src/Responses/Responses/Output/OutputApplyPatchToolCall.php new file mode 100644 index 000000000..a775ddfca --- /dev/null +++ b/src/Responses/Responses/Output/OutputApplyPatchToolCall.php @@ -0,0 +1,85 @@ + + */ +final class OutputApplyPatchToolCall implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'in_progress'|'completed' $status + * @param 'apply_patch_call' $type + * @param OutputApplyPatchToolCallCallerType|null $caller + */ + private function __construct( + public readonly string $id, + public readonly string $callId, + public readonly OutputApplyPatchOperationCreateFile|OutputApplyPatchOperationDeleteFile|OutputApplyPatchOperationUpdateFile $operation, + public readonly string $status, + public readonly string $type, + public readonly ?array $caller, + public readonly ?string $createdBy, + ) {} + + /** + * @param OutputApplyPatchToolCallType $attributes + */ + public static function from(array $attributes): self + { + $operation = match ($attributes['operation']['type']) { + 'create_file' => OutputApplyPatchOperationCreateFile::from($attributes['operation']), + 'delete_file' => OutputApplyPatchOperationDeleteFile::from($attributes['operation']), + 'update_file' => OutputApplyPatchOperationUpdateFile::from($attributes['operation']), + }; + + return new self( + id: $attributes['id'], + callId: $attributes['call_id'], + operation: $operation, + status: $attributes['status'], + type: $attributes['type'], + caller: $attributes['caller'] ?? null, + createdBy: $attributes['created_by'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'call_id' => $this->callId, + 'operation' => $this->operation->toArray(), + 'status' => $this->status, + 'type' => $this->type, + 'caller' => $this->caller, + 'created_by' => $this->createdBy, + ]; + } +} diff --git a/src/Responses/Responses/Output/OutputToolSearchOutput.php b/src/Responses/Responses/Output/OutputToolSearchOutput.php index 0e4108351..62efee962 100644 --- a/src/Responses/Responses/Output/OutputToolSearchOutput.php +++ b/src/Responses/Responses/Output/OutputToolSearchOutput.php @@ -7,6 +7,7 @@ use OpenAI\Actions\Responses\ToolObjects; use OpenAI\Contracts\ResponseContract; use OpenAI\Responses\Concerns\ArrayAccessible; +use OpenAI\Responses\Responses\Tool\ApplyPatchTool; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; use OpenAI\Responses\Responses\Tool\CustomTool; @@ -80,7 +81,7 @@ public function toArray(): array 'execution' => $this->execution, 'status' => $this->status, 'tools' => array_map( - fn (CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|ProgrammaticToolCallingTool|RemoteMcpTool|ToolSearchTool|WebSearchTool $tool): array => $tool->toArray(), + fn (ApplyPatchTool|CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|ProgrammaticToolCallingTool|RemoteMcpTool|ToolSearchTool|WebSearchTool $tool): array => $tool->toArray(), $this->tools, ), 'type' => $this->type, diff --git a/src/Responses/Responses/RetrieveResponse.php b/src/Responses/Responses/RetrieveResponse.php index 12df04cef..c94b55cbc 100644 --- a/src/Responses/Responses/RetrieveResponse.php +++ b/src/Responses/Responses/RetrieveResponse.php @@ -13,6 +13,7 @@ use OpenAI\Responses\Concerns\ArrayAccessible; use OpenAI\Responses\Concerns\HasMetaInformation; use OpenAI\Responses\Meta\MetaInformation; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; @@ -31,6 +32,7 @@ use OpenAI\Responses\Responses\Output\OutputToolSearchCall; use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; +use OpenAI\Responses\Responses\Tool\ApplyPatchTool; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; use OpenAI\Responses\Responses\Tool\CustomTool; @@ -199,7 +201,7 @@ public function toArray(): array 'metadata' => $this->metadata ?? [], 'model' => $this->model, 'output' => array_map( - fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction $output): array => $output->toArray(), + fn (OutputApplyPatchToolCall|OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput|OutputCompaction $output): array => $output->toArray(), $this->output ), 'output_text' => $this->outputText, @@ -217,7 +219,7 @@ public function toArray(): array ? $this->toolChoice : $this->toolChoice->toArray(), 'tools' => array_map( - fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool|ProgrammaticToolCallingTool $tool): array => $tool->toArray(), + fn (ApplyPatchTool|ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool|ProgrammaticToolCallingTool $tool): array => $tool->toArray(), $this->tools ), 'top_logprobs' => $this->topLogProbs, diff --git a/src/Responses/Responses/Streaming/OutputItem.php b/src/Responses/Responses/Streaming/OutputItem.php index 09f6432b9..9fd1a54b3 100644 --- a/src/Responses/Responses/Streaming/OutputItem.php +++ b/src/Responses/Responses/Streaming/OutputItem.php @@ -9,6 +9,7 @@ use OpenAI\Responses\Concerns\ArrayAccessible; use OpenAI\Responses\Concerns\HasMetaInformation; use OpenAI\Responses\Meta\MetaInformation; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCodeInterpreterToolCall; use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputComputerToolCall; @@ -28,6 +29,7 @@ use OpenAI\Testing\Responses\Concerns\Fakeable; /** + * @phpstan-import-type OutputApplyPatchToolCallType from OutputApplyPatchToolCall * @phpstan-import-type OutputComputerToolCallType from OutputComputerToolCall * @phpstan-import-type OutputFileSearchToolCallType from OutputFileSearchToolCall * @phpstan-import-type OutputFunctionToolCallType from OutputFunctionToolCall @@ -45,7 +47,7 @@ * @phpstan-import-type OutputToolSearchCallType from OutputToolSearchCall * @phpstan-import-type OutputToolSearchOutputType from OutputToolSearchOutput * - * @phpstan-type OutputItemType array{type: string, output_index: int, sequence_number: int, item: OutputCodeInterpreterToolCallType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputProgramType|OutputProgramOutputType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCompactionType|OutputToolSearchCallType|OutputToolSearchOutputType} + * @phpstan-type OutputItemType array{type: string, output_index: int, sequence_number: int, item: OutputApplyPatchToolCallType|OutputCodeInterpreterToolCallType|OutputComputerToolCallType|OutputFileSearchToolCallType|OutputFunctionToolCallType|OutputMessageType|OutputProgramType|OutputProgramOutputType|OutputReasoningType|OutputWebSearchToolCallType|OutputMcpListToolsType|OutputMcpApprovalRequestType|OutputMcpCallType|OutputImageGenerationToolCallType|OutputCompactionType|OutputToolSearchCallType|OutputToolSearchOutputType} * * @implements ResponseContract */ @@ -63,7 +65,7 @@ private function __construct( public readonly string $type, public readonly int $outputIndex, public readonly int $sequenceNumber, - public readonly OutputMessage|OutputCodeInterpreterToolCall|OutputFileSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputWebSearchToolCall|OutputComputerToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCompaction|OutputToolSearchCall|OutputToolSearchOutput $item, + public readonly OutputApplyPatchToolCall|OutputMessage|OutputCodeInterpreterToolCall|OutputFileSearchToolCall|OutputFunctionToolCall|OutputProgram|OutputProgramOutput|OutputWebSearchToolCall|OutputComputerToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCompaction|OutputToolSearchCall|OutputToolSearchOutput $item, private readonly MetaInformation $meta, ) {} @@ -72,7 +74,7 @@ private function __construct( */ public static function from(array $attributes, MetaInformation $meta): self { - /** @var 'message'|'file_search_call'|'function_call'|'program'|'program_output'|'web_search_call'|'computer_call'|'reasoning'|'image_generation_call'|'mcp_list_tools'|'mcp_approval_request'|'mcp_call'|'code_interpreter_call'|'compaction'|'tool_search_call'|'tool_search_output' $itemType */ + /** @var 'message'|'file_search_call'|'function_call'|'program'|'program_output'|'web_search_call'|'computer_call'|'reasoning'|'image_generation_call'|'mcp_list_tools'|'mcp_approval_request'|'mcp_call'|'code_interpreter_call'|'compaction'|'tool_search_call'|'tool_search_output'|'apply_patch_call' $itemType */ $itemType = $attributes['item']['type']; $item = match ($itemType) { @@ -92,6 +94,7 @@ public static function from(array $attributes, MetaInformation $meta): self 'compaction' => OutputCompaction::from($attributes['item']), 'tool_search_call' => OutputToolSearchCall::from($attributes['item']), 'tool_search_output' => OutputToolSearchOutput::from($attributes['item']), + 'apply_patch_call' => OutputApplyPatchToolCall::from($attributes['item']), }; return new self( diff --git a/src/Responses/Responses/Tool/ApplyPatchTool.php b/src/Responses/Responses/Tool/ApplyPatchTool.php new file mode 100644 index 000000000..e994a0799 --- /dev/null +++ b/src/Responses/Responses/Tool/ApplyPatchTool.php @@ -0,0 +1,55 @@ +|null} + * + * @implements ResponseContract + */ +final class ApplyPatchTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'apply_patch' $type + * @param array|null $allowedCallers + */ + private function __construct( + public readonly string $type, + public readonly ?array $allowedCallers, + ) {} + + /** + * @param ApplyPatchToolType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + allowedCallers: $attributes['allowed_callers'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'allowed_callers' => $this->allowedCallers, + ]; + } +} diff --git a/src/Responses/Responses/ToolChoice/HostedToolChoice.php b/src/Responses/Responses/ToolChoice/HostedToolChoice.php index 35e535f57..aeb667c69 100644 --- a/src/Responses/Responses/ToolChoice/HostedToolChoice.php +++ b/src/Responses/Responses/ToolChoice/HostedToolChoice.php @@ -9,7 +9,7 @@ use OpenAI\Testing\Responses\Concerns\Fakeable; /** - * @phpstan-type HostedToolChoiceType array{type: 'file_search'|'web_search'|'web_search_preview'|'computer_use_preview'|'programmatic_tool_calling'} + * @phpstan-type HostedToolChoiceType array{type: 'apply_patch'|'file_search'|'web_search'|'web_search_preview'|'computer_use_preview'|'programmatic_tool_calling'} * * @implements ResponseContract */ @@ -23,7 +23,7 @@ final class HostedToolChoice implements ResponseContract use Fakeable; /** - * @param 'file_search'|'web_search'|'web_search_preview'|'computer_use_preview'|'programmatic_tool_calling' $type + * @param 'apply_patch'|'file_search'|'web_search'|'web_search_preview'|'computer_use_preview'|'programmatic_tool_calling' $type */ private function __construct( public readonly string $type, diff --git a/tests/Fixtures/Responses.php b/tests/Fixtures/Responses.php index 8488aecd0..3d5d22504 100644 --- a/tests/Fixtures/Responses.php +++ b/tests/Fixtures/Responses.php @@ -25,6 +25,7 @@ function createResponseResource(): array outputComputerToolCall(), outputReasoning(), outputCodeInterpreterToolCall(), + outputApplyPatchToolCall(), ], 'parallel_tool_calls' => true, 'previous_response_id' => null, @@ -51,6 +52,7 @@ function createResponseResource(): array toolImageGeneration(), toolRemoteMcp(), toolConnectorMcp(), + toolApplyPatch(), ], 'top_logprobs' => null, 'top_p' => 1.0, @@ -233,6 +235,8 @@ function listInputItemsResource(): array outputProgramOutput(), functionToolCallOutputItem(), customToolCallOutputItem(), + outputApplyPatchToolCall(), + applyPatchToolCallOutputItem(), ], 'first_id' => 'msg_67ccf190ca3881909d433c50b1f6357e087bb177ab789d5c', 'last_id' => 'msg_67ccf190ca3881909d433c50b1f6357e087bb177ab789d5c', @@ -319,6 +323,25 @@ function functionToolCallOutputItem(): array ]; } +/** + * @return array + */ +function applyPatchToolCallOutputItem(): array +{ + return [ + 'id' => 'apco_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'call_id' => 'call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'status' => 'failed', + 'type' => 'apply_patch_call_output', + 'caller' => [ + 'caller_id' => 'prog_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'type' => 'program', + ], + 'created_by' => 'prog_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'output' => 'Could not apply patch: invalid context.', + ]; +} + /** * @return array */ @@ -802,6 +825,28 @@ function outputProgramOutput(): array ]; } +/** + * @return array + */ +function outputApplyPatchToolCall(): array +{ + return [ + 'id' => 'apc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'call_id' => 'call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'operation' => [ + 'type' => 'update_file', + 'diff' => "@@\n-old line\n+new line", + 'path' => 'src/Example.php', + ], + 'status' => 'completed', + 'type' => 'apply_patch_call', + 'caller' => [ + 'type' => 'direct', + ], + 'created_by' => 'user_123', + ]; +} + /** * @return array */ @@ -1053,6 +1098,20 @@ function toolProgrammaticToolCalling(): array ]; } +/** + * @return array + */ +function toolApplyPatch(): array +{ + return [ + 'type' => 'apply_patch', + 'allowed_callers' => [ + 'direct', + 'programmatic', + ], + ]; +} + /** * @return array */ @@ -1191,3 +1250,8 @@ function responseOutputItemToolSearchOutputDoneEvent() { return fopen(__DIR__.'/Streams/ResponseOutputItemToolSearchOutputDone.txt', 'r'); } + +function responseOutputItemApplyPatchCallDoneEvent() +{ + return fopen(__DIR__.'/Streams/ResponseOutputItemApplyPatchCallDone.txt', 'r'); +} diff --git a/tests/Fixtures/Streams/ResponseOutputItemApplyPatchCallDone.txt b/tests/Fixtures/Streams/ResponseOutputItemApplyPatchCallDone.txt new file mode 100644 index 000000000..365131402 --- /dev/null +++ b/tests/Fixtures/Streams/ResponseOutputItemApplyPatchCallDone.txt @@ -0,0 +1 @@ +data: {"type":"response.output_item.done","output_index":2,"sequence_number":14,"item":{"id":"apc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c","call_id":"call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c","operation":{"type":"create_file","diff":"@@\n+New file\n","path":"tasks.md"},"status":"completed","type":"apply_patch_call","caller":{"type":"direct"},"created_by":"user_123"}} diff --git a/tests/Resources/Responses.php b/tests/Resources/Responses.php index d340536e0..e685253c9 100644 --- a/tests/Resources/Responses.php +++ b/tests/Resources/Responses.php @@ -8,6 +8,7 @@ use OpenAI\Responses\Responses\CreateStreamedResponse; use OpenAI\Responses\Responses\DeleteResponse; use OpenAI\Responses\Responses\ListInputItems; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\ReferencePromptObject; use OpenAI\Responses\Responses\RetrieveResponse; use OpenAI\Responses\Responses\Streaming\Response as StreamedResponse; @@ -39,7 +40,7 @@ ->maxOutputTokens->toBeNull() ->model->toBe('gpt-4o-2024-08-06') ->output->toBeArray() - ->output->toHaveCount(6); + ->output->toHaveCount(7); expect($output[0]) ->type->toBe('message') @@ -61,6 +62,10 @@ expect($output[4]) ->type->toBe('reasoning'); + expect($output[6]) + ->toBeInstanceOf(OutputApplyPatchToolCall::class) + ->type->toBe('apply_patch_call'); + expect($result) ->parallelToolCalls->toBeTrue() ->previousResponseId->toBeNull() diff --git a/tests/Responses/Conversations/ConversationItem.php b/tests/Responses/Conversations/ConversationItem.php index 428b7d5ea..286c8657d 100644 --- a/tests/Responses/Conversations/ConversationItem.php +++ b/tests/Responses/Conversations/ConversationItem.php @@ -2,6 +2,8 @@ use OpenAI\Responses\Conversations\ConversationItem; use OpenAI\Responses\Conversations\Objects\Message; +use OpenAI\Responses\Responses\Input\ApplyPatchToolCallOutput; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputProgram; use OpenAI\Responses\Responses\Output\OutputProgramOutput; @@ -45,3 +47,19 @@ ->item->callId->toBe('call_prog_123') ->toArray()->toBe(outputProgramOutput()); }); + +test('from with apply patch call', function () { + $response = ConversationItem::from(outputApplyPatchToolCall()); + + expect($response->item) + ->toBeInstanceOf(OutputApplyPatchToolCall::class) + ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c'); +}); + +test('from with apply patch call output', function () { + $response = ConversationItem::from(applyPatchToolCallOutputItem()); + + expect($response->item) + ->toBeInstanceOf(ApplyPatchToolCallOutput::class) + ->status->toBe('failed'); +}); diff --git a/tests/Responses/Responses/CreateResponse.php b/tests/Responses/Responses/CreateResponse.php index d42830f0b..71d78c5b4 100644 --- a/tests/Responses/Responses/CreateResponse.php +++ b/tests/Responses/Responses/CreateResponse.php @@ -222,3 +222,14 @@ expect($response->toArray()['tool_choice']) ->toBe($payload['tool_choice']); }); + +test('from with apply patch tool choice', function () { + $payload = createResponseResource(); + $payload['tool_choice'] = ['type' => 'apply_patch']; + + $response = CreateResponse::from($payload, meta()); + + expect($response->toolChoice) + ->toBeInstanceOf(HostedToolChoice::class) + ->type->toBe('apply_patch'); +}); diff --git a/tests/Responses/Responses/CreateStreamedResponse.php b/tests/Responses/Responses/CreateStreamedResponse.php index 7dafaafd7..331377165 100644 --- a/tests/Responses/Responses/CreateStreamedResponse.php +++ b/tests/Responses/Responses/CreateStreamedResponse.php @@ -2,6 +2,8 @@ use OpenAI\Responses\Responses\CreateResponse; use OpenAI\Responses\Responses\CreateStreamedResponse; +use OpenAI\Responses\Responses\Output\ApplyPatchOperation\OutputApplyPatchOperationCreateFile; +use OpenAI\Responses\Responses\Output\OutputApplyPatchToolCall; use OpenAI\Responses\Responses\Output\OutputCompaction; use OpenAI\Responses\Responses\Output\OutputFunctionToolCall; use OpenAI\Responses\Responses\Output\OutputProgram; @@ -168,3 +170,18 @@ ->response->item->tools->{0}->toBeInstanceOf(WebSearchTool::class) ->response->item->type->toBe('tool_search_output'); }); + +test('output item done event with apply patch call item', function () { + $response = CreateStreamedResponse::fake(responseOutputItemApplyPatchCallDoneEvent()); + + expect($response->getIterator()->current()) + ->toBeInstanceOf(CreateStreamedResponse::class) + ->event->toBe('response.output_item.done') + ->response->toBeInstanceOf(OutputItem::class) + ->response->outputIndex->toBe(2) + ->response->sequenceNumber->toBe(14) + ->response->item->toBeInstanceOf(OutputApplyPatchToolCall::class) + ->response->item->operation->toBeInstanceOf(OutputApplyPatchOperationCreateFile::class) + ->response->item->operation->path->toBe('tasks.md') + ->response->item->type->toBe('apply_patch_call'); +}); diff --git a/tests/Responses/Responses/Input/ApplyPatchToolCallOutput.php b/tests/Responses/Responses/Input/ApplyPatchToolCallOutput.php new file mode 100644 index 000000000..75f7f6f1d --- /dev/null +++ b/tests/Responses/Responses/Input/ApplyPatchToolCallOutput.php @@ -0,0 +1,48 @@ +toBeInstanceOf(ApplyPatchToolCallOutput::class) + ->id->toBe('apco_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->status->toBe('failed') + ->type->toBe('apply_patch_call_output') + ->caller->toBe([ + 'caller_id' => 'prog_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'type' => 'program', + ]) + ->createdBy->toBe('prog_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->output->toBe('Could not apply patch: invalid context.'); +}); + +test('from without optional output metadata', function () { + $response = ApplyPatchToolCallOutput::from([ + 'id' => 'apco_123', + 'call_id' => 'call_123', + 'status' => 'completed', + 'type' => 'apply_patch_call_output', + ]); + + expect($response) + ->caller->toBeNull() + ->createdBy->toBeNull() + ->output->toBeNull(); +}); + +test('as array accessible', function () { + $response = ApplyPatchToolCallOutput::from(applyPatchToolCallOutputItem()); + + expect($response['status'])->toBe('failed'); +}); + +test('to array', function () { + $response = ApplyPatchToolCallOutput::from(applyPatchToolCallOutputItem()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(applyPatchToolCallOutputItem()); +}); diff --git a/tests/Responses/Responses/ListInputItems.php b/tests/Responses/Responses/ListInputItems.php index 4cca2154a..50df48d89 100644 --- a/tests/Responses/Responses/ListInputItems.php +++ b/tests/Responses/Responses/ListInputItems.php @@ -1,9 +1,11 @@ toBeInstanceOf(ListInputItems::class) ->object->toBe('list') ->data->toBeArray() - ->data->toHaveCount(17) + ->data->toHaveCount(19) ->firstId->toBe('msg_67ccf190ca3881909d433c50b1f6357e087bb177ab789d5c') ->lastId->toBe('msg_67ccf190ca3881909d433c50b1f6357e087bb177ab789d5c') ->hasMore->toBeFalse() @@ -26,7 +28,9 @@ ->{15}->toBeInstanceOf(FunctionToolCallOutput::class) ->{15}->caller->callerId->toBe('call_prog_123') ->{16}->toBeInstanceOf(CustomToolCallOutput::class) - ->{16}->caller->callerId->toBe('call_prog_123'); + ->{16}->caller->callerId->toBe('call_prog_123') + ->{17}->toBeInstanceOf(OutputApplyPatchToolCall::class) + ->{18}->toBeInstanceOf(ApplyPatchToolCallOutput::class); }); test('as array accessible', function () { diff --git a/tests/Responses/Responses/Output/OutputApplyPatchToolCall.php b/tests/Responses/Responses/Output/OutputApplyPatchToolCall.php new file mode 100644 index 000000000..19a95bfbe --- /dev/null +++ b/tests/Responses/Responses/Output/OutputApplyPatchToolCall.php @@ -0,0 +1,72 @@ +toBeInstanceOf(OutputApplyPatchToolCall::class) + ->id->toBe('apc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->operation->toBeInstanceOf(OutputApplyPatchOperationUpdateFile::class) + ->operation->diff->toBe("@@\n-old line\n+new line") + ->operation->path->toBe('src/Example.php') + ->status->toBe('completed') + ->type->toBe('apply_patch_call') + ->caller->toBe(['type' => 'direct']) + ->createdBy->toBe('user_123'); +}); + +test('parses every operation type', function (array $operation, string $expectedClass) { + $attributes = outputApplyPatchToolCall(); + $attributes['operation'] = $operation; + + $response = OutputApplyPatchToolCall::from($attributes); + + expect($response->operation) + ->toBeInstanceOf($expectedClass) + ->and($response->toArray()) + ->toBe($attributes); +})->with([ + 'create file' => [ + [ + 'type' => 'create_file', + 'diff' => "@@\n+new file", + 'path' => 'src/New.php', + ], + OutputApplyPatchOperationCreateFile::class, + ], + 'delete file' => [ + [ + 'type' => 'delete_file', + 'path' => 'src/Old.php', + ], + OutputApplyPatchOperationDeleteFile::class, + ], + 'update file' => [ + [ + 'type' => 'update_file', + 'diff' => "@@\n-old\n+new", + 'path' => 'src/Existing.php', + ], + OutputApplyPatchOperationUpdateFile::class, + ], +]); + +test('as array accessible', function () { + $response = OutputApplyPatchToolCall::from(outputApplyPatchToolCall()); + + expect($response['type'])->toBe('apply_patch_call'); +}); + +test('to array', function () { + $response = OutputApplyPatchToolCall::from(outputApplyPatchToolCall()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(outputApplyPatchToolCall()); +}); diff --git a/tests/Responses/Responses/Tool/ApplyPatchTool.php b/tests/Responses/Responses/Tool/ApplyPatchTool.php new file mode 100644 index 000000000..a46167558 --- /dev/null +++ b/tests/Responses/Responses/Tool/ApplyPatchTool.php @@ -0,0 +1,29 @@ +toBeInstanceOf(ApplyPatchTool::class) + ->type->toBe('apply_patch') + ->allowedCallers->toBe([ + 'direct', + 'programmatic', + ]); +}); + +test('as array accessible', function () { + $response = ApplyPatchTool::from(toolApplyPatch()); + + expect($response['type'])->toBe('apply_patch'); +}); + +test('to array', function () { + $response = ApplyPatchTool::from(toolApplyPatch()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(toolApplyPatch()); +}); From c45860f658be6429af01abb5f78382a187aaf4e5 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 28 Jul 2026 11:04:03 +0400 Subject: [PATCH 3/4] Add apply_patch fake fixtures --- .../Input/ApplyPatchToolCallOutputFixture.php | 18 +++++++++++++++ ...utApplyPatchOperationCreateFileFixture.php | 12 ++++++++++ ...utApplyPatchOperationDeleteFileFixture.php | 11 ++++++++++ ...utApplyPatchOperationUpdateFileFixture.php | 12 ++++++++++ .../OutputApplyPatchToolCallFixture.php | 22 +++++++++++++++++++ .../Responses/Tool/ApplyPatchToolFixture.php | 14 ++++++++++++ 6 files changed, 89 insertions(+) create mode 100644 src/Testing/Responses/Fixtures/Responses/Input/ApplyPatchToolCallOutputFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFileFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFileFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFileFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Output/OutputApplyPatchToolCallFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Tool/ApplyPatchToolFixture.php diff --git a/src/Testing/Responses/Fixtures/Responses/Input/ApplyPatchToolCallOutputFixture.php b/src/Testing/Responses/Fixtures/Responses/Input/ApplyPatchToolCallOutputFixture.php new file mode 100644 index 000000000..58863a029 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Input/ApplyPatchToolCallOutputFixture.php @@ -0,0 +1,18 @@ + 'apco_123', + 'call_id' => 'call_123', + 'status' => 'completed', + 'type' => 'apply_patch_call_output', + 'caller' => [ + 'type' => 'direct', + ], + 'created_by' => 'user_123', + 'output' => 'Patch applied successfully.', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFileFixture.php b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFileFixture.php new file mode 100644 index 000000000..ee026d641 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationCreateFileFixture.php @@ -0,0 +1,12 @@ + 'create_file', + 'diff' => "@@\n+new file", + 'path' => 'src/New.php', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFileFixture.php b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFileFixture.php new file mode 100644 index 000000000..05d3e9601 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationDeleteFileFixture.php @@ -0,0 +1,11 @@ + 'delete_file', + 'path' => 'src/Old.php', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFileFixture.php b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFileFixture.php new file mode 100644 index 000000000..b94c62bf5 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Output/ApplyPatchOperation/OutputApplyPatchOperationUpdateFileFixture.php @@ -0,0 +1,12 @@ + 'update_file', + 'diff' => "@@\n-old line\n+new line", + 'path' => 'src/Example.php', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Output/OutputApplyPatchToolCallFixture.php b/src/Testing/Responses/Fixtures/Responses/Output/OutputApplyPatchToolCallFixture.php new file mode 100644 index 000000000..6835f8451 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Output/OutputApplyPatchToolCallFixture.php @@ -0,0 +1,22 @@ + 'apc_123', + 'call_id' => 'call_123', + 'operation' => [ + 'type' => 'update_file', + 'diff' => "@@\n-old line\n+new line", + 'path' => 'src/Example.php', + ], + 'status' => 'completed', + 'type' => 'apply_patch_call', + 'caller' => [ + 'type' => 'direct', + ], + 'created_by' => 'user_123', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Tool/ApplyPatchToolFixture.php b/src/Testing/Responses/Fixtures/Responses/Tool/ApplyPatchToolFixture.php new file mode 100644 index 000000000..82414e610 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Tool/ApplyPatchToolFixture.php @@ -0,0 +1,14 @@ + 'apply_patch', + 'allowed_callers' => [ + 'direct', + 'programmatic', + ], + ]; +} From 69c869a98579d128cf8eb312cefacdf388ab8a38 Mon Sep 17 00:00:00 2001 From: Alexander Morozov Date: Tue, 28 Jul 2026 12:47:21 +0400 Subject: [PATCH 4/4] Add apply patch stream events --- .../Responses/CreateStreamedResponse.php | 6 +- .../ApplyPatchCallOperationDiffDelta.php | 74 +++++++++++++++++++ .../ApplyPatchCallOperationDiffDone.php | 66 +++++++++++++++++ ...pplyPatchCallOperationDiffDeltaFixture.php | 15 ++++ ...ApplyPatchCallOperationDiffDoneFixture.php | 14 ++++ tests/Fixtures/Responses.php | 10 +++ ...sponseApplyPatchCallOperationDiffDelta.txt | 1 + ...esponseApplyPatchCallOperationDiffDone.txt | 1 + .../Responses/CreateStreamedResponse.php | 29 ++++++++ 9 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDelta.php create mode 100644 src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDone.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDeltaFixture.php create mode 100644 src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDoneFixture.php create mode 100644 tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDelta.txt create mode 100644 tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDone.txt diff --git a/src/Responses/Responses/CreateStreamedResponse.php b/src/Responses/Responses/CreateStreamedResponse.php index 834b5d41b..7dada18e0 100644 --- a/src/Responses/Responses/CreateStreamedResponse.php +++ b/src/Responses/Responses/CreateStreamedResponse.php @@ -7,6 +7,8 @@ use OpenAI\Contracts\ResponseContract; use OpenAI\Exceptions\UnknownEventException; use OpenAI\Responses\Concerns\ArrayAccessible; +use OpenAI\Responses\Responses\Streaming\ApplyPatchCallOperationDiffDelta; +use OpenAI\Responses\Responses\Streaming\ApplyPatchCallOperationDiffDone; use OpenAI\Responses\Responses\Streaming\CodeInterpreterCall; use OpenAI\Responses\Responses\Streaming\CodeInterpreterCodeDelta; use OpenAI\Responses\Responses\Streaming\CodeInterpreterCodeDone; @@ -54,7 +56,7 @@ final class CreateStreamedResponse implements ResponseContract private function __construct( public readonly string $event, - public readonly Response|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|RateLimits|Error $response, + public readonly Response|OutputItem|ContentPart|OutputTextDelta|OutputTextAnnotationAdded|OutputTextDone|RefusalDelta|RefusalDone|FunctionCallArgumentsDelta|FunctionCallArgumentsDone|ApplyPatchCallOperationDiffDelta|ApplyPatchCallOperationDiffDone|FileSearchCall|WebSearchCall|CodeInterpreterCall|CodeInterpreterCodeDelta|CodeInterpreterCodeDone|ReasoningSummaryPart|ReasoningSummaryTextDelta|ReasoningSummaryTextDone|ReasoningTextDelta|ReasoningTextDone|McpListTools|McpListToolsInProgress|McpCall|McpCallArgumentsDelta|McpCallArgumentsDone|ImageGenerationPart|ImageGenerationPartialImage|RateLimits|Error $response, ) {} /** @@ -84,6 +86,8 @@ public static function from(array $attributes): self 'response.refusal.done' => RefusalDone::from($attributes, $meta), // @phpstan-ignore-line 'response.function_call_arguments.delta' => FunctionCallArgumentsDelta::from($attributes, $meta), // @phpstan-ignore-line 'response.function_call_arguments.done' => FunctionCallArgumentsDone::from($attributes, $meta), // @phpstan-ignore-line + 'response.apply_patch_call_operation_diff.delta' => ApplyPatchCallOperationDiffDelta::from($attributes, $meta), // @phpstan-ignore-line + 'response.apply_patch_call_operation_diff.done' => ApplyPatchCallOperationDiffDone::from($attributes, $meta), // @phpstan-ignore-line 'response.file_search_call.in_progress', 'response.file_search_call.searching', 'response.file_search_call.completed' => FileSearchCall::from($attributes, $meta), // @phpstan-ignore-line diff --git a/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDelta.php b/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDelta.php new file mode 100644 index 000000000..9c93a6d5e --- /dev/null +++ b/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDelta.php @@ -0,0 +1,74 @@ + + */ +final class ApplyPatchCallOperationDiffDelta implements ResponseContract, ResponseHasMetaInformationContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + use HasMetaInformation; + + private function __construct( + public readonly string $type, + public readonly string $delta, + public readonly string $itemId, + public readonly int $outputIndex, + public readonly int $sequenceNumber, + public readonly ?string $obfuscation, + private readonly MetaInformation $meta, + ) {} + + /** + * @param ApplyPatchCallOperationDiffDeltaType $attributes + */ + public static function from(array $attributes, MetaInformation $meta): self + { + return new self( + type: $attributes['type'], + delta: $attributes['delta'], + itemId: $attributes['item_id'], + outputIndex: $attributes['output_index'], + sequenceNumber: $attributes['sequence_number'], + obfuscation: $attributes['obfuscation'] ?? null, + meta: $meta, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + $attributes = [ + 'type' => $this->type, + 'delta' => $this->delta, + 'item_id' => $this->itemId, + 'output_index' => $this->outputIndex, + 'sequence_number' => $this->sequenceNumber, + ]; + + if ($this->obfuscation !== null) { + $attributes['obfuscation'] = $this->obfuscation; + } + + return $attributes; + } +} diff --git a/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDone.php b/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDone.php new file mode 100644 index 000000000..39e4c7ed7 --- /dev/null +++ b/src/Responses/Responses/Streaming/ApplyPatchCallOperationDiffDone.php @@ -0,0 +1,66 @@ + + */ +final class ApplyPatchCallOperationDiffDone implements ResponseContract, ResponseHasMetaInformationContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + use HasMetaInformation; + + private function __construct( + public readonly string $type, + public readonly string $diff, + public readonly string $itemId, + public readonly int $outputIndex, + public readonly int $sequenceNumber, + private readonly MetaInformation $meta, + ) {} + + /** + * @param ApplyPatchCallOperationDiffDoneType $attributes + */ + public static function from(array $attributes, MetaInformation $meta): self + { + return new self( + type: $attributes['type'], + diff: $attributes['diff'], + itemId: $attributes['item_id'], + outputIndex: $attributes['output_index'], + sequenceNumber: $attributes['sequence_number'], + meta: $meta, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'diff' => $this->diff, + 'item_id' => $this->itemId, + 'output_index' => $this->outputIndex, + 'sequence_number' => $this->sequenceNumber, + ]; + } +} diff --git a/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDeltaFixture.php b/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDeltaFixture.php new file mode 100644 index 000000000..31791d6fb --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDeltaFixture.php @@ -0,0 +1,15 @@ + 'response.apply_patch_call_operation_diff.delta', + 'delta' => "@@\n-old line\n+new line", + 'item_id' => 'apc_123', + 'output_index' => 0, + 'sequence_number' => 2, + 'obfuscation' => 'obfuscated_123', + ]; +} diff --git a/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDoneFixture.php b/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDoneFixture.php new file mode 100644 index 000000000..fe0871272 --- /dev/null +++ b/src/Testing/Responses/Fixtures/Responses/Streaming/ApplyPatchCallOperationDiffDoneFixture.php @@ -0,0 +1,14 @@ + 'response.apply_patch_call_operation_diff.done', + 'diff' => "@@\n-old line\n+new line", + 'item_id' => 'apc_123', + 'output_index' => 0, + 'sequence_number' => 3, + ]; +} diff --git a/tests/Fixtures/Responses.php b/tests/Fixtures/Responses.php index 3d5d22504..d94f48ad3 100644 --- a/tests/Fixtures/Responses.php +++ b/tests/Fixtures/Responses.php @@ -1231,6 +1231,16 @@ function responseReasoningTextDoneEvent() return fopen(__DIR__.'/Streams/ResponseReasoningTextDone.txt', 'r'); } +function responseApplyPatchCallOperationDiffDeltaEvent() +{ + return fopen(__DIR__.'/Streams/ResponseApplyPatchCallOperationDiffDelta.txt', 'r'); +} + +function responseApplyPatchCallOperationDiffDoneEvent() +{ + return fopen(__DIR__.'/Streams/ResponseApplyPatchCallOperationDiffDone.txt', 'r'); +} + function responseRateLimitsUpdatedEvent() { return fopen(__DIR__.'/Streams/ResponseRateLimitsUpdated.txt', 'r'); diff --git a/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDelta.txt b/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDelta.txt new file mode 100644 index 000000000..eef513cf1 --- /dev/null +++ b/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDelta.txt @@ -0,0 +1 @@ +data: {"type":"response.apply_patch_call_operation_diff.delta","delta":"@@\n-old line\n+new line","item_id":"apc_123","output_index":0,"sequence_number":2,"obfuscation":"obfuscated_123"} diff --git a/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDone.txt b/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDone.txt new file mode 100644 index 000000000..9bc8f0b64 --- /dev/null +++ b/tests/Fixtures/Streams/ResponseApplyPatchCallOperationDiffDone.txt @@ -0,0 +1 @@ +data: {"type":"response.apply_patch_call_operation_diff.done","diff":"@@\n-old line\n+new line","item_id":"apc_123","output_index":0,"sequence_number":3} diff --git a/tests/Responses/Responses/CreateStreamedResponse.php b/tests/Responses/Responses/CreateStreamedResponse.php index 331377165..196018448 100644 --- a/tests/Responses/Responses/CreateStreamedResponse.php +++ b/tests/Responses/Responses/CreateStreamedResponse.php @@ -10,6 +10,8 @@ use OpenAI\Responses\Responses\Output\OutputProgramOutput; use OpenAI\Responses\Responses\Output\OutputToolSearchCall; use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; +use OpenAI\Responses\Responses\Streaming\ApplyPatchCallOperationDiffDelta; +use OpenAI\Responses\Responses\Streaming\ApplyPatchCallOperationDiffDone; use OpenAI\Responses\Responses\Streaming\OutputItem; use OpenAI\Responses\Responses\Streaming\RateLimits; use OpenAI\Responses\Responses\Streaming\ReasoningTextDelta; @@ -67,6 +69,33 @@ ->response->sequenceNumber->toBe(10); }); +test('apply patch call operation diff delta event', function () { + $response = CreateStreamedResponse::fake(responseApplyPatchCallOperationDiffDeltaEvent()); + + expect($response->getIterator()->current()) + ->toBeInstanceOf(CreateStreamedResponse::class) + ->event->toBe('response.apply_patch_call_operation_diff.delta') + ->response->toBeInstanceOf(ApplyPatchCallOperationDiffDelta::class) + ->response->delta->toBe("@@\n-old line\n+new line") + ->response->itemId->toBe('apc_123') + ->response->outputIndex->toBe(0) + ->response->sequenceNumber->toBe(2) + ->response->obfuscation->toBe('obfuscated_123'); +}); + +test('apply patch call operation diff done event', function () { + $response = CreateStreamedResponse::fake(responseApplyPatchCallOperationDiffDoneEvent()); + + expect($response->getIterator()->current()) + ->toBeInstanceOf(CreateStreamedResponse::class) + ->event->toBe('response.apply_patch_call_operation_diff.done') + ->response->toBeInstanceOf(ApplyPatchCallOperationDiffDone::class) + ->response->diff->toBe("@@\n-old line\n+new line") + ->response->itemId->toBe('apc_123') + ->response->outputIndex->toBe(0) + ->response->sequenceNumber->toBe(3); +}); + test('rate limits updated event', function () { $response = CreateStreamedResponse::fake(responseRateLimitsUpdatedEvent());