Use symfony AI platform for AI provider

This commit is contained in:
Jan Böhmer 2026-04-23 23:26:23 +02:00
parent 90d327fdaa
commit 9cf16248e6
11 changed files with 1304 additions and 30 deletions

View file

@ -33,4 +33,5 @@ return [
Jbtronics\SettingsBundle\JbtronicsSettingsBundle::class => ['all' => true],
Jbtronics\TranslationEditorBundle\JbtronicsTranslationEditorBundle::class => ['dev' => true],
ApiPlatform\Symfony\Bundle\ApiPlatformBundle::class => ['all' => true],
Symfony\AI\AiBundle\AiBundle::class => ['all' => true],
];

27
config/packages/ai.yaml Normal file
View file

@ -0,0 +1,27 @@
ai:
platform:
# Inference Platform configuration
# see https://github.com/symfony/ai/tree/main/src/platform#platform-bridges
# openai:
# api_key: '%env(OPENAI_API_KEY)%'
agent:
# Agent configuration
# see https://symfony.com/doc/current/ai/bundles/ai-bundle.html
# default:
# platform: 'ai.platform.openai'
# model: 'gpt-5-mini'
# prompt: |
# You are a pirate and you write funny.
# tools:
# - 'Symfony\AI\Agent\Bridge\Clock\Clock'
store:
# Store configuration
# chromadb:
# default:
# client: 'client.service.id'
# collection: 'my_collection'

View file

@ -0,0 +1,5 @@
ai:
platform:
generic:
default:
base_url: '%env(GENERIC_BASE_URL)%'

View file

@ -0,0 +1,3 @@
ai:
platform:
lmstudio: null

View file

@ -0,0 +1,4 @@
ai:
platform:
openrouter:
api_key: '%env(OPENROUTER_API_KEY)%'

View file

