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

v0.4.1 2025-12-22 05:30 UTC

This package is not auto-updated.

Last update: 2025-12-22 04:35:45 UTC


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_KEY must be available in the environment (for example via your framework's .env handling or system-level configuration). This library does not manage or modify .env files.

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:

  • parameters are forwarded 1:1 to the SPI request (ToolCallingChatSpiRequest::$parameters).
  • The Gemini SPI mapper forwards this array 1:1 to the Gemini generationConfig payload.
  • The only implicit behaviour is a default temperature value taken from GeminiConfig::$defaultTemperature when the temperature key is missing or null.
  • 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 GeminiToolCallingChatSpi and registers it under provider id gemini.
  • Defines a simple sum(a, b) tool.
  • Passes generation parameters (for example temperature) via the parameters array to Gemini through the SPI provider.
  • Runs two calls (2.5 and 3.x) and prints the final answer.
  • Exits with code 0 when a non-empty final result is returned; otherwise exits with 1.

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 GeminiToolCallingChatSpi instance.
  • Registers it in ToolCallingChatFacadeBuilder under provider id gemini.
  • Uses a reveal_secret tool 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-empty thoughtSignature in response metadata).
  • Validates that, for Gemini 3.x models, the thoughtSignature field 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 under tests/Unit.
  • production - live integration test under tests/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) become ResourceExhaustedSpiException.
  • Transient errors (5xx) become RetriableSpiException with an optional retryAfterSeconds hint.
  • Invalid or unexpected JSON structures become SpiException via GeminiInvalidResponseException.

For Gemini 3.x models, the provider can additionally expose reasoning-related metadata in the SPI response, such as:

  • thinking and thoughtSignature (when present in Gemini response),
  • usageMetadata with token usage details.

These are made available through the metadata and tokenUsage fields of ToolCallingChatSpiResponse.