Google exposes Gemini through three different surfaces. Rolter has a provider kind for each:
| Kind | Upstream surface | When to use it |
|---|---|---|
gemini | The hosted OpenAI-compatible shim | Simplest option; no translation at all |
gemini_native | :generateContent / :streamGenerateContent | Native features the shim does not expose |
gemini_interactions | POST {api_base}/interactions | Stateful, agentic threads with server-side history |
This page covers gemini_interactions.
Configuration#
api_base points at the API version root — no /openai suffix and no /interactions suffix, Rolter appends that itself. The key is read from the environment (inline keys are rejected) and sent as x-goog-api-key, exactly as for gemini_native.
What Rolter translates#
Clients keep speaking their usual dialect. POST /v1/chat/completions, POST /v1/responses and POST /v1/messages are all mapped onto the single interactions endpoint.
Request
| Client field | Interactions field |
|---|---|
messages / input turns | input items with role user / model and typed content parts |
system / developer messages, instructions | system_instruction |
assistant tool_calls | function_call input items (call_id, name, arguments) |
tool messages / function_call_output | function_result input items |
temperature, top_p, max_tokens, stop, tool_choice | generation_config |
tools | flat tools entries (type: "function", name, description, parameters) |
stream | stream |
Response
The interaction’s steps[] are folded back into the client’s shape: model_output steps become assistant text, function_call steps become tool calls, and thought steps are dropped — reasoning traces are never surfaced as output. usage maps to the client’s token counters.
Streaming works the same way: interaction.created, step.start, step.delta and interaction.completed events are converted into OpenAI chunks, Anthropic message events, or Responses events depending on the endpoint the client called.
Threading a conversation#
The Interactions API keeps conversation history server-side. Rolter does not store interaction ids — the thread is client-driven, which keeps the gateway stateless and lets any replica serve the next turn.
The interaction id comes back as the response id. Send it on the next request as:
previous_response_id— OpenAI Responses clients, where this field already existsprevious_interaction_id— any dialect, passed through verbatim
store is forwarded when set, so a client that opts out of retention keeps that control.
Because the thread lives upstream, a route that load-balances a gemini_interactions provider against providers of other kinds will lose the thread on failover. Keep interaction-threaded traffic on a route whose members are all gemini_interactions.
Unsupported endpoints#
The Interactions API only serves chat-shaped traffic. Requests to endpoints with no interactions equivalent — embeddings, audio — are rejected with a 400 gemini_interactions_unsupported error rather than being forwarded to an endpoint that cannot answer them. Route those models to a gemini or gemini_native provider instead.
Unsupported content parts#
Interactions carries a typed union of content parts. Rolter translates text / input_text and image_url / input_image; there is no opaque carrier for anything else.
A message containing a part rolter cannot translate — input_audio, a file or document part, or any new part type your client SDK starts sending — is rejected with 400 unsupported_content_part naming the part:
This is deliberate. Dropping the part would send Gemini a request missing content you supplied, and both Gemini and rolter would answer 200 — you would get a confident answer that ignored your attachment, with nothing in the logs to say so. The same rule applies to the gemini_native (generateContent) dialect.
To send a modality Interactions cannot carry, route that model to a provider whose dialect supports it.