From 572d30b63fb23eec3b43363c6e0f671b42e597ca Mon Sep 17 00:00:00 2001 From: Guillaume Loulier Date: Thu, 20 Nov 2025 10:10:11 +0100 Subject: [PATCH] [Platform] `OllamaApiCatalog` improved --- .../src/Bridge/Ollama/OllamaApiCatalog.php | 45 ++++++++++--------- .../Bridge/Ollama/OllamaApiCatalogTest.php | 33 ++++++++++++++ 2 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/platform/src/Bridge/Ollama/OllamaApiCatalog.php b/src/platform/src/Bridge/Ollama/OllamaApiCatalog.php index 9f5c0d709..e40f51cba 100644 --- a/src/platform/src/Bridge/Ollama/OllamaApiCatalog.php +++ b/src/platform/src/Bridge/Ollama/OllamaApiCatalog.php @@ -13,38 +13,25 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\InvalidArgumentException; -use Symfony\AI\Platform\ModelCatalog\FallbackModelCatalog; +use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** * @author Guillaume Loulier */ -final class OllamaApiCatalog extends FallbackModelCatalog +final class OllamaApiCatalog implements ModelCatalogInterface { public function __construct( private readonly string $host, private readonly HttpClientInterface $httpClient, ) { - parent::__construct(); } public function getModel(string $modelName): Ollama { - $model = parent::getModel($modelName); - - if (\array_key_exists($model->getName(), $this->models)) { - $finalModel = $this->models[$model->getName()]; - - return new $finalModel['class']( - $model->getName(), - $finalModel['capabilities'], - $model->getOptions(), - ); - } - $response = $this->httpClient->request('POST', \sprintf('%s/api/show', $this->host), [ 'json' => [ - 'model' => $model->getName(), + 'model' => $modelName, ], ]); @@ -66,13 +53,27 @@ public function getModel(string $modelName): Ollama $payload['capabilities'], ); - $finalModel = new Ollama($model->getName(), $capabilities, $model->getOptions()); + return new Ollama($modelName, $capabilities); + } - $this->models[$finalModel->getName()] = [ - 'class' => Ollama::class, - 'capabilities' => $finalModel->getCapabilities(), - ]; + public function getModels(): array + { + $response = $this->httpClient->request('POST', \sprintf('%s/api/tags', $this->host)); + + $models = $response->toArray(); - return $finalModel; + return array_merge(...array_map( + function (array $model): array { + $retrievedModel = $this->getModel($model['name']); + + return [ + $retrievedModel->getName() => [ + 'class' => Ollama::class, + 'capabilities' => $retrievedModel->getCapabilities(), + ], + ]; + }, + $models['models'], + )); } } diff --git a/src/platform/tests/Bridge/Ollama/OllamaApiCatalogTest.php b/src/platform/tests/Bridge/Ollama/OllamaApiCatalogTest.php index 54c16f30b..8f79a28a6 100644 --- a/src/platform/tests/Bridge/Ollama/OllamaApiCatalogTest.php +++ b/src/platform/tests/Bridge/Ollama/OllamaApiCatalogTest.php @@ -12,6 +12,7 @@ namespace Symfony\AI\Platform\Tests\Bridge\Ollama; use PHPUnit\Framework\TestCase; +use Symfony\AI\Platform\Bridge\Ollama\Ollama; use Symfony\AI\Platform\Bridge\Ollama\OllamaApiCatalog; use Symfony\AI\Platform\Capability; use Symfony\Component\HttpClient\MockHttpClient; @@ -37,4 +38,36 @@ public function testModelCatalogCanReturnModelFromApi() ], $model->getCapabilities()); $this->assertSame(1, $httpClient->getRequestsCount()); } + + public function testModelCatalogCanReturnModelsFromApi() + { + $httpClient = new MockHttpClient([ + new JsonMockResponse([ + 'models' => [ + [ + 'name' => 'gemma3', + 'details' => [], + ], + ], + ]), + new JsonMockResponse([ + 'capabilities' => ['completion'], + ]), + ]); + + $modelCatalog = new OllamaApiCatalog('http://127.0.0.1:11434', $httpClient); + + $models = $modelCatalog->getModels(); + + $this->assertCount(1, $models); + $this->assertArrayHasKey('gemma3', $models); + + $model = $models['gemma3']; + $this->assertSame(Ollama::class, $model['class']); + $this->assertCount(1, $model['capabilities']); + $this->assertSame([ + Capability::INPUT_TEXT, + ], $model['capabilities']); + $this->assertSame(2, $httpClient->getRequestsCount()); + } }