Skip to content

Commit 00b4af3

Browse files
[PromptTemplate] Add new component with extensible renderer architecture
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.
1 parent 581d29b commit 00b4af3

21 files changed

+1291
-0
lines changed

src/prompt-template/.gitattributes

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/.gitattributes export-ignore
2+
/.gitignore export-ignore
3+
/phpstan.dist.neon export-ignore
4+
/phpunit.xml.dist export-ignore
5+
/tests export-ignore
6+
/AGENTS.md export-ignore
7+
/CLAUDE.md export-ignore

src/prompt-template/AGENTS.md

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# AGENTS.md
2+
3+
AI agent guidance for the Prompt Template component.
4+
5+
## Component Overview
6+
7+
Extensible prompt templating system with pluggable rendering strategies. Provides simple variable substitution by default and optional Symfony Expression Language integration for advanced use cases.
8+
9+
## Architecture
10+
11+
### Core Classes
12+
- **PromptTemplate**: Main implementation with renderer injection
13+
- **PromptTemplateInterface**: Core contract for template implementations
14+
- **RendererInterface**: Strategy interface for rendering implementations
15+
- **StringRenderer**: Default renderer using simple {variable} replacement (zero dependencies)
16+
- **ExpressionRenderer**: Optional renderer using Symfony Expression Language for advanced expressions
17+
18+
### Key Features
19+
- **Zero Core Dependencies**: Only requires PHP 8.2+
20+
- **Strategy Pattern**: Pluggable renderer system for extensibility
21+
- **Simple by Default**: Basic {variable} replacement out of the box
22+
- **Advanced Optional**: Expression Language for power users
23+
- **Immutable**: readonly classes ensure thread-safety
24+
25+
### Key Directories
26+
- `src/`: Source code with main classes
27+
- `src/Renderer/`: Renderer implementations
28+
- `src/Exception/`: Component-specific exceptions (factory pattern)
29+
- `tests/`: Comprehensive test suite mirroring src structure
30+
- `tests/Renderer/`: Tests for each renderer
31+
32+
## Essential Commands
33+
34+
### Testing
35+
```bash
36+
vendor/bin/phpunit
37+
vendor/bin/phpunit tests/PromptTemplateTest.php
38+
vendor/bin/phpunit --coverage-html coverage
39+
```
40+
41+
### Code Quality
42+
```bash
43+
vendor/bin/phpstan analyse
44+
cd ../../.. && vendor/bin/php-cs-fixer fix src/prompt-template/
45+
```
46+
47+
### Dependencies
48+
```bash
49+
composer install
50+
composer require symfony/expression-language # For ExpressionRenderer
51+
```
52+
53+
## Usage Patterns
54+
55+
### Default Renderer
56+
```php
57+
$template = new PromptTemplate('Hello {name}!');
58+
echo $template->format(['name' => 'World']);
59+
```
60+
61+
### Expression Renderer
62+
```php
63+
$renderer = new ExpressionRenderer();
64+
$template = new PromptTemplate('Total: {price * quantity}', $renderer);
65+
echo $template->format(['price' => 10, 'quantity' => 5]);
66+
```
67+
68+
### Custom Renderer
69+
```php
70+
class MyRenderer implements RendererInterface {
71+
public function render(string $template, array $values): string {
72+
// Custom implementation
73+
}
74+
}
75+
76+
$template = new PromptTemplate($template, new MyRenderer());
77+
```
78+
79+
## Testing Patterns
80+
81+
- PHPUnit 11.5+ with strict configuration
82+
- Test fixtures follow monorepo patterns
83+
- Each renderer has dedicated test class
84+
- Integration tests verify renderer injection
85+
- Component-specific exception testing using factory pattern
86+
- Prefer `self::assert*` over `$this->assert*`
87+
88+
## Development Notes
89+
90+
- Add `@author` tags to new classes
91+
- Use component-specific exceptions from `src/Exception/` with factory pattern
92+
- Follow Symfony coding standards with `@Symfony` PHP CS Fixer rules
93+
- Component is experimental (BC breaks possible)
94+
- StringRenderer must remain dependency-free
95+
- ExpressionRenderer requires symfony/expression-language
96+
- All classes use readonly for immutability

src/prompt-template/CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# CHANGELOG
2+
3+
## 0.1.0
4+
5+
**Initial release**
6+
7+
- Introduced `PromptTemplate` for prompt template management
8+
- Added `StringRenderer` for simple {variable} replacement (zero dependencies)
9+
- Added `ExpressionRenderer` for Symfony Expression Language integration
10+
- Implemented extensible renderer strategy pattern via `RendererInterface`
11+
- Added comprehensive exception hierarchy
12+
- Provides static factory methods for convenient template creation

