Skip to content

fix(config): fix duplicate model in /model picker for haiku/opus-only tenants (EPMCDME-12779) - #424

Merged
elluvium merged 2 commits into
codemie-ai:mainfrom
Sergei-Nikitin-epam:EPMCDME-12779
Jul 27, 2026
Merged

fix(config): fix duplicate model in /model picker for haiku/opus-only tenants (EPMCDME-12779)#424
elluvium merged 2 commits into
codemie-ai:mainfrom
Sergei-Nikitin-epam:EPMCDME-12779

Conversation

@Sergei-Nikitin-epam

@Sergei-Nikitin-epam Sergei-Nikitin-epam commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Root cause: `autoSelectModelTiers()` set `sonnetModel` only when `selectedModel.includes('sonnet')`. Selecting haiku as primary left `sonnetModel` undefined even when the tenant had real sonnet models, so `ANTHROPIC_DEFAULT_SONNET_MODEL` was never set and Claude Code fell back to the haiku model ID for the Custom Sonnet slot — producing a duplicate entry in `/model`.
  • Fix: Apply the same models-list auto-selection for the sonnet tier as already done for haiku and opus: filter by the `'sonnet'` keyword and pick the latest version regardless of which tier was chosen as primary.
  • Also re-introduces the opus-only and haiku-only tenant routing that was reverted in Revert "fix(config): correct model-tier assignment and subagent routing for opus-only tenants" #423, with the haiku-only case added:
    • Opus-only: set only `CLAUDE_CODE_SUBAGENT_MODEL` (not `ANTHROPIC_DEFAULT_SONNET_MODEL`) to prevent opus appearing in both Custom Sonnet and Custom Opus slots.
    • Haiku-only: route haiku through the sonnet slot and omit `ANTHROPIC_DEFAULT_HAIKU_MODEL` to prevent haiku appearing in both slots.

Files changed

File Change
`src/cli/commands/setup.ts` `autoSelectModelTiers()` — sonnet now auto-selects from models list like haiku/opus
`src/agents/core/BaseAgentAdapter.ts` `transformEnvVars()` — opus-only and haiku-only slot routing
`src/providers/plugins/bedrock/bedrock.template.ts` Bedrock hook — same opus-only / haiku-only fallback branches
`src/cli/commands/tests/model-tier-auto-selection.test.ts` New tests: haiku-primary-with-sonnet and no-sonnet-in-list cases
`src/agents/core/tests/model-tier-config.test.ts` Updated haiku-only test to reflect sonnet-slot routing

Test plan

  • `npm run test -- model-tier-auto-selection` — new EPMCDME-12779 test cases pass
  • `npm run test -- model-tier-config` — haiku-only routing test passes
  • Manual: run `codemie setup` with haiku as primary model on a tenant that also provisions sonnet → `/model` picker shows correct distinct IDs in Custom Haiku and Custom Sonnet slots
  • Manual: opus-only tenant → no duplicate in Custom Sonnet / Custom Opus slots
  • Manual: haiku-only tenant → model appears once (in sonnet slot), no duplicate

Closes EPMCDME-12779

@Sergei-Nikitin-epam Sergei-Nikitin-epam changed the title fix(config): fix duplicate model in /model picker when haiku or opus is the only provisioned tier (EPMCDME-12779) fix(config): fix duplicate model in /model picker for haiku/opus-only tenants (EPMCDME-12779) Jul 15, 2026
@Sergei-Nikitin-epam
Sergei-Nikitin-epam force-pushed the EPMCDME-12779 branch 3 times, most recently from bfc0434 to fc4b148 Compare July 16, 2026 06:51
…nly tenants (EPMCDME-12779)

When a tenant's global profile sets sonnetModel to the same value as haikuModel
(e.g. an SSO-provisioned haiku-only seat), transformEnvVars was mapping it to
ANTHROPIC_DEFAULT_SONNET_MODEL, causing the same model ID to appear twice in the
/model picker — once under Custom Haiku and once under Custom Sonnet.

