Skip to content

Commit 5e312e7

Browse files
committed
feature #595 [AI Bundle] Simplify memory configuration structure (OskarStark)
This PR was squashed before being merged into the main branch. Discussion ---------- [AI Bundle] Simplify memory configuration structure | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | Docs? | yes | Issues | Fix #591 | License | MIT Remove guessing logic for memory keys and introduce explicit syntax: ```yaml memory: 'text' # always creates static memory processor ``` ```yaml memory: service: 'service_name' # uses service reference ``` This makes the configuration more predictable and eliminates the need to check if a string refers to an existing service. Commits ------- 9530c67 [AI Bundle] Simplify memory configuration structure
2 parents 53823ec + 9530c67 commit 5e312e7

File tree

4 files changed

+103
-18
lines changed

4 files changed

+103
-18
lines changed

src/ai-bundle/config/options.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,13 +138,27 @@
138138
->end()
139139
->end()
140140
->booleanNode('structured_output')->defaultTrue()->end()
141-
->scalarNode('memory')
142-
->info('Plain string or service name of the memory provider implementing MemoryProviderInterface')
141+
->variableNode('memory')
142+
->info('Memory configuration: string for static memory, or array with "service" key for service reference')
143143
->defaultNull()
144144
->validate()
145-
->ifTrue(function ($v) { return \is_string($v) && '' === $v; })
145+
->ifTrue(function ($v) {
146+
return \is_string($v) && '' === $v;
147+
})
146148
->thenInvalid('Memory cannot be empty.')
147149
->end()
150+
->validate()
151+
->ifTrue(function ($v) {
152+
return \is_array($v) && !isset($v['service']);
153+
})
154+
->thenInvalid('Memory array configuration must contain a "service" key.')
155+
->end()
156+
->validate()
157+
->ifTrue(function ($v) {
158+
return \is_array($v) && isset($v['service']) && '' === $v['service'];
159+
})
160+
->thenInvalid('Memory service cannot be empty.')
161+
->end()
148162
->end()
149163
->arrayNode('prompt')
150164
->info('The system prompt configuration')

src/ai-bundle/doc/index.rst

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ Configuration
7171
model:
7272
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
7373
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
74-
memory: 'You have access to conversation history and user preferences' # Optional: static memory or service reference
74+
memory: 'You have access to conversation history and user preferences' # Optional: static memory content
7575
prompt: # The system prompt configuration
7676
text: 'You are a helpful assistant that can answer questions.' # The prompt text
7777
include_tools: true # Include tool definitions at the end of the system prompt
@@ -208,7 +208,8 @@ This static memory content is consistently available to the agent across all con
208208

209209
**Dynamic Memory (Advanced)**
210210

211-
For more sophisticated scenarios, you can reference an existing service that implements dynamic memory:
211+
For more sophisticated scenarios, you can reference an existing service that implements dynamic memory.
212+
Use the array syntax with a ``service`` key to explicitly reference a service:
212213

213214
.. code-block:: yaml
214215
@@ -218,7 +219,8 @@ For more sophisticated scenarios, you can reference an existing service that imp
218219
model:
219220
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
220221
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
221-
memory: 'my_memory_service' # References an existing service
222+
memory:
223+
service: 'my_memory_service' # Explicitly references an existing service
222224
prompt:
223225
text: 'You are a helpful assistant.'
224226
@@ -269,17 +271,17 @@ When using a service reference, the memory service must implement the ``Symfony\
269271

270272
**How Memory Works**
271273

272-
The system automatically detects whether to use static or dynamic memory:
274+
The system uses explicit configuration to determine memory behavior:
273275

274276
**Static Memory Processing:**
275-
1. When you provide a string that doesn't match any service name
277+
1. When you provide a string value (e.g., ``memory: 'some text'``)
276278
2. The system creates a ``StaticMemoryProvider`` automatically
277279
3. Content is formatted as "## Static Memory" with the provided text
278280
4. This memory is consistently available across all conversations
279281

280282
**Dynamic Memory Processing:**
281-
1. When the string matches an existing service in the container
282-
2. The ``MemoryInputProcessor`` uses that service directly
283+
1. When you provide an array with a service key (e.g., ``memory: {service: 'my_service'}``)
284+
2. The ``MemoryInputProcessor`` uses the specified service directly
283285
3. The service's ``loadMemory()`` method is called before processing user input
284286
4. Dynamic memory content is injected based on the current context
285287

src/ai-bundle/src/AiBundle.php

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -620,12 +620,11 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
620620
if (isset($config['memory'])) {
621621
$memoryValue = $config['memory'];
622622

623-
// Check if the value refers to an existing service
624-
if ($container->hasDefinition($memoryValue) || $container->hasAlias($memoryValue)) {
625-
// Use existing service as memory provider
626-
$memoryProviderReference = new Reference($memoryValue);
623+
if (\is_array($memoryValue) && isset($memoryValue['service'])) {
624+
// Array configuration with service key - use the service directly
625+
$memoryProviderReference = new Reference($memoryValue['service']);
627626
} else {
628-
// Create StaticMemoryProvider with the string as static content
627+
// String configuration - always create StaticMemoryProvider
629628
$staticMemoryProviderDefinition = (new Definition(StaticMemoryProvider::class))
630629
->setArguments([$memoryValue]);
631630

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

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,6 +1098,75 @@ public function testEmptyStringMemoryConfigurationThrowsException()
10981098
]);
10991099
}
11001100

