Add First-Class Gemini SDK Integration to Contrib#1378
Draft
JasonSteving99 wants to merge 5 commits intomainfrom
Draft
Add First-Class Gemini SDK Integration to Contrib#1378JasonSteving99 wants to merge 5 commits intomainfrom
JasonSteving99 wants to merge 5 commits intomainfrom
Conversation
More to be done to improve quality of the integration. This currently contains a dumping ground of in progress files that will need to be cleaned up before merged into main.
Replace the previous approach (wrapping generate_content in a Temporal activity with a manual agentic loop) with HTTP-level interception that lets the Gemini SDK's native automatic function calling (AFC) drive the conversation. TemporalHttpxClient overrides httpx.AsyncClient.send() so every HTTP call the Gemini SDK makes becomes a durable Temporal activity, recorded in the workflow event history and subject to Temporal retry/timeout semantics. activity_as_tool() converts @activity.defn functions into Gemini-compatible callables dispatched via workflow.execute_activity, making each tool invocation independently durable. Credentials (x-goog-api-key) are stripped from serialized requests before they reach event history and re-injected from os.environ inside the activity. OAuth/authorization headers are intentionally left in place since they are short-lived and cannot be reconstructed. The package __init__.py uses lazy imports for all httpx-dependent symbols (GeminiPlugin, TemporalHttpxClient, temporal_http_options) so that sandbox-safe imports like activity_as_tool never trigger an httpx load. GeminiPlugin stores the pre-built genai.Client in a passthrough'd module so workflows can retrieve it without os.environ access.
GeminiPlugin now accepts genai.Client kwargs (api_key, vertexai, project, etc.) directly and creates the client internally with temporal_http_options(), eliminating the need for users to manually wire up the HTTP transport. Activity timeout/retry configuration is exposed as explicit constructor parameters. Also adds TYPE_CHECKING imports for better IDE support in __init__.py and _client_store.py.
…story Replace the manual credential stripping/re-injection pattern (strip x-goog-api-key before serialization, re-inject from env vars in the activity) with a proper PayloadCodec that encrypts sensitive header values within HttpRequestData payloads using Fernet. This approach is better because: - All headers (including authorization) are now protected, not just x-goog-api-key - The Temporal UI still shows the full request structure with all non-sensitive fields readable — no Codec Server needed - No env var dependency in the activity for credential re-injection New module _sensitive_fields_codec.py provides three components: - TypeTaggingPydanticJSONConverter: writes the Python type name into payload metadata so the codec can identify which model produced it - SensitiveFieldsCodec: encrypts/decrypts specific keys within specific dict fields of registered Pydantic models - make_sensitive_fields_data_converter: factory wiring both into a single DataConverter GeminiPlugin gains two new parameters: - sensitive_activity_fields: which header keys to encrypt (defaults to x-goog-api-key and authorization) - sensitive_activity_fields_encryption_key: the Fernet key Encryption is on by default; pass sensitive_activity_fields=None to disable for local dev.
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
temporalio.contrib.google_gemini_sdk, a drop-in integration that makes the Google Gemini SDK fully durable inside Temporal workflows — every LLM HTTP call and every tool invocation becomes a Temporal activity, with minimal changes to how users write Gemini SDK code.This is intended to improve on and replace the previously demonstrated approach included in Google's official Gemini docs from our cross-promotion with Google, which required users to manually manage the agentic loop, build a tool registry, and wrap each model call in an activity. The new integration eliminates all that ceremony and allows users to get the full value out of the Gemini SDK while still running on Temporal.
How it works
GeminiPlugin— A Temporal Worker plugin that creates and owns thegenai.Client, registers the HTTP transport activity, configures the Pydantic data converter, and sets up sandbox passthrough modules. Users pass the samekwargsthey'd pass togenai.Client().httpx.AsyncClient(TemporalHttpxClient) intercepts every HTTP request the Gemini SDK makes and dispatches it as agemini_api_callactivity viaworkflow.execute_activity. This makes all model calls (including streaming) durable and visible in event history.activity_as_tool()— Converts any@activity.defnfunction into a Gemini-compatible tool callable. Gemini's automatic function calling (AFC) drives the agentic loop natively — no manualwhileloop orrun_agent()helper needed.get_gemini_client()— Lets workflows retrieve the pre-builtgenai.Clientfrom inside the sandbox withoutos.environaccess.SensitiveFieldsCodecencrypts credential headers (x-goog-api-key,authorization) within activity payloads usingFernet, so they never appear in plaintext in Temporal's event history. All other fields remain human-readable in the UI without a Codec Server.TODO: