Python Client library implementing the OpenResponses specification
Project description
openresponses-impl-client-google
Python client library for Google Gemini implementing the OpenResponses interface.
Overview
This package exposes a BaseResponsesClient-compatible Gemini client:
- Non-streaming: returns
ResponseResource - Streaming: returns
AsyncIterator[ResponseStreamingEvent] - Request model:
CreateResponseBody
It uses the Google GenAI SDK (google-genai) underneath, but normalizes requests and responses to OpenResponses models from openresponses-impl-core.
Installation
uv add openresponses-impl-client-google
Dependencies:
- Python
>=3.12 google-genai>=1.72.0openresponses-impl-core>=0.1.0
Basic Usage
from openresponses_impl_core.models.openresponses_models import CreateResponseBody
from openresponses_impl_client_google.client.gemini_responses_client import GeminiResponsesClient
client = GeminiResponsesClient(
model="gemini-3-flash-preview",
google_api_key="YOUR_API_KEY", # optional if GOOGLE_API_KEY / GEMINI_API_KEY is set
)
payload = CreateResponseBody(
input="Hello",
stream=False,
)
response = await client.create_response(payload=payload)
print(response.output)
Streaming:
payload = CreateResponseBody(
input="Explain recursion briefly.",
stream=True,
)
event_stream = await client.create_response(payload=payload)
async for event in event_stream:
print(event.type)
Special Handling
System and developer instructions are merged
Gemini does not consume OpenResponses instructions, system messages, and developer messages in the same shape as OpenAI Responses API.
This client merges:
payload.instructionsinputitems withrole="system"inputitems withrole="developer"
into a single Gemini system_instruction string.
Tool follow-up requires the same client instance
Gemini tool follow-up uses function_response, which requires the original function name.
OpenResponses function_call_output only carries call_id, so this client keeps an in-memory mapping:
call_id -> function name
Implications:
- Tool follow-up must happen on the same
GeminiResponsesClientinstance. - Stateless replay from
previous_response_idis not implemented. - If a
function_call_outputarrives for an unknowncall_id, the client raisesValueError.
Media input is normalized best-effort
OpenResponses input_image, input_file, and input_video are automatically converted to Gemini API format as follows:
1. Data URI format (URIs starting with data:)
# Example: data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...
Message(
role="user",
content=[
InputImage(image_url="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...")
]
)
- Base64-encoded data is decoded and sent as byte array
- Uses Gemini API's
types.Part.from_bytes(data=..., mime_type=...) - Use case: Embedding small images/videos directly in requests
2. URI format (GCS, YouTube, HTTPS, etc.)
# Example: gs://bucket/video.mp4, https://example.com/image.jpg
Message(
role="user",
content=[
InputVideo(video_url="gs://my-bucket/video.mp4")
]
)
- URIs are sent as-is as references
- Uses Gemini API's
types.Part.from_uri(file_uri=..., mime_type=...) - Use case: Referencing files on GCS, YouTube videos, external URLs
3. Automatic MIME type inference
- MIME type is automatically inferred from URI or filename extension (e.g.,
.mp4→video/mp4) - Falls back to
application/octet-streamif inference fails - Warning log is emitted on fallback
4. Files uploaded via Files API
# Pre-upload using Google GenAI SDK
video_file = genai_client.files.upload(file="path/to/video.mp4")
# Pass directly to input
payload = CreateResponseBody(input=[video_file, ...])
- Uploaded
Fileobjects can be included directly ininput - Gemini API handles them appropriately internally
Recommended method for video input (Files API)
For long or large video files, we recommend pre-uploading via Files API before using this library:
from google import genai
from openresponses_impl_core.models.openresponses_models import CreateResponseBody, Message
from openresponses_impl_client_google.client.gemini_responses_client import GeminiResponsesClient
import time
# 1. Upload video using Google GenAI SDK
genai_client = genai.Client()
video_file = genai_client.files.upload(file="path/to/video.mp4")
# 2. Wait for processing completion (videos may require processing time)
while True:
video_file = genai_client.files.get(name=video_file.name)
if video_file.state != "PROCESSING":
break
time.sleep(2)
# 3. Analyze using OpenResponses client
responses_client = GeminiResponsesClient(
model="gemini-3-flash-preview",
google_api_key="YOUR_API_KEY",
)
payload = CreateResponseBody(
input=[
video_file, # Pass uploaded File object directly
Message(role="user", content="Summarize this video and list key points in bullet format.")
],
stream=False,
)
response = await responses_client.create_response(payload=payload)
print(response.output)
# 4. Cleanup (optional)
genai_client.files.delete(name=video_file.name)
Notes:
- File objects uploaded via Files API can be included directly in the
inputarray - Wait for video processing to complete (
PROCESSING→ACTIVE) before use - For small videos, you can also use
data:URIs or GCS URIs
Reasoning is mapped approximately
OpenResponses reasoning settings do not map 1:1 to Gemini.
Current behavior:
reasoning.effort="none"->thinking_budget=0reasoning.effort="low" | "medium" | "high"-> Geminithinking_levelreasoning.effort="xhigh"-> mapped toHIGHwith warningreasoning.summary-> ignored with warning
Response fields are partially synthesized
Gemini does not return an object identical to ResponseResource, so some fields are reconstructed from:
- request payload
- Gemini response metadata
- client-generated fallback IDs and timestamps
Examples:
idfalls back to a synthetic response ID if Gemini does not returnresponse_idtools,tool_choice,text,service_tier, and similar fields are echoed from the effective request- Gemini-specific metadata is stored under
metadata["gemini_*"]
OpenResponses Compatibility Notes
This client is intentionally best-effort. It keeps the OpenResponses public interface, but not every field can be represented natively by Gemini.
Supported well
- plain text input
- user / assistant message history
- function tools
- Gemini built-in tools expressed as OpenAI-style flat tool objects
- non-streaming responses
- basic streaming text responses
- JSON-schema-style structured output via Gemini
response_json_schema
Supported with translation
instructions,system,developer-> mergedsystem_instructionfunction_call-> Geminifunction_callfunction_call_output-> Geminifunction_response- reasoning text / thought parts -> OpenResponses
reasoning - usage fields -> mapped from Gemini
usage_metadata
Warning-and-ignore fields
These fields are preserved in the normalized ResponseResource when possible, but are not sent to Gemini as functional request controls:
previous_response_idstorebackgroundparallel_tool_callsmax_tool_callstruncationincludesafety_identifierprompt_cache_key
Unsupported or partially supported behavior
-
previous_response_id- Gemini request execution ignores it.
- The field is kept in normalized responses for interface compatibility only.
-
generic tools
- Gemini built-in tools are converted dynamically when
tool.typematches a supportedgoogle.genai.types.Toolfield with object-style configuration. - When at least one Gemini built-in tool is present, the client automatically sets
tool_config.include_server_side_tool_invocations = true. - Built-in tool config follows the OpenAI-style flat request shape, for example:
{"type": "google_maps", "enable_widget": true}{"type": "file_search", "file_search_store_names": ["fileSearchStores/STORE_ID"], "top_k": 5}{"type": "code_execution"}
descriptionis preserved in the echoed request, but is not sent to Gemini as executable tool config.- Unknown generic tool types are ignored with warning.
- Known Gemini built-in tool types with invalid config fail fast with
ValueError. - Built-in outputs are still normalized best-effort; dedicated loops such as computer-use action roundtrips are not yet mapped to OpenResponses-specific output items.
- Gemini built-in tools are converted dynamically when
-
item types without Gemini equivalents
item_referenceis ignored with warning.- OpenResponses input
reasoningitems are ignored with warning.
-
exact tool-choice fidelity
- The client translates tool choice to Gemini function-calling config best-effort.
- OpenResponses/Core model serialization may already be lossy for some
tool_choiceshapes before the Gemini client sees them.
Streaming Semantics
Streaming is normalized to a minimal OpenResponses event set.
Currently emitted event families:
response.createdresponse.output_item.addedresponse.content_part.addedresponse.output_text.deltaresponse.output_text.doneresponse.content_part.doneresponse.output_item.doneresponse.reasoning.deltaresponse.reasoning.doneresponse.function_call_arguments.done- terminal event:
response.completedresponse.incompleteresponse.failed
error
Notes:
- Gemini stream chunks are merged cumulatively before event translation.
- Delta calculation is prefix-based best-effort.
- If Gemini emits unexpected chunk shapes, the client may emit an
errorevent.
Status Mapping
Gemini finish state is mapped to OpenResponses status as follows:
- prompt blocked / no candidate ->
failed MAX_TOKENS->incompletewithreason="max_tokens"STOP->completed- response containing function calls ->
completed - other Gemini finish reasons ->
incompletewith the Gemini reason string
Authentication
You can pass google_api_key= directly, or rely on the Google SDK environment variable resolution.
Common environment variables:
GOOGLE_API_KEYGEMINI_API_KEY
Logging Behavior
This client uses warnings for non-fatal incompatibilities.
You should expect warning logs when:
- unsupported OpenResponses fields are provided
- unsupported generic tool types are provided
- unsupported item/content types are provided
- MIME type inference falls back to
application/octet-stream reasoning.summaryis requestedreasoning.effort="xhigh"is downgraded
Testing
Run tests with:
UV_CACHE_DIR="$PWD/.uv_cache" uv run pytest -q
This now includes live Gemini API integration tests under test/integration_test/.
Live test behavior:
- the test module reads
GOOGLE_API_KEYorGEMINI_API_KEYfrom the repo-root.envfile if the variables are not already exported - if the key is missing or invalid, pytest fails immediately during test collection
- normal
pytest -qrequires network access and may incur Gemini API cost - the live suite covers non-stream, stream, function-call follow-up, JSON schema output, and reasoning smoke paths
Summary
Use this package when you want Gemini behind the OpenResponses interface, but keep in mind:
- it is interface-compatible, not wire-compatible
- several OpenResponses controls are emulated or ignored
- tool follow-up depends on client-local in-memory state
- streaming is normalized to a practical subset rather than a perfect Gemini-to-Responses projection
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openresponses_impl_client_google-0.2.3.tar.gz.
File metadata
- Download URL: openresponses_impl_client_google-0.2.3.tar.gz
- Upload date:
- Size: 27.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b174b40c6eb2b5ef5cce9e0057708ddbcea138820e54808dd93d229c6bcc9f4e
|
|
| MD5 |
878cf8c5b5de285effadf0456518f6f6
|
|
| BLAKE2b-256 |
cf8a37d1d2d0f1589058bf28c09b7f8b42952a966dde9ea84571f6139d85e278
|
File details
Details for the file openresponses_impl_client_google-0.2.3-py3-none-any.whl.
File metadata
- Download URL: openresponses_impl_client_google-0.2.3-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
98b354327b83e410e45214c03dfd6ca97b150714e30a71f7d07080f7c64a92a6
|
|
| MD5 |
de6895f6a5bf2585cfc8ad9e5945ea14
|
|
| BLAKE2b-256 |
7e0c2a90046db6cb1d1a5c078fca55ea07ef6ff5cef098908ce0a76cb9c61e4b
|