Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
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
1,275 changes: 1,275 additions & 0 deletions src/Builders/PromptBuilder.php

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion src/Common/AbstractDataTransferObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ abstract class AbstractDataTransferObject implements
*
* @since n.e.x.t
*
* @param TArrayShape $data The array data to validate.
* @param array<mixed> $data The array data to validate.
* @param string[] $requiredKeys The keys that must be present.
* @throws InvalidArgumentException If any required key is missing.
*/
Expand All @@ -62,6 +62,22 @@ protected static function validateFromArrayData(array $data, array $requiredKeys
}
}

/**
* {@inheritDoc}
*
* @since n.e.x.t
*/
public static function isArrayShape(array $array): bool
{
try {
/** @var TArrayShape $array */
static::fromArray($array);
return true;
} catch (InvalidArgumentException $e) {
return false;
}
}

/**
* Converts the object to a JSON-serializable format.
*
Expand Down
11 changes: 11 additions & 0 deletions src/Common/Contracts/WithArrayTransformationInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,15 @@ public function toArray(): array;
* @return self<TArrayShape> The created instance.
*/
public static function fromArray(array $array): self;

/**
* Checks if the array is a valid shape for this object.
*
* @since n.e.x.t
*
* @param array<mixed> $array The array to check.
* @return bool True if the array is a valid shape.
* @phpstan-assert-if-true TArrayShape $array
*/
public static function isArrayShape(array $array): bool;
}
12 changes: 12 additions & 0 deletions src/Files/DTO/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,18 @@ public function isText(): bool
return $this->mimeType->isText();
}

/**
* Checks if the file is a document.
*
* @since n.e.x.t
*
* @return bool True if the file is a document.
*/
public function isDocument(): bool
{
return $this->mimeType->isDocument();
}

/**
* Checks if the file is a specific MIME type.
*
Expand Down
16 changes: 16 additions & 0 deletions src/Messages/DTO/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,22 @@ public function getParts(): array
return $this->parts;
}

/**
* Returns a new instance with the given part appended.
*
* @since n.e.x.t
*
* @param MessagePart $part The part to append.
* @return Message A new instance with the part appended.
*/
public function withPart(MessagePart $part): Message
{
$newParts = $this->parts;
$newParts[] = $part;

return new Message($this->role, $newParts);
}

/**
* {@inheritDoc}
*
Expand Down
156 changes: 156 additions & 0 deletions src/Providers/Models/DTO/ModelConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use WordPress\AiClient\Files\Enums\FileTypeEnum;
use WordPress\AiClient\Files\Enums\MediaOrientationEnum;
use WordPress\AiClient\Messages\Enums\ModalityEnum;
use WordPress\AiClient\Providers\Models\Enums\OptionEnum;
use WordPress\AiClient\Tools\DTO\FunctionDeclaration;
use WordPress\AiClient\Tools\DTO\WebSearch;

Expand Down Expand Up @@ -202,6 +203,36 @@ public function getOutputModalities(): ?array
return $this->outputModalities;
}

/**
* Includes an output modality if not already present.
*
* Adds the given modality to the output modalities list if it's not
* already included. If output modalities is null, initializes it with
* the given modality.
*
* @since n.e.x.t
*
* @param ModalityEnum $modality The modality to include.
*/
public function includeOutputModality(ModalityEnum $modality): void
{
// Initialize if null
if ($this->outputModalities === null) {
$this->outputModalities = [$modality];
return;
}

// Check if modality already exists
foreach ($this->outputModalities as $existingModality) {
if ($existingModality === $modality) {
return; // Already included
}
}

// Add the modality
$this->outputModalities[] = $modality;
}

