diff --git a/demo/config/packages/ai.yaml b/demo/config/packages/ai.yaml index 845f8840e0..209203a0a0 100644 --- a/demo/config/packages/ai.yaml +++ b/demo/config/packages/ai.yaml @@ -6,8 +6,8 @@ ai: blog: # platform: 'symfony_ai.platform.anthropic' model: - name: 'GPT' - version: 'gpt-4o-mini' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI tools: - 'Symfony\AI\Agent\Toolbox\Tool\SimilaritySearch' - service: 'clock' @@ -16,13 +16,13 @@ ai: method: 'now' youtube: model: - name: 'GPT' - version: 'gpt-4o-mini' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI tools: false wikipedia: model: - name: 'GPT' - version: 'gpt-4o-mini' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI options: temperature: 0.5 system_prompt: 'Please answer the users question based on Wikipedia and provide a link to the article.' @@ -31,8 +31,8 @@ ai: - 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia' audio: model: - name: 'GPT' - version: 'gpt-4o-mini' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI system_prompt: 'You are a friendly chatbot that likes to have a conversation with users and asks them some questions.' tools: # Agent in agent 🤯 @@ -47,8 +47,8 @@ ai: indexer: default: model: - name: 'Embeddings' - version: 'text-embedding-ada-002' + class: 'Symfony\AI\Platform\Bridge\OpenAI\Embeddings' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\Embeddings::TEXT_ADA_002 services: _defaults: diff --git a/src/ai-bundle/config/options.php b/src/ai-bundle/config/options.php index d94953c630..f899143af1 100644 --- a/src/ai-bundle/config/options.php +++ b/src/ai-bundle/config/options.php @@ -70,8 +70,8 @@ ->end() ->arrayNode('model') ->children() - ->scalarNode('name')->isRequired()->end() - ->scalarNode('version')->defaultNull()->end() + ->scalarNode('class')->isRequired()->end() + ->scalarNode('name')->defaultNull()->end() ->arrayNode('options') ->variablePrototype()->end() ->end() @@ -196,8 +196,8 @@ ->end() ->arrayNode('model') ->children() - ->scalarNode('name')->isRequired()->end() - ->scalarNode('version')->defaultNull()->end() + ->scalarNode('class')->isRequired()->end() + ->scalarNode('name')->defaultNull()->end() ->arrayNode('options') ->variablePrototype()->end() ->end() diff --git a/src/ai-bundle/doc/index.rst b/src/ai-bundle/doc/index.rst index 0a5a2f0f47..c358f40fcd 100644 --- a/src/ai-bundle/doc/index.rst +++ b/src/ai-bundle/doc/index.rst @@ -31,7 +31,8 @@ Configuration agent: default: model: - name: 'GPT' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI **Advanced Example with Anthropic, Azure, Google and multiple agents** @@ -56,8 +57,8 @@ Configuration platform: 'symfony_ai.platform.azure.gpt_deployment' structured_output: false # Disables support for "output_structure" option, default is true model: - name: 'GPT' - version: 'gpt-4o-mini' + class: 'Symfony\AI\Platform\Bridge\OpenAI\GPT' + name: !php/const Symfony\AI\Platform\Bridge\OpenAI\GPT::GPT_4O_MINI system_prompt: 'You are a helpful assistant that can answer questions.' # The default system prompt of the agent include_tools: true # Include tool definitions at the end of the system prompt tools: @@ -78,7 +79,8 @@ Configuration research: platform: 'symfony_ai.platform.anthropic' model: - name: 'Claude' + class: 'Symfony\AI\Platform\Bridge\Anthropic\Claude' + name: !php/const Symfony\AI\Platform\Bridge\Anthropic\Claude::SONNET_37 tools: # If undefined, all tools are injected into the agent, use "tools: false" to disable tools. - 'Symfony\AI\Agent\Toolbox\Tool\Wikipedia' fault_tolerant_toolbox: false # Disables fault tolerant toolbox, default is true @@ -90,11 +92,11 @@ Configuration collection: 'my_collection' indexer: default: - # platform: 'symfony_ai.platform.anthropic' + # platform: 'symfony_ai.platform.mistral' # store: 'symfony_ai.store.chroma_db.default' model: - name: 'Embeddings' - version: 'text-embedding-ada-002' + class: 'Symfony\AI\Platform\Bridge\Mistral\Embeddings' + name: !php/const Symfony\AI\Platform\Bridge\Mistral\Embeddings::MISTRAL_EMBED Usage ----- diff --git a/src/ai-bundle/src/AIBundle.php b/src/ai-bundle/src/AIBundle.php index cc125e44b0..6710eb38f8 100644 --- a/src/ai-bundle/src/AIBundle.php +++ b/src/ai-bundle/src/AIBundle.php @@ -27,19 +27,12 @@ use Symfony\AI\AIBundle\Profiler\DataCollector; use Symfony\AI\AIBundle\Profiler\TraceablePlatform; use Symfony\AI\AIBundle\Profiler\TraceableToolbox; -use Symfony\AI\Platform\Bridge\Anthropic\Claude; use Symfony\AI\Platform\Bridge\Anthropic\PlatformFactory as AnthropicPlatformFactory; use Symfony\AI\Platform\Bridge\Azure\OpenAI\PlatformFactory as AzureOpenAIPlatformFactory; -use Symfony\AI\Platform\Bridge\Google\Gemini; use Symfony\AI\Platform\Bridge\Google\PlatformFactory as GooglePlatformFactory; -use Symfony\AI\Platform\Bridge\Meta\Llama; -use Symfony\AI\Platform\Bridge\Mistral\Mistral; use Symfony\AI\Platform\Bridge\Mistral\PlatformFactory as MistralPlatformFactory; -use Symfony\AI\Platform\Bridge\OpenAI\Embeddings; -use Symfony\AI\Platform\Bridge\OpenAI\GPT; use Symfony\AI\Platform\Bridge\OpenAI\PlatformFactory as OpenAIPlatformFactory; use Symfony\AI\Platform\Bridge\OpenRouter\PlatformFactory as OpenRouterPlatformFactory; -use Symfony\AI\Platform\Bridge\Voyage\Voyage; use Symfony\AI\Platform\Model; use Symfony\AI\Platform\ModelClientInterface; use Symfony\AI\Platform\Platform; @@ -261,20 +254,15 @@ private function processPlatformConfig(string $type, array $platform, ContainerB private function processAgentConfig(string $name, array $config, ContainerBuilder $container): void { // MODEL - ['name' => $modelName, 'version' => $version, 'options' => $options] = $config['model']; - - $modelClass = match (strtolower((string) $modelName)) { - 'gpt' => GPT::class, - 'claude' => Claude::class, - 'llama' => Llama::class, - 'gemini' => Gemini::class, - 'mistral' => Mistral::class, - 'openrouter' => Model::class, - default => throw new \InvalidArgumentException(\sprintf('Model "%s" is not supported.', $modelName)), - }; + ['class' => $modelClass, 'name' => $modelName, 'options' => $options] = $config['model']; + + if (!is_a($modelClass, Model::class, true)) { + throw new \InvalidArgumentException(\sprintf('"%s" class is not extending Symfony\AI\Platform\Model.', $modelClass)); + } + $modelDefinition = new Definition($modelClass); - if (null !== $version) { - $modelDefinition->setArgument('$name', $version); + if (null !== $modelName) { + $modelDefinition->setArgument('$name', $modelName); } if ([] !== $options) { $modelDefinition->setArgument('$options', $options); @@ -473,20 +461,20 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde */ private function processIndexerConfig(int|string $name, array $config, ContainerBuilder $container): void { - ['name' => $modelName, 'version' => $version, 'options' => $options] = $config['model']; - - $modelClass = match (strtolower((string) $modelName)) { - 'embeddings' => Embeddings::class, - 'voyage' => Voyage::class, - default => throw new \InvalidArgumentException(\sprintf('Model "%s" is not supported.', $modelName)), - }; - $modelDefinition = (new Definition($modelClass)); - if (null !== $version) { - $modelDefinition->setArgument('$name', $version); + ['class' => $modelClass, 'name' => $modelName, 'options' => $options] = $config['model']; + + if (!is_a($modelClass, Model::class, true)) { + throw new \InvalidArgumentException(\sprintf('"%s" class is not extending Symfony\AI\Platform\Model.', $modelClass)); + } + + $modelDefinition = (new Definition((string) $modelClass)); + if (null !== $modelName) { + $modelDefinition->setArgument('$name', $modelName); } if ([] !== $options) { $modelDefinition->setArgument('$options', $options); } + $modelDefinition->addTag('symfony_ai.model.embeddings_model'); $container->setDefinition('symfony_ai.indexer.'.$name.'.model', $modelDefinition); diff --git a/src/ai-bundle/tests/AIBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AIBundleTest.php similarity index 92% rename from src/ai-bundle/tests/AIBundleTest.php rename to src/ai-bundle/tests/DependencyInjection/AIBundleTest.php index 3375402ece..1269be0b78 100644 --- a/src/ai-bundle/tests/AIBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AIBundleTest.php @@ -77,8 +77,8 @@ private function getFullConfig(): array 'my_chat_agent' => [ 'platform' => 'openai_platform_service_id', 'model' => [ - 'name' => 'gpt', - 'version' => 'gpt-3.5-turbo', + 'class' => 'Symfony\AI\Platform\Bridge\OpenAI\GPT', + 'name' => 'gpt-3.5-turbo', 'options' => [ 'temperature' => 0.7, 'max_tokens' => 150, @@ -98,7 +98,7 @@ private function getFullConfig(): array 'fault_tolerant_toolbox' => false, ], 'another_agent' => [ - 'model' => ['name' => 'claude', 'version' => 'claude-3-opus-20240229'], + 'model' => ['class' => 'Symfony\AI\Platform\Bridge\Anthropic\Claude', 'name' => 'claude-3-opus-20240229'], 'system_prompt' => 'Be concise.', ], ], @@ -137,10 +137,10 @@ private function getFullConfig(): array 'indexer' => [ 'my_text_indexer' => [ 'store' => 'my_azure_search_store_service_id', - 'platform' => 'google_platform_service_id', + 'platform' => 'mistral_platform_service_id', 'model' => [ - 'name' => 'embeddings', - 'version' => 'text-embedding-004', + 'class' => 'Symfony\AI\Platform\Bridge\Mistral\Embeddings', + 'name' => 'mistral-embed', 'options' => ['dimension' => 768], ], ],