Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
92 changes: 2 additions & 90 deletions src/Builders/PromptBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
use WordPress\AiClient\Providers\Models\Contracts\ModelInterface;
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;
use WordPress\AiClient\Providers\Models\DTO\ModelRequirements;
use WordPress\AiClient\Providers\Models\DTO\RequiredOption;
use WordPress\AiClient\Providers\Models\Enums\CapabilityEnum;
use WordPress\AiClient\Providers\Models\Enums\OptionEnum;
use WordPress\AiClient\Providers\Models\ImageGeneration\Contracts\ImageGenerationModelInterface;
use WordPress\AiClient\Providers\Models\SpeechGeneration\Contracts\SpeechGenerationModelInterface;
use WordPress\AiClient\Providers\Models\TextGeneration\Contracts\TextGenerationModelInterface;
use WordPress\AiClient\Providers\Models\TextToSpeechConversion\Contracts\TextToSpeechConversionModelInterface;
use WordPress\AiClient\Providers\ProviderRegistry;
use WordPress\AiClient\Results\DTO\GenerativeAiResult;
use WordPress\AiClient\Tools\DTO\FunctionResponse;
use WordPress\AiClient\Utils\RequirementsUtil;

/**
* Fluent builder for constructing AI prompts.
Expand Down Expand Up @@ -387,68 +386,7 @@ public function asJsonResponse(?array $schema = null): self
*/
private function getModelRequirements(CapabilityEnum $capability): ModelRequirements
{
$capabilities = [$capability];
$inputModalities = [];

// Check if we have chat history (multiple messages)
if (count($this->messages) > 1) {
$capabilities[] = CapabilityEnum::chatHistory();
}

// Analyze all messages to determine required input modalities
$hasFunctionMessageParts = false;
foreach ($this->messages as $message) {
foreach ($message->getParts() as $part) {
// Check for text input
if ($part->getType()->isText()) {
$inputModalities[] = ModalityEnum::text();
}

// Check for file inputs
if ($part->getType()->isFile()) {
$file = $part->getFile();

if ($file !== null) {
if ($file->isImage()) {
$inputModalities[] = ModalityEnum::image();
} elseif ($file->isAudio()) {
$inputModalities[] = ModalityEnum::audio();
} elseif ($file->isVideo()) {
$inputModalities[] = ModalityEnum::video();
} elseif ($file->isDocument() || $file->isText()) {
$inputModalities[] = ModalityEnum::document();
}
}
}

// Check for function calls/responses (these might require special capabilities)
if ($part->getType()->isFunctionCall() || $part->getType()->isFunctionResponse()) {
$hasFunctionMessageParts = true;
}
}
}

// Build required options from ModelConfig
$requiredOptions = $this->modelConfig->toRequiredOptions();

if ($hasFunctionMessageParts) {
// Add function declarations option if we have function calls/responses
$requiredOptions = $this->includeInRequiredOptions(
$requiredOptions,
new RequiredOption(OptionEnum::functionDeclarations(), true)
);
}

// Add input modalities if we have any inputs
$requiredOptions = $this->includeInRequiredOptions(
$requiredOptions,
new RequiredOption(OptionEnum::inputModalities(), $inputModalities)
);

return new ModelRequirements(
$capabilities,
$requiredOptions
);
return RequirementsUtil::fromMessages($this->messages, $capability, $this->modelConfig);
}

/**
Expand Down Expand Up @@ -1148,32 +1086,6 @@ private function isMessagesList($value): bool
return true;
}

/**
* Includes a required option in the list if not already present.
*
* Checks if a RequiredOption with the same name already exists in the list.
* If not, adds the new option. Returns the updated list.
*
* @since n.e.x.t
*
* @param list<RequiredOption> $options The existing list of required options.
* @param RequiredOption $option The option to potentially add.
* @return list<RequiredOption> The updated list of required options.
*/
private function includeInRequiredOptions(array $options, RequiredOption $option): array
{
// Check if an option with the same name already exists
foreach ($options as $existingOption) {
if ($existingOption->getName()->equals($option->getName())) {
// Option already exists, return unchanged list
return $options;
}
}

// Add the new option
$options[] = $option;
return $options;
}

