Skip to content

Commit 5d86763

Browse files
committed
Add support for query parameters in model configuration
- Enable two model configuration formats: 1. Query parameter style: model.name with ?key=value parameters 2. Separate options style: model.name and model.options array - Add validation to prevent using both formats simultaneously - Use parse_url() for parsing query parameters - Query parameter values remain as strings (no type conversion) - Update demo configuration and documentation - Add comprehensive test coverage for both formats
1 parent 68b6181 commit 5d86763

File tree

5 files changed

+216
-1
lines changed

5 files changed

+216
-1
lines changed

demo/config/packages/ai.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ ai:
4242
audio:
4343
model:
4444
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
45-
name: !php/const Symfony\AI\Platform\Bridge\OpenAi\Gpt::GPT_4O_MINI
45+
name: 'gpt-4o-mini?temperature=1.0'
4646
prompt: 'You are a friendly chatbot that likes to have a conversation with users and asks them some questions.'
4747
tools:
4848
# Agent in agent 🤯

src/ai-bundle/config/options.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@
195195
->variablePrototype()->end()
196196
->end()
197197
->end()
198+
->validate()
199+
->ifTrue(function ($v) {
200+
return isset($v['name']) && str_contains($v['name'], '?') && !empty($v['options']);
201+
})
202+
->thenInvalid('Cannot specify both query parameters in model name and options array. Use either "model.name" with query parameters (e.g., "gpt-4o-mini?temperature=0.5") or separate "model.name" and "model.options".')
203+
->end()
198204
->end()
199205
->booleanNode('structured_output')->defaultTrue()->end()
200206
->variableNode('memory')
@@ -536,6 +542,12 @@
536542
->variablePrototype()->end()
537543
->end()
538544
->end()
545+
->validate()
546+
->ifTrue(function ($v) {
547+
return isset($v['name']) && str_contains($v['name'], '?') && !empty($v['options']);
548+
})
549+
->thenInvalid('Cannot specify both query parameters in model name and options array. Use either "model.name" with query parameters (e.g., "gpt-4o-mini?temperature=0.5") or separate "model.name" and "model.options".')
550+
->end()
539551
->end()
540552
->end()
541553
->end()

src/ai-bundle/doc/index.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,50 @@ Configuration
145145
vectorizer: 'ai.vectorizer.mistral_embeddings'
146146
store: 'ai.store.memory.research'
147147
148+
Model Configuration
149+
-------------------
150+
151+
Models can be configured in two different ways to specify model options and parameters. You can append query parameters directly to the model name using a URL-like syntax:
152+
153+
.. code-block:: yaml
154+
155+
ai:
156+
agent:
157+
my_agent:
158+
model:
159+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
160+
name: 'gpt-4o-mini?temperature=0.7&max_tokens=2000&stream=true'
161+
162+
Alternatively, you can specify model options in a separate ``options`` section:
163+
164+
.. code-block:: yaml
165+
166+
ai:
167+
agent:
168+
my_agent:
169+
model:
170+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Gpt'
171+
name: 'gpt-4o-mini'
172+
options:
173+
temperature: 0.7
174+
max_tokens: 2000
175+
stream: true
176+
177+
.. note::
178+
179+
You cannot use both query parameters in the model name and the ``options`` key simultaneously.
180+
181+
You can also define models for the vectorizer this way:
182+
183+
.. code-block:: yaml
184+
185+
ai:
186+
vectorizer:
187+
embeddings:
188+
model:
189+
class: 'Symfony\AI\Platform\Bridge\OpenAi\Embeddings'
190+
name: 'text-embedding-3-small?dimensions=512&encoding_format=float'
191+
148192
HTTP Client Configuration
149193
-------------------------
150194

