Google Gemini
Use Solwyn with Google Gemini — sync and async examples, thinking normalization, model support
import os
from google import genai
from solwyn import Solwyn
gc = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
client = Solwyn(
gc,
api_key=os.environ["SOLWYN_API_KEY"],
)
response = client.models.generate_content(
model="gemini-2.0-flash",
contents="Explain how a CPU works in two sentences.",
)
print(response.text)
client.close()Setup
Google Gemini uses the google-genai SDK (the current, recommended package). Install it separately:
pip install google-genaiSolwyn also auto-detects the legacy google-generativeai package. Both are supported -- use google-genai for new code.
Create a genai.Client with your Gemini API key, then wrap it with Solwyn. The call surface uses client.models.generate_content():
import os
from google import genai
from solwyn import Solwyn
gc = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
with Solwyn(
gc,
api_key=os.environ["SOLWYN_API_KEY"],
) as client:
response = client.models.generate_content(
model="gemini-2.0-flash",
contents="Explain how a CPU works in two sentences.",
)
print(response.text)Async usage
Use an async genai.Client with AsyncSolwyn:
import asyncio
import os
from google import genai
from solwyn import AsyncSolwyn
async def main():
gc = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
async with AsyncSolwyn(
gc,
api_key=os.environ["SOLWYN_API_KEY"],
) as client:
response = await client.models.generate_content(
model="gemini-2.0-flash",
contents="Explain how a CPU works in two sentences.",
)
print(response.text)
asyncio.run(main())Supported models
The SDK recognizes Google Gemini models by the gemini-* prefix:
| Prefix | Examples |
|---|---|
gemini-* | gemini-2.0-flash, gemini-2.5-pro, gemini-3-flash-preview |
Google is priced for all five modalities — text, embedding, image, audio, and video. See the provider × modality matrix.
Thinking token normalization
Google reports thinking tokens (thoughts_token_count) separately from the model's candidate output (candidates_token_count). The candidates count does not include thoughts. Solwyn normalizes these:
normalized output_tokens = candidates_token_count + thoughts_token_count| Normalized field | Google API source |
|---|---|
input_tokens | response.usage_metadata.prompt_token_count |
output_tokens | response.usage_metadata.candidates_token_count + response.usage_metadata.thoughts_token_count |
reasoning_tokens | response.usage_metadata.thoughts_token_count |
cached_input_tokens | response.usage_metadata.cached_content_token_count |
tool_use_input_tokens | response.usage_metadata.tool_use_prompt_token_count |
usage_metadata, not usage
Google Gemini exposes usage data on response.usage_metadata, not response.usage like OpenAI and Anthropic. Solwyn handles this automatically -- you do not need to change how you access the response.
Embeddings
client.models.embed_content(...) is intercepted, budget-checked, and recorded as a cost event with modality embedding. Google exposes no usage block on embeddings, so token counts are length-estimated locally and flagged is_estimated — an irreversible integer count, never the input text.
gemini-embedding-2 is deliberately unbilled: its card prices each media variant (text, image, audio, video) separately, which the flat token-priced embedding lane cannot represent. Calls to it resolve through the deliberately-unbilled fail-open path — allowed, visible, never guessed at.
Image generation
Two Google image surfaces are tracked:
- Imagen —
client.models.generate_images(...)is intercepted and budget-checked before the request fromconfig.number_of_images, then recorded as a cost event with modalityimageand priced per image at the model's flat $/image rate. - Image-output chat models —
generate_contentreports Google's per-modality token buckets, so an image-output model such asgemini-3-pro-imagehas its image tokens (reported asimage_input_tokens/image_output_tokens) priced at the model's image rate, alongside its text tokens.
Only token counts and the image count leave your process — never the prompt and never the generated image bytes.
Audio
Gemini's audio models — text-to-speech models such as gemini-2.5-flash-preview-tts and the native-audio models — produce audio through generate_content and report Google's per-modality token buckets. Their audio tokens (reported as audio_input_tokens / audio_output_tokens) are priced at the model's audio rate, alongside any text tokens.
Only token counts leave your process — never the prompt and never the generated audio bytes.
Google's music model lyria-3 is deliberately unbilled: its per-song card grids on a duration selector the wire contract does not carry, so rather than misprice it, calls resolve through the deliberately-unbilled fail-open path.
Video
client.models.generate_videos(...) (Veo) is intercepted, budget-checked, and recorded as a cost event with modality video. The pre-flight budget check is exact — config.duration_seconds at the config.resolution variant, priced at that variant's per-second rate — so an over-budget generation is denied before Google is called.
Video generation is asynchronous. generate_videos returns a long-running operation that carries no usage, and Solwyn passes that operation through untouched — you poll it as usual. Because the operation offers no billable basis and Google does not charge for failed or blocked generations, billing settles at initiation: the cost event is recorded at request time from the requested duration and resolution and is always flagged is_estimated, a deliberate and conservative over-count. Google publishes no default duration, so a call with no config.duration_seconds is recorded but left unpriced — never a guessed duration and never a silent $0.
Only the requested duration and the resolution selector leave your process — never the prompt, never a reference image, never the generated video.
Client detection
The SDK detects Google Gemini clients by checking if the client's module path contains google.genai or google.generativeai (two Google SDK packages). Both are supported.
Passthrough attributes
Any attribute not intercepted by Solwyn is passed through to the underlying genai.Client. You can still access any Google Gemini API method directly.