Skip to content

Conversation

@wachterjohannes
Copy link

@wachterjohannes wachterjohannes commented Nov 28, 2025

Q A
Bug fix? no
New feature? yes
Docs? yes
Issues N/A
License MIT

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:

  • StringRenderer (default): Simple {variable} replacement with zero dependencies
  • ExpressionRenderer: Advanced expression evaluation using Symfony Expression Language (optional)
  • Extensible: Ready for additional renderers like Twig, Mustache, or custom implementations

Key Features

  • Zero core dependencies (only PHP 8.2+)
  • Factory pattern for exceptions with typed error methods
  • Immutable readonly classes throughout
  • Interface-first design for maximum flexibility
  • Comprehensive test coverage (42 tests, 52 assertions)
  • PHPStan level 6 compliant

Usage Examples

Simple variable replacement:

$template = PromptTemplate::fromString('Hello {name}!');
echo $template->format(['name' => 'World']); // "Hello World!"

Expression-based rendering:
$renderer = new ExpressionRenderer();
$template = new PromptTemplate('Total: {price * quantity}', $renderer);
echo $template->format(['price' => 10, 'quantity' => 5]); // "Total: 50"

Custom renderer:
class TwigRenderer implements RendererInterface {
    public function render(string $template, array $values): string {
        // Twig implementation
    }
}

This provides a solid foundation for prompt template management across the Symfony AI ecosystem while maintaining the flexibility to adapt to different rendering needs.

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.
@chr-hertel
Copy link
Member

chr-hertel commented Nov 29, 2025

Thanks for this @wachterjohannes - would really love to get this in 🙏

two things that we need to sort here:

  1. is it a standalone component? for that we need to sort how we would use and render the template with the Message & Platform
  2. terminology: we currently don't use the terminology "prompt", but "message" - i'd prefer to go with that here as well like MessageTemplate

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?

@wachterjohannes
Copy link
Author

@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 :)

@chr-hertel
Copy link
Member

chr-hertel commented Nov 29, 2025

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

@wachterjohannes
Copy link
Author

@chr-hertel both are absolutly thru statements :) so let us wait for feedback from @OskarStark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants