feat: #3586427 Adopt symfony/ai v0.8's Platform concept
Dependencies
!! This MR builds upon the MR #1613 - please review this one first!
Description
This MR implements the Symfony AI integration architecture as documented in ADR 001 (AI Platform Integration Architecture), using the Plugin-in-Config pattern for both platform and inference provider configuration entities.
New Plugin Types & Config Entities
-
@AiInferenceProviderplugin type +ai_inference_providerconfig entity- One Symfony inference backend with API key / base URL / model list
- Ships
Generic(OpenAI-compatible) andGeminiproviders - Plugin namespace:
Plugin/ai/InferenceProvider/ - Config entity is a passive data container - uses Plugin-in-Config pattern (no
getProviderInstance())
-
@AiModelRouterplugin type- Routes model requests to appropriate providers
- Ships
CatalogBasedModelRouter(default) - Plugin namespace:
Plugin/ai/ModelRouter/
-
@AiPlatformplugin type with three categories:- Backend plugins (
isBackendOnly: TRUE) - internal engine options (e.g.,platform_with_model_routing) managed:<id>derivatives - one perai_platformconfig entity viaManagedPlatformDeriverprovider:<id>derivatives - one perai_inference_providerconfig entity viaProviderPlatformDeriver, auto-exposing every inference provider as a zero-config single-provider platform
- Backend plugins (
-
@AiPlatformMiddlewareplugin type- Cross-cutting wrappers (caching / retry / fallback / tracing)
- Ships Tracing middleware (
Symfony\AI\Platform\TraceablePlatformwrapper) - UI sub-form is
@todo
Config Entity Architecture (Plugin-in-Config Pattern)
Both config entities follow the Plugin-in-Config pattern per ADR 001:
ai_platform config entity:
- Stores:
platform_plugin_id,platform_plugin_configuration,middlewares[] - Pure data container - NO
getPlatform()method - Consumers MUST use plugin manager:
$platform_manager->createInstance('managed:' . $entity_id)
ai_inference_provider config entity:
- Stores:
plugin,configuration - Pure data container - NO
getProviderInstance()method - Consumers MUST use plugin manager:
$provider_manager->createInstance($entity->getPluginId(), $entity->getPluginConfiguration())
APIs
New canonical API:
// Get platforms for an operation type
$platforms = $platform_manager->getPlatformsForOperationType($operation_type);
// Access a specific platform
$platform = $platform_manager->createInstance('managed:my_platform');
$platform = $platform_manager->createInstance('provider:my_provider');Legacy API (deprecated but functional):
// Returns PlatformProxy BC adapters for 1.x consumers
$providers = $this->providerManager->getProvidersForOperationType($operation_type);The legacy AiProviderInterface / ChatInterface consumers continue working unchanged via PlatformProxy adapters.
Admin UI
/admin/config/ai/inference-providers- CRUD for inference provider configs/admin/config/ai/platforms- CRUD for platform configs- Backend plugin selector (e.g., "Platform with Model Routing")
- Provider multi-select + router picker
- Middleware stack (UI sub-form is
@todo)
Addressing Schemes
Via plugin.manager.ai.platform:
managed:<entity_id>- Composed platform from config entityprovider:<inference_provider_id>- Auto-derived single-provider platform
Via legacy ai.provider API:
ai_platform:managed:<id>- Wrapped in PlatformProxy for BCai_platform:provider:<id>- Wrapped in PlatformProxy for BC
Echo Test Fixture
Migrated to new architecture:
Plugin/ai/InferenceProvider/EchoProvider- Config:
ai.inference_provider.echo_test+ai.platform.echo_test
Testing instructions
-
Add an inference provider at
/admin/config/ai/inference-providers:- Plugin: Generic
- Base URL + Key entity + models (one per line)
-
Add a platform entity at
/admin/config/ai/platforms:- Label: "My Composed Platform"
- Backend plugin: Platform with Model Routing
- Providers: select the provider from step 1
- Routing plugin: Catalog-based
- (Middleware sub-form is
@todo— field exists but UI is placeholder)
-
Configure default provider at
/admin/config/ai/settings:- For the chat operation, try both addressing schemes:
ai_platform:provider:<inference_provider_id>(auto-derived single-provider platform — no config entity required)ai_platform:managed:<your_platform_id>(the explicit composed platform from step 2)
- For the chat operation, try both addressing schemes:
-
Test in API Explorer at
/admin/config/ai/ai-api-explorer/chat-generation:- Send a prompt with each configuration
- Both should reply successfully
Architecture Documentation
- ADR 001:
docs/adr/symfony-ai-integration/001-ai-platform-integration.md- Core architecture decision - ADR 002:
docs/adr/symfony-ai-integration/002-inference-provider-config-migration.md- Migration to Plugin-in-Config pattern
Checklist
- I have linked the related issue in the MR title or description
- I have performed a self-review of my own code
- I have added or updated tests, or explained in the description why this change is not covered by tests
- I have updated documentation for any new or changed functionality
- I have written testing instructions and verified them locally
- I have noted any required post-merge steps (config imports, cache rebuilds, manual changes)
- This MR contains no breaking API or hook changes, or they are explicitly documented in the description
AI Compliance
Note
Check the one that best describes your usage, or leave all unchecked if AI was not significantly used.
-
AI Assisted Code
Mainly written by a human; AI used for autocomplete or partial generation under full human supervision. -
AI Generated Code
Mainly generated by AI, reviewed and approved by a human before this MR was created. -
Vibe Coded
Generated by AI and only functionally reviewed before this MR was created.
Closes #3586427 (closed)