src/prompt-template/CLAUDE.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this component.
4+
5+
## Component Overview
6+
7+
This is the Prompt Template component of the Symfony AI monorepo - an extensible prompt templating system with pluggable rendering strategies. It provides simple variable substitution by default and optional Symfony Expression Language integration for advanced use cases.
8+
9+
## Architecture
10+
11+
### Core Classes
12+
13+
- **PromptTemplate**: Main implementation with renderer injection
14+
- **PromptTemplateInterface**: Core contract for template implementations
15+
- **RendererInterface**: Strategy interface for rendering implementations
16+
- **StringRenderer**: Default renderer using simple {variable} replacement (zero dependencies)
17+
- **ExpressionRenderer**: Optional renderer using Symfony Expression Language for advanced expressions
18+
19+
### Key Features
20+
21+
- **Zero Core Dependencies**: Only requires PHP 8.2+
22+
- **Strategy Pattern**: Pluggable renderer system for extensibility
23+
- **Simple by Default**: Basic {variable} replacement out of the box
24+
- **Advanced Optional**: Expression Language for power users
25+
- **Immutable**: readonly classes ensure thread-safety
26+
27+
### Key Directories
28+
29+
- `src/`: Source code with main classes
30+
- `src/Renderer/`: Renderer implementations
31+
- `src/Exception/`: Component-specific exceptions
32+
- `tests/`: Comprehensive test suite mirroring src structure
33+
- `tests/Renderer/`: Tests for each renderer
34+
35+
## Development Commands
36+
37+
### Testing
38+
39+
```bash
40+
# Run all tests
41+
vendor/bin/phpunit
42+
43+
# Run specific test
44+
vendor/bin/phpunit tests/PromptTemplateTest.php
45+
46+
# Run with coverage
47+
vendor/bin/phpunit --coverage-html coverage
48+
```
49+
50+
### Code Quality
51+
52+
```bash
53+
# Run PHPStan static analysis
54+
vendor/bin/phpstan analyse
55+
56+
# Fix code style (run from project root)
57+
cd ../../.. && vendor/bin/php-cs-fixer fix src/prompt-template/
58+
```
59+
60+
### Installing Dependencies
61+
62+
```bash
63+
# Install dependencies
64+
composer install
65+
66+
# Install with expression language
67+
composer require symfony/expression-language
68+
```
69+
70+
## Testing Architecture
71+
72+
- Uses PHPUnit 11.5+ with strict configuration
73+
- Test fixtures follow monorepo patterns
74+
- Each renderer has dedicated test class
75+
- Integration tests verify renderer injection
76+
- Component-specific exception testing
77+
- Prefer `self::assert*` over `$this->assert*`
78+
79+
## Usage Patterns
80+
81+
### Default Renderer
82+
83+
```php
84+
$template = new PromptTemplate('Hello {name}!');
85+
echo $template->format(['name' => 'World']);
86+
```
87+
88+
### Expression Renderer
89+
90+
```php
91+
$renderer = new ExpressionRenderer();
92+
$template = new PromptTemplate('Total: {price * quantity}', $renderer);
93+
echo $template->format(['price' => 10, 'quantity' => 5]);
94+
```
95+
96+
### Custom Renderer
97+
98+
```php
99+
class MyRenderer implements RendererInterface {
100+
public function render(string $template, array $values): string {
101+
// Custom implementation
102+
}
103+
}
104+
105+
$template = new PromptTemplate($template, new MyRenderer());
106+
```
107+
108+
## Development Notes
109+
110+
- All new classes should have `@author` tags
111+
- Use component-specific exceptions from `src/Exception/`
112+
- Follow Symfony coding standards with `@Symfony` PHP CS Fixer rules
113+
- Component is marked as experimental (BC breaks possible)
114+
- StringRenderer must remain dependency-free
115+
- ExpressionRenderer requires symfony/expression-language
116+
- All classes use readonly for immutability

