Skip to main content

Google Gemini SDK Integration for Temporal

Release stage: Public Preview.

Overview

This plugin lets you use the Google Gemini SDK (google-genai) inside Temporal workflows with durable execution. Every Gemini API call becomes a Temporal activity, so model calls, tool calls, file operations, interactions, and managed agents are retried, recorded in history, and survive worker restarts.

Key properties:

  • Credentials never enter the workflow. The real genai.Client lives only on the worker, inside activities; no API keys or tokens appear in event history.
  • The SDK's automatic function calling (AFC) loop runs in the workflow, so tool wrappers (activity_as_tool) work naturally — no manual agent loop.
  • Temporal owns retries. Configure them via the activity retry_policy; the SDK's own retry loop is rejected to avoid double-retry (see Retries & errors).

Install

uv add temporalio-google-genai

Hello World

import os
from datetime import timedelta

from google import genai
from google.genai import types

from temporalio import activity, workflow
from temporalio.client import Client
from temporalio.google_genai import (
    GoogleGenAIPlugin,
    TemporalAsyncClient,
    activity_as_tool,
)
from temporalio.worker import Worker
from temporalio.workflow import ActivityConfig


# ---- a tool, as a normal Temporal activity (runs on the worker) ----
@activity.defn
async def get_weather(city: str) -> str:
    return f"It's sunny in {city}."


# ---- the workflow (runs in the Temporal sandbox) ----
@workflow.defn
class WeatherAgent:
    @workflow.run
    async def run(self, prompt: str) -> str:
        client = TemporalAsyncClient()
        response = await client.models.generate_content(
            model="gemini-2.5-flash",
            contents=prompt,
            config=types.GenerateContentConfig(
                tools=[
                    activity_as_tool(
                        get_weather,
                        activity_config=ActivityConfig(
                            start_to_close_timeout=timedelta(seconds=30),
                        ),
                    ),
                ],
            ),
        )
        return response.text or ""


# ---- worker setup (outside the sandbox: real client + credentials) ----
async def main() -> None:
    gemini = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
    plugin = GoogleGenAIPlugin(gemini)

    client = await Client.connect("localhost:7233", plugins=[plugin])
    async with Worker(
        client,
        task_queue="gemini",
        workflows=[WeatherAgent],
        activities=[get_weather],
    ):
        result = await client.execute_workflow(
            WeatherAgent.run,
            "What's the weather in Tokyo?",
            id="weather-1",
            task_queue="gemini",
        )
        print(result)

Construct TemporalAsyncClient inside the workflow; construct the real genai.Client and GoogleGenAIPlugin on the worker.

What this plugin gives you

Surface Workflow API Runs as
Model calls client.models.generate_content / generate_content_stream activity (AFC loop in workflow)
Tools activity_as_tool(fn, ...) one activity per tool call
Files client.files.upload / download activity
File search client.file_search_stores.upload_to_file_search_store activity
Interactions client.interactions.create / get / cancel / delete whole-operation activity
Managed agents client.agents.create / get / list / delete whole-operation activity
MCP (client-side) TemporalMcpClientSession(name) in tools=[...] list_tools / call_tool activities

Streamed responses are batched: the activity drains the stream and the workflow iterates the collected chunks/events. client.webhooks is not supported in workflows and raises.

Tool calling

activity_as_tool wraps any @activity.defn function as a Gemini tool. When the model calls it, the AFC loop (running in the workflow) dispatches it as a durable activity:

activity_as_tool(
    get_weather,
    activity_config=ActivityConfig(start_to_close_timeout=timedelta(seconds=30)),
)

A timeout is required — activity_config must set start_to_close_timeout or schedule_to_close_timeout (Temporal needs one; there is no default for tools).

MCP support

Install the optional dependency for client-side MCP:

uv add "temporalio-google-genai[mcp]"

This adapter requires MCP Python SDK v1. The base package does not require MCP and can be installed alongside MCP v2 integrations. Server-side MCP on Vertex AI and the Interactions API do not require this extra.

Client-side MCP (Gemini Developer API) is wired through the plugin: register the server on the worker and reference it by name in the workflow.

from contextlib import asynccontextmanager
import sys

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

from temporalio.google_genai import TemporalMcpClientSession


