Skip to content

antigravity-ls-proxy: reasoning_effort 'default'/'name' resolves to a non-enum model — MODEL_MAP metadata keys pass the family lookup #686

Closed

Description

@dezren39

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_effort is 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. medium on gemini-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 of bin/muse-proxy.test.mjs (spawn the proxy against a stub language server via ANTIGRAVITY_LS_ADDRESS/ANTIGRAVITY_CSRF_TOKEN and capture the model the proxy sends) or an equivalent unit test of the resolver is acceptable.

Activity

  1. added a commit that references this issue on Oct 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions