Skip to content

feat(routing): canonicalize model names before [[models]] lookup#307

Merged
Destynova2 merged 1 commit intofix/preset-mod-include-strfrom
feat/model-name-normalizer
Apr 28, 2026
Merged

feat(routing): canonicalize model names before [[models]] lookup#307
Destynova2 merged 1 commit intofix/preset-mod-include-strfrom
feat/model-name-normalizer

Conversation

@Destynova2
Copy link
Copy Markdown
Contributor

Summary

  • Add routing::classify::model_name::canonicalize_model_name and call it as step 0 of Router::route, so cosmetic variations of provider model IDs collapse to a single canonical key before any subsequent step (background regex, auto-map, prompt rules, [[models]] lookup).
  • Idempotent and zero-allocation on already-canonical inputs (Cow::Borrowed), so configs that use the canonical IDs (gpt-4o, claude-sonnet-4-5, …) pay nothing.
  • Bump the 7 enterprise preset snapshots whose default_non_claude row now carries the canonicalized gpt-5-2 instead of gpt-5.2.

Rules (in order)

  1. Lowercase ASCII.
  2. Strip trailing -latest.
  3. Strip trailing -YYYYMMDD (exactly 8 digits — guards gpt-5-2).
  4. <digit>.<digit> to <digit>-<digit>, gated on a known family prefix (claude-, gpt-, gemini-, grok-, deepseek-) so unrelated IDs like glm-4.6 survive untouched.
  5. Anthropic family-version reorder: claude-{N}-{M}-{family}claude-{family}-{N}-{M} so older claude-3-5-sonnet and modern claude-sonnet-4-5 spellings collapse into one key (matching the spelling used in presets/*.toml).

Why this base branch

Based on fix/preset-mod-include-str (PR #304) because main does not currently build (referenced presets/{medium,local,cheap,fast}.toml files were deleted but include_str! calls remain). Will rebase to main automatically once #304 lands.

Test plan

  • 25-row coverage matrix in model_name.rs exercises Anthropic 3.x/4.x, OpenAI (gpt-4o, gpt-5, gpt-5-2, gpt-5.2), DeepSeek, Gemini, Grok families.
  • Proptest verifies idempotence over arbitrary alphanumeric inputs (regression seed for 0.0.0 checked in).
  • All 130 existing routing tests pass (including the regression guards test_auto_map_skips_explicit_virtual_model and test_no_auto_map_non_matchingglm-4.6 is unaffected by rule 4 thanks to the family-prefix gate).
  • All 1263 lib + integration tests pass.
  • cargo fmt --check, cargo clippy --all-targets -- -D warnings, and cargo nextest run all green locally.

Users frequently send small variations of provider model IDs (date
suffixes like `claude-3-5-sonnet-20241022`, `-latest` aliases, dotted
versions like `gemini-2.5-flash`, mixed casing, or the older Anthropic
`claude-{N}-{M}-{family}` ordering). Without canonicalization those all
miss the configured `[[models]]` entry and fall through to the
auto-mapper's catch-all default — bypassing the user's intended
fallback chain.

Add `routing::classify::model_name::canonicalize_model_name` and call
it as step 0 of `Router::route`, before any background / auto-map /
prompt-rule / lookup logic. The function is idempotent and returns
`Cow::Borrowed` for already-canonical inputs (steady state in
production), so configs that use the canonical IDs (`claude-sonnet-4-5`,
`gpt-4o`, `gemini-3-pro`, …) pay zero allocations.

Coverage: 25-row matrix across Anthropic 3.x/4.x, OpenAI (gpt-4o /
gpt-5 / gpt-5.2), DeepSeek, Gemini, and Grok families plus a proptest
that exhaustively checks idempotence over arbitrary alphanumeric
strings. All 130 existing routing tests still pass.
@Destynova2 Destynova2 merged commit d50de3f into fix/preset-mod-include-str Apr 28, 2026
1 check passed
@Destynova2 Destynova2 deleted the feat/model-name-normalizer branch April 28, 2026 06:36
Destynova2 pushed a commit that referenced this pull request Apr 28, 2026
ADR hygiene pass identified two zombie statuses where designs were marked
`done` but production code does not match the design yet. Reverts both to
`accepted` with inline status notes recording the correction:

- ADR-0013 — storage refactor on atomic files + JSONL: redb persistence
  (`GrobStore` in `src/storage/mod.rs`) is still the live code path;
  `~/.grob/spend/*.jsonl` has not yet been written. A-7 deferred.
- ADR-0015 — indirect prompt injection coverage: A-6 implementation
  deferred; framework design final but no production code merges yet.

Adds four new ADRs as `status: proposed`, formalising decisions that have
been operating informally:

- ADR-0023 — Preset Naming and Composition Strategy. Adds the `[meta] tier`
  taxonomy (base/audience-specific/experimental), kebab-case naming
  convention, the rule that presets do not nest, and the
  `grob preset info` shippability gate. Compliance overlays are a separate
  `kind = "overlay"` concept.
- ADR-0024 — Preset-as-Compliance-Template. Treats compliance presets as
  packaged decisions bundling topology + policies + ADR-0022
  `[endpoints.compliance]` metadata + `[meta] compliance_reviewed`
  attestation. Customer overlays may tighten but never loosen; documents
  the certification-renewal lifecycle.
- ADR-0025 — RPC Mutation Transactionality and In-Flight Visibility.
  Resolves issue #228: in-flight requests see pre-mutation snapshots,
  persistence is atomic with the in-memory swap (rollback on disk-write
  failure), per-namespace mutex permits parallel writes, every mutation
  emits an `AuditEvent::RpcMutation` with before/after hashes.
- ADR-0026 — Model Name Canonicalization Policy. Records the fixed rule
  set landed in PR #307 (lowercase, strip `-latest`, strip 8-digit date
  suffix, dot-to-dash version on a known-prefix gate, Anthropic
  family-version reorder), the idempotence guarantee, the operator
  escape hatches (per-endpoint `actual_model` mapping or table patch),
  and the 2-minor-release deprecation window for renamed models.

CHANGELOG `[Unreleased]` records the additions under a `Documentation`
heading. No code changes — doc only.

Co-Authored-By: Claude Opus 4.7 (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