Skip to content

Commit d5eab19

Browse files
feat(platform): Add support for Google vertex AI
- Adds support for embeddings
1 parent b7095e1 commit d5eab19

File tree

4 files changed

+213
-0
lines changed

4 files changed

+213
-0
lines changed
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+
namespace Symfony\AI\Platform\Bridge\VertexAi\Embeddings;
13+
14+
use Symfony\AI\Platform\Capability;
15+
use Symfony\AI\Platform\Model as BaseModel;
16+
17+
/**
18+
* @author Junaid Farooq <[email protected]>
19+
*/
20+
class Model extends BaseModel
21+
{
22+
/** Upto 3072 dimensions */
23+
public const GEMINI_EMBEDDING_001 = 'gemini-embedding-001';
24+
/** Upto 768 dimensions */
25+
public const TEXT_EMBEDDING_005 = 'text-embedding-005';
26+
/** Upto 768 dimensions */
27+
public const TEXT_MULTILINGUAL_EMBEDDING_002 = 'text-multilingual-embedding-002';
28+
29+
/**
30+
* @param array{task_type?: TaskType} $options
31+
* @see https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/text-embeddings-api for various options
32+
*/
33+
public function __construct(string $name = self::GEMINI_EMBEDDING_001, array $options = [])
34+
{
35+
parent::__construct($name, [Capability::INPUT_MULTIPLE], $options);
36+
}
37+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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\Embeddings;
13+
14+
use Symfony\AI\Platform\Model as BaseModel;
15+
use Symfony\AI\Platform\ModelClientInterface;
16+
use Symfony\AI\Platform\Result\RawHttpResult;
17+
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
18+
use Symfony\Contracts\HttpClient\HttpClientInterface;
19+
20+
/**
21+
* @author Junaid Farooq <[email protected]>
22+
*/
23+
final readonly class ModelClient implements ModelClientInterface
24+
{
25+
public function __construct(
26+
private HttpClientInterface $httpClient,
27+
private string $location,
28+
private string $projectId,
29+
) {
30+
}
31+
32+
public function supports(BaseModel $model): bool
33+
{
34+
return $model instanceof Model;
35+
}
36+
37+
/**
38+
* @throws TransportExceptionInterface
39+
*/
40+
public function request(BaseModel $model, array|string $payload, array $options = []): RawHttpResult
41+
{
42+
$url = \sprintf(
43+
'https://%s-aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:%s',
44+
$this->location,
45+
$this->projectId,
46+
$this->location,
47+
$model->getName(),
48+
'predict',
49+
);
50+
51+
$modelOptions = $model->getOptions();
52+
53+
if (isset($modelOptions['task_type']) && $modelOptions['task_type'] instanceof TaskType) {
54+
$modelOptions['task_type'] = $modelOptions['task_type']->value;
55+
}
56+
57+
$payload = [
58+
'instances' => array_map(
59+
static fn (string $text) => [
60+
'content' => ['parts' => [['text' => $text]]],
61+
'title' => $options['title'] ?? null,
62+
'task_type' => isset($modelOptions['task_type']) ? $modelOptions['task_type']->value : null,
63+
],
64+
\is_array($payload) ? $payload : [$payload],
65+
),
66+
];
67+
68+
unset($modelOptions['task_type']);
69+
70+
return new RawHttpResult(
71+
$this->httpClient->request(
72+
'POST',
73+
$url,
74+
[
75+
'headers' => [
76+
'Content-Type' => 'application/json',
77+
],
78+
'json' => array_merge($payload, $modelOptions),
79+
]
80+
)
81+
);
82+
}
83+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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\Embeddings;
13+
14+
use Symfony\AI\Platform\Exception\RuntimeException;
15+
use Symfony\AI\Platform\Model as BaseModel;
16+
use Symfony\AI\Platform\Result\RawResultInterface;
17+
use Symfony\AI\Platform\Result\VectorResult;
18+
use Symfony\AI\Platform\ResultConverterInterface;
19+
use Symfony\AI\Platform\Vector\Vector;
20+
21+
/**
22+
* @author Junaid Farooq <[email protected]>
23+
*/
24+
final readonly class ResultConverter implements ResultConverterInterface
25+
{
26+
public function supports(BaseModel $model): bool
27+
{
28+
return $model instanceof Model;
29+
}
30+
31+
public function convert(RawResultInterface $result, array $options = []): VectorResult
32+
{
33+
$data = $result->getData();
34+
35+
if (!isset($data['predictions'])) {
36+
throw new RuntimeException('Response does not contain data.');
37+
}
38+
39+
return new VectorResult(
40+
...array_map(
41+
static fn (array $item): Vector => new Vector($item['embeddings']['values']),
42+
$data['predictions'],
43+
),
44+
);
45+
}
46+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\Embeddings;
13+
14+
enum TaskType: string
15+
{
16+
/** Used to generate embeddings that are optimized to classify texts according to preset labels. */
17+
public const CLASSIFICATION = 'CLASSIFICATION';
18+
19+
/** Used to generate embeddings that are optimized to cluster texts based on their similarities */
20+
public const CLUSTERING = 'CLUSTERING';
21+
22+
/** Specifies the given text is a document from the corpus being searched. */
23+
public const RETRIEVAL_DOCUMENT = 'RETRIEVAL_DOCUMENT';
24+
25+
/** Specifies the given text is a query in a search/retrieval setting.
26+
* This is the recommended default for all the embeddings use case
27+
* that do not align with a documented use case.
28+
*/
29+
public const RETRIEVAL_QUERY = 'RETRIEVAL_QUERY';
30+
31+
/** Specifies that the given text will be used for question answering. */
32+
public const QUESTION_ANSWERING = 'QUESTION_ANSWERING';
33+
34+
/** Specifies that the given text will be used for fact verification. */
35+
public const FACT_VERIFICATION = 'FACT_VERIFICATION';
36+
37+
/** Used to retrieve a code block based on a natural language query,
38+
* such as sort an array or reverse a linked list.
39+
* Embeddings of the code blocks are computed using RETRIEVAL_DOCUMENT.
40+
*/
41+
public const CODE_RETRIEVAL_QUERY = 'CODE_RETRIEVAL_QUERY';
42+
43+
/** Used to generate embeddings that are optimized to assess text similarity.
44+
* This is not intended for retrieval use cases.
45+
*/
46+
public const SEMANTIC_SIMILARITY = 'SEMANTIC_SIMILARITY';
47+
}

0 commit comments

Comments
 (0)