Skip to content

Commit a42f298

Browse files
committed
Introduce generic platform for openai embeddings- and completions-based platforms
1 parent 917cab3 commit a42f298

37 files changed

+1245
-1303
lines changed

docs/bundles/ai-bundle.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,40 @@ Advanced Example with Multiple Agents
136136
vectorizer: 'ai.vectorizer.mistral_embeddings'
137137
store: 'ai.store.memory.research'
138138
139+
Generic Platform
140+
----------------
141+
142+
Based on the generic bridge, you can configure any service, that complies with the original OpenAI API, like LiteLLM:
143+
144+
.. code-block:: yaml
145+
146+
# config/packages/ai.yaml
147+
ai:
148+
platform:
149+
generic:
150+
litellm:
151+
base_url: '%env(LITELLM_HOST_URL)%' # e.g. http://localhost:4000
152+
api_key: '%env(LITELLM_API_KEY)%' # e.g. sk-1234
153+
model_catalog: 'Symfony\AI\Platform\Bridge\Generic\ModelCatalog' # see below
154+
agent:
155+
test:
156+
platform: 'ai.platform.generic.litellm'
157+
model: 'mistral-small-latest'
158+
tools: false
159+
160+
services:
161+
Symfony\AI\Platform\Bridge\Generic\ModelCatalog:
162+
$models:
163+
mistral-small-latest:
164+
class: 'Symfony\AI\Platform\Bridge\Generic\CompletionsModel'
165+
capabilities:
166+
- !php/const 'Symfony\AI\Platform\Capability::INPUT_MESSAGES'
167+
- !php/const 'Symfony\AI\Platform\Capability::OUTPUT_TEXT'
168+
- !php/const 'Symfony\AI\Platform\Capability::OUTPUT_STREAMING'
169+
- !php/const 'Symfony\AI\Platform\Capability::OUTPUT_STRUCTURED'
170+
- !php/const 'Symfony\AI\Platform\Capability::INPUT_IMAGE'
171+
- !php/const 'Symfony\AI\Platform\Capability::TOOL_CALLING'
172+
139173
Cached Platform
140174
---------------
141175

docs/components/platform.rst

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ When using the bundle, the usage of ``OllamaApiCatalog`` is available via the ``
106106
api_catalog: true
107107

108108
Supported Models & Platforms
109-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
109+
----------------------------
110110

111111
* **Language Models**
112112
* `OpenAI's GPT`_ with `OpenAI`_, `Azure`_ and `OpenRouter`_ as Platform
@@ -136,6 +136,31 @@ Supported Models & Platforms
136136
* `Cartesia TTS`_ with `Cartesia`_ as Platform
137137
* `Cartesia STT`_ with `Cartesia`_ as Platform
138138

139+
Generic Platforms
140+
~~~~~~~~~~~~~~~~~
141+
142+
Platforms like `LiteLLM`_ or `OpenRouter`_ provide a unified API to access multiple models from different providers.
143+
Therefore, they rely on endpoint and contract design, that is inspired by OpenAI's original GPT API - an implicit
144+
standard in the industry. Platforms using this de facto standard can be used with the generic bridge::
145+
146+
use Symfony\AI\Platform\Bridge\Generic\PlatformFactory;
147+
use Symfony\AI\Platform\Message\Message;
148+
use Symfony\AI\Platform\Message\MessageBag;
149+
150+
$platform = PlatformFactory::create('https://api.example.com', 'sk-xxxxxx', $httpClient, $modelCatalog);
151+
152+
$messages = new MessageBag(
153+
Message::forSystem('You are a pirate and you write funny.'),
154+
Message::ofUser('What is the Symfony framework?'),
155+
);
156+
$result = $platform->invoke('model-name', $messages);
157+
158+
echo $result->asText();
159+
160+
This requires to configure a :class:`Symfony\\AI\\Platform\\Bridge\\Generic\\ModelCatalog` explicitly, using
161+
:class:`Symfony\\AI\\Platform\\Bridge\\Generic\\CompletionsModel` or :class:`Symfony\\AI\\Platform\\Bridge\\Generic\\EmbeddingsModel`,
162+
see `LiteLLM example`_ for more details.
163+
139164
Options
140165
-------
141166

@@ -524,6 +549,7 @@ Code Examples
524549
.. _`Cartesia`: https://cartesia.ai/
525550
.. _`Cartesia STT`: https://cartesia.ai/ink
526551
.. _`Cartesia TTS`: https://cartesia.ai/sonic
552+
.. _`LiteLLM example`: https://github.com/symfony/ai/blob/main/examples/litellm/chat.php
527553
.. _`Meta's Llama`: https://www.llama.com/
528554
.. _`Ollama`: https://ollama.com/
529555
.. _`Replicate`: https://replicate.com/

examples/litellm/chat.php

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,35 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
use Symfony\AI\Platform\Bridge\LiteLlm\PlatformFactory;
12+
use Symfony\AI\Platform\Bridge\Generic\CompletionsModel;
13+
use Symfony\AI\Platform\Bridge\Generic\ModelCatalog;
14+
use Symfony\AI\Platform\Bridge\Generic\PlatformFactory;
15+
use Symfony\AI\Platform\Capability;
1316
use Symfony\AI\Platform\Message\Message;
1417
use Symfony\AI\Platform\Message\MessageBag;
1518

