Skip to content
Merged
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
1 change: 1 addition & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ direction LR
+convertTextToSpeeches(?int $candidateCount) File[]
+generateSpeech() File
+generateSpeeches(?int $candidateCount) File[]
+isSupported(?CapabilityEnum $capability) bool
+isSupportedForTextGeneration() bool
+isSupportedForImageGeneration() bool
+isSupportedForTextToSpeechConversion() bool
Expand Down
26 changes: 19 additions & 7 deletions src/Builders/PromptBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ public function usingWebSearch(WebSearch $webSearch): self
/**
* Sets the request options for HTTP transport.
*
* @since n.e.x.t
* @since 0.3.0
*
* @param RequestOptions $requestOptions The request options.
* @return self
Expand Down Expand Up @@ -676,19 +676,31 @@ private function inferCapabilityFromModelInterfaces(ModelInterface $model): ?Cap
* Checks if the current prompt is supported by the selected model.
*
* @since 0.1.0
* @since 0.3.0 Method visibility changed to public.
*
* @param CapabilityEnum|null $intendedCapability Optional capability to check support for.
* @param CapabilityEnum|null $capability Optional capability to check support for.
* @return bool True if supported, false otherwise.
*/
private function isSupported(?CapabilityEnum $intendedCapability = null): bool
public function isSupported(?CapabilityEnum $capability = null): bool
{
// If no intended capability provided, infer from output modalities
if ($intendedCapability === null) {
$intendedCapability = $this->inferCapabilityFromOutputModalities();
if ($capability === null) {
// First try to infer from a specific model if one is set
if ($this->model !== null) {
$inferredCapability = $this->inferCapabilityFromModelInterfaces($this->model);
if ($inferredCapability !== null) {
$capability = $inferredCapability;
}
}

// If still no capability, infer from output modalities
if ($capability === null) {
$capability = $this->inferCapabilityFromOutputModalities();
}
}

// Build requirements with the specified capability
$requirements = ModelRequirements::fromPromptData($intendedCapability, $this->messages, $this->modelConfig);
$requirements = ModelRequirements::fromPromptData($capability, $this->messages, $this->modelConfig);

// If the model has been set, check if it meets the requirements
if ($this->model !== null) {
Expand Down Expand Up @@ -1192,7 +1204,7 @@ private function getConfiguredModel(CapabilityEnum $capability): ModelInterface
*
* Request options are only applicable to API-based models that make HTTP requests.
*
* @since n.e.x.t
* @since 0.3.0
*
* @param ModelInterface $model The model to bind request options to.
* @return void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ final public function getConfig(): ModelConfig
/**
* {@inheritDoc}
*
* @since n.e.x.t
* @since 0.3.0
*/
final public function setRequestOptions(RequestOptions $requestOptions): void
{
Expand All @@ -118,7 +118,7 @@ final public function setRequestOptions(RequestOptions $requestOptions): void
/**
* {@inheritDoc}
*
* @since n.e.x.t
* @since 0.3.0
*/
final public function getRequestOptions(): ?RequestOptions
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@
* This interface extends ModelInterface to add request options support
* for models that communicate with external APIs via HTTP.
*
* @since n.e.x.t
* @since 0.3.0
*/
interface ApiBasedModelInterface extends ModelInterface
{
/**
* Sets the request options for HTTP transport.
*
* @since n.e.x.t
* @since 0.3.0
*
* @param RequestOptions $requestOptions The request options to use.
* @return void
Expand All @@ -30,7 +30,7 @@ public function setRequestOptions(RequestOptions $requestOptions): void;
/**
* Gets the request options for HTTP transport.
*
* @since n.e.x.t
* @since 0.3.0
*
* @return RequestOptions|null The request options, or null if not set.
*/
Expand Down
119 changes: 119 additions & 0 deletions tests/unit/Builders/PromptBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3390,4 +3390,123 @@ public function testMethodChainingWithNewMethods(): void
$this->assertTrue($config->getLogprobs());
$this->assertEquals(3, $config->getTopLogprobs());
}

/**
* Tests isSupported method with explicit capability.
*
* @return void
*/
public function testIsSupportedWithExplicitCapability(): void
{
$builder = new PromptBuilder($this->registry, 'Test prompt');

// Mock registry to return a model supporting text generation
$this->registry->method('findModelsMetadataForSupport')
->willReturn([$this->createMock(ProviderModelsMetadata::class)]);

$this->assertTrue($builder->isSupported(CapabilityEnum::textGeneration()));
}

/**
* Tests isSupported method with inferred capability from output modalities.
*
* @return void
*/
public function testIsSupportedWithInferredCapability(): void
{
$builder = new PromptBuilder($this->registry, 'Test prompt');
$builder->asOutputModalities(ModalityEnum::image());

// Mock registry to return a model supporting image generation
$this->registry->method('findModelsMetadataForSupport')
->willReturn([$this->createMock(ProviderModelsMetadata::class)]);

// Should infer image generation capability
$this->assertTrue($builder->isSupported());
}

/**
* Tests isSupported method with inferred capability from model interfaces.
*
* @return void
*/
public function testIsSupportedWithInferredCapabilityFromModel(): void
{
$metadata = $this->createMock(ModelMetadata::class);
$metadata->method('getId')->willReturn('test-model');
$metadata->method('getSupportedCapabilities')->willReturn([
CapabilityEnum::textGeneration()
]);
$metadata->method('getSupportedOptions')->willReturn([
new SupportedOption(OptionEnum::inputModalities(), [
[ModalityEnum::text()]
])
]);

$result = new GenerativeAiResult('test-id', [
new Candidate(
new ModelMessage([new MessagePart('Test')]),
FinishReasonEnum::stop()
)
], new TokenUsage(10, 5, 15), $this->createTestProviderMetadata(), $this->createTestTextModelMetadata());

$model = $this->createMockTextGenerationModel($result, $metadata);

$builder = new PromptBuilder($this->registry, 'Test prompt');
$builder->usingModel($model);

// Should infer text generation capability from the model interface
$this->assertTrue($builder->isSupported());
}

/**
* Tests isSupported method when a model is explicitly set.
*
* @return void
*/
public function testIsSupportedWithModelSet(): void
{
$metadata = $this->createMock(ModelMetadata::class);
$metadata->method('getId')->willReturn('text-model');
$metadata->method('getSupportedCapabilities')->willReturn([
CapabilityEnum::textGeneration()
]);
// Mock getSupportedOptions to return required options
$metadata->method('getSupportedOptions')->willReturn([
new SupportedOption(OptionEnum::inputModalities(), [
[ModalityEnum::text()]
])
]);

$result = new GenerativeAiResult('test-id', [
new Candidate(
new ModelMessage([new MessagePart('Test')]),
FinishReasonEnum::stop()
)
], new TokenUsage(10, 5, 15), $this->createTestProviderMetadata(), $this->createTestTextModelMetadata());

$model = $this->createMockTextGenerationModel($result, $metadata);

$builder = new PromptBuilder($this->registry, 'Test prompt');
$builder->usingModel($model);

$this->assertTrue($builder->isSupported(CapabilityEnum::textGeneration()));
$this->assertFalse($builder->isSupported(CapabilityEnum::imageGeneration()));
}

/**
* Tests isSupported method when no models support the requirements.
*
* @return void
*/
public function testIsSupportedWithNoSupport(): void
{
$builder = new PromptBuilder($this->registry, 'Test prompt');

// Mock registry to return no models
$this->registry->method('findModelsMetadataForSupport')
->willReturn([]);

$this->assertFalse($builder->isSupported(CapabilityEnum::textGeneration()));
}
}