adachsoft / ai-integration-gemini
Gemini SPI provider for adachsoft/ai-integration: tool-calling chat integration with Google Gemini 2.5 and 3.x models.
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/adachsoft/ai-integration-gemini
Requires
- php: ^8.3
- adachsoft/ai-integration: ^0.4.0
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
README
Integration library that provides a single gemini SPI provider for adachsoft/ai-integration.
It enables tool-calling and reasoning for Gemini 2.5 and 3.x models using Guzzle as HTTP client.
Status: Provider implementation and unit tests are complete. A production test and example script are available for real end-to-end verification against Gemini API.
Requirements
- PHP:
^8.3 adachsoft/ai-integration: ^0.4.0(installed as a dependency)guzzlehttp/guzzle: ^7.0- A valid Gemini API key exported as environment variable
GOOGLE_GEMINI_API_KEY - Network access to
https://generativelanguage.googleapis.com(v1beta/v1alpha endpoints, depending on model)
Installation
Install via Composer in a project that already uses adachsoft/ai-integration:
composer require adachsoft/ai-integration-gemini
Composer will register the AdachSoft\AiIntegrationGemini\ namespace pointing to the src/ directory.
Configuration
The provider is configured through the GeminiConfig value object:
use AdachSoft\AiIntegrationGemini\Config\GeminiConfig;
$config = new GeminiConfig(
apiKey: getenv('GOOGLE_GEMINI_API_KEY') ?: '',
defaultModelId: 'gemini-2.5-pro', // e.g. gemini-2.5-pro or gemini-3-pro-preview
defaultThinkingLevel: 'HIGH', // optional, reasoning level for v3 models
defaultThinkingBudget: 2000, // optional, reasoning token budget for v3 models
// baseUri and defaultTemperature have sensible defaults
toolCallingPipelineStrategy: 'self_healing', // 'self_healing' (default), 'noop', or 'retriable'
);
GOOGLE_GEMINI_API_KEYmust be available in the environment (for example via your framework's.envhandling or system-level configuration). This library does not manage or modify.envfiles.
Quick Start
The Gemini provider is exposed as a single SPI provider gemini for the ToolCallingChatFacade from adachsoft/ai-integration.
1. Build the SPI provider
use AdachSoft\AiIntegrationGemini\Config\GeminiConfig;
use AdachSoft\AiIntegrationGemini\GeminiToolCallingChatSpiFactory;
use GuzzleHttp\Client;
$apiKey = getenv('GOOGLE_GEMINI_API_KEY');
if ($apiKey === false || $apiKey === '') {
throw new RuntimeException('GOOGLE_GEMINI_API_KEY is not configured.');
}
$config = new GeminiConfig(
apiKey: $apiKey,
defaultModelId: 'gemini-2.5-pro',
defaultThinkingLevel: 'HIGH',
defaultThinkingBudget: 2000,
toolCallingPipelineStrategy: 'self_healing',
);
$factory = new GeminiToolCallingChatSpiFactory(
httpClient: new Client(),
config: $config,
);
$spiProvider = $factory->create();
2. Register provider in the facade
use AdachSoft\AiIntegration\PublicApi\Builder\ToolCallingChatFacadeBuilder;
$facadeBuilder = new ToolCallingChatFacadeBuilder();
$facadeBuilder->withSpiProvider('gemini', $spiProvider);
$facade = $facadeBuilder->build();
3. Define tools, generation parameters and run a simple roundtrip
use AdachSoft\AiIntegration\PublicApi\ToolCalling\Dto\ChatMessageDto;
use AdachSoft\AiIntegration\PublicApi\ToolCalling\Dto\Collection\ChatMessageDtoCollection;
use AdachSoft\AiIntegration\PublicApi\ToolCalling\Dto\Collection\ToolDefinitionDtoCollection;
use AdachSoft\AiIntegration\PublicApi\ToolCalling\Dto\ToolCallingChatRequestDto;
use AdachSoft\AiIntegration\PublicApi\ToolCalling\Dto\ToolDefinitionDto;
$tools = new ToolDefinitionDtoCollection([
new ToolDefinitionDto(
name: 'sum',
description: 'Sums two integers',
parametersSchema: [
'type' => 'object',
'properties' => [
'a' => ['type' => 'integer'],
'b' => ['type' => 'integer'],
],
'required' => ['a', 'b'],
],
),
]);
$messages = new ChatMessageDtoCollection([
new ChatMessageDto('user', 'Please call sum(a=2, b=3).'),
]);
// Generation parameters are forwarded 1:1 to the underlying SPI provider
// and from there to Gemini `generationConfig`.
$parameters = [
'temperature' => 0.0,
];
$request1 = new ToolCallingChatRequestDto(
messages: $messages,
tools: $tools,
providerId: 'gemini',
modelId: 'gemini-2.5-pro',
parameters: $parameters,
);
$response1 = $facade->chat($request1);
$toolCall = $response1->toolCalls->first();
$result = ($toolCall->arguments['a'] ?? 0) + ($toolCall->arguments['b'] ?? 0);
$toolResponseMessage = new ChatMessageDto(
role: 'tool',
content: (string) $result,
metadata: ['toolName' => $toolCall->toolName],
);
$messages2 = new ChatMessageDtoCollection([
...$messages,
$toolResponseMessage,
]);
$request2 = new ToolCallingChatRequestDto(
messages: $messages2,
tools: $tools,
providerId: 'gemini',
modelId: 'gemini-3-pro-preview',
parameters: $parameters,
);
$response2 = $facade->chat($request2);
$finalText = $response2->result; // string|null
Generation parameters semantics
On the adachsoft/ai-integration side, generation parameters are carried in the
ToolCallingChatRequestDto::$parameters array. For the Gemini provider:
parametersare forwarded 1:1 to the SPI request (ToolCallingChatSpiRequest::$parameters).- The Gemini SPI mapper forwards this array 1:1 to the Gemini
generationConfigpayload. - The only implicit behaviour is a default
temperaturevalue taken fromGeminiConfig::$defaultTemperaturewhen thetemperaturekey is missing ornull. - Any invalid or unsupported keys are considered configuration issues and are not filtered or altered by the mapper.
This means you can control Gemini generation knobs (such as temperature) directly
from the public API request without changing the Gemini provider code.
Examples and production test
This repository contains two ways to validate the integration end-to-end against real Gemini models:
1. CLI example
examples/test_gemini_tool_calling_via_spi.php shows a full roundtrip:
- Builds
GeminiToolCallingChatSpiand registers it under provider idgemini. - Defines a simple
sum(a, b)tool. - Passes generation parameters (for example
temperature) via theparametersarray to Gemini through the SPI provider. - Runs two calls (2.5 and 3.x) and prints the final answer.
- Exits with code
0when a non-empty final result is returned; otherwise exits with1.
Run it with:
export GOOGLE_GEMINI_API_KEY="your-key-here" # or equivalent for your environment
php examples/test_gemini_tool_calling_via_spi.php
2. Production PHPUnit test
tests/Production/GeminiProviderProductionTest.php contains a production-style PHPUnit test that:
- Builds a real
GeminiToolCallingChatSpiinstance. - Registers it in
ToolCallingChatFacadeBuilderunder provider idgemini. - Uses a
reveal_secrettool that returns a secret value. - Executes a two- or three-step tool-calling roundtrip for:
gemini-2.5-pro(expects at least one tool call and that the final response contains the secret),gemini-3-pro-preview(same behaviour, additionally asserting presence of a non-emptythoughtSignaturein response metadata).
- Validates that, for Gemini 3.x models, the
thoughtSignaturefield is propagated into SPI response metadata.
The test is skipped automatically when GOOGLE_GEMINI_API_KEY is not configured in the environment.
The PHPUnit configuration defines separate test suites:
unit(default) - fast unit tests undertests/Unit.production- live integration test undertests/Production.
Run tests with:
# unit tests only (default)
vendor/bin/phpunit
# production test against real Gemini API
vendor/bin/phpunit --testsuite production
Error handling and reasoning metadata
The provider translates Gemini-specific HTTP and JSON errors into SPI-level exceptions defined by adachsoft/ai-integration:
- Quota/limit issues (HTTP 429 or
RESOURCE_EXHAUSTED) becomeResourceExhaustedSpiException. - Transient errors (5xx) become
RetriableSpiExceptionwith an optionalretryAfterSecondshint. - Invalid or unexpected JSON structures become
SpiExceptionviaGeminiInvalidResponseException.
For Gemini 3.x models, the provider can additionally expose reasoning-related metadata in the SPI response, such as:
thinkingandthoughtSignature(when present in Gemini response),usageMetadatawith token usage details.
These are made available through the metadata and tokenUsage fields of ToolCallingChatSpiResponse.