Summary
Gemini is nominally supported in Continue, but several independent defects make it fail in practice — most visibly for users behind corporate proxies, users on newly created API keys, and anyone relying on embeddings. A common report is "the Gemini API works fine in Google's Python sample, but the same key fails when I put it in my Continue config."
This issue tracks a set of small, independently reviewable PRs that fix each layer. I've split the work into four scoped PRs rather than one large change, since they touch different packages and some overlap with existing (currently stalled) PRs — I'd be glad to coordinate with those authors before merge. This issue is the shared anchor; each PR links back here.
Changes reviewers will want to know about up front
- Token usage — non-breaking:
completion_tokens now counts reasoning ("thoughts") tokens for thinking models, matching the OpenAI-compatible usage spec. No shape change; values rise for thinking-enabled models, which were previously undercounted (Google's totalTokenCount already included them).
- Live-test model IDs — non-breaking:
main.test.ts's live suite moves from gemini-2.5-* to the -latest aliases so it runs for newly created API keys; the trade-off is the suite is no longer pinned to a fixed model.
- New public export — additive:
getProxyFromEnv is re-exported from @continuedev/fetch (an existing internal helper) so the adapter shares one source of truth for env-proxy interpretation.
Root causes (each verified against the source)
- Config silently ignored on the chat path. The Gemini adapter built the
@google/genai client with only apiKey — apiBase, requestOptions.headers, timeout, and all proxy/TLS options were dropped for chat/stream while embed() honored them. Python's requests honors HTTPS_PROXY/REQUESTS_CA_BUNDLE out of the box, which is why a plain sample works where a gateway-fronted Continue config does not.
- API key leaked into a URL.
core/llm/fetchModels.ts passed the Gemini key as a ?key= query parameter when listing models — keys in URLs leak via server logs, proxies, and referer headers.
- Errors rendered as raw JSON / "Unknown error." Gemini quota/
RESOURCE_EXHAUSTED responses carry the real message double-nested in JSON that nothing extracted, so failures were undiagnosable.
embed() broken for bare model names. new URL("<model>:batchEmbedContents", apiBase) parsed the model name as a URL scheme and discarded apiBase; the response was also read from the wrong field (batchEmbedContents vs Google's embeddings). Anyone enabling @codebase/embeddings hit this.
- Docs pointed at gated model IDs. New API keys cannot invoke
gemini-2.5-* ("no longer available to new users") even though the API still lists them; examples now use the -latest aliases (checked against the live API).
The four PRs
| PR |
Scope |
Files |
Related upstream work |
Depends on |
| 1 — Gemini adapter |
Honor apiBase/headers/timeout/proxy/TLS on all paths; surface nested errors; SDK-native embed; thinking-token accounting; serialize the fetch swap |
packages/openai-adapters/**, one export line in packages/fetch/src/index.ts |
Addresses #12008; builds on #12014 (credit to its author) and extends it to proxy/TLS on all paths; incorporates the embed fix from #12517 |
— |
| 2 — Key-in-URL |
Send x-goog-api-key header instead of ?key= in fetchModels |
core/llm/fetchModels.ts (+ test) |
Same fix as #12190 (credit to its author) |
— |
| 3 — CLI errors |
Extract nested provider error messages in formatError |
extensions/cli/src/util/formatError.ts (+ test) |
Adapter/CLI counterpart to #13014 (which fixes the GUI layer) |
— |
| 4 — Docs |
Provider pages + a new end-to-end Gemini guide, real model IDs, role matrix, proxy/gateway config |
docs/** |
— |
PR 1 (documents its behavior) |
PRs 1–3 share no files and can be reviewed and merged in any order. PR 4 follows PR 1. The Closes #12008 keyword will live in PR 1's own description (a tracking issue doesn't auto-close).
Verification
Every behavioral change is backed by tests that run against a no-mocks local rig, plus manual runs against the real Google API (with a real key, outside CI):
- Automated, no mocks: proxy routing (real local proxy, request hit-counters), TLS and mutual-TLS (self-signed mini-CA, passphrase-protected key), custom headers on the wire,
embed through a proxy, env-var-proxy engagement with NO_PROXY bypass, config-over-env precedence, timeout abort, and cross-config credential isolation.
- Manual, against the live API: Google's own live model tests pass once pointed at current model IDs (including tool calls, on two models);
fetchModels returns real models with the key absent from the URL.
Other notes for reviewers
- openssl is used by the TLS test rig at
beforeAll (present on the CI image; same approach as packages/fetch's existing e2e tests).
- The "Supported roles" table is a new docs convention applied to the Gemini and Vertex pages; happy to extend it to the other providers if that's wanted.
Summary
Gemini is nominally supported in Continue, but several independent defects make it fail in practice — most visibly for users behind corporate proxies, users on newly created API keys, and anyone relying on embeddings. A common report is "the Gemini API works fine in Google's Python sample, but the same key fails when I put it in my Continue config."
This issue tracks a set of small, independently reviewable PRs that fix each layer. I've split the work into four scoped PRs rather than one large change, since they touch different packages and some overlap with existing (currently stalled) PRs — I'd be glad to coordinate with those authors before merge. This issue is the shared anchor; each PR links back here.
Changes reviewers will want to know about up front
completion_tokensnow counts reasoning ("thoughts") tokens for thinking models, matching the OpenAI-compatible usage spec. No shape change; values rise for thinking-enabled models, which were previously undercounted (Google'stotalTokenCountalready included them).main.test.ts's live suite moves fromgemini-2.5-*to the-latestaliases so it runs for newly created API keys; the trade-off is the suite is no longer pinned to a fixed model.getProxyFromEnvis re-exported from@continuedev/fetch(an existing internal helper) so the adapter shares one source of truth for env-proxy interpretation.Root causes (each verified against the source)
@google/genaiclient with onlyapiKey—apiBase,requestOptions.headers,timeout, and all proxy/TLS options were dropped for chat/stream whileembed()honored them. Python'srequestshonorsHTTPS_PROXY/REQUESTS_CA_BUNDLEout of the box, which is why a plain sample works where a gateway-fronted Continue config does not.core/llm/fetchModels.tspassed the Gemini key as a?key=query parameter when listing models — keys in URLs leak via server logs, proxies, and referer headers.RESOURCE_EXHAUSTEDresponses carry the real message double-nested in JSON that nothing extracted, so failures were undiagnosable.embed()broken for bare model names.new URL("<model>:batchEmbedContents", apiBase)parsed the model name as a URL scheme and discardedapiBase; the response was also read from the wrong field (batchEmbedContentsvs Google'sembeddings). Anyone enabling@codebase/embeddings hit this.gemini-2.5-*("no longer available to new users") even though the API still lists them; examples now use the-latestaliases (checked against the live API).The four PRs
apiBase/headers/timeout/proxy/TLS on all paths; surface nested errors; SDK-nativeembed; thinking-token accounting; serialize the fetch swappackages/openai-adapters/**, one export line inpackages/fetch/src/index.tsx-goog-api-keyheader instead of?key=infetchModelscore/llm/fetchModels.ts(+ test)formatErrorextensions/cli/src/util/formatError.ts(+ test)docs/**PRs 1–3 share no files and can be reviewed and merged in any order. PR 4 follows PR 1. The
Closes #12008keyword will live in PR 1's own description (a tracking issue doesn't auto-close).Verification
Every behavioral change is backed by tests that run against a no-mocks local rig, plus manual runs against the real Google API (with a real key, outside CI):
embedthrough a proxy, env-var-proxy engagement withNO_PROXYbypass, config-over-env precedence, timeout abort, and cross-config credential isolation.fetchModelsreturns real models with the key absent from the URL.Other notes for reviewers
beforeAll(present on the CI image; same approach aspackages/fetch's existing e2e tests).