# Google (OpenAI-compat) — gemma-4-31b grade card

OpenAI chat/completions shim over generateContent · serves `gemma-4-31b-it` as gemma-4-31b · probed live 2026-07-26

Google's OpenAI-compatibility shim, serving the same model as the native provider — graded on what the shim delivers.

HTML version: [https://inferencecanary.com/google-openai/gemma-4-31b/](https://inferencecanary.com/google-openai/gemma-4-31b/) · matrix: [https://inferencecanary.com/index.md](https://inferencecanary.com/index.md)

## Grades


| Axis | Grade | Basis |
|---|---|---|
| Harness | D− | A − 3 red − 1 yellow, per the published ladder |
| Billing | OK | Reported usage consistent with bytes on the wire. Footnote: completion_tokens under-counts its own delivered content — under-bills in the customer's favor. |
| Caching | testing | Context reuse across tool-call turns is still being probed on this provider. |
| Faithfulness | 29/29 | Agreement with the reference provider over the 29-cell deterministic battery (temperature 0, thinking disabled). |

## Flags

- **[red · harness] Four providers drop replayed reasoning: the model never sees its own prior thinking** — The gemma-4 chat template renders reasoning replayed on model turns, and the model card mandates preserving thinking across tool-call turns — the reference provider reproduces this live. These four providers delete it before templating: on a continuation after a tool call, the model re-derives or invents instead of recalling, and no error is raised. AWS Bedrock's Responses dialect is the sharpest case: it defines an explicit input item for replayed reasoning, accepts it without error, and never renders it to the model. (details: [/replayed-reasoning-dropped/index.md](https://inferencecanary.com/replayed-reasoning-dropped/index.md))
- **[red · harness] Google's OpenAI-compat shim has no reasoning channel; thought text leaks into content** — The shim never populates reasoning or reasoning_content. With thinking on — its default when no knob is sent — the model's thought text arrives inside literal <thought> tags in the answer field, with no structured way to separate them. Google's native protocol serves the same model with a proper thought channel; the shim discards it. (details: [/findings/index.md](https://inferencecanary.com/findings/index.md#google-openai-no-reasoning-channel))
- **[red · harness] Images in tool results: rejected, 500'd, or silently blinded on 7 of 11 providers** — The model reads images returned by tools — the reference provider delivers them, perception-judged, and three OpenAI-compat providers prove the standard shape works. Seven providers fail on schema choice, not model limits: four reject the request (400/422), SambaNova returns a deterministic 500, and AWS Bedrock (Responses) is the worst class — it returns 200 and the model never sees the image, answering confidently about content it never saw. Google's own shim rejects a capability Google's native protocol serves. (details: [/findings/index.md](https://inferencecanary.com/findings/index.md#tool-result-images))
- **[yellow · harness] Google's shim mislabels streamed tool calls finish_reason: "stop"** — The same deterministic tool prompt yields finish_reason "tool_calls" non-streaming but "stop" streaming. A harness that dispatches on finish_reason behaves differently depending on transport. (details: [/findings/index.md](https://inferencecanary.com/findings/index.md#google-openai-finish-reason))

