Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/agent/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"symfony/dom-crawler": "^7.3|^8.0",
"symfony/event-dispatcher": "^7.3|^8.0",
"symfony/http-foundation": "^7.3|^8.0",
"symfony/translation": "^7.3|^8.0",
"symfony/translation-contracts": "^3.6"
},
"autoload": {
Expand Down
17 changes: 8 additions & 9 deletions src/agent/src/InputProcessor/SystemPromptInputProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Tool\Tool;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

/**
Expand All @@ -27,19 +28,17 @@
final readonly class SystemPromptInputProcessor implements InputProcessorInterface
{
/**
* @param \Stringable|string $systemPrompt the system prompt to prepend to the input messages
* @param ToolboxInterface|null $toolbox the tool box to be used to append the tool definitions to the system prompt
* @param \Stringable|TranslatableInterface|string $systemPrompt the system prompt to prepend to the input messages
* @param ToolboxInterface|null $toolbox the tool box to be used to append the tool definitions to the system prompt
*/
public function __construct(
private \Stringable|string $systemPrompt,
private \Stringable|TranslatableInterface|string $systemPrompt,
private ?ToolboxInterface $toolbox = null,
private ?TranslatorInterface $translator = null,
private bool $enableTranslation = false,
private ?string $translationDomain = null,
private LoggerInterface $logger = new NullLogger(),
) {
if ($this->enableTranslation && !$this->translator) {
throw new RuntimeException('Prompt translation is enabled but no translator was provided.');
if ($this->systemPrompt instanceof TranslatableInterface && !$this->translator) {
throw new RuntimeException('Translatable system prompt is not supported when no translator is provided.');
}
}

Expand All @@ -53,8 +52,8 @@ public function processInput(Input $input): void
return;
}

$message = $this->enableTranslation
? $this->translator->trans((string) $this->systemPrompt, [], $this->translationDomain)
$message = $this->systemPrompt instanceof TranslatableInterface
? $this->systemPrompt->trans($this->translator)
: (string) $this->systemPrompt;

if ($this->toolbox instanceof ToolboxInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
use Symfony\AI\Platform\Result\ToolCall;
use Symfony\AI\Platform\Tool\ExecutionReference;
use Symfony\AI\Platform\Tool\Tool;
use Symfony\Component\Translation\TranslatableMessage;
use Symfony\Contracts\Translation\TranslatorInterface;

#[CoversClass(SystemPromptInputProcessor::class)]
Expand Down Expand Up @@ -106,7 +107,7 @@ public function execute(ToolCall $toolCall): mixed
public function testIncludeToolDefinitions()
{
$processor = new SystemPromptInputProcessor(
'This is a',
new TranslatableMessage('This is a'),
new class implements ToolboxInterface {
public function getTools(): array
{
Expand All @@ -130,7 +131,6 @@ public function execute(ToolCall $toolCall): mixed
}
},
$this->getTranslator(),
true,
);

$input = new Input(new Gpt(), new MessageBag(Message::ofUser('This is a user message')));
Expand Down Expand Up @@ -196,7 +196,7 @@ public function execute(ToolCall $toolCall): mixed

public function testWithTranslatedSystemPrompt()
{
$processor = new SystemPromptInputProcessor('This is a', null, $this->getTranslator(), true);
$processor = new SystemPromptInputProcessor(new TranslatableMessage('This is a'), null, $this->getTranslator());

$input = new Input(new Gpt(), new MessageBag(Message::ofUser('This is a user message')), []);
$processor->processInput($input);
Expand All @@ -211,11 +211,9 @@ public function testWithTranslatedSystemPrompt()
public function testWithTranslationDomainSystemPrompt()
{
$processor = new SystemPromptInputProcessor(
'This is a',
new TranslatableMessage('This is a', domain: 'prompts'),
null,
$this->getTranslator(),
true,
'prompts'
);

$input = new Input(new Gpt(), new MessageBag(), []);
Expand All @@ -229,13 +227,12 @@ public function testWithTranslationDomainSystemPrompt()

public function testWithMissingTranslator()
{
$this->expectExceptionMessage('Prompt translation is enabled but no translator was provided');
$this->expectExceptionMessage('Translatable system prompt is not supported when no translator is provided.');

new SystemPromptInputProcessor(
'This is a',
new TranslatableMessage('This is a'),
null,
null,
true,
);
}

Expand Down
3 changes: 2 additions & 1 deletion src/ai-bundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^11.5",
"symfony/expression-language": "^7.3|^8.0",
"symfony/security-core": "^7.3|^8.0"
"symfony/security-core": "^7.3|^8.0",
"symfony/translation": "^7.3|^8.0"
},
"autoload": {
"psr-4": {
Expand Down
15 changes: 12 additions & 3 deletions src/ai-bundle/src/AiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Translation\TranslatableMessage;
use Symfony\Contracts\HttpClient\HttpClientInterface;

use function Symfony\Component\String\u;
Expand Down Expand Up @@ -623,13 +624,21 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
if (isset($config['prompt'])) {
$includeTools = isset($config['prompt']['include_tools']) && $config['prompt']['include_tools'];

if ($config['prompt']['enable_translation']) {
if (!class_exists(TranslatableMessage::class)) {
throw new RuntimeException('For using prompt translataion, symfony/translation package is required. Try running "composer require symfony/translation".');
}

$prompt = new TranslatableMessage($config['prompt']['text'], domain: $config['prompt']['translation_domain']);
} else {
$prompt = $config['prompt']['text'];
}

$systemPromptInputProcessorDefinition = (new Definition(SystemPromptInputProcessor::class))
->setArguments([
$config['prompt']['text'],
$prompt,
$includeTools ? new Reference('ai.toolbox.'.$name) : null,
new Reference('translator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
$config['prompt']['enable_translation'],
$config['prompt']['translation_domain'],
new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
])
->addTag('ai.agent.input_processor', ['agent' => $agentId, 'priority' => -30]);
Expand Down
5 changes: 2 additions & 3 deletions src/ai-bundle/tests/DependencyInjection/AiBundleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\Translation\TranslatableMessage;

#[CoversClass(AiBundle::class)]
#[UsesClass(ContainerBuilder::class)]
Expand Down Expand Up @@ -682,10 +683,8 @@ public function testSystemPromptWithArrayStructure()
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
$arguments = $definition->getArguments();

$this->assertSame('You are a helpful assistant.', $arguments[0]);
$this->assertEquals(new TranslatableMessage('You are a helpful assistant.', domain: 'prompts'), $arguments[0]);
$this->assertNull($arguments[1]); // include_tools is false, so null reference
$this->assertTrue($arguments[3]);
$this->assertSame('prompts', $arguments[4]);
}

#[TestDox('System prompt with include_tools enabled works correctly')]
Expand Down
Loading