Google Gemini¶
The native generateContent protocol. Google's OpenAI-compatibility layer is documented
as beta and ignores parameters it does not implement, while thinking levels, response
schemas, safety settings, and context caching are native-only or better supported.
Setup¶
import anyinfer as ai
client = ai.Client(
[
ai.ProviderSettings.of("gemini", api_key="env://GEMINI_API_KEY"),
]
)
result = client.generate(prompt, target="gemini:gemini-2.5-flash")
The key is sent as x-goog-api-key. google:, google-gemini:, and ai-studio: are
accepted as aliases of gemini:.
Reasoning¶
Gemini names thinking levels rather than budgeting tokens, so the four normalized effort levels map straight across:
result = client.generate(prompt, target="gemini:gemini-2.5-pro", reasoning="high")
print(result.usage.reasoning_tokens) # thoughts, reported separately
print(result.usage.output_tokens) # answer + thoughts, because both bill as output
Gemini's own candidatesTokenCount excludes thoughts even though they bill at the
output rate, so AnyInfer reports output_tokens as the sum; cost stays right, and
reasoning_tokens keeps the breakdown visible.
Thinking text arrives as ReasoningDelta events and is excluded from result.text:
with client.stream(prompt, target="gemini:gemini-2.5-flash", reasoning="medium") as stream:
for event in stream:
if isinstance(event, ai.ReasoningDelta):
print("[thinking]", event.text, end="")
elif isinstance(event, ai.TextDelta):
print(event.text, end="")
Models that cannot disable thinking (2.5 Pro, and the Gemini 3 family) clamp a low request upward server-side rather than failing.
Structured Output¶
SUMMARY = {
"type": "object",
"properties": {
"headline": {"type": "string"},
"points": {"type": "array", "items": {"type": "string"}},
},
"required": ["headline", "points"],
}
result = client.generate(article, target="gemini:gemini-2.5-flash", schema=SUMMARY)
print(result.structured["headline"])
Gemini's responseSchema accepts an OpenAPI subset, and rejects the entire request on
an unknown keyword. AnyInfer projects the schema down to the accepted subset before
sending (dropping things like $schema, pattern, and unevaluatedProperties), and
then validates the response against the original
schema. Some wire-level strictness is lost, never result correctness.
Tool Calling¶
result = client.generate(prompt, target="gemini:gemini-2.5-flash", tools=[lookup_spec])
for call in result.tool_calls:
print(call.name, call.arguments)
Gemini emits complete function calls rather than streamed argument fragments, and
supports several calls in one turn. Tool results are sent back on a user turn as
functionResponse parts; the adapter handles that translation, so
run_tools() works the same as everywhere else.
Embeddings¶
Gemini embeds through batchEmbedContents, so batches are native:
result = client.embed(
["What is deep learning?"],
target="gemini:gemini-embedding-2",
dimensions=768, # 128-3072; both models default to 3072
)
- The legacy
gemini-embedding-001accepts task types, mapped frominput_type(query→RETRIEVAL_QUERYand so on). The currentgemini-embedding-2documents no task types (prompt instructions replace them), so aninput_typethere is never sent and the result says so in a warning. - No batch ceiling is documented, so requests above the library's
sanity ceiling are refused rather than split at a
guessed size; set
BatchPolicy.max_items_overrideafter independently verifying a limit. - There is no reranking endpoint on this API.
Discovery¶
The model listing reports real limits, so context windows carry discovered
provenance:
for model in client.models("gemini"):
caps = model.capabilities
if caps and caps.context_window:
print(model.id, caps.context_window.value, caps.context_window.provenance)
Reaching Native Features¶
Anything AnyInfer does not model (context caching, safety settings, grounded search, numeric thinking budgets) passes straight through the escape hatch:
client.generate(
prompt,
target="gemini:gemini-2.5-flash",
provider_options={
"gemini": {
"cachedContent": "cachedContents/abc123",
"safetySettings": [
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_ONLY_HIGH"}
],
}
},
)
Content Filtering¶
A prompt Gemini blocks returns no candidates at all, with the reason on
promptFeedback. That surfaces as finish_reason == "content_filter" rather than an
empty successful answer, and a Route with
content_policy_targets can redirect it to a differently-governed provider.
Multimodal Inputs¶
Images, documents, and audio use native inlineData blocks for bytes and fileData for
remote references. Support and limits remain model-specific.
Wire Contract¶
For the exact request/response fields this adapter depends on, see contracts/gemini.md.