claude-code/docs/test-plans/05-model-routing.md
2026-04-01 21:19:41 +08:00

5.1 KiB
Raw Permalink Blame History

模型路由测试计划

概述

模型路由系统负责 API provider 选择、模型别名解析、模型名规范化和运行时模型决策。测试重点是纯函数和环境变量驱动的逻辑。

被测文件

文件 关键导出
src/utils/model/aliases.ts MODEL_ALIASES, MODEL_FAMILY_ALIASES, isModelAlias, isModelFamilyAlias
src/utils/model/providers.ts APIProvider, getAPIProvider, isFirstPartyAnthropicBaseUrl
src/utils/model/model.ts firstPartyNameToCanonical, getCanonicalName, parseUserSpecifiedModel, normalizeModelStringForAPI, getRuntimeMainLoopModel, getDefaultMainLoopModelSetting

测试用例

src/utils/model/aliases.ts

describe('isModelAlias')

  • test('returns true for "sonnet"') — 有效别名
  • test('returns true for "opus"')
  • test('returns true for "haiku"')
  • test('returns true for "best"')
  • test('returns true for "sonnet[1m]"')
  • test('returns true for "opus[1m]"')
  • test('returns true for "opusplan"')
  • test('returns false for full model ID') — 'claude-sonnet-4-6-20250514' → false
  • test('returns false for unknown string') — 'gpt-4' → false
  • test('is case-sensitive') — 'Sonnet' → false别名是小写

describe('isModelFamilyAlias')

  • test('returns true for "sonnet"')
  • test('returns true for "opus"')
  • test('returns true for "haiku"')
  • test('returns false for "best"') — best 不是 family alias
  • test('returns false for "opusplan"')
  • test('returns false for "sonnet[1m]"')

src/utils/model/providers.ts

describe('getAPIProvider')

  • test('returns "firstParty" by default') — 无相关 env 时返回 firstParty
  • test('returns "bedrock" when CLAUDE_CODE_USE_BEDROCK is set') — env 为 truthy 值
  • test('returns "vertex" when CLAUDE_CODE_USE_VERTEX is set')
  • test('returns "foundry" when CLAUDE_CODE_USE_FOUNDRY is set')
  • test('bedrock takes precedence over vertex') — 多个 env 同时设置时 bedrock 优先

describe('isFirstPartyAnthropicBaseUrl')

  • test('returns true when ANTHROPIC_BASE_URL is not set') — 默认 API
  • test('returns true for api.anthropic.com') — 'https://api.anthropic.com' → true
  • test('returns false for custom URL') — 'https://my-proxy.com' → false
  • test('returns false for invalid URL') — 非法 URL → false
  • test('returns true for staging URL when USER_TYPE is ant') — 'https://api-staging.anthropic.com' + ant → true

src/utils/model/model.ts

describe('firstPartyNameToCanonical')

  • test('maps opus-4-6 full name to canonical') — 'claude-opus-4-6-20250514''claude-opus-4-6'
  • test('maps sonnet-4-6 full name') — 'claude-sonnet-4-6-20250514''claude-sonnet-4-6'
  • test('maps haiku-4-5') — 'claude-haiku-4-5-20251001''claude-haiku-4-5'
  • test('maps 3P provider format') — 'us.anthropic.claude-opus-4-6-v1:0''claude-opus-4-6'
  • test('maps claude-3-7-sonnet') — 'claude-3-7-sonnet-20250219''claude-3-7-sonnet'
  • test('maps claude-3-5-sonnet') → 'claude-3-5-sonnet'
  • test('maps claude-3-5-haiku') → 'claude-3-5-haiku'
  • test('maps claude-3-opus') → 'claude-3-opus'
  • test('is case insensitive') — 'Claude-Opus-4-6''claude-opus-4-6'
  • test('falls back to input for unknown model') — 'unknown-model''unknown-model'
  • test('differentiates opus-4 vs opus-4-5 vs opus-4-6') — 更具体的版本优先匹配

describe('parseUserSpecifiedModel')

  • test('resolves "sonnet" to default sonnet model')
  • test('resolves "opus" to default opus model')
  • test('resolves "haiku" to default haiku model')
  • test('resolves "best" to best model')
  • test('resolves "opusplan" to default sonnet model') — opusplan 默认用 sonnet
  • test('appends [1m] suffix when alias has [1m]') — 'sonnet[1m]' → 模型名 + '[1m]'
  • test('preserves original case for custom model names') — 'my-Custom-Model' 保留大小写
  • test('handles [1m] suffix on non-alias models') — 'custom-model[1m]''custom-model[1m]'
  • test('trims whitespace') — ' sonnet ' → 正确解析

describe('getRuntimeMainLoopModel')

  • test('returns mainLoopModel by default') — 无特殊条件时原样返回
  • test('returns opus in plan mode when opusplan is set') — opusplan + plan mode → opus
  • test('returns sonnet in plan mode when haiku is set') — haiku + plan mode → sonnet 升级
  • test('returns mainLoopModel in non-plan mode') — 非 plan 模式不做替换

Mock 需求

依赖 Mock 方式 说明
process.env.CLAUDE_CODE_USE_BEDROCK/VERTEX/FOUNDRY 直接设置/恢复 provider 选择
process.env.ANTHROPIC_BASE_URL 直接设置/恢复 URL 检测
process.env.USER_TYPE 直接设置/恢复 staging URL 和 ant 功能
getModelStrings() mock.module 返回固定模型 ID
getMainLoopModelOverride mock.module 会话中模型覆盖
getSettings_DEPRECATED mock.module 用户设置中的模型
getUserSpecifiedModelSetting mock.module getRuntimeMainLoopModel 依赖
isModelAllowed mock.module allowlist 检查