# ---- worker: a factory yielding a connected, initialized session ----
@asynccontextmanager
async def weather_mcp():
    params = StdioServerParameters(command=sys.executable, args=["weather_server.py"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            yield session


plugin = GoogleGenAIPlugin(
    genai.Client(api_key=os.environ["GOOGLE_API_KEY"]),
    mcp_servers={"weather": weather_mcp},
    mcp_connection_idle_timeout=timedelta(minutes=5),
)


# ---- workflow: reference the server by name in the tools list ----
@workflow.defn
class McpAgent:
    @workflow.run
    async def run(self, prompt: str) -> str:
        client = TemporalAsyncClient()
        session = TemporalMcpClientSession(
            "weather",
            activity_config=ActivityConfig(start_to_close_timeout=timedelta(seconds=30)),
        )
        response = await client.models.generate_content(
            model="gemini-2.5-flash",
            contents=prompt,
            config=types.GenerateContentConfig(tools=[session]),
        )
        return response.text or ""

The MCP connection lives on the worker (pooled, idle-evicted); the workflow only carries the server name. Tool discovery and calls run as {name}-list-tools / {name}-call-tool activities, so the full tool parameter schema reaches the model. Set cache_tools=True to list a server's tools once per workflow instead of per turn.

Streaming

generate_content_stream works as usual — the workflow iterates chunks (batched from the activity). To let an external consumer (a chat UI) observe chunks in real time while the workflow runs durably, set streaming_topic on the client and host a WorkflowStream in the workflow. Each streamed GenerateContentResponse is published to that topic as it arrives:

from temporalio.contrib.workflow_streams import WorkflowStream


@workflow.defn
class StreamingAgent:
    @workflow.init
    def __init__(self, prompt: str) -> None:
        self.stream = WorkflowStream()  # required when streaming_topic is set

    @workflow.run
    async def run(self, prompt: str) -> str:
        client = TemporalAsyncClient(streaming_topic="gemini")
        text = []
        async for chunk in await client.models.generate_content_stream(
            model="gemini-2.5-flash", contents=prompt,
        ):
            text.append(chunk.text or "")
        return "".join(text)

Consume the stream from outside the workflow:

from temporalio.contrib.workflow_streams import WorkflowStreamClient


async def consume(client, workflow_id):
    stream = WorkflowStreamClient.create(client, workflow_id)
    async for item in stream.subscribe(
        ["gemini"], result_type=types.GenerateContentResponse,
    ):
        print(item.data.text, end="", flush=True)

The workflow's own iteration is unchanged (it still receives batched chunks for the SDK to parse); the topic is purely for external real-time observation. If streaming_topic is set but the workflow hosts no WorkflowStream, the call raises GoogleGenAIError. Tune flush cadence with TemporalAsyncClient(streaming_topic=..., streaming_batch_interval=...) (default 100ms).

Retries & errors

Temporal owns retries. Configure them with the activity retry_policy via activity_config. The plugin rejects the SDK's own retry config so retries don't compound:

  • Constructing the plugin with a genai.Client that has http_options.retry_options raises ValueError.
  • Setting http_options.retry_options on a per-request call raises GoogleGenAIError.

API-call activities classify failures: transient statuses (408, 429, 5xx) stay retryable (the activity's retry_policy applies); other statuses (e.g. 4xx) are non-retryable so the workflow fails fast.

Vertex AI

Pass vertexai=True to both the worker-side genai.Client and the workflow-side TemporalAsyncClient. On the workflow side you must also set project and location explicitly:

# worker
genai.Client(vertexai=True, project="my-project", location="us-central1")

# workflow
TemporalAsyncClient(vertexai=True, project="my-project", location="us-central1")

Normally the SDK auto-discovers project/location from the environment (credentials, ADC, metadata server). That discovery would be non-deterministic and break replay. Setting them by hand keeps it deterministic.

Composing with other plugins

GoogleGenAIPlugin is a temporalio.plugin.SimplePlugin; pass it in the plugins=[...] list alongside others (e.g. OpenTelemetry). It contributes a Pydantic data converter, the Gemini activities, a sandbox-passthrough config for google.genai (and mcp), and registers GoogleGenAIError as a workflow failure type. When composing data converters, construct the plugins so their converters are compatible.

Metadata

Release files for temporalio-google-genai 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for temporalio-google-genai 0.1.0
File Size Uploaded
temporalio_google_genai-0.1.0.tar.gz 31.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for temporalio-google-genai 0.1.0
File Interpreter ABI Platform
temporalio_google_genai-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 74.9 kB

Release files / temporalio_google_genai-0.1.0.tar.gz

Download URL temporalio_google_genai-0.1.0.tar.gz
Size 31.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ce90309de31f8080930a7de1fec16ac9542419325b420e487c88aa38f9ab1f53
BLAKE2b-256 checksum
How to use checksums
3d2aeeaf7cab52aa6500e7b325378fd20347dba4a905293959ffb91b0468b35f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release files / temporalio_google_genai-0.1.0-py3-none-any.whl

Download URL temporalio_google_genai-0.1.0-py3-none-any.whl
Size 43.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
48dac47903277100434aa8687a71dcf93bfd652f49c43ac0671872f6b175680e
BLAKE2b-256 checksum
How to use checksums
a00e979153cfdba59d0be935e3235ffd3388c043ec4b5d519bc99f9927661763
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 7, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page