module documentation

Temporal-aware AsyncClient.

TemporalAsyncClient is an AsyncClient whose every Gemini API call runs as a Temporal activity. It builds and wraps a private BaseApiClient that dispatches HTTP through workflow.execute_activity instead of making network calls, so the SDK's request-formatting code (including the AFC loop) runs in the workflow while the real genai.Client with real credentials only exists on the worker side inside the activity.

Construct it from within a workflow:

client = TemporalAsyncClient(activity_config=...)
response = await client.models.generate_content(...)

This ensures:

  • No credential fetching or refreshing happens in the workflow.
  • No auth material (tokens, API keys) appears in Temporal event history.
  • The SDK's AFC (automatic function calling) loop runs in the workflow, so activity_as_tool() wrappers work naturally.

AsyncFiles and AsyncFileSearchStores are replaced with shims that run upload/download as activities; interactions and agents — which bypass BaseApiClient via a vendored HTTP client — are likewise replaced with activity-backed shims; webhooks is not supported in workflows and raises.

Replay determinism

The SDK's request-formatting and automatic-function-calling loop run in the workflow, so they must be deterministic. A survey of google.genai found no time/uuid/random use on the generate_content/AFC path; the SDK's own non-deterministic code (HTTP retry backoff, the interactions/agents vendored client, local tokenizer temp paths) runs only inside activities on the worker. The SDK exposes no time/id provider hooks to override, and none are needed.

The one in-workflow exception is batches.create on Vertex AI: when display_name/dest are omitted the SDK auto-generates them from a timestamp + UUID (_common.timestamped_unique_name), which is not replay-safe. Pass explicit display_name and dest when creating Vertex batch jobs from a workflow.

Class _TemporalAsyncModels AsyncModels that closure-wraps bound-method tools before each call.
Function _closure_if_bound_method Return a plain-function wrapper for a bound method, else tool as-is.
Function _wrap_bound_method_tools Closure-wrap any bound-method tools in config (see _closure_if_bound_method).
def _closure_if_bound_method(tool: object) -> object: (source)

Return a plain-function wrapper for a bound method, else tool as-is.

google-genai >= 2.8.0 deep-copies the request config internally (config.model_copy(deep=True)). copy.deepcopy clones a bound method's __self__, so a workflow-method tool would run against a throwaway clone of the workflow instance and its in-workflow state mutation would be lost. deepcopy leaves plain functions untouched, so wrapping the method in a closure keeps the tool bound to the real instance. Plain functions and activity_as_tool wrappers are already closures and pass through unchanged.

def _wrap_bound_method_tools(config: types.GenerateContentConfigOrDict | None) -> types.GenerateContentConfigOrDict | None: (source)

Closure-wrap any bound-method tools in config (see _closure_if_bound_method).

Returns config unchanged when it holds no bound-method tools; otherwise returns a shallow copy with the tools list rewritten, so the caller's config is never mutated.