Models.dev Platform
The models.dev bridge brings model catalogs for many AI providers to Symfony AI,
sourced from the models.dev community registry and shipped as the standalone
symfony/models-dev package.
Its main benefit is decoupling the model catalog lifecycle from the Symfony AI
release cycle. Every bridge ships a hand-curated catalog of known models that only
changes when you upgrade the bridge itself. The models.dev bridge replaces that with a
dynamic catalog you refresh independently: composer update symfony/models-dev picks
up newly released models and their capabilities without bumping symfony/ai-platform
or editing any catalog by hand.
Installation
1
$ composer require symfony/ai-models-dev-platform
Authentication
Each provider requires its own API key. Set it as an environment variable:
1 2 3 4 5
# Example for DeepSeek
DEEPSEEK_API_KEY=your-api-key
# Example for Groq
GROQ_API_KEY=your-api-key
Refer to each provider's documentation for how to obtain an API key.
Usage
Using the Model Catalog
At its core the bridge provides a ModelCatalog that reads the models.dev data for a
given provider. It is wired with the model classes the matching bridge expects, so it drops
straight into that bridge in place of its bundled catalog.
For any OpenAI-compatible provider, pair it with the Generic bridge:
1 2 3 4 5 6 7 8
use Symfony\AI\Platform\Bridge\Generic\Factory as GenericFactory;
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
$platform = GenericFactory::createPlatform(
baseUrl: 'https://api.deepseek.com',
apiKey: $_ENV['DEEPSEEK_API_KEY'],
modelCatalog: new ModelCatalog('deepseek'),
);
For a provider that needs a specialized bridge, pair it with that bridge. The catalog already
carries the model class that bridge requires (e.g. Claude for Anthropic), based on the
provider's models.dev entry:
1 2 3 4 5 6 7
use Symfony\AI\Platform\Bridge\Anthropic\Factory as AnthropicFactory;
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
$platform = AnthropicFactory::createPlatform(
apiKey: $_ENV['ANTHROPIC_API_KEY'],
modelCatalog: new ModelCatalog('anthropic'),
);
This is the key feature of the bridge: your model definitions stay current independently
of the Symfony AI release cycle, refreshed with composer update symfony/models-dev.
Multiple Providers in one Platform
Compose providers from the regular bridges — each backed by a models.dev ModelCatalog —
into a single Platform. ProviderRegistry resolves the base URL for OpenAI-compatible
providers (including the ones models.dev does not publish directly):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
use Symfony\AI\Platform\Bridge\Anthropic\Factory as AnthropicFactory;
use Symfony\AI\Platform\Bridge\Generic\Factory as GenericFactory;
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
use Symfony\AI\Platform\Bridge\ModelsDev\ProviderRegistry;
use Symfony\AI\Platform\Platform;
$registry = new ProviderRegistry();
$platform = new Platform([
GenericFactory::createProvider(
baseUrl: $registry->getApiBaseUrl('deepseek'),
apiKey: $_ENV['DEEPSEEK_API_KEY'],
modelCatalog: new ModelCatalog('deepseek'),
name: 'deepseek',
),
AnthropicFactory::createProvider(
apiKey: $_ENV['ANTHROPIC_API_KEY'],
modelCatalog: new ModelCatalog('anthropic'),
),
]);
$platform->invoke('deepseek-chat', $messages); // → deepseek
$platform->invoke('claude-haiku-4-5', $messages); // → anthropic
Routing is a core Platform feature, the bridge only supplies the providers and catalogs. See the Platform component documentation (Providers and Multi-Provider Platforms) for the routing mechanics and custom strategies.
By default a model id resolves to the first provider (in array order) whose catalog knows it. When several providers expose the same model id (e.g. a first-party provider and an aggregator that re-lists it), order the array so the preferred provider comes first.
Embeddings
Embedding models are detected automatically and wired to the matching embeddings model class. Use them like any other embedding model:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Symfony\AI\Platform\Bridge\Generic\Factory as GenericFactory;
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
use Symfony\AI\Platform\Bridge\ModelsDev\ProviderRegistry;
$registry = new ProviderRegistry();
$platform = GenericFactory::createPlatform(
baseUrl: $registry->getApiBaseUrl('openai'),
apiKey: $_ENV['OPENAI_API_KEY'],
modelCatalog: new ModelCatalog('openai'),
);
$result = $platform->invoke('text-embedding-3-small', 'What is Symfony?');
$vectors = $result->asVectors();
Streaming
All completions models include the OUTPUT_STREAMING capability. Enable
streaming as you would with any other platform:
1 2 3 4 5 6 7
$result = $platform->invoke('deepseek-chat', $messages, [
'stream' => true,
]);
foreach ($result->asTextStream() as $delta) {
echo $delta;
}
Tool Calling
Models that support tool calling are automatically flagged with the
TOOL_CALLING capability:
1 2 3 4 5 6 7 8 9
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
$catalog = new ModelCatalog('deepseek');
$model = $catalog->getModel('deepseek-chat');
// Check if the model supports tool calling
if ($model->supports(\Symfony\AI\Platform\Capability::TOOL_CALLING)) {
// Use with an Agent that has tools configured
}
Adding Custom Models
If a model is missing from the data or you need to override its capabilities,
pass additional models when creating the ModelCatalog:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\AI\Platform\Bridge\Generic\CompletionsModel;
use Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog;
use Symfony\AI\Platform\Capability;
$catalog = new ModelCatalog('deepseek', additionalModels: [
'deepseek-custom-finetune' => [
'class' => CompletionsModel::class,
'capabilities' => [
Capability::INPUT_MESSAGES,
Capability::OUTPUT_TEXT,
Capability::OUTPUT_STREAMING,
Capability::TOOL_CALLING,
],
],
]);
Additional models are merged with and take precedence over the bundled data.
Discovering Providers
The models.dev registry covers many providers. ProviderRegistry gives you access to
their metadata, e.g. to list everything available and resolve their API base URL:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\AI\Platform\Bridge\ModelsDev\ProviderRegistry;
$registry = new ProviderRegistry();
$registry->has('deepseek'); // true
$registry->getProviderName('deepseek'); // "DeepSeek"
foreach ($registry->getProviderIds() as $id) {
// null when no base URL is known (must then be passed explicitly)
$url = $registry->getApiBaseUrl($id) ?? '(manual)';
echo sprintf("%s: %s\n", $id, $url);
}
Symfony Bundle Configuration
When using the AI Bundle, configure the models.dev bridge under the generic
platform section. The ModelCatalog replaces the manually curated model
list:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
# config/packages/ai.yaml
ai:
platform:
generic:
deepseek:
base_url: 'https://api.deepseek.com'
api_key: '%env(DEEPSEEK_API_KEY)%'
model_catalog: 'Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog'
agent:
deepseek:
platform: 'ai.platform.generic.deepseek'
model: 'deepseek-chat'
tools: false
services:
Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog:
arguments:
$providerId: 'deepseek'
Multiple Providers
Configure multiple providers in the same application:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# config/packages/ai.yaml
ai:
platform:
generic:
deepseek:
base_url: 'https://api.deepseek.com'
api_key: '%env(DEEPSEEK_API_KEY)%'
model_catalog: 'app.model_catalog.deepseek'
groq:
base_url: 'https://api.groq.com/openai/v1'
api_key: '%env(GROQ_API_KEY)%'
model_catalog: 'app.model_catalog.groq'
services:
app.model_catalog.deepseek:
class: 'Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog'
arguments:
$providerId: 'deepseek'
app.model_catalog.groq:
class: 'Symfony\AI\Platform\Bridge\ModelsDev\ModelCatalog'
arguments:
$providerId: 'groq'