Skip to content

Commit 27a8121

Browse files
committed
Extend gemini catalog for image preview and editing example
1 parent ae670c0 commit 27a8121

File tree

6 files changed

+74
-8
lines changed

6 files changed

+74
-8
lines changed

examples/gemini/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
result.png

examples/gemini/image-editing.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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\Platform\Bridge\Gemini\PlatformFactory;
13+
use Symfony\AI\Platform\Message\Content\Image;
14+
use Symfony\AI\Platform\Message\Message;
15+
use Symfony\AI\Platform\Message\MessageBag;
16+
17+
require_once dirname(__DIR__).'/bootstrap.php';
18+
19+
$platform = PlatformFactory::create(env('GEMINI_API_KEY'), http_client());
20+
21+
$messages = new MessageBag(
22+
Message::ofUser(
23+
'Please colorize the elephant in red.',
24+
Image::fromFile(dirname(__DIR__, 2).'/fixtures/image.jpg'),
25+
),
26+
);
27+
$result = $platform->invoke('gemini-2.5-flash-image', $messages);
28+
29+
file_put_contents(__DIR__.'/result.png', $result->asBinary());
30+
31+
echo 'Result image saved to result.png'.\PHP_EOL;

src/platform/src/Bridge/Gemini/Gemini/ResultConverter.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ private function convertChoice(array $choice): ToolCallResult|TextResult|BinaryR
127127
}
128128

129129
if (isset($contentPart['inlineData'])) {
130-
return new BinaryResult($contentPart['inlineData']['data'], $contentPart['inlineData']['mimeType'] ?? null);
130+
return BinaryResult::fromBase64($contentPart['inlineData']['data'], $contentPart['inlineData']['mimeType'] ?? null);
131131
}
132132

133133
throw new RuntimeException(\sprintf('Unsupported finish reason "%s".', $choice['finishReason']));
@@ -150,7 +150,8 @@ private function convertChoice(array $choice): ToolCallResult|TextResult|BinaryR
150150
return new TextResult($content);
151151
}
152152

153-
throw new RuntimeException('Code execution failed.');
153+
// TODO: see https://github.com/symfony/ai/issues/1053
154+
throw new RuntimeException('Choice conversion failed. Potentially due to multiple content parts.');
154155
}
155156

156157
/**

src/platform/src/Bridge/Gemini/ModelCatalog.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ public function __construct(array $additionalModels = [])
3737
Capability::TOOL_CALLING,
3838
],
3939
],
40+
'gemini-3-pro-image-preview' => [
41+
'class' => Gemini::class,
42+
'capabilities' => [
43+
Capability::INPUT_MESSAGES,
44+
Capability::INPUT_IMAGE,
45+
Capability::OUTPUT_IMAGE,
46+
Capability::OUTPUT_TEXT,
47+
],
48+
],
49+
'gemini-2.5-flash-image' => [
50+
'class' => Gemini::class,
51+
'capabilities' => [
52+
Capability::INPUT_MESSAGES,
53+
Capability::INPUT_IMAGE,
54+
Capability::OUTPUT_IMAGE,
55+
Capability::OUTPUT_TEXT,
56+
],
57+
],
4058
'gemini-2.5-flash' => [
4159
'class' => Gemini::class,
4260
'capabilities' => [

src/platform/src/Result/BinaryResult.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,17 @@ public function __construct(
2424
) {
2525
}
2626

27+
public static function fromBase64(string $base64Data, ?string $mimeType = null): self
28+
{
29+
$data = base64_decode($base64Data, true);
30+
31+
if (false === $data) {
32+
throw new RuntimeException('The provided data is not valid base64-encoded data.');
33+
}
34+
35+
return new self($data, $mimeType);
36+
}
37+
2738
public function getMimeType(): ?string
2839
{
2940
return $this->mimeType;

src/platform/tests/Bridge/Gemini/Gemini/ResultConverterTest.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\AI\Platform\Bridge\Gemini\Gemini\ResultConverter;
1616
use Symfony\AI\Platform\Exception\RuntimeException;
17+
use Symfony\AI\Platform\Message\Content\Image;
1718
use Symfony\AI\Platform\Result\BinaryResult;
1819
use Symfony\AI\Platform\Result\RawHttpResult;
1920
use Symfony\AI\Platform\Result\ToolCall;
@@ -83,15 +84,16 @@ public function testConvertsInlineDataToBinaryResult()
8384
$converter = new ResultConverter();
8485
$httpResponse = self::createMock(ResponseInterface::class);
8586
$httpResponse->method('getStatusCode')->willReturn(200);
87+
$image = Image::fromFile(\dirname(__DIR__, 6).'/fixtures/image.jpg');
8688
$httpResponse->method('toArray')->willReturn([
8789
'candidates' => [
8890
[
8991
'content' => [
9092
'parts' => [
9193
[
9294
'inlineData' => [
93-
'mimeType' => 'image/png',
94-
'data' => 'base64EncodedImageData',
95+
'mimeType' => 'image/jpeg',
96+
'data' => $image->asBase64(),
9597
],
9698
],
9799
],
@@ -102,23 +104,25 @@ public function testConvertsInlineDataToBinaryResult()
102104

103105
$result = $converter->convert(new RawHttpResult($httpResponse));
104106
$this->assertInstanceOf(BinaryResult::class, $result);
105-
$this->assertSame('base64EncodedImageData', $result->getContent());
106-
$this->assertSame('image/png', $result->getMimeType());
107+
$this->assertSame($image->asBinary(), $result->getContent());
108+
$this->assertSame('image/jpeg', $result->getMimeType());
109+
$this->assertSame($image->asDataUrl(), $result->toDataUri());
107110
}
108111

109112
public function testConvertsInlineDataWithoutMimeTypeToBinaryResult()
110113
{
111114
$converter = new ResultConverter();
112115
$httpResponse = self::createMock(ResponseInterface::class);
113116
$httpResponse->method('getStatusCode')->willReturn(200);
117+
$image = Image::fromFile(\dirname(__DIR__, 6).'/fixtures/image.jpg');
114118
$httpResponse->method('toArray')->willReturn([
115119
'candidates' => [
116120
[
117121
'content' => [
118122
'parts' => [
119123
[
120124
'inlineData' => [
121-
'data' => 'base64EncodedData',
125+
'data' => $image->asBase64(),
122126
],
123127
],
124128
],
@@ -129,7 +133,7 @@ public function testConvertsInlineDataWithoutMimeTypeToBinaryResult()
129133

130134
$result = $converter->convert(new RawHttpResult($httpResponse));
131135
$this->assertInstanceOf(BinaryResult::class, $result);
132-
$this->assertSame('base64EncodedData', $result->getContent());
136+
$this->assertSame($image->asBinary(), $result->getContent());
133137
$this->assertNull($result->getMimeType());
134138
}
135139
}

0 commit comments

Comments
 (0)