1619
require_once dirname(__DIR__).'/bootstrap.php';
1720

18-
$platform = PlatformFactory::create(env('LITELLM_HOST_URL'), env('LITELLM_API_KEY'), http_client());
21+
$modelCatalog = new ModelCatalog([
22+
'mistral-small-latest' => [
23+
'class' => CompletionsModel::class,
24+
'capabilities' => [
25+
Capability::INPUT_MESSAGES,
26+
Capability::OUTPUT_TEXT,
27+
Capability::OUTPUT_STREAMING,
28+
Capability::OUTPUT_STRUCTURED,
29+
Capability::INPUT_IMAGE,
30+
Capability::TOOL_CALLING,
31+
],
32+
],
33+
]);
34+
35+
$platform = PlatformFactory::create(
36+
env('LITELLM_HOST_URL'),
37+
env('LITELLM_API_KEY'),
38+
http_client(),
39+
$modelCatalog,
40+
);
1941

2042
$messages = new MessageBag(
2143
Message::forSystem('You are a pirate and you write funny.'),

src/ai-bundle/config/options.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@
111111
->end()
112112
->end()
113113
->end()
114+
->arrayNode('generic')
115+
->useAttributeAsKey('name')
116+
->arrayPrototype()
117+
->children()
118+
->stringNode('base_url')->isRequired()->end()
119+
->stringNode('api_key')->end()
120+
->stringNode('http_client')
121+
->defaultValue('http_client')
122+
->info('Service ID of the HTTP client to use')
123+
->end()
124+
->stringNode('model_catalog')
125+
->info('Service ID of the model catalog to use')
126+
->end()
127+
->booleanNode('supports_completions')->defaultTrue()->end()
128+
->booleanNode('supports_embeddings')->defaultTrue()->end()
129+
->stringNode('completions_path')->defaultValue('/v1/chat/completions')->end()
130+
->stringNode('embeddings_path')->defaultValue('/v1/embeddings')->end()
131+
->end()
132+
->end()
133+
->end()
114134
->arrayNode('huggingface')
115135
->children()
116136
->stringNode('api_key')->isRequired()->end()

src/ai-bundle/src/AiBundle.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
use Symfony\AI\Platform\Bridge\DockerModelRunner\PlatformFactory as DockerModelRunnerPlatformFactory;
5959
use Symfony\AI\Platform\Bridge\ElevenLabs\PlatformFactory as ElevenLabsPlatformFactory;
6060
use Symfony\AI\Platform\Bridge\Gemini\PlatformFactory as GeminiPlatformFactory;
61+
use Symfony\AI\Platform\Bridge\Generic\PlatformFactory as GenericPlatformFactory;
6162
use Symfony\AI\Platform\Bridge\HuggingFace\PlatformFactory as HuggingFacePlatformFactory;
6263
use Symfony\AI\Platform\Bridge\LmStudio\PlatformFactory as LmStudioPlatformFactory;
6364
use Symfony\AI\Platform\Bridge\Mistral\PlatformFactory as MistralPlatformFactory;
@@ -466,6 +467,33 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
466467
return;
467468
}
468469

470+
if ('generic' === $type) {
471+
foreach ($platform as $name => $config) {
472+
$platformId = 'ai.platform.generic.'.$name;
473+
$definition = (new Definition(Platform::class))
474+
->setFactory(GenericPlatformFactory::class.'::create')
475+
->setLazy(true)
476+
->addTag('proxy', ['interface' => PlatformInterface::class])
477+
->setArguments([
478+
$config['base_url'],
479+
$config['api_key'],
480+
new Reference($config['http_client'], ContainerInterface::NULL_ON_INVALID_REFERENCE),
481+
new Reference($config['model_catalog'], ContainerInterface::NULL_ON_INVALID_REFERENCE),
482+
null,
483+
new Reference('event_dispatcher'),
484+
$config['supports_completions'],
485+
$config['supports_embeddings'],
486+
$config['completions_path'],
487+
$config['embeddings_path'],
488+
])
489+
->addTag('ai.platform', ['name' => 'generic.'.$name]);
490+
491+
$container->setDefinition($platformId, $definition);
492+
}
493+
494+
return;
495+
}
496+
469497
if ('huggingface' === $type) {
470498
$platformId = 'ai.platform.huggingface';
471499
$definition = (new Definition(Platform::class))

src/platform/src/Bridge/AiMlApi/Completions/ModelClient.php

Lines changed: 0 additions & 47 deletions
This file was deleted.

src/platform/src/Bridge/AiMlApi/Completions/ResultConverter.php

Lines changed: 0 additions & 40 deletions
This file was deleted.

src/platform/src/Bridge/AiMlApi/Embeddings/ModelClient.php

Lines changed: 0 additions & 50 deletions
This file was deleted.

0 commit comments

Comments
 (0)