diff --git a/src/Actions/Responses/CustomToolInputObjects.php b/src/Actions/Responses/CustomToolInputObjects.php new file mode 100644 index 000000000..ede190bfd --- /dev/null +++ b/src/Actions/Responses/CustomToolInputObjects.php @@ -0,0 +1,29 @@ + TextInput::from($attributes), + 'grammar' => GrammarInput::from($attributes), + }; + } +} diff --git a/src/Actions/Responses/NamespaceToolObjects.php b/src/Actions/Responses/NamespaceToolObjects.php new file mode 100644 index 000000000..b884a284a --- /dev/null +++ b/src/Actions/Responses/NamespaceToolObjects.php @@ -0,0 +1,33 @@ + + * @phpstan-type ResponseNamespaceToolObjectReturnType array + */ +final class NamespaceToolObjects +{ + /** + * @param ResponseNamespaceToolObjectTypes $toolItems + * @return ResponseNamespaceToolObjectReturnType + */ + public static function parse(array $toolItems): array + { + return array_map( + fn (array $tool): FunctionTool|CustomTool => match ($tool['type']) { + 'function' => FunctionTool::from($tool), + 'custom' => CustomTool::from($tool), + }, + $toolItems, + ); + } +} diff --git a/src/Actions/Responses/OutputObjects.php b/src/Actions/Responses/OutputObjects.php index c5c90d47a..fca94ba6e 100644 --- a/src/Actions/Responses/OutputObjects.php +++ b/src/Actions/Responses/OutputObjects.php @@ -16,6 +16,8 @@ use OpenAI\Responses\Responses\Output\OutputMcpListTools; use OpenAI\Responses\Responses\Output\OutputMessage; use OpenAI\Responses\Responses\Output\OutputReasoning; +use OpenAI\Responses\Responses\Output\OutputToolSearchCall; +use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; /** @@ -32,9 +34,11 @@ * @phpstan-import-type OutputCodeInterpreterToolCallType from OutputCodeInterpreterToolCall * @phpstan-import-type OutputLocalShellCallType from OutputLocalShellCall * @phpstan-import-type OutputCustomToolCallType from OutputCustomToolCall + * @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 { @@ -45,7 +49,7 @@ final class OutputObjects public static function parse(array $outputItems): array { return array_map( - fn (array $item): OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall => match ($item['type']) { + fn (array $item): OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput => match ($item['type']) { 'message' => OutputMessage::from($item), 'file_search_call' => OutputFileSearchToolCall::from($item), 'function_call' => OutputFunctionToolCall::from($item), @@ -59,6 +63,9 @@ public static function parse(array $outputItems): array 'code_interpreter_call' => OutputCodeInterpreterToolCall::from($item), 'local_shell_call' => OutputLocalShellCall::from($item), 'custom_tool_call' => OutputCustomToolCall::from($item), + 'tool_search_call' => OutputToolSearchCall::from($item), + 'tool_search_output' => OutputToolSearchOutput::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/ToolObjects.php b/src/Actions/Responses/ToolObjects.php index 774d04cc5..a99c8a905 100644 --- a/src/Actions/Responses/ToolObjects.php +++ b/src/Actions/Responses/ToolObjects.php @@ -6,23 +6,29 @@ use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; +use OpenAI\Responses\Responses\Tool\CustomTool; use OpenAI\Responses\Responses\Tool\FileSearchTool; use OpenAI\Responses\Responses\Tool\FunctionTool; use OpenAI\Responses\Responses\Tool\ImageGenerationTool; +use OpenAI\Responses\Responses\Tool\NamespaceTool; use OpenAI\Responses\Responses\Tool\RemoteMcpTool; +use OpenAI\Responses\Responses\Tool\ToolSearchTool; use OpenAI\Responses\Responses\Tool\WebSearchTool; /** + * @phpstan-import-type CodeInterpreterToolType from CodeInterpreterTool * @phpstan-import-type ComputerUseToolType from ComputerUseTool + * @phpstan-import-type CustomToolType from CustomTool * @phpstan-import-type FileSearchToolType from FileSearchTool + * @phpstan-import-type FunctionToolType from FunctionTool * @phpstan-import-type ImageGenerationToolType from ImageGenerationTool + * @phpstan-import-type NamespaceToolType from NamespaceTool * @phpstan-import-type RemoteMcpToolType from RemoteMcpTool - * @phpstan-import-type FunctionToolType from FunctionTool + * @phpstan-import-type ToolSearchToolType from ToolSearchTool * @phpstan-import-type WebSearchToolType from WebSearchTool - * @phpstan-import-type CodeInterpreterToolType from CodeInterpreterTool * - * @phpstan-type ResponseToolObjectTypes array - * @phpstan-type ResponseToolObjectReturnType array + * @phpstan-type ResponseToolObjectTypes array + * @phpstan-type ResponseToolObjectReturnType array */ final class ToolObjects { @@ -33,7 +39,7 @@ final class ToolObjects public static function parse(array $toolItems): array { return array_map( - fn (array $tool): ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool => match ($tool['type']) { + fn (array $tool): CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|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), @@ -41,6 +47,9 @@ public static function parse(array $toolItems): array 'image_generation' => ImageGenerationTool::from($tool), 'mcp' => RemoteMcpTool::from($tool), 'code_interpreter' => CodeInterpreterTool::from($tool), + 'tool_search' => ToolSearchTool::from($tool), + 'namespace' => NamespaceTool::from($tool), + 'custom' => CustomTool::from($tool), }, $toolItems, ); diff --git a/src/Responses/Responses/CreateResponse.php b/src/Responses/Responses/CreateResponse.php index 6d7db8465..6c4a78526 100644 --- a/src/Responses/Responses/CreateResponse.php +++ b/src/Responses/Responses/CreateResponse.php @@ -25,13 +25,18 @@ use OpenAI\Responses\Responses\Output\OutputMcpListTools; use OpenAI\Responses\Responses\Output\OutputMessage; use OpenAI\Responses\Responses\Output\OutputReasoning; +use OpenAI\Responses\Responses\Output\OutputToolSearchCall; +use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; +use OpenAI\Responses\Responses\Tool\CustomTool; use OpenAI\Responses\Responses\Tool\FileSearchTool; use OpenAI\Responses\Responses\Tool\FunctionTool; use OpenAI\Responses\Responses\Tool\ImageGenerationTool; +use OpenAI\Responses\Responses\Tool\NamespaceTool; use OpenAI\Responses\Responses\Tool\RemoteMcpTool; +use OpenAI\Responses\Responses\Tool\ToolSearchTool; use OpenAI\Responses\Responses\Tool\WebSearchTool; use OpenAI\Responses\Responses\ToolChoice\FunctionToolChoice; use OpenAI\Responses\Responses\ToolChoice\HostedToolChoice; @@ -47,6 +52,9 @@ * @phpstan-import-type ResponseOutputObjectTypes from OutputObjects * @phpstan-import-type ResponseToolChoiceTypes from ToolChoiceObjects * @phpstan-import-type ResponseToolObjectTypes from ToolObjects + * @phpstan-import-type ResponseOutputObjectReturnType from OutputObjects + * @phpstan-import-type ResponseToolChoiceReturnType from ToolChoiceObjects + * @phpstan-import-type ResponseToolObjectReturnType from ToolObjects * * @phpstan-type InstructionsType array|string|null * @phpstan-type CreateResponseType array{id: string, background?: bool|null, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details?: IncompleteDetailsType|null, instructions: InstructionsType, max_output_tokens?: int|null, max_tool_calls?: int|null, model: string, output: ResponseOutputObjectTypes, output_text: string|null, parallel_tool_calls: bool, previous_response_id?: string|null, prompt: ReferencePromptObjectType|null, prompt_cache_key?: string|null, reasoning?: ReasoningType|null, safety_identifier?: string|null, service_tier?: string|null, store?: bool|null, temperature?: float|null, text?: ResponseFormatType|null, tool_choice: ResponseToolChoiceTypes, tools: ResponseToolObjectTypes, top_logprobs?: int|null, top_p?: float|null, truncation?: 'auto'|'disabled'|null, usage?: UsageType|null, user?: string|null, verbosity?: string|null, metadata?: array|null} @@ -67,8 +75,8 @@ final class CreateResponse implements ResponseContract, ResponseHasMetaInformati * @param 'response' $object * @param 'completed'|'failed'|'in_progress'|'incomplete' $status * @param array|string|null $instructions - * @param array $output - * @param array $tools + * @param ResponseOutputObjectReturnType $output + * @param ResponseToolObjectReturnType $tools * @param 'auto'|'disabled'|null $truncation * @param array $metadata */ @@ -187,7 +195,7 @@ public function toArray(): array 'metadata' => $this->metadata ?? [], 'model' => $this->model, 'output' => array_map( - fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall $output): array => $output->toArray(), + fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput $output): array => $output->toArray(), $this->output ), 'parallel_tool_calls' => $this->parallelToolCalls, @@ -204,7 +212,7 @@ public function toArray(): array ? $this->toolChoice : $this->toolChoice->toArray(), 'tools' => array_map( - fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool $tool): array => $tool->toArray(), + fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool $tool): array => $tool->toArray(), $this->tools ), 'top_logprobs' => $this->topLogProbs, diff --git a/src/Responses/Responses/Output/OutputToolSearchCall.php b/src/Responses/Responses/Output/OutputToolSearchCall.php new file mode 100644 index 000000000..2a944cd75 --- /dev/null +++ b/src/Responses/Responses/Output/OutputToolSearchCall.php @@ -0,0 +1,71 @@ + + */ +final class OutputToolSearchCall implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'server'|'client' $execution + * @param 'in_progress'|'completed'|'incomplete' $status + * @param 'tool_search_call' $type + */ + private function __construct( + public readonly string $id, + public readonly mixed $arguments, + public readonly ?string $callId, + public readonly string $execution, + public readonly string $status, + public readonly string $type, + public readonly ?string $createdBy, + ) {} + + /** + * @param OutputToolSearchCallType $attributes + */ + public static function from(array $attributes): self + { + return new self( + id: $attributes['id'], + arguments: $attributes['arguments'], + callId: $attributes['call_id'] ?? null, + execution: $attributes['execution'], + status: $attributes['status'], + type: $attributes['type'], + createdBy: $attributes['created_by'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'arguments' => $this->arguments, + 'call_id' => $this->callId, + 'execution' => $this->execution, + 'status' => $this->status, + 'type' => $this->type, + 'created_by' => $this->createdBy, + ]; + } +} diff --git a/src/Responses/Responses/Output/OutputToolSearchOutput.php b/src/Responses/Responses/Output/OutputToolSearchOutput.php new file mode 100644 index 000000000..8308530ff --- /dev/null +++ b/src/Responses/Responses/Output/OutputToolSearchOutput.php @@ -0,0 +1,89 @@ + + */ +final class OutputToolSearchOutput implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'server'|'client' $execution + * @param 'in_progress'|'completed'|'incomplete' $status + * @param ResponseToolObjectReturnType $tools + * @param 'tool_search_output' $type + */ + private function __construct( + public readonly string $id, + public readonly ?string $callId, + public readonly string $execution, + public readonly string $status, + public readonly array $tools, + public readonly string $type, + public readonly ?string $createdBy, + ) {} + + /** + * @param OutputToolSearchOutputType $attributes + */ + public static function from(array $attributes): self + { + return new self( + id: $attributes['id'], + callId: $attributes['call_id'] ?? null, + execution: $attributes['execution'], + status: $attributes['status'], + tools: ToolObjects::parse($attributes['tools']), + type: $attributes['type'], + createdBy: $attributes['created_by'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'call_id' => $this->callId, + 'execution' => $this->execution, + 'status' => $this->status, + 'tools' => array_map( + fn (CodeInterpreterTool|ComputerUseTool|CustomTool|FileSearchTool|FunctionTool|ImageGenerationTool|NamespaceTool|RemoteMcpTool|ToolSearchTool|WebSearchTool $tool): array => $tool->toArray(), + $this->tools, + ), + 'type' => $this->type, + 'created_by' => $this->createdBy, + ]; + } +} diff --git a/src/Responses/Responses/RetrieveResponse.php b/src/Responses/Responses/RetrieveResponse.php index 097a1156e..2c1d99099 100644 --- a/src/Responses/Responses/RetrieveResponse.php +++ b/src/Responses/Responses/RetrieveResponse.php @@ -25,13 +25,18 @@ use OpenAI\Responses\Responses\Output\OutputMcpListTools; use OpenAI\Responses\Responses\Output\OutputMessage; use OpenAI\Responses\Responses\Output\OutputReasoning; +use OpenAI\Responses\Responses\Output\OutputToolSearchCall; +use OpenAI\Responses\Responses\Output\OutputToolSearchOutput; use OpenAI\Responses\Responses\Output\OutputWebSearchToolCall; use OpenAI\Responses\Responses\Tool\CodeInterpreterTool; use OpenAI\Responses\Responses\Tool\ComputerUseTool; +use OpenAI\Responses\Responses\Tool\CustomTool; use OpenAI\Responses\Responses\Tool\FileSearchTool; use OpenAI\Responses\Responses\Tool\FunctionTool; use OpenAI\Responses\Responses\Tool\ImageGenerationTool; +use OpenAI\Responses\Responses\Tool\NamespaceTool; use OpenAI\Responses\Responses\Tool\RemoteMcpTool; +use OpenAI\Responses\Responses\Tool\ToolSearchTool; use OpenAI\Responses\Responses\Tool\WebSearchTool; use OpenAI\Responses\Responses\ToolChoice\FunctionToolChoice; use OpenAI\Responses\Responses\ToolChoice\HostedToolChoice; @@ -47,6 +52,9 @@ * @phpstan-import-type ResponseOutputObjectTypes from OutputObjects * @phpstan-import-type ResponseToolChoiceTypes from ToolChoiceObjects * @phpstan-import-type ResponseToolObjectTypes from ToolObjects + * @phpstan-import-type ResponseOutputObjectReturnType from OutputObjects + * @phpstan-import-type ResponseToolChoiceReturnType from ToolChoiceObjects + * @phpstan-import-type ResponseToolObjectReturnType from ToolObjects * * @phpstan-type InstructionsType array|string|null * @phpstan-type RetrieveResponseType array{id: string, background?: bool|null, object: 'response', created_at: int, status: 'completed'|'failed'|'in_progress'|'incomplete', error: ErrorType|null, incomplete_details?: IncompleteDetailsType|null, instructions: InstructionsType, max_output_tokens?: int|null, max_tool_calls?: int|null, model: string, output: ResponseOutputObjectTypes, output_text: string|null, parallel_tool_calls: bool, previous_response_id?: string|null, prompt: ReferencePromptObjectType|null, prompt_cache_key?: string|null, reasoning?: ReasoningType|null, safety_identifier?: string|null, service_tier?: string|null, store?: bool|null, temperature?: float|null, text?: ResponseFormatType|null, tool_choice: ResponseToolChoiceTypes, tools: ResponseToolObjectTypes, top_logprobs?: int|null, top_p?: float|null, truncation?: 'auto'|'disabled'|null, usage?: UsageType|null, user?: string|null, verbosity?: string|null, metadata?: array|null} @@ -67,8 +75,8 @@ final class RetrieveResponse implements ResponseContract, ResponseHasMetaInforma * @param 'response' $object * @param 'completed'|'failed'|'in_progress'|'incomplete' $status * @param array|string|null $instructions - * @param array $output - * @param array $tools + * @param ResponseOutputObjectReturnType $output + * @param ResponseToolObjectReturnType $tools * @param 'auto'|'disabled'|null $truncation * @param array $metadata */ @@ -187,7 +195,7 @@ public function toArray(): array 'metadata' => $this->metadata ?? [], 'model' => $this->model, 'output' => array_map( - fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall $output): array => $output->toArray(), + fn (OutputMessage|OutputComputerToolCall|OutputFileSearchToolCall|OutputWebSearchToolCall|OutputFunctionToolCall|OutputReasoning|OutputMcpListTools|OutputMcpApprovalRequest|OutputMcpCall|OutputImageGenerationToolCall|OutputCodeInterpreterToolCall|OutputLocalShellCall|OutputCustomToolCall|OutputToolSearchCall|OutputToolSearchOutput $output): array => $output->toArray(), $this->output ), 'output_text' => $this->outputText, @@ -205,7 +213,7 @@ public function toArray(): array ? $this->toolChoice : $this->toolChoice->toArray(), 'tools' => array_map( - fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool $tool): array => $tool->toArray(), + fn (ComputerUseTool|FileSearchTool|FunctionTool|WebSearchTool|ImageGenerationTool|RemoteMcpTool|CodeInterpreterTool|ToolSearchTool|NamespaceTool|CustomTool $tool): array => $tool->toArray(), $this->tools ), 'top_logprobs' => $this->topLogProbs, diff --git a/src/Responses/Responses/Tool/CustomTool.php b/src/Responses/Responses/Tool/CustomTool.php new file mode 100644 index 000000000..d670cc2b6 --- /dev/null +++ b/src/Responses/Responses/Tool/CustomTool.php @@ -0,0 +1,70 @@ + + */ +final class CustomTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'custom' $type + */ + private function __construct( + public readonly string $name, + public readonly string $type, + public readonly ?bool $deferLoading = null, + public readonly ?string $description = null, + public readonly TextInput|GrammarInput|null $format = null, + ) {} + + /** + * @param CustomToolType $attributes + */ + public static function from(array $attributes): self + { + return new self( + name: $attributes['name'], + type: $attributes['type'], + deferLoading: $attributes['defer_loading'] ?? null, + description: $attributes['description'] ?? null, + format: isset($attributes['format']) + ? CustomToolInputObjects::parse($attributes['format']) + : null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return array_filter([ + 'name' => $this->name, + 'type' => $this->type, + 'defer_loading' => $this->deferLoading, + 'description' => $this->description, + 'format' => $this->format?->toArray(), + ], fn (mixed $value): bool => $value !== null); + } +} diff --git a/src/Responses/Responses/Tool/CustomToolInputs/GrammarInput.php b/src/Responses/Responses/Tool/CustomToolInputs/GrammarInput.php new file mode 100644 index 000000000..31d9ceb96 --- /dev/null +++ b/src/Responses/Responses/Tool/CustomToolInputs/GrammarInput.php @@ -0,0 +1,57 @@ + + */ +final class GrammarInput implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'grammar' $type + */ + private function __construct( + public readonly string $type, + public readonly string $definition, + public readonly string $syntax, + ) {} + + /** + * @param GrammarInputType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + definition: $attributes['definition'], + syntax: $attributes['syntax'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'definition' => $this->definition, + 'syntax' => $this->syntax, + ]; + } +} diff --git a/src/Responses/Responses/Tool/CustomToolInputs/TextInput.php b/src/Responses/Responses/Tool/CustomToolInputs/TextInput.php new file mode 100644 index 000000000..215545ce7 --- /dev/null +++ b/src/Responses/Responses/Tool/CustomToolInputs/TextInput.php @@ -0,0 +1,51 @@ + + */ +final class TextInput implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'text' $type + */ + private function __construct( + public readonly string $type, + ) {} + + /** + * @param TextInputType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + ]; + } +} diff --git a/src/Responses/Responses/Tool/NamespaceTool.php b/src/Responses/Responses/Tool/NamespaceTool.php new file mode 100644 index 000000000..4e5c20dc7 --- /dev/null +++ b/src/Responses/Responses/Tool/NamespaceTool.php @@ -0,0 +1,74 @@ +, type: 'namespace'} + * + * @implements ResponseContract + */ +final class NamespaceTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'namespace' $type + * @param array $tools + */ + private function __construct( + public readonly string $description, + public readonly string $name, + public readonly array $tools, + public readonly string $type, + ) {} + + /** + * @param NamespaceToolType $attributes + */ + public static function from(array $attributes): self + { + /** @var array $tools */ + $tools = NamespaceToolObjects::parse($attributes['tools']); + + return new self( + description: $attributes['description'], + name: $attributes['name'], + tools: $tools, + type: $attributes['type'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'description' => $this->description, + 'name' => $this->name, + 'tools' => array_map( + fn (FunctionTool|CustomTool $tool): array => $tool->toArray(), + $this->tools, + ), + 'type' => $this->type, + ]; + } +} diff --git a/src/Responses/Responses/Tool/NamespaceTools/CustomTool.php b/src/Responses/Responses/Tool/NamespaceTools/CustomTool.php new file mode 100644 index 000000000..824fc22b2 --- /dev/null +++ b/src/Responses/Responses/Tool/NamespaceTools/CustomTool.php @@ -0,0 +1,70 @@ + + */ +final class CustomTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'custom' $type + */ + private function __construct( + public readonly string $name, + public readonly string $type, + public readonly ?bool $deferLoading = null, + public readonly ?string $description = null, + public readonly TextInput|GrammarInput|null $format = null, + ) {} + + /** + * @param CustomToolType $attributes + */ + public static function from(array $attributes): self + { + return new self( + name: $attributes['name'], + type: $attributes['type'], + deferLoading: $attributes['defer_loading'] ?? null, + description: $attributes['description'] ?? null, + format: isset($attributes['format']) + ? CustomToolInputObjects::parse($attributes['format']) + : null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return array_filter([ + 'name' => $this->name, + 'type' => $this->type, + 'defer_loading' => $this->deferLoading, + 'description' => $this->description, + 'format' => $this->format?->toArray(), + ], fn (mixed $value): bool => $value !== null); + } +} diff --git a/src/Responses/Responses/Tool/NamespaceTools/FunctionTool.php b/src/Responses/Responses/Tool/NamespaceTools/FunctionTool.php new file mode 100644 index 000000000..623e8fe38 --- /dev/null +++ b/src/Responses/Responses/Tool/NamespaceTools/FunctionTool.php @@ -0,0 +1,64 @@ +, strict: bool, type: 'function', description: ?string} + * + * @implements ResponseContract + */ +final class FunctionTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param array $parameters + * @param 'function' $type + */ + private function __construct( + public readonly string $name, + public readonly array $parameters, + public readonly bool $strict, + public readonly string $type, + public readonly ?string $description = null, + ) {} + + /** + * @param FunctionToolType $attributes + */ + public static function from(array $attributes): self + { + return new self( + name: $attributes['name'], + parameters: $attributes['parameters'], + strict: $attributes['strict'], + type: $attributes['type'], + description: $attributes['description'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'name' => $this->name, + 'parameters' => $this->parameters, + 'strict' => $this->strict, + 'type' => $this->type, + 'description' => $this->description, + ]; + } +} diff --git a/src/Responses/Responses/Tool/ToolSearchTool.php b/src/Responses/Responses/Tool/ToolSearchTool.php new file mode 100644 index 000000000..9c69ad14f --- /dev/null +++ b/src/Responses/Responses/Tool/ToolSearchTool.php @@ -0,0 +1,61 @@ + + */ +final class ToolSearchTool implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'tool_search' $type + * @param 'server'|'client'|null $execution + */ + private function __construct( + public readonly string $type, + public readonly ?string $description, + public readonly ?string $execution, + public readonly mixed $parameters + ) {} + + /** + * @param ToolSearchToolType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + description: $attributes['description'] ?? null, + execution: $attributes['execution'] ?? null, + parameters: $attributes['parameters'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'type' => $this->type, + 'description' => $this->description, + 'execution' => $this->execution, + 'parameters' => $this->parameters, + ]; + } +} diff --git a/tests/Fixtures/Responses.php b/tests/Fixtures/Responses.php index b0eda37ab..5dfb701eb 100644 --- a/tests/Fixtures/Responses.php +++ b/tests/Fixtures/Responses.php @@ -903,6 +903,86 @@ function toolFileSearchNestedFilters(): array ]; } +/** + * @return array + */ +function toolCustom(): array +{ + return [ + 'name' => 'my_custom_tool', + 'type' => 'custom', + 'defer_loading' => false, + 'description' => 'A custom tool.', + 'format' => [ + 'type' => 'text', + ], + ]; +} + +/** + * @return array + */ +function toolNamespace(): array +{ + return [ + 'description' => 'A namespace of tools.', + 'name' => 'my_namespace', + 'tools' => [], + 'type' => 'namespace', + ]; +} + +/** + * @return array + */ +function toolToolSearch(): array +{ + return [ + 'type' => 'tool_search', + 'description' => 'A tool for searching tools.', + 'execution' => 'server', + 'parameters' => [], + ]; +} + +/** + * @return array + */ +function outputToolSearchCall(): array +{ + return [ + 'id' => 'tsc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'arguments' => [], + 'call_id' => 'call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'execution' => 'server', + 'status' => 'completed', + 'type' => 'tool_search_call', + 'created_by' => 'user_123', + ]; +} + +/** + * @return array + */ +function outputToolSearchOutput(): array +{ + return [ + 'id' => 'tso_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'call_id' => 'call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c', + 'execution' => 'server', + 'status' => 'completed', + 'tools' => [ + [ + 'type' => 'web_search', + 'search_context_size' => 'low', + 'user_location' => null, + ], + ], + 'type' => 'tool_search_output', + 'created_by' => 'user_123', + ]; +} + /** * @return resource */ diff --git a/tests/Responses/Responses/Output/OutputToolSearchCall.php b/tests/Responses/Responses/Output/OutputToolSearchCall.php new file mode 100644 index 000000000..6410a9ade --- /dev/null +++ b/tests/Responses/Responses/Output/OutputToolSearchCall.php @@ -0,0 +1,31 @@ +toBeInstanceOf(OutputToolSearchCall::class) + ->id->toBe('tsc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->arguments->toBeArray() + ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->execution->toBe('server') + ->status->toBe('completed') + ->type->toBe('tool_search_call') + ->createdBy->toBe('user_123'); +}); + +test('as array accessible', function () { + $response = OutputToolSearchCall::from(outputToolSearchCall()); + + expect($response['id'])->toBe('tsc_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c'); +}); + +test('to array', function () { + $response = OutputToolSearchCall::from(outputToolSearchCall()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(outputToolSearchCall()); +}); diff --git a/tests/Responses/Responses/Output/OutputToolSearchOutput.php b/tests/Responses/Responses/Output/OutputToolSearchOutput.php new file mode 100644 index 000000000..131cd8113 --- /dev/null +++ b/tests/Responses/Responses/Output/OutputToolSearchOutput.php @@ -0,0 +1,34 @@ +toBeInstanceOf(OutputToolSearchOutput::class) + ->id->toBe('tso_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->callId->toBe('call_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c') + ->execution->toBe('server') + ->status->toBe('completed') + ->tools->toBeArray() + ->tools->toHaveCount(1) + ->tools->{0}->toBeInstanceOf(WebSearchTool::class) + ->type->toBe('tool_search_output') + ->createdBy->toBe('user_123'); +}); + +test('as array accessible', function () { + $response = OutputToolSearchOutput::from(outputToolSearchOutput()); + + expect($response['id'])->toBe('tso_67ccf18f64008190a39b619f4c8455ef087bb177ab789d5c'); +}); + +test('to array', function () { + $response = OutputToolSearchOutput::from(outputToolSearchOutput()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(outputToolSearchOutput()); +}); diff --git a/tests/Responses/Responses/Tool/CustomToolTest.php b/tests/Responses/Responses/Tool/CustomToolTest.php new file mode 100644 index 000000000..801d25a39 --- /dev/null +++ b/tests/Responses/Responses/Tool/CustomToolTest.php @@ -0,0 +1,44 @@ +toBeInstanceOf(CustomTool::class) + ->name->toBe('my_custom_tool') + ->type->toBe('custom') + ->deferLoading->toBeFalse() + ->description->toBe('A custom tool.') + ->format->toBeInstanceOf(TextInput::class) + ->format->type->toBe('text'); +}); + +test('to array with custom tool', function () { + $response = CustomTool::from(toolCustom()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(toolCustom()); +}); + +test('namespace tool with custom tool', function () { + $data = toolNamespace(); + $data['tools'] = [ + toolCustom(), + ]; + + $response = NamespaceTool::from($data); + + expect($response) + ->toBeInstanceOf(NamespaceTool::class) + ->tools->toHaveCount(1) + ->tools->{0}->toBeInstanceOf(NamespaceCustomTool::class); + + expect($response->toArray()) + ->toBe($data); +}); diff --git a/tests/Responses/Responses/Tool/NamespaceTool.php b/tests/Responses/Responses/Tool/NamespaceTool.php new file mode 100644 index 000000000..58019c381 --- /dev/null +++ b/tests/Responses/Responses/Tool/NamespaceTool.php @@ -0,0 +1,28 @@ +toBeInstanceOf(NamespaceTool::class) + ->description->toBe('A namespace of tools.') + ->name->toBe('my_namespace') + ->tools->toBeArray() + ->type->toBe('namespace'); +}); + +test('as array accessible', function () { + $response = NamespaceTool::from(toolNamespace()); + + expect($response['type'])->toBe('namespace'); +}); + +test('to array', function () { + $response = NamespaceTool::from(toolNamespace()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(toolNamespace()); +}); diff --git a/tests/Responses/Responses/Tool/ToolSearchTool.php b/tests/Responses/Responses/Tool/ToolSearchTool.php new file mode 100644 index 000000000..81785358c --- /dev/null +++ b/tests/Responses/Responses/Tool/ToolSearchTool.php @@ -0,0 +1,28 @@ +toBeInstanceOf(ToolSearchTool::class) + ->type->toBe('tool_search') + ->description->toBe('A tool for searching tools.') + ->execution->toBe('server') + ->parameters->toBeArray(); +}); + +test('as array accessible', function () { + $response = ToolSearchTool::from(toolToolSearch()); + + expect($response['type'])->toBe('tool_search'); +}); + +test('to array', function () { + $response = ToolSearchTool::from(toolToolSearch()); + + expect($response->toArray()) + ->toBeArray() + ->toBe(toolToolSearch()); +});