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 | _ |
AsyncModels that closure-wraps bound-method tools before each call. |
| Function | _closure |
Return a plain-function wrapper for a bound method, else tool as-is. |
| Function | _wrap |
Closure-wrap any bound-method tools in config (see _closure_if_bound_method). |
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.
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.