From 6a2e9f552765fc55f36073703f626afc8cd4fc78 Mon Sep 17 00:00:00 2001 From: Jason Adams Date: Fri, 29 Aug 2025 15:07:37 -0700 Subject: [PATCH 1/2] fix: stores required options as enums not strings --- src/Providers/Models/DTO/ModelConfig.php | 4 +- .../Providers/Models/DTO/ModelConfigTest.php | 244 ++++++++++++++++++ 2 files changed, 246 insertions(+), 2 deletions(-) diff --git a/src/Providers/Models/DTO/ModelConfig.php b/src/Providers/Models/DTO/ModelConfig.php index 63cf6aba..e4894447 100644 --- a/src/Providers/Models/DTO/ModelConfig.php +++ b/src/Providers/Models/DTO/ModelConfig.php @@ -1068,13 +1068,13 @@ public function toRequiredOptions(): array } if ($this->outputFileType !== null) { - $requiredOptions[] = new RequiredOption(OptionEnum::outputFileType(), $this->outputFileType->value); + $requiredOptions[] = new RequiredOption(OptionEnum::outputFileType(), $this->outputFileType); } if ($this->outputMediaOrientation !== null) { $requiredOptions[] = new RequiredOption( OptionEnum::outputMediaOrientation(), - $this->outputMediaOrientation->value + $this->outputMediaOrientation ); } diff --git a/tests/unit/Providers/Models/DTO/ModelConfigTest.php b/tests/unit/Providers/Models/DTO/ModelConfigTest.php index e875f993..d8efa917 100644 --- a/tests/unit/Providers/Models/DTO/ModelConfigTest.php +++ b/tests/unit/Providers/Models/DTO/ModelConfigTest.php @@ -12,6 +12,7 @@ use WordPress\AiClient\Files\Enums\MediaOrientationEnum; use WordPress\AiClient\Messages\Enums\ModalityEnum; use WordPress\AiClient\Providers\Models\DTO\ModelConfig; +use WordPress\AiClient\Providers\Models\Enums\OptionEnum; use WordPress\AiClient\Tools\DTO\FunctionDeclaration; use WordPress\AiClient\Tools\DTO\WebSearch; @@ -703,4 +704,247 @@ public function testSetCustomOption(): void $restored = ModelConfig::fromArray($array); $this->assertEquals($customOptions, $restored->getCustomOptions()); } + + /** + * Tests toRequiredOptions method with all properties. + * + * @return void + */ + public function testToRequiredOptionsWithAllProperties(): void + { + $config = new ModelConfig(); + + // Set all properties that map to RequiredOptions + $config->setOutputModalities([ModalityEnum::text(), ModalityEnum::image()]); + $config->setSystemInstruction('Be helpful'); + $config->setCandidateCount(2); + $config->setMaxTokens(1000); + $config->setTemperature(0.7); + $config->setTopP(0.9); + $config->setTopK(40); + $config->setStopSequences(['STOP', 'END']); + $config->setPresencePenalty(0.5); + $config->setFrequencyPenalty(0.3); + $config->setLogprobs(true); + $config->setTopLogprobs(5); + $config->setFunctionDeclarations([$this->createSampleFunctionDeclaration()]); + $config->setWebSearch($this->createSampleWebSearch()); + $config->setOutputFileType(FileTypeEnum::inline()); + $config->setOutputMimeType('application/json'); + $config->setOutputSchema(['type' => 'object']); + $config->setOutputMediaOrientation(MediaOrientationEnum::landscape()); + $config->setOutputMediaAspectRatio('16:9'); + $config->setCustomOptions(['key1' => 'value1', 'key2' => 'value2']); + + $requiredOptions = $config->toRequiredOptions(); + + $this->assertIsArray($requiredOptions); + $this->assertNotEmpty($requiredOptions); + + // Helper function to find option by name + /** + * @param list<\WordPress\AiClient\Providers\Models\DTO\RequiredOption> $options + * @param OptionEnum $name + * @return \WordPress\AiClient\Providers\Models\DTO\RequiredOption|null + */ + $findOption = function ( + array $options, + OptionEnum $name + ): ?\WordPress\AiClient\Providers\Models\DTO\RequiredOption { + foreach ($options as $option) { + if ($option->getName()->equals($name)) { + return $option; + } + } + return null; + }; + + // Test output modalities + $outputModalitiesOption = $findOption($requiredOptions, OptionEnum::outputModalities()); + $this->assertNotNull($outputModalitiesOption); + $this->assertEquals([ModalityEnum::text(), ModalityEnum::image()], $outputModalitiesOption->getValue()); + + // Test system instruction + $systemInstructionOption = $findOption($requiredOptions, OptionEnum::systemInstruction()); + $this->assertNotNull($systemInstructionOption); + $this->assertEquals('Be helpful', $systemInstructionOption->getValue()); + + // Test candidate count + $candidateCountOption = $findOption($requiredOptions, OptionEnum::candidateCount()); + $this->assertNotNull($candidateCountOption); + $this->assertEquals(2, $candidateCountOption->getValue()); + + // Test max tokens + $maxTokensOption = $findOption($requiredOptions, OptionEnum::maxTokens()); + $this->assertNotNull($maxTokensOption); + $this->assertEquals(1000, $maxTokensOption->getValue()); + + // Test temperature + $temperatureOption = $findOption($requiredOptions, OptionEnum::temperature()); + $this->assertNotNull($temperatureOption); + $this->assertEquals(0.7, $temperatureOption->getValue()); + + // Test top-p + $topPOption = $findOption($requiredOptions, OptionEnum::topP()); + $this->assertNotNull($topPOption); + $this->assertEquals(0.9, $topPOption->getValue()); + + // Test top-k + $topKOption = $findOption($requiredOptions, OptionEnum::topK()); + $this->assertNotNull($topKOption); + $this->assertEquals(40, $topKOption->getValue()); + + // Test stop sequences + $stopSequencesOption = $findOption($requiredOptions, OptionEnum::stopSequences()); + $this->assertNotNull($stopSequencesOption); + $this->assertEquals(['STOP', 'END'], $stopSequencesOption->getValue()); + + // Test presence penalty + $presencePenaltyOption = $findOption($requiredOptions, OptionEnum::presencePenalty()); + $this->assertNotNull($presencePenaltyOption); + $this->assertEquals(0.5, $presencePenaltyOption->getValue()); + + // Test frequency penalty + $frequencyPenaltyOption = $findOption($requiredOptions, OptionEnum::frequencyPenalty()); + $this->assertNotNull($frequencyPenaltyOption); + $this->assertEquals(0.3, $frequencyPenaltyOption->getValue()); + + // Test logprobs + $logprobsOption = $findOption($requiredOptions, OptionEnum::logprobs()); + $this->assertNotNull($logprobsOption); + $this->assertTrue($logprobsOption->getValue()); + + // Test top logprobs + $topLogprobsOption = $findOption($requiredOptions, OptionEnum::topLogprobs()); + $this->assertNotNull($topLogprobsOption); + $this->assertEquals(5, $topLogprobsOption->getValue()); + + // Test function declarations (should be boolean true) + $functionDeclarationsOption = $findOption($requiredOptions, OptionEnum::functionDeclarations()); + $this->assertNotNull($functionDeclarationsOption); + $this->assertTrue($functionDeclarationsOption->getValue()); + + // Test web search (should be boolean true) + $webSearchOption = $findOption($requiredOptions, OptionEnum::webSearch()); + $this->assertNotNull($webSearchOption); + $this->assertTrue($webSearchOption->getValue()); + + // Test output file type - IMPORTANT: Should be the enum object, not the string value + $outputFileTypeOption = $findOption($requiredOptions, OptionEnum::outputFileType()); + $this->assertNotNull($outputFileTypeOption); + $this->assertInstanceOf(FileTypeEnum::class, $outputFileTypeOption->getValue()); + $this->assertSame(FileTypeEnum::inline(), $outputFileTypeOption->getValue()); + + // Test output MIME type + $outputMimeTypeOption = $findOption($requiredOptions, OptionEnum::outputMimeType()); + $this->assertNotNull($outputMimeTypeOption); + $this->assertEquals('application/json', $outputMimeTypeOption->getValue()); + + // Test output schema + $outputSchemaOption = $findOption($requiredOptions, OptionEnum::outputSchema()); + $this->assertNotNull($outputSchemaOption); + $this->assertEquals(['type' => 'object'], $outputSchemaOption->getValue()); + + // Test output media orientation - IMPORTANT: Should be the enum object, not the string value + $outputMediaOrientationOption = $findOption($requiredOptions, OptionEnum::outputMediaOrientation()); + $this->assertNotNull($outputMediaOrientationOption); + $this->assertInstanceOf(MediaOrientationEnum::class, $outputMediaOrientationOption->getValue()); + $this->assertSame(MediaOrientationEnum::landscape(), $outputMediaOrientationOption->getValue()); + + // Test output media aspect ratio + $outputMediaAspectRatioOption = $findOption($requiredOptions, OptionEnum::outputMediaAspectRatio()); + $this->assertNotNull($outputMediaAspectRatioOption); + $this->assertEquals('16:9', $outputMediaAspectRatioOption->getValue()); + + // Test custom options - each custom option should be a separate RequiredOption + $customOptions = array_filter($requiredOptions, function ($option) { + return $option->getName()->equals(OptionEnum::customOptions()); + }); + $this->assertCount(2, $customOptions); // We set 2 custom options + + // Verify custom option values + $customOptionValues = array_map(function ($option) { + return $option->getValue(); + }, $customOptions); + $this->assertContains(['key1' => 'value1'], $customOptionValues); + $this->assertContains(['key2' => 'value2'], $customOptionValues); + } + + /** + * Tests toRequiredOptions method with no properties set. + * + * @return void + */ + public function testToRequiredOptionsWithNoProperties(): void + { + $config = new ModelConfig(); + $requiredOptions = $config->toRequiredOptions(); + + $this->assertIsArray($requiredOptions); + $this->assertEmpty($requiredOptions); + } + + /** + * Tests toRequiredOptions method with partial properties. + * + * @return void + */ + public function testToRequiredOptionsWithPartialProperties(): void + { + $config = new ModelConfig(); + + // Only set a few properties + $config->setTemperature(0.8); + $config->setMaxTokens(500); + $config->setOutputFileType(FileTypeEnum::remote()); + $config->setOutputMediaOrientation(MediaOrientationEnum::portrait()); + + $requiredOptions = $config->toRequiredOptions(); + + $this->assertIsArray($requiredOptions); + $this->assertCount(4, $requiredOptions); + + // Helper function to find option by name + /** + * @param list<\WordPress\AiClient\Providers\Models\DTO\RequiredOption> $options + * @param OptionEnum $name + * @return \WordPress\AiClient\Providers\Models\DTO\RequiredOption|null + */ + $findOption = function ( + array $options, + OptionEnum $name + ): ?\WordPress\AiClient\Providers\Models\DTO\RequiredOption { + foreach ($options as $option) { + if ($option->getName()->equals($name)) { + return $option; + } + } + return null; + }; + + // Test temperature + $temperatureOption = $findOption($requiredOptions, OptionEnum::temperature()); + $this->assertNotNull($temperatureOption); + $this->assertEquals(0.8, $temperatureOption->getValue()); + + // Test max tokens + $maxTokensOption = $findOption($requiredOptions, OptionEnum::maxTokens()); + $this->assertNotNull($maxTokensOption); + $this->assertEquals(500, $maxTokensOption->getValue()); + + // Test output file type - Should be the enum object + $outputFileTypeOption = $findOption($requiredOptions, OptionEnum::outputFileType()); + $this->assertNotNull($outputFileTypeOption); + $this->assertInstanceOf(FileTypeEnum::class, $outputFileTypeOption->getValue()); + $this->assertSame(FileTypeEnum::remote(), $outputFileTypeOption->getValue()); + $this->assertTrue($outputFileTypeOption->getValue()->isRemote()); + + // Test output media orientation - Should be the enum object + $outputMediaOrientationOption = $findOption($requiredOptions, OptionEnum::outputMediaOrientation()); + $this->assertNotNull($outputMediaOrientationOption); + $this->assertInstanceOf(MediaOrientationEnum::class, $outputMediaOrientationOption->getValue()); + $this->assertSame(MediaOrientationEnum::portrait(), $outputMediaOrientationOption->getValue()); + $this->assertTrue($outputMediaOrientationOption->getValue()->isPortrait()); + } + } From 707683d08a23b9556d616223467afb950997b0c2 Mon Sep 17 00:00:00 2001 From: Jason Adams Date: Fri, 29 Aug 2025 15:10:20 -0700 Subject: [PATCH 2/2] test: fixes linting issue --- tests/unit/Providers/Models/DTO/ModelConfigTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/Providers/Models/DTO/ModelConfigTest.php b/tests/unit/Providers/Models/DTO/ModelConfigTest.php index d8efa917..e99f0585 100644 --- a/tests/unit/Providers/Models/DTO/ModelConfigTest.php +++ b/tests/unit/Providers/Models/DTO/ModelConfigTest.php @@ -946,5 +946,4 @@ public function testToRequiredOptionsWithPartialProperties(): void $this->assertSame(MediaOrientationEnum::portrait(), $outputMediaOrientationOption->getValue()); $this->assertTrue($outputMediaOrientationOption->getValue()->isPortrait()); } - }