Skip to content

Commit cca319e

Browse files
committed
Use JSON Path to convert responses
1 parent bf941b4 commit cca319e

File tree

64 files changed

+2636
-1714
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2636
-1714
lines changed

examples/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"symfony/event-dispatcher": "^6.4|^7.0",
2323
"symfony/filesystem": "^6.4|^7.0",
2424
"symfony/finder": "^6.4|^7.0",
25+
"symfony/json-path": "7.3.*",
2526
"symfony/process": "^6.4|^7.0",
2627
"symfony/var-dumper": "^6.4|^7.0"
2728
},

examples/gemini/server-tools.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
$platform = PlatformFactory::create(env('GEMINI_API_KEY'), http_client());
2424

2525
// Available server-side tools as of 2025-06-28: url_context, google_search, code_execution
26-
$llm = new Gemini('gemini-2.5-pro-preview-03-25', ['server_tools' => ['url_context' => true], 'temperature' => 1.0]);
26+
$model = new Gemini('gemini-2.5-pro-preview-03-25', ['server_tools' => ['url_context' => true], 'temperature' => 1.0]);
2727

2828
$toolbox = new Toolbox([new Clock()], logger: logger());
2929
$processor = new AgentProcessor($toolbox);
30-
$agent = new Agent($platform, $llm, logger: logger());
30+
$agent = new Agent($platform, $model, logger: logger());
3131

3232
$messages = new MessageBag(
3333
Message::ofUser(

examples/gemini/toolcall.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121
require_once dirname(__DIR__).'/bootstrap.php';
2222

2323
$platform = PlatformFactory::create(env('GEMINI_API_KEY'), http_client());
24-
$llm = new Gemini(Gemini::GEMINI_2_FLASH);
24+
$model = new Gemini(Gemini::GEMINI_2_FLASH);
2525

2626
$toolbox = new Toolbox([new Clock()], logger: logger());
2727
$processor = new AgentProcessor($toolbox);
28-
$agent = new Agent($platform, $llm, [$processor], [$processor], logger());
28+
$agent = new Agent($platform, $model, [$processor], [$processor], logger());
2929

3030
$messages = new MessageBag(Message::ofUser('What time is it?'));
3131
$result = $agent->call($messages);

examples/ollama/stream.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\AI\Agent\Agent;
13+
use Symfony\AI\Platform\Bridge\Ollama\Ollama;
14+
use Symfony\AI\Platform\Bridge\Ollama\PlatformFactory;
15+
use Symfony\AI\Platform\Message\Message;
16+
use Symfony\AI\Platform\Message\MessageBag;
17+
18+
require_once dirname(__DIR__).'/bootstrap.php';
19+
20+
$platform = PlatformFactory::create(env('OLLAMA_HOST_URL'), http_client());
21+
$model = new Ollama();
22+
23+
$agent = new Agent($platform, $model, logger: logger());
24+
$messages = new MessageBag(
25+
Message::forSystem('You are a helpful assistant.'),
26+
Message::ofUser('Tina has one brother and one sister. How many sisters do Tina\'s siblings have?'),
27+
);
28+
$result = $agent->call($messages, [
29+
'stream' => true,
30+
]);
31+
32+
foreach ($result->getContent() as $word) {
33+
echo $word;
34+
}
35+
echo \PHP_EOL;

examples/openrouter/stream-gemini.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
use Symfony\AI\Agent\Agent;
13+
use Symfony\AI\Platform\Bridge\OpenRouter\PlatformFactory;
14+
use Symfony\AI\Platform\Message\Message;
15+
use Symfony\AI\Platform\Message\MessageBag;
16+
use Symfony\AI\Platform\Model;
17+
18+
require_once dirname(__DIR__).'/bootstrap.php';
19+
20+
$platform = PlatformFactory::create(env('OPENROUTER_KEY'), http_client());
21+
// In case free is running into 429 rate limit errors, you can use the paid model:
22+
// $model = new Model('google/gemini-2.0-flash-lite-001');
23+
$model = new Model('google/gemini-2.0-flash-exp:free');
24+
25+
$agent = new Agent($platform, $model, logger: logger());
26+
$messages = new MessageBag(
27+
Message::forSystem('You are a helpful assistant and explain your answer lengthy.'),
28+
Message::ofUser('Tina has one brother and one sister. How many sisters do Tina\'s siblings have?'),
29+
);
30+
$result = $agent->call($messages, [
31+
'stream' => true,
32+
]);
33+
34+
foreach ($result->getContent() as $word) {
35+
echo $word;
36+
}
37+
echo \PHP_EOL;

src/agent/src/Toolbox/AgentProcessor.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\AI\Platform\Capability;
2424
use Symfony\AI\Platform\Message\AssistantMessage;
2525
use Symfony\AI\Platform\Message\Message;
26+
use Symfony\AI\Platform\Result\ChoiceResult;
2627
use Symfony\AI\Platform\Result\ResultInterface;
2728
use Symfony\AI\Platform\Result\StreamResult as GenericStreamResponse;
2829
use Symfony\AI\Platform\Result\ToolCallResult;
@@ -76,6 +77,15 @@ public function processOutput(Output $output): void
7677
return;
7778
}
7879

80+
// Promote ToolCallResult from collection of choices
81+
if ($output->result instanceof ChoiceResult) {
82+
foreach ($output->result->getContent() as $result) {
83+
if ($result instanceof ToolCallResult) {
84+
$output->result = $result;
85+
}
86+
}
87+
}
88+
7989
if (!$output->result instanceof ToolCallResult) {
8090
return;
8191
}

src/platform/composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"psr/log": "^3.0",
2929
"symfony/clock": "^6.4 || ^7.1",
3030
"symfony/http-client": "^6.4 || ^7.1",
31+
"symfony/json-path": "7.3.*",
3132
"symfony/property-access": "^6.4 || ^7.1",
3233
"symfony/property-info": "^6.4 || ^7.1",
3334
"symfony/serializer": "^6.4 || ^7.1",

src/platform/phpstan.dist.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ parameters:
77
paths:
88
- src/
99
- tests/
10+
treatPhpDocTypesAsCertain: false
1011
ignoreErrors:
1112
-
1213
message: "#^Method .*::test.*\\(\\) has no return type specified\\.$#"

src/platform/src/Bridge/Albert/PlatformFactory.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
namespace Symfony\AI\Platform\Bridge\Albert;
1313

14-
use Symfony\AI\Platform\Bridge\OpenAi\Embeddings;
15-
use Symfony\AI\Platform\Bridge\OpenAi\Gpt;
1614
use Symfony\AI\Platform\Contract;
1715
use Symfony\AI\Platform\Exception\InvalidArgumentException;
1816
use Symfony\AI\Platform\Platform;
@@ -40,7 +38,7 @@ public static function create(
4038
new GptModelClient($httpClient, $apiKey, $baseUrl),
4139
new EmbeddingsModelClient($httpClient, $apiKey, $baseUrl),
4240
],
43-
[new Gpt\ResultConverter(), new Embeddings\ResultConverter()],
41+
[Contract\ResultConverter::create()],
4442
Contract::create(),
4543
);
4644
}