1101+
#[TestDox('Memory array configuration without service key throws validation exception')]
1102+
public function testMemoryArrayConfigurationWithoutServiceKeyThrowsException()
1103+
{
1104+
$this->expectException(InvalidConfigurationException::class);
1105+
$this->expectExceptionMessage('Memory array configuration must contain a "service" key.');
1106+
1107+
$this->buildContainer([
1108+
'ai' => [
1109+
'agent' => [
1110+
'test_agent' => [
1111+
'model' => ['class' => Gpt::class],
1112+
'memory' => ['invalid' => 'value'],
1113+
'prompt' => [
1114+
'text' => 'Test prompt',
1115+
],
1116+
],
1117+
],
1118+
],
1119+
]);
1120+
}
1121+
1122+
#[TestDox('Memory array configuration with empty service throws validation exception')]
1123+
public function testMemoryArrayConfigurationWithEmptyServiceThrowsException()
1124+
{
1125+
$this->expectException(InvalidConfigurationException::class);
1126+
$this->expectExceptionMessage('Memory service cannot be empty.');
1127+
1128+
$this->buildContainer([
1129+
'ai' => [
1130+
'agent' => [
1131+
'test_agent' => [
1132+
'model' => ['class' => Gpt::class],
1133+
'memory' => ['service' => ''],
1134+
'prompt' => [
1135+
'text' => 'Test prompt',
1136+
],
1137+
],
1138+
],
1139+
],
1140+
]);
1141+
}
1142+
1143+
#[TestDox('Memory service configuration works correctly')]
1144+
public function testMemoryServiceConfigurationWorksCorrectly()
1145+
{
1146+
$container = $this->buildContainer([
1147+
'ai' => [
1148+
'agent' => [
1149+
'test_agent' => [
1150+
'model' => ['class' => Gpt::class],
1151+
'memory' => ['service' => 'my_custom_memory_service'],
1152+
'prompt' => [
1153+
'text' => 'Test prompt',
1154+
],
1155+
],
1156+
],
1157+
],
1158+
]);
1159+
1160+
// Should use the service directly, not create a StaticMemoryProvider
1161+
$this->assertTrue($container->hasDefinition('ai.agent.test_agent.memory_input_processor'));
1162+
$this->assertFalse($container->hasDefinition('ai.agent.test_agent.static_memory_provider'));
1163+
1164+
$memoryProcessor = $container->getDefinition('ai.agent.test_agent.memory_input_processor');
1165+
$arguments = $memoryProcessor->getArguments();
1166+
$this->assertInstanceOf(Reference::class, $arguments[0]);
1167+
$this->assertSame('my_custom_memory_service', (string) $arguments[0]);
1168+
}
1169+
11011170
#[TestDox('Memory configuration preserves correct processor priority ordering')]
11021171
public function testMemoryProcessorPriorityOrdering()
11031172
{
@@ -1182,7 +1251,7 @@ public function testMemoryWithExistingServiceUsesServiceReference()
11821251
'agent' => [
11831252
'test_agent' => [
11841253
'model' => ['class' => Gpt::class],
1185-
'memory' => 'existing_memory_service', // This service exists
1254+
'memory' => ['service' => 'existing_memory_service'], // New array syntax for service
11861255
'prompt' => [
11871256
'text' => 'You are a helpful assistant.',
11881257
],
@@ -1254,7 +1323,7 @@ public function testMemoryWithServiceAliasUsesAlias()
12541323
'agent' => [
12551324
'test_agent' => [
12561325
'model' => ['class' => Gpt::class],
1257-
'memory' => 'memory_alias', // This alias exists
1326+
'memory' => ['service' => 'memory_alias'], // Use new array syntax for service alias
12581327
'prompt' => [
12591328
'text' => 'You are a helpful assistant.',
12601329
],
@@ -1290,7 +1359,7 @@ public function testDifferentAgentsCanUseDifferentMemoryTypes()
12901359
'agent' => [
12911360
'agent_with_service' => [
12921361
'model' => ['class' => Gpt::class],
1293-
'memory' => 'dynamic_memory_service', // Existing service
1362+
'memory' => ['service' => 'dynamic_memory_service'], // Use new array syntax for service
12941363
'prompt' => [
12951364
'text' => 'Agent with service.',
12961365
],
@@ -1312,6 +1381,7 @@ public function testDifferentAgentsCanUseDifferentMemoryTypes()
13121381

13131382
$serviceMemoryProcessor = $container->getDefinition('ai.agent.agent_with_service.memory_input_processor');
13141383
$serviceArgs = $serviceMemoryProcessor->getArguments();
1384+
$this->assertInstanceOf(Reference::class, $serviceArgs[0]);
13151385
$this->assertSame('dynamic_memory_service', (string) $serviceArgs[0]);
13161386

13171387
// Second agent uses StaticMemoryProvider

0 commit comments

Comments
 (0)