/**
* Sets the system instruction.
*
Expand Down Expand Up @@ -913,6 +944,131 @@ static function (FunctionDeclaration $function_declaration): array {
return $data;
}

/**
* Converts the model configuration to required options.
*
* @since n.e.x.t
*
* @return list<RequiredOption> The required options.
*/
public function toRequiredOptions(): array
{
$requiredOptions = [];

// Map properties that have corresponding OptionEnum values
if ($this->outputModalities !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::outputModalities()->value,
$this->outputModalities
);
}

if ($this->systemInstruction !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::systemInstruction()->value,
$this->systemInstruction
);
}

if ($this->candidateCount !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::candidateCount()->value,
$this->candidateCount
);
}

if ($this->maxTokens !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::maxTokens()->value,
$this->maxTokens
);
}

if ($this->temperature !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::temperature()->value,
$this->temperature
);
}

if ($this->topP !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::topP()->value,
$this->topP
);
}

if ($this->topK !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::topK()->value,
$this->topK
);
}

if ($this->outputMimeType !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::outputMimeType()->value,
$this->outputMimeType
);
}

if ($this->outputSchema !== null) {
$requiredOptions[] = new RequiredOption(
OptionEnum::outputSchema()->value,
true // Just indicate that schema is required
);
}

// Handle properties without OptionEnum values as custom options
// These would need to be handled specially by providers
if ($this->stopSequences !== null) {
$requiredOptions[] = new RequiredOption('stop_sequences', $this->stopSequences);
}

if ($this->presencePenalty !== null) {
$requiredOptions[] = new RequiredOption('presence_penalty', $this->presencePenalty);
}

if ($this->frequencyPenalty !== null) {
$requiredOptions[] = new RequiredOption('frequency_penalty', $this->frequencyPenalty);
}

if ($this->logprobs !== null) {
$requiredOptions[] = new RequiredOption('logprobs', $this->logprobs);
}

if ($this->topLogprobs !== null) {
$requiredOptions[] = new RequiredOption('top_logprobs', $this->topLogprobs);
}

if ($this->functionDeclarations !== null) {
$requiredOptions[] = new RequiredOption('function_declarations', true);
}

if ($this->webSearch !== null) {
$requiredOptions[] = new RequiredOption('web_search', true);
}

if ($this->outputFileType !== null) {
$requiredOptions[] = new RequiredOption('output_file_type', $this->outputFileType->value);
}

if ($this->outputMediaOrientation !== null) {
$requiredOptions[] = new RequiredOption('output_media_orientation', $this->outputMediaOrientation->value);
}

if ($this->outputMediaAspectRatio !== null) {
$requiredOptions[] = new RequiredOption('output_media_aspect_ratio', $this->outputMediaAspectRatio);
}

// Add custom options as individual RequiredOptions
foreach ($this->customOptions as $key => $value) {
$requiredOptions[] = new RequiredOption($key, $value);
}

return $requiredOptions;
}

/**
* {@inheritDoc}
*
Expand Down
29 changes: 29 additions & 0 deletions tests/traits/ArrayTransformationTestTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,33 @@ protected function assertArrayNotHasKeys(array $array, array $unexpectedKeys): v
$this->assertArrayNotHasKey($key, $array, "Array should not contain key: {$key}");
}
}

/**
* Tests isArrayShape with valid and invalid arrays.
*
* @param string $className The class name to test.
* @param array $validArray A valid array that should pass isArrayShape.
* @param array[] $invalidArrays Arrays that should fail isArrayShape.
* @return void
*/
protected function assertIsArrayShapeValidation(string $className, array $validArray, array $invalidArrays): void
{
// Test valid array
$this->assertTrue(
$className::isArrayShape($validArray),
'isArrayShape() should return true for valid array structure'
);

// Test that fromArray works with the valid array (ensures consistency)
$instance = $className::fromArray($validArray);
$this->assertInstanceOf($className, $instance);

// Test invalid arrays
foreach ($invalidArrays as $description => $invalidArray) {
$this->assertFalse(
$className::isArrayShape($invalidArray),
"isArrayShape() should return false for: {$description}"
);
}
}
}
Loading