src/platform/src/Bridge/Anthropic/PlatformFactory.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
use Symfony\AI\Platform\Bridge\Anthropic\Contract\AnthropicContract;
1515
use Symfony\AI\Platform\Contract;
16+
use Symfony\AI\Platform\Contract\ResultExtractor\StreamResultExtractor;
17+
use Symfony\AI\Platform\Contract\ResultExtractor\TextResultExtractor;
18+
use Symfony\AI\Platform\Contract\ResultExtractor\ToolCallResultExtractor;
1619
use Symfony\AI\Platform\Platform;
1720
use Symfony\Component\HttpClient\EventSourceHttpClient;
1821
use Symfony\Contracts\HttpClient\HttpClientInterface;
@@ -33,7 +36,20 @@ public static function create(
3336

3437
return new Platform(
3538
[new ModelClient($httpClient, $apiKey, $version)],
36-
[new ResultConverter()],
39+
[Contract\ResultConverter::create([
40+
new TextResultExtractor('$.content[[email protected] == "text"].text'),
41+
new ToolCallResultExtractor(
42+
'$.content[[email protected] == "tool_use"]',
43+
'$.content[[email protected] == "tool_use"].id',
44+
'$.content[[email protected] == "tool_use"].name',
45+
'$.content[[email protected] == "tool_use"].input',
46+
),
47+
new StreamResultExtractor(
48+
'$.delta.text',
49+
'$.delta[[email protected] == "tool_use"]',
50+
'$.delta.stop_reason',
51+
),
52+
])],
3753
$contract ?? AnthropicContract::create(),
3854
);
3955
}

0 commit comments

Comments
 (0)