Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/bundles/ai-bundle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Advanced Example with Multiple Agents
vertexai:
location: '%env(GOOGLE_CLOUD_LOCATION)%'
project_id: '%env(GOOGLE_CLOUD_PROJECT)%'
api_key: '%env(GOOGLE_CLOUD_VERTEX_API_KEY)%' # Needed only if authenticating with API keys
ollama:
host_url: '%env(OLLAMA_HOST_URL)%'
agent:
Expand Down
41 changes: 31 additions & 10 deletions docs/components/platform/vertexai.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ Setup
Authentication
~~~~~~~~~~~~~~

Vertex AI requires Google Cloud authentication. Follow the `Google cloud authentication guide`_ to set up your credentials.
Vertex AI supports 3 different authentication methods:

You can authenticate using:
1. Application Default Credentials (ADC)
----------------------------------------

1. **Application Default Credentials (ADC)** - Recommended for production
2. **Service Account Key** - For development or specific service accounts
Follow the `Google cloud authentication guide`_ to set up your credentials.

For ADC, install the Google Cloud SDK and authenticate:

Expand All @@ -36,18 +36,13 @@ For ADC, install the Google Cloud SDK and authenticate:

For detailed authentication setup, see `Setting up authentication for Vertex AI`_.

Environment Variables
~~~~~~~~~~~~~~~~~~~~~

Configure your Google Cloud project and location:

.. code-block:: bash

GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=us-central1

Usage
-----

Basic usage example::

Expand All @@ -59,7 +54,7 @@ Basic usage example::
$platform = PlatformFactory::create(
$_ENV['GOOGLE_CLOUD_LOCATION'],
$_ENV['GOOGLE_CLOUD_PROJECT'],
$httpClient
httpClient: $httpClient
);

$messages = new MessageBag(
Expand All @@ -69,6 +64,31 @@ Basic usage example::
$result = $platform->invoke('gemini-2.5-flash', $messages);
echo $result->getContent();

2. Service Account Key
----------------------

Similar to the first approach, but instead of authenticating with the `gcloud` command, you provide the service account key directly using an environment variable:

.. code-block:: bash

GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"

3. API keys
-----------

To get an API key, visit: `Vertex AI Studio (API keys)`_.

Similar to the first approach, but instead of authenticating with the `gcloud` command, you provide the API keys when creating the Platform:

Basic usage example with API keys::

$platform = PlatformFactory::create(
$_ENV['GOOGLE_CLOUD_LOCATION'],
$_ENV['GOOGLE_CLOUD_PROJECT'],
apiKey: $_ENV['GOOGLE_CLOUD_VERTEX_API_KEY'],
httpClient: $httpClient
);

Model Availability by Location
------------------------------

Expand Down Expand Up @@ -164,3 +184,4 @@ See the ``examples/vertexai/`` directory for complete working examples:
.. _Google cloud authentication guide: https://cloud.google.com/docs/authentication
.. _Setting up authentication for Vertex AI: https://cloud.google.com/vertex-ai/docs/authentication
.. _Google Cloud Console for Vertex AI: https://console.cloud.google.com/vertex-ai
.. _Vertex AI Studio (API keys): https://console.cloud.google.com/vertex-ai/studio/settings/api-keys
1 change: 1 addition & 0 deletions examples/.env
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ AIMLAPI_API_KEY=
# Vertex AI
GOOGLE_CLOUD_LOCATION=global
GOOGLE_CLOUD_PROJECT=GOOGLE_CLOUD_PROJECT
GOOGLE_CLOUD_VERTEX_API_KEY=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
GOOGLE_CLOUD_VERTEX_API_KEY=
GOOGLE_CLOUD_API_KEY=

in the whole PR

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't google cloud have hundreds of APIs that can be accessed with a product specific API key? This API key is for the key that can be created at https://console.cloud.google.com/vertex-ai/studio/settings/api-keys

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm you might be right 🤔


# For using Albert API (French Sovereign AI)
ALBERT_API_KEY=
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/audio-input.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::ofUser(
Expand Down
26 changes: 26 additions & 0 deletions examples/vertexai/chat-with-api-key.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

use Symfony\AI\Platform\Bridge\VertexAi\PlatformFactory;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), env('GOOGLE_CLOUD_VERTEX_API_KEY'));

$messages = new MessageBag(
Message::forSystem('You are an expert assistant in geography.'),
Message::ofUser('Where is Mount Fuji?'),
);
$result = $platform->invoke('gemini-2.5-flash', $messages);

echo $result->asText().\PHP_EOL;
2 changes: 1 addition & 1 deletion examples/vertexai/chat.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::forSystem('You are an expert assistant in geography.'),
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/embeddings.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$result = $platform->invoke('gemini-embedding-001', <<<TEXT
Once upon a time, there was a country called Japan. It was a beautiful country with a lot of mountains and rivers.
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/image-input.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::forSystem('You are an image analyzer bot that helps identify the content of images.'),
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/pdf-input-binary.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::ofUser(
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/server-tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::ofUser(
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/stream.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$messages = new MessageBag(
Message::forSystem('You are an expert assistant in geography.'),
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/structured-output-clock.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PlatformSubscriber());

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client(), eventDispatcher: $dispatcher);
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client(), eventDispatcher: $dispatcher);

$clock = new Clock(new SymfonyClock());
$toolbox = new Toolbox([$clock], logger: logger());
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/structured-output-math.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new PlatformSubscriber());

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client(), eventDispatcher: $dispatcher);
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client(), eventDispatcher: $dispatcher);
$messages = new MessageBag(
Message::forSystem('You are a helpful math tutor. Guide the user through the solution step by step.'),
Message::ofUser('how can I solve 8x + 7 = -23'),
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/token-metadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$agent = new Agent($platform, 'gemini-2.0-flash-lite');
$messages = new MessageBag(
Expand Down
2 changes: 1 addition & 1 deletion examples/vertexai/toolcall.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

require_once __DIR__.'/bootstrap.php';

$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), adc_aware_http_client());
$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), httpClient: adc_aware_http_client());

