Skip to content

Commit 5fe7df9

Browse files
author
Mohamed Khaled
committed
Restore missing ModelRequirements methods from trunk
1 parent 34fec79 commit 5fe7df9

File tree

1 file changed

+289
-2
lines changed

1 file changed

+289
-2
lines changed

src/Providers/Models/DTO/ModelRequirements.php

Lines changed: 289 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
namespace WordPress\AiClient\Providers\Models\DTO;
66

7+
use InvalidArgumentException;
78
use WordPress\AiClient\Common\AbstractDataTransferObject;
8-
use WordPress\AiClient\Common\Exception\InvalidArgumentException;
9+
use WordPress\AiClient\Messages\DTO\Message;
10+
use WordPress\AiClient\Messages\Enums\ModalityEnum;
911
use WordPress\AiClient\Providers\Models\Enums\CapabilityEnum;
12+
use WordPress\AiClient\Providers\Models\Enums\OptionEnum;
1013

1114
/**
1215
* Represents requirements that implementing code has for AI model selection.
@@ -88,6 +91,290 @@ public function getRequiredOptions(): array
8891
return $this->requiredOptions;
8992
}
9093

94+
/**
95+
* Checks whether the given model metadata meets these requirements.
96+
*
97+
* @since n.e.x.t
98+
*
99+
* @param ModelMetadata $metadata The model metadata to check against.
100+
* @return bool True if the model meets all requirements, false otherwise.
101+
*/
102+
public function areMetBy(ModelMetadata $metadata): bool
103+
{
104+
// Create lookup maps for better performance (instead of nested foreach loops)
105+
$capabilitiesMap = [];
106+
foreach ($metadata->getSupportedCapabilities() as $capability) {
107+
$capabilitiesMap[$capability->value] = $capability;
108+
}
109+
110+
$optionsMap = [];
111+
foreach ($metadata->getSupportedOptions() as $option) {
112+
$optionsMap[$option->getName()->value] = $option;
113+
}
114+
115+
// Check if all required capabilities are supported using map lookup
116+
foreach ($this->requiredCapabilities as $requiredCapability) {
117+
if (!isset($capabilitiesMap[$requiredCapability->value])) {
118+
return false;
119+
}
120+
}
121+
122+
// Check if all required options are supported with the specified values
123+
foreach ($this->requiredOptions as $requiredOption) {
124+
// Use map lookup instead of linear search
125+
if (!isset($optionsMap[$requiredOption->getName()->value])) {
126+
return false;
127+
}
128+
129+
$supportedOption = $optionsMap[$requiredOption->getName()->value];
130+
131+
// Check if the required value is supported by this option
132+
if (!$supportedOption->isSupportedValue($requiredOption->getValue())) {
133+
return false;
134+
}
135+
}
136+
137+
return true;
138+
}
139+
140+
/**
141+
* Creates ModelRequirements from prompt data and model configuration.
142+
*
143+
* @since n.e.x.t
144+
*
145+
* @param CapabilityEnum $capability The capability the model must support.
146+
* @param list<Message> $messages The messages in the conversation.
147+
* @param ModelConfig $modelConfig The model configuration.
148+
* @return self The created requirements.
149+
*/
150+
public static function fromPromptData(CapabilityEnum $capability, array $messages, ModelConfig $modelConfig): self
151+
{
152+
// Start with base capability
153+
$capabilities = [$capability];
154+
$inputModalities = [];
155+
156+
// Check if we have chat history (multiple messages)
157+
if (count($messages) > 1) {
158+
$capabilities[] = CapabilityEnum::chatHistory();
159+
}
160+
161+
// Analyze all messages to determine required input modalities
162+
$hasFunctionMessageParts = false;
163+
foreach ($messages as $message) {
164+
foreach ($message->getParts() as $part) {
165+
// Check for text input
166+
if ($part->getType()->isText()) {
167+
$inputModalities[] = ModalityEnum::text();
168+
}
169+
170+
// Check for file inputs
171+
if ($part->getType()->isFile()) {
172+
$file = $part->getFile();
173+
174+
if ($file !== null) {
175+
if ($file->isImage()) {
176+
$inputModalities[] = ModalityEnum::image();
177+
} elseif ($file->isAudio()) {
178+
$inputModalities[] = ModalityEnum::audio();
179+
} elseif ($file->isVideo()) {
180+
$inputModalities[] = ModalityEnum::video();
181+
} elseif ($file->isDocument() || $file->isText()) {
182+
$inputModalities[] = ModalityEnum::document();
183+
}
184+
}
185+
}
186+
187+
// Check for function calls/responses (these might require special capabilities)
188+
if ($part->getType()->isFunctionCall() || $part->getType()->isFunctionResponse()) {
189+
$hasFunctionMessageParts = true;
190+
}
191+
}
192+
}
193+
194+
// Convert ModelConfig to RequiredOptions
195+
$requiredOptions = self::toRequiredOptions($modelConfig);
196+
197+
// Add additional options based on message analysis
198+
if ($hasFunctionMessageParts) {
199+
$requiredOptions = self::includeInRequiredOptions(
200+
$requiredOptions,
201+
new RequiredOption(OptionEnum::functionDeclarations(), true)
202+
);
203+
}
204+
205+
// Add input modalities if we have any inputs
206+
if (!empty($inputModalities)) {
207+
// Remove duplicates
208+
$inputModalities = array_unique($inputModalities, SORT_REGULAR);
209+
$requiredOptions = self::includeInRequiredOptions(
210+
$requiredOptions,
211+
new RequiredOption(OptionEnum::inputModalities(), array_values($inputModalities))
212+
);
213+
}
214+
215+
// Step 6: Return new ModelRequirements
216+
return new self($capabilities, $requiredOptions);
217+
}
218+
219+
/**
220+
* Converts ModelConfig to an array of RequiredOptions.
221+
*
222+
* @since n.e.x.t
223+
*
224+
* @param ModelConfig $modelConfig The model configuration.
225+
* @return list<RequiredOption> The required options.
226+
*/
227+
private static function toRequiredOptions(ModelConfig $modelConfig): array
228+
{
229+
$requiredOptions = [];
230+
231+
// Map properties that have corresponding OptionEnum values
232+
if ($modelConfig->getOutputModalities() !== null) {
233+
$requiredOptions[] = new RequiredOption(
234+
OptionEnum::outputModalities(),
235+
$modelConfig->getOutputModalities()
236+
);
237+
}
238+
239+
if ($modelConfig->getSystemInstruction() !== null) {
240+
$requiredOptions[] = new RequiredOption(
241+
OptionEnum::systemInstruction(),
242+
$modelConfig->getSystemInstruction()
243+
);
244+
}
245+
246+
if ($modelConfig->getCandidateCount() !== null) {
247+
$requiredOptions[] = new RequiredOption(
248+
OptionEnum::candidateCount(),
249+
$modelConfig->getCandidateCount()
250+
);
251+
}
252+
253+
if ($modelConfig->getMaxTokens() !== null) {
254+
$requiredOptions[] = new RequiredOption(
255+
OptionEnum::maxTokens(),
256+
$modelConfig->getMaxTokens()
257+
);
258+
}
259+
260+
if ($modelConfig->getTemperature() !== null) {
261+
$requiredOptions[] = new RequiredOption(
262+
OptionEnum::temperature(),
263+
$modelConfig->getTemperature()
264+
);
265+
}
266+
267+
if ($modelConfig->getTopP() !== null) {
268+
$requiredOptions[] = new RequiredOption(
269+
OptionEnum::topP(),
270+
$modelConfig->getTopP()
271+
);
272+
}
273+
274+
if ($modelConfig->getTopK() !== null) {
275+
$requiredOptions[] = new RequiredOption(
276+
OptionEnum::topK(),
277+
$modelConfig->getTopK()
278+
);
279+
}
280+
281+
if ($modelConfig->getOutputMimeType() !== null) {
282+
$requiredOptions[] = new RequiredOption(
283+
OptionEnum::outputMimeType(),
284+
$modelConfig->getOutputMimeType()
285+
);
286+
}
287+
288+
if ($modelConfig->getOutputSchema() !== null) {
289+
$requiredOptions[] = new RequiredOption(
290+
OptionEnum::outputSchema(),
291+
$modelConfig->getOutputSchema()
292+
);
293+
}
294+
295+
// Handle properties without OptionEnum values as custom options
296+
if ($modelConfig->getStopSequences() !== null) {
297+
$requiredOptions[] = new RequiredOption(OptionEnum::stopSequences(), $modelConfig->getStopSequences());
298+
}
299+
300+
if ($modelConfig->getPresencePenalty() !== null) {
301+
$requiredOptions[] = new RequiredOption(OptionEnum::presencePenalty(), $modelConfig->getPresencePenalty());
302+
}
303+
304+
if ($modelConfig->getFrequencyPenalty() !== null) {
305+
$requiredOptions[] = new RequiredOption(
306+
OptionEnum::frequencyPenalty(),
307+
$modelConfig->getFrequencyPenalty()
308+
);
309+
}
310+
311+
if ($modelConfig->getLogprobs() !== null) {
312+
$requiredOptions[] = new RequiredOption(OptionEnum::logprobs(), $modelConfig->getLogprobs());
313+
}
314+
315+
if ($modelConfig->getTopLogprobs() !== null) {
316+
$requiredOptions[] = new RequiredOption(OptionEnum::topLogprobs(), $modelConfig->getTopLogprobs());
317+
}
318+
319+
if ($modelConfig->getFunctionDeclarations() !== null) {
320+
$requiredOptions[] = new RequiredOption(OptionEnum::functionDeclarations(), true);
321+
}
322+
323+
if ($modelConfig->getWebSearch() !== null) {
324+
$requiredOptions[] = new RequiredOption(OptionEnum::webSearch(), true);
325+
}
326+
327+
if ($modelConfig->getOutputFileType() !== null) {
328+
$requiredOptions[] = new RequiredOption(OptionEnum::outputFileType(), $modelConfig->getOutputFileType());
329+
}
330+
331+
if ($modelConfig->getOutputMediaOrientation() !== null) {
332+
$requiredOptions[] = new RequiredOption(
333+
OptionEnum::outputMediaOrientation(),
334+
$modelConfig->getOutputMediaOrientation()
335+
);
336+
}
337+
338+
if ($modelConfig->getOutputMediaAspectRatio() !== null) {
339+
$requiredOptions[] = new RequiredOption(
340+
OptionEnum::outputMediaAspectRatio(),
341+
$modelConfig->getOutputMediaAspectRatio()
342+
);
343+
}
344+
345+
// Add custom options as individual RequiredOptions
346+
foreach ($modelConfig->getCustomOptions() as $key => $value) {
347+
$requiredOptions[] = new RequiredOption(OptionEnum::customOptions(), [$key => $value]);
348+
}
349+
350+
return $requiredOptions;
351+
}
352+
353+
/**
354+
* Includes a RequiredOption in the array, ensuring no duplicates based on option name.
355+
*
356+
* @since n.e.x.t
357+
*
358+
* @param list<RequiredOption> $requiredOptions The existing required options.
359+
* @param RequiredOption $newOption The new option to include.
360+
* @return list<RequiredOption> The updated required options array.
361+
*/
362+
private static function includeInRequiredOptions(array $requiredOptions, RequiredOption $newOption): array
363+
{
364+
// Check if we already have this option name
365+
foreach ($requiredOptions as $index => $existingOption) {
366+
if ($existingOption->getName()->equals($newOption->getName())) {
367+
// Replace existing option with new one
368+
$requiredOptions[$index] = $newOption;
369+
return $requiredOptions;
370+
}
371+
}
372+
373+
// Option not found, add it
374+
$requiredOptions[] = $newOption;
375+
return $requiredOptions;
376+
}
377+
91378
/**
92379
* {@inheritDoc}
93380
*
@@ -157,4 +444,4 @@ public static function fromArray(array $array): self
157444
)
158445
);
159446
}
160-
}
447+
}

0 commit comments

Comments
 (0)