Skip to content

Commit d673f98

Browse files
committed
[AIBundle] Wire & configure services explicitly
1 parent 24797e3 commit d673f98

File tree

4 files changed

+84
-49
lines changed

4 files changed

+84
-49
lines changed

src/ai-bundle/config/options.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Config\Definition\Configurator;
1313

14+
use Codewithkyrian\ChromaDB\Client as ChromaDBClient;
1415
use Symfony\AI\Platform\PlatformInterface;
1516
use Symfony\AI\Store\StoreInterface;
1617

@@ -154,6 +155,7 @@
154155
->useAttributeAsKey('name')
155156
->arrayPrototype()
156157
->children()
158+
->scalarNode('client_service')->cannotBeEmpty()->defaultValue(ChromaDBClient::class)->end()
157159
->scalarNode('collection')->isRequired()->end()
158160
->end()
159161
->end()

src/ai-bundle/config/services.php

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,65 +19,89 @@
1919
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
2020
use Symfony\AI\Agent\Toolbox\ToolCallArgumentResolver;
2121
use Symfony\AI\Agent\Toolbox\ToolFactory\ReflectionToolFactory;
22-
use Symfony\AI\Agent\Toolbox\ToolFactoryInterface;
2322
use Symfony\AI\Agent\Toolbox\ToolResultConverter;
2423
use Symfony\AI\AIBundle\Profiler\DataCollector;
2524
use Symfony\AI\AIBundle\Profiler\TraceableToolbox;
2625
use Symfony\AI\AIBundle\Security\EventListener\IsGrantedToolAttributeListener;
26+
use Symfony\AI\Platform\Contract;
27+
use Symfony\AI\Platform\Contract\JsonSchema\Factory;
2728

2829
return static function (ContainerConfigurator $container): void {
2930
$container->services()
30-
->defaults()
31-
->autowire()
32-
31+
->set('ai.platform.contract', Contract::class)
32+
->factory([Contract::class, 'create'])
3333
// structured output
34-
->set(ResponseFormatFactory::class)
35-
->alias(ResponseFormatFactoryInterface::class, ResponseFormatFactory::class)
36-
->set(StructureOutputProcessor::class)
34+
->set('ai.response_format_factory', ResponseFormatFactory::class)
35+
->args([
36+
'$schemaFactory' => service('ai.json_schema_factory'),
37+
])
38+
->set('ai.json_schema_factory', Factory::class)
39+
->alias(ResponseFormatFactoryInterface::class, 'ai.response_format_factory')
40+
->set('ai.agent.structured_output_processor', StructureOutputProcessor::class)
41+
->args([
42+
'$responseFormatFactory' => service('ai.response_format_factory'),
43+
'$serializer' => service('serializer'),
44+
])
3745
->tag('ai.agent.input_processor')
3846
->tag('ai.agent.output_processor')
3947

4048
// tools
41-
->set('ai.toolbox.abstract')
42-
->class(Toolbox::class)
43-
->autowire()
49+
->set('ai.toolbox.abstract', Toolbox::class)
4450
->abstract()
4551
->args([
46-
'$toolFactory' => service(ToolFactoryInterface::class),
52+
'$toolFactory' => service('ai.tool_factory'),
4753
'$tools' => abstract_arg('Collection of tools'),
4854
])
49-
->set(Toolbox::class)
55+
->set('ai.toolbox', Toolbox::class)
5056
->parent('ai.toolbox.abstract')
5157
->args([
5258
'$tools' => tagged_iterator('ai.tool'),
5359
])
54-
->alias(ToolboxInterface::class, Toolbox::class)
55-
->set(ReflectionToolFactory::class)
56-
->alias(ToolFactoryInterface::class, ReflectionToolFactory::class)
57-
->set(ToolResultConverter::class)
58-
->set(ToolCallArgumentResolver::class)
59-
->set('ai.tool.agent_processor.abstract')
60-
->class(ToolProcessor::class)
60+
->alias(ToolboxInterface::class, 'ai.toolbox')
61+
->set('ai.tool_factory', ReflectionToolFactory::class)
62+
->set('ai.tool_result_converter', ToolResultConverter::class)
63+
->args([
64+
'$serializer' => service('serializer'),
65+
])
66+
->set('ai.tool_call_argument_resolver', ToolCallArgumentResolver::class)
67+
->args([
68+
'$denormalizer' => service('serializer'),
69+
])
70+
->set('ai.tool.agent_processor.abstract', ToolProcessor::class)
6171
->abstract()
6272
->args([
6373
'$toolbox' => abstract_arg('Toolbox'),
6474
])
65-
->set(ToolProcessor::class)
75+
->set('ai.tool.agent_processor', ToolProcessor::class)
6676
->parent('ai.tool.agent_processor.abstract')
6777
->tag('ai.agent.input_processor')
6878
->tag('ai.agent.output_processor')
6979
->args([
70-
'$toolbox' => service(ToolboxInterface::class),
80+
'$toolbox' => service('ai.toolbox'),
81+
'$resultConverter' => service('ai.tool_result_converter'),
7182
'$eventDispatcher' => service('event_dispatcher')->nullOnInvalid(),
83+
'$keepToolMessages' => false,
7284
])
7385
->set('ai.security.is_granted_attribute_listener', IsGrantedToolAttributeListener::class)
86+
->args([
87+
'$authChecker' => service('security.authorization_checker'),
88+
'$expressionLanguage' => service('expression_language')->nullOnInvalid(),
89+
])
7490
->tag('kernel.event_listener')
7591

7692
// profiler
77-
->set(DataCollector::class)
93+
->set('ai.data_collector', DataCollector::class)
94+
->args([
95+
'$platforms' => tagged_iterator('ai.traceable_platform'),
96+
'$defaultToolBox' => service('ai.toolbox'),
97+
'$toolboxes' => tagged_iterator('ai.traceable_toolbox'),
98+
])
7899
->tag('data_collector')
79-
->set(TraceableToolbox::class)
80-
->decorate(ToolboxInterface::class)
100+
->set('ai.traceable_toolbox', TraceableToolbox::class)
101+
->decorate('ai.toolbox')
102+
->args([
103+
'$toolbox' => service('.inner'),
104+
])
81105
->tag('ai.traceable_toolbox')
82106
;
83107
};