Fix: skip ANTHROPIC_DEFAULT_SONNET_MODEL when CODEMIE_SONNET_MODEL equals
CODEMIE_HAIKU_MODEL, falling back to the opus-routing or haiku-only subagent
branch instead. Also always emit CODEMIE_SONNET_MODEL / CODEMIE_OPUS_MODEL from
exportProviderEnvVars (empty string when absent) so stale shell values are
overridden at env-merge time. Same guard applied to bedrock.template.ts.

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>

@yanaSelin yanaSelin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review — 3 findings (2 critical, 1 major)

The fix correctly addresses the duplicate-model-in-/model-picker symptom in the common case, but two bugs in the surrounding code mean the fix doesn't work for existing users who already have a saved profile or a populated shell environment.


CR-001 [CRITICAL] — src/cli/commands/setup.ts:299-301 — Stale tier config survives re-setup

// setup.ts lines 299-301 (NOT changed by this PR)
if (modelTiers.haikuModel) config.haikuModel = modelTiers.haikuModel;
if (modelTiers.sonnetModel) config.sonnetModel = modelTiers.sonnetModel;
if (modelTiers.opusModel) config.opusModel = modelTiers.opusModel;

When autoSelectModelTiers() returns { sonnetModel: undefined } (e.g. an opus-only tenant), the if (modelTiers.sonnetModel) guard is falsy, so the assignment is skipped and the existing config.sonnetModel from the saved profile is left unchanged. exportProviderEnvVars then emits it as CODEMIE_SONNET_MODEL = 'claude-sonnet-4-6', and transformEnvVars hits the "distinct sonnet tier" branch — the new opus-only routing never applies.

Any user re-running codemie setup to switch to an opus-only or haiku-only tenant will still see the duplicate.

Fix: Remove the if guards; assign unconditionally:

config.haikuModel = modelTiers.haikuModel;
config.sonnetModel = modelTiers.sonnetModel;
config.opusModel = modelTiers.opusModel;

CR-002 [CRITICAL] — src/cli/commands/setup.ts:587-597 — Early-return bypasses new auto-selection logic when shell has stale env vars

// setup.ts lines 587-597 (NOT changed by this PR)
if (envHaiku && envSonnet && envOpus) {
  return { haikuModel: envHaiku, sonnetModel: envSonnet, opusModel: envOpus };
}

autoSelectModelTiers() reads ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL, and ANTHROPIC_DEFAULT_OPUS_MODEL directly from the process environment. If all three are set in the shell — which is the normal state after any prior multi-tier session — the function returns immediately with the old values. The new else if (sonnetModels.length > 0) and else { // no sonnet tier } branches are never reached. The config.ts empty-string override only covers CODEMIE_* vars, not the ANTHROPIC_DEFAULT_* vars that this early-return reads.

This means the fix is entirely bypassed for users who have run codemie setup before.

Fix: Either remove the early-return, or validate that the env vars are consistent with the models list before trusting them (e.g. skip envSonnet if models contains no sonnet-keyword entry).

Comment thread src/agents/core/BaseAgentAdapter.ts Outdated
…779)

**CR-002 [CRITICAL]** — autoSelectModelTiers early-return bypassed validation
- Removed early-return that checked if all three ANTHROPIC_DEFAULT_*_MODEL vars
  were set, which prevented the new auto-selection validation logic from running
- Added validation checks to verify env vars are in the current models list
  before trusting them (haiku, sonnet, opus)
- Falls through to auto-selection if env vars are stale or invalid
- Fixes: users running setup repeatedly now get correct model assignments even
  with stale env vars from previous sessions

**CR-004 [MAJOR]** — removed redundant delete blocks in transformEnvVars
- Deleted three dead delete blocks (haikuModel, sonnetModel, opusModel) that
  were already cleared in Step 1 of transformEnvVars
- Added clarifying comment to document the dependency on Step 1
- Prevents future maintenance drift between Step 1 and tier-routing block

Generated with AI

Co-Authored-By: codemie-ai <codemie.ai@gmail.com>
Claude-Session: https://claude.ai/code/session_01Qp7pTNUw74uh3J7CFQaneq
@elluvium
elluvium merged commit 901ffd1 into codemie-ai:main Jul 27, 2026
5 checks passed
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.

3 participants