Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a36f93a
feat: adds provider DTOs
JasonTheAdams Jul 28, 2025
52ef504
test: adds DTO tests
JasonTheAdams Jul 28, 2025
f5d4251
refactor: simplifies file type checks
JasonTheAdams Jul 29, 2025
0a5ed46
refactor: imports built-in PHP classes and interfaces
JasonTheAdams Jul 29, 2025
7c13ac1
refactor: removes type check from File and MessagePart fromArray
JasonTheAdams Jul 29, 2025
4342636
Merge branch 'dto-serlialization' into extender-dtos
JasonTheAdams Jul 29, 2025
3a56ff2
Merge branch 'dto-serlialization' into extender-dtos
JasonTheAdams Jul 29, 2025
1fbd566
refactor: removes useless constructor
JasonTheAdams Jul 29, 2025
22b0a11
refactor: removes model conflig fluency
JasonTheAdams Jul 29, 2025
18092d2
feat: validates ProviderMetaData::fromArray
JasonTheAdams Jul 29, 2025
0cf2617
test: fixes tests assuming config fluency
JasonTheAdams Jul 29, 2025
e1c9e5d
refactor: adds constants for scheam keys
JasonTheAdams Jul 29, 2025
5cb08bf
Merge branch 'dto-serlialization' into extender-dtos
JasonTheAdams Jul 29, 2025
e1e7312
refactor: removes final on DTOs
JasonTheAdams Jul 29, 2025
5eb9294
feat: adds validation and improves RequiredOption description
JasonTheAdams Jul 29, 2025
3db0127
Merge branch 'dto-serlialization' into extender-dtos
JasonTheAdams Jul 29, 2025
7025568
feat: adds setCustomOption method
JasonTheAdams Jul 30, 2025
8f6ff62
fix: resolved PHP 7.4 incompatibility
JasonTheAdams Jul 30, 2025
02d3433
feat: adds output schema and mime type support
JasonTheAdams Jul 30, 2025
07df1e7
feat: changes support values to be optional
JasonTheAdams Jul 30, 2025
fb76c99
refactor: removes array_values and supresses phpstan errors
JasonTheAdams Jul 30, 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
7 changes: 4 additions & 3 deletions src/Common/AbstractEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use BadMethodCallException;
use InvalidArgumentException;
use ReflectionClass;
use RuntimeException;

