Add First-Class Gemini SDK Integration to Contrib#1378
Add First-Class Gemini SDK Integration to Contrib#1378JasonSteving99 wants to merge 1 commit intomainfrom
Conversation
500ab1e to
b770922
Compare
|
Semgrep found 1 Risk: Affected versions of litellm are vulnerable to Exposure of Sensitive Information to an Unauthorized Actor / Use of Password Hash With Insufficient Computational Effort / Use of a Broken or Risky Cryptographic Algorithm. LiteLLM exposes password hashes via authenticated API endpoints and its login flow accepts a stored SHA-256 hash as a valid password, allowing an authenticated user to steal another user's hash and log in as that user, resulting in full authentication bypass and privilege escalation. Fix: Upgrade this library to at least version 1.83.0 at sdk-python/uv.lock:2102. Reference(s): GHSA-69x8-hrgq-fjj8 Semgrep found 1 Risk: Affected versions of litellm are vulnerable to Incorrect Authorization. This vulnerability stems from inadequate enforcement of access control policies, allowing authenticated users to perform actions beyond their intended privilege level and potentially alter sensitive system configurations or access restricted resources. Fix: Upgrade this library to at least version 1.83.0 at sdk-python/uv.lock:2102. Reference(s): GHSA-53mr-6c8q-9789, CVE-2026-35029 Semgrep found 1 Risk: Affected versions of google-adk are vulnerable to Missing Authentication for Critical Function. Google Agent Development Kit (ADK) contains a missing authentication flaw that can let an unauthenticated remote attacker reach code-injection paths and execute arbitrary code on the server running the ADK instance, including ADK Web deployments on Python, Cloud Run, and GKE. Fix: Upgrade this library to at least version 1.28.1 at sdk-python/uv.lock:982. Reference(s): GHSA-rg7c-g689-fr3x, CVE-2026-4810 |
19b495c to
6763054
Compare
# Temporal Integration for the Google Gemini SDK
This adds a first-class integration that lets users call the Gemini SDK's `AsyncClient` directly from within Temporal workflows. Every API call and tool invocation becomes a durable Temporal activity — giving full crash recovery, visibility in workflow event history, and replay safety — while keeping credentials entirely on the worker side.
## How it works
The integration shims three layers of the Gemini SDK so that workflows can use `client.models`, `client.files`, `client.file_search_stores`, `client.chats`, and all other SDK modules naturally:
### `TemporalApiClient` (`_temporal_api_client.py`)
A `BaseApiClient` subclass that replaces the SDK's HTTP layer. Instead of making network calls, `async_request` and `async_request_streamed` serialize the request and dispatch it through `workflow.execute_activity`. The real HTTP call happens inside the activity on the worker, where the actual `genai.Client` with real credentials lives. Sync methods raise immediately. Per-request `http_options` are validated (non-serializable fields like `httpx_client` are rejected), and `timeout` is mapped to Temporal's `start_to_close_timeout`.
### `TemporalAsyncFiles` / `TemporalAsyncFileSearchStores` (`_temporal_files.py`, `_temporal_file_search_stores.py`)
Subclasses of `AsyncFiles` and `AsyncFileSearchStores` that override `upload`, `download`, `register_files`, and `upload_to_file_search_store` to dispatch the entire operation as a Temporal activity. This avoids filesystem access (`os` module) and credential token refresh in the workflow sandbox. Methods like `get`, `delete`, `list` are inherited and work through the `TemporalApiClient`'s `async_request` activity. File uploads accept `str` paths (resolved on the worker), `os.PathLike`, or `io.IOBase` (bytes serialized across the activity boundary).
### `TemporalAsyncClient` (`_temporal_async_client.py`)
An `AsyncClient` subclass that wires in `TemporalAsyncFiles` and `TemporalAsyncFileSearchStores`. All other SDK modules (`models`, `tunings`, `caches`, `batches`, `live`, `tokens`, `operations`) are inherited unchanged since they only use `async_request` under the hood.
### `GeminiPlugin` (`_gemini_plugin.py`)
A `SimplePlugin` that registers all activities, configures the Pydantic data converter, and passes `google.genai` through the workflow sandbox. Users pass a fully configured `genai.Client` — the plugin never constructs one itself. An optional `extra_credentials` parameter supports operations like `register_files` that need separate GCS credentials.
### `activity_as_tool` (`workflow.py`)
Wraps any `@activity.defn` function so it looks like a regular async callable to Gemini's automatic function calling (AFC). When the model decides to call the tool, the SDK invokes the wrapper, which dispatches through `workflow.execute_activity`. Users can also pass plain workflow methods directly as tools — these run in-workflow without an activity.
### Batched streaming
`generate_content_stream` is supported via a batched approach: the `async_request_streamed` activity collects all chunks from the real streaming response and returns them as a list. The workflow-side `TemporalApiClient` yields them back as an async generator so the SDK sees the expected interface.
## Usage
```python
# Worker side
client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
plugin = GeminiPlugin(client)
# Workflow side
@workflow.defn
class MyWorkflow:
@workflow.run
async def run(self, query: str) -> str:
client = gemini_client()
response = await client.models.generate_content(
model="gemini-2.5-flash",
contents=query,
config=types.GenerateContentConfig(
tools=[activity_as_tool(my_tool)],
),
)
return response.text
```
## Testing
31 integration tests covering:
- Basic `generate_content` and multi-chunk streaming
- AFC tool calling (single-arg, multi-arg, workflow methods, sequential multi-tool, failure propagation)
- Per-request `http_options` propagation (headers, api_version, base_url)
- File upload via str path and `io.BytesIO`, file download
- File search store upload
- Multi-turn chat via `client.chats`
- `TemporalAsyncClient` wiring verification
- `TemporalApiClient` error paths (sync raises, low-level upload/download raises)
- `activity_as_tool` validation and signature preservation
- A full end-to-end integration test that exercises all real activity implementations (generate, stream, file upload, download, store upload, RAG query, store delete) with a mocked `genai.Client` — ensuring the actual activity code in `_gemini_activity.py` is covered, not just the workflow-side shims.
6763054 to
bf58318
Compare
Temporal Integration for the Google Gemini SDK
This adds a first-class integration that lets users call the Gemini SDK's
AsyncClientdirectly from within Temporal workflows. Every API call and tool invocation becomes a durable Temporal activity — giving full crash recovery, visibility in workflow event history, and replay safety — while keeping credentials entirely on the worker side.How it works
The integration shims three layers of the Gemini SDK so that workflows can use
client.models,client.files,client.file_search_stores,client.chats, and all other SDK modules naturally:TemporalApiClient(_temporal_api_client.py)A
BaseApiClientsubclass that replaces the SDK's HTTP layer. Instead of making network calls,async_requestandasync_request_streamedserialize the request and dispatch it throughworkflow.execute_activity. The real HTTP call happens inside the activity on the worker, where the actualgenai.Clientwith real credentials lives. Sync methods raise immediately. Per-requesthttp_optionsare validated (non-serializable fields likehttpx_clientare rejected), andtimeoutis mapped to Temporal'sstart_to_close_timeout.TemporalAsyncFiles/TemporalAsyncFileSearchStores(_temporal_files.py,_temporal_file_search_stores.py)Subclasses of
AsyncFilesandAsyncFileSearchStoresthat overrideupload,download,register_files, andupload_to_file_search_storeto dispatch the entire operation as a Temporal activity. This avoids filesystem access (osmodule) and credential token refresh in the workflow sandbox. Methods likeget,delete,listare inherited and work through theTemporalApiClient'sasync_requestactivity. File uploads acceptstrpaths (resolved on the worker),os.PathLike, orio.IOBase(bytes serialized across the activity boundary).TemporalAsyncClient(_temporal_async_client.py)An
AsyncClientsubclass that wires inTemporalAsyncFilesandTemporalAsyncFileSearchStores. All other SDK modules (models,tunings,caches,batches,live,tokens,operations) are inherited unchanged since they only useasync_requestunder the hood.GeminiPlugin(_gemini_plugin.py)A
SimplePluginthat registers all activities, configures the Pydantic data converter, and passesgoogle.genaithrough the workflow sandbox. Users pass a fully configuredgenai.Client— the plugin never constructs one itself. An optionalextra_credentialsparameter supports operations likeregister_filesthat need separate GCS credentials.activity_as_tool(workflow.py)Wraps any
@activity.defnfunction so it looks like a regular async callable to Gemini's automatic function calling (AFC). When the model decides to call the tool, the SDK invokes the wrapper, which dispatches throughworkflow.execute_activity. Users can also pass plain workflow methods directly as tools — these run in-workflow without an activity.Batched streaming
generate_content_streamis supported via a batched approach: theasync_request_streamedactivity collects all chunks from the real streaming response and returns them as a list. The workflow-sideTemporalApiClientyields them back as an async generator so the SDK sees the expected interface.Usage
Testing
31 integration tests covering:
generate_contentand multi-chunk streaminghttp_optionspropagation (headers, api_version, base_url)io.BytesIO, file downloadclient.chatsTemporalAsyncClientwiring verificationTemporalApiClienterror paths (sync raises, low-level upload/download raises)activity_as_toolvalidation and signature preservationgenai.Client— ensuring the actual activity code in_gemini_activity.pyis covered, not just the workflow-side shims.