Skip to content

Commit 7a1e134

Browse files
committed
feature #635 [Agent] Allow translatable prompts in SystemPromptInputProcessor (VincentLanglet)
This PR was squashed before being merged into the main branch. Discussion ---------- [Agent] Allow translatable prompts in `SystemPromptInputProcessor` | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes/no | Docs? | no <!-- required for new features --> | Issues | Fix #... | License | MIT This is a follow up of symfony/ai#514 with a suggestion of using TranslatableMessage instance for prompts rather than the need in SystemPromptInputProcessor constructor of - A boolean enableTranslation - A domain Since we're passing a TranslatableMessage this also allow to use translation parameters. cc `@OskarStark` that was the suggestion in symfony/ai#514 (comment) Commits ------- 868581f4 [Agent] Allow translatable prompts in `SystemPromptInputProcessor`
2 parents 4fe3082 + 33f3170 commit 7a1e134

File tree

6 files changed

+31
-25
lines changed

6 files changed

+31
-25
lines changed

src/agent/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
"symfony/dom-crawler": "^7.3|^8.0",
4444
"symfony/event-dispatcher": "^7.3|^8.0",
4545
"symfony/http-foundation": "^7.3|^8.0",
46+
"symfony/translation": "^7.3|^8.0",
4647
"symfony/translation-contracts": "^3.6"
4748
},
4849
"autoload": {

src/agent/src/InputProcessor/SystemPromptInputProcessor.php

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\AI\Agent\Toolbox\ToolboxInterface;
2020
use Symfony\AI\Platform\Message\Message;
2121
use Symfony\AI\Platform\Tool\Tool;
22+
use Symfony\Contracts\Translation\TranslatableInterface;
2223
use Symfony\Contracts\Translation\TranslatorInterface;
2324

2425
/**
@@ -27,19 +28,17 @@
2728
final readonly class SystemPromptInputProcessor implements InputProcessorInterface
2829
{
2930
/**
30-
* @param \Stringable|string $systemPrompt the system prompt to prepend to the input messages
31-
* @param ToolboxInterface|null $toolbox the tool box to be used to append the tool definitions to the system prompt
31+
* @param \Stringable|TranslatableInterface|string $systemPrompt the system prompt to prepend to the input messages
32+
* @param ToolboxInterface|null $toolbox the tool box to be used to append the tool definitions to the system prompt
3233
*/
3334
public function __construct(
34-
private \Stringable|string $systemPrompt,
35+
private \Stringable|TranslatableInterface|string $systemPrompt,
3536
private ?ToolboxInterface $toolbox = null,
3637
private ?TranslatorInterface $translator = null,
37-
private bool $enableTranslation = false,
38-
private ?string $translationDomain = null,
3938
private LoggerInterface $logger = new NullLogger(),
4039
) {
41-
if ($this->enableTranslation && !$this->translator) {
42-
throw new RuntimeException('Prompt translation is enabled but no translator was provided.');
40+
if ($this->systemPrompt instanceof TranslatableInterface && !$this->translator) {
41+
throw new RuntimeException('Translatable system prompt is not supported when no translator is provided.');
4342
}
4443
}
4544

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

56-
$message = $this->enableTranslation
57-
? $this->translator->trans((string) $this->systemPrompt, [], $this->translationDomain)
55+
$message = $this->systemPrompt instanceof TranslatableInterface
56+
? $this->systemPrompt->trans($this->translator)
5857
: (string) $this->systemPrompt;
5958

6059
if ($this->toolbox instanceof ToolboxInterface

src/agent/tests/InputProcessor/SystemPromptInputProcessorTest.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
use Symfony\AI\Platform\Result\ToolCall;
2626
use Symfony\AI\Platform\Tool\ExecutionReference;
2727
use Symfony\AI\Platform\Tool\Tool;
28+
use Symfony\Component\Translation\TranslatableMessage;
2829
use Symfony\Contracts\Translation\TranslatorInterface;
2930

3031
final class SystemPromptInputProcessorTest extends TestCase
@@ -91,7 +92,7 @@ public function execute(ToolCall $toolCall): mixed
9192
public function testIncludeToolDefinitions()
9293
{
9394
$processor = new SystemPromptInputProcessor(
94-
'This is a',
95+
new TranslatableMessage('This is a'),
9596
new class implements ToolboxInterface {
9697
public function getTools(): array
9798
{
@@ -115,7 +116,6 @@ public function execute(ToolCall $toolCall): mixed
115116
}
116117
},
117118
$this->getTranslator(),
118-
true,
119119
);
120120

121121
$input = new Input(new Gpt(Gpt::GPT_4O), new MessageBag(Message::ofUser('This is a user message')));
@@ -181,7 +181,7 @@ public function execute(ToolCall $toolCall): mixed
181181

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

186186
$input = new Input(new Gpt(Gpt::GPT_4O), new MessageBag(Message::ofUser('This is a user message')), []);
187187
$processor->processInput($input);
@@ -196,11 +196,9 @@ public function testWithTranslatedSystemPrompt()
196196
public function testWithTranslationDomainSystemPrompt()
197197
{
198198
$processor = new SystemPromptInputProcessor(
199-
'This is a',
199+
new TranslatableMessage('This is a', domain: 'prompts'),
200200
null,
201201
$this->getTranslator(),
202-
true,
203-
'prompts'
204202
);
205203

206204
$input = new Input(new Gpt(Gpt::GPT_4O), new MessageBag(), []);
@@ -214,13 +212,12 @@ public function testWithTranslationDomainSystemPrompt()
214212

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

219217
new SystemPromptInputProcessor(
220-
'This is a',
218+
new TranslatableMessage('This is a'),
221219
null,
222220
null,
223-
true,
224221
);
225222
}
226223

src/ai-bundle/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@
3030
"phpstan/phpstan-strict-rules": "^2.0",
3131
"phpunit/phpunit": "^11.5",
3232
"symfony/expression-language": "^7.3|^8.0",
33-
"symfony/security-core": "^7.3|^8.0"
33+
"symfony/security-core": "^7.3|^8.0",
34+
"symfony/translation": "^7.3|^8.0"
3435
},
3536
"autoload": {
3637
"psr-4": {

src/ai-bundle/src/AiBundle.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
use Symfony\Component\HttpClient\HttpClient;
8686
use Symfony\Component\HttpKernel\Bundle\AbstractBundle;
8787
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
88+
use Symfony\Component\Translation\TranslatableMessage;
8889
use Symfony\Contracts\HttpClient\HttpClientInterface;
8990

9091
use function Symfony\Component\String\u;
@@ -649,13 +650,21 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
649650
if (isset($config['prompt'])) {
650651
$includeTools = isset($config['prompt']['include_tools']) && $config['prompt']['include_tools'];
651652

653+
if ($config['prompt']['enable_translation']) {
654+
if (!class_exists(TranslatableMessage::class)) {
655+
throw new RuntimeException('For using prompt translataion, symfony/translation package is required. Try running "composer require symfony/translation".');
656+
}
657+
658+
$prompt = new TranslatableMessage($config['prompt']['text'], domain: $config['prompt']['translation_domain']);
659+
} else {
660+
$prompt = $config['prompt']['text'];
661+
}
662+
652663
$systemPromptInputProcessorDefinition = (new Definition(SystemPromptInputProcessor::class))
653664
->setArguments([
654-
$config['prompt']['text'],
665+
$prompt,
655666
$includeTools ? new Reference('ai.toolbox.'.$name) : null,
656667
new Reference('translator', ContainerInterface::NULL_ON_INVALID_REFERENCE),
657-
$config['prompt']['enable_translation'],
658-
$config['prompt']['translation_domain'],
659668
new Reference('logger', ContainerInterface::IGNORE_ON_INVALID_REFERENCE),
660669
])
661670
->addTag('ai.agent.input_processor', ['agent' => $agentId, 'priority' => -30]);

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
use Symfony\Component\DependencyInjection\ContainerBuilder;
3333
use Symfony\Component\DependencyInjection\ContainerInterface;
3434
use Symfony\Component\DependencyInjection\Reference;
35+
use Symfony\Component\Translation\TranslatableMessage;
3536

3637
class AiBundleTest extends TestCase
3738
{
@@ -707,10 +708,8 @@ public function testSystemPromptWithArrayStructure()
707708
$definition = $container->getDefinition('ai.agent.test_agent.system_prompt_processor');
708709
$arguments = $definition->getArguments();
709710

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

716715
#[TestDox('System prompt with include_tools enabled works correctly')]

0 commit comments

Comments
 (0)