-
-
Notifications
You must be signed in to change notification settings - Fork 140
[AI Bundle][Platform] Integrate template rendering into Message API #1017
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
[AI Bundle][Platform] Integrate template rendering into Message API #1017
Conversation
Introduces a new prompt-template component providing simple yet extensible
prompt templating with pluggable rendering strategies.
Features:
- Zero core dependencies (only PHP 8.2+)
- StringRenderer for simple {variable} replacement (default)
- ExpressionRenderer for advanced expressions (optional, requires symfony/expression-language)
- Factory pattern for exceptions with typed error methods
- Comprehensive test coverage (42 tests, 52 assertions)
- PHPStan level 6 compliant
- Follows Symfony coding standards
Architecture:
- Strategy pattern for extensible rendering
- Immutable readonly classes throughout
- Interface-first design (PromptTemplateInterface, RendererInterface)
- Component-specific exception hierarchy
The component allows users to create prompt templates with variable substitution
using either simple string replacement or advanced expression evaluation, with
the ability to implement custom renderers for specific use cases.
c290706 to
00b4af3
Compare
|
Thanks for this @wachterjohannes - would really love to get this in 🙏 two things that we need to sort here:
we could have something like: $template = new Symfony\AI\Platform\Message\Template::expression('Total: {price * quantity}');
$message = Message::forSystem($template);
$messages = new MessageBag($message);
$result = $platform->invoke('gpt-4o-mini', $messages, [
'template_vars' => ['price' => 10, 'quantity' => 5],
]);and later - while serializing - take care of the rendering - potentially throw an error on unsupported types. with type being only a string instead of a renderer instance + named construct for promoted use: class Template
{
public function __construct(
private string $template,
private string $type,
) { }
public static function string(string $template): self;
public static function expression(string $template): self;
public static function twig(string $template): self
{
return new self($template, 'twig');
}
}WDYT? |
|
@chr-hertel in my opinion an own component makes sense as it is totally independent to the other component and as you mentioned we could integrate it into the agent and platform. @OskarStark what do you think. Regarding the renaming - makes totally sense and i will do that when the decission over the component is final :) |
|
My thinking always is: API first, then implementation, then slicing. That's why i would delay that component discussion. Templates alone are not valuable, they need an integration into the Message I'd say |
|
@chr-hertel both are absolutly thru statements :) so let us wait for feedback from @OskarStark |
|
Lets go with the platform component first 👍 |
8216936 to
d7911d1
Compare
Merge PromptTemplate component into Platform by integrating template rendering directly into the Message API. This provides a more cohesive developer experience where templates are rendered automatically through event listeners rather than requiring a separate component. Key changes: - Add Template class for message content with variable placeholders - Implement extensible TemplateRenderer system (string and expression) - Add TemplateRendererListener for automatic rendering via events - Register renderers and listener in AI Bundle service configuration - Remove standalone PromptTemplate component - Add comprehensive test coverage for all new functionality - Add example demonstrating template usage with different renderers The template rendering system supports both simple string replacement and complex expression evaluation, with an extensible architecture allowing custom renderers to be registered.
d7911d1 to
f67c952
Compare
|
@OskarStark that was luck 😂 pushed a few minutes ago! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it needed for an assistant message, which comes from the LLM? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are totally right :) will remove that from the AssistantMessage
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still in
src/platform/src/Message/TemplateRenderer/ExpressionTemplateRenderer.php
Outdated
Show resolved
Hide resolved
6200479 to
3d1112e
Compare
chr-hertel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome - you pretty much nailed it - only small stuff & fabbot - thanks already!
src/platform/src/Message/TemplateRenderer/ChainTemplateRenderer.php
Outdated
Show resolved
Hide resolved
|
@chr-hertel fixed your comments |
| if (class_exists(ExpressionLanguage::class)) { | ||
| $this->assertTrue($container->hasDefinition('ai.platform.template_renderer.expression')); | ||
| $expressionRendererDefinition = $container->getDefinition('ai.platform.template_renderer.expression'); | ||
| $this->assertSame(ExpressionLanguageTemplateRenderer::class, $expressionRendererDefinition->getClass()); | ||
| $this->assertTrue($expressionRendererDefinition->hasTag('ai.platform.template_renderer')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how would we ever end up here - it's not installed in the bundle, right? should we do it with req --dev?
| * @param ?ToolCall[] $toolCalls | ||
| */ | ||
| public static function ofAssistant(?string $content = null, ?array $toolCalls = null): AssistantMessage | ||
| public static function ofAssistant(string|Template|null $content = null, ?array $toolCalls = null): AssistantMessage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also not needed?
| public static function ofAssistant(string|Template|null $content = null, ?array $toolCalls = null): AssistantMessage | |
| public static function ofAssistant(?string $content = null, ?array $toolCalls = null): AssistantMessage |
| /** | ||
| * @author Johannes Wachter <johannes@sulu.io> | ||
| */ | ||
| final readonly class ExpressionLanguageTemplateRenderer implements TemplateRendererInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missed that before
| final readonly class ExpressionLanguageTemplateRenderer implements TemplateRendererInterface | |
| final class ExpressionLanguageTemplateRenderer implements TemplateRendererInterface |
| * | ||
| * @author Johannes Wachter <johannes@sulu.io> | ||
| */ | ||
| final readonly class StringTemplateRenderer implements TemplateRendererInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| final readonly class StringTemplateRenderer implements TemplateRendererInterface | |
| final class StringTemplateRenderer implements TemplateRendererInterface |
| $this->model = new Model('gpt-4o'); | ||
| } | ||
|
|
||
| public function testRendersTemplateWhenTemplateVarsProvided(): void |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all those : void return type declarations on test* methods have to go, according to Symfony code style
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please rename the folder to template and split into 4 different files - locally I'm executing all examples from time to time in parallel - and it will go faster like that
Summary
Adds a new prompt-template component to the Symfony AI monorepo, filling a gap we identified in our (Christopher Hertel and me) recent discussion about missing core functionality for prompt management.
Background
This implementation merges and adapts code from both sulu.ai and modelflow-ai projects, bringing battle-tested prompt templating into the Symfony AI ecosystem with a more extensible architecture.
Architecture
The component uses a strategy pattern for rendering, allowing different template processors to be plugged in:
{variable}replacement with zero dependenciesKey Features
Usage Examples
Simple variable replacement:
This provides a solid foundation for prompt template management across the Symfony AI ecosystem while maintaining the flexibility to adapt to different rendering needs.