Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5604d1d
feat: first draft of PromptBuilder
JasonTheAdams Aug 14, 2025
3e436ee
feat: adds Prompts utility class
JasonTheAdams Aug 21, 2025
73c2a09
feat: expands builder message handling
JasonTheAdams Aug 21, 2025
c91e520
feat: improves model requirement system
JasonTheAdams Aug 21, 2025
7654608
feat: adds DTO::isArrayShape
JasonTheAdams Aug 21, 2025
c996c45
test: adds isArrayShape tests
JasonTheAdams Aug 21, 2025
8f390d6
test: adds some isArrayShape tests
JasonTheAdams Aug 21, 2025
e8ac1c2
refactor: cleans up file type checking
JasonTheAdams Aug 21, 2025
9638c41
feat: adds type checking methods to File DTO
JasonTheAdams Aug 21, 2025
0da377e
refactor: uses new file type checking
JasonTheAdams Aug 21, 2025
eb9f197
refactor: removes inferred property in favor of late checking
JasonTheAdams Aug 21, 2025
88242f6
feat: adds means of converting ModelConfig to requirements
JasonTheAdams Aug 21, 2025
2a65902
refactor: cleans up how system instructions work
JasonTheAdams Aug 21, 2025
f480ce1
refactor: switches validating to getting a model
JasonTheAdams Aug 21, 2025
59cc9df
fix: always sets the model config
JasonTheAdams Aug 22, 2025
01f294b
feat: adds generate methods
JasonTheAdams Aug 22, 2025
3fd1fc2
refactor: tweaks enum comparison
JasonTheAdams Aug 22, 2025
c56bf85
test: adds includeOutputModality tests
JasonTheAdams Aug 22, 2025
539a278
refactor: uses system instructions instead of message
JasonTheAdams Aug 22, 2025
e86190e
test: adds a lot of Prompt Builder tests
JasonTheAdams Aug 22, 2025
29b59fe
test: fixes failing builder tests
JasonTheAdams Aug 22, 2025
0fd756e
refactor: removes the need for the Prompts utility class
JasonTheAdams Aug 22, 2025
78fa2aa
feat: improves methods for checking if prompt is supported
JasonTheAdams Aug 22, 2025
4d91dc4
test: fixes a bajillion linting errors
JasonTheAdams Aug 22, 2025
d976ccf
fix: uses output schema as require option value
JasonTheAdams Aug 23, 2025
bba08dd
refactor: moves includeOutputModalities to PromptBuilder
JasonTheAdams Aug 23, 2025
9281c54
refactor: improves handling of message arrays
JasonTheAdams Aug 23, 2025
d7320ce
refactor: consolidates builder file methods
JasonTheAdams Aug 23, 2025
cb54e0b
feat: adds file isInline and isRemote methods
JasonTheAdams Aug 23, 2025
f324b96
refactor: removes usingRegistry method for now
JasonTheAdams Aug 25, 2025
a07c242
feat: validates message parts
JasonTheAdams Aug 25, 2025
0d39e74
fix: prepends message in withHistory
JasonTheAdams Aug 25, 2025
7d25a03
feat: turns OptionEnum into a superset of ModelConfig
JasonTheAdams Aug 25, 2025
3c6d641
refactor: switches required and supports options to use enum as name
JasonTheAdams Aug 26, 2025
3396da6
refactor: tightens AbstractEnum cache scope
JasonTheAdams Aug 26, 2025
5d081b4
fix: corrects isSupported to use capabilities
JasonTheAdams Aug 26, 2025
cb7fb19
refactor: corrects input modality collection
JasonTheAdams Aug 26, 2025
2c7b443
feat: adds function call checking to requirements
JasonTheAdams Aug 26, 2025
b1a3f44
refactor: goes back to checking if model meets requirements in suppor…
JasonTheAdams Aug 26, 2025
f366cdd
refactor: adjusts generateResult to use capability
JasonTheAdams Aug 26, 2025
4f4779c
refactor: improves variable naming
JasonTheAdams Aug 26, 2025
5d9a2a9
refactor: cleans up generate methods big time
JasonTheAdams Aug 26, 2025
e7ffc3c
refactor: uses consistent to- naming convention
JasonTheAdams Aug 26, 2025
98dd0a2
chore: corrects array types as lists
JasonTheAdams Aug 26, 2025
d5d782e
refactor: adjusts isSupported to work more like generateResults
JasonTheAdams Aug 26, 2025
b8dcc95
chore: provides Prompt type
JasonTheAdams Aug 26, 2025
32e1fb8
refactor: renames output modalities method to use as- prefix
JasonTheAdams Aug 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/Common/AbstractEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ abstract class AbstractEnum
/**
* @var array<string, array<string, string>> Cache for reflection data.
*/
private static array $cache = [];
protected static array $cache = [];

