[Platform][Generic]  Fix FallbackModelCatalog

This commit is contained in:
Fabien Potencier
2026-02-26 19:55:26 +01:00
committed by Christopher Hertel
parent f2a6f0bb1d
commit 38fa438be9
3 changed files with 135 additions and 1 deletions

46
FallbackModelCatalog.php Normal file
View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\AI\Platform\Bridge\Generic;
use Symfony\AI\Platform\Capability;
use Symfony\AI\Platform\Model;
use Symfony\AI\Platform\ModelCatalog\AbstractModelCatalog;
/**
* A fallback model catalog for the Generic bridge that creates the
* appropriate model subclass based on the model name.
*
* The Generic bridge requires models to be instances of CompletionsModel
* or EmbeddingsModel for the corresponding ModelClient to accept them.
* This catalog uses a naming convention (model name contains "embed")
* to determine the correct type when no explicit catalog is provided.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FallbackModelCatalog extends AbstractModelCatalog
{
public function __construct()
{
$this->models = [];
}
public function getModel(string $modelName): Model
{
$parsed = self::parseModelName($modelName);
if (str_contains(strtolower($parsed['name']), 'embed')) {
return new EmbeddingsModel($parsed['name'], Capability::cases(), $parsed['options']);
}
return new CompletionsModel($parsed['name'], Capability::cases(), $parsed['options']);
}
}

View File

@@ -13,7 +13,6 @@ namespace Symfony\AI\Platform\Bridge\Generic;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\AI\Platform\Contract;
use Symfony\AI\Platform\ModelCatalog\FallbackModelCatalog;
use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface;
use Symfony\AI\Platform\Platform;
use Symfony\Component\HttpClient\EventSourceHttpClient;

View File

@@ -0,0 +1,89 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\AI\Platform\Bridge\Generic\Tests;
use PHPUnit\Framework\Attributes\TestWith;
use PHPUnit\Framework\TestCase;
use Symfony\AI\Platform\Bridge\Generic\CompletionsModel;
use Symfony\AI\Platform\Bridge\Generic\EmbeddingsModel;
use Symfony\AI\Platform\Bridge\Generic\FallbackModelCatalog;
use Symfony\AI\Platform\Capability;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
final class FallbackModelCatalogTest extends TestCase
{
#[TestWith(['gpt-4o'])]
#[TestWith(['claude-3-5-sonnet-20241022'])]
#[TestWith(['mistral-large'])]
#[TestWith(['deepseek-chat'])]
public function testCompletionsModel(string $modelName)
{
$catalog = new FallbackModelCatalog();
$model = $catalog->getModel($modelName);
$this->assertInstanceOf(CompletionsModel::class, $model);
$this->assertSame($modelName, $model->getName());
}
#[TestWith(['text-embedding-3-small'])]
#[TestWith(['text-embedding-ada-002'])]
#[TestWith(['text-embedding-004'])]
#[TestWith(['gemini-embedding-001'])]
public function testEmbeddingsModel(string $modelName)
{
$catalog = new FallbackModelCatalog();
$model = $catalog->getModel($modelName);
$this->assertInstanceOf(EmbeddingsModel::class, $model);
$this->assertSame($modelName, $model->getName());
}
public function testAllCapabilitiesArePresent()
{
$catalog = new FallbackModelCatalog();
$model = $catalog->getModel('test-model');
foreach (Capability::cases() as $capability) {
$this->assertTrue($model->supports($capability), \sprintf('Model should have capability %s', $capability->value));
}
}
public function testCompletionsModelWithOptions()
{
$catalog = new FallbackModelCatalog();
$model = $catalog->getModel('gpt-4o?temperature=0.7&max_tokens=1000');
$this->assertInstanceOf(CompletionsModel::class, $model);
$this->assertSame('gpt-4o', $model->getName());
$this->assertSame(0.7, $model->getOptions()['temperature']);
$this->assertSame(1000, $model->getOptions()['max_tokens']);
}
public function testEmbeddingsModelWithOptions()
{
$catalog = new FallbackModelCatalog();
$model = $catalog->getModel('text-embedding-3-small?dimensions=256');
$this->assertInstanceOf(EmbeddingsModel::class, $model);
$this->assertSame('text-embedding-3-small', $model->getName());
$this->assertSame(256, $model->getOptions()['dimensions']);
}
public function testGetModelsReturnsEmptyArray()
{
$catalog = new FallbackModelCatalog();
$this->assertSame([], $catalog->getModels());
}
}