src/ai-bundle/src/AiBundle.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,16 @@ private function processAgentConfig(string $name, array $config, ContainerBuilde
503503
// MODEL
504504
['class' => $modelClass, 'name' => $modelName, 'options' => $options] = $config['model'];
505505

506+
// Parse query parameters from model name if present
507+
if (str_contains((string) $modelName, '?')) {
508+
$parsed = parse_url($modelName);
509+
$modelName = $parsed['path'] ?? '';
510+
511+
if (isset($parsed['query'])) {
512+
parse_str($parsed['query'], $options);
513+
}
514+
}
515+
506516
$modelDefinition = new Definition($modelClass);
507517
if (null !== $modelName) {
508518
$modelDefinition->setArgument(0, $modelName);
@@ -1104,6 +1114,16 @@ private function processVectorizerConfig(string $name, array $config, ContainerB
11041114
{
11051115
['class' => $modelClass, 'name' => $modelName, 'options' => $options] = $config['model'];
11061116

1117+
// Parse query parameters from model name if present
1118+
if (str_contains((string) $modelName, '?')) {
1119+
$parsed = parse_url($modelName);
1120+
$modelName = $parsed['path'] ?? '';
1121+
1122+
if (isset($parsed['query'])) {
1123+
parse_str($parsed['query'], $options);
1124+
}
1125+
}
1126+
11071127
$modelDefinition = (new Definition((string) $modelClass));
11081128
if (null !== $modelName) {
11091129
$modelDefinition->setArgument(0, $modelName);

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

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,145 @@ public function testDifferentAgentsCanUseDifferentMemoryTypes()
14011401
$this->assertSame('Static memory context for this agent', $staticProviderArgs[0]);
14021402
}
14031403

1404+
#[TestDox('Model configuration with query parameters in model name works correctly')]
1405+
public function testModelConfigurationWithQueryParameters()
1406+
{
1407+
$container = $this->buildContainer([
1408+
'ai' => [
1409+
'agent' => [
1410+
'test' => [
1411+
'model' => [
1412+
'class' => Gpt::class,
1413+
'name' => 'gpt-4o-mini?temperature=0.5&max_tokens=2000',
1414+
],
1415+
],
1416+
],
1417+
],
1418+
]);
1419+
1420+
$modelDefinition = $container->getDefinition('ai.agent.test.model');
1421+
$this->assertSame('gpt-4o-mini', $modelDefinition->getArgument(0));
1422+
$this->assertEquals(['temperature' => '0.5', 'max_tokens' => '2000'], $modelDefinition->getArgument(1));
1423+
}
1424+
1425+
#[TestDox('Model configuration with separate options array works correctly')]
1426+
public function testModelConfigurationWithSeparateOptions()
1427+
{
1428+
$container = $this->buildContainer([
1429+
'ai' => [
1430+
'agent' => [
1431+
'test' => [
1432+
'model' => [
1433+
'class' => Gpt::class,
1434+
'name' => 'gpt-4o-mini',
1435+
'options' => [
1436+
'temperature' => 0.7,
1437+
'max_tokens' => 1500,
1438+
],
1439+
],
1440+
],
1441+
],
1442+
],
1443+
]);
1444+
1445+
$modelDefinition = $container->getDefinition('ai.agent.test.model');
1446+
$this->assertSame('gpt-4o-mini', $modelDefinition->getArgument(0));
1447+
$this->assertEquals(['temperature' => 0.7, 'max_tokens' => 1500], $modelDefinition->getArgument(1));
1448+
}
1449+
1450+
#[TestDox('Model configuration with conflicting query parameters and options throws exception')]
1451+
public function testModelConfigurationConflictThrowsException()
1452+
{
1453+
$this->expectException(InvalidConfigurationException::class);
1454+
$this->expectExceptionMessage('Cannot specify both query parameters in model name and options array');
1455+
1456+
$this->buildContainer([
1457+
'ai' => [
1458+
'agent' => [
1459+
'test' => [
1460+
'model' => [
1461+
'class' => Gpt::class,
1462+
'name' => 'gpt-4o-mini?temperature=0.5',
1463+
'options' => [
1464+
'temperature' => 0.7,
1465+
],
1466+
],
1467+
],
1468+
],
1469+
],
1470+
]);
1471+
}
1472+
1473+
#[TestDox('Model configuration query parameters are passed as strings')]
1474+
public function testModelConfigurationTypeConversion()
1475+
{
1476+
$container = $this->buildContainer([
1477+
'ai' => [
1478+
'agent' => [
1479+
'test' => [
1480+
'model' => [
1481+
'class' => Gpt::class,
1482+
'name' => 'gpt-4o-mini?temperature=0.5&max_tokens=2000&stream=true&presence_penalty=0',
1483+
],
1484+
],
1485+
],
1486+
],
1487+
]);
1488+
1489+
$modelDefinition = $container->getDefinition('ai.agent.test.model');
1490+
$this->assertSame('gpt-4o-mini', $modelDefinition->getArgument(0));
1491+
1492+
$options = $modelDefinition->getArgument(1);
1493+
$this->assertSame('0.5', $options['temperature']); // string
1494+
$this->assertSame('2000', $options['max_tokens']); // string
1495+
$this->assertSame('true', $options['stream']); // string
1496+
$this->assertSame('0', $options['presence_penalty']); // string
1497+
}
1498+
1499+
#[TestDox('Vectorizer model configuration with query parameters works correctly')]
1500+
public function testVectorizerModelConfigurationWithQueryParameters()
1501+
{
1502+
$container = $this->buildContainer([
1503+
'ai' => [
1504+
'vectorizer' => [
1505+
'test' => [
1506+
'model' => [
1507+
'class' => Gpt::class,
1508+
'name' => 'text-embedding-3-small?dimensions=512',
1509+
],
1510+
],
1511+
],
1512+
],
1513+
]);
1514+
1515+
$modelDefinition = $container->getDefinition('ai.vectorizer.test.model');
1516+
$this->assertSame('text-embedding-3-small', $modelDefinition->getArgument(0));
1517+
$this->assertEquals(['dimensions' => '512'], $modelDefinition->getArgument(1));
1518+
}
1519+
1520+
#[TestDox('Vectorizer model configuration with conflicting parameters throws exception')]
1521+
public function testVectorizerModelConfigurationConflictThrowsException()
1522+
{
1523+
$this->expectException(InvalidConfigurationException::class);
1524+
$this->expectExceptionMessage('Cannot specify both query parameters in model name and options array');
1525+
1526+
$this->buildContainer([
1527+
'ai' => [
1528+
'vectorizer' => [
1529+
'test' => [
1530+
'model' => [
1531+
'class' => Gpt::class,
1532+
'name' => 'text-embedding-3-small?dimensions=512',
1533+
'options' => [
1534+
'dimensions' => 1536,
1535+
],
1536+
],
1537+
],
1538+
],
1539+
],
1540+
]);
1541+
}
1542+
14041543
public function testVectorizerConfiguration()
14051544
{
14061545
$container = $this->buildContainer([

0 commit comments

Comments
 (0)