Skip to content

Commit c43991b

Browse files
committed
[AI Bundle] Add tests for processors
1 parent 148413f commit c43991b

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed

src/ai-bundle/tests/DependencyInjection/AiBundleTest.php

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\Attributes\CoversClass;
1515
use PHPUnit\Framework\Attributes\DoesNotPerformAssertions;
16+
use PHPUnit\Framework\Attributes\TestDox;
1617
use PHPUnit\Framework\Attributes\TestWith;
1718
use PHPUnit\Framework\Attributes\UsesClass;
1819
use PHPUnit\Framework\TestCase;
@@ -303,6 +304,191 @@ public function testConfigurationWithUseAttributeAsKeyWorksWithoutNormalizeKeys(
303304
$this->assertTrue($container->hasDefinition('ai.store.mongodb.Production_DB-v3'));
304305
}
305306

307+
/**
308+
* Tests that processor tags use the full agent ID (ai.agent.my_agent) instead of just the agent name (my_agent).
309+
* This regression test prevents issues where processors would not be correctly associated with their agents.
310+
*/
311+
#[TestDox('Processor tags use the full agent ID instead of just the agent name')]
312+
public function testProcessorTagsUseFullAgentId()
313+
{
314+
$container = $this->buildContainer([
315+
'ai' => [
316+
'agent' => [
317+
'test_agent' => [
318+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
319+
'tools' => [
320+
['service' => 'some_tool', 'description' => 'Test tool'],
321+
],
322+
'structured_output' => true,
323+
'system_prompt' => 'You are a test assistant.',
324+
],
325+
],
326+
],
327+
]);
328+
329+
$agentId = 'ai.agent.test_agent';
330+
331+
// Test tool processor tags
332+
$toolProcessorDefinition = $container->getDefinition('ai.tool.agent_processor.test_agent');
333+
$toolProcessorTags = $toolProcessorDefinition->getTag('ai.agent.input_processor');
334+
$this->assertNotEmpty($toolProcessorTags, 'Tool processor should have input processor tags');
335+
$this->assertSame($agentId, $toolProcessorTags[0]['agent'], 'Tool input processor tag should use full agent ID');
336+
337+
$outputTags = $toolProcessorDefinition->getTag('ai.agent.output_processor');
338+
$this->assertNotEmpty($outputTags, 'Tool processor should have output processor tags');
339+
$this->assertSame($agentId, $outputTags[0]['agent'], 'Tool output processor tag should use full agent ID');
340+
341+
// Test structured output processor tags
342+
$structuredOutputTags = $container->getDefinition('ai.agent.structured_output_processor')
343+
->getTag('ai.agent.input_processor');
344+
$this->assertNotEmpty($structuredOutputTags, 'Structured output processor should have input processor tags');
345+
346+
// Find the tag for our specific agent
347+
$foundAgentTag = false;
348+
foreach ($structuredOutputTags as $tag) {
349+
if (($tag['agent'] ?? '') === $agentId) {
350+
$foundAgentTag = true;
351+
break;
352+
}
353+
}
354+
$this->assertTrue($foundAgentTag, 'Structured output processor should have tag with full agent ID');
355+
356+
// Test system prompt processor tags
357+
$systemPromptDefinition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
358+
$systemPromptTags = $systemPromptDefinition->getTag('ai.agent.input_processor');
359+
$this->assertNotEmpty($systemPromptTags, 'System prompt processor should have input processor tags');
360+
$this->assertSame($agentId, $systemPromptTags[0]['agent'], 'System prompt processor tag should use full agent ID');
361+
}
362+
363+
#[TestDox('Processors work correctly with multiple agents')]
364+
public function testMultipleAgentsWithProcessors()
365+
{
366+
$container = $this->buildContainer([
367+
'ai' => [
368+
'agent' => [
369+
'first_agent' => [
370+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
371+
'tools' => [
372+
['service' => 'tool_one', 'description' => 'Tool for first agent'],
373+
],
374+
'system_prompt' => 'First agent prompt',
375+
],
376+
'second_agent' => [
377+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\Anthropic\Claude'],
378+
'tools' => [
379+
['service' => 'tool_two', 'description' => 'Tool for second agent'],
380+
],
381+
'system_prompt' => 'Second agent prompt',
382+
],
383+
],
384+
],
385+
]);
386+
387+
// Check that each agent has its own properly tagged processors
388+
$firstAgentId = 'ai.agent.first_agent';
389+
$secondAgentId = 'ai.agent.second_agent';
390+
391+
// First agent tool processor
392+
$firstToolProcessor = $container->getDefinition('ai.tool.agent_processor.first_agent');
393+
$firstToolTags = $firstToolProcessor->getTag('ai.agent.input_processor');
394+
$this->assertSame($firstAgentId, $firstToolTags[0]['agent']);
395+
396+
// Second agent tool processor
397+
$secondToolProcessor = $container->getDefinition('ai.tool.agent_processor.second_agent');
398+
$secondToolTags = $secondToolProcessor->getTag('ai.agent.input_processor');
399+
$this->assertSame($secondAgentId, $secondToolTags[0]['agent']);
400+
401+
// First agent system prompt processor
402+
$firstSystemPrompt = $container->getDefinition('ai.agent.first_agent.system_prompt_processor');
403+
$firstSystemTags = $firstSystemPrompt->getTag('ai.agent.input_processor');
404+
$this->assertSame($firstAgentId, $firstSystemTags[0]['agent']);
405+
406+
// Second agent system prompt processor
407+
$secondSystemPrompt = $container->getDefinition('ai.agent.second_agent.system_prompt_processor');
408+
$secondSystemTags = $secondSystemPrompt->getTag('ai.agent.input_processor');
409+
$this->assertSame($secondAgentId, $secondSystemTags[0]['agent']);
410+
}
411+
412+
#[TestDox('Processors work correctly when using the default toolbox')]
413+
public function testDefaultToolboxProcessorTags()
414+
{
415+
$container = $this->buildContainer([
416+
'ai' => [
417+
'agent' => [
418+
'agent_with_default_toolbox' => [
419+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
420+
'tools' => true,
421+
],
422+
],
423+
],
424+
]);
425+
426+
$agentId = 'ai.agent.agent_with_default_toolbox';
427+
428+
// When using default toolbox, the ai.tool.agent_processor service gets the tags
429+
$defaultToolProcessor = $container->getDefinition('ai.tool.agent_processor');
430+
$inputTags = $defaultToolProcessor->getTag('ai.agent.input_processor');
431+
$outputTags = $defaultToolProcessor->getTag('ai.agent.output_processor');
432+
433+
// Find tags for our specific agent
434+
$foundInput = false;
435+
$foundOutput = false;
436+
437+
foreach ($inputTags as $tag) {
438+
if (($tag['agent'] ?? '') === $agentId) {
439+
$foundInput = true;
440+
break;
441+
}
442+
}
443+
444+
foreach ($outputTags as $tag) {
445+
if (($tag['agent'] ?? '') === $agentId) {
446+
$foundOutput = true;
447+
break;
448+
}
449+
}
450+
451+
$this->assertTrue($foundInput, 'Default tool processor should have input tag with full agent ID');
452+
$this->assertTrue($foundOutput, 'Default tool processor should have output tag with full agent ID');
453+
}
454+
455+
#[TestDox('Token usage processor tags use the correct agent ID')]
456+
public function testTokenUsageProcessorTags()
457+
{
458+
$container = $this->buildContainer([
459+
'ai' => [
460+
'platform' => [
461+
'openai' => [
462+
'api_key' => 'test_key',
463+
],
464+
],
465+
'agent' => [
466+
'tracked_agent' => [
467+
'platform' => 'ai.platform.openai',
468+
'model' => ['class' => 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'],
469+
'track_token_usage' => true,
470+
],
471+
],
472+
],
473+
]);
474+
475+
$agentId = 'ai.agent.tracked_agent';
476+
477+
// Token usage processor must exist for OpenAI platform
478+
$tokenUsageProcessor = $container->getDefinition('ai.platform.token_usage_processor.openai');
479+
$outputTags = $tokenUsageProcessor->getTag('ai.agent.output_processor');
480+
481+
$foundTag = false;
482+
foreach ($outputTags as $tag) {
483+
if (($tag['agent'] ?? '') === $agentId) {
484+
$foundTag = true;
485+
break;
486+
}
487+
}
488+
489+
$this->assertTrue($foundTag, 'Token usage processor should have output tag with full agent ID');
490+
}
491+
306492
private function buildContainer(array $configuration): ContainerBuilder
307493
{
308494
$container = new ContainerBuilder();

0 commit comments

Comments
 (0)