diff --git a/src/Capabilities/Chat/Parameters/JsonSchema/JsonAnyOf.php b/src/Capabilities/Chat/Parameters/JsonSchema/JsonAnyOf.php new file mode 100644 index 0000000..2896ce7 --- /dev/null +++ b/src/Capabilities/Chat/Parameters/JsonSchema/JsonAnyOf.php @@ -0,0 +1,25 @@ +schemas[] = $schema instanceof Arrayable ? $schema->toArray() : $schema; + return $this; + } + + public function toArray(): array + { + return [ + 'anyOf' => $this->schemas, + ]; + } +} diff --git a/src/Capabilities/Chat/Parameters/JsonSchema/JsonBoolean.php b/src/Capabilities/Chat/Parameters/JsonSchema/JsonBoolean.php new file mode 100644 index 0000000..e0c1186 --- /dev/null +++ b/src/Capabilities/Chat/Parameters/JsonSchema/JsonBoolean.php @@ -0,0 +1,25 @@ + 'boolean', + ]; + + if ($this->description) { + $return['description'] = $this->description; + } + + return $return; + } +} diff --git a/src/Capabilities/Chat/Parameters/JsonSchema/JsonEnum.php b/src/Capabilities/Chat/Parameters/JsonSchema/JsonEnum.php new file mode 100644 index 0000000..aff34eb --- /dev/null +++ b/src/Capabilities/Chat/Parameters/JsonSchema/JsonEnum.php @@ -0,0 +1,40 @@ +enum = $enum; + return $this; + } + + public function toArray(): array + { + if (!$this->enum) { + throw new Exception('Enum values are required for JsonEnum'); + } + + $return = [ + 'type' => 'string', + ]; + + if ($this->description) { + $return['description'] = $this->description; + } + + if ($this->enum) { + $return['enum'] = $this->enum; + } + + return $return; + } +} diff --git a/src/Capabilities/Chat/Parameters/JsonSchema/JsonInteger.php b/src/Capabilities/Chat/Parameters/JsonSchema/JsonInteger.php new file mode 100644 index 0000000..5780c88 --- /dev/null +++ b/src/Capabilities/Chat/Parameters/JsonSchema/JsonInteger.php @@ -0,0 +1,25 @@ + 'integer', + ]; + + if ($this->description) { + $return['description'] = $this->description; + } + + return $return; + } +} diff --git a/src/Capabilities/Chat/Parameters/JsonSchema/JsonNumber.php b/src/Capabilities/Chat/Parameters/JsonSchema/JsonNumber.php index 0e5aa1e..fe5d246 100644 --- a/src/Capabilities/Chat/Parameters/JsonSchema/JsonNumber.php +++ b/src/Capabilities/Chat/Parameters/JsonSchema/JsonNumber.php @@ -8,22 +8,18 @@ class JsonNumber implements Arrayable { use StaticMake; - public function __construct(protected ?string $description = null, protected ?array $enum = null) {} + public function __construct(protected ?string $description = null) {} public function toArray(): array { $return = [ - 'type' => 'string', + 'type' => 'number', ]; if ($this->description) { $return['description'] = $this->description; } - if ($this->enum) { - $return['enum'] = $this->enum; - } - return $return; } } diff --git a/tests/Unit/ChatTest.php b/tests/Unit/ChatTest.php index bdaa2bc..829b4c6 100644 --- a/tests/Unit/ChatTest.php +++ b/tests/Unit/ChatTest.php @@ -4,14 +4,18 @@ use Outl1ne\NovaOpenAI\Facades\OpenAI; use Orchestra\Testbench\Concerns\WithWorkbench; +use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonAnyOf; use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\Messages; use Outl1ne\NovaOpenAI\Capabilities\Chat\Responses\ChatResponse; use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\ResponseFormat; +use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonEnum; use Outl1ne\NovaOpenAI\Capabilities\Chat\Responses\StreamedChatResponse; use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonArray; +use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonNumber; use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonObject; -use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonSchema; use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonString; +use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonBoolean; +use Outl1ne\NovaOpenAI\Capabilities\Chat\Parameters\JsonSchema\JsonInteger; class ChatTest extends \Orchestra\Testbench\TestCase { @@ -50,11 +54,28 @@ public function test_chat_json_schema_response(): void { $response = OpenAI::chat()->create( model: 'gpt-4o-mini', - messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me tasty fruits.'), - responseFormat: ResponseFormat::make()->jsonSchema(JsonObject::make()->property('fruits', JsonArray::make()->items(JsonString::make()))), + messages: Messages::make()->system('You are a helpful assistant.')->user('Suggest me 10 tasty fruits.'), + responseFormat: ResponseFormat::make()->jsonSchema( + JsonObject::make() + ->property('fruits', JsonArray::make()->items(JsonString::make())) + ->property('number_of_fruits_in_response', JsonInteger::make()) + ->property('number_of_fruits_in_response_divided_by_three', JsonNumber::make()) + ->property('is_number_of_fruits_in_response_even', JsonBoolean::make()) + ->property('fruit_most_occurring_color', JsonEnum::make()->enums(['red', 'green', 'blue'])) + ->property( + 'random_integer_or_string_max_one_character', + JsonAnyOf::make() + ->schema(JsonInteger::make()) + ->schema(JsonString::make()) + ), + ), ); $this->assertTrue($response instanceof ChatResponse); $this->assertIsArray($response->json()?->fruits); + $this->assertIsInt($response->json()?->number_of_fruits_in_response); + $this->assertIsFloat($response->json()?->number_of_fruits_in_response_divided_by_three); + $this->assertIsBool($response->json()?->is_number_of_fruits_in_response_even); + $this->assertIsString($response->json()?->fruit_most_occurring_color); $response = OpenAI::chat()->create( model: 'gpt-4o-mini',