paybondpaybond
Sign in

Gemini with agent-agnostic middleware

Use Paybond agent-agnostic interceptor with the raw Google AI SDK for Gemini function calling. For Google ADK apps, prefer the native Google ADK adapter.

Paybond ships a native Google ADK adapter for FunctionTool execute guards in TypeScript and Python. Use this page when you call Gemini through the raw Google AI SDK (function calling without ADK): bind a PaybondAgentRun, register side-effecting tools in paybond.policy.yaml, and wrap your function-call handlers with createPaybondGenericAgentConfig or paybond.instrument().

Model inference stays on the Google AI SDK. Paybond governs paid and side-effecting tools only — Harbor verify, approval holds, spend finalize, and auto-evidence.

Install

npm install @paybond/kit @google/genai
# or
pip install paybond-kit google-genai

Policy

Declare Gemini-exposed tools in paybond.policy.yaml and optionally fail closed on provider-native tools you do not execute locally:

version: 1
name: gemini-travel-agent-v1
default_deny: true

adapter:
  deny_provider_executed_tools: true

tools:
  travel.book_hotel:
    side_effecting: true
    max_spend_cents: 20000
    evidence_preset: cost_and_completion

  search.web:
    side_effecting: false

The adapter.deny_provider_executed_tools flag is consumed by AI SDK-based adapters (Vercel AI, Cloudflare Agents). For Gemini function calling, keep read-only tools unregistered or map only locally executed handlers.

TypeScript (Google GenAI SDK)

import { GoogleGenAI } from "@google/genai";
import { Paybond } from "@paybond/kit";

const paybond = await Paybond.open({ apiKey: process.env.PAYBOND_API_KEY! });
const gemini = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY! });

const instrumented = await paybond.instrument({
  policy: "./paybond.policy.yaml",
  tools: {
    "travel.book_hotel": async (args: { city: string; estimatedPriceCents: number }) =>
      bookHotel(args),
    "search.web": async (args: { query: string }) => searchWeb(args),
  },
  sandbox: true,
});

const run = instrumented.run;
const tools = instrumented.tools;

// When Gemini returns a functionCall, dispatch to the guarded tool by name:
async function handleGeminiFunctionCall(name: string, args: Record<string, unknown>) {
  const tool = tools.find((entry) => entry.name === name);
  if (!tool) {
    throw new Error(`unregistered tool: ${name}`);
  }
  return tool.execute({
    toolName: name,
    toolCallId: crypto.randomUUID(),
    arguments: args,
  });
}

Wire handleGeminiFunctionCall inside your chat loop after generateContent returns tool calls. Keep the Gemini client pointed at Google — do not proxy model traffic through Paybond Gateway.

Python

import os
import uuid

from google import genai
from paybond_kit import Paybond

paybond = await Paybond.open(api_key=os.environ["PAYBOND_API_KEY"])
client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])

result = await paybond.instrument(
    policy="./paybond.policy.yaml",
    framework="generic",
    tools={
        "travel.book_hotel": book_hotel,
        "search.web": search_web,
    },
    sandbox=True,
)

tools_by_name = {tool.name: tool for tool in result.agent_tools}

async def handle_gemini_function_call(name: str, args: dict) -> object:
    tool = tools_by_name.get(name)
    if tool is None:
        raise ValueError(f"unregistered tool: {name}")
    return await tool.execute(
        tool_name=name,
        tool_call_id=str(uuid.uuid4()),
        arguments=args,
    )

Roadmap: Google ADK vs raw Gemini SDK

For Google ADK apps (LlmAgent, FunctionTool, multi-agent graphs), use the shipped Google ADK adapter and /guides/google-adk-spend-controlscreatePaybondGoogleAdkConfig / create_paybond_google_adk_config replace manual function-call dispatch.

This agent-agnostic guide remains the supported path for raw Gemini SDK function calling and custom orchestrators that do not use ADK runners.