Skip to content

Commit 88ca18a

Browse files
valtzuchr-hertel
authored andcommitted
feat: Google Gemini server tools (#356)
Add support for Google server-side tools like [Google search](https://ai.google.dev/gemini-api/docs/google-search), [URL context](https://ai.google.dev/gemini-api/docs/url-context) & [Code execution](https://ai.google.dev/gemini-api/docs/code-execution) --- I thought of using `Tool` classes for this but didn't find any solution I was happy with, so went the easy way for now. Ideally no changes would've been required for this approach at all, but there were 2 issues: 1. Gemini API insist on providing empty object, and IMO it's not feasible to expect end-user to provide `new \ArrayObject()` 2. If you provide `tools` in `call`'s `$options`, then all toolbox tools go away – hence the new `server_tools` _(open to better naming – `remote_tools`?)_ --- If someone has already given this a thought, please share your thoughts / if there is some clear better approach
1 parent c6e32b6 commit 88ca18a

File tree

3 files changed

+63
-1
lines changed

3 files changed

+63
-1
lines changed

examples/google/server-tools.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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 PhpLlm\LlmChain\Chain\Chain;
13+
use PhpLlm\LlmChain\Chain\Toolbox\ChainProcessor;
14+
use PhpLlm\LlmChain\Chain\Toolbox\Tool\Clock;
15+
use PhpLlm\LlmChain\Chain\Toolbox\Toolbox;
16+
use PhpLlm\LlmChain\Platform\Bridge\Google\Gemini;
17+
use PhpLlm\LlmChain\Platform\Bridge\Google\PlatformFactory;
18+
use PhpLlm\LlmChain\Platform\Message\Message;
19+
use PhpLlm\LlmChain\Platform\Message\MessageBag;
20+
use Symfony\Component\Dotenv\Dotenv;
21+
22+
require_once dirname(__DIR__, 2).'/vendor/autoload.php';
23+
(new Dotenv())->loadEnv(dirname(__DIR__, 2).'/.env');
24+
25+
if (empty($_ENV['GOOGLE_API_KEY'])) {
26+
echo 'Please set the GOOGLE_API_KEY environment variable.'.\PHP_EOL;
27+
exit(1);
28+
}
29+
30+
$platform = PlatformFactory::create($_ENV['GOOGLE_API_KEY']);
31+
32+
// Available server-side tools as of 2025-06-28: url_context, google_search, code_execution
33+
$llm = new Gemini('gemini-2.5-pro-preview-03-25', ['server_tools' => ['url_context' => true], 'temperature' => 1.0]);
34+
35+
$toolbox = Toolbox::create(new Clock());
36+
$processor = new ChainProcessor($toolbox);
37+
$chain = new Chain($platform, $llm);
38+
39+
$messages = new MessageBag(
40+
Message::ofUser(
41+
<<<'PROMPT'
42+
What was the 12 month Euribor rate a week ago based on https://www.euribor-rates.eu/en/current-euribor-rates/4/euribor-rate-12-months/
43+
PROMPT,
44+
),
45+
);
46+
47+
$response = $chain->call($messages);
48+
49+
echo $response->getContent().\PHP_EOL;

src/platform/src/Bridge/Google/ModelHandler.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,21 @@ public function request(Model $model, array|string $payload, array $options = []
6464
$generationConfig = ['generationConfig' => $options];
6565
unset($generationConfig['generationConfig']['stream']);
6666
unset($generationConfig['generationConfig']['tools']);
67+
unset($generationConfig['generationConfig']['server_tools']);
6768

6869
if (isset($options['tools'])) {
6970
$generationConfig['tools'] = $options['tools'];
7071
unset($options['tools']);
7172
}
7273

74+
foreach ($options['server_tools'] ?? [] as $tool => $params) {
75+
if (!$params) {
76+
continue;
77+
}
78+
79+
$generationConfig['tools'][] = [$tool => true === $params ? new \ArrayObject() : $params];
80+
}
81+
7382
return $this->httpClient->request('POST', $url, [
7483
'headers' => [
7584
'x-goog-api-key' => $this->apiKey,

src/platform/src/Contract.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use Symfony\AI\Platform\Contract\Normalizer\Response\ToolCallNormalizer;
2424
use Symfony\AI\Platform\Contract\Normalizer\ToolNormalizer;
2525
use Symfony\AI\Platform\Tool\Tool;
26+
use Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer;
2627
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
2728
use Symfony\Component\Serializer\Serializer;
2829

@@ -81,6 +82,9 @@ public function createRequestPayload(Model $model, object|array|string $input):
8182
*/
8283
public function createToolOption(array $tools, Model $model): array
8384
{
84-
return $this->normalizer->normalize($tools, context: [self::CONTEXT_MODEL => $model]);
85+
return $this->normalizer->normalize($tools, context: [
86+
self::CONTEXT_MODEL => $model,
87+
AbstractObjectNormalizer::PRESERVE_EMPTY_OBJECTS => true,
88+
]);
8589
}
8690
}

0 commit comments

Comments
 (0)