Skip to content

Gemini

Available · function declarations, call dispatch, genai automatic function calling · google-genai optional

Functions

Function (agent_adapter.integrations.gemini) Returns Needs
function_declarations(registry) every tool as a declaration dict nothing
function_declaration(spec) {"name", "description", "parameters_json_schema"} nothing
genai_tool(registry) one google.genai.types.Tool holding every tool pip install "agent-adapter[gemini]"
run_function_calls(calls, *, executor=None, ctx=None, registry=None) one function-response types.Part per call, in order the extra
afc_callable(spec, *, executor=None) an async function genai AFC can call the extra, to use it
afc_callables(registry, *, executor=None) afc_callable for every tool exported to gemini as above
INTEGRATION the integration object: declaration dicts; bind_context(function_call) reads its id get_integration("gemini") needs the extra

Without google-genai, genai_tool and run_function_calls raise MissingDependency (also an ImportError). executor=None uses the default executor.

Send the declarations, turn AFC off, and answer the model's calls with run_function_calls. Every call runs through the executor.

from agent_adapter.integrations.gemini import genai_tool, run_function_calls

executor = Executor(registry)
config = types.GenerateContentConfig(
    tools=[genai_tool(registry)],
    automatic_function_calling=types.AutomaticFunctionCallingConfig(disable=True),
)
# response = await client.aio.models.generate_content(model=..., contents=..., config=config)
# calls = response.function_calls
calls = [types.FunctionCall(id="fc_1", name="shipping_rate", args={"item": "laptop"})]
parts = await run_function_calls(calls, executor=executor)  # one function response each
sequenceDiagram
  participant App
  participant G as Gemini API
  participant E as Executor
  App->>G: generate_content(tools=[genai_tool(registry)], AFC off)
  G-->>App: function_calls (name, args, id)
  App->>E: run_function_calls(calls, executor=E)
  E-->>App: function-response Parts
  App->>G: next turn with the Parts
What How
calls types.FunctionCalls, parts with a function_call, or {"name", "args", "id"} dicts
Tool lookup registry=, else the executor's registry; only tools exported to gemini
Concurrency an Executor runs the calls at once in one run (parallel_safe honoured); another runner runs them in order
Call id FunctionCall.id becomes ctx.call_id and the response's id; a call without one gets a stable id from its name, arguments and occurrence (the n-th identical call), so a reordered retry matches
Result {"result": value} (JSON mode)
Model-visible errors {"error": {"kind", "message"}}
Other errors (ApprovalPending, ToolNotFound, ToolTimeout, ToolFailed) raised: stop the turn, decide, retry
Run ctx=, else current_context() inside an adapter run, else a fresh run per batch
Approval (DeferredApproval) pass ctx=executor.context(run_id=<stable id for this turn>); after ApprovalPending, decide, then call again with the same calls and ctx. Calls that already completed are answered from memory (bounded, in process), not run again; without a caller run id they run again

Automatic function calling (AFC)

afc_callables gives genai real async functions. genai reads each one's synthesised signature and docstring, declares it, and calls it during generate_content.

from agent_adapter.integrations.gemini import afc_callables

executor = Executor(registry)
config = types.GenerateContentConfig(tools=afc_callables(registry, executor=executor))
response = await client.aio.models.generate_content(
    model="gemini-2.5-flash", contents="Shipping a laptop to the US?", config=config
)
What How
Declared schema each property keeps its Gemini schema; optional ones carry default (null when they have none), because genai marks every property without a default as required
Parameter names schema property names; ones that are not Python identifiers are sanitised (X-Trace → X_Trace, class → p_class) and mapped back
Validation the spec's own (genai's type check is bypassed)
Errors genai turns any exception into an error reply to the model; CancelledError propagates
Approval the port must decide at once (AutoApprove, CallbackApproval); AFC cannot pause. For approval, use run_function_calls
Call id and run AFC hands the function no call id: each call gets a fresh id and, outside an adapter run (current_context()), its own run, so run guards such as once_per_run do not span calls

AFC is being removed from models.generate_content in genai 3.0, so the manual path is the durable one.

What the schema looks like

The schema goes in parameters_json_schema.

  • Keywords Gemini does not accept are dropped. It keeps type, description, properties, required, enum, format, minimum, maximum, items, minItems, maxItems, anyOf, additionalProperties and nullable.
  • default is removed. Each argument's default is written into its description (Default: "c".).
  • Optional fields stay optional (not in required).

Your pydantic model still validates every call, so dropped keywords never weaken checking.