Skip to content

Google GenAI Python SDK ​

The official google-genai package, on plugboard's Gemini API.

Connect ​

The SDK adds /v1beta itself, so the base URL is the plugboard origin:

python
from google import genai
from google.genai import types

client = genai.Client(api_key=PLUGBOARD_KEY, http_options=types.HttpOptions(base_url="http://127.0.0.1:4010"))

Chat ​

py
response = client.models.generate_content(model=model, contents="Reply with exactly: pong", config=types.GenerateContentConfig(max_output_tokens=1024))

Tool calls ​

Gemini calls carry no id: the function name pairs a functionResponse with its call.

py
tools = [
    types.Tool(
        function_declarations=[
            types.FunctionDeclaration(
                name="get_weather",
                description="Current weather for a city",
                parameters_json_schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
            )
        ]
    )
]
config = types.GenerateContentConfig(tools=tools, max_output_tokens=1024)
contents = [types.Content(role="user", parts=[types.Part(text="What is the weather in Paris? Use the tool.")])]
first = client.models.generate_content(model=model, contents=contents, config=config)
call = first.function_calls[0] if first.function_calls else None
# Send the model's turn back as it came, then the result as a functionResponse. Gemini calls carry no id: the name pairs them.
turn = list(contents)
if first.candidates and first.candidates[0].content:
    turn.append(first.candidates[0].content)
if call:
    turn.append(types.Content(role="user", parts=[types.Part.from_function_response(name=call.name, response={"temp_c": 18, "sky": "cloudy"})]))
second = client.models.generate_content(model=model, contents=turn, config=config)

Streaming ​

py
text = ""
finish = None
usage = None
for chunk in client.models.generate_content_stream(model=model, contents="Reply with exactly: pong", config=types.GenerateContentConfig(max_output_tokens=1024)):
    text += chunk.text or ""  # each chunk carries the next text delta
    if chunk.candidates and chunk.candidates[0].finish_reason:
        finish = chunk.candidates[0].finish_reason.value.lower()
    if chunk.usage_metadata:
        usage = chunk.usage_metadata.model_dump(exclude_none=True)

A streamed tool call arrives whole, in one chunk:

py
tools = [
    types.Tool(
        function_declarations=[
            types.FunctionDeclaration(
                name="get_weather",
                description="Current weather for a city",
                parameters_json_schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
            )
        ]
    )
]
call = None
finish = None
for chunk in client.models.generate_content_stream(
    model=model,
    contents="What is the weather in Paris? Use the tool.",
    config=types.GenerateContentConfig(tools=tools, max_output_tokens=1024),
):
    if chunk.function_calls:
        call = chunk.function_calls[0]
    if chunk.candidates and chunk.candidates[0].finish_reason:
        finish = chunk.candidates[0].finish_reason.value.lower()

Images ​

py
# A 32px red PNG (examples/_shared/red32.png). Part.from_bytes takes the raw bytes and the mime type.
response = client.models.generate_content(
    model=model,
    contents=[
        types.Content(
            role="user",
            parts=[
                types.Part(text="What color is this image? One word."),
                types.Part.from_bytes(data=RED_PNG.read_bytes(), mime_type="image/png"),
            ],
        )
    ],
    config=types.GenerateContentConfig(max_output_tokens=1024),
)

Structured output ​

py
# Gemini enforces response_json_schema on the model's output, unlike plugboard's forced-tool route for other APIs.
schema = {
    "type": "object",
    "properties": {"calories": {"type": "number"}, "protein_g": {"type": "number"}},
    "required": ["calories", "protein_g"],
    "additionalProperties": False,
}
response = client.models.generate_content(
    model=model,
    contents="Estimate: 2 boiled eggs and a slice of toast.",
    config=types.GenerateContentConfig(response_mime_type="application/json", response_json_schema=schema, max_output_tokens=1024),
)

Files ​

client.files.upload(file="doc.pdf") uploads through plugboard; pass the returned file to generate_content like any other part. See Files.

Model names ​

A # in a model name must be sent as %23 (openai/gpt-5.6-luna%23low); a / goes as it is. Aliases avoid both. See Gemini model names.

Limits ​

  • Errors raise ClientError with the HTTP status; the body's status is Google's name (UNAUTHENTICATED, NOT_FOUND).
  • The schema in structured output is not enforced by the provider. Validate the answer.