C.W.K.
Stream
Lesson 04 of 05 · published

Google Gemini Function Declarations

~22 min · gemini, google, function-declaration, automatic-fc

Level 0Curious Reader
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

Google's Gemini wraps tool calling in a slightly more object-oriented envelope. Tools are FunctionDeclaration objects bundled inside a Tool, and the response carries function_call parts with name and args (a real dict, like Anthropic).

The unique feature in google-genai is automatic function calling: instead of writing the loop yourself, you can pass real Python functions to the SDK and let it execute them and feed the results back automatically. It is convenient for prototyping and limiting for production — you give up control over error handling, parallelism, and audit trails. Use it to feel the loop, then write the loop yourself when you ship.

Gemini also exposes function-calling modes: AUTO (default), ANY (must call something), and NONE (forbid calls). The same three semantics as OpenAI's tool_choice, expressed as enum values. The mode lives inside GenerationConfig; you do not pass it as a top-level parameter.

The streaming model uses generate_content_stream and yields Candidate objects with growing parts. The function-call part arrives all at once when the model decides — Gemini does not stream argument deltas the way OpenAI does. That makes streaming-with-tool-calls easier to consume and slightly slower to first byte.

Code

Manual loop with google-genai·python
from google import genai
from google.genai import types

client = genai.Client()

def get_weather(location: str) -> str:
    return f"68°F and clear in {location}"

tools = [types.Tool(function_declarations=[types.FunctionDeclaration(
    name="get_weather",
    description="Get current weather for a city.",
    parameters=types.Schema(
        type=types.Type.OBJECT,
        properties={"location": types.Schema(type=types.Type.STRING)},
        required=["location"],
    ),
)])]

contents = [types.Content(role="user", parts=[types.Part(text="Weather in Seoul?")])]
while True:
    resp = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=contents,
        config=types.GenerateContentConfig(tools=tools),
    )
    contents.append(resp.candidates[0].content)
    fc = next((p.function_call for p in resp.candidates[0].content.parts if p.function_call), None)
    if not fc:
        print(resp.text)
        break
    result = get_weather(**dict(fc.args))
    contents.append(types.Content(role="user", parts=[types.Part(
        function_response=types.FunctionResponse(name=fc.name, response={"result": result})
    )]))
Automatic function calling — convenience mode·python
# Pass real Python functions; the SDK handles the loop for you.
resp = client.models.generate_content(
    model="gemini-2.5-flash",
    contents="Weather in Seoul?",
    config=types.GenerateContentConfig(tools=[get_weather]),
)
print(resp.text)

External links

Exercise

Run the same prompt through both modes — automatic function calling and the manual loop — using one tool. Note where you lose control in the auto path: error handling, parallelism, intermediate logging. That gap is exactly the surface area you keep when you write the loop yourself.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.