src/ai-bundle/src/AIBundle.php

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,12 @@
1616
use Symfony\AI\Agent\InputProcessor\SystemPromptInputProcessor;
1717
use Symfony\AI\Agent\InputProcessorInterface;
1818
use Symfony\AI\Agent\OutputProcessorInterface;
19-
use Symfony\AI\Agent\StructuredOutput\AgentProcessor as StructureOutputProcessor;
2019
use Symfony\AI\Agent\Toolbox\AgentProcessor as ToolProcessor;
2120
use Symfony\AI\Agent\Toolbox\Attribute\AsTool;
2221
use Symfony\AI\Agent\Toolbox\FaultTolerantToolbox;
2322
use Symfony\AI\Agent\Toolbox\Tool\Agent as AgentTool;
2423
use Symfony\AI\Agent\Toolbox\ToolFactory\ChainFactory;
2524
use Symfony\AI\Agent\Toolbox\ToolFactory\MemoryToolFactory;
26-
use Symfony\AI\Agent\Toolbox\ToolFactory\ReflectionToolFactory;
2725
use Symfony\AI\AIBundle\Exception\InvalidArgumentException;
2826
use Symfony\AI\AIBundle\Profiler\DataCollector;
2927
use Symfony\AI\AIBundle\Profiler\TraceablePlatform;
@@ -52,6 +50,7 @@
5250
use Symfony\Component\Config\Definition\Configurator\DefinitionConfigurator;
5351
use Symfony\Component\DependencyInjection\ChildDefinition;
5452
use Symfony\Component\DependencyInjection\ContainerBuilder;
53+
use Symfony\Component\DependencyInjection\ContainerInterface;
5554
use Symfony\Component\DependencyInjection\Definition;
5655
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
5756
use Symfony\Component\DependencyInjection\Reference;
@@ -158,11 +157,12 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
158157
$platformId = 'ai.platform.anthropic';
159158
$definition = (new Definition(Platform::class))
160159
->setFactory(AnthropicPlatformFactory::class.'::create')
161-
->setAutowired(true)
162160
->setLazy(true)
163161
->addTag('proxy', ['interface' => PlatformInterface::class])
164162
->setArguments([
165163
'$apiKey' => $platform['api_key'],
164+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
165+
'$contract' => new Reference('ai.platform.contract'),
166166
])
167167
->addTag('ai.platform');
168168

