diff --git a/docs/components/platform.rst b/docs/components/platform.rst index 400d0ce96..2eb65c1a1 100644 --- a/docs/components/platform.rst +++ b/docs/components/platform.rst @@ -48,9 +48,9 @@ very flexible and powerful interface for working with AI models. Models ------ -The component provides a model base class :class:`Symfony\\AI\\Platform\\Model` which is a combination of a model name, a set of -capabilities, and additional options. Usually, bridges to specific providers extend this base class to provide a quick -start for vendor-specific models and their capabilities. +The component provides a generic model base class :class:`Symfony\\AI\\Platform\\Model` which is a combination of a +model name, a set of capabilities, and additional options. Usually, bridges to specific providers extend this base class +to provide a quick start for vendor-specific models and their capabilities. Capabilities are a list of strings defined by :class:`Symfony\\AI\\Platform\\Capability`, which can be used to check if a model supports a specific feature, like ``Capability::INPUT_AUDIO`` or ``Capability::OUTPUT_IMAGE``. @@ -58,6 +58,12 @@ supports a specific feature, like ``Capability::INPUT_AUDIO`` or ``Capability::O Options are additional parameters that can be passed to the model, like ``temperature`` or ``max_tokens``, and are usually defined by the specific models and their documentation. +Additionally, the component provides two more specific base classes, that are needed to trigger built-in API endpoint +and result conversion implementations: + +* :class:`Symfony\\AI\\Platform\\Model\\CompletionsModel` - for models that support text completions +* :class:`Symfony\\AI\\Platform\\Model\\EmbeddingsModel` - for models that support vector embeddings + Model Size Variants ~~~~~~~~~~~~~~~~~~~ @@ -106,7 +112,7 @@ When using the bundle, the usage of ``OllamaApiCatalog`` is available via the `` api_catalog: true Supported Models & Platforms -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------- * **Language Models** * `OpenAI's GPT`_ with `OpenAI`_, `Azure`_ and `OpenRouter`_ as Platform @@ -118,7 +124,6 @@ Supported Models & Platforms * `Amazon's Nova`_ with `AWS Bedrock`_ as Platform * `Mistral's Mistral`_ with `Mistral`_ and `OpenRouter`_ as Platform * `Albert API`_ models with `Albert`_ as Platform (French government's sovereign AI gateway) - * `LiteLLM`_ as unified Platform * **Embeddings Models** * `Gemini Text Embeddings`_ with `Google`_ and `OpenRouter`_ * `Vertex AI Text Embeddings`_ with `Vertex AI`_ @@ -136,6 +141,16 @@ Supported Models & Platforms * `Cartesia TTS`_ with `Cartesia`_ as Platform * `Cartesia STT`_ with `Cartesia`_ as Platform +Generic Platforms +~~~~~~~~~~~~~~~~~ + +Platforms like `LiteLLM`_ or `OpenRouter`_ provide a unified API to access multiple models from different providers. +Therefore, they rely on endpoint and contract design, that is inspired by OpenAI's original GPT API - an implicit +standard in the industry. Platforms using this de facto standard can be used without the need of a dedicated bridge, by +using built-in classes:: + + ??? + Options ------- diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index a15fafe21..728ab1cbe 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -71,10 +71,10 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Message\Content\File; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Platform; use Symfony\AI\Platform\PlatformInterface; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Store\Bridge\Azure\SearchStore as AzureSearchStore; use Symfony\AI\Store\Bridge\ChromaDb\Store as ChromaDbStore; use Symfony\AI\Store\Bridge\ClickHouse\Store as ClickHouseStore; diff --git a/src/platform/src/Bridge/AiMlApi/Completions.php b/src/platform/src/Bridge/AiMlApi/Completions.php deleted file mode 100644 index c5cbe694c..000000000 --- a/src/platform/src/Bridge/AiMlApi/Completions.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\AiMlApi; - -use Symfony\AI\Platform\Model; - -/** - * @author Tim Lochmüller - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\AiMlApi\Completions; - -use Symfony\AI\Platform\Bridge\AiMlApi\Completions; -use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; -use Symfony\AI\Platform\Result\RawHttpResult; -use Symfony\Contracts\HttpClient\HttpClientInterface; - -/** - * @author Tim Lochmüller httpClient->request('POST', \sprintf('%s/v1/chat/completions', $this->hostUrl), [ - 'json' => array_merge($options, $payload), - 'headers' => [ - 'Authorization' => 'Bearer '.$this->apiKey, - 'Content-Type' => 'application/json', - ], - ])); - } -} diff --git a/src/platform/src/Bridge/AiMlApi/Completions/ResultConverter.php b/src/platform/src/Bridge/AiMlApi/Completions/ResultConverter.php deleted file mode 100644 index 1b01fda97..000000000 --- a/src/platform/src/Bridge/AiMlApi/Completions/ResultConverter.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\AiMlApi\Completions; - -use Symfony\AI\Platform\Bridge\AiMlApi\Completions; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter as OpenAiResponseConverter; -use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\Result\RawResultInterface; -use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; - -/** - * @author Tim Lochmüller gptResponseConverter->convert($result, $options); - } -} diff --git a/src/platform/src/Bridge/AiMlApi/Embeddings/ModelClient.php b/src/platform/src/Bridge/AiMlApi/Embeddings/ModelClient.php deleted file mode 100644 index 77322a063..000000000 --- a/src/platform/src/Bridge/AiMlApi/Embeddings/ModelClient.php +++ /dev/null @@ -1,50 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; - -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; -use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; -use Symfony\AI\Platform\Result\RawHttpResult; -use Symfony\Contracts\HttpClient\HttpClientInterface; - -/** - * @author Tim Lochmüller httpClient->request('POST', \sprintf('%s/v1/embeddings', $this->hostUrl), [ - 'json' => array_merge($options, [ - 'model' => $model->getName(), - 'input' => $payload, - ]), - 'headers' => [ - 'Authorization' => 'Bearer '.$this->apiKey, - 'Content-Type' => 'application/json', - ], - ])); - } -} diff --git a/src/platform/src/Bridge/AiMlApi/Embeddings/ResultConverter.php b/src/platform/src/Bridge/AiMlApi/Embeddings/ResultConverter.php deleted file mode 100644 index 10a80f0e6..000000000 --- a/src/platform/src/Bridge/AiMlApi/Embeddings/ResultConverter.php +++ /dev/null @@ -1,47 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; - -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; -use Symfony\AI\Platform\Exception\RuntimeException; -use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\Result\RawResultInterface; -use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; -use Symfony\AI\Platform\Vector\Vector; - -/** - * @author Tim Lochmüller getData(); - - if (!isset($data['data'])) { - throw new RuntimeException('Response does not contain data.'); - } - - return new VectorResult( - ...array_map( - static fn (array $item): Vector => new Vector($item['embedding']), - $data['data'] - ), - ); - } -} diff --git a/src/platform/src/Bridge/AiMlApi/ModelCatalog.php b/src/platform/src/Bridge/AiMlApi/ModelCatalog.php index f4936fe1e..da27822fc 100644 --- a/src/platform/src/Bridge/AiMlApi/ModelCatalog.php +++ b/src/platform/src/Bridge/AiMlApi/ModelCatalog.php @@ -12,6 +12,8 @@ namespace Symfony\AI\Platform\Bridge\AiMlApi; use Symfony\AI\Platform\Capability; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\ModelCatalog\AbstractModelCatalog; /** @@ -27,7 +29,7 @@ public function __construct(array $additionalModels = []) $defaultModels = [ // Completion models (GPT variants) 'gpt-3.5-turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -36,7 +38,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-3.5-turbo-0125' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -45,7 +47,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-3.5-turbo-1106' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -54,7 +56,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -65,7 +67,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-2024-08-06' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -76,7 +78,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-2024-05-13' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -87,7 +89,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-mini' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -98,7 +100,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-mini-2024-07-18' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -109,7 +111,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4-turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -119,7 +121,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -128,7 +130,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4-turbo-2024-04-09' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -137,7 +139,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4-0125-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -146,7 +148,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4-1106-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -155,7 +157,7 @@ public function __construct(array $additionalModels = []) ], ], 'chatgpt-4o-latest' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -166,7 +168,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-audio-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -177,7 +179,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-mini-audio-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -188,7 +190,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-search-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -199,7 +201,7 @@ public function __construct(array $additionalModels = []) ], ], 'gpt-4o-mini-search-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -210,7 +212,7 @@ public function __construct(array $additionalModels = []) ], ], 'o1-mini' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -218,7 +220,7 @@ public function __construct(array $additionalModels = []) ], ], 'o1-mini-2024-09-12' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -226,7 +228,7 @@ public function __construct(array $additionalModels = []) ], ], 'o1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -234,7 +236,7 @@ public function __construct(array $additionalModels = []) ], ], 'o3-mini' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -244,7 +246,7 @@ public function __construct(array $additionalModels = []) ], // OpenAI future models 'openai/o3-2025-04-16' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -252,7 +254,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/o3-pro' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -260,7 +262,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-4.1-2025-04-14' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -269,7 +271,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-4.1-mini-2025-04-14' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -278,7 +280,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-4.1-nano-2025-04-14' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -287,7 +289,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/o4-mini-2025-04-16' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -295,7 +297,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-oss-20b' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -304,7 +306,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-oss-120b' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -313,7 +315,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-5-2025-08-07' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -322,7 +324,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-5-mini-2025-08-07' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -331,7 +333,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-5-nano-2025-08-07' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -340,7 +342,7 @@ public function __construct(array $additionalModels = []) ], ], 'openai/gpt-5-chat-latest' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -350,7 +352,7 @@ public function __construct(array $additionalModels = []) ], // DeepSeek models 'deepseek-chat' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -359,7 +361,7 @@ public function __construct(array $additionalModels = []) ], ], 'deepseek/deepseek-chat' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -368,7 +370,7 @@ public function __construct(array $additionalModels = []) ], ], 'deepseek/deepseek-chat-v3-0324' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -377,28 +379,28 @@ public function __construct(array $additionalModels = []) ], ], 'deepseek/deepseek-r1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, ], ], 'deepseek-reasoner' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, ], ], 'deepseek/deepseek-prover-v2' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, ], ], 'deepseek/deepseek-chat-v3.1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -407,7 +409,7 @@ public function __construct(array $additionalModels = []) ], ], 'deepseek/deepseek-reasoner-v3.1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -415,7 +417,7 @@ public function __construct(array $additionalModels = []) ], // Qwen models 'Qwen/Qwen2-72B-Instruct' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -424,7 +426,7 @@ public function __construct(array $additionalModels = []) ], ], 'qwen-max' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -433,7 +435,7 @@ public function __construct(array $additionalModels = []) ], ], 'qwen-plus' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -442,7 +444,7 @@ public function __construct(array $additionalModels = []) ], ], 'qwen-turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -451,7 +453,7 @@ public function __construct(array $additionalModels = []) ], ], 'qwen-max-2025-01-25' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -460,7 +462,7 @@ public function __construct(array $additionalModels = []) ], ], 'Qwen/Qwen2.5-72B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -469,7 +471,7 @@ public function __construct(array $additionalModels = []) ], ], 'Qwen/QwQ-32B' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -478,7 +480,7 @@ public function __construct(array $additionalModels = []) ], ], 'Qwen/Qwen3-235B-A22B-fp8-tput' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -487,7 +489,7 @@ public function __construct(array $additionalModels = []) ], ], 'alibaba/qwen3-32b' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -496,7 +498,7 @@ public function __construct(array $additionalModels = []) ], ], 'alibaba/qwen3-coder-480b-a35b-instruct' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -505,14 +507,14 @@ public function __construct(array $additionalModels = []) ], ], 'alibaba/qwen3-235b-a22b-thinking-2507' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, ], ], 'Qwen/Qwen2.5-7B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -521,7 +523,7 @@ public function __construct(array $additionalModels = []) ], ], 'Qwen/Qwen2.5-Coder-32B-Instruct' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -531,7 +533,7 @@ public function __construct(array $additionalModels = []) ], // Mistral models 'mistralai/Mixtral-8x7B-Instruct-v0.1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -540,7 +542,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/Mistral-7B-Instruct-v0.2' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -549,7 +551,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/Mistral-7B-Instruct-v0.1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -558,7 +560,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/Mistral-7B-Instruct-v0.3' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -567,7 +569,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/mistral-tiny' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -576,7 +578,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/mistral-nemo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -585,7 +587,7 @@ public function __construct(array $additionalModels = []) ], ], 'mistralai/codestral-2501' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -595,7 +597,7 @@ public function __construct(array $additionalModels = []) ], // Meta Llama models 'meta-llama/Llama-3.3-70B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -604,7 +606,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Llama-3.2-3B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -613,7 +615,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Meta-Llama-3-8B-Instruct-Lite' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -622,7 +624,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Llama-3-70b-chat-hf' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -631,7 +633,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -640,7 +642,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -649,7 +651,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -658,7 +660,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/llama-4-scout' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -667,7 +669,7 @@ public function __construct(array $additionalModels = []) ], ], 'meta-llama/llama-4-maverick' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -677,7 +679,7 @@ public function __construct(array $additionalModels = []) ], // Claude models 'claude-3-opus-20240229' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -687,7 +689,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-3-haiku-20240307' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -697,7 +699,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-3-5-sonnet-20240620' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -707,7 +709,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-3-5-sonnet-20241022' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -717,7 +719,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-3-5-haiku-20241022' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -727,7 +729,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-3-7-sonnet-20250219' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -737,7 +739,7 @@ public function __construct(array $additionalModels = []) ], ], 'anthropic/claude-opus-4' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -747,7 +749,7 @@ public function __construct(array $additionalModels = []) ], ], 'anthropic/claude-sonnet-4' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -757,7 +759,7 @@ public function __construct(array $additionalModels = []) ], ], 'anthropic/claude-opus-4.1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -767,7 +769,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-opus-4-1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -777,7 +779,7 @@ public function __construct(array $additionalModels = []) ], ], 'claude-opus-4-1-20250805' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -788,7 +790,7 @@ public function __construct(array $additionalModels = []) ], // Gemini models 'gemini-2.0-flash-exp' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -798,7 +800,7 @@ public function __construct(array $additionalModels = []) ], ], 'gemini-2.0-flash' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -808,7 +810,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemini-2.5-flash-lite-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -818,7 +820,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemini-2.5-flash' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -828,7 +830,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemini-2.5-pro' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -838,7 +840,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemma-2-27b-it' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -847,7 +849,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemma-3-4b-it' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -856,7 +858,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemma-3-12b-it' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -865,7 +867,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemma-3-27b-it' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -874,7 +876,7 @@ public function __construct(array $additionalModels = []) ], ], 'google/gemma-3n-e4b-it' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -884,7 +886,7 @@ public function __construct(array $additionalModels = []) ], // X.AI models 'x-ai/grok-3-beta' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -893,7 +895,7 @@ public function __construct(array $additionalModels = []) ], ], 'x-ai/grok-3-mini-beta' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -902,7 +904,7 @@ public function __construct(array $additionalModels = []) ], ], 'x-ai/grok-4-07-09' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -912,7 +914,7 @@ public function __construct(array $additionalModels = []) ], // Other models 'anthracite-org/magnum-v4-72b' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -921,7 +923,7 @@ public function __construct(array $additionalModels = []) ], ], 'nvidia/llama-3.1-nemotron-70b-instruct' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -930,7 +932,7 @@ public function __construct(array $additionalModels = []) ], ], 'cohere/command-r-plus' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -939,7 +941,7 @@ public function __construct(array $additionalModels = []) ], ], 'cohere/command-a' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -948,7 +950,7 @@ public function __construct(array $additionalModels = []) ], ], 'MiniMax-Text-01' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -957,7 +959,7 @@ public function __construct(array $additionalModels = []) ], ], 'minimax/m1' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -966,7 +968,7 @@ public function __construct(array $additionalModels = []) ], ], 'moonshot/kimi-k2-preview' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -975,7 +977,7 @@ public function __construct(array $additionalModels = []) ], ], 'perplexity/sonar' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -984,7 +986,7 @@ public function __construct(array $additionalModels = []) ], ], 'perplexity/sonar-pro' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -993,7 +995,7 @@ public function __construct(array $additionalModels = []) ], ], 'zhipu/glm-4.5-air' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -1002,7 +1004,7 @@ public function __construct(array $additionalModels = []) ], ], 'zhipu/glm-4.5' => [ - 'class' => Completions::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -1012,67 +1014,67 @@ public function __construct(array $additionalModels = []) ], // Embedding models 'text-embedding-3-small' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'text-embedding-3-large' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'text-embedding-ada-002' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'togethercomputer/m2-bert-80M-32k-retrieval' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'BAAI/bge-base-en-v1.5' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'BAAI/bge-large-en-v1.' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-large-2-instruct' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-finance-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-multilingual-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-law-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-code-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-large-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'voyage-2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'textembedding-gecko@003' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'textembedding-gecko-multilingual@001' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], 'text-multilingual-embedding-002' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_MULTIPLE], ], ]; diff --git a/src/platform/src/Bridge/AiMlApi/PlatformFactory.php b/src/platform/src/Bridge/AiMlApi/PlatformFactory.php index 1592393c7..f0bdf66cf 100644 --- a/src/platform/src/Bridge/AiMlApi/PlatformFactory.php +++ b/src/platform/src/Bridge/AiMlApi/PlatformFactory.php @@ -12,9 +12,12 @@ namespace Symfony\AI\Platform\Bridge\AiMlApi; use Psr\EventDispatcher\EventDispatcherInterface; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings\ModelClient; use Symfony\AI\Platform\Contract; +use Symfony\AI\Platform\ModelClient\CompletionsModelClient; +use Symfony\AI\Platform\ModelClient\EmbeddingsModelClient; use Symfony\AI\Platform\Platform; +use Symfony\AI\Platform\ResultConverter\CompletionsResultConverter; +use Symfony\AI\Platform\ResultConverter\EmbeddingsResultConverter; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -26,17 +29,17 @@ public static function create( #[\SensitiveParameter] string $apiKey, ?HttpClientInterface $httpClient = null, ?Contract $contract = null, - string $hostUrl = 'https://api.aimlapi.com', + string $baseUrl = 'https://api.aimlapi.com', ?EventDispatcherInterface $eventDispatcher = null, ): Platform { return new Platform( [ - new ModelClient($apiKey, $httpClient, $hostUrl), - new Completions\ModelClient($apiKey, $httpClient, $hostUrl), + new CompletionsModelClient($apiKey, $baseUrl, httpClient: $httpClient), + new EmbeddingsModelClient($apiKey, $baseUrl, httpClient: $httpClient), ], [ - new Embeddings\ResultConverter(), - new Completions\ResultConverter(), + new CompletionsResultConverter(), + new EmbeddingsResultConverter(), ], new ModelCatalog(), $contract, diff --git a/src/platform/src/Bridge/Albert/ModelCatalog.php b/src/platform/src/Bridge/Albert/ModelCatalog.php index d57e2ba52..c067193e5 100644 --- a/src/platform/src/Bridge/Albert/ModelCatalog.php +++ b/src/platform/src/Bridge/Albert/ModelCatalog.php @@ -11,10 +11,10 @@ namespace Symfony\AI\Platform\Bridge\Albert; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\ModelCatalog\AbstractModelCatalog; /** @@ -29,7 +29,7 @@ public function __construct(array $additionalModels = []) { $defaultModels = [ 'albert-small' => [ - 'class' => Gpt::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -37,7 +37,7 @@ public function __construct(array $additionalModels = []) ], ], 'albert-large' => [ - 'class' => Gpt::class, + 'class' => CompletionsModel::class, 'capabilities' => [ Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, @@ -45,7 +45,7 @@ public function __construct(array $additionalModels = []) ], ], 'embeddings-small' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_TEXT], ], ]; diff --git a/src/platform/src/Bridge/Albert/PlatformFactory.php b/src/platform/src/Bridge/Albert/PlatformFactory.php index d83722581..416a050a6 100644 --- a/src/platform/src/Bridge/Albert/PlatformFactory.php +++ b/src/platform/src/Bridge/Albert/PlatformFactory.php @@ -12,12 +12,14 @@ namespace Symfony\AI\Platform\Bridge\Albert; use Psr\EventDispatcher\EventDispatcherInterface; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; +use Symfony\AI\Platform\ModelClient\CompletionsModelClient; +use Symfony\AI\Platform\ModelClient\EmbeddingsModelClient; use Symfony\AI\Platform\Platform; +use Symfony\AI\Platform\ResultConverter\CompletionsResultConverter; +use Symfony\AI\Platform\ResultConverter\EmbeddingsResultConverter; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -50,10 +52,10 @@ public static function create( return new Platform( [ - new GptModelClient($httpClient, $apiKey, $baseUrl), - new EmbeddingsModelClient($httpClient, $apiKey, $baseUrl), + new CompletionsModelClient($apiKey, $baseUrl, httpClient: $httpClient), + new EmbeddingsModelClient($apiKey, $baseUrl, httpClient: $httpClient), ], - [new Gpt\ResultConverter(), new Embeddings\ResultConverter()], + [new CompletionsResultConverter(), new EmbeddingsResultConverter()], $modelCatalog, Contract::create(), $eventDispatcher, diff --git a/src/platform/src/Bridge/Anthropic/Claude.php b/src/platform/src/Bridge/Anthropic/Claude.php index 544413929..0cb67729a 100644 --- a/src/platform/src/Bridge/Anthropic/Claude.php +++ b/src/platform/src/Bridge/Anthropic/Claude.php @@ -11,12 +11,12 @@ namespace Symfony\AI\Platform\Bridge\Anthropic; -use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\CompletionsModel; /** * @author Christopher Hertel */ -class Claude extends Model +class Claude extends CompletionsModel { public const HAIKU_3 = 'claude-3-haiku-20240307'; public const HAIKU_35 = 'claude-3-5-haiku-latest'; diff --git a/src/platform/src/Bridge/Anthropic/ModelClient.php b/src/platform/src/Bridge/Anthropic/ModelClient.php index 39b3d9d09..3730ef635 100644 --- a/src/platform/src/Bridge/Anthropic/ModelClient.php +++ b/src/platform/src/Bridge/Anthropic/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\Anthropic; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Anthropic/ResultConverter.php b/src/platform/src/Bridge/Anthropic/ResultConverter.php index c7515de6e..268c23373 100644 --- a/src/platform/src/Bridge/Anthropic/ResultConverter.php +++ b/src/platform/src/Bridge/Anthropic/ResultConverter.php @@ -21,7 +21,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/Azure/Meta/LlamaModelClient.php b/src/platform/src/Bridge/Azure/Meta/LlamaModelClient.php index 84cd2a280..3a6339ba2 100644 --- a/src/platform/src/Bridge/Azure/Meta/LlamaModelClient.php +++ b/src/platform/src/Bridge/Azure/Meta/LlamaModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Meta\Llama; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Azure/Meta/LlamaResultConverter.php b/src/platform/src/Bridge/Azure/Meta/LlamaResultConverter.php index f9cf2226c..86ad5233c 100644 --- a/src/platform/src/Bridge/Azure/Meta/LlamaResultConverter.php +++ b/src/platform/src/Bridge/Azure/Meta/LlamaResultConverter.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/Azure/OpenAi/EmbeddingsModelClient.php b/src/platform/src/Bridge/Azure/OpenAi/EmbeddingsModelClient.php index 86c1a67b8..38c8a10b9 100644 --- a/src/platform/src/Bridge/Azure/OpenAi/EmbeddingsModelClient.php +++ b/src/platform/src/Bridge/Azure/OpenAi/EmbeddingsModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Azure/OpenAi/GptModelClient.php b/src/platform/src/Bridge/Azure/OpenAi/GptModelClient.php index e952c7f9f..c2f5e000d 100644 --- a/src/platform/src/Bridge/Azure/OpenAi/GptModelClient.php +++ b/src/platform/src/Bridge/Azure/OpenAi/GptModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Azure/OpenAi/WhisperModelClient.php b/src/platform/src/Bridge/Azure/OpenAi/WhisperModelClient.php index b1656f60c..3533edece 100644 --- a/src/platform/src/Bridge/Azure/OpenAi/WhisperModelClient.php +++ b/src/platform/src/Bridge/Azure/OpenAi/WhisperModelClient.php @@ -15,7 +15,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\Whisper\Task; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeModelClient.php b/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeModelClient.php index 1d47b7690..ba8bfffd0 100644 --- a/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeModelClient.php +++ b/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeModelClient.php @@ -18,7 +18,7 @@ use Symfony\AI\Platform\Bridge\Bedrock\RawBedrockResult; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; diff --git a/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeResultConverter.php b/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeResultConverter.php index 61b49d0b1..aea21cfd1 100644 --- a/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeResultConverter.php +++ b/src/platform/src/Bridge/Bedrock/Anthropic/ClaudeResultConverter.php @@ -19,7 +19,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Björn Altmann diff --git a/src/platform/src/Bridge/Bedrock/Meta/LlamaModelClient.php b/src/platform/src/Bridge/Bedrock/Meta/LlamaModelClient.php index 2607b83e3..c374b9b21 100644 --- a/src/platform/src/Bridge/Bedrock/Meta/LlamaModelClient.php +++ b/src/platform/src/Bridge/Bedrock/Meta/LlamaModelClient.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Bridge\Bedrock\RawBedrockResult; use Symfony\AI\Platform\Bridge\Meta\Llama; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; /** * @author Björn Altmann diff --git a/src/platform/src/Bridge/Bedrock/Meta/LlamaResultConverter.php b/src/platform/src/Bridge/Bedrock/Meta/LlamaResultConverter.php index a1e0e5cf5..d02fa5d56 100644 --- a/src/platform/src/Bridge/Bedrock/Meta/LlamaResultConverter.php +++ b/src/platform/src/Bridge/Bedrock/Meta/LlamaResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Björn Altmann diff --git a/src/platform/src/Bridge/Bedrock/Nova/NovaModelClient.php b/src/platform/src/Bridge/Bedrock/Nova/NovaModelClient.php index a1990da62..99abd5944 100644 --- a/src/platform/src/Bridge/Bedrock/Nova/NovaModelClient.php +++ b/src/platform/src/Bridge/Bedrock/Nova/NovaModelClient.php @@ -15,7 +15,7 @@ use AsyncAws\BedrockRuntime\Input\InvokeModelRequest; use Symfony\AI\Platform\Bridge\Bedrock\RawBedrockResult; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; /** * @author Björn Altmann diff --git a/src/platform/src/Bridge/Bedrock/Nova/NovaResultConverter.php b/src/platform/src/Bridge/Bedrock/Nova/NovaResultConverter.php index bf2c1169d..686809224 100644 --- a/src/platform/src/Bridge/Bedrock/Nova/NovaResultConverter.php +++ b/src/platform/src/Bridge/Bedrock/Nova/NovaResultConverter.php @@ -18,7 +18,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Björn Altmann diff --git a/src/platform/src/Bridge/Cartesia/CartesiaClient.php b/src/platform/src/Bridge/Cartesia/CartesiaClient.php index d5df536fe..49f393bab 100644 --- a/src/platform/src/Bridge/Cartesia/CartesiaClient.php +++ b/src/platform/src/Bridge/Cartesia/CartesiaClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Cartesia/CartesiaResultConverter.php b/src/platform/src/Bridge/Cartesia/CartesiaResultConverter.php index 6248ea8b2..54a48a172 100644 --- a/src/platform/src/Bridge/Cartesia/CartesiaResultConverter.php +++ b/src/platform/src/Bridge/Cartesia/CartesiaResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Contracts\HttpClient\ResponseInterface; /** diff --git a/src/platform/src/Bridge/Cerebras/ModelClient.php b/src/platform/src/Bridge/Cerebras/ModelClient.php index cdc14f103..a15bfae42 100644 --- a/src/platform/src/Bridge/Cerebras/ModelClient.php +++ b/src/platform/src/Bridge/Cerebras/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model as BaseModel; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Cerebras/ResultConverter.php b/src/platform/src/Bridge/Cerebras/ResultConverter.php index 99d5f0f09..0820ec92f 100644 --- a/src/platform/src/Bridge/Cerebras/ResultConverter.php +++ b/src/platform/src/Bridge/Cerebras/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\StreamResult; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Junaid Farooq diff --git a/src/platform/src/Bridge/DeepSeek/ModelClient.php b/src/platform/src/Bridge/DeepSeek/ModelClient.php index 39d3625c0..db0480fb9 100644 --- a/src/platform/src/Bridge/DeepSeek/ModelClient.php +++ b/src/platform/src/Bridge/DeepSeek/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\DeepSeek; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/DeepSeek/ResultConverter.php b/src/platform/src/Bridge/DeepSeek/ResultConverter.php index 35f64ddee..00c76303f 100644 --- a/src/platform/src/Bridge/DeepSeek/ResultConverter.php +++ b/src/platform/src/Bridge/DeepSeek/ResultConverter.php @@ -22,7 +22,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Oskar Stark diff --git a/src/platform/src/Bridge/DockerModelRunner/Completions/ModelClient.php b/src/platform/src/Bridge/DockerModelRunner/Completions/ModelClient.php index 940971dfb..03c58b0a6 100644 --- a/src/platform/src/Bridge/DockerModelRunner/Completions/ModelClient.php +++ b/src/platform/src/Bridge/DockerModelRunner/Completions/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\DockerModelRunner\Completions; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/DockerModelRunner/Completions/ResultConverter.php b/src/platform/src/Bridge/DockerModelRunner/Completions/ResultConverter.php index 5b208dd5a..09e48acff 100644 --- a/src/platform/src/Bridge/DockerModelRunner/Completions/ResultConverter.php +++ b/src/platform/src/Bridge/DockerModelRunner/Completions/ResultConverter.php @@ -25,7 +25,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Mathieu Santostefano diff --git a/src/platform/src/Bridge/DockerModelRunner/Embeddings/ModelClient.php b/src/platform/src/Bridge/DockerModelRunner/Embeddings/ModelClient.php index 56ed51690..b51d3a0b3 100644 --- a/src/platform/src/Bridge/DockerModelRunner/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/DockerModelRunner/Embeddings/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\DockerModelRunner\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/DockerModelRunner/Embeddings/ResultConverter.php b/src/platform/src/Bridge/DockerModelRunner/Embeddings/ResultConverter.php index a63af9b55..b9fe5eeaa 100644 --- a/src/platform/src/Bridge/DockerModelRunner/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/DockerModelRunner/Embeddings/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/ElevenLabs/ElevenLabsClient.php b/src/platform/src/Bridge/ElevenLabs/ElevenLabsClient.php index f3d0cd55b..8d8f421ca 100644 --- a/src/platform/src/Bridge/ElevenLabs/ElevenLabsClient.php +++ b/src/platform/src/Bridge/ElevenLabs/ElevenLabsClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/ElevenLabs/ElevenLabsResultConverter.php b/src/platform/src/Bridge/ElevenLabs/ElevenLabsResultConverter.php index 70d561c4f..735dc3bd5 100644 --- a/src/platform/src/Bridge/ElevenLabs/ElevenLabsResultConverter.php +++ b/src/platform/src/Bridge/ElevenLabs/ElevenLabsResultConverter.php @@ -19,7 +19,7 @@ use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\StreamResult; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\ResponseInterface; diff --git a/src/platform/src/Bridge/Gemini/Embeddings/ModelClient.php b/src/platform/src/Bridge/Gemini/Embeddings/ModelClient.php index 1bd527135..a91c4517b 100644 --- a/src/platform/src/Bridge/Gemini/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/Gemini/Embeddings/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Gemini\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Gemini/Embeddings/ResultConverter.php b/src/platform/src/Bridge/Gemini/Embeddings/ResultConverter.php index 1f858718f..f8415d94d 100644 --- a/src/platform/src/Bridge/Gemini/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/Gemini/Embeddings/ResultConverter.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/Gemini/Gemini/ModelClient.php b/src/platform/src/Bridge/Gemini/Gemini/ModelClient.php index 11d76150d..764f02d7a 100644 --- a/src/platform/src/Bridge/Gemini/Gemini/ModelClient.php +++ b/src/platform/src/Bridge/Gemini/Gemini/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Gemini\Gemini; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\StructuredOutput\PlatformSubscriber; use Symfony\Component\HttpClient\EventSourceHttpClient; diff --git a/src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php b/src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php index d69999378..bb0cafda9 100644 --- a/src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php +++ b/src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php @@ -24,7 +24,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Roy Garrido diff --git a/src/platform/src/Bridge/HuggingFace/ModelClient.php b/src/platform/src/Bridge/HuggingFace/ModelClient.php index cb38a4cf6..7ec989791 100644 --- a/src/platform/src/Bridge/HuggingFace/ModelClient.php +++ b/src/platform/src/Bridge/HuggingFace/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\HuggingFace; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/HuggingFace/ResultConverter.php b/src/platform/src/Bridge/HuggingFace/ResultConverter.php index 0af31d68e..33b786107 100644 --- a/src/platform/src/Bridge/HuggingFace/ResultConverter.php +++ b/src/platform/src/Bridge/HuggingFace/ResultConverter.php @@ -30,7 +30,7 @@ use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/LiteLlm/ModelClient.php b/src/platform/src/Bridge/LiteLlm/ModelClient.php index 635c7abe3..73e57d8b2 100644 --- a/src/platform/src/Bridge/LiteLlm/ModelClient.php +++ b/src/platform/src/Bridge/LiteLlm/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\LiteLlm; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/LiteLlm/ResultConverter.php b/src/platform/src/Bridge/LiteLlm/ResultConverter.php index 452af93cc..fbdca6afe 100644 --- a/src/platform/src/Bridge/LiteLlm/ResultConverter.php +++ b/src/platform/src/Bridge/LiteLlm/ResultConverter.php @@ -11,29 +11,16 @@ namespace Symfony\AI\Platform\Bridge\LiteLlm; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter as OpenAiResponseConverter; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\Result\RawResultInterface; -use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\CompletionsResultConverter; /** * @author Mathieu Santostefano */ -final class ResultConverter implements ResultConverterInterface +final class ResultConverter extends CompletionsResultConverter { - public function __construct( - private readonly OpenAiResponseConverter $gptResponseConverter = new OpenAiResponseConverter(), - ) { - } - public function supports(Model $model): bool { return true; } - - public function convert(RawResultInterface $result, array $options = []): ResultInterface - { - return $this->gptResponseConverter->convert($result, $options); - } } diff --git a/src/platform/src/Bridge/LmStudio/Completions.php b/src/platform/src/Bridge/LmStudio/Completions.php deleted file mode 100644 index cda9fdbbd..000000000 --- a/src/platform/src/Bridge/LmStudio/Completions.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\LmStudio; - -use Symfony\AI\Platform\Model; - -/** - * @author André Lubian - */ -class Completions extends Model -{ -} diff --git a/src/platform/src/Bridge/LmStudio/Completions/ModelClient.php b/src/platform/src/Bridge/LmStudio/Completions/ModelClient.php index 8b6af2d8b..6e2d42077 100644 --- a/src/platform/src/Bridge/LmStudio/Completions/ModelClient.php +++ b/src/platform/src/Bridge/LmStudio/Completions/ModelClient.php @@ -11,9 +11,9 @@ namespace Symfony\AI\Platform\Bridge\LmStudio\Completions; -use Symfony\AI\Platform\Bridge\LmStudio\Completions; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -34,7 +34,7 @@ public function __construct( public function supports(Model $model): bool { - return $model instanceof Completions; + return $model instanceof CompletionsModel; } public function request(Model $model, array|string $payload, array $options = []): RawHttpResult diff --git a/src/platform/src/Bridge/LmStudio/Completions/ResultConverter.php b/src/platform/src/Bridge/LmStudio/Completions/ResultConverter.php deleted file mode 100644 index b9c7ad373..000000000 --- a/src/platform/src/Bridge/LmStudio/Completions/ResultConverter.php +++ /dev/null @@ -1,40 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\LmStudio\Completions; - -use Symfony\AI\Platform\Bridge\LmStudio\Completions; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter as OpenAiResponseConverter; -use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\Result\RawResultInterface; -use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; - -/** - * @author André Lubian - */ -final class ResultConverter implements ResultConverterInterface -{ - public function __construct( - private readonly OpenAiResponseConverter $gptResponseConverter = new OpenAiResponseConverter(), - ) { - } - - public function supports(Model $model): bool - { - return $model instanceof Completions; - } - - public function convert(RawResultInterface $result, array $options = []): ResultInterface - { - return $this->gptResponseConverter->convert($result, $options); - } -} diff --git a/src/platform/src/Bridge/LmStudio/Embeddings.php b/src/platform/src/Bridge/LmStudio/Embeddings.php deleted file mode 100644 index 7d899c215..000000000 --- a/src/platform/src/Bridge/LmStudio/Embeddings.php +++ /dev/null @@ -1,21 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Bridge\LmStudio; - -use Symfony\AI\Platform\Model; - -/** - * @author André Lubian - */ -class Embeddings extends Model -{ -} diff --git a/src/platform/src/Bridge/LmStudio/Embeddings/ModelClient.php b/src/platform/src/Bridge/LmStudio/Embeddings/ModelClient.php index be1bbbe48..4532668c3 100644 --- a/src/platform/src/Bridge/LmStudio/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/LmStudio/Embeddings/ModelClient.php @@ -11,9 +11,9 @@ namespace Symfony\AI\Platform\Bridge\LmStudio\Embeddings; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\Model\EmbeddingsModel; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -31,7 +31,7 @@ public function __construct( public function supports(Model $model): bool { - return $model instanceof Embeddings; + return $model instanceof EmbeddingsModel; } public function request(Model $model, array|string $payload, array $options = []): RawHttpResult diff --git a/src/platform/src/Bridge/LmStudio/PlatformFactory.php b/src/platform/src/Bridge/LmStudio/PlatformFactory.php index 411a88e46..b15deff0e 100644 --- a/src/platform/src/Bridge/LmStudio/PlatformFactory.php +++ b/src/platform/src/Bridge/LmStudio/PlatformFactory.php @@ -12,10 +12,11 @@ namespace Symfony\AI\Platform\Bridge\LmStudio; use Psr\EventDispatcher\EventDispatcherInterface; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ModelClient; use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; use Symfony\AI\Platform\Platform; +use Symfony\AI\Platform\ResultConverter\CompletionsResultConverter; +use Symfony\AI\Platform\ResultConverter\EmbeddingsResultConverter; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -35,12 +36,12 @@ public static function create( return new Platform( [ - new ModelClient($httpClient, $hostUrl), + new Embeddings\ModelClient($httpClient, $hostUrl), new Completions\ModelClient($httpClient, $hostUrl), ], [ - new Embeddings\ResultConverter(), - new Completions\ResultConverter(), + new EmbeddingsResultConverter(), + new CompletionsResultConverter(), ], $modelCatalog, $contract, diff --git a/src/platform/src/Bridge/Mistral/Embeddings/ModelClient.php b/src/platform/src/Bridge/Mistral/Embeddings/ModelClient.php index 4a5e5e993..5e764aac8 100644 --- a/src/platform/src/Bridge/Mistral/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/Mistral/Embeddings/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Mistral\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Mistral/Embeddings/ResultConverter.php b/src/platform/src/Bridge/Mistral/Embeddings/ResultConverter.php index af5de3547..f8daa0285 100644 --- a/src/platform/src/Bridge/Mistral/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/Mistral/Embeddings/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/Mistral/Llm/ModelClient.php b/src/platform/src/Bridge/Mistral/Llm/ModelClient.php index ebc4dc469..40d7a8c7a 100644 --- a/src/platform/src/Bridge/Mistral/Llm/ModelClient.php +++ b/src/platform/src/Bridge/Mistral/Llm/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Mistral\Mistral; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Mistral/Llm/ResultConverter.php b/src/platform/src/Bridge/Mistral/Llm/ResultConverter.php index 987d8b9c4..660748796 100644 --- a/src/platform/src/Bridge/Mistral/Llm/ResultConverter.php +++ b/src/platform/src/Bridge/Mistral/Llm/ResultConverter.php @@ -22,7 +22,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/Ollama/OllamaClient.php b/src/platform/src/Bridge/Ollama/OllamaClient.php index 8cc893bcb..aaa391a2c 100644 --- a/src/platform/src/Bridge/Ollama/OllamaClient.php +++ b/src/platform/src/Bridge/Ollama/OllamaClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\StructuredOutput\PlatformSubscriber; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Ollama/OllamaResultConverter.php b/src/platform/src/Bridge/Ollama/OllamaResultConverter.php index cf62c61e1..92a5e6518 100644 --- a/src/platform/src/Bridge/Ollama/OllamaResultConverter.php +++ b/src/platform/src/Bridge/Ollama/OllamaResultConverter.php @@ -20,7 +20,7 @@ use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/OpenAi/DallE/ModelClient.php b/src/platform/src/Bridge/OpenAi/DallE/ModelClient.php index 11ed7a905..846da596e 100644 --- a/src/platform/src/Bridge/OpenAi/DallE/ModelClient.php +++ b/src/platform/src/Bridge/OpenAi/DallE/ModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\AbstractModelClient; use Symfony\AI\Platform\Bridge\OpenAi\DallE; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenAi/DallE/ResultConverter.php b/src/platform/src/Bridge/OpenAi/DallE/ResultConverter.php index e95381d22..b31226bfb 100644 --- a/src/platform/src/Bridge/OpenAi/DallE/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/DallE/ResultConverter.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\StructuredOutput\PlatformSubscriber; /** diff --git a/src/platform/src/Bridge/OpenAi/Embeddings/ModelClient.php b/src/platform/src/Bridge/OpenAi/Embeddings/ModelClient.php index c3208fbee..fd619d2d2 100644 --- a/src/platform/src/Bridge/OpenAi/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/OpenAi/Embeddings/ModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\AbstractModelClient; use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenAi/Embeddings/ResultConverter.php b/src/platform/src/Bridge/OpenAi/Embeddings/ResultConverter.php index cae4321fd..a5c7ca6c8 100644 --- a/src/platform/src/Bridge/OpenAi/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/Embeddings/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/OpenAi/Gpt/ModelClient.php b/src/platform/src/Bridge/OpenAi/Gpt/ModelClient.php index 5317e82c2..068c69182 100644 --- a/src/platform/src/Bridge/OpenAi/Gpt/ModelClient.php +++ b/src/platform/src/Bridge/OpenAi/Gpt/ModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\AbstractModelClient; use Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php b/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php index 29765a4a4..1a058cf8b 100644 --- a/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/Gpt/ResultConverter.php @@ -26,7 +26,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/OpenAi/TextToSpeech/ModelClient.php b/src/platform/src/Bridge/OpenAi/TextToSpeech/ModelClient.php index 9b60fe8e1..bcfcd678d 100644 --- a/src/platform/src/Bridge/OpenAi/TextToSpeech/ModelClient.php +++ b/src/platform/src/Bridge/OpenAi/TextToSpeech/ModelClient.php @@ -15,7 +15,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\TextToSpeech; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenAi/TextToSpeech/ResultConverter.php b/src/platform/src/Bridge/OpenAi/TextToSpeech/ResultConverter.php index 1f93f2e9a..98d0c752c 100644 --- a/src/platform/src/Bridge/OpenAi/TextToSpeech/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/TextToSpeech/ResultConverter.php @@ -18,7 +18,7 @@ use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface as BaseResponseConverter; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface as BaseResponseConverter; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/OpenAi/Whisper/ModelClient.php b/src/platform/src/Bridge/OpenAi/Whisper/ModelClient.php index c4d5f901d..02dd47a6c 100644 --- a/src/platform/src/Bridge/OpenAi/Whisper/ModelClient.php +++ b/src/platform/src/Bridge/OpenAi/Whisper/ModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\OpenAi\AbstractModelClient; use Symfony\AI\Platform\Bridge\OpenAi\Whisper; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenAi/Whisper/ResultConverter.php b/src/platform/src/Bridge/OpenAi/Whisper/ResultConverter.php index 97ad909ba..7c9acabaf 100644 --- a/src/platform/src/Bridge/OpenAi/Whisper/ResultConverter.php +++ b/src/platform/src/Bridge/OpenAi/Whisper/ResultConverter.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface as BaseResponseConverter; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface as BaseResponseConverter; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/OpenRouter/Completions/ModelClient.php b/src/platform/src/Bridge/OpenRouter/Completions/ModelClient.php index ed4c19ddd..a2d1b7677 100644 --- a/src/platform/src/Bridge/OpenRouter/Completions/ModelClient.php +++ b/src/platform/src/Bridge/OpenRouter/Completions/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/OpenRouter/Completions/ResultConverter.php b/src/platform/src/Bridge/OpenRouter/Completions/ResultConverter.php index 5b0d4d060..947a8f18b 100644 --- a/src/platform/src/Bridge/OpenRouter/Completions/ResultConverter.php +++ b/src/platform/src/Bridge/OpenRouter/Completions/ResultConverter.php @@ -11,29 +11,16 @@ namespace Symfony\AI\Platform\Bridge\OpenRouter\Completions; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt\ResultConverter as OpenAiResponseConverter; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\Result\RawResultInterface; -use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\CompletionsResultConverter; /** * @author rglozman */ -final class ResultConverter implements ResultConverterInterface +final class ResultConverter extends CompletionsResultConverter { - public function __construct( - private readonly OpenAiResponseConverter $gptResponseConverter = new OpenAiResponseConverter(), - ) { - } - public function supports(Model $model): bool { return true; } - - public function convert(RawResultInterface $result, array $options = []): ResultInterface - { - return $this->gptResponseConverter->convert($result, $options); - } } diff --git a/src/platform/src/Bridge/OpenRouter/Embeddings/ModelClient.php b/src/platform/src/Bridge/OpenRouter/Embeddings/ModelClient.php index 688797d7e..935384e23 100644 --- a/src/platform/src/Bridge/OpenRouter/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/OpenRouter/Embeddings/ModelClient.php @@ -11,10 +11,10 @@ namespace Symfony\AI\Platform\Bridge\OpenRouter\Embeddings; -use Symfony\AI\Platform\Bridge\OpenRouter\Embeddings; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\Model\EmbeddingsModel; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; @@ -37,7 +37,7 @@ public function __construct( public function supports(Model $model): bool { - return $model instanceof Embeddings; + return $model instanceof EmbeddingsModel; } public function request(Model $model, array|string $payload, array $options = []): RawHttpResult diff --git a/src/platform/src/Bridge/OpenRouter/Embeddings/ResultConverter.php b/src/platform/src/Bridge/OpenRouter/Embeddings/ResultConverter.php index 27e1b0d64..e5442c7f0 100644 --- a/src/platform/src/Bridge/OpenRouter/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/OpenRouter/Embeddings/ResultConverter.php @@ -11,15 +11,15 @@ namespace Symfony\AI\Platform\Bridge\OpenRouter\Embeddings; -use Symfony\AI\Platform\Bridge\OpenRouter\Embeddings; use Symfony\AI\Platform\Exception\AuthenticationException; use Symfony\AI\Platform\Exception\BadRequestException; use Symfony\AI\Platform\Exception\RateLimitExceededException; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** @@ -29,7 +29,7 @@ final class ResultConverter implements ResultConverterInterface { public function supports(Model $model): bool { - return $model instanceof Embeddings; + return $model instanceof EmbeddingsModel; } public function convert(RawResultInterface $result, array $options = []): VectorResult diff --git a/src/platform/src/Bridge/OpenRouter/ModelApiCatalog.php b/src/platform/src/Bridge/OpenRouter/ModelApiCatalog.php index 36bda17e1..7008bbbaf 100644 --- a/src/platform/src/Bridge/OpenRouter/ModelApiCatalog.php +++ b/src/platform/src/Bridge/OpenRouter/ModelApiCatalog.php @@ -14,6 +14,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\Contracts\HttpClient\HttpClientInterface; /** @@ -107,14 +108,14 @@ protected function fetchRemoteModels(): iterable } /** - * @return iterable, capabilities: list}> + * @return iterable, capabilities: list}> */ protected function fetchRemoteEmbeddings(): iterable { $responseEmbeddings = $this->httpClient->request('GET', 'https://openrouter.ai/api/v1/embeddings/models'); foreach ($responseEmbeddings->toArray()['data'] as $embedding) { yield $embedding['id'] => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [Capability::INPUT_TEXT, Capability::EMBEDDINGS], ]; } diff --git a/src/platform/src/Bridge/OpenRouter/ModelCatalog.php b/src/platform/src/Bridge/OpenRouter/ModelCatalog.php index 54bda0b5c..d2d5bfaff 100644 --- a/src/platform/src/Bridge/OpenRouter/ModelCatalog.php +++ b/src/platform/src/Bridge/OpenRouter/ModelCatalog.php @@ -14,6 +14,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\EmbeddingsModel; /** * @author Oskar Stark @@ -2582,147 +2583,147 @@ public function __construct( // Embeddings 'thenlper/gte-base' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'thenlper/gte-large' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'intfloat/e5-large-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'intfloat/e5-base-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'intfloat/multilingual-e5-large' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'sentence-transformers/paraphrase-minilm-l6-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'sentence-transformers/all-minilm-l12-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'baai/bge-base-en-v1.5' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'sentence-transformers/multi-qa-mpnet-base-dot-v1' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'baai/bge-large-en-v1.5' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'baai/bge-m3' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'sentence-transformers/all-mpnet-base-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'sentence-transformers/all-minilm-l6-v2' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'mistralai/mistral-embed-2312' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'google/gemini-embedding-001' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'openai/text-embedding-ada-002' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'mistralai/codestral-embed-2505' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'openai/text-embedding-3-large' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'openai/text-embedding-3-small' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'qwen/qwen3-embedding-8b' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, ], ], 'qwen/qwen3-embedding-4b' => [ - 'class' => Embeddings::class, + 'class' => EmbeddingsModel::class, 'capabilities' => [ Capability::INPUT_TEXT, Capability::EMBEDDINGS, diff --git a/src/platform/src/Bridge/OpenRouter/PlatformFactory.php b/src/platform/src/Bridge/OpenRouter/PlatformFactory.php index 9261a7cce..1e2e0a4fc 100644 --- a/src/platform/src/Bridge/OpenRouter/PlatformFactory.php +++ b/src/platform/src/Bridge/OpenRouter/PlatformFactory.php @@ -15,10 +15,6 @@ use Symfony\AI\Platform\Bridge\Gemini\Contract\AssistantMessageNormalizer; use Symfony\AI\Platform\Bridge\Gemini\Contract\MessageBagNormalizer; use Symfony\AI\Platform\Bridge\Gemini\Contract\UserMessageNormalizer; -use Symfony\AI\Platform\Bridge\OpenRouter\Completions\ModelClient as CompletionsModelClient; -use Symfony\AI\Platform\Bridge\OpenRouter\Completions\ResultConverter as CompletionsResultConverter; -use Symfony\AI\Platform\Bridge\OpenRouter\Embeddings\ModelClient as EmbeddingsModelClient; -use Symfony\AI\Platform\Bridge\OpenRouter\Embeddings\ResultConverter as EmbeddingsResultConverter; use Symfony\AI\Platform\Contract; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; use Symfony\AI\Platform\Platform; @@ -40,8 +36,8 @@ public static function create( $httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); return new Platform( - [new EmbeddingsModelClient($httpClient, $apiKey), new CompletionsModelClient($httpClient, $apiKey)], - [new EmbeddingsResultConverter(), new CompletionsResultConverter()], + [new Embeddings\ModelClient($httpClient, $apiKey), new Completions\ModelClient($httpClient, $apiKey)], + [new Embeddings\ResultConverter(), new Completions\ResultConverter()], $modelCatalog, $contract ?? Contract::create( new AssistantMessageNormalizer(), diff --git a/src/platform/src/Bridge/Perplexity/ModelClient.php b/src/platform/src/Bridge/Perplexity/ModelClient.php index f412ead24..7cc143f8d 100644 --- a/src/platform/src/Bridge/Perplexity/ModelClient.php +++ b/src/platform/src/Bridge/Perplexity/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\Component\HttpClient\EventSourceHttpClient; diff --git a/src/platform/src/Bridge/Perplexity/ResultConverter.php b/src/platform/src/Bridge/Perplexity/ResultConverter.php index 147886d7f..c1b61aff7 100644 --- a/src/platform/src/Bridge/Perplexity/ResultConverter.php +++ b/src/platform/src/Bridge/Perplexity/ResultConverter.php @@ -19,7 +19,7 @@ use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\StreamResult; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Mathieu Santostefano diff --git a/src/platform/src/Bridge/Replicate/LlamaModelClient.php b/src/platform/src/Bridge/Replicate/LlamaModelClient.php index 9f84ee909..f4be1688e 100644 --- a/src/platform/src/Bridge/Replicate/LlamaModelClient.php +++ b/src/platform/src/Bridge/Replicate/LlamaModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\Meta\Llama; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; /** diff --git a/src/platform/src/Bridge/Replicate/LlamaResultConverter.php b/src/platform/src/Bridge/Replicate/LlamaResultConverter.php index 8c5ff8cbf..39c93fbe3 100644 --- a/src/platform/src/Bridge/Replicate/LlamaResultConverter.php +++ b/src/platform/src/Bridge/Replicate/LlamaResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Bridge/Scaleway/Embeddings/ModelClient.php b/src/platform/src/Bridge/Scaleway/Embeddings/ModelClient.php index 3072cfd0e..98816823d 100644 --- a/src/platform/src/Bridge/Scaleway/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/Scaleway/Embeddings/ModelClient.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Bridge\Scaleway\Embeddings; use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Scaleway/Embeddings/ResultConverter.php b/src/platform/src/Bridge/Scaleway/Embeddings/ResultConverter.php index 97cdc6134..93965c28c 100644 --- a/src/platform/src/Bridge/Scaleway/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/Scaleway/Embeddings/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/Scaleway/Llm/ModelClient.php b/src/platform/src/Bridge/Scaleway/Llm/ModelClient.php index f6d59c1c2..c857c59c2 100644 --- a/src/platform/src/Bridge/Scaleway/Llm/ModelClient.php +++ b/src/platform/src/Bridge/Scaleway/Llm/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Bridge\Scaleway\Scaleway; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Scaleway/Llm/ResultConverter.php b/src/platform/src/Bridge/Scaleway/Llm/ResultConverter.php index fbfc6b549..2e6101584 100644 --- a/src/platform/src/Bridge/Scaleway/Llm/ResultConverter.php +++ b/src/platform/src/Bridge/Scaleway/Llm/ResultConverter.php @@ -22,7 +22,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Marcus Stöhr diff --git a/src/platform/src/Bridge/TransformersPhp/ModelClient.php b/src/platform/src/Bridge/TransformersPhp/ModelClient.php index a41e38791..f7892f840 100644 --- a/src/platform/src/Bridge/TransformersPhp/ModelClient.php +++ b/src/platform/src/Bridge/TransformersPhp/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Exception\InvalidArgumentException; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use function Codewithkyrian\Transformers\Pipelines\pipeline; diff --git a/src/platform/src/Bridge/TransformersPhp/ResultConverter.php b/src/platform/src/Bridge/TransformersPhp/ResultConverter.php index 587844dbd..0dbe1a70f 100644 --- a/src/platform/src/Bridge/TransformersPhp/ResultConverter.php +++ b/src/platform/src/Bridge/TransformersPhp/ResultConverter.php @@ -16,9 +16,9 @@ use Symfony\AI\Platform\Result\ObjectResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; -final readonly class ResultConverter implements ResultConverterInterface +final class ResultConverter implements ResultConverterInterface { public function supports(Model $model): bool { diff --git a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php index 8a0ae98da..a04ad45dd 100644 --- a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\VertexAi\Embeddings; use Symfony\AI\Platform\Model as BaseModel; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/VertexAi/Embeddings/ResultConverter.php b/src/platform/src/Bridge/VertexAi/Embeddings/ResultConverter.php index 8c637170a..5f41b7b75 100644 --- a/src/platform/src/Bridge/VertexAi/Embeddings/ResultConverter.php +++ b/src/platform/src/Bridge/VertexAi/Embeddings/ResultConverter.php @@ -15,7 +15,7 @@ use Symfony\AI\Platform\Model as BaseModel; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php index 70155f758..6d76bbcb8 100644 --- a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php @@ -12,7 +12,7 @@ namespace Symfony\AI\Platform\Bridge\VertexAi\Gemini; use Symfony\AI\Platform\Model as BaseModel; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\StructuredOutput\PlatformSubscriber; use Symfony\Component\HttpClient\EventSourceHttpClient; diff --git a/src/platform/src/Bridge/VertexAi/Gemini/ResultConverter.php b/src/platform/src/Bridge/VertexAi/Gemini/ResultConverter.php index 16a70e391..0c22d9ef3 100644 --- a/src/platform/src/Bridge/VertexAi/Gemini/ResultConverter.php +++ b/src/platform/src/Bridge/VertexAi/Gemini/ResultConverter.php @@ -23,7 +23,7 @@ use Symfony\AI\Platform\Result\TextResult; use Symfony\AI\Platform\Result\ToolCall; use Symfony\AI\Platform\Result\ToolCallResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; /** diff --git a/src/platform/src/Bridge/Voyage/ModelClient.php b/src/platform/src/Bridge/Voyage/ModelClient.php index 27ee31b73..53116c7f6 100644 --- a/src/platform/src/Bridge/Voyage/ModelClient.php +++ b/src/platform/src/Bridge/Voyage/ModelClient.php @@ -13,7 +13,7 @@ use Symfony\AI\Platform\Capability; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\Contracts\HttpClient\HttpClientInterface; diff --git a/src/platform/src/Bridge/Voyage/ResultConverter.php b/src/platform/src/Bridge/Voyage/ResultConverter.php index e4619cf06..72ac651dc 100644 --- a/src/platform/src/Bridge/Voyage/ResultConverter.php +++ b/src/platform/src/Bridge/Voyage/ResultConverter.php @@ -16,7 +16,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/Bridge/OpenRouter/Embeddings.php b/src/platform/src/Model/CompletionsModel.php similarity index 77% rename from src/platform/src/Bridge/OpenRouter/Embeddings.php rename to src/platform/src/Model/CompletionsModel.php index dd5c16fd6..ac5b3631f 100644 --- a/src/platform/src/Bridge/OpenRouter/Embeddings.php +++ b/src/platform/src/Model/CompletionsModel.php @@ -9,10 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform\Bridge\OpenRouter; +namespace Symfony\AI\Platform\Model; use Symfony\AI\Platform\Model; -class Embeddings extends Model +class CompletionsModel extends Model { } diff --git a/src/platform/src/Bridge/AiMlApi/Embeddings.php b/src/platform/src/Model/EmbeddingsModel.php similarity index 67% rename from src/platform/src/Bridge/AiMlApi/Embeddings.php rename to src/platform/src/Model/EmbeddingsModel.php index c600792e5..94668d9a2 100644 --- a/src/platform/src/Bridge/AiMlApi/Embeddings.php +++ b/src/platform/src/Model/EmbeddingsModel.php @@ -9,13 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform\Bridge\AiMlApi; +namespace Symfony\AI\Platform\Model; use Symfony\AI\Platform\Model; -/** - * @author Tim Lochmüller + * This default implementation is based on OpenAI's initial completion endpoint, that got later adopted by other + * providers as well. It can be used by any bridge or directly with the default PlatformFactory. + * + * @author Christopher Hertel */ -final class GptModelClient implements ModelClientInterface +class CompletionsModelClient implements ModelClientInterface { private readonly EventSourceHttpClient $httpClient; public function __construct( - HttpClientInterface $httpClient, #[\SensitiveParameter] private readonly string $apiKey, private readonly string $baseUrl, + private readonly string $path = '/v1/chat/completions', + ?HttpClientInterface $httpClient = null, ) { $this->httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient); } public function supports(Model $model): bool { - return $model instanceof Gpt; + return $model instanceof CompletionsModel; } - public function request(Model $model, array|string $payload, array $options = []): RawResultInterface + public function request(Model $model, array|string $payload, array $options = []): RawHttpResult { - return new RawHttpResult($this->httpClient->request('POST', \sprintf('%s/chat/completions', $this->baseUrl), [ + return new RawHttpResult($this->httpClient->request('POST', $this->baseUrl.$this->path, [ 'auth_bearer' => $this->apiKey, - 'json' => \is_array($payload) ? array_merge($payload, $options) : $payload, + 'headers' => ['Content-Type' => 'application/json'], + 'json' => array_merge($options, $payload), ])); } } diff --git a/src/platform/src/Bridge/Albert/EmbeddingsModelClient.php b/src/platform/src/ModelClient/EmbeddingsModelClient.php similarity index 56% rename from src/platform/src/Bridge/Albert/EmbeddingsModelClient.php rename to src/platform/src/ModelClient/EmbeddingsModelClient.php index 67f3ebf46..84f3a345d 100644 --- a/src/platform/src/Bridge/Albert/EmbeddingsModelClient.php +++ b/src/platform/src/ModelClient/EmbeddingsModelClient.php @@ -9,36 +9,39 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform\Bridge\Albert; +namespace Symfony\AI\Platform\ModelClient; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; use Symfony\AI\Platform\Model; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\Result\RawHttpResult; -use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\Contracts\HttpClient\HttpClientInterface; /** - * @author Oskar Stark + * This default implementation is based on OpenAI's initial embeddings endpoint, that got later adopted by other + * providers as well. It can be used by any bridge or directly with the default PlatformFactory. + * + * @author Christopher Hertel */ -final class EmbeddingsModelClient implements ModelClientInterface +class EmbeddingsModelClient implements ModelClientInterface { public function __construct( - private readonly HttpClientInterface $httpClient, #[\SensitiveParameter] private readonly string $apiKey, private readonly string $baseUrl, + private readonly string $path = '/v1/embeddings', + private readonly ?HttpClientInterface $httpClient = null, ) { } public function supports(Model $model): bool { - return $model instanceof Embeddings; + return $model instanceof EmbeddingsModel; } - public function request(Model $model, array|string $payload, array $options = []): RawResultInterface + public function request(Model $model, array|string $payload, array $options = []): RawHttpResult { - return new RawHttpResult($this->httpClient->request('POST', \sprintf('%s/embeddings', $this->baseUrl), [ + return new RawHttpResult($this->httpClient->request('POST', $this->baseUrl.$this->path, [ 'auth_bearer' => $this->apiKey, + 'headers' => ['Content-Type' => 'application/json'], 'json' => array_merge($options, [ 'model' => $model->getName(), 'input' => $payload, diff --git a/src/platform/src/ModelClientInterface.php b/src/platform/src/ModelClient/ModelClientInterface.php similarity index 89% rename from src/platform/src/ModelClientInterface.php rename to src/platform/src/ModelClient/ModelClientInterface.php index 877bbd7e4..65dc42a15 100644 --- a/src/platform/src/ModelClientInterface.php +++ b/src/platform/src/ModelClient/ModelClientInterface.php @@ -9,8 +9,9 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform; +namespace Symfony\AI\Platform\ModelClient; +use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; /** diff --git a/src/platform/src/Platform.php b/src/platform/src/Platform.php index 16ff947c7..9881d87fb 100644 --- a/src/platform/src/Platform.php +++ b/src/platform/src/Platform.php @@ -16,8 +16,10 @@ use Symfony\AI\Platform\Event\ResultEvent; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Result\DeferredResult; use Symfony\AI\Platform\Result\RawResultInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; /** * @author Christopher Hertel diff --git a/src/platform/src/Result/DeferredResult.php b/src/platform/src/Result/DeferredResult.php index ea9ce05cd..26fa73f8d 100644 --- a/src/platform/src/Result/DeferredResult.php +++ b/src/platform/src/Result/DeferredResult.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Exception\ExceptionInterface; use Symfony\AI\Platform\Exception\UnexpectedResultTypeException; use Symfony\AI\Platform\Metadata\MetadataAwareTrait; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** diff --git a/src/platform/src/ResultConverter/CompletionsResultConverter.php b/src/platform/src/ResultConverter/CompletionsResultConverter.php new file mode 100644 index 000000000..a462c480c --- /dev/null +++ b/src/platform/src/ResultConverter/CompletionsResultConverter.php @@ -0,0 +1,223 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\ResultConverter; + +use Symfony\AI\Platform\Exception\AuthenticationException; +use Symfony\AI\Platform\Exception\BadRequestException; +use Symfony\AI\Platform\Exception\ContentFilterException; +use Symfony\AI\Platform\Exception\RateLimitExceededException; +use Symfony\AI\Platform\Exception\RuntimeException; +use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Result\ChoiceResult; +use Symfony\AI\Platform\Result\RawHttpResult; +use Symfony\AI\Platform\Result\RawResultInterface; +use Symfony\AI\Platform\Result\ResultInterface; +use Symfony\AI\Platform\Result\StreamResult; +use Symfony\AI\Platform\Result\TextResult; +use Symfony\AI\Platform\Result\ToolCall; +use Symfony\AI\Platform\Result\ToolCallResult; + +/** + * This default implementation is based on the OpenAI GPT completion API. + * + * @author Christopher Hertel + * @author Denis Zunke + */ +class CompletionsResultConverter implements ResultConverterInterface +{ + public function supports(Model $model): bool + { + return $model instanceof CompletionsModel; + } + + public function convert(RawResultInterface|RawHttpResult $result, array $options = []): ResultInterface + { + $response = $result->getObject(); + + if (401 === $response->getStatusCode()) { + $errorMessage = json_decode($response->getContent(false), true)['error']['message']; + throw new AuthenticationException($errorMessage); + } + + if (400 === $response->getStatusCode()) { + $errorMessage = json_decode($response->getContent(false), true)['error']['message'] ?? 'Bad Request'; + throw new BadRequestException($errorMessage); + } + + if (429 === $response->getStatusCode()) { + $headers = $response->getHeaders(false); + $resetTime = $headers['x-ratelimit-reset-requests'][0] + ?? $headers['x-ratelimit-reset-tokens'][0] + ?? null; + + throw new RateLimitExceededException($resetTime ? self::parseResetTime($resetTime) : null); + } + + if ($options['stream'] ?? false) { + return new StreamResult($this->convertStream($result)); + } + + $data = $result->getData(); + + if (isset($data['error']['code']) && 'content_filter' === $data['error']['code']) { + throw new ContentFilterException($data['error']['message']); + } + + if (isset($data['error'])) { + throw new RuntimeException(\sprintf('Error "%s"-%s (%s): "%s".', $data['error']['code'] ?? '-', $data['error']['type'] ?? '-', $data['error']['param'] ?? '-', $data['error']['message'] ?? '-')); + } + + if (!isset($data['choices'])) { + throw new RuntimeException('Response does not contain choices.'); + } + + $choices = array_map($this->convertChoice(...), $data['choices']); + + return 1 === \count($choices) ? $choices[0] : new ChoiceResult(...$choices); + } + + private function convertStream(RawResultInterface|RawHttpResult $result): \Generator + { + $toolCalls = []; + foreach ($result->getDataStream() as $data) { + if ($this->streamIsToolCall($data)) { + $toolCalls = $this->convertStreamToToolCalls($toolCalls, $data); + } + + if ([] !== $toolCalls && $this->isToolCallsStreamFinished($data)) { + yield new ToolCallResult(...array_map($this->convertToolCall(...), $toolCalls)); + } + + if (!isset($data['choices'][0]['delta']['content'])) { + continue; + } + + yield $data['choices'][0]['delta']['content']; + } + } + + /** + * @param array $toolCalls + * @param array $data + * + * @return array + */ + private function convertStreamToToolCalls(array $toolCalls, array $data): array + { + if (!isset($data['choices'][0]['delta']['tool_calls'])) { + return $toolCalls; + } + + foreach ($data['choices'][0]['delta']['tool_calls'] as $i => $toolCall) { + if (isset($toolCall['id'])) { + // initialize tool call + $toolCalls[$i] = [ + 'id' => $toolCall['id'], + 'function' => $toolCall['function'], + ]; + continue; + } + + // add arguments delta to tool call + $toolCalls[$i]['function']['arguments'] .= $toolCall['function']['arguments']; + } + + return $toolCalls; + } + + /** + * @param array $data + */ + private function streamIsToolCall(array $data): bool + { + return isset($data['choices'][0]['delta']['tool_calls']); + } + + /** + * @param array $data + */ + private function isToolCallsStreamFinished(array $data): bool + { + return isset($data['choices'][0]['finish_reason']) && 'tool_calls' === $data['choices'][0]['finish_reason']; + } + + /** + * @param array{ + * index: int, + * message: array{ + * role: 'assistant', + * content: ?string, + * tool_calls: array{ + * id: string, + * type: 'function', + * function: array{ + * name: string, + * arguments: string + * }, + * }, + * refusal: ?mixed + * }, + * logprobs: string, + * finish_reason: 'stop'|'length'|'tool_calls'|'content_filter', + * } $choice + */ + private function convertChoice(array $choice): ToolCallResult|TextResult + { + if ('tool_calls' === $choice['finish_reason']) { + return new ToolCallResult(...array_map([$this, 'convertToolCall'], $choice['message']['tool_calls'])); + } + + if (\in_array($choice['finish_reason'], ['stop', 'length'], true)) { + return new TextResult($choice['message']['content']); + } + + throw new RuntimeException(\sprintf('Unsupported finish reason "%s".', $choice['finish_reason'])); + } + + /** + * @param array{ + * id: string, + * type: 'function', + * function: array{ + * name: string, + * arguments: string + * } + * } $toolCall + */ + private function convertToolCall(array $toolCall): ToolCall + { + $arguments = json_decode($toolCall['function']['arguments'], true, flags: \JSON_THROW_ON_ERROR); + + return new ToolCall($toolCall['id'], $toolCall['function']['name'], $arguments); + } + + /** + * Converts OpenAI's reset time format (e.g. "1s", "6m0s", "2m30s") into seconds. + * + * Supported formats: + * - "1s" + * - "6m0s" + * - "2m30s" + */ + private static function parseResetTime(string $resetTime): ?int + { + if (preg_match('/^(?:(\d+)m)?(?:(\d+)s)?$/', $resetTime, $matches)) { + $minutes = isset($matches[1]) ? (int) $matches[1] : 0; + $secs = isset($matches[2]) ? (int) $matches[2] : 0; + + return ($minutes * 60) + $secs; + } + + return null; + } +} diff --git a/src/platform/src/Bridge/LmStudio/Embeddings/ResultConverter.php b/src/platform/src/ResultConverter/EmbeddingsResultConverter.php similarity index 76% rename from src/platform/src/Bridge/LmStudio/Embeddings/ResultConverter.php rename to src/platform/src/ResultConverter/EmbeddingsResultConverter.php index cf06c55bf..270a87f01 100644 --- a/src/platform/src/Bridge/LmStudio/Embeddings/ResultConverter.php +++ b/src/platform/src/ResultConverter/EmbeddingsResultConverter.php @@ -9,25 +9,25 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform\Bridge\LmStudio\Embeddings; +namespace Symfony\AI\Platform\ResultConverter; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings; use Symfony\AI\Platform\Exception\RuntimeException; use Symfony\AI\Platform\Model; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; /** + * This default result converter assumes the same response format as OpenAI's embeddings endpoint. + * * @author Christopher Hertel - * @author André Lubian */ -final class ResultConverter implements ResultConverterInterface +class EmbeddingsResultConverter implements ResultConverterInterface { public function supports(Model $model): bool { - return $model instanceof Embeddings; + return $model instanceof EmbeddingsModel; } public function convert(RawResultInterface $result, array $options = []): VectorResult diff --git a/src/platform/src/ResultConverterInterface.php b/src/platform/src/ResultConverter/ResultConverterInterface.php similarity index 90% rename from src/platform/src/ResultConverterInterface.php rename to src/platform/src/ResultConverter/ResultConverterInterface.php index 71f605f3d..c184afade 100644 --- a/src/platform/src/ResultConverterInterface.php +++ b/src/platform/src/ResultConverter/ResultConverterInterface.php @@ -9,9 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\AI\Platform; +namespace Symfony\AI\Platform\ResultConverter; use Symfony\AI\Platform\Exception\ExceptionInterface; +use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; diff --git a/src/platform/src/StructuredOutput/ResultConverter.php b/src/platform/src/StructuredOutput/ResultConverter.php index 2e2c5f5d3..6bf22bad3 100644 --- a/src/platform/src/StructuredOutput/ResultConverter.php +++ b/src/platform/src/StructuredOutput/ResultConverter.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Component\Serializer\Exception\ExceptionInterface as SerializerExceptionInterface; use Symfony\Component\Serializer\SerializerInterface; diff --git a/src/platform/src/Test/PlainConverter.php b/src/platform/src/Test/PlainConverter.php index df139ce7f..ca9e829a3 100644 --- a/src/platform/src/Test/PlainConverter.php +++ b/src/platform/src/Test/PlainConverter.php @@ -14,7 +14,7 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; final class PlainConverter implements ResultConverterInterface { diff --git a/src/platform/tests/Bridge/AiMlApi/Completions/ModelClientTest.php b/src/platform/tests/Bridge/AiMlApi/Completions/ModelClientTest.php deleted file mode 100644 index 96e770ddd..000000000 --- a/src/platform/tests/Bridge/AiMlApi/Completions/ModelClientTest.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\AiMlApi\Completions; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\AiMlApi\Completions; -use Symfony\AI\Platform\Bridge\AiMlApi\Completions\ModelClient; -use Symfony\Component\HttpClient\MockHttpClient; -use Symfony\Component\HttpClient\Response\MockResponse; - -class ModelClientTest extends TestCase -{ - public function testItIsSupportingTheCorrectModel() - { - $client = new ModelClient('', new MockHttpClient(), 'http://localhost:1234'); - - $this->assertTrue($client->supports(new Completions('test-model'))); - } - - public function testItIsExecutingTheCorrectRequest() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:1234/v1/chat/completions', $url); - self::assertSame( - '{"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}', - $options['body'] - ); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient('', $httpClient, 'http://localhost:1234'); - - $payload = [ - 'model' => 'test-model', - 'messages' => [ - ['role' => 'user', 'content' => 'Hello, world!'], - ], - ]; - - $client->request(new Completions('test-model'), $payload); - } - - public function testItMergesOptionsWithPayload() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:1234/v1/chat/completions', $url); - self::assertSame( - '{"temperature":0.7,"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}', - $options['body'] - ); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient('', $httpClient, 'http://localhost:1234'); - - $payload = [ - 'model' => 'test-model', - 'messages' => [ - ['role' => 'user', 'content' => 'Hello, world!'], - ], - ]; - - $client->request(new Completions('test-model'), $payload, ['temperature' => 0.7]); - } -} diff --git a/src/platform/tests/Bridge/AiMlApi/Completions/ResultConverterTest.php b/src/platform/tests/Bridge/AiMlApi/Completions/ResultConverterTest.php deleted file mode 100644 index 2871399fc..000000000 --- a/src/platform/tests/Bridge/AiMlApi/Completions/ResultConverterTest.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\AiMlApi\Completions; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\AiMlApi\Completions; -use Symfony\AI\Platform\Bridge\AiMlApi\Completions\ResultConverter; - -class ResultConverterTest extends TestCase -{ - public function testItSupportsCompletionsModel() - { - $converter = new ResultConverter(); - - $this->assertTrue($converter->supports(new Completions('test-model'))); - } -} diff --git a/src/platform/tests/Bridge/AiMlApi/Embeddings/ModelClientTest.php b/src/platform/tests/Bridge/AiMlApi/Embeddings/ModelClientTest.php deleted file mode 100644 index 98ed7968b..000000000 --- a/src/platform/tests/Bridge/AiMlApi/Embeddings/ModelClientTest.php +++ /dev/null @@ -1,85 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\AiMlApi\Embeddings; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings\ModelClient; -use Symfony\Component\HttpClient\MockHttpClient; -use Symfony\Component\HttpClient\Response\MockResponse; - -class ModelClientTest extends TestCase -{ - public function testItIsSupportingTheCorrectModel() - { - $client = new ModelClient('', new MockHttpClient(), 'http://localhost:1234'); - - $this->assertTrue($client->supports(new Embeddings('test-model'))); - } - - public function testItIsExecutingTheCorrectRequest() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:1234/v1/embeddings', $url); - self::assertSame('{"model":"test-model","input":"Hello, world!"}', $options['body']); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient('', $httpClient, 'http://localhost:1234'); - - $model = new Embeddings('test-model'); - - $client->request($model, 'Hello, world!'); - } - - public function testItMergesOptionsWithPayload() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:1234/v1/embeddings', $url); - self::assertSame( - '{"custom_option":"value","model":"test-model","input":"Hello, world!"}', - $options['body'] - ); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient('', $httpClient, 'http://localhost:1234'); - - $model = new Embeddings('test-model'); - - $client->request($model, 'Hello, world!', ['custom_option' => 'value']); - } - - public function testItHandlesArrayInput() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:1234/v1/embeddings', $url); - self::assertSame('{"model":"test-model","input":["Hello","world"]}', $options['body']); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient('', $httpClient, 'http://localhost:1234'); - - $model = new Embeddings('test-model'); - - $client->request($model, ['Hello', 'world']); - } -} diff --git a/src/platform/tests/Bridge/AiMlApi/Embeddings/ResultConverterTest.php b/src/platform/tests/Bridge/AiMlApi/Embeddings/ResultConverterTest.php deleted file mode 100644 index 9ce31cd63..000000000 --- a/src/platform/tests/Bridge/AiMlApi/Embeddings/ResultConverterTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\AiMlApi\Embeddings; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings\ResultConverter; -use Symfony\AI\Platform\Exception\RuntimeException; -use Symfony\AI\Platform\Result\RawHttpResult; -use Symfony\Contracts\HttpClient\ResponseInterface; - -class ResultConverterTest extends TestCase -{ - public function testItConvertsAResponseToAVectorResult() - { - $result = $this->createStub(ResponseInterface::class); - $result - ->method('toArray') - ->willReturn( - json_decode( - <<<'JSON' - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": [0.3, 0.4, 0.4] - }, - { - "object": "embedding", - "index": 1, - "embedding": [0.0, 0.0, 0.2] - } - ] - } - JSON, - true - ) - ); - - $vectorResult = (new ResultConverter())->convert(new RawHttpResult($result)); - $convertedContent = $vectorResult->getContent(); - - $this->assertCount(2, $convertedContent); - - $this->assertSame([0.3, 0.4, 0.4], $convertedContent[0]->getData()); - $this->assertSame([0.0, 0.0, 0.2], $convertedContent[1]->getData()); - } - - public function testItThrowsExceptionWhenResponseDoesNotContainData() - { - $result = $this->createStub(ResponseInterface::class); - $result - ->method('toArray') - ->willReturn(['invalid' => 'response']); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Response does not contain data'); - - (new ResultConverter())->convert(new RawHttpResult($result)); - } - - public function testItSupportsEmbeddingsModel() - { - $converter = new ResultConverter(); - - $this->assertTrue($converter->supports(new Embeddings('test-model'))); - } -} diff --git a/src/platform/tests/Bridge/AiMlApi/ModelCatalogTest.php b/src/platform/tests/Bridge/AiMlApi/ModelCatalogTest.php index 8cf6d76fe..e0e888e08 100644 --- a/src/platform/tests/Bridge/AiMlApi/ModelCatalogTest.php +++ b/src/platform/tests/Bridge/AiMlApi/ModelCatalogTest.php @@ -11,10 +11,10 @@ namespace Symfony\AI\Platform\Tests\Bridge\AiMlApi; -use Symfony\AI\Platform\Bridge\AiMlApi\Completions; -use Symfony\AI\Platform\Bridge\AiMlApi\Embeddings; use Symfony\AI\Platform\Bridge\AiMlApi\ModelCatalog; use Symfony\AI\Platform\Capability; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; use Symfony\AI\Platform\Test\ModelCatalogTestCase; @@ -23,148 +23,148 @@ final class ModelCatalogTest extends ModelCatalogTestCase public static function modelsProvider(): iterable { // Completion models (GPT variants) - yield 'gpt-3.5-turbo' => ['gpt-3.5-turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-3.5-turbo-0125' => ['gpt-3.5-turbo-0125', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-3.5-turbo-1106' => ['gpt-3.5-turbo-1106', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-4o' => ['gpt-4o', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-2024-08-06' => ['gpt-4o-2024-08-06', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-2024-05-13' => ['gpt-4o-2024-05-13', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-mini' => ['gpt-4o-mini', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-mini-2024-07-18' => ['gpt-4o-mini-2024-07-18', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4-turbo' => ['gpt-4-turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'gpt-4' => ['gpt-4', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-4-turbo-2024-04-09' => ['gpt-4-turbo-2024-04-09', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-4-0125-preview' => ['gpt-4-0125-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'gpt-4-1106-preview' => ['gpt-4-1106-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'chatgpt-4o-latest' => ['chatgpt-4o-latest', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-audio-preview' => ['gpt-4o-audio-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-mini-audio-preview' => ['gpt-4o-mini-audio-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-search-preview' => ['gpt-4o-search-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'gpt-4o-mini-search-preview' => ['gpt-4o-mini-search-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; - yield 'o1-mini' => ['o1-mini', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'o1-mini-2024-09-12' => ['o1-mini-2024-09-12', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'o1' => ['o1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'o3-mini' => ['o3-mini', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-3.5-turbo' => ['gpt-3.5-turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-3.5-turbo-0125' => ['gpt-3.5-turbo-0125', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-3.5-turbo-1106' => ['gpt-3.5-turbo-1106', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-4o' => ['gpt-4o', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-2024-08-06' => ['gpt-4o-2024-08-06', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-2024-05-13' => ['gpt-4o-2024-05-13', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-mini' => ['gpt-4o-mini', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-mini-2024-07-18' => ['gpt-4o-mini-2024-07-18', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4-turbo' => ['gpt-4-turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'gpt-4' => ['gpt-4', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-4-turbo-2024-04-09' => ['gpt-4-turbo-2024-04-09', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-4-0125-preview' => ['gpt-4-0125-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gpt-4-1106-preview' => ['gpt-4-1106-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'chatgpt-4o-latest' => ['chatgpt-4o-latest', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-audio-preview' => ['gpt-4o-audio-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-mini-audio-preview' => ['gpt-4o-mini-audio-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-search-preview' => ['gpt-4o-search-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'gpt-4o-mini-search-preview' => ['gpt-4o-mini-search-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; + yield 'o1-mini' => ['o1-mini', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'o1-mini-2024-09-12' => ['o1-mini-2024-09-12', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'o1' => ['o1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'o3-mini' => ['o3-mini', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE, Capability::OUTPUT_STRUCTURED]]; // OpenAI future models - yield 'openai/o3-2025-04-16' => ['openai/o3-2025-04-16', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'openai/o3-pro' => ['openai/o3-pro', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'openai/gpt-4.1-2025-04-14' => ['openai/gpt-4.1-2025-04-14', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-4.1-mini-2025-04-14' => ['openai/gpt-4.1-mini-2025-04-14', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-4.1-nano-2025-04-14' => ['openai/gpt-4.1-nano-2025-04-14', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/o4-mini-2025-04-16' => ['openai/o4-mini-2025-04-16', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; - yield 'openai/gpt-oss-20b' => ['openai/gpt-oss-20b', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-oss-120b' => ['openai/gpt-oss-120b', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-5-2025-08-07' => ['openai/gpt-5-2025-08-07', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-5-mini-2025-08-07' => ['openai/gpt-5-mini-2025-08-07', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-5-nano-2025-08-07' => ['openai/gpt-5-nano-2025-08-07', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'openai/gpt-5-chat-latest' => ['openai/gpt-5-chat-latest', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/o3-2025-04-16' => ['openai/o3-2025-04-16', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'openai/o3-pro' => ['openai/o3-pro', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'openai/gpt-4.1-2025-04-14' => ['openai/gpt-4.1-2025-04-14', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-4.1-mini-2025-04-14' => ['openai/gpt-4.1-mini-2025-04-14', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-4.1-nano-2025-04-14' => ['openai/gpt-4.1-nano-2025-04-14', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/o4-mini-2025-04-16' => ['openai/o4-mini-2025-04-16', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::INPUT_IMAGE]]; + yield 'openai/gpt-oss-20b' => ['openai/gpt-oss-20b', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-oss-120b' => ['openai/gpt-oss-120b', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-5-2025-08-07' => ['openai/gpt-5-2025-08-07', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-5-mini-2025-08-07' => ['openai/gpt-5-mini-2025-08-07', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-5-nano-2025-08-07' => ['openai/gpt-5-nano-2025-08-07', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'openai/gpt-5-chat-latest' => ['openai/gpt-5-chat-latest', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // DeepSeek models - yield 'deepseek-chat' => ['deepseek-chat', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'deepseek/deepseek-chat' => ['deepseek/deepseek-chat', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'deepseek/deepseek-chat-v3-0324' => ['deepseek/deepseek-chat-v3-0324', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'deepseek/deepseek-r1' => ['deepseek/deepseek-r1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; - yield 'deepseek-reasoner' => ['deepseek-reasoner', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; - yield 'deepseek/deepseek-prover-v2' => ['deepseek/deepseek-prover-v2', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; - yield 'deepseek/deepseek-chat-v3.1' => ['deepseek/deepseek-chat-v3.1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'deepseek/deepseek-reasoner-v3.1' => ['deepseek/deepseek-reasoner-v3.1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; + yield 'deepseek-chat' => ['deepseek-chat', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'deepseek/deepseek-chat' => ['deepseek/deepseek-chat', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'deepseek/deepseek-chat-v3-0324' => ['deepseek/deepseek-chat-v3-0324', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'deepseek/deepseek-r1' => ['deepseek/deepseek-r1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; + yield 'deepseek-reasoner' => ['deepseek-reasoner', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; + yield 'deepseek/deepseek-prover-v2' => ['deepseek/deepseek-prover-v2', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; + yield 'deepseek/deepseek-chat-v3.1' => ['deepseek/deepseek-chat-v3.1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'deepseek/deepseek-reasoner-v3.1' => ['deepseek/deepseek-reasoner-v3.1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; // Qwen models - yield 'Qwen/Qwen2-72B-Instruct' => ['Qwen/Qwen2-72B-Instruct', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'qwen-max' => ['qwen-max', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'qwen-plus' => ['qwen-plus', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'qwen-turbo' => ['qwen-turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'qwen-max-2025-01-25' => ['qwen-max-2025-01-25', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'Qwen/Qwen2.5-72B-Instruct-Turbo' => ['Qwen/Qwen2.5-72B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'Qwen/QwQ-32B' => ['Qwen/QwQ-32B', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'Qwen/Qwen3-235B-A22B-fp8-tput' => ['Qwen/Qwen3-235B-A22B-fp8-tput', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'alibaba/qwen3-32b' => ['alibaba/qwen3-32b', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'alibaba/qwen3-coder-480b-a35b-instruct' => ['alibaba/qwen3-coder-480b-a35b-instruct', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'alibaba/qwen3-235b-a22b-thinking-2507' => ['alibaba/qwen3-235b-a22b-thinking-2507', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; - yield 'Qwen/Qwen2.5-7B-Instruct-Turbo' => ['Qwen/Qwen2.5-7B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'Qwen/Qwen2.5-Coder-32B-Instruct' => ['Qwen/Qwen2.5-Coder-32B-Instruct', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'Qwen/Qwen2-72B-Instruct' => ['Qwen/Qwen2-72B-Instruct', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'qwen-max' => ['qwen-max', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'qwen-plus' => ['qwen-plus', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'qwen-turbo' => ['qwen-turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'qwen-max-2025-01-25' => ['qwen-max-2025-01-25', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'Qwen/Qwen2.5-72B-Instruct-Turbo' => ['Qwen/Qwen2.5-72B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'Qwen/QwQ-32B' => ['Qwen/QwQ-32B', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'Qwen/Qwen3-235B-A22B-fp8-tput' => ['Qwen/Qwen3-235B-A22B-fp8-tput', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'alibaba/qwen3-32b' => ['alibaba/qwen3-32b', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'alibaba/qwen3-coder-480b-a35b-instruct' => ['alibaba/qwen3-coder-480b-a35b-instruct', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'alibaba/qwen3-235b-a22b-thinking-2507' => ['alibaba/qwen3-235b-a22b-thinking-2507', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT]]; + yield 'Qwen/Qwen2.5-7B-Instruct-Turbo' => ['Qwen/Qwen2.5-7B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'Qwen/Qwen2.5-Coder-32B-Instruct' => ['Qwen/Qwen2.5-Coder-32B-Instruct', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // Mistral models - yield 'mistralai/Mixtral-8x7B-Instruct-v0.1' => ['mistralai/Mixtral-8x7B-Instruct-v0.1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/Mistral-7B-Instruct-v0.2' => ['mistralai/Mistral-7B-Instruct-v0.2', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/Mistral-7B-Instruct-v0.1' => ['mistralai/Mistral-7B-Instruct-v0.1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/Mistral-7B-Instruct-v0.3' => ['mistralai/Mistral-7B-Instruct-v0.3', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/mistral-tiny' => ['mistralai/mistral-tiny', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/mistral-nemo' => ['mistralai/mistral-nemo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'mistralai/codestral-2501' => ['mistralai/codestral-2501', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/Mixtral-8x7B-Instruct-v0.1' => ['mistralai/Mixtral-8x7B-Instruct-v0.1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/Mistral-7B-Instruct-v0.2' => ['mistralai/Mistral-7B-Instruct-v0.2', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/Mistral-7B-Instruct-v0.1' => ['mistralai/Mistral-7B-Instruct-v0.1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/Mistral-7B-Instruct-v0.3' => ['mistralai/Mistral-7B-Instruct-v0.3', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/mistral-tiny' => ['mistralai/mistral-tiny', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/mistral-nemo' => ['mistralai/mistral-nemo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'mistralai/codestral-2501' => ['mistralai/codestral-2501', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // Meta Llama models - yield 'meta-llama/Llama-3.3-70B-Instruct-Turbo' => ['meta-llama/Llama-3.3-70B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Llama-3.2-3B-Instruct-Turbo' => ['meta-llama/Llama-3.2-3B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Meta-Llama-3-8B-Instruct-Lite' => ['meta-llama/Meta-Llama-3-8B-Instruct-Lite', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Llama-3-70b-chat-hf' => ['meta-llama/Llama-3-70b-chat-hf', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/llama-4-scout' => ['meta-llama/llama-4-scout', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'meta-llama/llama-4-maverick' => ['meta-llama/llama-4-maverick', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Llama-3.3-70B-Instruct-Turbo' => ['meta-llama/Llama-3.3-70B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Llama-3.2-3B-Instruct-Turbo' => ['meta-llama/Llama-3.2-3B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Meta-Llama-3-8B-Instruct-Lite' => ['meta-llama/Meta-Llama-3-8B-Instruct-Lite', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Llama-3-70b-chat-hf' => ['meta-llama/Llama-3-70b-chat-hf', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo' => ['meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/llama-4-scout' => ['meta-llama/llama-4-scout', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'meta-llama/llama-4-maverick' => ['meta-llama/llama-4-maverick', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // Claude models - yield 'claude-3-opus-20240229' => ['claude-3-opus-20240229', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-3-haiku-20240307' => ['claude-3-haiku-20240307', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-3-5-sonnet-20240620' => ['claude-3-5-sonnet-20240620', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-3-5-sonnet-20241022' => ['claude-3-5-sonnet-20241022', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-3-5-haiku-20241022' => ['claude-3-5-haiku-20241022', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-3-7-sonnet-20250219' => ['claude-3-7-sonnet-20250219', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'anthropic/claude-opus-4' => ['anthropic/claude-opus-4', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'anthropic/claude-sonnet-4' => ['anthropic/claude-sonnet-4', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'anthropic/claude-opus-4.1' => ['anthropic/claude-opus-4.1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-opus-4-1' => ['claude-opus-4-1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'claude-opus-4-1-20250805' => ['claude-opus-4-1-20250805', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-opus-20240229' => ['claude-3-opus-20240229', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-haiku-20240307' => ['claude-3-haiku-20240307', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-5-sonnet-20240620' => ['claude-3-5-sonnet-20240620', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-5-sonnet-20241022' => ['claude-3-5-sonnet-20241022', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-5-haiku-20241022' => ['claude-3-5-haiku-20241022', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-3-7-sonnet-20250219' => ['claude-3-7-sonnet-20250219', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'anthropic/claude-opus-4' => ['anthropic/claude-opus-4', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'anthropic/claude-sonnet-4' => ['anthropic/claude-sonnet-4', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'anthropic/claude-opus-4.1' => ['anthropic/claude-opus-4.1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-opus-4-1' => ['claude-opus-4-1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'claude-opus-4-1-20250805' => ['claude-opus-4-1-20250805', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; // Gemini models - yield 'gemini-2.0-flash-exp' => ['gemini-2.0-flash-exp', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'gemini-2.0-flash' => ['gemini-2.0-flash', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'google/gemini-2.5-flash-lite-preview' => ['google/gemini-2.5-flash-lite-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'google/gemini-2.5-flash' => ['google/gemini-2.5-flash', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'google/gemini-2.5-pro' => ['google/gemini-2.5-pro', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; - yield 'google/gemma-2-27b-it' => ['google/gemma-2-27b-it', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'google/gemma-3-4b-it' => ['google/gemma-3-4b-it', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'google/gemma-3-12b-it' => ['google/gemma-3-12b-it', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'google/gemma-3-27b-it' => ['google/gemma-3-27b-it', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'google/gemma-3n-e4b-it' => ['google/gemma-3n-e4b-it', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'gemini-2.0-flash-exp' => ['gemini-2.0-flash-exp', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'gemini-2.0-flash' => ['gemini-2.0-flash', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'google/gemini-2.5-flash-lite-preview' => ['google/gemini-2.5-flash-lite-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'google/gemini-2.5-flash' => ['google/gemini-2.5-flash', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'google/gemini-2.5-pro' => ['google/gemini-2.5-pro', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING, Capability::INPUT_IMAGE]]; + yield 'google/gemma-2-27b-it' => ['google/gemma-2-27b-it', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'google/gemma-3-4b-it' => ['google/gemma-3-4b-it', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'google/gemma-3-12b-it' => ['google/gemma-3-12b-it', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'google/gemma-3-27b-it' => ['google/gemma-3-27b-it', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'google/gemma-3n-e4b-it' => ['google/gemma-3n-e4b-it', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // X.AI models - yield 'x-ai/grok-3-beta' => ['x-ai/grok-3-beta', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'x-ai/grok-3-mini-beta' => ['x-ai/grok-3-mini-beta', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'x-ai/grok-4-07-09' => ['x-ai/grok-4-07-09', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'x-ai/grok-3-beta' => ['x-ai/grok-3-beta', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'x-ai/grok-3-mini-beta' => ['x-ai/grok-3-mini-beta', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'x-ai/grok-4-07-09' => ['x-ai/grok-4-07-09', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // Other models - yield 'anthracite-org/magnum-v4-72b' => ['anthracite-org/magnum-v4-72b', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'nvidia/llama-3.1-nemotron-70b-instruct' => ['nvidia/llama-3.1-nemotron-70b-instruct', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'cohere/command-r-plus' => ['cohere/command-r-plus', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'cohere/command-a' => ['cohere/command-a', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'MiniMax-Text-01' => ['MiniMax-Text-01', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'minimax/m1' => ['minimax/m1', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'moonshot/kimi-k2-preview' => ['moonshot/kimi-k2-preview', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'perplexity/sonar' => ['perplexity/sonar', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'perplexity/sonar-pro' => ['perplexity/sonar-pro', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'zhipu/glm-4.5-air' => ['zhipu/glm-4.5-air', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; - yield 'zhipu/glm-4.5' => ['zhipu/glm-4.5', Completions::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'anthracite-org/magnum-v4-72b' => ['anthracite-org/magnum-v4-72b', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'nvidia/llama-3.1-nemotron-70b-instruct' => ['nvidia/llama-3.1-nemotron-70b-instruct', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'cohere/command-r-plus' => ['cohere/command-r-plus', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'cohere/command-a' => ['cohere/command-a', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'MiniMax-Text-01' => ['MiniMax-Text-01', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'minimax/m1' => ['minimax/m1', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'moonshot/kimi-k2-preview' => ['moonshot/kimi-k2-preview', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'perplexity/sonar' => ['perplexity/sonar', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'perplexity/sonar-pro' => ['perplexity/sonar-pro', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'zhipu/glm-4.5-air' => ['zhipu/glm-4.5-air', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; + yield 'zhipu/glm-4.5' => ['zhipu/glm-4.5', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING, Capability::TOOL_CALLING]]; // Embedding models - yield 'text-embedding-3-small' => ['text-embedding-3-small', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'text-embedding-3-large' => ['text-embedding-3-large', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'text-embedding-ada-002' => ['text-embedding-ada-002', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'togethercomputer/m2-bert-80M-32k-retrieval' => ['togethercomputer/m2-bert-80M-32k-retrieval', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'BAAI/bge-base-en-v1.5' => ['BAAI/bge-base-en-v1.5', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'BAAI/bge-large-en-v1.' => ['BAAI/bge-large-en-v1.', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-large-2-instruct' => ['voyage-large-2-instruct', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-finance-2' => ['voyage-finance-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-multilingual-2' => ['voyage-multilingual-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-law-2' => ['voyage-law-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-code-2' => ['voyage-code-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-large-2' => ['voyage-large-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'voyage-2' => ['voyage-2', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'textembedding-gecko@003' => ['textembedding-gecko@003', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'textembedding-gecko-multilingual@001' => ['textembedding-gecko-multilingual@001', Embeddings::class, [Capability::INPUT_MULTIPLE]]; - yield 'text-multilingual-embedding-002' => ['text-multilingual-embedding-002', Embeddings::class, [Capability::INPUT_MULTIPLE]]; + yield 'text-embedding-3-small' => ['text-embedding-3-small', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'text-embedding-3-large' => ['text-embedding-3-large', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'text-embedding-ada-002' => ['text-embedding-ada-002', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'togethercomputer/m2-bert-80M-32k-retrieval' => ['togethercomputer/m2-bert-80M-32k-retrieval', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'BAAI/bge-base-en-v1.5' => ['BAAI/bge-base-en-v1.5', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'BAAI/bge-large-en-v1.' => ['BAAI/bge-large-en-v1.', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-large-2-instruct' => ['voyage-large-2-instruct', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-finance-2' => ['voyage-finance-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-multilingual-2' => ['voyage-multilingual-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-law-2' => ['voyage-law-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-code-2' => ['voyage-code-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-large-2' => ['voyage-large-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'voyage-2' => ['voyage-2', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'textembedding-gecko@003' => ['textembedding-gecko@003', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'textembedding-gecko-multilingual@001' => ['textembedding-gecko-multilingual@001', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; + yield 'text-multilingual-embedding-002' => ['text-multilingual-embedding-002', EmbeddingsModel::class, [Capability::INPUT_MULTIPLE]]; } protected function createModelCatalog(): ModelCatalogInterface diff --git a/src/platform/tests/Bridge/Albert/EmbeddingsModelClientTest.php b/src/platform/tests/Bridge/Albert/EmbeddingsModelClientTest.php deleted file mode 100644 index a30f9d166..000000000 --- a/src/platform/tests/Bridge/Albert/EmbeddingsModelClientTest.php +++ /dev/null @@ -1,135 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\Albert; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\Albert\EmbeddingsModelClient; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt; -use Symfony\Component\HttpClient\MockHttpClient; -use Symfony\Component\HttpClient\Response\JsonMockResponse; -use Symfony\Component\HttpClient\Response\MockResponse; -use Symfony\Contracts\HttpClient\ResponseInterface as HttpResponse; - -final class EmbeddingsModelClientTest extends TestCase -{ - public function testSupportsEmbeddingsModel() - { - $client = new EmbeddingsModelClient( - new MockHttpClient(), - 'test-api-key', - 'https://albert.example.com/' - ); - - $embeddingsModel = new Embeddings('embedding-small'); - $this->assertTrue($client->supports($embeddingsModel)); - } - - public function testDoesNotSupportNonEmbeddingsModel() - { - $client = new EmbeddingsModelClient( - new MockHttpClient(), - 'test-api-key', - 'https://albert.example.com/' - ); - - $gptModel = new Gpt('gpt-3.5-turbo'); - $this->assertFalse($client->supports($gptModel)); - } - - public function testItIsExecutingTheCorrectRequest() - { - $resultCallback = static function (string $method, string $url, array $options): HttpResponse { - self::assertSame('POST', $method); - self::assertSame('https://albert.example.com/v1/embeddings', $url); - self::assertSame('Authorization: Bearer api-key', $options['normalized_headers']['authorization'][0]); - self::assertSame('{"model":"embedding-small","input":"test text"}', $options['body']); - - return new MockResponse(); - }; - $httpClient = new MockHttpClient([$resultCallback]); - $modelClient = new EmbeddingsModelClient($httpClient, 'api-key', 'https://albert.example.com/v1'); - $modelClient->request(new Embeddings('embedding-small'), 'test text', []); - } - - public function testItIsExecutingTheCorrectRequestWithCustomOptions() - { - $resultCallback = static function (string $method, string $url, array $options): HttpResponse { - self::assertSame('POST', $method); - self::assertSame('https://albert.example.com/v1/embeddings', $url); - self::assertSame('Authorization: Bearer api-key', $options['normalized_headers']['authorization'][0]); - self::assertSame('{"dimensions":256,"model":"embedding-small","input":"test text"}', $options['body']); - - return new MockResponse(); - }; - $httpClient = new MockHttpClient([$resultCallback]); - $modelClient = new EmbeddingsModelClient($httpClient, 'api-key', 'https://albert.example.com/v1'); - $modelClient->request(new Embeddings('embedding-small'), 'test text', ['dimensions' => 256]); - } - - public function testItIsExecutingTheCorrectRequestWithArrayInput() - { - $resultCallback = static function (string $method, string $url, array $options): HttpResponse { - self::assertSame('POST', $method); - self::assertSame('https://albert.example.com/v1/embeddings', $url); - self::assertSame('Authorization: Bearer api-key', $options['normalized_headers']['authorization'][0]); - self::assertSame('{"model":"embedding-small","input":["text1","text2","text3"]}', $options['body']); - - return new MockResponse(); - }; - $httpClient = new MockHttpClient([$resultCallback]); - $modelClient = new EmbeddingsModelClient($httpClient, 'api-key', 'https://albert.example.com/v1'); - $modelClient->request(new Embeddings('embedding-small'), ['text1', 'text2', 'text3'], []); - } - - public function testRequestHandlesBaseUrlWithoutTrailingSlash() - { - $capturedUrl = null; - $httpClient = new MockHttpClient(function ($method, $url) use (&$capturedUrl) { - $capturedUrl = $url; - - return new JsonMockResponse(['data' => []]); - }); - - $client = new EmbeddingsModelClient( - $httpClient, - 'test-api-key', - 'https://albert.example.com/v1' - ); - - $model = new Embeddings('embedding-small'); - $client->request($model, ['input' => 'test']); - - $this->assertSame('https://albert.example.com/v1/embeddings', $capturedUrl); - } - - public function testRequestHandlesBaseUrlWithTrailingSlash() - { - $capturedUrl = null; - $httpClient = new MockHttpClient(function ($method, $url) use (&$capturedUrl) { - $capturedUrl = $url; - - return new JsonMockResponse(['data' => []]); - }); - - $client = new EmbeddingsModelClient( - $httpClient, - 'test-api-key', - 'https://albert.example.com/v1' - ); - - $model = new Embeddings('embedding-small'); - $client->request($model, ['input' => 'test']); - - $this->assertSame('https://albert.example.com/v1/embeddings', $capturedUrl); - } -} diff --git a/src/platform/tests/Bridge/Albert/GptModelClientTest.php b/src/platform/tests/Bridge/Albert/GptModelClientTest.php deleted file mode 100644 index 04eb84c91..000000000 --- a/src/platform/tests/Bridge/Albert/GptModelClientTest.php +++ /dev/null @@ -1,200 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\Albert; - -use PHPUnit\Framework\Attributes\DataProvider; -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\Albert\GptModelClient; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt; -use Symfony\Component\HttpClient\EventSourceHttpClient; -use Symfony\Component\HttpClient\MockHttpClient; -use Symfony\Component\HttpClient\Response\JsonMockResponse; - -final class GptModelClientTest extends TestCase -{ - public function testConstructorWrapsHttpClientInEventSourceHttpClient() - { - self::expectNotToPerformAssertions(); - - $mockHttpClient = new MockHttpClient(); - - $client = new GptModelClient( - $mockHttpClient, - 'test-api-key', - 'https://albert.example.com/' - ); - - // We can't directly test the private property, but we can verify the behavior - // by making a request and checking that it works correctly - $mockResponse = new JsonMockResponse(['choices' => []]); - $mockHttpClient->setResponseFactory([$mockResponse]); - - $model = new Gpt('gpt-3.5-turbo'); - $client->request($model, ['messages' => []]); - } - - public function testConstructorAcceptsEventSourceHttpClient() - { - self::expectNotToPerformAssertions(); - - $mockHttpClient = new MockHttpClient(); - $eventSourceClient = new EventSourceHttpClient($mockHttpClient); - - $client = new GptModelClient( - $eventSourceClient, - 'test-api-key', - 'https://albert.example.com/' - ); - - // Verify it works with EventSourceHttpClient - $mockResponse = new JsonMockResponse(['choices' => []]); - $mockHttpClient->setResponseFactory([$mockResponse]); - - $model = new Gpt('gpt-3.5-turbo'); - $client->request($model, ['messages' => []]); - } - - public function testSupportsGptModel() - { - $client = new GptModelClient( - new MockHttpClient(), - 'test-api-key', - 'https://albert.example.com/' - ); - - $gptModel = new Gpt('gpt-3.5-turbo'); - $this->assertTrue($client->supports($gptModel)); - } - - public function testDoesNotSupportNonGptModel() - { - $client = new GptModelClient( - new MockHttpClient(), - 'test-api-key', - 'https://albert.example.com/' - ); - - $embeddingsModel = new Embeddings('text-embedding-ada-002'); - $this->assertFalse($client->supports($embeddingsModel)); - } - - #[DataProvider('providePayloadToJson')] - public function testRequestSendsCorrectHttpRequest(array|string $payload, array $options, array|string $expectedJson) - { - $capturedRequest = null; - $httpClient = new MockHttpClient(function ($method, $url, $options) use (&$capturedRequest) { - $capturedRequest = ['method' => $method, 'url' => $url, 'options' => $options]; - - return new JsonMockResponse(['choices' => []]); - }); - - $client = new GptModelClient( - $httpClient, - 'test-api-key', - 'https://albert.example.com/v1' - ); - - $model = new Gpt('gpt-3.5-turbo'); - $result = $client->request($model, $payload, $options); - - $this->assertNotNull($capturedRequest); - $this->assertSame('POST', $capturedRequest['method']); - $this->assertSame('https://albert.example.com/v1/chat/completions', $capturedRequest['url']); - $this->assertArrayHasKey('normalized_headers', $capturedRequest['options']); - $this->assertArrayHasKey('authorization', $capturedRequest['options']['normalized_headers']); - $this->assertStringContainsString('Bearer test-api-key', (string) $capturedRequest['options']['normalized_headers']['authorization'][0]); - - // Check JSON body - it might be in 'body' after processing - if (isset($capturedRequest['options']['body'])) { - $actualJson = json_decode($capturedRequest['options']['body'], true); - $this->assertEquals($expectedJson, $actualJson); - } else { - $this->assertSame($expectedJson, $capturedRequest['options']['json']); - } - } - - public static function providePayloadToJson(): iterable - { - yield 'with array payload and no options' => [ - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], - [], - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], - ]; - - yield 'with string payload and no options' => [ - 'test message', - [], - 'test message', - ]; - - yield 'with array payload and options' => [ - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], - ['temperature' => 0.7, 'max_tokens' => 150], - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 0.7, 'max_tokens' => 150], - ]; - - yield 'options override payload values' => [ - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 1.0], - ['temperature' => 0.5], - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'temperature' => 0.5], - ]; - - yield 'with streaming option' => [ - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo'], - ['stream' => true], - ['messages' => [['role' => 'user', 'content' => 'Hello']], 'model' => 'gpt-3.5-turbo', 'stream' => true], - ]; - } - - public function testRequestHandlesBaseUrlWithoutTrailingSlash() - { - $capturedUrl = null; - $httpClient = new MockHttpClient(function ($method, $url) use (&$capturedUrl) { - $capturedUrl = $url; - - return new JsonMockResponse(['choices' => []]); - }); - - $client = new GptModelClient( - $httpClient, - 'test-api-key', - 'https://albert.example.com/v1' - ); - - $model = new Gpt('gpt-3.5-turbo'); - $client->request($model, ['messages' => []]); - - $this->assertSame('https://albert.example.com/v1/chat/completions', $capturedUrl); - } - - public function testRequestHandlesBaseUrlWithTrailingSlash() - { - $capturedUrl = null; - $httpClient = new MockHttpClient(function ($method, $url) use (&$capturedUrl) { - $capturedUrl = $url; - - return new JsonMockResponse(['choices' => []]); - }); - - $client = new GptModelClient( - $httpClient, - 'test-api-key', - 'https://albert.example.com/v1' - ); - - $model = new Gpt('gpt-3.5-turbo'); - $client->request($model, ['messages' => []]); - - $this->assertSame('https://albert.example.com/v1/chat/completions', $capturedUrl); - } -} diff --git a/src/platform/tests/Bridge/Albert/ModelCatalogTest.php b/src/platform/tests/Bridge/Albert/ModelCatalogTest.php index ff8b610dd..3d7020090 100644 --- a/src/platform/tests/Bridge/Albert/ModelCatalogTest.php +++ b/src/platform/tests/Bridge/Albert/ModelCatalogTest.php @@ -12,9 +12,9 @@ namespace Symfony\AI\Platform\Tests\Bridge\Albert; use Symfony\AI\Platform\Bridge\Albert\ModelCatalog; -use Symfony\AI\Platform\Bridge\OpenAi\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAi\Gpt; use Symfony\AI\Platform\Capability; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; use Symfony\AI\Platform\Test\ModelCatalogTestCase; @@ -25,9 +25,9 @@ final class ModelCatalogTest extends ModelCatalogTestCase { public static function modelsProvider(): iterable { - yield 'albert-small' => ['albert-small', Gpt::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING]]; - yield 'albert-large' => ['albert-large', Gpt::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING]]; - yield 'embeddings-small' => ['embeddings-small', Embeddings::class, [Capability::INPUT_TEXT]]; + yield 'albert-small' => ['albert-small', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING]]; + yield 'albert-large' => ['albert-large', CompletionsModel::class, [Capability::INPUT_MESSAGES, Capability::OUTPUT_TEXT, Capability::OUTPUT_STREAMING]]; + yield 'embeddings-small' => ['embeddings-small', EmbeddingsModel::class, [Capability::INPUT_TEXT]]; } protected function createModelCatalog(): ModelCatalogInterface diff --git a/src/platform/tests/Bridge/LiteLlm/ModelClientTest.php b/src/platform/tests/Bridge/LiteLlm/ModelClientTest.php deleted file mode 100644 index f45430dea..000000000 --- a/src/platform/tests/Bridge/LiteLlm/ModelClientTest.php +++ /dev/null @@ -1,101 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\LiteLlm; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\LiteLlm\ModelClient; -use Symfony\AI\Platform\Model; -use Symfony\Component\HttpClient\EventSourceHttpClient; -use Symfony\Component\HttpClient\MockHttpClient; -use Symfony\Component\HttpClient\Response\MockResponse; - -class ModelClientTest extends TestCase -{ - public function testItIsSupportingTheCorrectModel() - { - $client = new ModelClient(new MockHttpClient(), 'http://localhost:4000'); - - $this->assertTrue($client->supports(new Model('test-model'))); - } - - public function testItIsExecutingTheCorrectRequest() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:4000/v1/chat/completions', $url); - self::assertSame( - '{"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}', - $options['body'] - ); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient($httpClient, 'http://localhost:4000'); - - $payload = [ - 'model' => 'test-model', - 'messages' => [ - ['role' => 'user', 'content' => 'Hello, world!'], - ], - ]; - - $client->request(new Model('test-model'), $payload); - } - - public function testItMergesOptionsWithPayload() - { - $resultCallback = static function (string $method, string $url, array $options): MockResponse { - self::assertSame('POST', $method); - self::assertSame('http://localhost:4000/v1/chat/completions', $url); - self::assertSame( - '{"temperature":0.7,"model":"test-model","messages":[{"role":"user","content":"Hello, world!"}]}', - $options['body'] - ); - - return new MockResponse(); - }; - - $httpClient = new MockHttpClient([$resultCallback]); - $client = new ModelClient($httpClient, 'http://localhost:4000'); - - $payload = [ - 'model' => 'test-model', - 'messages' => [ - ['role' => 'user', 'content' => 'Hello, world!'], - ], - ]; - - $client->request(new Model('test-model'), $payload, ['temperature' => 0.7]); - } - - public function testItUsesEventSourceHttpClient() - { - $httpClient = new MockHttpClient(); - $client = new ModelClient($httpClient, 'http://localhost:4000'); - - $reflection = new \ReflectionProperty($client, 'httpClient'); - - $this->assertInstanceOf(EventSourceHttpClient::class, $reflection->getValue($client)); - } - - public function testItKeepsExistingEventSourceHttpClient() - { - $eventSourceHttpClient = new EventSourceHttpClient(new MockHttpClient()); - $client = new ModelClient($eventSourceHttpClient, 'http://localhost:4000'); - - $reflection = new \ReflectionProperty($client, 'httpClient'); - - $this->assertSame($eventSourceHttpClient, $reflection->getValue($client)); - } -} diff --git a/src/platform/tests/Bridge/LmStudio/Completions/ModelClientTest.php b/src/platform/tests/Bridge/LmStudio/Completions/ModelClientTest.php index e62650a20..40f2de5e3 100644 --- a/src/platform/tests/Bridge/LmStudio/Completions/ModelClientTest.php +++ b/src/platform/tests/Bridge/LmStudio/Completions/ModelClientTest.php @@ -12,8 +12,8 @@ namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Completions; use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\LmStudio\Completions; use Symfony\AI\Platform\Bridge\LmStudio\Completions\ModelClient; +use Symfony\AI\Platform\Model\CompletionsModel; use Symfony\Component\HttpClient\EventSourceHttpClient; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; @@ -24,7 +24,7 @@ public function testItIsSupportingTheCorrectModel() { $client = new ModelClient(new MockHttpClient(), 'http://localhost:1234'); - $this->assertTrue($client->supports(new Completions('test-model'))); + $this->assertTrue($client->supports(new CompletionsModel('test-model'))); } public function testItIsExecutingTheCorrectRequest() @@ -50,7 +50,7 @@ public function testItIsExecutingTheCorrectRequest() ], ]; - $client->request(new Completions('test-model'), $payload); + $client->request(new CompletionsModel('test-model'), $payload); } public function testItMergesOptionsWithPayload() @@ -76,7 +76,7 @@ public function testItMergesOptionsWithPayload() ], ]; - $client->request(new Completions('test-model'), $payload, ['temperature' => 0.7]); + $client->request(new CompletionsModel('test-model'), $payload, ['temperature' => 0.7]); } public function testItUsesEventSourceHttpClient() diff --git a/src/platform/tests/Bridge/LmStudio/Completions/ResultConverterTest.php b/src/platform/tests/Bridge/LmStudio/Completions/ResultConverterTest.php deleted file mode 100644 index 1e0c144d6..000000000 --- a/src/platform/tests/Bridge/LmStudio/Completions/ResultConverterTest.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Completions; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\LmStudio\Completions; -use Symfony\AI\Platform\Bridge\LmStudio\Completions\ResultConverter; - -class ResultConverterTest extends TestCase -{ - public function testItSupportsCompletionsModel() - { - $converter = new ResultConverter(); - - $this->assertTrue($converter->supports(new Completions('test-model'))); - } -} diff --git a/src/platform/tests/Bridge/LmStudio/Embeddings/ModelClientTest.php b/src/platform/tests/Bridge/LmStudio/Embeddings/ModelClientTest.php index c7eab16e9..36e40e4f2 100644 --- a/src/platform/tests/Bridge/LmStudio/Embeddings/ModelClientTest.php +++ b/src/platform/tests/Bridge/LmStudio/Embeddings/ModelClientTest.php @@ -12,8 +12,8 @@ namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Embeddings; use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings; use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ModelClient; +use Symfony\AI\Platform\Model\EmbeddingsModel; use Symfony\Component\HttpClient\MockHttpClient; use Symfony\Component\HttpClient\Response\MockResponse; @@ -23,7 +23,7 @@ public function testItIsSupportingTheCorrectModel() { $client = new ModelClient(new MockHttpClient(), 'http://localhost:1234'); - $this->assertTrue($client->supports(new Embeddings('test-model'))); + $this->assertTrue($client->supports(new EmbeddingsModel('test-model'))); } public function testItIsExecutingTheCorrectRequest() @@ -39,7 +39,7 @@ public function testItIsExecutingTheCorrectRequest() $httpClient = new MockHttpClient([$resultCallback]); $client = new ModelClient($httpClient, 'http://localhost:1234'); - $model = new Embeddings('test-model'); + $model = new EmbeddingsModel('test-model'); $client->request($model, 'Hello, world!'); } @@ -60,7 +60,7 @@ public function testItMergesOptionsWithPayload() $httpClient = new MockHttpClient([$resultCallback]); $client = new ModelClient($httpClient, 'http://localhost:1234'); - $model = new Embeddings('test-model'); + $model = new EmbeddingsModel('test-model'); $client->request($model, 'Hello, world!', ['custom_option' => 'value']); } @@ -78,7 +78,7 @@ public function testItHandlesArrayInput() $httpClient = new MockHttpClient([$resultCallback]); $client = new ModelClient($httpClient, 'http://localhost:1234'); - $model = new Embeddings('test-model'); + $model = new EmbeddingsModel('test-model'); $client->request($model, ['Hello', 'world']); } diff --git a/src/platform/tests/Bridge/LmStudio/Embeddings/ResultConverterTest.php b/src/platform/tests/Bridge/LmStudio/Embeddings/ResultConverterTest.php deleted file mode 100644 index 8c29fc61f..000000000 --- a/src/platform/tests/Bridge/LmStudio/Embeddings/ResultConverterTest.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\AI\Platform\Tests\Bridge\LmStudio\Embeddings; - -use PHPUnit\Framework\TestCase; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings; -use Symfony\AI\Platform\Bridge\LmStudio\Embeddings\ResultConverter; -use Symfony\AI\Platform\Exception\RuntimeException; -use Symfony\AI\Platform\Result\RawHttpResult; -use Symfony\Contracts\HttpClient\ResponseInterface; - -class ResultConverterTest extends TestCase -{ - public function testItConvertsAResponseToAVectorResult() - { - $result = $this->createStub(ResponseInterface::class); - $result - ->method('toArray') - ->willReturn( - json_decode( - <<<'JSON' - { - "object": "list", - "data": [ - { - "object": "embedding", - "index": 0, - "embedding": [0.3, 0.4, 0.4] - }, - { - "object": "embedding", - "index": 1, - "embedding": [0.0, 0.0, 0.2] - } - ] - } - JSON, - true - ) - ); - - $vectorResult = (new ResultConverter())->convert(new RawHttpResult($result)); - $convertedContent = $vectorResult->getContent(); - - $this->assertCount(2, $convertedContent); - - $this->assertSame([0.3, 0.4, 0.4], $convertedContent[0]->getData()); - $this->assertSame([0.0, 0.0, 0.2], $convertedContent[1]->getData()); - } - - public function testItThrowsExceptionWhenResponseDoesNotContainData() - { - $result = $this->createStub(ResponseInterface::class); - $result - ->method('toArray') - ->willReturn(['invalid' => 'response']); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Response does not contain data'); - - (new ResultConverter())->convert(new RawHttpResult($result)); - } - - public function testItSupportsEmbeddingsModel() - { - $converter = new ResultConverter(); - - $this->assertTrue($converter->supports(new Embeddings('test-model'))); - } -} diff --git a/src/platform/tests/CachedPlatformTest.php b/src/platform/tests/CachedPlatformTest.php index 934334404..cbedfde22 100644 --- a/src/platform/tests/CachedPlatformTest.php +++ b/src/platform/tests/CachedPlatformTest.php @@ -17,7 +17,7 @@ use Symfony\AI\Platform\Result\DeferredResult; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; use Symfony\Contracts\HttpClient\ResponseInterface as SymfonyHttpResponse; diff --git a/src/platform/tests/ModelClient/CompletionsModelClientTest.php b/src/platform/tests/ModelClient/CompletionsModelClientTest.php new file mode 100644 index 000000000..ed8c22f80 --- /dev/null +++ b/src/platform/tests/ModelClient/CompletionsModelClientTest.php @@ -0,0 +1,119 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\Tests\ModelClient; + +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\ModelClient\CompletionsModelClient; +use Symfony\Component\HttpClient\EventSourceHttpClient; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; +use Symfony\Contracts\HttpClient\ResponseInterface as HttpResponse; + +/** + * @author Oskar Stark + */ +final class CompletionsModelClientTest extends TestCase +{ + public function testItAcceptsValidApiKey() + { + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org'); + + $this->assertInstanceOf(CompletionsModelClient::class, $modelClient); + } + + public function testItWrapsHttpClientInEventSourceHttpClient() + { + $httpClient = new MockHttpClient(); + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org', httpClient: $httpClient); + + $this->assertInstanceOf(CompletionsModelClient::class, $modelClient); + } + + public function testItAcceptsEventSourceHttpClientDirectly() + { + $httpClient = new EventSourceHttpClient(new MockHttpClient()); + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org', httpClient: $httpClient); + + $this->assertInstanceOf(CompletionsModelClient::class, $modelClient); + } + + public function testItIsSupportingTheCorrectModel() + { + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org'); + + $this->assertTrue($modelClient->supports(new CompletionsModel('gpt-4o'))); + } + + public function testItIsExecutingTheCorrectRequest() + { + $resultCallback = static function (string $method, string $url, array $options): HttpResponse { + self::assertSame('POST', $method); + self::assertSame('https://example.org/v1/chat/completions', $url); + self::assertSame('Authorization: Bearer sk-valid-api-key', $options['normalized_headers']['authorization'][0]); + self::assertSame('{"temperature":1,"model":"gpt-4o","messages":[{"role":"user","content":"test message"}]}', $options['body']); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org', httpClient: $httpClient); + $modelClient->request(new CompletionsModel('gpt-4o'), ['model' => 'gpt-4o', 'messages' => [['role' => 'user', 'content' => 'test message']]], ['temperature' => 1]); + } + + public function testItIsExecutingTheCorrectRequestWithArrayPayload() + { + $resultCallback = static function (string $method, string $url, array $options): HttpResponse { + self::assertSame('POST', $method); + self::assertSame('https://example.org/v1/chat/completions', $url); + self::assertSame('Authorization: Bearer sk-valid-api-key', $options['normalized_headers']['authorization'][0]); + self::assertSame('{"temperature":0.7,"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}', $options['body']); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://example.org', httpClient: $httpClient); + $modelClient->request(new CompletionsModel('gpt-4o'), ['model' => 'gpt-4o', 'messages' => [['role' => 'user', 'content' => 'Hello']]], ['temperature' => 0.7]); + } + + #[TestWith(['https://api.inference.eu', 'https://api.inference.eu/v1/chat/completions'])] + #[TestWith(['https://api.inference.com', 'https://api.inference.com/v1/chat/completions'])] + public function testItUsesCorrectBaseUrl(string $baseUrl, string $expectedUrl) + { + $resultCallback = static function (string $method, string $url, array $options) use ($expectedUrl): HttpResponse { + self::assertSame('POST', $method); + self::assertSame($expectedUrl, $url); + self::assertSame('Authorization: Bearer sk-valid-api-key', $options['normalized_headers']['authorization'][0]); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new CompletionsModelClient('sk-valid-api-key', $baseUrl, httpClient: $httpClient); + $modelClient->request(new CompletionsModel('gpt-4o'), ['messages' => []]); + } + + #[TestWith(['/custom/path', 'https://api.inference.com/custom/path'])] + #[TestWith(['/v1/alternative/endpoint', 'https://api.inference.com/v1/alternative/endpoint'])] + public function testsItUsesCorrectPathIfProvided(string $path, string $expectedUrl) + { + $resultCallback = static function (string $method, string $url, array $options) use ($expectedUrl): HttpResponse { + self::assertSame('POST', $method); + self::assertSame($expectedUrl, $url); + self::assertSame('Authorization: Bearer sk-valid-api-key', $options['normalized_headers']['authorization'][0]); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new CompletionsModelClient('sk-valid-api-key', 'https://api.inference.com', $path, $httpClient); + $modelClient->request(new CompletionsModel('gpt-4o'), ['messages' => []]); + } +} diff --git a/src/platform/tests/ModelClient/EmbeddingsModelClientTest.php b/src/platform/tests/ModelClient/EmbeddingsModelClientTest.php new file mode 100644 index 000000000..099ad5bab --- /dev/null +++ b/src/platform/tests/ModelClient/EmbeddingsModelClientTest.php @@ -0,0 +1,102 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\AI\Platform\Tests\ModelClient; + +use PHPUnit\Framework\Attributes\TestWith; +use PHPUnit\Framework\TestCase; +use Symfony\AI\Platform\Model\CompletionsModel; +use Symfony\AI\Platform\Model\EmbeddingsModel; +use Symfony\AI\Platform\ModelClient\EmbeddingsModelClient; +use Symfony\Component\HttpClient\MockHttpClient; +use Symfony\Component\HttpClient\Response\MockResponse; +use Symfony\Contracts\HttpClient\ResponseInterface as HttpResponse; + +/** + * @author Oskar Stark + */ +final class EmbeddingsModelClientTest extends TestCase +{ + public function testItIsSupportingTheCorrectModel() + { + $modelClient = new EmbeddingsModelClient('sk-api-key', 'https://example.org/'); + + $this->assertTrue($modelClient->supports(new EmbeddingsModel('text-embedding-3-small'))); + } + + public function testItIsNotSupportingTheIncorrectModel() + { + $modelClient = new EmbeddingsModelClient('sk-api-key', 'https://example.org/'); + + $this->assertFalse($modelClient->supports(new CompletionsModel('gpt-4o-mini'))); + } + + public function testItIsExecutingTheCorrectRequest() + { + $resultCallback = static function (string $method, string $url, array $options): HttpResponse { + self::assertSame('POST', $method); + self::assertSame('https://example.org/v1/embeddings', $url); + self::assertSame('Authorization: Bearer sk-api-key', $options['normalized_headers']['authorization'][0]); + self::assertSame('{"model":"text-embedding-3-small","input":"test text"}', $options['body']); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new EmbeddingsModelClient('sk-api-key', 'https://example.org', httpClient: $httpClient); + $modelClient->request(new EmbeddingsModel('text-embedding-3-small'), 'test text', []); + } + + public function testItIsExecutingTheCorrectRequestWithCustomOptions() + { + $resultCallback = static function (string $method, string $url, array $options): HttpResponse { + self::assertSame('POST', $method); + self::assertSame('https://example.org/v1/embeddings', $url); + self::assertSame('Authorization: Bearer sk-api-key', $options['normalized_headers']['authorization'][0]); + self::assertSame('{"dimensions":256,"model":"text-embedding-3-large","input":"test text"}', $options['body']); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new EmbeddingsModelClient('sk-api-key', 'https://example.org', httpClient: $httpClient); + $modelClient->request(new EmbeddingsModel('text-embedding-3-large'), 'test text', ['dimensions' => 256]); + } + + public function testItIsExecutingTheCorrectRequestWithArrayInput() + { + $resultCallback = static function (string $method, string $url, array $options): HttpResponse { + self::assertSame('POST', $method); + self::assertSame('https://example.org/v1/embeddings', $url); + self::assertSame('Authorization: Bearer sk-api-key', $options['normalized_headers']['authorization'][0]); + self::assertSame('{"model":"text-embedding-3-small","input":["text1","text2","text3"]}', $options['body']); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new EmbeddingsModelClient('sk-api-key', 'https://example.org', httpClient: $httpClient); + $modelClient->request(new EmbeddingsModel('text-embedding-3-small'), ['text1', 'text2', 'text3'], []); + } + + #[TestWith(['https://api.inference.eu', 'https://api.inference.eu/v1/embeddings'])] + #[TestWith(['https://api.inference.com', 'https://api.inference.com/v1/embeddings'])] + public function testItUsesCorrectBaseUrl(string $baseUrl, string $expectedUrl) + { + $resultCallback = static function (string $method, string $url, array $options) use ($expectedUrl): HttpResponse { + self::assertSame('POST', $method); + self::assertSame($expectedUrl, $url); + self::assertSame('Authorization: Bearer sk-api-key', $options['normalized_headers']['authorization'][0]); + + return new MockResponse(); + }; + $httpClient = new MockHttpClient([$resultCallback]); + $modelClient = new EmbeddingsModelClient('sk-api-key', $baseUrl, httpClient: $httpClient); + $modelClient->request(new EmbeddingsModel('text-embedding-3-small'), 'test input', []); + } +} diff --git a/src/platform/tests/Result/DeferredResultTest.php b/src/platform/tests/Result/DeferredResultTest.php index 31c38a2ea..a09982af9 100644 --- a/src/platform/tests/Result/DeferredResultTest.php +++ b/src/platform/tests/Result/DeferredResultTest.php @@ -18,7 +18,7 @@ use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\TextResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\Contracts\HttpClient\ResponseInterface as SymfonyHttpResponse; final class DeferredResultTest extends TestCase diff --git a/src/store/tests/Double/PlatformTestHandler.php b/src/store/tests/Double/PlatformTestHandler.php index bc0e923e0..11d8f3b95 100644 --- a/src/store/tests/Double/PlatformTestHandler.php +++ b/src/store/tests/Double/PlatformTestHandler.php @@ -14,13 +14,13 @@ use Symfony\AI\Platform\Model; use Symfony\AI\Platform\ModelCatalog\FallbackModelCatalog; use Symfony\AI\Platform\ModelCatalog\ModelCatalogInterface; -use Symfony\AI\Platform\ModelClientInterface; +use Symfony\AI\Platform\ModelClient\ModelClientInterface; use Symfony\AI\Platform\Platform; use Symfony\AI\Platform\Result\RawHttpResult; use Symfony\AI\Platform\Result\RawResultInterface; use Symfony\AI\Platform\Result\ResultInterface; use Symfony\AI\Platform\Result\VectorResult; -use Symfony\AI\Platform\ResultConverterInterface; +use Symfony\AI\Platform\ResultConverter\ResultConverterInterface; use Symfony\AI\Platform\Vector\Vector; use Symfony\Component\HttpClient\Response\MockResponse;