src/prompt-template/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2025-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/prompt-template/README.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# Symfony AI Prompt Template Component
2+
3+
**This Component is [experimental](https://github.com/symfony/ai?tab=readme-ov-file#component-status). Expect breaking changes in minor and patch versions.**
4+
5+
PHP library for prompt template management with extensible rendering strategies.
6+
7+
## Installation
8+
9+
```bash
10+
composer require symfony/ai-prompt-template
11+
```
12+
13+
For advanced expression-based rendering:
14+
15+
```bash
16+
composer require symfony/expression-language
17+
```
18+
19+
## Basic Usage
20+
21+
### Simple Variable Replacement (Default)
22+
23+
```php
24+
use Symfony\AI\PromptTemplate\PromptTemplate;
25+
26+
$template = new PromptTemplate('Hello {name}!');
27+
echo $template->format(['name' => 'World']); // "Hello World!"
28+
29+
// Using static factory
30+
$template = PromptTemplate::fromString(<<<'PROMPT'
31+
You are a helpful assistant.
32+
33+
User: {user}
34+
Query: {query}
35+
PROMPT);
36+
37+
echo $template->format([
38+
'user' => 'Alice',
39+
'query' => 'What is AI?',
40+
]);
41+
```
42+
43+
The default `StringRenderer` performs simple `{variable}` replacement with zero external dependencies.
44+
45+
### Advanced Expression-Based Rendering
46+
47+
Install `symfony/expression-language` to use the `ExpressionRenderer`:
48+
49+
```php
50+
use Symfony\AI\PromptTemplate\PromptTemplate;
51+
use Symfony\AI\PromptTemplate\Renderer\ExpressionRenderer;
52+
53+
$renderer = new ExpressionRenderer();
54+
$template = new PromptTemplate('Total: {price * quantity}', $renderer);
55+
echo $template->format(['price' => 10, 'quantity' => 5]); // "Total: 50"
56+
57+
// Conditional expressions
58+
$template = PromptTemplate::fromStringWithRenderer(
59+
'Status: {age >= 18 ? "adult" : "minor"}',
60+
new ExpressionRenderer()
61+
);
62+
echo $template->format(['age' => 25]); // "Status: adult"
63+
64+
// Object property access
65+
$template = new PromptTemplate('Name: {user.name}', $renderer);
66+
echo $template->format(['user' => $userObject]); // "Name: Alice"
67+
```
68+
69+
### Custom Renderers
70+
71+
Implement `RendererInterface` to create custom rendering strategies:
72+
73+
```php
74+
use Symfony\AI\PromptTemplate\Renderer\RendererInterface;
75+
76+
class MustacheRenderer implements RendererInterface
77+
{
78+
public function render(string $template, array $values): string
79+
{
80+
foreach ($values as $key => $value) {
81+
$template = str_replace('{{'.$key.'}}', (string) $value, $template);
82+
}
83+
return $template;
84+
}
85+
}
86+
87+
$template = new PromptTemplate('Hello {{name}}!', new MustacheRenderer());
88+
echo $template->format(['name' => 'World']); // "Hello World!"
89+
```
90+
91+
## Available Renderers
92+
93+
### StringRenderer (Default)
94+
95+
- Simple `{variable}` placeholder replacement
96+
- Validates variable names and values
97+
- Zero external dependencies
98+
- Supports strings, numbers, and `\Stringable` objects
99+
100+
### ExpressionRenderer (Optional)
101+
102+
Requires `symfony/expression-language`
103+
104+
- Variable access: `{user.name}`
105+
- Math operations: `{price * quantity}`
106+
- Conditionals: `{age > 18 ? "adult" : "minor"}`
107+
- String concatenation: `{firstName ~ " " ~ lastName}`
108+
- Array access: `{items[0]}`
109+
- Custom functions via `ExpressionLanguage`
110+
111+
## API Reference
112+
113+
### PromptTemplate
114+
115+
```php
116+
// Constructor
117+
new PromptTemplate(string $template, ?RendererInterface $renderer = null)
118+
119+
// Methods
120+
$template->format(array $values = []): string
121+
$template->getTemplate(): string
122+
(string) $template // Returns template string
123+
124+
// Static factories
125+
PromptTemplate::fromString(string $template): self
126+
PromptTemplate::fromStringWithRenderer(string $template, RendererInterface $renderer): self
127+
```
128+
129+
### RendererInterface
130+
131+
```php
132+
interface RendererInterface
133+
{
134+
public function render(string $template, array $values): string;
135+
}
136+
```
137+
138+
## Resources
139+
140+
- [Main Repository](https://github.com/symfony/ai)
141+
- [Report Issues](https://github.com/symfony/ai/issues)
142+
- [Submit Pull Requests](https://github.com/symfony/ai/pulls)

0 commit comments

Comments
 (0)