Repository navigation
Description
Bug
bin/antigravity-ls-proxy.mjs — resolveModelEnum, step 3 (family-with-effort lookup, lines 138–140):
let effort = (requestedEffort || family.default || 'medium').toLowerCase().trim();
if (!family[effort]) effort = family.default || Object.keys(family)[0];
const enumVal = family[effort];The guard only checks that family[effort] is truthy. A family entry in MODEL_MAP is not just effort → enum: it also carries the metadata keys default (an effort name, e.g. 'medium') and name (a display name, e.g. 'Gemini 3.8 Flash'). A client that sends either of those strings as reasoning_effort / effort (both are read from the request body at the chat-completions handler) therefore "resolves" successfully — to a value that is not a model enum at all. That bogus value is then sent to the language server as model: in GetModelResponse and as plannerConfig.requestedModel.model in cascade mode, where it can only fail (or be misinterpreted) server-side, instead of falling back to the family's default effort the way a genuinely unknown effort does.
Evidence (verified this run)
The function and MODEL_MAP were extracted verbatim from current main and driven directly:
| request | result modelEnum |
|---|---|
gemini-3.8-flash, effort "default" |
"medium" ❌ (an effort name, not an enum) |
gemini-3.8-flash, effort "name" |
"Gemini 3.8 Flash" ❌ (the display name) |
gemini-3.1-pro, effort "default" |
"high" ❌ |
gemini-3.8-flash, effort "high" |
"MODEL_PLACEHOLDER_M318" ✅ |
gemini-3.1-pro, effort "medium" (not offered) |
"MODEL_PLACEHOLDER_M37" ✅ correct default fallback |
gemini-3.8-flash, no effort |
"MODEL_PLACEHOLDER_M319" ✅ |
So an unrecognized effort falls back correctly, but an effort that happens to collide with a MODEL_MAP metadata key slips through with a garbage "enum".
Why it matters
- The proxy's own contract (step 5 of the same function) is "refuse rather than quietly run a different model than the one asked for" — this path does the opposite: it quietly runs with a non-model value.
reasoning_effortis client-controlled in the OpenAI-shaped API; values like"default"are plausible from generic clients/configs. The failure surfaces downstream as an opaque language-server error naming a model the user never asked for, far from the real cause.
Suggested fix
In step 3, accept an effort only when its mapped value is actually an enum (e.g. a string starting with MODEL_, matching every effort value in MODEL_MAP); otherwise fall back to family.default (and validate that fallback the same way). Equivalently, restrict the accepted effort names to the known effort keys (low / medium / high / extra-low) before indexing the family object.
Acceptance criteria
resolveModelEnum('gemini-3.8-flash', 'default')and('gemini-3.8-flash', 'name')return the family's default-effort enum (MODEL_PLACEHOLDER_M319), never"medium"/"Gemini 3.8 Flash".- Existing correct behaviour is unchanged: valid efforts resolve to their enums, an unoffered effort (e.g.
mediumongemini-3.1-pro) still falls back to the family default, and unknown models still throw the 400 "Unknown model" error. - A regression test covers the metadata-key efforts (
default,name) — this proxy currently has no test file, so a black-box test in the style ofbin/muse-proxy.test.mjs(spawn the proxy against a stub language server viaANTIGRAVITY_LS_ADDRESS/ANTIGRAVITY_CSRF_TOKENand capture themodelthe proxy sends) or an equivalent unit test of the resolver is acceptable.
Activity
Merge pull request #687 from developing-today/creator/686-ls-proxy-ef…