From c80dc080526d078e7fb5259a85244d39579bd7a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Mon, 15 Dec 2025 19:22:01 +0200 Subject: [PATCH 01/16] [Platform][VertexAI] Add support for API key authentication --- src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php | 5 +++++ src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php | 5 +++++ src/platform/src/Bridge/VertexAi/PlatformFactory.php | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php index 8a0ae98da..070493230 100644 --- a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php @@ -26,6 +26,7 @@ public function __construct( private readonly HttpClientInterface $httpClient, private readonly string $location, private readonly string $projectId, + private readonly ?string $apiKey = null, ) { } @@ -47,6 +48,10 @@ public function request(BaseModel $model, array|string $payload, array $options 'predict', ); + if (null !== $this->apiKey) { + $url .= '?key=' . $this->apiKey; + } + $modelOptions = $model->getOptions(); $payload = [ diff --git a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php index 70155f758..94feabc2d 100644 --- a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php @@ -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); } @@ -52,6 +53,10 @@ public function request(BaseModel $model, array|string $payload, array $options $options['stream'] ?? false ? 'streamGenerateContent' : 'generateContent', ); + if (null !== $this->apiKey) { + $url .= '?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']; diff --git a/src/platform/src/Bridge/VertexAi/PlatformFactory.php b/src/platform/src/Bridge/VertexAi/PlatformFactory.php index e80865edd..5b50aad80 100644 --- a/src/platform/src/Bridge/VertexAi/PlatformFactory.php +++ b/src/platform/src/Bridge/VertexAi/PlatformFactory.php @@ -33,6 +33,7 @@ final class PlatformFactory public static function create( string $location, string $projectId, + ?string $apiKey = null, ?HttpClientInterface $httpClient = null, ModelCatalogInterface $modelCatalog = new ModelCatalog(), ?Contract $contract = null, @@ -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(), From 60736896cb4fdb743ba63460c817e26b9f153c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:10:41 +0200 Subject: [PATCH 02/16] Fix examples --- examples/vertexai/audio-input.php | 2 +- examples/vertexai/chat.php | 2 +- examples/vertexai/embeddings.php | 2 +- examples/vertexai/image-input.php | 2 +- examples/vertexai/pdf-input-binary.php | 2 +- examples/vertexai/server-tools.php | 2 +- examples/vertexai/stream.php | 2 +- examples/vertexai/structured-output-clock.php | 2 +- examples/vertexai/structured-output-math.php | 2 +- examples/vertexai/token-metadata.php | 2 +- examples/vertexai/toolcall.php | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/vertexai/audio-input.php b/examples/vertexai/audio-input.php index 7cc5dea59..ac1c5a96b 100644 --- a/examples/vertexai/audio-input.php +++ b/examples/vertexai/audio-input.php @@ -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( diff --git a/examples/vertexai/chat.php b/examples/vertexai/chat.php index 25e2ca5d1..b93c50e75 100644 --- a/examples/vertexai/chat.php +++ b/examples/vertexai/chat.php @@ -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.'), diff --git a/examples/vertexai/embeddings.php b/examples/vertexai/embeddings.php index 71187a29a..4288b254c 100644 --- a/examples/vertexai/embeddings.php +++ b/examples/vertexai/embeddings.php @@ -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', <<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()); diff --git a/examples/vertexai/structured-output-math.php b/examples/vertexai/structured-output-math.php index 16d9b748f..b54d7fdd8 100644 --- a/examples/vertexai/structured-output-math.php +++ b/examples/vertexai/structured-output-math.php @@ -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'), diff --git a/examples/vertexai/token-metadata.php b/examples/vertexai/token-metadata.php index e469b7b68..095432b7b 100644 --- a/examples/vertexai/token-metadata.php +++ b/examples/vertexai/token-metadata.php @@ -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( diff --git a/examples/vertexai/toolcall.php b/examples/vertexai/toolcall.php index 7fe5c788a..fc8a24b16 100644 --- a/examples/vertexai/toolcall.php +++ b/examples/vertexai/toolcall.php @@ -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); From fd87fab863e282c7fda02f72983108b512864ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:12:12 +0200 Subject: [PATCH 03/16] Add GOOGLE_CLOUD_VERTEX_API_KEY to .env --- examples/.env | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/.env b/examples/.env index fce85f4b8..8ca0b115b 100644 --- a/examples/.env +++ b/examples/.env @@ -98,6 +98,7 @@ AIMLAPI_API_KEY= # Vertex AI GOOGLE_CLOUD_LOCATION=global GOOGLE_CLOUD_PROJECT=GOOGLE_CLOUD_PROJECT +GOOGLE_CLOUD_VERTEX_API_KEY=~ # For using Albert API (French Sovereign AI) ALBERT_API_KEY= From 954074aae57ef84b8ea566efc94951e14b3bfaf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:12:34 +0200 Subject: [PATCH 04/16] Update docs for Vertex AI --- docs/bundles/ai-bundle.rst | 1 + docs/components/platform/vertexai.rst | 44 +++++++++++++++++++++------ 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/docs/bundles/ai-bundle.rst b/docs/bundles/ai-bundle.rst index 8e81f6258..f02135ef9 100644 --- a/docs/bundles/ai-bundle.rst +++ b/docs/bundles/ai-bundle.rst @@ -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: diff --git a/docs/components/platform/vertexai.rst b/docs/components/platform/vertexai.rst index 86bacf93a..d0e1dc871 100644 --- a/docs/components/platform/vertexai.rst +++ b/docs/components/platform/vertexai.rst @@ -21,12 +21,13 @@ 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: @@ -36,9 +37,6 @@ 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 @@ -46,8 +44,6 @@ Configure your Google Cloud project and location: GOOGLE_CLOUD_PROJECT=your-project-id GOOGLE_CLOUD_LOCATION=us-central1 -Usage ------ Basic usage example:: @@ -59,7 +55,7 @@ Basic usage example:: $platform = PlatformFactory::create( $_ENV['GOOGLE_CLOUD_LOCATION'], $_ENV['GOOGLE_CLOUD_PROJECT'], - $httpClient + httpClient: $httpClient ); $messages = new MessageBag( @@ -69,6 +65,33 @@ 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 ------------------------------ @@ -164,3 +187,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 From da3efa4f83839df97e755e155960389a0f1bd662 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:12:57 +0200 Subject: [PATCH 05/16] Fix newly introduced apiKey parameter --- src/ai-bundle/src/AiBundle.php | 1 + src/ai-bundle/tests/DependencyInjection/AiBundleTest.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index 6810c6c03..47b9d1a91 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -586,6 +586,7 @@ private function processPlatformConfig(string $type, array $platform, ContainerB ->setArguments([ $platform['location'], $platform['project_id'], + $platform['api_key'], $httpClient, new Reference('ai.platform.model_catalog.vertexai.gemini'), new Reference('ai.platform.contract.vertexai.gemini'), diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index c59c9f5f7..98ca84266 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -91,7 +91,7 @@ public function testExtensionLoadDoesNotThrow() // Mock services that are used as platform create arguments, but should not be testet 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'); From cbd5290974b58963a48a5ac45bde9af7ddb833ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:14:29 +0200 Subject: [PATCH 06/16] Update code style --- src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php | 2 +- src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php index 070493230..4d1161259 100644 --- a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php @@ -49,7 +49,7 @@ public function request(BaseModel $model, array|string $payload, array $options ); if (null !== $this->apiKey) { - $url .= '?key=' . $this->apiKey; + $url .= '?key='.$this->apiKey; } $modelOptions = $model->getOptions(); diff --git a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php index 94feabc2d..c187a2c20 100644 --- a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php @@ -54,7 +54,7 @@ public function request(BaseModel $model, array|string $payload, array $options ); if (null !== $this->apiKey) { - $url .= '?key=' . $this->apiKey; + $url .= '?key='.$this->apiKey; } if (isset($options[PlatformSubscriber::RESPONSE_FORMAT]['json_schema']['schema'])) { From 35a4a75d9f8e76a09c46075d30cd2e95e6e40302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:21:08 +0200 Subject: [PATCH 07/16] Make api_key optional --- src/ai-bundle/src/AiBundle.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ai-bundle/src/AiBundle.php b/src/ai-bundle/src/AiBundle.php index 47b9d1a91..8c025d490 100644 --- a/src/ai-bundle/src/AiBundle.php +++ b/src/ai-bundle/src/AiBundle.php @@ -586,7 +586,7 @@ private function processPlatformConfig(string $type, array $platform, ContainerB ->setArguments([ $platform['location'], $platform['project_id'], - $platform['api_key'], + $platform['api_key'] ?? null, $httpClient, new Reference('ai.platform.model_catalog.vertexai.gemini'), new Reference('ai.platform.contract.vertexai.gemini'), From 7161cc1adc7da2539fc57b19bc9d22d04b4a3948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:21:18 +0200 Subject: [PATCH 08/16] Fix typo in comments --- src/ai-bundle/tests/DependencyInjection/AiBundleTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index 98ca84266..3c6d304f0 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -89,7 +89,7 @@ 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(3, $this->createMock(HttpClientInterface::class)); From 9ebf514b26e10fcadba00b91629e0a5b948469c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:44:28 +0200 Subject: [PATCH 09/16] Add api_key to AiBundleTest --- src/ai-bundle/tests/DependencyInjection/AiBundleTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php index 3c6d304f0..e51ed534c 100644 --- a/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php +++ b/src/ai-bundle/tests/DependencyInjection/AiBundleTest.php @@ -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', From f7d3c2d879be040a1753bffb2ab2b75950a005ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 22:50:53 +0200 Subject: [PATCH 10/16] Add configuration option --- src/ai-bundle/config/options.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ai-bundle/config/options.php b/src/ai-bundle/config/options.php index adc0a656c..5d6d0da73 100644 --- a/src/ai-bundle/config/options.php +++ b/src/ai-bundle/config/options.php @@ -158,6 +158,7 @@ ->children() ->stringNode('location')->isRequired()->end() ->stringNode('project_id')->isRequired()->end() + ->stringNode('api_key')->defaultNull()->end() ->end() ->end() ->arrayNode('openai') From 73695b46f02c7679c220f7e892270ce1bee0c4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 23:29:01 +0200 Subject: [PATCH 11/16] Update examples/.env Co-authored-by: Oskar Stark --- examples/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/.env b/examples/.env index 8ca0b115b..660444127 100644 --- a/examples/.env +++ b/examples/.env @@ -98,7 +98,7 @@ AIMLAPI_API_KEY= # Vertex AI GOOGLE_CLOUD_LOCATION=global GOOGLE_CLOUD_PROJECT=GOOGLE_CLOUD_PROJECT -GOOGLE_CLOUD_VERTEX_API_KEY=~ +GOOGLE_CLOUD_VERTEX_API_KEY= # For using Albert API (French Sovereign AI) ALBERT_API_KEY= From 8f1853cfab9cd41dd2fc89554006ae9c5d1a7211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 23:32:59 +0200 Subject: [PATCH 12/16] Update headings --- docs/components/platform/vertexai.rst | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/components/platform/vertexai.rst b/docs/components/platform/vertexai.rst index d0e1dc871..ea28c472c 100644 --- a/docs/components/platform/vertexai.rst +++ b/docs/components/platform/vertexai.rst @@ -23,9 +23,8 @@ Authentication Vertex AI supports 3 different authentication methods: -======================================== 1. Application Default Credentials (ADC) -======================================== +---------------------------------------- Follow the `Google cloud authentication guide`_ to set up your credentials. @@ -65,9 +64,8 @@ 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: @@ -75,9 +73,8 @@ Similar to the first approach, but instead of authenticating with the `gcloud` c GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json" -=========== 3. API keys -=========== +----------- To get an API key, visit: `Vertex AI Studio (API keys)`_. From cc910eaef62472852354058c191e05d480cf47a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 23:35:20 +0200 Subject: [PATCH 13/16] Add example to chat with API key --- examples/vertexai/chat-with-api-key.php | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 examples/vertexai/chat-with-api-key.php diff --git a/examples/vertexai/chat-with-api-key.php b/examples/vertexai/chat-with-api-key.php new file mode 100644 index 000000000..833304294 --- /dev/null +++ b/examples/vertexai/chat-with-api-key.php @@ -0,0 +1,26 @@ + + * + * 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'), httpClient: adc_aware_http_client()); + +$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; From 8dcdb2fe9e5b7466f106d7372fc13a95b887ec71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Thu, 18 Dec 2025 23:35:36 +0200 Subject: [PATCH 14/16] Make API key with SensitiveParameter attribute --- src/platform/src/Bridge/VertexAi/PlatformFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platform/src/Bridge/VertexAi/PlatformFactory.php b/src/platform/src/Bridge/VertexAi/PlatformFactory.php index 5b50aad80..ff775739c 100644 --- a/src/platform/src/Bridge/VertexAi/PlatformFactory.php +++ b/src/platform/src/Bridge/VertexAi/PlatformFactory.php @@ -33,7 +33,7 @@ final class PlatformFactory public static function create( string $location, string $projectId, - ?string $apiKey = null, + #[\SensitiveParameter] ?string $apiKey = null, ?HttpClientInterface $httpClient = null, ModelCatalogInterface $modelCatalog = new ModelCatalog(), ?Contract $contract = null, From 01fafa7c3e79d09d06dcd88014cb9ab6e21bf2a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Fri, 19 Dec 2025 00:20:33 +0200 Subject: [PATCH 15/16] Remove adc_aware_http_client from chat-with-api-key.php example --- examples/vertexai/chat-with-api-key.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/vertexai/chat-with-api-key.php b/examples/vertexai/chat-with-api-key.php index 833304294..fc58a0924 100644 --- a/examples/vertexai/chat-with-api-key.php +++ b/examples/vertexai/chat-with-api-key.php @@ -15,7 +15,7 @@ require_once __DIR__.'/bootstrap.php'; -$platform = PlatformFactory::create(env('GOOGLE_CLOUD_LOCATION'), env('GOOGLE_CLOUD_PROJECT'), env('GOOGLE_CLOUD_VERTEX_API_KEY'), httpClient: adc_aware_http_client()); +$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.'), From c5bd3ec2282d73688189c8836437cdf20a9e4e16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Hiort=20af=20Orn=C3=A4s?= Date: Fri, 19 Dec 2025 13:39:42 +0200 Subject: [PATCH 16/16] Pass query parameters as array --- src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php | 4 +++- src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php index 4d1161259..918b1bb91 100644 --- a/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Embeddings/ModelClient.php @@ -48,8 +48,9 @@ public function request(BaseModel $model, array|string $payload, array $options 'predict', ); + $query = []; if (null !== $this->apiKey) { - $url .= '?key='.$this->apiKey; + $query['key'] = $this->apiKey; } $modelOptions = $model->getOptions(); @@ -76,6 +77,7 @@ public function request(BaseModel $model, array|string $payload, array $options 'Content-Type' => 'application/json', ], 'json' => array_merge($payload, $modelOptions), + 'query' => $query, ] ) ); diff --git a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php index c187a2c20..e6bef1836 100644 --- a/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php +++ b/src/platform/src/Bridge/VertexAi/Gemini/ModelClient.php @@ -53,8 +53,9 @@ public function request(BaseModel $model, array|string $payload, array $options $options['stream'] ?? false ? 'streamGenerateContent' : 'generateContent', ); + $query = []; if (null !== $this->apiKey) { - $url .= '?key='.$this->apiKey; + $query['key'] = $this->apiKey; } if (isset($options[PlatformSubscriber::RESPONSE_FORMAT]['json_schema']['schema'])) { @@ -112,6 +113,7 @@ public function request(BaseModel $model, array|string $payload, array $options $url, [ 'json' => array_merge($options, $payload), + 'query' => $query, ] ) );