/**
* Abstract base class for enum-like behavior in PHP 7.4.
Expand Down Expand Up @@ -250,7 +251,7 @@ private static function getInstance(string $value, string $name): self
* @since n.e.x.t
*
* @return array<string, string> Map of constant names to values.
* @throws \RuntimeException If invalid constant found.
* @throws RuntimeException If invalid constant found.
*/
final protected static function getConstants(): array
{
Expand All @@ -265,7 +266,7 @@ final protected static function getConstants(): array
foreach ($constants as $name => $value) {
// Check if constant name follows uppercase snake_case pattern
if (!preg_match('/^[A-Z][A-Z0-9_]*$/', $name)) {
throw new \RuntimeException(
throw new RuntimeException(
sprintf(
'Invalid enum constant name "%s" in %s. Constants must be UPPER_SNAKE_CASE.',
$name,
Expand All @@ -276,7 +277,7 @@ final protected static function getConstants(): array

// Check if value is valid type
if (!is_string($value)) {
throw new \RuntimeException(
throw new RuntimeException(
sprintf(
'Invalid enum value type for constant %s::%s. ' .
'Only string values are allowed, %s given.',
Expand Down
18 changes: 9 additions & 9 deletions src/Files/DTO/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class File extends AbstractDataValueObject
*
* @param string $file The file string (URL, base64 data, or local path).
* @param string|null $mimeType The MIME type of the file (optional).
* @throws \InvalidArgumentException If the file format is invalid or MIME type cannot be determined.
* @throws InvalidArgumentException If the file format is invalid or MIME type cannot be determined.
*/
public function __construct(string $file, ?string $mimeType = null)
{
Expand All @@ -75,7 +75,7 @@ public function __construct(string $file, ?string $mimeType = null)
*
* @param string $file The file string to process.
* @param string|null $providedMimeType The explicitly provided MIME type.
* @throws \InvalidArgumentException If the file format is invalid or MIME type cannot be determined.
* @throws InvalidArgumentException If the file format is invalid or MIME type cannot be determined.
*/
private function detectAndProcessFile(string $file, ?string $providedMimeType): void
{
Expand Down Expand Up @@ -110,7 +110,7 @@ private function detectAndProcessFile(string $file, ?string $providedMimeType):
// Check if it's plain base64
if (preg_match('/^[A-Za-z0-9+\/]*={0,2}$/', $file)) {
if ($providedMimeType === null) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'MIME type is required when providing plain base64 data without data URI format.'
);
}
Expand All @@ -120,7 +120,7 @@ private function detectAndProcessFile(string $file, ?string $providedMimeType):
return;
}

throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Invalid file provided. Expected URL, base64 data, or valid local file path.'
);
}
Expand All @@ -146,14 +146,14 @@ private function isUrl(string $string): bool
*
* @param string $filePath The path to the local file.
* @return string The base64-encoded file data.
* @throws \RuntimeException If the file cannot be read.
* @throws RuntimeException If the file cannot be read.
*/
private function convertFileToBase64(string $filePath): string
{
$fileContent = @file_get_contents($filePath);

if ($fileContent === false) {
throw new \RuntimeException(
throw new RuntimeException(
sprintf('Unable to read file: %s', $filePath)
);
}
Expand Down Expand Up @@ -294,7 +294,7 @@ public function isText(): bool
* @param string|null $extractedMimeType The MIME type extracted from data URI.
* @param string|null $pathOrUrl The file path or URL to extract extension from.
* @return MimeType The determined MIME type.
* @throws \InvalidArgumentException If MIME type cannot be determined.
* @throws InvalidArgumentException If MIME type cannot be determined.
*/
private function determineMimeType(
?string $providedMimeType,
Expand Down Expand Up @@ -326,14 +326,14 @@ private function determineMimeType(
if (!empty($extension)) {
try {
return MimeType::fromExtension($extension);
} catch (\InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
// Extension not recognized, continue to error
unset($e);
}
}
}

throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'Unable to determine MIME type. Please provide it explicitly.'
);
}
Expand Down
12 changes: 7 additions & 5 deletions src/Files/ValueObjects/MimeType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace WordPress\AiClient\Files\ValueObjects;

use InvalidArgumentException;

/**
* Value object representing a MIME type.
*
Expand Down Expand Up @@ -115,12 +117,12 @@ final class MimeType
* @since n.e.x.t
*
* @param string $value The MIME type value.
* @throws \InvalidArgumentException If the MIME type is invalid.
* @throws InvalidArgumentException If the MIME type is invalid.
*/
public function __construct(string $value)
{
if (!self::isValid($value)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf('Invalid MIME type: %s', $value)
);
}
Expand All @@ -135,14 +137,14 @@ public function __construct(string $value)
*
* @param string $extension The file extension (without the dot).
* @return self The MimeType instance.
* @throws \InvalidArgumentException If the extension is not recognized.
* @throws InvalidArgumentException If the extension is not recognized.
*/
public static function fromExtension(string $extension): self
{
$extension = strtolower($extension);

if (!isset(self::$extensionMap[$extension])) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf('Unknown file extension: %s', $extension)
);
}
Expand Down Expand Up @@ -245,7 +247,7 @@ public function equals($other): bool
return $this->value === strtolower($other);
}

throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf('Invalid MIME type comparison: %s', gettype($other))
);
}
Expand Down
158 changes: 158 additions & 0 deletions src/Providers/DTO/ProviderMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

declare(strict_types=1);

namespace WordPress\AiClient\Providers\DTO;

use WordPress\AiClient\Common\AbstractDataValueObject;
use WordPress\AiClient\Providers\Enums\ProviderTypeEnum;

/**
* Represents metadata about an AI provider.
*
* This class contains information about an AI provider, including its
* unique identifier, display name, and type (cloud, server, or client).
*
* @since n.e.x.t
*
* @phpstan-type ProviderMetadataArrayShape array{
* id: string,
* name: string,
* type: string
* }
*
* @extends AbstractDataValueObject<ProviderMetadataArrayShape>
*/
class ProviderMetadata extends AbstractDataValueObject
{
public const KEY_ID = 'id';
public const KEY_NAME = 'name';
public const KEY_TYPE = 'type';

/**
* @var string The provider's unique identifier.
*/
protected string $id;

/**
* @var string The provider's display name.
*/
protected string $name;

/**
* @var ProviderTypeEnum The provider type.
*/
protected ProviderTypeEnum $type;

/**
* Constructor.
*
* @since n.e.x.t
*
* @param string $id The provider's unique identifier.
* @param string $name The provider's display name.
* @param ProviderTypeEnum $type The provider type.
*/
public function __construct(string $id, string $name, ProviderTypeEnum $type)
{
$this->id = $id;
$this->name = $name;
$this->type = $type;
}

/**
* Gets the provider's unique identifier.
*
* @since n.e.x.t
*
* @return string The provider ID.
*/
public function getId(): string
{
return $this->id;
}

/**
* Gets the provider's display name.
*
* @since n.e.x.t
*
* @return string The provider name.
*/
public function getName(): string
{
return $this->name;
}

/**
* Gets the provider type.
*
* @since n.e.x.t
*
* @return ProviderTypeEnum The provider type.
*/
public function getType(): ProviderTypeEnum
{
return $this->type;
}

/**
* {@inheritDoc}
*
* @since n.e.x.t
*/
public static function getJsonSchema(): array
{
return [
'type' => 'object',
'properties' => [
self::KEY_ID => [
'type' => 'string',
'description' => 'The provider\'s unique identifier.',
],
self::KEY_NAME => [
'type' => 'string',
'description' => 'The provider\'s display name.',
],
self::KEY_TYPE => [
'type' => 'string',
'enum' => ProviderTypeEnum::getValues(),
'description' => 'The provider type (cloud, server, or client).',
],
],
'required' => [self::KEY_ID, self::KEY_NAME, self::KEY_TYPE],
];
}

/**
* {@inheritDoc}
*
* @since n.e.x.t
*
* @return ProviderMetadataArrayShape
*/
public function toArray(): array
{
return [
self::KEY_ID => $this->id,
self::KEY_NAME => $this->name,
self::KEY_TYPE => $this->type->value,
];
}

/**
* {@inheritDoc}
*
* @since n.e.x.t
*/
public static function fromArray(array $array): self
{
static::validateFromArrayData($array, [self::KEY_ID, self::KEY_NAME, self::KEY_TYPE]);

return new self(
$array[self::KEY_ID],
$array[self::KEY_NAME],
ProviderTypeEnum::from($array[self::KEY_TYPE])
);
}
}
Loading