Files
OmniRoute/docs/fix-opencode-context.md
Hernan Javier Ardila Sanchez dbd70ddd1f fix(catalog): make combos auto-compute context_length for any provider id form (#3417)
Integrated into release/v3.8.17
2026-06-08 18:50:21 -03:00

109 lines
4.0 KiB
Markdown

# Fix: OpenCode combo context window detection
## Bug
OpenCode v1's `opencode.json` requires an explicit `limit.context` (and
`limit.output`) for every model. Without these fields, OpenCode's heuristic
kicks in and reports a wrong context window.
The OmniRoute `/v1/models` catalog is the single source of truth for context
windows. The bug was that some combos were published to the catalog WITHOUT
a computed `context_length`, so any OpenCode client pulling the catalog got
no answer for them.
## Root cause
The "Opencode FREE Omni" combo references four `opencode/<model>` targets
(`big-pickle`, `deepseek-v4-flash-free`, `minimax-m3-free`,
`nemotron-3-super-free`). The catalog's `buildComboCatalogMetadata` computes
the combo's `context_length` as the **minimum of its targets' known
contexts**, but every target's lookup was returning `null`.
The lookup chain in `getCanonicalModelMetadata`:
1. `getSyncedCapability("opencode", "big-pickle")` → returns `null` because
the DB row is stored under `provider = "opencode-zen"`, not `"opencode"`.
2. `getRegistryModel("opencode", "big-pickle")` → returns `null` because
`PROVIDER_MODELS["opencode"]` (and `["oc"]`) don't have static entries
for these models.
3. `getModelSpec("big-pickle")` → returns `null` (no static spec).
All three lookups fail → metadata is `null` → combo has no context.
Why was the DB row under `"opencode-zen"` and not `"opencode"`?
`MODELS_DEV_PROVIDER_MAP["opencode"]` was `["opencode-zen"]` only — a
historical one-way mapping. New syncs continued to write under the alias
side of the pair, while the catalog & combo targets use the canonical id
side.
## Fix (3 layers, in this order)
### 1. `src/lib/modelsDevSync.ts` — symmetric `mapProviderId` mapping
```ts
// Before:
opencode: ["opencode-zen"],
"opencode-go": ["opencode-go"],
// After:
opencode: ["opencode", "opencode-zen"],
"opencode-go": ["opencode-go", "opencode-zen"],
```
Now models.dev data lands under BOTH the canonical id and the historical
alias, so any future sync keeps the lookup paths in sync.
### 2. `src/lib/modelsDevSync.ts` — alias-aware fallback in `getSyncedCapability`
The runtime fix that takes effect **immediately**, without waiting for a
re-sync. Existing DB rows under `"opencode-zen"` are now found when
callers pass `"opencode"` (or vice-versa).
```ts
const SYNCED_CAPABILITY_FALLBACK_ALIASES: Record<string, string[]> = {
opencode: ["opencode-zen"],
"opencode-zen": ["opencode"],
"opencode-go": ["opencode-zen"],
};
```
### 3. `src/lib/cli-helper/config-generator/opencode.ts` — drop the hardcoded 128K fallback
The previous band-aid hardcoded `FALLBACK_CONTEXT_LENGTH = 128_000` for
models whose context was unknown. That's wrong: combos like "Opencode
FREE Omni" should report **200K** (the min of their 200K targets), not
the universal 128K default.
The generator now:
- Uses the catalog as the **single source of truth** for `limit.context`.
- Emits the model **without** `limit.context` if the catalog has no entry
— OpenCode's own heuristic applies and the user can fix the upstream.
- **Throws** if the catalog fetch fails outright — the CLI catches and
surfaces the error. We never silently write a stale opencode.json.
## Deployment steps (for the operator)
1. Pull the latest from the branch:
`git fetch && git checkout fix/opencode-context-window`
2. Rebuild OmniRoute: `npm run build`
3. Restart the OmniRoute server (kill the running process and re-run).
4. Trigger a models.dev sync from the Settings → Models.dev panel
(or POST `/api/settings/models-dev` with `{"action": "sync"}`).
5. Re-run the opencode.json generator (the CLI command or the
`scripts/regen-opencode-config.ts` script).
After step 5, `Opencode FREE Omni`'s `limit.context` will be **200000**
and every other combo will reflect its targets' min context.
## Verification
After the rebuild, hit `GET /v1/models` and inspect the response:
```bash
curl -s http://localhost:20128/v1/models \
-H "Authorization: Bearer $API_KEY" \
| jq '.data[] | select(.id == "Opencode FREE Omni") | .context_length'
# → 200000
```