/**
* Includes output modalities if not already present.
Expand Down
235 changes: 235 additions & 0 deletions src/Utils/CapabilityUtil.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
<?php

declare(strict_types=1);

namespace WordPress\AiClient\Utils;

use WordPress\AiClient\Messages\Enums\ModalityEnum;
use WordPress\AiClient\Providers\Models\Enums\CapabilityEnum;

/**
* Utility class for working with AI capabilities and modalities.
*
* This class provides helper methods for mapping between different capability
* types, determining compatibility, and working with input/output modalities.
*
* @since n.e.x.t
*/
class CapabilityUtil
{
/**
* Maps generation types to their corresponding capabilities.
*
* @since n.e.x.t
*
* @param string $generationType The generation type (e.g., 'text', 'image', 'speech').
* @return CapabilityEnum|null The corresponding capability or null if not found.
*/
public static function getCapabilityForGenerationType(string $generationType): ?CapabilityEnum
{
switch (strtolower($generationType)) {
case 'text':
return CapabilityEnum::textGeneration();
case 'image':
return CapabilityEnum::imageGeneration();
case 'speech':
return CapabilityEnum::speechGeneration();
case 'text-to-speech':
case 'tts':
return CapabilityEnum::textToSpeechConversion();
case 'music':
return CapabilityEnum::musicGeneration();
case 'video':
return CapabilityEnum::videoGeneration();
case 'embedding':
case 'embeddings':
return CapabilityEnum::embeddingGeneration();
default:
return null;
}
}

/**
* Gets the primary output modality for a capability.
*
* @since n.e.x.t
*
* @param CapabilityEnum $capability The capability.
* @return ModalityEnum|null The primary output modality or null if not applicable.
*/
public static function getPrimaryOutputModality(CapabilityEnum $capability): ?ModalityEnum
{
if ($capability->isTextGeneration()) {
return ModalityEnum::text();
}

if ($capability->isImageGeneration()) {
return ModalityEnum::image();
}

if ($capability->isSpeechGeneration() || $capability->isTextToSpeechConversion()) {
return ModalityEnum::audio();
}

if ($capability->isMusicGeneration()) {
return ModalityEnum::audio();
}

if ($capability->isVideoGeneration()) {
return ModalityEnum::video();
}

// Embedding generation doesn't have a traditional modality
return null;
}

/**
* Determines if a capability requires specific input modalities.
*
* @since n.e.x.t
*
* @param CapabilityEnum $capability The capability to check.
* @return list<ModalityEnum> Default input modalities for the capability.
*/
public static function getDefaultInputModalities(CapabilityEnum $capability): array
{
// Most generation types primarily use text input for prompts
if (
$capability->isTextGeneration() ||
$capability->isImageGeneration() ||
$capability->isSpeechGeneration() ||
$capability->isMusicGeneration() ||
$capability->isVideoGeneration()
) {
return [ModalityEnum::text()];
}

// Text-to-speech typically uses text input
if ($capability->isTextToSpeechConversion()) {
return [ModalityEnum::text()];
}

// Embedding generation can handle various inputs
if ($capability->isEmbeddingGeneration()) {
return [ModalityEnum::text()];
}

return [];
}

/**
* Checks if two capabilities are compatible for the same operation.
*
* @since n.e.x.t
*
* @param CapabilityEnum $capability1 First capability.
* @param CapabilityEnum $capability2 Second capability.
* @return bool True if compatible, false otherwise.
*/
public static function areCompatible(CapabilityEnum $capability1, CapabilityEnum $capability2): bool
{
// Same capability is always compatible
if ($capability1->equals($capability2)) {
return true;
}

// Chat history is compatible with most generation types
if ($capability1->isChatHistory() || $capability2->isChatHistory()) {
return true;
}

// Different generation types are generally not compatible
$generationCapabilities = [
$capability1->isTextGeneration(),
$capability1->isImageGeneration(),
$capability1->isSpeechGeneration(),
$capability1->isTextToSpeechConversion(),
$capability1->isMusicGeneration(),
$capability1->isVideoGeneration(),
$capability1->isEmbeddingGeneration(),
];

$capability2Generations = [
$capability2->isTextGeneration(),
$capability2->isImageGeneration(),
$capability2->isSpeechGeneration(),
$capability2->isTextToSpeechConversion(),
$capability2->isMusicGeneration(),
$capability2->isVideoGeneration(),
$capability2->isEmbeddingGeneration(),
];

// If both are generation types, they're not compatible
if (in_array(true, $generationCapabilities, true) && in_array(true, $capability2Generations, true)) {
return false;
}

return true;
}

/**
* Gets all generation-type capabilities.
*
* @since n.e.x.t
*
* @return list<CapabilityEnum> All generation capabilities.
*/
public static function getAllGenerationCapabilities(): array
{
return [
CapabilityEnum::textGeneration(),
CapabilityEnum::imageGeneration(),
CapabilityEnum::speechGeneration(),
CapabilityEnum::textToSpeechConversion(),
CapabilityEnum::musicGeneration(),
CapabilityEnum::videoGeneration(),
CapabilityEnum::embeddingGeneration(),
];
}

/**
* Determines if a capability produces file output.
*
* @since n.e.x.t
*
* @param CapabilityEnum $capability The capability to check.
* @return bool True if the capability produces file output.
*/
public static function producesFileOutput(CapabilityEnum $capability): bool
{
return $capability->isImageGeneration() ||
$capability->isSpeechGeneration() ||
$capability->isTextToSpeechConversion() ||
$capability->isMusicGeneration() ||
$capability->isVideoGeneration();
}

/**
* Gets suggested file extensions for a capability's output.
*
* @since n.e.x.t
*
* @param CapabilityEnum $capability The capability.
* @return list<string> Suggested file extensions (without dots).
*/
public static function getSuggestedFileExtensions(CapabilityEnum $capability): array
{
if ($capability->isImageGeneration()) {
return ['png', 'jpg', 'jpeg', 'webp'];
}

if ($capability->isSpeechGeneration() || $capability->isTextToSpeechConversion()) {
return ['mp3', 'wav', 'ogg'];
}

if ($capability->isMusicGeneration()) {
return ['mp3', 'wav', 'midi'];
}

if ($capability->isVideoGeneration()) {
return ['mp4', 'webm', 'mov'];
}

return [];
}
}
Loading