@@ -180,14 +180,15 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
180180
$platformId = 'ai.platform.azure.'.$name;
181181
$definition = (new Definition(Platform::class))
182182
->setFactory(AzureOpenAIPlatformFactory::class.'::create')
183-
->setAutowired(true)
184183
->setLazy(true)
185184
->addTag('proxy', ['interface' => PlatformInterface::class])
186185
->setArguments([
187186
'$baseUrl' => $config['base_url'],
188187
'$deployment' => $config['deployment'],
189188
'$apiVersion' => $config['api_version'],
190189
'$apiKey' => $config['api_key'],
190+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
191+
'$contract' => new Reference('ai.platform.contract'),
191192
])
192193
->addTag('ai.platform');
193194

@@ -201,10 +202,13 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
201202
$platformId = 'ai.platform.google';
202203
$definition = (new Definition(Platform::class))
203204
->setFactory(GooglePlatformFactory::class.'::create')
204-
->setAutowired(true)
205205
->setLazy(true)
206206
->addTag('proxy', ['interface' => PlatformInterface::class])
207-
->setArguments(['$apiKey' => $platform['api_key']])
207+
->setArguments([
208+
'$apiKey' => $platform['api_key'],
209+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
210+
'$contract' => new Reference('ai.platform.contract'),
211+
])
208212
->addTag('ai.platform');
209213

210214
$container->setDefinition($platformId, $definition);
@@ -216,10 +220,13 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
216220
$platformId = 'ai.platform.openai';
217221
$definition = (new Definition(Platform::class))
218222
->setFactory(OpenAIPlatformFactory::class.'::create')
219-
->setAutowired(true)
220223
->setLazy(true)
221224
->addTag('proxy', ['interface' => PlatformInterface::class])
222-
->setArguments(['$apiKey' => $platform['api_key']])
225+
->setArguments([
226+
'$apiKey' => $platform['api_key'],
227+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
228+
'$contract' => new Reference('ai.platform.contract'),
229+
])
223230
->addTag('ai.platform');
224231

225232
$container->setDefinition($platformId, $definition);
@@ -231,10 +238,13 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
231238
$platformId = 'ai.platform.openrouter';
232239
$definition = (new Definition(Platform::class))
233240
->setFactory(OpenRouterPlatformFactory::class.'::create')
234-
->setAutowired(true)
235241
->setLazy(true)
236242
->addTag('proxy', ['interface' => PlatformInterface::class])
237-
->setArguments(['$apiKey' => $platform['api_key']])
243+
->setArguments([
244+
'$apiKey' => $platform['api_key'],
245+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
246+
'$contract' => new Reference('ai.platform.contract'),
247+
])
238248
->addTag('ai.platform');
239249

240250
$container->setDefinition($platformId, $definition);
@@ -246,10 +256,13 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
246256
$platformId = 'ai.platform.mistral';
247257
$definition = (new Definition(Platform::class))
248258
->setFactory(MistralPlatformFactory::class.'::create')
249-
->setAutowired(true)
250259
->setLazy(true)
251260
->addTag('proxy', ['interface' => PlatformInterface::class])
252-
->setArguments(['$apiKey' => $platform['api_key']])
261+
->setArguments([
262+
'$apiKey' => $platform['api_key'],
263+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
264+
'$contract' => new Reference('ai.platform.contract'),
265+
])
253266
->addTag('ai.platform');
254267

255268
$container->setDefinition($platformId, $definition);
@@ -261,10 +274,13 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
261274
$platformId = 'symfony_ai.platform.lmstudio';
262275
$definition = (new Definition(Platform::class))
263276
->setFactory(LMStudioPlatformFactory::class.'::create')
264-
->setAutowired(true)
265277
->setLazy(true)
266278
->addTag('proxy', ['interface' => PlatformInterface::class])
267-
->setArguments(['$hostUrl' => $platform['host_url']])
279+
->setArguments([
280+
'$hostUrl' => $platform['host_url'],
281+
'$httpClient' => new Reference('http_client', ContainerInterface::NULL_ON_INVALID_REFERENCE),
282+
'$contract' => new Reference('ai.platform.contract'),
283+
])
268284
->addTag('symfony_ai.platform');
269285

