diff --git a/src/Responses/Responses/Input/FunctionToolCallOutput.php b/src/Responses/Responses/Input/FunctionToolCallOutput.php index 267e7232..6ca9a76b 100644 --- a/src/Responses/Responses/Input/FunctionToolCallOutput.php +++ b/src/Responses/Responses/Input/FunctionToolCallOutput.php @@ -9,7 +9,11 @@ use OpenAI\Testing\Responses\Concerns\Fakeable; /** - * @phpstan-type FunctionToolCallOutputType array{call_id: string, id: string, output: string, type: 'function_call_output', status: 'in_progress'|'completed'|'incompleted'} + * @phpstan-import-type FunctionToolCallOutputTextType from FunctionToolCallOutputText + * @phpstan-import-type FunctionToolCallOutputImageType from FunctionToolCallOutputImage + * @phpstan-import-type FunctionToolCallOutputFileType from FunctionToolCallOutputFile + * + * @phpstan-type FunctionToolCallOutputType array{call_id: string, id: string, output: string|array, type: 'function_call_output', status: 'in_progress'|'completed'|'incompleted'} * * @implements ResponseContract */ @@ -25,11 +29,12 @@ final class FunctionToolCallOutput implements ResponseContract /** * @param 'function_call_output' $type * @param 'in_progress'|'completed'|'incompleted' $status + * @param string|array $output Output can be a string (for text/JSON) or array (for structured content like files/images) */ private function __construct( public readonly string $callId, public readonly string $id, - public readonly string $output, + public readonly string|array $output, public readonly string $type, public readonly string $status, ) {} @@ -39,10 +44,23 @@ private function __construct( */ public static function from(array $attributes): self { + $output = $attributes['output']; + + if (is_array($output)) { + $output = array_map( + fn (array $item): FunctionToolCallOutputText|FunctionToolCallOutputImage|FunctionToolCallOutputFile => match ($item['type']) { + 'input_text' => FunctionToolCallOutputText::from($item), + 'input_image' => FunctionToolCallOutputImage::from($item), + 'input_file' => FunctionToolCallOutputFile::from($item), + }, + $output, + ); + } + return new self( callId: $attributes['call_id'], id: $attributes['id'], - output: $attributes['output'], + output: $output, type: $attributes['type'], status: $attributes['status'], ); @@ -56,7 +74,12 @@ public function toArray(): array return [ 'call_id' => $this->callId, 'id' => $this->id, - 'output' => $this->output, + 'output' => is_array($this->output) + ? array_map( + fn (FunctionToolCallOutputText|FunctionToolCallOutputImage|FunctionToolCallOutputFile $item): array => $item->toArray(), + $this->output, + ) + : $this->output, 'type' => $this->type, 'status' => $this->status, ]; diff --git a/src/Responses/Responses/Input/FunctionToolCallOutputFile.php b/src/Responses/Responses/Input/FunctionToolCallOutputFile.php new file mode 100644 index 00000000..1e0a60fe --- /dev/null +++ b/src/Responses/Responses/Input/FunctionToolCallOutputFile.php @@ -0,0 +1,63 @@ + + */ +final class FunctionToolCallOutputFile implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'input_file' $type + */ + private function __construct( + public readonly string $type, + public readonly ?string $fileData, + public readonly ?string $fileId, + public readonly ?string $fileUrl, + public readonly ?string $filename, + ) {} + + /** + * @param FunctionToolCallOutputFileType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + fileData: $attributes['file_data'] ?? null, + fileId: $attributes['file_id'] ?? null, + fileUrl: $attributes['file_url'] ?? null, + filename: $attributes['filename'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return array_filter([ + 'type' => $this->type, + 'file_data' => $this->fileData, + 'file_id' => $this->fileId, + 'file_url' => $this->fileUrl, + 'filename' => $this->filename, + ], fn ($value) => $value !== null); + } +} diff --git a/src/Responses/Responses/Input/FunctionToolCallOutputImage.php b/src/Responses/Responses/Input/FunctionToolCallOutputImage.php new file mode 100644 index 00000000..cb7593f3 --- /dev/null +++ b/src/Responses/Responses/Input/FunctionToolCallOutputImage.php @@ -0,0 +1,60 @@ + + */ +final class FunctionToolCallOutputImage implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'input_image' $type + */ + private function __construct( + public readonly string $type, + public readonly ?string $detail, + public readonly ?string $fileId, + public readonly ?string $imageUrl, + ) {} + + /** + * @param FunctionToolCallOutputImageType $attributes + */ + public static function from(array $attributes): self + { + return new self( + type: $attributes['type'], + detail: $attributes['detail'] ?? null, + fileId: $attributes['file_id'] ?? null, + imageUrl: $attributes['image_url'] ?? null, + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return array_filter([ + 'type' => $this->type, + 'detail' => $this->detail, + 'file_id' => $this->fileId, + 'image_url' => $this->imageUrl, + ], fn ($value) => $value !== null); + } +} diff --git a/src/Responses/Responses/Input/FunctionToolCallOutputText.php b/src/Responses/Responses/Input/FunctionToolCallOutputText.php new file mode 100644 index 00000000..694428ab --- /dev/null +++ b/src/Responses/Responses/Input/FunctionToolCallOutputText.php @@ -0,0 +1,54 @@ + + */ +final class FunctionToolCallOutputText implements ResponseContract +{ + /** + * @use ArrayAccessible + */ + use ArrayAccessible; + + use Fakeable; + + /** + * @param 'input_text' $type + */ + private function __construct( + public readonly string $text, + public readonly string $type + ) {} + + /** + * @param FunctionToolCallOutputTextType $attributes + */ + public static function from(array $attributes): self + { + return new self( + text: $attributes['text'], + type: $attributes['type'], + ); + } + + /** + * {@inheritDoc} + */ + public function toArray(): array + { + return [ + 'text' => $this->text, + 'type' => $this->type, + ]; + } +}