Skip to content

Commit 56c0a62

Browse files
feat(platform): Add support for Google vertex AI
- Adds initial support to integrate text generation using vertex AI
1 parent 11ef98f commit 56c0a62

File tree

11 files changed

+690
-0
lines changed

11 files changed

+690
-0
lines changed

examples/.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ RUN_EXPENSIVE_EXAMPLES=false
7272
# For using Gemini
7373
GEMINI_API_KEY=
7474

75+
# Vertex AI
76+
GOOGLE_CLOUD_PROJECT=GOOGLE_CLOUD_PROJECT
77+
GOOGLE_CLOUD_LOCATION=global
78+
7579
# For using Albert API (French Sovereign AI)
7680
ALBERT_API_KEY=
7781
ALBERT_API_URL=
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Bridge\VertexAi\Gemini\Model;
15+
use Symfony\AI\Platform\Contract\Normalizer\ModelContractNormalizer;
16+
use Symfony\AI\Platform\Message\AssistantMessage;
17+
use Symfony\AI\Platform\Model as BaseModel;
18+
19+
/**
20+
* @author Junaid Farooq <[email protected]>
21+
*/
22+
final class AssistantMessageNormalizer extends ModelContractNormalizer
23+
{
24+
/**
25+
* @param AssistantMessage $data
26+
*
27+
* @return array{
28+
* array{
29+
* text: string,
30+
* functionCall?: array{
31+
* name: string,
32+
* args?: array<int|string, mixed>
33+
* }
34+
* }
35+
* }
36+
*/
37+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
38+
{
39+
$normalized = [];
40+
41+
if (isset($data->content)) {
42+
$normalized['text'] = $data->content;
43+
}
44+
45+
if (isset($data->toolCalls[0])) {
46+
$normalized['functionCall'] = [
47+
'name' => $data->toolCalls[0]->name,
48+
];
49+
50+
if ($data->toolCalls[0]->arguments) {
51+
$normalized['functionCall']['args'] = $data->toolCalls[0]->arguments;
52+
}
53+
}
54+
55+
return [$normalized];
56+
}
57+
58+
protected function supportedDataClass(): string
59+
{
60+
return AssistantMessage::class;
61+
}
62+
63+
protected function supportsModel(BaseModel $model): bool
64+
{
65+
return $model instanceof Model;
66+
}
67+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Contract;
15+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
16+
17+
/**
18+
* @author Junaid Farooq <[email protected]>
19+
*/
20+
final readonly class GeminiContract extends Contract
21+
{
22+
public static function create(NormalizerInterface ...$normalizer): Contract
23+
{
24+
return parent::create(
25+
new AssistantMessageNormalizer(),
26+
new MessageBagNormalizer(),
27+
new ToolNormalizer(),
28+
new ToolCallMessageNormalizer(),
29+
new UserMessageNormalizer(),
30+
...$normalizer,
31+
);
32+
}
33+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Bridge\VertexAi\Gemini\Model;
15+
use Symfony\AI\Platform\Contract\Normalizer\ModelContractNormalizer;
16+
use Symfony\AI\Platform\Message\MessageBagInterface;
17+
use Symfony\AI\Platform\Message\Role;
18+
use Symfony\AI\Platform\Model as BaseModel;
19+
use Symfony\Component\Serializer\Exception\ExceptionInterface;
20+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
21+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
22+
23+
/**
24+
* @author Junaid Farooq <[email protected]>
25+
*/
26+
final class MessageBagNormalizer extends ModelContractNormalizer implements NormalizerAwareInterface
27+
{
28+
use NormalizerAwareTrait;
29+
30+
/**
31+
* @param MessageBagInterface $data
32+
*
33+
* @return array{
34+
* contents: list<array{
35+
* role: 'model'|'user',
36+
* parts: array<int, mixed>
37+
* }>,
38+
* systemInstruction?: array{parts: array{text: string}[]}
39+
* }
40+
*
41+
* @throws ExceptionInterface
42+
*/
43+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
44+
{
45+
$requestData = ['contents' => []];
46+
47+
if (null !== $systemMessage = $data->getSystemMessage()) {
48+
$requestData['systemInstruction'] = [
49+
'parts' => [['text' => $systemMessage->content]],
50+
];
51+
}
52+
53+
foreach ($data->withoutSystemMessage()->getMessages() as $message) {
54+
$requestData['contents'][] = [
55+
'role' => $message->getRole()->equals(Role::Assistant) ? 'model' : 'user',
56+
'parts' => [['text' => $this->normalizer->normalize($message, $format, $context)]],
57+
];
58+
}
59+
60+
return $requestData;
61+
}
62+
63+
protected function supportedDataClass(): string
64+
{
65+
return MessageBagInterface::class;
66+
}
67+
68+
protected function supportsModel(BaseModel $model): bool
69+
{
70+
return $model instanceof Model;
71+
}
72+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Bridge\VertexAi\Gemini\Model;
15+
use Symfony\AI\Platform\Contract\Normalizer\ModelContractNormalizer;
16+
use Symfony\AI\Platform\Message\ToolCallMessage;
17+
use Symfony\AI\Platform\Model as BaseModel;
18+
19+
/**
20+
* @author Junaid Farooq <[email protected]>
21+
*/
22+
final class ToolCallMessageNormalizer extends ModelContractNormalizer
23+
{
24+
/**
25+
* @param ToolCallMessage $data
26+
*
27+
* @return array{
28+
* functionResponse: array{
29+
* name: string,
30+
* response: array<int|string, mixed>
31+
* }
32+
* }[]
33+
*
34+
* @throws \JsonException
35+
*/
36+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
37+
{
38+
$resultContent = json_validate($data->content) ? json_decode($data->content, true, 512, \JSON_THROW_ON_ERROR) : $data->content;
39+
40+
return [[
41+
'functionResponse' => array_filter([
42+
'name' => $data->toolCall->name,
43+
'response' => \is_array($resultContent) ? $resultContent : [
44+
'rawResponse' => $resultContent,
45+
],
46+
]),
47+
]];
48+
}
49+
50+
protected function supportedDataClass(): string
51+
{
52+
return ToolCallMessage::class;
53+
}
54+
55+
protected function supportsModel(BaseModel $model): bool
56+
{
57+
return $model instanceof Model;
58+
}
59+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Bridge\VertexAi\Gemini\Model;
15+
use Symfony\AI\Platform\Contract\JsonSchema\Factory;
16+
use Symfony\AI\Platform\Contract\Normalizer\ModelContractNormalizer;
17+
use Symfony\AI\Platform\Model as BaseModel;
18+
use Symfony\AI\Platform\Tool\Tool;
19+
20+
/**
21+
* @author Junaid Farooq <[email protected]>
22+
*
23+
* @phpstan-import-type JsonSchema from Factory
24+
*/
25+
final class ToolNormalizer extends ModelContractNormalizer
26+
{
27+
/**
28+
* @param Tool $data
29+
*
30+
* @return array{
31+
* name: string,
32+
* description: string,
33+
* parameters: JsonSchema|array{type: 'object'}
34+
* }
35+
*/
36+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
37+
{
38+
$parameters = $data->parameters ? $this->removeAdditionalProperties($data->parameters) : null;
39+
40+
return [
41+
'name' => $data->name,
42+
'description' => $data->description,
43+
'parameters' => $parameters,
44+
'response' => $parameters,
45+
];
46+
}
47+
48+
protected function supportedDataClass(): string
49+
{
50+
return Tool::class;
51+
}
52+
53+
protected function supportsModel(BaseModel $model): bool
54+
{
55+
return $model instanceof Model;
56+
}
57+
58+
/**
59+
* @template T of array
60+
*
61+
* @phpstan-param T $data
62+
*
63+
* @phpstan-return T
64+
*/
65+
private function removeAdditionalProperties(array $data): array
66+
{
67+
unset($data['additionalProperties']);
68+
69+
foreach ($data as &$value) {
70+
if (\is_array($value)) {
71+
$value = $this->removeAdditionalProperties($value);
72+
}
73+
}
74+
75+
return $data;
76+
}
77+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Contract;
13+
14+
use Symfony\AI\Platform\Bridge\VertexAi\Gemini\Model;
15+
use Symfony\AI\Platform\Contract\Normalizer\ModelContractNormalizer;
16+
use Symfony\AI\Platform\Message\Content\File;
17+
use Symfony\AI\Platform\Message\Content\Text;
18+
use Symfony\AI\Platform\Message\UserMessage;
19+
use Symfony\AI\Platform\Model as BaseModel;
20+
21+
/**
22+
* @author Junaid Farooq <[email protected]>
23+
*/
24+
final class UserMessageNormalizer extends ModelContractNormalizer
25+
{
26+
/**
27+
* @param UserMessage $data
28+
*
29+
* @return list<array{inline_data?: array{mime_type: string, data: string}}>
30+
*/
31+
public function normalize(mixed $data, ?string $format = null, array $context = []): array
32+
{
33+
$parts = [];
34+
foreach ($data->content as $content) {
35+
if ($content instanceof Text) {
36+
$parts[] = ['text' => $content->text];
37+
}
38+
39+
if ($content instanceof File) {
40+
$parts[] = [
41+
'inlineData' => [
42+
'mimeType' => $content->getFormat(),
43+
'data' => $content->asBase64(),
44+
],
45+
];
46+
}
47+
}
48+
49+
return $parts;
50+
}
51+
52+
protected function supportedDataClass(): string
53+
{
54+
return UserMessage::class;
55+
}
56+
57+
protected function supportsModel(BaseModel $model): bool
58+
{
59+
return $model instanceof Model;
60+
}
61+
}

0 commit comments

Comments
 (0)