@ -2674,6 +2674,465 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* ...<string, mixed>
* },
* }
* @psalm-type AiConfig = array{
* platform?: array{
* albert?: array{
* api_key?: string|Param,
* base_url?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* amazeeai?: array{
* base_url?: string|Param,
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* anthropic?: array{
* api_key?: string|Param,
* version?: string|Param, // Default: null
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* cache_retention?: "none"|"short"|"long"|Param, // Prompt cache retention policy for Anthropic models // Default: "short"
* },
* azure?: array<string, array{ // Default: []
* api_key?: string|Param,
* base_url?: string|Param,
* deployment?: string|Param,
* api_version?: string|Param, // The used API version
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* }>,
* bedrock?: array<string, array{ // Default: []
* bedrock_runtime_client?: string|Param, // Service ID of the Bedrock runtime client to use // Default: null
* model_catalog?: string|Param, // Default: null
* }>,
* cache?: array<string, array{ // Default: []
* platform?: string|Param,
* service?: string|Param, // The cache service id as defined under the "cache" configuration key // Default: "cache.app"
* cache_key?: string|Param, // Key used to store platform results, if not set, the current platform name will be used, the "prompt_cache_key" can be set during platform call to override this value
* ttl?: int|Param,
* }>,
* cartesia?: array{
* api_key?: string|Param,
* version?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* cerebras?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* cohere?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* decart?: array{
* api_key?: string|Param,
* host?: string|Param, // Default: "https://api.decart.ai/v1"
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* deepseek?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* dockermodelrunner?: array{
* host_url?: string|Param, // Default: "http://127.0.0.1:12434"
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* elevenlabs?: array{
* api_key?: string|Param,
* endpoint?: string|Param, // Default: "https://api.elevenlabs.io/v1/"
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* failover?: array<string, array{ // Default: []
* platforms?: list<scalar|Param|null>,
* rate_limiter?: string|Param,
* }>,
* gemini?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* generic?: array<string, array{ // Default: []
* base_url?: string|Param,
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* model_catalog?: string|Param, // Service ID of the model catalog to use
* supports_completions?: bool|Param, // Default: true
* supports_embeddings?: bool|Param, // Default: true
* completions_path?: string|Param, // Default: "/v1/chat/completions"
* embeddings_path?: string|Param, // Default: "/v1/embeddings"
* }>,
* huggingface?: array{
* api_key?: string|Param,
* provider?: string|Param, // Default: "hf-inference"
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* lmstudio?: array{
* host_url?: string|Param, // Default: "http://127.0.0.1:1234"
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* mistral?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* ollama?: array{
* endpoint?: string|Param, // Endpoint for Ollama (e.g. "http://127.0.0.1:11434" for local, or a cloud endpoint). If null, the http_client is used as-is and must already be configured with a base URI.
* api_key?: string|Param, // API key for Ollama Cloud authentication (optional for local usage)
* http_client?: string|Param, // Service ID of the HTTP client to use. When "endpoint" is null, this client must be pre-configured (e.g. with a base_uri). // Default: "http_client"
* },
* openai?: array{
* api_key?: string|Param,
* region?: scalar|Param|null, // The region for OpenAI API (EU, US, or null for default) // Default: null
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* openrouter?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* ovh?: array{
* api_key?: scalar|Param|null,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* perplexity?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* scaleway?: array{
* api_key?: scalar|Param|null,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* transformersphp?: array<mixed>,
* vertexai?: array{
* location?: string|Param, // Required for the project-scoped endpoint. Must be set together with "project_id". // Default: null
* project_id?: string|Param, // Required for the project-scoped endpoint. Must be set together with "location". // Default: null
* api_key?: string|Param, // When set without "location" and "project_id", uses the global endpoint. Note: API keys only identify the project for billing and do not provide identity-based access control. For production use with IAM, audit logging, or data residency, prefer the project-scoped endpoint with service account authentication. // Default: null
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* voyage?: array{
* api_key?: string|Param,
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* },
* },
* model?: array<string, array<string, array{ // Default: []
* class?: string|Param, // The fully qualified class name of the model (must extend Symfony\AI\Platform\Model) // Default: "Symfony\\AI\\Platform\\Model"
* capabilities?: list<value-of<\Symfony\AI\Platform\Capability>|\Symfony\AI\Platform\Capability|Param>,
* }>>,
* agent?: array<string, array{ // Default: []
* platform?: string|Param, // Service name of platform // Default: "Symfony\\AI\\Platform\\PlatformInterface"
* model?: mixed,
* memory?: mixed, // Memory configuration: string for static memory, or array with "service" key for service reference // Default: null
* prompt?: string|array{ // The system prompt configuration
* text?: string|Param, // The system prompt text
* file?: string|Param, // Path to file containing the system prompt
* include_tools?: bool|Param, // Include tool definitions at the end of the system prompt // Default: false
* enable_translation?: bool|Param, // Enable translation for the system prompt // Default: false
* translation_domain?: string|Param, // The translation domain for the system prompt // Default: null
* },
* tools?: bool|array{
* enabled?: bool|Param, // Default: true
* services?: list<string|array{ // Default: []
* service?: string|Param,
* agent?: string|Param,
* name?: string|Param,
* description?: string|Param,
* method?: string|Param,
* }>,
* },
* keep_tool_messages?: bool|Param, // Keep tool messages in the conversation history // Default: false
* include_sources?: bool|Param, // Include sources exposed by tools as part of the tool result metadata // Default: false
* fault_tolerant_toolbox?: bool|Param, // Continue the agent run even if a tool call fails // Default: true
* speech?: bool|array{ // Speech (TTS/STT) decorator configuration
* enabled?: bool|Param, // Default: true
* text_to_speech_platform?: string|Param, // Service name of the TTS platform (e.g. ai.platform.elevenlabs). // Default: null
* speech_to_text_platform?: string|Param, // Service name of the STT platform (e.g. ai.platform.openai). // Default: null
* tts_model?: string|Param, // Text-to-speech model name // Default: null
* tts_options?: mixed, // Provider-specific TTS options // Default: []
* stt_model?: string|Param, // Speech-to-text model name // Default: null
* stt_options?: mixed, // Provider-specific STT options // Default: []
* },
* }>,
* multi_agent?: array<string, array{ // Default: []
* orchestrator?: string|Param, // Service ID of the orchestrator agent
* handoffs?: array<string, list<scalar|Param|null>>,
* fallback?: string|Param, // Service ID of the fallback agent for unmatched requests
* }>,
* store?: array{
* azuresearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* api_version?: string|Param,
* index_name?: string|Param, // The name of the store will be used if the "index_name" option is not set
* http_client?: string|Param, // Default: "http_client"
* vector_field?: string|Param, // Default: "vector"
* }>,
* cache?: array<string, array{ // Default: []
* service?: string|Param, // Default: "cache.app"
* cache_key?: string|Param, // The name of the store will be used if the key is not set.
* strategy?: string|Param, // Default: "cosine"
* }>,
* chromadb?: array<string, array{ // Default: []
* client?: string|Param, // Default: "Codewithkyrian\\ChromaDB\\Client"
* collection?: string|Param,
* }>,
* clickhouse?: array<string, array{ // Default: []
* dsn?: string|Param,
* http_client?: string|Param,
* database?: string|Param,
* table?: string|Param,
* }>,
* cloudflare?: array<string, array{ // Default: []
* account_id?: string|Param,
* api_key?: string|Param,
* index_name?: string|Param,
* dimensions?: int|Param, // Default: 1536
* metric?: string|Param, // Default: "cosine"
* endpoint?: string|Param,
* }>,
* elasticsearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* index_name?: string|Param,
* vectors_field?: string|Param, // Default: "_vectors"
* dimensions?: int|Param, // Default: 1536
* similarity?: string|Param, // Default: "cosine"
* http_client?: string|Param, // Default: "http_client"
* }>,
* manticoresearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* table?: string|Param,
* field?: string|Param, // Default: "_vectors"
* type?: string|Param, // Default: "hnsw"
* similarity?: string|Param, // Default: "cosine"
* dimensions?: int|Param, // Default: 1536
* quantization?: string|Param,
* }>,
* mariadb?: array<string, array{ // Default: []
* connection?: string|Param,
* table_name?: string|Param,
* index_name?: string|Param,
* vector_field_name?: string|Param,
* setup_options?: array{
* dimensions?: int|Param,
* },
* distance?: "cosine"|"euclidean"|"distance"|Param, // Distance metric to use for vector similarity search // Default: "euclidean"
* }>,
* meilisearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* index_name?: string|Param,
* embedder?: string|Param, // Default: "default"
* vector_field?: string|Param, // Default: "_vectors"
* dimensions?: int|Param, // Default: 1536
* semantic_ratio?: float|Param, // The ratio between semantic (vector) and full-text search (0.0 to 1.0). Default: 1.0 (100% semantic) // Default: 1.0
* }>,
* memory?: array<string, array{ // Default: []
* strategy?: string|Param,
* }>,
* milvus?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* database?: string|Param,
* collection?: string|Param,
* vector_field?: string|Param, // Default: "_vectors"
* dimensions?: int|Param, // Default: 1536
* metric_type?: string|Param, // Default: "COSINE"
* }>,
* mongodb?: array<string, array{ // Default: []
* client?: string|Param, // Default: "MongoDB\\Client"
* database?: string|Param,
* collection?: string|Param,
* index_name?: string|Param,
* vector_field?: string|Param, // Default: "vector"
* bulk_write?: bool|Param, // Default: false
* setup_options?: array{
* fields?: mixed, // Default: []
* },
* }>,
* neo4j?: array<string, array{ // Default: []
* endpoint?: string|Param,
* username?: string|Param,
* password?: string|Param,
* database?: string|Param,
* vector_index_name?: string|Param,
* node_name?: string|Param,
* vector_field?: string|Param, // Default: "embeddings"
* dimensions?: int|Param, // Default: 1536
* distance?: string|Param, // Default: "cosine"
* quantization?: bool|Param,
* }>,
* opensearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* index_name?: string|Param,
* vectors_field?: string|Param, // Default: "_vectors"
* dimensions?: int|Param, // Default: 1536
* space_type?: string|Param, // Default: "l2"
* http_client?: string|Param, // Default: "http_client"
* }>,
* pinecone?: array<string, array{ // Default: []
* client?: string|Param, // Default: "Probots\\Pinecone\\Client"
* index_name?: string|Param,
* namespace?: string|Param,
* filter?: list<scalar|Param|null>,
* top_k?: int|Param,
* }>,
* postgres?: array<string, array{ // Default: []
* dsn?: string|Param,
* username?: string|Param,
* password?: string|Param,
* table_name?: string|Param,
* vector_field?: string|Param, // Default: "embedding"
* distance?: "cosine"|"inner_product"|"l1"|"l2"|Param, // Distance metric to use for vector similarity search // Default: "l2"
* dbal_connection?: string|Param,
* setup_options?: array{
* vector_type?: string|Param, // Default: "vector"
* vector_size?: int|Param, // Default: 1536
* index_method?: string|Param, // Default: "ivfflat"
* index_opclass?: string|Param, // Default: "vector_cosine_ops"
* },
* }>,
* qdrant?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* collection_name?: string|Param, // The name of the store will be used if the "collection_name" is not set
* http_client?: string|Param, // Default: "http_client"
* dimensions?: int|Param, // Default: 1536
* distance?: string|Param, // Default: "Cosine"
* async?: bool|Param, // Default: false
* }>,
* redis?: array<string, array{ // Default: []
* connection_parameters?: mixed, // see https://github.com/phpredis/phpredis?tab=readme-ov-file#example-1
* client?: string|Param, // a service id of a Redis client
* index_name?: string|Param,
* key_prefix?: string|Param, // Default: "vector:"
* distance?: "COSINE"|"L2"|"IP"|Param, // Distance metric to use for vector similarity search // Default: "COSINE"
* }>,
* s3vectors?: array<string, array{ // Default: []
* client?: string|Param, // Service reference to an existing S3VectorsClient
* configuration?: array<mixed>,
* vector_bucket_name?: string|Param,
* index_name?: string|Param,
* filter?: array<mixed>,
* top_k?: int|Param, // Default number of results to return // Default: 3
* }>,
* sqlite?: array<string, array{ // Default: []
* dsn?: string|Param,
* connection?: string|Param,
* table_name?: string|Param,
* strategy?: string|Param,
* vec?: bool|Param, // Default: false
* distance?: "cosine"|"L2"|Param, // Default: "cosine"
* vector_dimension?: int|Param, // Default: 1536
* }>,
* supabase?: array<string, array{ // Default: []
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
* url?: string|Param,
* api_key?: string|Param,
* table?: string|Param,
* vector_field?: string|Param, // Default: "embedding"
* vector_dimension?: int|Param, // Default: 1536
* function_name?: string|Param, // Default: "match_documents"
* }>,
* surrealdb?: array<string, array{ // Default: []
* endpoint?: string|Param,
* username?: string|Param,
* password?: string|Param,
* namespace?: string|Param,
* database?: string|Param,
* table?: string|Param,
* vector_field?: string|Param, // Default: "_vectors"
* strategy?: string|Param, // Default: "cosine"
* dimensions?: int|Param, // Default: 1536
* namespaced_user?: bool|Param,
* }>,
* typesense?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* collection?: string|Param,
* vector_field?: string|Param, // Default: "_vectors"
* dimensions?: int|Param, // Default: 1536
* }>,
* weaviate?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* http_client?: string|Param, // Default: "http_client"
* collection?: string|Param, // The name of the store will be used if the "collection" is not set
* }>,
* vektor?: array<string, array{ // Default: []
* storage_path?: string|Param, // Default: "%kernel.project_dir%/var/share"
* dimensions?: int|Param, // Default: 1536
* }>,
* },
* message_store?: array{
* cache?: array<string, array{ // Default: []
* service?: string|Param, // Default: "cache.app"
* key?: string|Param, // The name of the message store will be used if the key is not set
* ttl?: int|Param,
* }>,
* cloudflare?: array<string, array{ // Default: []
* account_id?: string|Param,
* api_key?: string|Param,
* namespace?: string|Param,
* endpoint_url?: string|Param, // If the version of the Cloudflare API is updated, use this key to support it.
* }>,
* doctrine?: array{
* dbal?: array<string, array{ // Default: []
* connection?: string|Param,
* table_name?: string|Param, // The name of the message store will be used if the table_name is not set
* }>,
* },
* meilisearch?: array<string, array{ // Default: []
* endpoint?: string|Param,
* api_key?: string|Param,
* index_name?: string|Param,
* }>,
* memory?: array<string, array{ // Default: []
* identifier?: string|Param,
* }>,
* mongodb?: array<string, array{ // Default: []
* client?: string|Param, // Default: "MongoDB\\Client"
* database?: string|Param,
* collection?: string|Param,
* }>,
* pogocache?: array<string, array{ // Default: []
* endpoint?: string|Param,
* password?: string|Param,
* key?: string|Param,
* }>,
* redis?: array<string, array{ // Default: []
* connection_parameters?: mixed, // see https://github.com/phpredis/phpredis?tab=readme-ov-file#example-1
* client?: string|Param, // a service id of a Redis client
* endpoint?: string|Param,
* index_name?: string|Param,
* }>,
* session?: array<string, array{ // Default: []
* identifier?: string|Param,
* }>,
* surrealdb?: array<string, array{ // Default: []
* endpoint?: string|Param,
* username?: string|Param,
* password?: string|Param,
* namespace?: string|Param,
* database?: string|Param,
* table?: string|Param,
* namespaced_user?: bool|Param, // Using a namespaced user is a good practice to prevent any undesired access to a specific table, see https://surrealdb.com/docs/surrealdb/reference-guide/security-best-practices
* }>,
* },
* chat?: array<string, array{ // Default: []
* agent?: string|Param,
* message_store?: string|Param,
* }>,
* vectorizer?: array<string, array{ // Default: []
* platform?: string|Param, // Service name of platform // Default: "Symfony\\AI\\Platform\\PlatformInterface"
* model?: mixed,
* }>,
* indexer?: array<string, array{ // Default: []
* loader?: string|Param, // Service name of loader // Default: null
* source?: mixed, // Source identifier (file path, URL, etc.) or array of sources // Default: null
* transformers?: list<scalar|Param|null>,
* filters?: list<scalar|Param|null>,
* vectorizer?: scalar|Param|null, // Service name of vectorizer // Default: "Symfony\\AI\\Store\\Document\\VectorizerInterface"
* store?: string|Param, // Service name of store // Default: "Symfony\\AI\\Store\\StoreInterface"
* }>,
* retriever?: array<string, array{ // Default: []
* vectorizer?: scalar|Param|null, // Service name of vectorizer // Default: "Symfony\\AI\\Store\\Document\\VectorizerInterface"
* store?: string|Param, // Service name of store // Default: "Symfony\\AI\\Store\\StoreInterface"
* }>,
* }
* @psalm-type ConfigType = array{
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
@ -2703,6 +3162,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* nelmio_cors?: NelmioCorsConfig,
* jbtronics_settings?: JbtronicsSettingsConfig,
* api_platform?: ApiPlatformConfig,
* ai?: AiConfig,
* "when@dev"?: array{
* imports?: ImportsConfig,
* parameters?: ParametersConfig,
@ -2736,6 +3196,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* jbtronics_settings?: JbtronicsSettingsConfig,
* jbtronics_translation_editor?: JbtronicsTranslationEditorConfig,
* api_platform?: ApiPlatformConfig,
* ai?: AiConfig,
* },
* "when@docker"?: array{
* imports?: ImportsConfig,
@ -2766,6 +3227,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* nelmio_cors?: NelmioCorsConfig,
* jbtronics_settings?: JbtronicsSettingsConfig,
* api_platform?: ApiPlatformConfig,
* ai?: AiConfig,
* },
* "when@prod"?: array{
* imports?: ImportsConfig,
@ -2796,6 +3258,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* nelmio_cors?: NelmioCorsConfig,
* jbtronics_settings?: JbtronicsSettingsConfig,
* api_platform?: ApiPlatformConfig,
* ai?: AiConfig,
* },
* "when@test"?: array{
* imports?: ImportsConfig,
@ -2829,6 +3292,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
* nelmio_cors?: NelmioCorsConfig,
* jbtronics_settings?: JbtronicsSettingsConfig,
* api_platform?: ApiPlatformConfig,
* ai?: AiConfig,
* },
* ...<string, ExtensionType|array{ // extra keys must follow the when@%env% pattern or match an extension alias
* imports?: ImportsConfig,