Skip to content

feat(routing): match model config names spelling-agnostically - #478

Merged
Destynova2 merged 1 commit into
mainfrom
feat/model-name-canonical-routing
Jul 26, 2026
Merged

feat(routing): match model config names spelling-agnostically#478
Destynova2 merged 1 commit into
mainfrom
feat/model-name-canonical-routing

Conversation

@Destynova2

Copy link
Copy Markdown
Contributor

The friction

The router canonicalizes an inbound model name — dots→dashes for known families,
so gpt-5.5 becomes gpt-5-5before routing. But the [[models]] lookup
then compared config names verbatim. A config entry spelled gpt-5.5 never
matched the request the router had just rewritten to gpt-5-5, and the turn died
with:

Model 'gpt-5-5' is not configured. Add a [[models]] entry ... or set pass_through = true

…even though the operator had configured it. Routing was spelling-agnostic for
the request but not for the config — the mismatch reported with
K4DI_VISION_MODEL=gpt-5.5.

Fix — make the config side agnostic too, like text

Canonicalize the config side of every model-name comparison, so both ends are
compared in canonical form:

Site Before After
AppState::model_index key + find_model query to_lowercase() canonicalize(...).to_lowercase()
ProviderRegistry::model_to_provider key + provider_for_model query verbatim canonical
Router auto-map guard (m.name == request.model) verbatim canonical

canonicalize_model_name borrows unchanged for names outside a known family
(glm-4.6, mistral-3.1, …), so configs already using canonical IDs are
untouched — this only adds matches, never removes them.

Verified live (Codex OAuth, config entry named gpt-5.5 with a dot)

before (0.36.79):  "Model 'gpt-5-5' is not configured"
after:             routes, returns "OK"  (model reported as gpt-5-5)

Unit test: provider_for_model resolves both gpt-5.5 and gpt-5-5 for a
dotted [[models]] entry. Full local prek gate — clean.

Note

This closes the last of the three points from the vision report:

Independent files from #476; no conflict.

🤖 Generated with Claude Code

The router canonicalizes an inbound model name (dots→dashes for known
families: `gpt-5.5` → `gpt-5-5`) before routing, but the `[[models]]` lookup
compared config names verbatim. So a config entry spelled `gpt-5.5` never
matched a request the router had rewritten to `gpt-5-5`, and the turn failed
with "Model 'gpt-5-5' is not configured" — even though the operator had clearly
configured it. Routing was agnostic to the spelling for the request but not for
the config.

Canonicalizes the config side of every model-name comparison:
- `AppState::model_index` is keyed by the canonical name, and `find_model`
  canonicalizes its query — the authoritative `[[models]]` resolution.
- `ProviderRegistry::model_to_provider` (the backward-compat direct lookup) does
  the same.
- The router's auto-map guard compares canonical config names, so a dotted
  explicit entry still takes precedence instead of being auto-mapped away.

Canonicalization borrows unchanged for names outside a known family, so configs
that already use canonical IDs are untouched.

Verified live against Codex OAuth with a config entry named `gpt-5.5` (dotted):

  before (0.36.79):  "Model 'gpt-5-5' is not configured"
  after:             routes, returns "OK"

Tests: `provider_for_model` resolves both `gpt-5.5` and `gpt-5-5` for a dotted
config entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Destynova2
Destynova2 enabled auto-merge July 26, 2026 07:32
@Destynova2
Destynova2 merged commit 572f539 into main Jul 26, 2026
38 checks passed
@Destynova2
Destynova2 deleted the feat/model-name-canonical-routing branch July 26, 2026 07:39
@github-actions

Copy link
Copy Markdown
Contributor

Mutation testing (PR diff sample)

Informational — never blocks merge. Full matrix runs on main.

Metric Value
Status clean
Duration 1432 s
Total 30
Caught 0
Missed 0
Timeout 0
Unviable 0

Legend: clean (no survivors), missed (inspect artifact), timed-out (25 min cap reached).

Artifact: mutants-pr-results-05d8c0f867f69ae21bf2bb42a713cf0210c1ff87.

Destynova2 added a commit that referenced this pull request Jul 26, 2026
## The CI/CD bug this fixes

`feat(routing)` (#478) merged cleanly but **never produced a release**.
release-plz
kept reporting `grob: no commit matches the release_commits regex` and
there was
no way to cut v0.36.84.

### Root cause

Every PR was merged with a **merge commit** (`gh pr merge --merge`), so
the
feature commit sits on the merge's **second parent**:

```
572f539 Merge #478      parents: 95db3f6 (release v0.36.83)  de4b690 (feat routing)
```

When release-plz cut v0.36.83 **between** the routing branch's creation
and its
merge, `de4b690` ended up *beside* the release commit rather than after
it. Its
version walk over `v0.36.83..HEAD` doesn't traverse into that second
parent —
`git log --first-parent v0.36.83..main` is **empty** — so the `feat`
commit is
invisible to the bump calc. It works for most PRs only because they're
released
immediately after merging, before the next merge buries them.

## Fix

**Squash-merge only.** Repo settings now:
- `allow_merge_commit = false`
- `squash_merge_commit_title = PR_TITLE`

Every PR becomes one conventional commit on a **linear** `main`, which
release-plz
reads reliably — the second-parent hiding place no longer exists.
Applied to the
repo already; this PR aligns CLAUDE.md (`--auto --merge` → `--auto
--squash`,
plus a note on why the PR title must be a conventional commit) and
documents the
manual `gh workflow run release-plz.yml` trigger added in #479.

## For the currently-stranded routing fix

It's on `main` and ships with the next release cut for any releasable
commit
(now reliably, thanks to squash). Meanwhile it has a trivial workaround
— name
the config entry `gpt-5-5` instead of `gpt-5.5`.

## Note

This PR is the first squash-only merge — its single commit validates the
new
policy end to end.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.

1 participant