270286
$container->setDefinition($platformId, $definition);
@@ -299,7 +315,6 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
299315

300316
// AGENT
301317
$agentDefinition = (new Definition(Agent::class))
302-
->setAutowired(true)
303318
->setArgument('$platform', new Reference($config['platform']))
304319
->setArgument('$model', new Reference('ai.agent.'.$name.'.model'));
305320

@@ -313,7 +328,7 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
313328
$memoryFactoryDefinition = new Definition(MemoryToolFactory::class);
314329
$container->setDefinition('ai.toolbox.'.$name.'.memory_factory', $memoryFactoryDefinition);
315330
$chainFactoryDefinition = new Definition(ChainFactory::class, [
316-
'$factories' => [new Reference('ai.toolbox.'.$name.'.memory_factory'), new Reference(ReflectionToolFactory::class)],
331+
'$factories' => [new Reference('ai.toolbox.'.$name.'.memory_factory'), new Reference('ai.tool_factory')],
317332
]);
318333
$container->setDefinition('ai.toolbox.'.$name.'.chain_factory', $chainFactoryDefinition);
319334

@@ -368,8 +383,8 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
368383

369384
// STRUCTURED OUTPUT
370385
if ($config['structured_output']) {
371-
$inputProcessors[] = new Reference(StructureOutputProcessor::class);
372-
$outputProcessors[] = new Reference(StructureOutputProcessor::class);
386+
$inputProcessors[] = new Reference('ai.agent.structured_output_processor');
387+
$outputProcessors[] = new Reference('ai.agent.structured_output_processor');
373388
}
374389

375390
// SYSTEM PROMPT
@@ -412,7 +427,6 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
412427

413428
$definition = new Definition(AzureSearchStore::class);
414429
$definition
415-
->setAutowired(true)
416430
->addTag('ai.store')
417431
->setArguments($arguments);
418432

@@ -424,7 +438,7 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
424438
foreach ($stores as $name => $store) {
425439
$definition = new Definition(ChromaDBStore::class);
426440
$definition
427-
->setAutowired(true)
441+
->setArgument('$client', new Reference($store['client_service']))
428442
->setArgument('$collectionName', $store['collection'])
429443
->addTag('ai.store');
430444

@@ -450,7 +464,6 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
450464

451465
$definition = new Definition(MongoDBStore::class);
452466
$definition
453-
->setAutowired(true)
454467
->addTag('ai.store')
455468
->setArguments($arguments);
456469

@@ -474,7 +487,6 @@ private function processStoreConfig(string $type, array $stores, ContainerBuilde
474487

475488
$definition = new Definition(PineconeStore::class);
476489
$definition
477-
->setAutowired(true)
478490
->addTag('ai.store')
479491
->setArguments($arguments);
480492

src/ai-bundle/src/Profiler/DataCollector.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
use Symfony\AI\Platform\Model;
1616
use Symfony\AI\Platform\Tool\Tool;
1717
use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
18-
use Symfony\Component\DependencyInjection\Attribute\TaggedIterator;
1918
use Symfony\Component\HttpFoundation\Request;
2019
use Symfony\Component\HttpFoundation\Response;
2120

@@ -42,10 +41,8 @@ final class DataCollector extends AbstractDataCollector
4241
* @param TraceableToolbox[] $toolboxes
4342
*/
4443
public function __construct(
45-
#[TaggedIterator('ai.traceable_platform')]
4644
iterable $platforms,
4745
private readonly ToolboxInterface $defaultToolBox,
48-
#[TaggedIterator('ai.traceable_toolbox')]
4946
iterable $toolboxes,
5047
) {
5148
$this->platforms = $platforms instanceof \Traversable ? iterator_to_array($platforms) : $platforms;

0 commit comments

Comments
 (0)