$toolbox = new Toolbox([new Clock()], logger: logger());
$processor = new AgentProcessor($toolbox);
Expand Down
1 change: 1 addition & 0 deletions src/ai-bundle/config/options.php
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@
->children()
->stringNode('location')->isRequired()->end()
->stringNode('project_id')->isRequired()->end()
->stringNode('api_key')->defaultNull()->end()
->end()
->end()
->arrayNode('openai')
Expand Down
1 change: 1 addition & 0 deletions src/ai-bundle/src/AiBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ private function processPlatformConfig(string $type, array $platform, ContainerB
->setArguments([
$platform['location'],
$platform['project_id'],
$platform['api_key'] ?? null,
$httpClient,
new Reference('ai.platform.model_catalog.vertexai.gemini'),
new Reference('ai.platform.contract.vertexai.gemini'),
Expand Down
5 changes: 3 additions & 2 deletions src/ai-bundle/tests/DependencyInjection/AiBundleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ public function testExtensionLoadDoesNotThrow()
{
$container = $this->buildContainer($this->getFullConfig());

// Mock services that are used as platform create arguments, but should not be testet here or are not available.
// Mock services that are used as platform create arguments, but should not be tested here or are not available.
$container->set('event_dispatcher', $this->createMock(EventDispatcherInterface::class));
$container->getDefinition('ai.platform.vertexai')->replaceArgument(2, $this->createMock(HttpClientInterface::class));
$container->getDefinition('ai.platform.vertexai')->replaceArgument(3, $this->createMock(HttpClientInterface::class));

$platforms = $container->findTaggedServiceIds('ai.platform');

Expand Down Expand Up @@ -7069,6 +7069,7 @@ private function getFullConfig(): array
'vertexai' => [
'location' => 'global',
'project_id' => '123',
'api_key' => 'vertex_key_full',
],
'dockermodelrunner' => [
'host_url' => 'http://127.0.0.1:12434',
Expand Down
7 changes: 7 additions & 0 deletions src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly string $location,
private readonly string $projectId,
private readonly ?string $apiKey = null,
) {
}

Expand All @@ -47,6 +48,11 @@ public function request(BaseModel $model, array|string $payload, array $options
'predict',
);

$query = [];
if (null !== $this->apiKey) {
$query['key'] = $this->apiKey;
}

$modelOptions = $model->getOptions();

$payload = [
Expand All @@ -71,6 +77,7 @@ public function request(BaseModel $model, array|string $payload, array $options
'Content-Type' => 'application/json',
],
'json' => array_merge($payload, $modelOptions),
'query' => $query,
]
)
);
Expand Down
7 changes: 7 additions & 0 deletions src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function __construct(
HttpClientInterface $httpClient,
private readonly string $location,
private readonly string $projectId,
private readonly ?string $apiKey = null,
) {
$this->httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);
}
Expand All @@ -52,6 +53,11 @@ public function request(BaseModel $model, array|string $payload, array $options
$options['stream'] ?? false ? 'streamGenerateContent' : 'generateContent',
);

$query = [];
if (null !== $this->apiKey) {
$query['key'] = $this->apiKey;
}

if (isset($options[PlatformSubscriber::RESPONSE_FORMAT]['json_schema']['schema'])) {
$options['generationConfig']['responseMimeType'] = 'application/json';
$options['generationConfig']['responseSchema'] = $options[PlatformSubscriber::RESPONSE_FORMAT]['json_schema']['schema'];
Expand Down Expand Up @@ -107,6 +113,7 @@ public function request(BaseModel $model, array|string $payload, array $options
$url,
[
'json' => array_merge($options, $payload),
'query' => $query,
]
)
);
Expand Down
3 changes: 2 additions & 1 deletion src/platform/src/Bridge/VertexAi/PlatformFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ final class PlatformFactory
public static function create(
string $location,
string $projectId,
#[\SensitiveParameter] ?string $apiKey = null,
?HttpClientInterface $httpClient = null,
ModelCatalogInterface $modelCatalog = new ModelCatalog(),
?Contract $contract = null,
Expand All @@ -45,7 +46,7 @@ public static function create(
$httpClient = $httpClient instanceof EventSourceHttpClient ? $httpClient : new EventSourceHttpClient($httpClient);

return new Platform(
[new GeminiModelClient($httpClient, $location, $projectId), new EmbeddingsModelClient($httpClient, $location, $projectId)],
[new GeminiModelClient($httpClient, $location, $projectId, $apiKey), new EmbeddingsModelClient($httpClient, $location, $projectId, $apiKey)],
[new GeminiResultConverter(), new EmbeddingsResultConverter()],
$modelCatalog,
$contract ?? GeminiContract::create(),
Expand Down
Loading