/**
* @var array<string, array<string, self>> Cache for enum instances.
Expand Down Expand Up @@ -138,7 +138,7 @@ final public static function from(string $value): self
*/
final public static function tryFrom(string $value): ?self
{
$constants = self::getConstants();
$constants = static::getConstants();
foreach ($constants as $name => $constantValue) {
if ($constantValue === $value) {
return self::getInstance($constantValue, $name);
Expand All @@ -157,7 +157,7 @@ final public static function tryFrom(string $value): ?self
final public static function cases(): array
{
$cases = [];
$constants = self::getConstants();
$constants = static::getConstants();
foreach ($constants as $name => $value) {
$cases[] = self::getInstance($value, $name);
}
Expand Down Expand Up @@ -203,7 +203,7 @@ final public function is(self $other): bool
*/
final public static function getValues(): array
{
return array_values(self::getConstants());
return array_values(static::getConstants());
}

/**
Expand Down Expand Up @@ -253,7 +253,7 @@ private static function getInstance(string $value, string $name): self
* @return array<string, string> Map of constant names to values.
* @throws RuntimeException If invalid constant found.
*/
final protected static function getConstants(): array
protected static function getConstants(): array
{
$className = static::class;

Expand Down Expand Up @@ -312,7 +312,7 @@ final public function __call(string $name, array $arguments): bool
// Handle is* methods
if (strpos($name, 'is') === 0) {
$constantName = self::camelCaseToConstant(substr($name, 2));
$constants = self::getConstants();
$constants = static::getConstants();

if (isset($constants[$constantName])) {
return $this->value === $constants[$constantName];
Expand All @@ -337,7 +337,7 @@ final public function __call(string $name, array $arguments): bool
final public static function __callStatic(string $name, array $arguments): self
{
$constantName = self::camelCaseToConstant($name);
$constants = self::getConstants();
$constants = static::getConstants();

if (isset($constants[$constantName])) {
return self::getInstance($constants[$constantName], $constantName);
Expand Down
126 changes: 80 additions & 46 deletions src/Providers/Models/Enums/OptionEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,117 @@

namespace WordPress\AiClient\Providers\Models\Enums;

use ReflectionClass;
use WordPress\AiClient\Common\AbstractEnum;
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;

/**
* Enum for model options.
*
* @since n.e.x.t
* This enum dynamically includes all options from ModelConfig KEY_* constants
* in addition to the explicitly defined constants below.
*
* Explicitly defined option (not in ModelConfig):
* @method static self inputModalities() Creates an instance for INPUT_MODALITIES option.
* @method static self outputModalities() Creates an instance for OUTPUT_MODALITIES option.
* @method static self systemInstruction() Creates an instance for SYSTEM_INSTRUCTION option.
* @method bool isInputModalities() Checks if the option is INPUT_MODALITIES.
*
* Dynamically loaded from ModelConfig KEY_* constants:
* @method static self candidateCount() Creates an instance for CANDIDATE_COUNT option.
* @method static self customOptions() Creates an instance for CUSTOM_OPTIONS option.
* @method static self frequencyPenalty() Creates an instance for FREQUENCY_PENALTY option.
* @method static self functionDeclarations() Creates an instance for FUNCTION_DECLARATIONS option.
* @method static self logprobs() Creates an instance for LOGPROBS option.
* @method static self maxTokens() Creates an instance for MAX_TOKENS option.
* @method static self outputFileType() Creates an instance for OUTPUT_FILE_TYPE option.
* @method static self outputMediaAspectRatio() Creates an instance for OUTPUT_MEDIA_ASPECT_RATIO option.
* @method static self outputMediaOrientation() Creates an instance for OUTPUT_MEDIA_ORIENTATION option.
* @method static self outputMimeType() Creates an instance for OUTPUT_MIME_TYPE option.
* @method static self outputModalities() Creates an instance for OUTPUT_MODALITIES option.
* @method static self outputSchema() Creates an instance for OUTPUT_SCHEMA option.
* @method static self presencePenalty() Creates an instance for PRESENCE_PENALTY option.
* @method static self stopSequences() Creates an instance for STOP_SEQUENCES option.
* @method static self systemInstruction() Creates an instance for SYSTEM_INSTRUCTION option.
* @method static self temperature() Creates an instance for TEMPERATURE option.
* @method static self topK() Creates an instance for TOP_K option.
* @method static self topLogprobs() Creates an instance for TOP_LOGPROBS option.
* @method static self topP() Creates an instance for TOP_P option.
* @method static self outputMimeType() Creates an instance for OUTPUT_MIME_TYPE option.
* @method static self outputSchema() Creates an instance for OUTPUT_SCHEMA option.
* @method bool isInputModalities() Checks if the option is INPUT_MODALITIES.
* @method bool isOutputModalities() Checks if the option is OUTPUT_MODALITIES.
* @method bool isSystemInstruction() Checks if the option is SYSTEM_INSTRUCTION.
* @method static self webSearch() Creates an instance for WEB_SEARCH option.
* @method bool isCandidateCount() Checks if the option is CANDIDATE_COUNT.
* @method bool isCustomOptions() Checks if the option is CUSTOM_OPTIONS.
* @method bool isFrequencyPenalty() Checks if the option is FREQUENCY_PENALTY.
* @method bool isFunctionDeclarations() Checks if the option is FUNCTION_DECLARATIONS.
* @method bool isLogprobs() Checks if the option is LOGPROBS.
* @method bool isMaxTokens() Checks if the option is MAX_TOKENS.
* @method bool isOutputFileType() Checks if the option is OUTPUT_FILE_TYPE.
* @method bool isOutputMediaAspectRatio() Checks if the option is OUTPUT_MEDIA_ASPECT_RATIO.
* @method bool isOutputMediaOrientation() Checks if the option is OUTPUT_MEDIA_ORIENTATION.
* @method bool isOutputMimeType() Checks if the option is OUTPUT_MIME_TYPE.
* @method bool isOutputModalities() Checks if the option is OUTPUT_MODALITIES.
* @method bool isOutputSchema() Checks if the option is OUTPUT_SCHEMA.
* @method bool isPresencePenalty() Checks if the option is PRESENCE_PENALTY.
* @method bool isStopSequences() Checks if the option is STOP_SEQUENCES.
* @method bool isSystemInstruction() Checks if the option is SYSTEM_INSTRUCTION.
* @method bool isTemperature() Checks if the option is TEMPERATURE.
* @method bool isTopK() Checks if the option is TOP_K.
* @method bool isTopLogprobs() Checks if the option is TOP_LOGPROBS.
* @method bool isTopP() Checks if the option is TOP_P.
* @method bool isOutputMimeType() Checks if the option is OUTPUT_MIME_TYPE.
* @method bool isOutputSchema() Checks if the option is OUTPUT_SCHEMA.
* @method bool isWebSearch() Checks if the option is WEB_SEARCH.
*
* @since n.e.x.t
*/
class OptionEnum extends AbstractEnum
{
/**
* Input modalities option.
*
* This constant is not in ModelConfig as it's derived from message content,
* not configured directly.
*/
public const INPUT_MODALITIES = 'input_modalities';

/**
* Output modalities option.
*/
public const OUTPUT_MODALITIES = 'output_modalities';

/**
* System instruction option.
* Gets the constants for this enum.
*
* Overrides the parent method to dynamically add constants from ModelConfig
* that are prefixed with KEY_. These are transformed to remove the KEY_ prefix
* and converted to snake_case values.
*
* @since n.e.x.t
*
* @return array<string, string> The enum constants.
*/
public const SYSTEM_INSTRUCTION = 'system_instruction';
protected static function getConstants(): array
{
// Check if we already have cached constants for this class
$className = static::class;
if (isset(self::$cache[$className])) {
return self::$cache[$className];
}

/**
* Candidate count option.
*/
public const CANDIDATE_COUNT = 'candidate_count';

/**
* Maximum tokens option.
*/
public const MAX_TOKENS = 'max_tokens';
// Start with the constants defined in this class
$constants = parent::getConstants();

/**
* Temperature option.
*/
public const TEMPERATURE = 'temperature';
// Use reflection to get all constants from ModelConfig
$modelConfigReflection = new ReflectionClass(ModelConfig::class);
$modelConfigConstants = $modelConfigReflection->getConstants();

/**
* Top K option.
*/
public const TOP_K = 'top_k';
// Add ModelConfig constants that start with KEY_
foreach ($modelConfigConstants as $constantName => $constantValue) {
if (strpos($constantName, 'KEY_') === 0) {
// Remove KEY_ prefix to get the enum constant name
$enumConstantName = substr($constantName, 4);

/**
* Top P option.
*/
public const TOP_P = 'top_p';
// The value is the snake_case version stored in ModelConfig
// ModelConfig already stores these as snake_case strings
if (is_string($constantValue)) {
$constants[$enumConstantName] = $constantValue;
}
}
}

/**
* Output MIME type option.
*/
public const OUTPUT_MIME_TYPE = 'output_mime_type';
// Cache the combined constants
self::$cache[$className] = $constants;

/**
* Output schema option.
*/
public const OUTPUT_SCHEMA = 'output_schema';
return $constants;
}
}
86 changes: 78 additions & 8 deletions tests/unit/Providers/Models/Enums/OptionEnumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,30 @@ protected function getEnumClass(): string
protected function getExpectedValues(): array
{
return [
// Explicitly defined constant (not in ModelConfig)
'INPUT_MODALITIES' => 'input_modalities',
'OUTPUT_MODALITIES' => 'output_modalities',
'SYSTEM_INSTRUCTION' => 'system_instruction',
'CANDIDATE_COUNT' => 'candidate_count',
'MAX_TOKENS' => 'max_tokens',

// Dynamically added from ModelConfig KEY_* constants
'OUTPUT_MODALITIES' => 'outputModalities',
'SYSTEM_INSTRUCTION' => 'systemInstruction',
'CANDIDATE_COUNT' => 'candidateCount',
'MAX_TOKENS' => 'maxTokens',
'TEMPERATURE' => 'temperature',
'TOP_K' => 'top_k',
'TOP_P' => 'top_p',
'OUTPUT_MIME_TYPE' => 'output_mime_type',
'OUTPUT_SCHEMA' => 'output_schema',
'TOP_P' => 'topP',
'TOP_K' => 'topK',
'STOP_SEQUENCES' => 'stopSequences',
'PRESENCE_PENALTY' => 'presencePenalty',
'FREQUENCY_PENALTY' => 'frequencyPenalty',
'LOGPROBS' => 'logprobs',
'TOP_LOGPROBS' => 'topLogprobs',
'FUNCTION_DECLARATIONS' => 'functionDeclarations',
'WEB_SEARCH' => 'webSearch',
'OUTPUT_FILE_TYPE' => 'outputFileType',
'OUTPUT_MIME_TYPE' => 'outputMimeType',
'OUTPUT_SCHEMA' => 'outputSchema',
'OUTPUT_MEDIA_ORIENTATION' => 'outputMediaOrientation',
'OUTPUT_MEDIA_ASPECT_RATIO' => 'outputMediaAspectRatio',
'CUSTOM_OPTIONS' => 'customOptions',
];
}

Expand All @@ -65,4 +79,60 @@ public function testSpecificEnumMethods(): void
$this->assertTrue($outputSchema->isOutputSchema());
$this->assertFalse($outputSchema->isOutputMimeType());
}

/**
* Tests that dynamically loaded constants from ModelConfig work.
*
* @return void
*/
public function testDynamicallyLoadedConstants(): void
{
// Test a dynamically loaded constant
$stopSequences = OptionEnum::stopSequences();
$this->assertInstanceOf(OptionEnum::class, $stopSequences);
$this->assertEquals('stopSequences', $stopSequences->value);
$this->assertTrue($stopSequences->isStopSequences());
$this->assertFalse($stopSequences->isTemperature());

// Test another dynamically loaded constant
$presencePenalty = OptionEnum::presencePenalty();
$this->assertInstanceOf(OptionEnum::class, $presencePenalty);
$this->assertEquals('presencePenalty', $presencePenalty->value);
$this->assertTrue($presencePenalty->isPresencePenalty());
$this->assertFalse($presencePenalty->isFrequencyPenalty());

// Test that all expected dynamic constants are available
$this->assertInstanceOf(OptionEnum::class, OptionEnum::frequencyPenalty());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::logprobs());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::topLogprobs());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::functionDeclarations());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::webSearch());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::outputFileType());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::outputMediaOrientation());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::outputMediaAspectRatio());
$this->assertInstanceOf(OptionEnum::class, OptionEnum::customOptions());
}

/**
* Tests that getValues includes all dynamically loaded constants.
*
* @return void
*/
public function testGetValuesIncludesDynamicConstants(): void
{
$values = OptionEnum::getValues();

// Check that dynamic values are included
$this->assertContains('stopSequences', $values);
$this->assertContains('presencePenalty', $values);
$this->assertContains('frequencyPenalty', $values);
$this->assertContains('logprobs', $values);
$this->assertContains('topLogprobs', $values);
$this->assertContains('functionDeclarations', $values);
$this->assertContains('webSearch', $values);
$this->assertContains('outputFileType', $values);
$this->assertContains('outputMediaOrientation', $values);
$this->assertContains('outputMediaAspectRatio', $values);
$this->assertContains('customOptions', $values);
}
}