Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/phpaisdk/google/llms.txt

Use this file to discover all available pages before exploring further.

Tool calling lets Gemini request your PHP functions by name during a conversation. Instead of returning a final answer, the model returns a structured request describing which function to call and with what arguments. The SDK parses function_call parts from the API response and wraps them as ToolCallPart objects, making it straightforward to detect, execute, and respond to them.

How it works

The function calling flow follows a multi-turn pattern:
  1. Send the initial request with one or more tool definitions attached.
  2. Gemini responds with a ToolCallPart$result->finishReason will be FinishReason::ToolCalls whenever a function_call part is detected in the response.
  3. Your code executes the function locally using the name and arguments from the ToolCallPart.
  4. Send a follow-up request containing a tool_result message with the output of that execution.
  5. Gemini produces the final text response, incorporating the tool result.

Defining tools

Pass an array of tool definitions to ->tools([...]) on your generate call. Each tool describes a function name, a description, and the parameters it accepts as a JSON schema.
use AiSdk\Generate;
use AiSdk\Google;
use AiSdk\Tool;
use AiSdk\ToolParameter;

Google::create(['apiKey' => env('GOOGLE_GENERATIVE_AI_API_KEY')]);

$getWeather = Tool::define(
    name: 'get_weather',
    description: 'Returns current weather conditions for a given city.',
    parameters: [
        ToolParameter::string('city', 'The name of the city, e.g. "London".', required: true),
        ToolParameter::string('unit', 'Temperature unit: "celsius" or "fahrenheit".'),
    ],
);

$result = Generate::text('What is the weather like in Tokyo right now?')
    ->model(Google::model('gemini-3.5-flash'))
    ->tools([$getWeather])
    ->run();

Handling the tool call response

After running the request, check whether Gemini is requesting a function call before processing $result->text.
use AiSdk\FinishReason;
use AiSdk\Responses\Parts\ToolCallPart;

if ($result->finishReason === FinishReason::ToolCalls) {
    foreach ($result->toolCalls as $toolCall) {
        // $toolCall is a ToolCallPart
        $name      = $toolCall->name;       // e.g. "get_weather"
        $arguments = $toolCall->arguments;  // e.g. ["city" => "Tokyo", "unit" => "celsius"]
        $callId    = $toolCall->id;

        // Execute your local function
        $toolOutput = match ($name) {
            'get_weather' => getWeather($arguments['city'], $arguments['unit'] ?? 'celsius'),
            default       => throw new \RuntimeException("Unknown tool: {$name}"),
        };

        echo "Tool called: {$name}\n";
        echo "Result: {$toolOutput}\n";
    }
}

Multi-turn loop: sending the tool result back

After executing the function, send the result back to Gemini as a tool_result message. Include the original assistant turn so the model has full context.
use AiSdk\Message;

// Re-run the conversation with the tool result appended
$finalResult = Generate::text()
    ->model(Google::model('gemini-3.5-flash'))
    ->tools([$getWeather])
    ->messages([
        Message::user('What is the weather like in Tokyo right now?'),
        Message::assistant($result->parts),           // assistant's tool call turn
        Message::toolResult($callId, $toolOutput),    // your function's output
    ])
    ->run();

echo $finalResult->text;
// "The current weather in Tokyo is 22 °C with partly cloudy skies."

Streaming tool calls

When streaming, the parser emits two parts for each tool call in sequence:
  • ToolCallStartPart — carries the index, id, and name of the tool call.
  • ToolCallDeltaPart — carries the index and a JSON string of the (potentially partial) arguments.
Because the Google SSE stream delivers function call arguments as a complete args object rather than token-by-token JSON, there is typically a single ToolCallDeltaPart per call containing the full argument payload. Reconstruct the call by matching on index.
use AiSdk\Streaming\ToolCallStartPart;
use AiSdk\Streaming\ToolCallDeltaPart;
use AiSdk\Streaming\TextDeltaPart;
use AiSdk\Streaming\FinishPart;
use AiSdk\FinishReason;

$stream = Generate::text('Book a table for 2 in Paris tonight.')
    ->model(Google::model('gemini-3.5-flash'))
    ->tools([$bookTable])
    ->stream();

$pendingCalls = [];

foreach ($stream->parts() as $part) {
    if ($part instanceof ToolCallStartPart) {
        $pendingCalls[$part->index] = [
            'id'   => $part->id,
            'name' => $part->name,
            'args' => '',
        ];
    }

    if ($part instanceof ToolCallDeltaPart) {
        $pendingCalls[$part->index]['args'] .= $part->arguments;
    }

    if ($part instanceof TextDeltaPart) {
        echo $part->text;
    }

    if ($part instanceof FinishPart && $part->finishReason === FinishReason::ToolCalls) {
        foreach ($pendingCalls as $call) {
            $arguments = json_decode($call['args'], true);
            // execute $call['name'] with $arguments …
        }
    }
}
The finish reason is unconditionally set to FinishReason::ToolCalls whenever a function_call part is detected. In non-streaming responses, GoogleResponseParser::finishReason() scans the parsed parts list and returns FinishReason::ToolCalls if any ToolCallPart is present, regardless of the finish_reason string sent by the API. In streaming responses, GoogleStreamParser::yieldDelta() overrides the running finish reason to FinishReason::ToolCalls as soon as it encounters a function_call delta.

Build docs developers (and LLMs) love