Skip to content

fix(models): reject unknown model aliases instead of assuming OpenAI - #84

Open
Agnik47 wants to merge 1 commit into
supermemoryai:mainfrom
Agnik47:fix/unrecognised-judge-model
Open

fix(models): reject unknown model aliases instead of assuming OpenAI#84
Agnik47 wants to merge 1 commit into
supermemoryai:mainfrom
Agnik47:fix/unrecognised-judge-model

Conversation

@Agnik47

@Agnik47 Agnik47 commented Aug 14, 2026

Copy link
Copy Markdown

Fixes #73

The bug

getModelConfig (src/utils/models.ts) ended with an unconditional fallback:

// Default fallback
return { id: alias, provider: "openai", displayName: alias, supportsTemperature: true, ... }

The orchestrator derives the judge provider from that return value:

// src/orchestrator/index.ts:90-91
const judgeModelInfo = resolveModel(judgeModel)
const judgeName = judgeModelInfo.provider as JudgeName

So any alias absent from MODEL_CONFIGS and not matching a known prefix was routed to OpenAI with the unrecognised string used verbatim as the model id. --judge sonnet-4-5 (the registry key is sonnet-4.5), opus4.5, gemini2.5-pro — all became OpenAI judges.

The failure then lands as either a 401 when OPENAI_API_KEY is unset, blaming a provider the user never selected, or a 404 for a model they believe is Anthropic. Both arrive in the evaluate phase, after a full ingest has been paid for, and neither message says the alias was unrecognised.

Casing made it worse. The registry lookup used alias.toLowerCase() but every prefix branch tested the original casing, so GPT-4.5 missed the registry and the gpt- branch, landing in the fallback with supportsTemperature: true — wrong for a reasoning model.

The fix

  • Match on lowerAlias in every prefix branch, so casing no longer decides whether a provider is recognised.
  • Return the normalised id rather than the caller's spelling — all three providers reject a mis-cased model id, so GPT-4.5 must go out as gpt-4.5.
  • Replace the fallback with a thrown error naming the alias and listing listAvailableModels(). Reaching that point means the alias matched no registry entry and no provider prefix (gpt-, o1/o3/o4, claude-, gemini-), so its provider genuinely cannot be inferred — guessing is precisely what produced the misattribution.

The error is raised at Orchestrator.run startup, before any phase executes, so an unusable judge now fails in a second rather than after a full ingest.

I also added one line resolving the answering model next to the judge. It is otherwise first resolved inside the answer phase (phases/answer.ts:28), so a typo there would still have cost a full ingest before surfacing. This is slightly beyond the reported issue but it is the same bug on the same code path, and leaving it would have made the fix only half-effective.

A note on the throw

This turns a silent misroute into a hard failure, so it is worth being explicit about the blast radius. The prefix branches already cover every model family the repo supports, and all call sites are internal (judges/{openai,anthropic,google}.ts, phases/answer.ts, orchestrator/index.ts) — none passes arbitrary user input anywhere except the --judge / --answering-model CLI flags and the equivalent API fields, which is exactly where failing fast is wanted. A genuinely new model from a supported provider still resolves through its prefix; only strings with no inferable provider now throw.

Verification

  • bun test — 19 new tests in src/utils/models.test.ts pass.
  • Against unfixed main, 10 of the 19 fail (registry-casing, prefix-casing, id normalisation, and every unrecognised-alias case); they fail cleanly rather than hanging.
  • Covered: registry hits, each prefix family, gemini-3 vs gemini- temperature defaults, o-series reasoning params, mixed-case inference, id normalisation, six unrecognised aliases including empty and whitespace, and a guard that every registered alias still resolves.
  • tsc --noEmit clean for both changed files.

src/utils/models.ts and src/orchestrator/index.ts already fail prettier --check on main; I left that alone and confirmed every line I added is within the configured printWidth.

`getModelConfig` ended with an unconditional fallback returning
`provider: "openai"` with the caller's string as the model id. The
orchestrator derives the judge *provider* from that return value
(`orchestrator/index.ts:90-91`), so any alias not in `MODEL_CONFIGS` and
not matching a known prefix was silently routed to OpenAI.

`--judge sonnet-4-5` (the registry key is `sonnet-4.5`), `opus4.5` or
`gemini2.5-pro` therefore became an OpenAI judge using the typo verbatim.
That surfaces either as a 401 when `OPENAI_API_KEY` is unset — pointing at
the wrong provider entirely — or a 404 for a model the user believes is
Anthropic, and only in the evaluate phase, after a full ingest has been
paid for. Neither message mentions that the alias was unrecognised.

The prefix branches also tested the original casing while the registry
lookup used `alias.toLowerCase()`, so `GPT-4.5` missed the registry *and*
the `gpt-` branch and landed in the fallback with `supportsTemperature:
true` — wrong for a reasoning model.

Match on `lowerAlias` in every branch, and return the normalised id rather
than the caller's spelling, since providers reject a mis-cased model id.
Replace the fallback with an error naming the alias and listing
`listAvailableModels()`: reaching it means the alias matched no registry
entry and no provider prefix, so its provider genuinely cannot be inferred
and guessing is what caused the misattribution.

Also resolve the answering model alongside the judge at the top of
`Orchestrator.run`. It is otherwise first resolved in the answer phase, so
an unusable alias would still only surface after ingest.

Fixes supermemoryai#73
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A misspelled or unrecognised judge model silently becomes an OpenAI judge instead of failing

1 participant