diff --git a/devlog/_plan/260911_deepseek_v41_transition/000_plan.md b/devlog/_plan/260911_deepseek_v41_transition/000_plan.md new file mode 100644 index 0000000000..9c2493b74a --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/000_plan.md @@ -0,0 +1,45 @@ +# 260911 — DeepSeek V4.1 전환 + +DeepSeek가 2026-09-10에 V4.1-Flash를 내면서 V4 계열의 이름이 한 번에 움직였다. `deepseek-v4-flash`와 `deepseek-v4-flash-vision-exp`는 모델로서 은퇴하고 이름만 V4.1-Flash로 라우팅되는 별칭이 됐고, `deepseek-v4-pro`는 2026-09-14 04:00 UTC부터 단계적으로 퇴역하며 그 시점부터 요청이 V4.1-Flash로 넘어간다. opencodex는 이 두 id를 13개 프로바이더 프리셋에 손으로 박아두고 있어서, 그대로 두면 Pro 컨텍스트 창과 Pro 가격을 광고하면서 실제로는 Flash를 서빙하는 상태가 된다. 이 유닛은 V4.1을 전개하고 v4-pro를 걷어내고, 같은 영역을 건드리는 기여자 PR을 먼저 정리한 뒤 둘 다 dev에 머지한다. 바뀌는 사람은 DeepSeek 경로를 쓰는 모든 사용자다. + +근거는 `001_evidence.md`, 출현 지점 집계는 `002_inventory.md`에 있다. + +## 루프 스펙 + +| 항목 | 내용 | +| --- | --- | +| Loop archetype | satisfy-spec | +| Trigger | 사용자 지시: v4.1-flash를 v4-flash가 있는 모든 곳에 전개하고, 퇴역한 v4-pro를 전부 제거하고, PR #4258과 #4274를 머지하라 | +| Goal | V4.1 전개 + v4-pro 제거가 focused 테스트와 함께 dev에 머지되고, #4258/#4274도 머지된다 | +| Non-goals | 새 사용자 config 필드, 어댑터 와이어 동작 변경, main/preview 승격, 릴리스, 생성 메타데이터 수작업 편집 | +| Verifier | `bun test` 영향 도메인, `bun run typecheck`, `bun run privacy:scan`, 머지 전 exact-head CI | +| Stop condition | 두 PR과 이번 변경이 dev에 머지된 시점 | +| Memory artifact | `devlog/_plan/260911_deepseek_v41_transition/` | +| Expected terminal outcomes | DONE = 머지 완료. BLOCKED = CI가 이 변경과 무관한 이유로 반복 실패하거나 머지 권한이 거부될 때 | +| Escalation condition | 사용자가 머지를 명시 승인했다. main/preview 승격과 릴리스는 별도 승인 필요 | +| Resource bounds | 쓰기 범위: `src/`, `tests/`, `docs-site/`, 이 플랜 유닛. 전체 스위트는 사용자 지시로 로컬에서 돌리지 않고 CI에 위임한다 | + +## 작업 단계 지도 + +| work-phase | 문서 | 내용 | +| --- | --- | --- | +| wp1 | 000-002 | 근거·인벤토리·로드맵 잠금 (docs only) | +| wp2 | `010_phase1_pr4258.md` | 기여자 PR #4258 리뷰와 머지 | +| wp3 | `020_phase2_v41_rollout.md` | V4.1-Flash 전개 | +| wp4 | `030_phase3_v4pro_removal.md` | v4-pro 퇴역 제거 | +| wp5 | `040_phase4_merge.md` | docs-site 동기화, PR 게시와 머지 | + +## 이 유닛이 내린 두 가지 판단 + +**1. id는 프로바이더별로 다르다.** DeepSeek 1st-party API의 공식 id는 `deepseek-flash`다. 게이트웨이가 노출하는 철자는 `deepseek-v4.1-flash`이고, 이건 이슈 #4253과 PR #4258이 저장소 안에서 확인해 준 사실이다. "모든 곳에 같은 id"로 넣으면 네이티브 쪽이 틀린 id를 갖는다. + +**2. 벤더 호스팅 스냅샷은 DeepSeek 수명주기와 별개다.** Volcengine Ark는 `deepseek-v4-pro-260425`처럼 날짜가 박힌 스냅샷을 고정하고, Alibaba·Ollama Cloud·NVIDIA NIM·Baseten도 각자 로스터를 따로 발표한다. DeepSeek 1st-party 퇴역 공지가 그 벤더들의 배포까지 끝내지는 않는다. 그래서 제거는 **DeepSeek 1st-party와 그것을 되파는 Zen 계열을 먼저** 확정하고, 벤더 호스팅 프리셋은 같은 커밋에서 분리해 PR 본문에 근거와 함께 드러낸다 — 리뷰어가 한 커밋만 떼어낼 수 있게. + +## wp1 감사 반영 (2026-09-11) + +독립 감사가 로드맵 초안의 결함 6건을 잡았고 전부 수용했다. 가장 큰 것 둘: + +- 초안은 공유 상수 `DEEPSEEK_THINKING_MODELS`에 V4.1을 넣으려 했는데, 그 상수는 `deepseek` 1st-party 프리셋의 `models:` 배열 자체를 포함해 6개 프리셋 21곳이 소비한다(`registry.ts:2045`). 그대로 하면 게이트웨이 철자가 네이티브 프리셋으로 새서 020의 수용기준이 자기모순이 된다. 상수를 분리하는 설계로 다시 썼다. +- 초안의 "Pro 사다리를 광고한다"는 근거가 없다. `DEEPSEEK_PRO_*`와 `DEEPSEEK_FLASH_*` 효율 맵은 값이 같다(`registry.ts:701-715`). 실제로 어긋나는 건 **컨텍스트 창과 가격**이다. + +나머지는 002/020/030의 해당 절에 반영했다. diff --git a/devlog/_plan/260911_deepseek_v41_transition/001_evidence.md b/devlog/_plan/260911_deepseek_v41_transition/001_evidence.md new file mode 100644 index 0000000000..2d4e93b1ce --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/001_evidence.md @@ -0,0 +1,25 @@ +# 001 — 근거 + +2026-09-11 웹 조사. 출처는 DeepSeek 공식 API 문서와 9/10 공지. + +## 확인된 사실 + +| 사실 | 출처 | +| --- | --- | +| V4.1-Flash 출시 2026-09-10 | | +| 공식 API id는 `deepseek-flash` | | +| `deepseek-v4-flash`와 `deepseek-v4-flash-vision-exp`는 모델로서 은퇴, 이름은 V4.1-Flash로 라우팅되는 호환 별칭으로 유지, Flash 가격 과금 | | +| `deepseek-v4-pro`는 2026-09-14 04:00 UTC부터 단계적 퇴역, 이후 요청은 V4.1-Flash로 자동 라우팅, 신규 연동은 `deepseek-flash` 권고 | | + +## 기록해 두는 불일치 + +같은 체인지로그를 근거로, 질의 표현에 따라 상반된 요약이 돌아왔다. 한쪽은 위 표대로 v4-pro 퇴역과 Flash 요금 적용을 말했고, 다른 쪽은 "9월 14일 이후에도 서비스 계속, 과금 변동 없음, 7월 24일 퇴역한 건 `deepseek-chat`/`deepseek-reasoner`"라고 답했다. + +이 유닛은 전자를 따른다. 다만 두 해석이 공통으로 인정하는 사실 하나만으로도 변경 근거는 충분하다: **9월 14일부터 `deepseek-v4-pro` 요청은 V4.1-Flash로 라우팅된다.** 퇴역이냐 임시 라우팅이냐와 무관하게, 그 시점 이후 `deepseek-v4-pro` 행은 Pro 사다리·Pro 컨텍스트·Pro 가격을 광고하면서 Flash를 서빙한다. 잘못된 광고를 남겨두는 쪽이 제거보다 나쁘다. + +저장소 내부 근거로는 이슈 #4253과 PR #4258이 Command Code 라이브 로스터에서 `deepseek/deepseek-v4.1-flash`가 실제로 서빙되는 것을 확인해 준다. + +## 이 유닛이 주장하지 않는 것 + +- 벤더 호스팅(Volcengine, Alibaba, Ollama Cloud, NVIDIA NIM, Baseten, cline-pass, orcarouter, codebuddy, qoder) 로스터에서 v4-pro가 중단됐다는 주장은 **하지 않는다**. 그쪽은 각자 스냅샷과 일정이 있고, Volcengine은 `deepseek-v4-pro-260425`처럼 날짜가 박힌 id를 쓴다. +- Zen 게이트웨이가 `deepseek-flash` 철자를 받는다는 주장도 하지 않는다. 게이트웨이 쪽은 관측된 `deepseek-v4.1-flash`를 쓴다. diff --git a/devlog/_plan/260911_deepseek_v41_transition/002_inventory.md b/devlog/_plan/260911_deepseek_v41_transition/002_inventory.md new file mode 100644 index 0000000000..bb774fff9a --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/002_inventory.md @@ -0,0 +1,49 @@ +# 002 — 출현 지점 집계 + +`rg` 기준, 2026-09-11 브랜치 `codex/260911-opencode-go-free-stabilization`. + +| id | 파일 수 | 히트 수 | +| --- | --- | --- | +| `deepseek-v4-pro` | 62 | 293 | +| `deepseek-v4-flash` | 99 | 585 | + +## `DEEPSEEK_THINKING_MODELS` 소비처 (감사 정정) + +이 상수(`registry.ts:619`)는 Zen 3종만 먹이는 게 아니다. **6개 프리셋 21곳**이 소비하며, 그중에는 `deepseek` 1st-party 프리셋의 `models:` 배열 자체가 포함된다. + +| 프리셋 | 앵커 | +| --- | --- | +| `opencode-go` | 1760, 1768, 1776, 1803, 1813 | +| `deepseek` 1st-party | **2045 (`models:` spread)**, 2114-2121 | +| `alibaba-token-plan` | 2813-2818 | +| `opencode-zen` | 3047-3064 | +| `opencode-free` | 3108 | + +이것 때문에 "공유 상수에 V4.1을 추가" 설계는 성립하지 않는다. 020이 상수 분리로 다시 설계됐다. + +## v4-pro를 선언하는 프로바이더 (registry.ts) + +| 프로바이더 | 성격 | 앵커 | +| --- | --- | --- | +| `deepseek` (1st-party) | **DeepSeek 직접** | 2038-2078 (`modelContextWindows`, `modelWireDefaults`, `modelResponsesTerminalRepair`) | +| `opencode-go` / `opencode-zen` / `opencode-free` | Zen 게이트웨이가 DeepSeek을 되팜 | 619 `DEEPSEEK_THINKING_MODELS`, 1793 | +| `command-code` (OAuth + API key) | 게이트웨이 | 631, 1180-1190, 2305 | +| `alibaba-token-plan` / `-intl` | 벤더 호스팅 | 736, 749, 758, 2832, 2857-2913 | +| `volcengine` ark / coding / agent | 벤더 호스팅, **날짜 스냅샷** `deepseek-v4-pro-260425` | 791, 807, 816, 838, 850, 2785, 2791 | +| `ollama` cloud | 벤더 호스팅 | 2951, 2963 | +| `nvidia-nim` | 벤더 호스팅 | 969 | +| `baseten` | 벤더 호스팅 (`deepseek-ai/DeepSeek-V4-Pro`) | 1010-1059 | +| `cline-pass` | 게이트웨이 | 1144, 1199 | +| `orcarouter` | 게이트웨이 | 1180-1190 | +| `codebuddy` / `qoder` | 게이트웨이 | `codebuddy-models.ts`, `qoder-models.ts` | + +## 손대지 않는 영역과 이유 + +| 영역 | 이유 | +| --- | --- | +| `scripts/model-metadata.source.json` (47건), `src/generated/model-metadata.ts` (3건) | 벤더 스냅샷에서 **생성되는** 파일이다. 손으로 지우면 다음 생성에서 되돌아온다. 게다가 `src/usage/cost.ts`가 과거 요청 비용을 이 표로 계산하므로, 행을 지우면 이미 기록된 사용량의 원가가 깨진다 | +| 임의 fixture id로 v4-pro를 쓰는 테스트 | 레지스트리 멤버십을 주장하지 않는 테스트는 모델 id를 문자열로만 쓴다. 깨지는 것만 고친다 | + +## 테스트 영향 예상 + +감사 정정: 영향 파일은 5개가 아니라 **24개**다. 위 다섯 외에 `tests/routing/router.test.ts:450`(정확 목록), `tests/providers/orcarouter-provider.test.ts:139`, `tests/gui/alibaba-intl-token-plan.test.ts:31`, `tests/routing/fastwire-policy.test.ts`, `tests/codex-integration/slug-codec.test.ts`, `tests/server/adapter-resolve.test.ts` 등이 포함된다. diff --git a/devlog/_plan/260911_deepseek_v41_transition/010_phase1_pr4258.md b/devlog/_plan/260911_deepseek_v41_transition/010_phase1_pr4258.md new file mode 100644 index 0000000000..abafbf70c4 --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/010_phase1_pr4258.md @@ -0,0 +1,31 @@ +# 010 — wp2: 기여자 PR #4258 리뷰와 머지 + + · `gitgarmin` · base `dev` · head `codex/command-code-v41-qwen-efforts` + +파일 2개: `src/providers/command-code-efforts.ts` (+23/-0), `tests/providers/command-code-provider.test.ts` (+33/-0). + +## 왜 먼저인가 + +같은 파일을 wp3에서 건드린다. 기여자 PR을 먼저 넣고 그 위에 리베이스하는 게 순서다. 반대로 하면 기여자가 리베이스 부담을 진다. + +## 리뷰 항목 + +1. 추가된 두 행(`deepseek/deepseek-v4.1-flash`, `Qwen/Qwen3.8-Flash`)이 `COMMAND_CODE_MODEL_EFFORTS` 조회 계약과 맞는가. +2. 사다리 값의 출처가 본문 주장과 일치하는가. 본문은 같은 패밀리 행에서 추론했다고 밝히고, 라이브 200 응답을 근거로 든다. +3. 신규 테스트가 케이스 폴딩과 두 프리셋(OAuth/API key)을 모두 고정하는가. +4. AGENTS.md 리뷰 규칙: base `dev` ✓, 보안 표면 미접촉, 테스트 동반. +5. CI가 exact head에서 green인가. + +## 수용 기준 + +- 리뷰 코멘트가 영어로 남는다 (AGENTS.md 리뷰 규칙). +- exact-head CI green을 확인한 뒤 머지한다. +- 머지 후 `dev`를 받아 내 브랜치를 리베이스하고 충돌이 없음을 확인한다. + +## 검증 + +``` +gh pr checks 4258 +gh pr view 4258 --json mergeStateStatus,reviewDecision +bun test tests/providers/command-code-provider.test.ts +``` diff --git a/devlog/_plan/260911_deepseek_v41_transition/020_phase2_v41_rollout.md b/devlog/_plan/260911_deepseek_v41_transition/020_phase2_v41_rollout.md new file mode 100644 index 0000000000..c2cf34f776 --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/020_phase2_v41_rollout.md @@ -0,0 +1,67 @@ +# 020 — wp3: V4.1-Flash 전개 (2차 감사 후 재설계) + +## 두 번 틀렸던 지점 + +**1차 초안**: `DEEPSEEK_THINKING_MODELS`에 V4.1을 그냥 얹으려 했다. 그 상수는 `deepseek` 1st-party 프리셋의 `models:`를 포함해 6개 프리셋이 공유하므로, 게이트웨이 철자가 네이티브로 샌다. + +**2차 초안**: 그래서 상수를 레거시 전용으로 고정하고 신규 id를 따로 넣으려 했다. 감사가 `fail`을 냈고 이유가 맞다 — `deepseek` 프리셋의 모델별 맵 **다섯 개**가 전부 그 상수에서 파생된다(`registry.ts:2121-2124, 2128`). 상수를 레거시로 묶으면 `deepseek-flash`는 사다리·요약·`reasoning_content` 리플레이·비전 차단을 **전부** 잃고 #78형 400이 재발한다. + +## 확정 설계: 상수를 세 갈래로 파생시킨다 + +```ts +// 업스트림이 호환 별칭으로 유지하는 레거시 V4 id +const DEEPSEEK_V4_LEGACY_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"]; +// DeepSeek 1st-party: 공식 id는 deepseek-flash +const DEEPSEEK_NATIVE_THINKING_MODELS = ["deepseek-flash", ...DEEPSEEK_V4_LEGACY_MODELS]; +// Zen 게이트웨이가 노출하는 철자 +const DEEPSEEK_GATEWAY_THINKING_MODELS = ["deepseek-v4.1-flash", ...DEEPSEEK_V4_LEGACY_MODELS]; +``` + +기존 이름 `DEEPSEEK_THINKING_MODELS`는 `DEEPSEEK_V4_LEGACY_MODELS`로 바뀐다. 벤더 호스팅 프리셋(volcengine 플랜, alibaba)은 그 레거시 상수를 계속 쓴다 — 그쪽은 V4 스냅샷을 자기 일정으로 서빙한다. + +## 파일 변경 지도 + +| 위치 | 변경 | +| --- | --- | +| `registry.ts:619` | 상수 3개로 재구성 | +| `registry.ts:2121-2124, 2128` (deepseek 프리셋) | 다섯 맵을 `DEEPSEEK_NATIVE_THINKING_MODELS`로 전환 | +| `registry.ts:2049` (`models:`) | 같은 상수로 전환 | +| `registry.ts:2053` | `defaultModel`을 `deepseek-flash`로 | +| `registry.ts:2062, 2078` | `modelContextWindows`·`modelWireDefaults`·`modelResponsesTerminalRepair`에 `deepseek-flash` 항목 추가 | +| `registry.ts:1760, 1768, 1776, 1803, 1813` (opencode-go) | `DEEPSEEK_GATEWAY_THINKING_MODELS`로 전환 | +| `registry.ts:1791` (go `noVisionModels`, 리터럴) | `deepseek-v4.1-flash` 추가 | +| `registry.ts:3053-3071` (opencode-zen) | 게이트웨이 상수로 전환. 이 프리셋엔 `modelSupportsReasoningSummaries` 필드 자체가 없다 — 새로 만들지 않는다 | +| `registry.ts:3115` (opencode-free `noJsonSchemaModels`) | 게이트웨이 상수로 전환 | +| `src/providers/default-aliases.ts:54` 앞 | `/^deepseek-v4\.1/ → "ds41"` 을 `/^deepseek-v4/` **앞**에 둔다(첫 매치 승리). `/^deepseek-flash/ → "dsf"` 는 위치 무관 | + +**건드리지 않는 것**: `opencode-free`의 `noVisionModels`(`3111`)는 `OPENCODE_ZEN_TEXT_ONLY_MODELS` 참조라 여기에 넣으면 zen까지 오염된다. free는 원래 DeepSeek id를 이 목록에 갖고 있지 않으므로 그대로 둔다. `command-code`는 PR #4258 소유. 벤더 호스팅 9곳은 V4.1 서빙 근거가 없어 제외. + +## 수용 기준 + +1. `deepseek` 프리셋에서 `deepseek-flash`가 사다리·효율맵·요약·replay·noVision **다섯 곳 모두**에 나타난다. 이게 2차 감사가 잡은 실패 지점이므로 테스트로 직접 관측한다. +2. `opencode-go`에서 `deepseek-v4.1-flash`가 같은 대우를 받는다. +3. **반대 증거**: `deepseek` 프리셋에 `deepseek-v4.1-flash`가 없고, Zen 프리셋에 `deepseek-flash`가 없다. +4. 벤더 호스팅 프리셋(volcengine coding plan)의 DeepSeek 목록은 변하지 않는다. +5. `deepseek` `defaultModel`이 `deepseek-flash`다. + +## 갱신해야 하는 기존 테스트 (감사 열거) + +`tests/providers/provider-registry-parity.test.ts`: `197`(deepseek preserveReasoningContentModels `toEqual`), `199-201`(deepseek noVisionModels `toEqual`), `309`(defaultModel), `73-80`(go noVision `toEqual`), `86-92`(3종 noJsonSchema `toEqual`), `1421-1453`(DeepSeek id 열거). `tests/providers/opencode-go-deepseek.test.ts:159-160`(noJsonSchema `toEqual`). `tests/codex-integration/reasoning-effort.test.ts:274`(동일 `toEqual`). + +`parity:184`는 `toContain`이라 안전하고, `model-metadata-sync.test.ts`는 `scripts/model-metadata.source.json`만 입력으로 재생성·바이트 비교하므로 레지스트리 추가로 깨지지 않는다. + +## 기록해 두는 부수 사실 + +`scripts/model-metadata.source.json`에 `deepseek-flash`와 `deepseek-v4.1-flash` 행이 모두 없어 두 id의 비용 추정이 빈다. 생성 파일은 손대지 않는 방침(002)이므로 다음 메타데이터 생성에서 채워진다. PR 본문에 명시한다. + +`opencode-free`는 `liveModels: true`인데 게이트웨이 상수 전환이 `noJsonSchemaModels` 한 곳뿐이라 `deepseek-v4.1-flash`가 사다리와 replay를 받지 못한다. 기존 `deepseek-v4-pro`/`-flash`도 같은 비대칭이므로 신규 결함은 아니다. PR 본문에 한 줄 남긴다. + +## 검증 + +``` +bun test tests/providers/provider-registry-parity.test.ts +bun test tests/providers/opencode-go-deepseek.test.ts +bun test tests/providers/deepseek-reasoning-replay.test.ts +bun test tests/codex-integration/slug-codec.test.ts +bun run typecheck +``` diff --git a/devlog/_plan/260911_deepseek_v41_transition/030_phase3_v4pro_removal.md b/devlog/_plan/260911_deepseek_v41_transition/030_phase3_v4pro_removal.md new file mode 100644 index 0000000000..f4ea052422 --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/030_phase3_v4pro_removal.md @@ -0,0 +1,90 @@ +# 030 — wp4: `deepseek-v4-pro` 퇴역 제거 + +## 커밋 분리 + +제거 근거의 강도가 프로바이더마다 다르므로 두 커밋으로 나눈다. 리뷰어가 뒤쪽만 떼어낼 수 있어야 한다. + +**커밋 A — DeepSeek 1st-party와 그것을 되파는 경로 (근거 강함)** + +| 대상 | 앵커 | +| --- | --- | +| `deepseek` 프리셋 | `registry.ts:2038-2078` — `modelContextWindows`, `modelWireDefaults`, `modelResponsesTerminalRepair`에서 제거 | +| `DEEPSEEK_THINKING_MODELS` | `registry.ts:619` — v4-pro 제거. Zen 3종과 volcengine 플랜이 이 상수를 공유하므로 파급을 각 사용처에서 확인 | +| `opencode-go` `noVisionModels` | `registry.ts:1793` | +| `command-code` 계열 | `registry.ts:631, 1180-1190, 2305`, `command-code-efforts.ts`, `adapters/command-code.ts:498` | +| `cline-pass` | `registry.ts:1144, 1199`, `adapters/cline-pass-deepseek-v4-tool-replay.ts:5` | +| `orcarouter` | `registry.ts:1180-1190` | +| `codebuddy` / `qoder` | `codebuddy-models.ts:38,124,145`, `qoder-models.ts:13` | +| `router.ts:686` | 잔여 참조 | +| 주석 (감사 추가) | `registry.ts:631, 719, 2038, 2305` — 코드에서 사라진 뒤에도 주석이 남으면 수용기준 1이 성립하지 않는다 | + +## wp4 감사 반영 (2026-09-11): 삭제만으로는 사라지지 않는다 + +감사가 결정적인 사실을 잡았다. `liveModels: true`인 프로바이더(cline-pass, orcarouter, baseten, commandcode, command-code, digitalocean, qoder)는 **정적 행을 지워도 모델이 라이브 디스커버리로 다시 올라온다.** 지워지는 건 모델이 아니라 컨텍스트 창·사다리·text-only 힌트뿐이다. 그 결과는 제거가 아니라 순수 퇴행이다 — 비전 사이드카가 이미지를 떨구고 replay 완화가 사라진 채로 모델이 계속 보인다. + +그래서 제거는 두 메커니즘으로 갈린다. + +| 프로바이더 성격 | 대상 | 방법 | +| --- | --- | --- | +| 정적 `models:` 로스터 | alibaba-token-plan, alibaba-token-plan-intl, volcengine ark/coding/agent, ollama, nvidia-nim | 행 삭제 — 실제로 사라진다 | +| 라이브 디스커버리 | cline-pass, orcarouter, baseten, commandcode, command-code, digitalocean, qoder | `ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS`(`src/codex/catalog/parsing.ts:180`)에 슬러그 등록 — 이게 실제로 카탈로그에서 빼는 유일한 수단이다. 그 위에서 정적 메타데이터 행도 함께 정리한다 | + +### 제외 슬러그 형식 (확인됨) + +`catalogModelSlug`(`parsing.ts:842`)는 `model.alias ?? routedSlug(provider, id)`이고, 모델 id 안의 슬래시는 하이픈이 된다. 실제 예시가 테스트에 박혀 있다: `commandcode/deepseek-deepseek-v4-pro`(`tests/codex-integration/codex-catalog.test.ts:2230`). + +따라서 등록할 슬러그는 다음 형태다. **각각 실제 카탈로그 출력으로 확인한 뒤 넣는다 — 형식이 틀리면 제외가 조용히 아무 일도 하지 않는다.** + +| 프로바이더 | 모델 id | 슬러그 | +| --- | --- | --- | +| `commandcode` | `deepseek/deepseek-v4-pro` | `commandcode/deepseek-deepseek-v4-pro` | +| `command-code` | `deepseek/deepseek-v4-pro` | `command-code/deepseek-deepseek-v4-pro` | +| `orcarouter` | `deepseek/deepseek-v4-pro` | `orcarouter/deepseek-deepseek-v4-pro` | +| `cline-pass` | `cline-pass/deepseek-v4-pro` | `cline-pass/cline-pass-deepseek-v4-pro` | +| `baseten` | `deepseek-ai/DeepSeek-V4-Pro` | `baseten/deepseek-ai-DeepSeek-V4-Pro` | +| `digitalocean` | (확인 필요) | (확인 필요) | +| `qoder` | (확인 필요) | (확인 필요) | + +## 감사가 잡은 나머지 + +- `registry.ts:2864` volcengine-agent-plan `defaultModel`이 `deepseek-v4-pro`다. 같은 커밋에서 로스터 내 다른 id로 교체한다. +- `ORCAROUTER_TEXT_ONLY_MODELS`(`1204`)와 `ORCAROUTER_MODEL_REASONING_EFFORT_MAP`(`1210`)은 v4-pro만 담고 있어 빈 컬렉션이 된다. `types/provider.ts:735`가 빈 배열을 "명시적 opt-out"으로 정의하므로 **빈 채로 두지 말고 상수와 소비 필드를 함께 삭제**한다. +- 대문자 id는 소문자 `rg`에 안 잡힌다: `registry.ts:1031,1042,1052`(baseten `deepseek-ai/DeepSeek-V4-Pro`), `qoder-models.ts:13`. 완료 기준의 `rg`는 `-i`를 쓴다. +- 내가 baseten이라고 적었던 `registry.ts:1080`은 실제로 DigitalOcean 목록이다. +- `command-code-efforts.ts:4` 행을 지우면 `router.ts:107`의 `knownModelIdsForProvider`가 그 키맵을 known-id 소스로 쓰므로 슬러그 디코드가 사라진다. 방금 머지된 v4.1-flash 행은 다른 키라 대체가 아니다. `146`행 주석도 사라진 행을 가리키게 되므로 같이 고친다. +- 후속 대상: 9개 로케일 문서, `frontier-benchmarks.json`, `src/generated/model-metadata.ts`, `model-rename-migration.ts:111`(사용자 config 마이그레이션), `structure:check`. + +**커밋 B — 벤더 호스팅 (근거 약함, 분리)** + +`alibaba-token-plan`/`-intl`, `volcengine` ark/coding/agent (`deepseek-v4-pro-260425` 포함), `ollama`, `nvidia-nim`, `baseten`. + +**`volcengine-agent-plan`의 `defaultModel`이 `deepseek-v4-pro`다(`registry.ts:2832`).** 제거하면 기본 모델이 비므로 같은 커밋에서 대체 기본값을 정해야 한다. 이 프리셋의 나머지 로스터에서 고른다. + +이 벤더들은 자체 스냅샷과 일정으로 배포한다. DeepSeek 1st-party 퇴역 공지가 그들의 로스터를 끝내지 않는다. 지시는 전부 제거였으므로 실행하되, PR 본문에 이 구분과 되돌리는 방법을 명시한다. + +## 손대지 않는 것 + +`scripts/model-metadata.source.json`과 `src/generated/model-metadata.ts`. 생성 파일이고, `src/usage/cost.ts`가 과거 사용량 원가를 이 표로 계산한다. 행을 지우면 이미 기록된 요청의 비용이 깨진다. 002 참조. + +## 수용 기준 + +1. `rg "deepseek-v4-pro" src`가 생성 파일을 제외하고 0건이다. +2. 레지스트리 멤버십을 고정하던 테스트가 갱신되고 통과한다. +3. 반대 증거: `deepseek-v4-flash` 별칭은 남는다 — DeepSeek이 이름을 유지한다고 명시했고, 그걸 지우면 기존 사용자 config가 깨진다. +4. **어느 프리셋의 `defaultModel`도** 퇴역 id를 가리키지 않는다. `deepseek`뿐 아니라 `volcengine-agent-plan`(2832)을 포함한다. +5. 주석에도 `deepseek-v4-pro`가 남지 않는다. + +## 검증 + +``` +bun test tests/providers tests/codex-integration/codex-catalog.test.ts +bun test tests/gui/volcengine-providers.test.ts tests/providers/baseten-provider.test.ts +bun run typecheck +rg "deepseek-v4-pro" src --glob "!src/generated/**" +``` + +## 리스크 + +영향 파일이 62개이고, 레지스트리 멤버십을 고정하는 테스트만 24개다(002 정정). 전체 스위트를 로컬에서 돌리지 않으므로(사용자 지시) 놓친 참조는 CI가 잡는다. CI 실패 시 해당 파일만 좁혀 고친다. + +사다리 자체는 바뀌지 않는다는 점도 기록해 둔다: `DEEPSEEK_PRO_THINKING_EFFORTS`와 `DEEPSEEK_FLASH_THINKING_EFFORTS`는 값이 같다(`registry.ts:701-715`). 퇴역으로 실제로 어긋나는 건 컨텍스트 창과 가격이다. diff --git a/devlog/_plan/260911_deepseek_v41_transition/040_phase4_merge.md b/devlog/_plan/260911_deepseek_v41_transition/040_phase4_merge.md new file mode 100644 index 0000000000..b9f263923e --- /dev/null +++ b/devlog/_plan/260911_deepseek_v41_transition/040_phase4_merge.md @@ -0,0 +1,31 @@ +# 040 — wp5: PR 게시와 머지 + +## docs-site 동기화 (감사 추가) + +`deepseek-v4-pro`는 9개 로케일의 `guides/providers.md`, `guides/sidecars.md`, `guides/model-ordering.md`, `reference/configuration/providers.md`와 `docs-site/src/data/frontier-benchmarks.json`에 등장한다. 코드에서 모델을 지우면서 문서가 그대로면 영문 원문과 로케일이 동시에 거짓이 된다. + +범위: 제거된 모델을 **사용 가능한 모델로 제시하는** 문장만 고친다. 벤치마크 데이터(`frontier-benchmarks.json`)는 과거 측정 기록이므로 손대지 않는다 — 생성 메타데이터를 남기는 것과 같은 이유다. + +## 순서 + +1. #4258 머지 (wp2에서 완료) → `dev` fetch → 내 브랜치 리베이스 +2. #4274(Zen 프리셋 안정화) CI green 확인 후 머지 +3. V4.1 전환 변경을 새 PR로 게시하고 CI green 확인 후 머지 + +#4274를 먼저 머지하는 이유: 이미 리뷰가 끝났고 CI가 거의 다 통과했다. V4.1 변경과 같은 파일(`registry.ts`)을 건드리므로, 뒤에 올리는 쪽이 리베이스한다. + +## 머지 조건 (MAINTAINERS.md) + +- base `dev` +- exact-head CI green — 머지 직전 `gh pr checks`로 확인하고 커밋 SHA와 함께 기록 +- 유지관리자 단독 통합 시 결정 근거를 남긴다 +- `main`/`preview` 승격과 릴리스는 이번 범위 밖 + +## PR 본문에 반드시 들어갈 것 + +- V4.1 전환 근거와 출처 링크 +- 조사 결과가 갈렸다는 사실과 어느 해석을 택했는지 (001 참조) +- id 분기 이유: 네이티브 `deepseek-flash` vs 게이트웨이 `deepseek-v4.1-flash` +- v4-pro 제거를 두 커밋으로 나눈 이유와, 벤더 호스팅 커밋만 되돌리는 방법 +- 생성 메타데이터를 손대지 않은 이유 (과거 사용량 원가 계산) +- 전체 스위트를 로컬에서 돌리지 않았다는 사실 diff --git a/devlog/_plan/260911_r2_merge_train/000_plan.md b/devlog/_plan/260911_r2_merge_train/000_plan.md new file mode 100644 index 0000000000..48fc4be82d --- /dev/null +++ b/devlog/_plan/260911_r2_merge_train/000_plan.md @@ -0,0 +1,97 @@ +# 260911 R2 merge train — land #4244, #4248, #4246, #4247 on dev + +## Objective + +Four open PRs authored on 2026-09-11 (`codex/260911-r2-*`) are each 65 commits behind +`origin/dev` at `18e553a52`. All four were green at their pre-rebase heads, and two of +them have since gone `CONFLICTING`. This unit rebases each onto the current `dev`, +re-proves it, and merges it — one at a time, as a serialized train. + +The train is serialized rather than parallel for one concrete reason: #4246 and #4248 +both append to `scripts/test-layout/layout.json` and +`tests/fixtures/test-layout-expected.json`. Those two files are sorted registries that +`tests/test-layout.test.ts` and `tests/test-layout-tooling.test.ts` enforce, so two +branches that each add one line to the same sorted block will conflict textually no +matter how trivially compatible the changes are. Rebasing the second one only after the +first is already on `dev` turns a two-sided conflict into a one-sided replay. + +## Scope + +In scope: the files already touched by the four branches, their conflict resolutions +against `dev`, and this planning unit. + +Out of scope: every other open PR (#4256, #4258, #4259 and all third-party PRs), any new +feature work, any promotion of `main` or `preview`, any force-push to a protected +branch, and any edit to another author's branch. + +## Authority + +The user explicitly authorized rebase, force-push to these four PR branches, and merge +into `dev` in this session. `MAINTAINERS.md` permits a maintainer with `maintain` or +`admin` access to integrate their own PR into `dev` through a PR without a second +approval, provided the decision and exact-head CI evidence are recorded. This document +plus the per-phase records below are that record. + +That authority stops at `dev`. It does not cover `main`/`preview` promotion, releases, +branch deletion beyond the merged PR branches, or any other author's work. + +## Work-phase map (dependency-ordered) + +| Phase | PR | Branch | Pre-state | Doc | +|-------|----|--------|-----------|-----| +| wp1 | — | — | this roadmap | `000_plan.md` | +| wp2 | #4244 | `codex/260911-r2-catalog-pool` | MERGEABLE, clean replay | `010_phase1_pr4244.md` | +| wp3 | #4248 | `codex/260911-r2-pool-account-attribution` | MERGEABLE, clean replay | `020_phase2_pr4248.md` | +| wp4 | #4246 | `codex/260911-r2-client-display` | CONFLICTING, registry-only | `030_phase3_pr4246.md` | +| wp5 | #4247 | `codex/260911-r2-docs-locales` | CONFLICTING, substantive | `040_phase4_pr4247.md` | + +Order is cheapest-and-safest first. #4244 and #4248 replay cleanly onto `dev` +(`git merge-tree --write-tree` exits 0 for both, and `dev` has no commits touching their +source files since the merge base), so they land first and shrink the train before the +two conflicting branches are touched. #4246's conflict is a single sorted-registry line. +#4247's is the only one where `dev` and the PR edited the same prose and the same test +oracle, so it goes last, when nothing else is queued behind it. + +## Verification protocol (every implementation phase) + +Each of wp2–wp5 runs one full PABCD cycle and clears the same gate before its merge: + +1. `git rebase origin/dev` on the PR branch, conflicts resolved by hand, PR intent preserved. +2. `bun run typecheck` — exit 0. +3. The PR's own test files, run by path. Whenever `layout.json` or + `test-layout-expected.json` is in the touch set, add `tests/test-layout.test.ts` and + `tests/test-layout-tooling.test.ts`; those two are the guards that a hand-resolved + registry conflict can silently break. +4. `git push --force-with-lease` to that PR branch only. +5. `gh pr checks ` green at the exact new head SHA — not at a previous head. +6. A comment on the PR recording the maintainer-integration decision and the exact head + SHA that CI verified. `MAINTAINERS.md:59-64` permits a maintainer with `admin` or + `maintain` access to integrate their own PR into `dev` without a second approval, and + requires that the choice and the exact-head verification be recorded in the PR + description or a comment. The account driving this train holds `admin`. +7. `gh pr merge --merge` only after steps 5 and 6. +8. `git fetch origin` and re-check the remaining branches' mergeability, because the + merge just moved the base out from under them. + +The merge method is `--merge`, not `--squash`. The repository allows both, but every +recent integration on `dev` is a merge commit (`18e553a52`, `42184ead0`, `6d8ed37ad`, +`5557612d4`, ...) with the branch's individual commits preserved beneath it. Squashing +these four would break that convention and, for #4246, would discard the two review-round +commit messages that explain what the adversarial review changed. + +`AGENTS.md` reserves the repository-wide `bun run test` for the PR-ready gate and for +touch sets whose dependencies are not visible to Bun's module graph. Every phase here is +already a published PR, so CI runs the full suite on three platforms at step 5 regardless; +the local runs above exist to catch a bad conflict resolution before it costs a CI cycle. + +## Acceptance + +DONE when all four PRs are merged into `dev`, each with green required CI recorded at its +own rebased head SHA, and no target PR is left open or conflicting. + +BLOCKED if a conflict cannot be resolved without changing what the PR meant, or CI fails +at a rebased head for a reason the rebase did not introduce, and the same blocker survives +three goal turns. + +NEEDS_HUMAN if merging requires authority this session does not hold — for example a +branch protection rule that refuses the maintainer self-integration path. diff --git a/devlog/_plan/260911_r2_merge_train/010_phase1_pr4244.md b/devlog/_plan/260911_r2_merge_train/010_phase1_pr4244.md new file mode 100644 index 0000000000..3ee73f39d4 --- /dev/null +++ b/devlog/_plan/260911_r2_merge_train/010_phase1_pr4244.md @@ -0,0 +1,47 @@ +# wp2 — PR #4244 `provider: seed GLM-5.3-Flash on the BigModel Responses preset` + +Branch `codex/260911-r2-catalog-pool`, head `481230445`, one commit, base `dev`. + +## What it changes + +MODIFY `src/providers/registry.ts` — the `zhipu-bigmodel-responses` entry gains +`glm-5.3-flash` in `models`, plus matching entries in `modelContextWindows` +(`1_048_576`), `modelInputModalities` (`["text", "image"]` — the only vision-capable row +on this preset), `modelReasoningEfforts` (`ZAI_GLM_53_REASONING_EFFORTS`), +`modelDefaultReasoningEfforts` (`"max"`) and `modelSupportsReasoningSummaries` (`true`). +`liveModels: false` and `apiKeyValidation: "unknown"` are deliberately unchanged, because +no upstream page establishes an authenticated `/models` contract for this endpoint. + +MODIFY `tests/providers/provider-registry-parity.test.ts` — the oracle test is renamed +from "exports only the officially documented static Codex models" to "exports the +documented Coding Plan roster for the Codex endpoint" and its expected `models` array +becomes `["glm-5.3", "glm-5.3-flash", "glm-5-turbo"]`. The locked-down assertions on +`liveModels` and `apiKeyValidation` stay. + +## Rebase expectation + +Clean. `git merge-tree --write-tree origin/dev origin/codex/260911-r2-catalog-pool` exits +0, and `git log ..origin/dev -- src/providers/registry.ts` is empty, so no +commit on `dev` has touched the registry since this branch forked. The replay should be a +straight fast-forward of one commit onto `18e553a52` or its successor. + +If a conflict does appear, it means another provider row landed on `dev` between this +plan and execution; re-read the incoming hunk before resolving, and keep this PR's row +additive rather than reordering neighbours. + +## Verification + +``` +bun run typecheck +bun test tests/providers/provider-registry-parity.test.ts +``` + +No layout-registry files are touched, so the test-layout guards are not required here. + +## Land + +``` +git push --force-with-lease origin codex/260911-r2-catalog-pool +gh pr checks 4244 --watch +gh pr merge 4244 --merge +``` diff --git a/devlog/_plan/260911_r2_merge_train/020_phase2_pr4248.md b/devlog/_plan/260911_r2_merge_train/020_phase2_pr4248.md new file mode 100644 index 0000000000..8798597029 --- /dev/null +++ b/devlog/_plan/260911_r2_merge_train/020_phase2_pr4248.md @@ -0,0 +1,59 @@ +# wp3 — PR #4248 `pool: name the account when a refresh fails or its models vanish` + +Branch `codex/260911-r2-pool-account-attribution`, head `605034a6d`, one commit, base `dev`. + +## What it changes + +MODIFY `src/codex/catalog/sync.ts`, `src/server/responses/compact.ts`, +`src/server/responses/core.ts` — pool refresh failures and disappearing model rosters are +attributed to the specific account they came from instead of being reported anonymously. + +MODIFY `scripts/test-layout/layout.json` and `tests/fixtures/test-layout-expected.json` — +registry entries for the new test files. + +NEW `tests/responses/responses-pool-refresh-attribution.test.ts` and +`tests/codex-integration/catalog-gated-native-suppression-reason.test.ts`. + +## Rebase expectation + +Clean. `git merge-tree --write-tree` exits 0 against the current `dev`, and `dev` has no +commits touching `sync.ts`, `compact.ts` or `core.ts` since the merge base. The two +registry files auto-merge because this branch's added keys do not collide with the key +`dev` added (`cli-config-show-client.test.ts`). + +This phase runs *before* #4246 deliberately: #4246 adds `cli-connect-readiness.test.ts` to +the same sorted block that `dev` just touched and does conflict. Landing the non-conflicting +registry change first means #4246 later replays against one settled block instead of two +moving ones. + +Note the core-path constraint from `AGENTS.md`: `src/server/responses/core.ts` is one of +the three files that must not reach `src/lab/`. The conflict resolution must not introduce +an import that violates it; `tests/lab/core-lab-boundary.test.ts` is the guard. + +## Verification + +``` +bun run typecheck +bun test tests/responses/responses-pool-refresh-attribution.test.ts +bun test tests/codex-integration/catalog-gated-native-suppression-reason.test.ts +bun test tests/test-layout.test.ts tests/test-layout-tooling.test.ts +bun test tests/lab/core-lab-boundary.test.ts +``` + +The layout guards are mandatory here because both registry files are in the touch set. +The lab-boundary guard is cheap and `core.ts` is in the touch set. + +`compact.ts` has no focused test of its own in this list. Its change threads the account +namespace into `poolCredentialRefreshIncompleteResponse`, which lives in `core.ts` and is +covered by `responses-pool-refresh-attribution.test.ts`, so the behaviour is reached +indirectly rather than unverified. Accepted as-is for a phase that is replaying an already +green PR: CI runs the full suite at the rebased head, which is where a compact-path +regression would surface. Worth a dedicated test if this code is touched again. + +## Land + +``` +git push --force-with-lease origin codex/260911-r2-pool-account-attribution +gh pr checks 4248 --watch +gh pr merge 4248 --merge +``` diff --git a/devlog/_plan/260911_r2_merge_train/030_phase3_pr4246.md b/devlog/_plan/260911_r2_merge_train/030_phase3_pr4246.md new file mode 100644 index 0000000000..cfbed7601e --- /dev/null +++ b/devlog/_plan/260911_r2_merge_train/030_phase3_pr4246.md @@ -0,0 +1,83 @@ +# wp4 — PR #4246 `client: report local Codex readiness instead of bare connected state` + +Branch `codex/260911-r2-client-display`, head `e53999762`, three commits, base `dev`. +State before rebase: `CONFLICTING` / `DIRTY`. + +## What it changes + +MODIFY `src/cli/connect.ts`, `src/cli/status.ts`, `src/client/catalog-compatibility.ts`. +MODIFY `scripts/test-layout/layout.json`, `tests/fixtures/test-layout-expected.json`. +NEW `tests/cli/cli-connect-readiness.test.ts`. +MODIFY `tests/cli/cli-status-json.test.ts`, `tests/clients/client-catalog-compatibility.test.ts`. + +Three commits, the second and third of which fold an adversarial review and give the +write-time gate the same observer in production. Keep all three on the rebase and do not +squash them locally: this repository merges with merge commits, so all three land on +`dev` individually and their messages stay the record of what the review changed. + +## The conflict, exactly + +Two files, one hunk each, and both are the same shape. In `scripts/test-layout/layout.json`: + +``` + "cli-config-command.test.ts": "cli", +<<<<<<< origin/dev + "cli-config-show-client.test.ts": "cli", +======= + "cli-connect-readiness.test.ts": "cli", +>>>>>>> origin/codex/260911-r2-client-display + "cli-dispatch.test.ts": "cli", +``` + +`tests/fixtures/test-layout-expected.json` carries the identical conflict at the same +position with two fewer spaces of indentation. + +This is an additive collision, not a disagreement: `dev` registered +`cli-config-show-client.test.ts` while this branch registered +`cli-connect-readiness.test.ts`. The resolution keeps **both** lines, in sorted order — +`cli-config-show-client.test.ts` first, because `config` sorts before `connect` at the +fourth character (`f` < `n`). + +Resolved form, in both files: + +``` + "cli-config-command.test.ts": "cli", + "cli-config-show-client.test.ts": "cli", + "cli-connect-readiness.test.ts": "cli", + "cli-dispatch.test.ts": "cli", +``` + +Taking either side alone is a silent failure with two different signatures, which is why +the guards below are not optional: dropping `dev`'s line un-registers a test file that is +already on `dev` (`tests/test-layout.test.ts` fails — a file that resolves to no domain), +and dropping this branch's line un-registers the new one (`tests/test-layout-tooling.test.ts` +fails and names the missing entry). + +The guards catch *membership*, not ordering. `tests/test-layout-tooling.test.ts` compares +the fixture with `toEqual` on a parsed object, which is key-order independent, and +`tests/test-layout.test.ts` carries no sort assertion at all. So the sorted placement above +is file hygiene — it keeps the next diff on this block one line instead of a reshuffle — +while the thing the guards would actually fail on is a dropped or mismatched entry. Both +matter; only one of them is machine-enforced, and the resolution should not lean on the +wrong one. + +## Verification + +``` +bun run typecheck +bun test tests/test-layout.test.ts tests/test-layout-tooling.test.ts +bun test tests/cli/cli-connect-readiness.test.ts +bun test tests/cli/cli-status-json.test.ts +bun test tests/clients/client-catalog-compatibility.test.ts +``` + +The two layout guards run first here, not last: they are the direct oracle for the only +hand-edit the rebase requires. + +## Land + +``` +git push --force-with-lease origin codex/260911-r2-client-display +gh pr checks 4246 --watch +gh pr merge 4246 --merge +``` diff --git a/devlog/_plan/260911_r2_merge_train/040_phase4_pr4247.md b/devlog/_plan/260911_r2_merge_train/040_phase4_pr4247.md new file mode 100644 index 0000000000..a4aa41a222 --- /dev/null +++ b/devlog/_plan/260911_r2_merge_train/040_phase4_pr4247.md @@ -0,0 +1,127 @@ +# wp5 — PR #4247 `docs(i18n): make the remote hub guide runnable in every locale` + +Branch `codex/260911-r2-docs-locales`, head `7730f08a7`, one commit, base `dev`. +State before rebase: `CONFLICTING` / `DIRTY`. This is the only substantive conflict in +the train, which is why it is last. + +## What it changes + +MODIFY the seven translated copies of the remote hub guide — +`docs-site/src/content/docs/{fr,ja,ko,ru,tr,zh-cn,zh-tw}/guides/remote-hub.md` — so each +one carries the corrected command ordering that round one (#4200) applied to the English +source only. + +MODIFY `tests/ci-workflows/docs-remote-hub-claims.test.ts` — the oracle stops reading one +file. It gains `TRANSLATED` and `LOCALE_GUIDES` and a `remote hub guide translations` +describe block that runs the same expectations over all eight locales, English included. + +## Why it conflicts + +`dev` landed #4236 in the same two files while this branch was open. #4236 rewrote the +Korean guide for the one-port recipe and added its own `the one-port hub recipe` describe +block plus a `KO_GUIDE` constant to the same test file. So both sides added a block to the +same oracle and both sides rewrote `ko/guides/remote-hub.md`. + +`git merge-tree` reports two conflicted files: the test file with three conflicted regions, +and `ko/guides/remote-hub.md` with four (twelve markers). + +## Resolution contract + +Both sides are additive in intent and neither may be dropped. Concretely: + +**`tests/ci-workflows/docs-remote-hub-claims.test.ts`** + +1. *Header comment.* Keep both paragraphs. `dev`'s explains why the manual + `export OPENCODEX_API_AUTH_TOKEN` step must stay gone; this branch's explains why the + oracle stopped reading one file. They document different groups and neither replaces + the other. +2. *Constants.* Keep `GUIDE`, then this branch's `TRANSLATED` / `LOCALE_GUIDES`, and keep + `dev`'s `KO_GUIDE` — `the one-port hub recipe` block references it directly. Do not + try to derive one from the other; a lookup into `LOCALE_GUIDES` to save four lines would + make `dev`'s block depend on this branch's array ordering for no benefit. +3. *Describe blocks.* Keep both, side by side: `the one-port hub recipe` (en + ko) from + `dev`, and `remote hub guide translations` (all eight) from this branch. + +**`docs-site/src/content/docs/ko/guides/remote-hub.md`** + +The Korean page must satisfy both oracles after the merge, and that is the actual +acceptance test for this resolution rather than any judgement about prose. It must keep +#4236's one-port content — the port-less companion form +`ocx config set unauthenticatedLoopbackListener '{"enabled":true}'`, the ported +`{"enabled":true,"port":10104}` alternative, `service-api-token`, `ocx hub invite`, +`ocx config set corsAllowOrigins '["http://localhost:10100"]'`, `--pairing-code-stdin`, and +**no** line matching `/^\s*export\s+OPENCODEX_API_AUTH_TOKEN/m` — while also keeping this +branch's ordering fix: `ocx config set hub '{}'` before any `ocx config set hub.`, the same +for `remoteGui`, and the literal string `config parent path not found: hub`. + +Where the two rewrites touch the same paragraph, `dev`'s newer one-port wording wins on +content and this branch's corrected command ordering wins on sequence. They are compatible: +the ordering fix is about which `ocx config set` line comes first, not about what the +recipe says. + +## The port reconciliation (audited blocker, must be done) + +This branch's translations block includes `"en"` in `LOCALE_GUIDES` and asserts the same +markers over every locale. #4236 rewrote the English guide after this branch forked, and +the A-phase audit found one marker pair that genuinely diverged. This is not a risk to +check — it is a confirmed conflict with a required fix. + +The branch asserts, for all eight locales: + +``` +socat TCP-LISTEN:10100,bind=127.0.0.1 +tailscale serve --bg --https=8443 http://127.0.0.1:10100 +``` + +`origin/dev` now carries `10110` in both lines, in the English guide and in the Korean one, +and all seven translated guides on this branch still carry `10100`. + +**10110 is the correct value and 10100 is now a defect.** #4236 enabled the loopback +companion listener, which binds `127.0.0.1:10100` — the proxy port itself. The English +guide says so in the comment directly above the command: "Pick a port the hub is not +already using: with the loopback companion enabled, `127.0.0.1:10100` belongs to opencodex +itself." A reader following any of the seven translations would bind socat onto the +companion listener's own port and get a collision. + +So the resolution is not "make the assertion match the file". It is to finish the job this +PR exists to do — carry the English fix into the translations: + +1. In all seven translated guides, change `socat TCP-LISTEN:10100,bind=127.0.0.1` to + `socat TCP-LISTEN:10110,bind=127.0.0.1` and + `tailscale serve --bg --https=8443 http://127.0.0.1:10100` to `...:10110`. The forwarder + *destination* `TCP:100.64.0.10:10100` stays 10100 — that is the tailnet-bound proxy + port and it did not move. Only the loopback listen port changes. +2. Carry the explanatory comment above the command too, in each locale's own language, + and the `tailscale serve status # expect both mappings: 443 -> 10101, 8443 -> 10110` + line. A translation that changes the port without the reason is a worse artifact than + one that is merely stale. +3. Update the two assertions in the translations block to `10110`. +4. Keep `dev`'s `10110` in the Korean guide when resolving its four conflicted regions. + +Every other marker the block pins was audited against the current `dev` English guide and +is still satisfied: the `hub` / `remoteGui` `'{}'` initializer ordering, the literal +`config parent path not found: hub`, the whole-object `ocx config set hub '{"managementPublicOrigin"` +form with its replace-not-merge caveat, `403 origin_rejected`, `X-Forwarded-Host`, and the +absence of `--allow-insecure-http`. + +## Verification + +``` +bun run typecheck +bun test tests/ci-workflows/docs-remote-hub-claims.test.ts +``` + +The oracle reads the eight markdown files as data, which `bun run test:changed` cannot see +through its module graph. That is the indirect-dependency exception in `AGENTS.md`, so run +this file by path and do not rely on change detection to select it. + +No source under `src/` is touched, so this is docs-only work with a test oracle attached; +the relevant consistency gate is the oracle itself. + +## Land + +``` +git push --force-with-lease origin codex/260911-r2-docs-locales +gh pr checks 4247 --watch +gh pr merge 4247 --merge +``` diff --git a/docs-site/src/content/docs/fr/guides/model-ordering.md b/docs-site/src/content/docs/fr/guides/model-ordering.md index b22bf9b827..fab755afdf 100644 --- a/docs-site/src/content/docs/fr/guides/model-ordering.md +++ b/docs-site/src/content/docs/fr/guides/model-ordering.md @@ -124,7 +124,7 @@ au-delà de ce bloc mis en avant : ```json { "modelPickerOrder": [ - "tyler/deepseek-v4-pro", + "tyler/deepseek-v4-flash", "jd-chat/kimi-k3", "jd-chat/glm-5.2" ] diff --git a/docs-site/src/content/docs/fr/guides/providers.md b/docs-site/src/content/docs/fr/guides/providers.md index 67289f68dd..7c9f3fc682 100644 --- a/docs-site/src/content/docs/fr/guides/providers.md +++ b/docs-site/src/content/docs/fr/guides/providers.md @@ -370,7 +370,7 @@ modèle ; les flux mal formés ou partiels sont fermés comme incomplets, et non > des ressources d'embedding, d'image, de vidéo et de 3D, la passerelle Coding renvoie le même catalogue étendu, > et la passerelle Agent Plan ne possède aucune ressource `/models`. Le modèle par défaut de la route facturée à > l'usage est `doubao-seed-2-1-pro-260628` ; son catalogue sélectionné comprend également les modèles de texte -> DeepSeek et GLM actuels. Coding Plan utilise `ark-code-latest` par défaut, et Agent Plan `deepseek-v4-pro`. +> DeepSeek et GLM actuels. Coding Plan utilise `ark-code-latest` par défaut, et Agent Plan `deepseek-v4-flash`. > **Restriction d'utilisation des forfaits Volcengine :** selon la documentation de Volcengine, les quotas > Coding Plan et Agent Plan ne sont valables que dans les outils de programmation par IA pris en charge. Elle @@ -604,7 +604,7 @@ native d'Ollama (`POST /api/chat`) plutôt que via la surface compatible OpenAI, liste des modèles auprès du fournisseur : les nouveaux modèles Ollama Cloud apparaissent sans modifier la configuration. opencodex classe les modèles cloud selon leurs capacités visuelles, afin que le [service auxiliaire de vision](/fr/guides/sidecars/) n'intervienne que pour les modèles -exclusivement textuels. Ces derniers, par exemple `glm-5.2`, `deepseek-v4-pro`, `gpt-oss`, `qwen3-coder`, +exclusivement textuels. Ces derniers, par exemple `glm-5.2`, `deepseek-v4-flash`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x` et `nemotron-3-*`, figurent dans `noVisionModels` ; les modèles à vision native, comme `kimi-k2.6`, `minimax-m3`, `gemma4`, `qwen3.5` et `gemini-3-flash-preview`, n'y figurent pas. La correspondance tolère les balises `:size` d'Ollama : `gpt-oss` couvre donc `gpt-oss:120b` et `gpt-oss:20b`. diff --git a/docs-site/src/content/docs/fr/guides/sidecars.md b/docs-site/src/content/docs/fr/guides/sidecars.md index b778c4c99e..27bcda75cb 100644 --- a/docs-site/src/content/docs/fr/guides/sidecars.md +++ b/docs-site/src/content/docs/fr/guides/sidecars.md @@ -146,7 +146,7 @@ Un modèle est marqué en texte uniquement par fournisseur : "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/fr/reference/configuration/providers.md b/docs-site/src/content/docs/fr/reference/configuration/providers.md index 563286c2bb..b8da2537bb 100644 --- a/docs-site/src/content/docs/fr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/fr/reference/configuration/providers.md @@ -514,7 +514,7 @@ le nom actuel avant d'effectuer une autre modification. "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "glm-5.3", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "glm-5.3", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/guides/model-ordering.md b/docs-site/src/content/docs/guides/model-ordering.md index 5333b839ce..45ec0666d6 100644 --- a/docs-site/src/content/docs/guides/model-ordering.md +++ b/docs-site/src/content/docs/guides/model-ordering.md @@ -122,7 +122,7 @@ featured block: ```json { "modelPickerOrder": [ - "tyler/deepseek-v4-pro", + "tyler/deepseek-v4-flash", "jd-chat/kimi-k3", "jd-chat/glm-5.2" ] diff --git a/docs-site/src/content/docs/guides/providers.md b/docs-site/src/content/docs/guides/providers.md index 6b359a8ad5..7119be8643 100644 --- a/docs-site/src/content/docs/guides/providers.md +++ b/docs-site/src/content/docs/guides/providers.md @@ -401,7 +401,7 @@ free-experimentation model. **OrcaRouter** ([sponsor](https://github.com/lidge-jun/opencodex/blob/main/SPONSORS.md)) is an OpenAI-compatible gateway at `https://api.orcarouter.ai/v1` with vendor-namespaced model ids -(`openai/gpt-5.5`, `anthropic/claude-opus-4.8`, `deepseek/deepseek-v4-pro`, ...) and an adaptive +(`openai/gpt-5.5`, `anthropic/claude-opus-4.8`, `deepseek/deepseek-v4-flash`, ...) and an adaptive router, `orcarouter/auto`, that grades each prompt and picks the model. Create a key in the [OrcaRouter console](https://www.orcarouter.ai/console); the preset pins the row near the top of the Add provider picker and marks it as a sponsor, and nothing else about routing or defaults changes. @@ -524,7 +524,7 @@ streams close as incomplete rather than being reported as successful. > and the Agent Plan gateway has no `/models` resource. Pay-as-you-go defaults to > `doubao-seed-2-1-pro-260628`; its curated catalog also includes current DeepSeek and GLM text > models. Coding Plan defaults to `ark-code-latest`, while Agent Plan defaults to -> `deepseek-v4-pro`. +> `deepseek-v4-flash`. > **Volcengine Plan usage restriction:** Volcengine documents Coding Plan and Agent Plan quota as > valid only inside supported AI coding tools, and warns that using a plan key for general API @@ -961,7 +961,7 @@ Ollama's own REST API (`POST /api/chat`) rather than the OpenAI-compatible surfa the live model roster from the provider, so new Ollama Cloud models appear without a config change. opencodex classifies its cloud lineup by vision capability so the [vision sidecar](/guides/sidecars/) only kicks in for -text-only models. Text-only models (e.g. `glm-5.2`, `deepseek-v4-pro`, `gpt-oss`, `qwen3-coder`, +text-only models. Text-only models (e.g. `glm-5.2`, `deepseek-v4-flash`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x`, `nemotron-3-*`) are listed in `noVisionModels`; vision-native models (e.g. `kimi-k2.6`, `minimax-m3`, `gemma4`, `qwen3.5`, `gemini-3-flash-preview`) are not. Matching is tolerant of Ollama's `:size` tags, so `gpt-oss` covers `gpt-oss:120b` and `gpt-oss:20b`. diff --git a/docs-site/src/content/docs/guides/sidecars.md b/docs-site/src/content/docs/guides/sidecars.md index d0c79d272e..543fe49e96 100644 --- a/docs-site/src/content/docs/guides/sidecars.md +++ b/docs-site/src/content/docs/guides/sidecars.md @@ -185,7 +185,7 @@ A model is marked text-only per provider: "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/ja/guides/providers.md b/docs-site/src/content/docs/ja/guides/providers.md index 5056d63594..d0025981aa 100644 --- a/docs-site/src/content/docs/ja/guides/providers.md +++ b/docs-site/src/content/docs/ja/guides/providers.md @@ -250,7 +250,7 @@ Volcengine Agent Plan は `openai-responses` アダプターでネイティブ R > Agent Plan ゲートウェイには `/models` リソースがありません。従量課金のデフォルトは > `doubao-seed-2-1-pro-260628` で、静的カタログには現在の DeepSeek と GLM のテキストモデルも > 含まれます。Coding Plan のデフォルトは `ark-code-latest`、Agent Plan は -> `deepseek-v4-pro` です。 +> `deepseek-v4-flash` です。 **Chutes の discovery:** `chutes` preset は Chutes の固定された共有 OpenAI 互換 LLM gateway を使います。 公開 `/v1/models` catalog から `supported_features` が `tools` を示す行だけを残し、スラッシュを含む @@ -427,7 +427,7 @@ Ollama Cloud はホステッド型(ローカルではない)Ollama です。`htt サーフェスではなく Ollama 自身の REST API(`POST /api/chat`)で接続し、モデル一覧はプロバイダーから 動的に取得するため、新しい Ollama Cloud モデルは設定変更なしで現れます。opencodex はクラウド ラインナップをビジョン機能で分類し、[ビジョンサイドカー](/ja/guides/sidecars/)がテキスト専用モデルにのみ -動作するようにします。テキスト専用モデル(例: `glm-5.2`、`deepseek-v4-pro`、`gpt-oss`、`qwen3-coder`、 +動作するようにします。テキスト専用モデル(例: `glm-5.2`、`deepseek-v4-flash`、`gpt-oss`、`qwen3-coder`、 `minimax-m2.x`、`nemotron-3-*`)は `noVisionModels` に列挙され、ビジョンネイティブモデル(例: `kimi-k2.6`、`minimax-m3`、`gemma4`、`qwen3.5`、`gemini-3-flash-preview`)は含まれません。マッチングは Ollama の `:size` タグに寛容なので `gpt-oss` は `gpt-oss:120b` と `gpt-oss:20b` の両方を含みます。 diff --git a/docs-site/src/content/docs/ja/guides/sidecars.md b/docs-site/src/content/docs/ja/guides/sidecars.md index dec111160f..8d2fd18ef1 100644 --- a/docs-site/src/content/docs/ja/guides/sidecars.md +++ b/docs-site/src/content/docs/ja/guides/sidecars.md @@ -125,7 +125,7 @@ OpenAI 実行経路、ダッシュボード、管理 API は `gpt-5.4-mini` を "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/ja/reference/configuration/providers.md b/docs-site/src/content/docs/ja/reference/configuration/providers.md index b8ee061b4a..002cedbec0 100644 --- a/docs-site/src/content/docs/ja/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ja/reference/configuration/providers.md @@ -437,7 +437,7 @@ Vercel AI Gateway は、1 つのモデルを複数の基盤となる推論プロ "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/ko/guides/providers.md b/docs-site/src/content/docs/ko/guides/providers.md index 951dff100e..9fb1bf9ed1 100644 --- a/docs-site/src/content/docs/ko/guides/providers.md +++ b/docs-site/src/content/docs/ko/guides/providers.md @@ -248,7 +248,7 @@ Volcengine Agent Plan은 `openai-responses` 어댑터로 네이티브 Responses > Embedding, 이미지, 비디오, 3D 리소스도 반환하고 Coding 게이트웨이도 같은 광범위한 카탈로그를 > 반환합니다. Agent Plan 게이트웨이에는 `/models` 리소스가 없습니다. 종량제 기본값은 > `doubao-seed-2-1-pro-260628`이며 정적 카탈로그에는 현재 DeepSeek와 GLM 텍스트 모델도 -> 포함됩니다. Coding Plan의 기본값은 `ark-code-latest`, Agent Plan은 `deepseek-v4-pro`입니다. +> 포함됩니다. Coding Plan의 기본값은 `ark-code-latest`, Agent Plan은 `deepseek-v4-flash`입니다. **Chutes 검색:** `chutes` 프리셋은 Chutes의 고정된 공유 OpenAI 호환 LLM gateway를 사용합니다. 공개 `/v1/models` catalog에서 `supported_features`가 `tools`를 명시한 행만 유지하고, 슬래시가 포함된 @@ -417,7 +417,7 @@ Ollama Cloud는 호스팅형(로컬이 아님) Ollama입니다. `https://ollama. 표면이 아니라 Ollama 자체 REST API(`POST /api/chat`)로 연결하며, 모델 목록을 공급자에서 직접 발견하므로 새 Ollama Cloud 모델이 설정 변경 없이 나타납니다. opencodex는 클라우드 라인업을 비전 기능에 따라 분류하여 [비전 사이드카](/ko/guides/sidecars/)가 텍스트 전용 모델에만 -작동하도록 합니다. 텍스트 전용 모델(예: `glm-5.2`, `deepseek-v4-pro`, `gpt-oss`, `qwen3-coder`, +작동하도록 합니다. 텍스트 전용 모델(예: `glm-5.2`, `deepseek-v4-flash`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x`, `nemotron-3-*`)은 `noVisionModels`에 나열되며, 비전 네이티브 모델(예: `kimi-k2.6`, `minimax-m3`, `gemma4`, `qwen3.5`, `gemini-3-flash-preview`)은 포함되지 않습니다. 매칭은 Ollama의 `:size` 태그에 관대하므로 `gpt-oss`는 `gpt-oss:120b`와 `gpt-oss:20b`를 모두 포괄합니다. diff --git a/docs-site/src/content/docs/ko/guides/sidecars.md b/docs-site/src/content/docs/ko/guides/sidecars.md index 55af37a150..c07e8c589d 100644 --- a/docs-site/src/content/docs/ko/guides/sidecars.md +++ b/docs-site/src/content/docs/ko/guides/sidecars.md @@ -127,7 +127,7 @@ OpenAI 실행 경로, Dashboard, 관리 API는 `gpt-5.4-mini`를 폴백으로 "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/ko/reference/configuration/providers.md b/docs-site/src/content/docs/ko/reference/configuration/providers.md index 0b5f8bf36a..7cfe363870 100644 --- a/docs-site/src/content/docs/ko/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ko/reference/configuration/providers.md @@ -442,7 +442,7 @@ Vercel AI Gateway는 하나의 모델을 여러 기반 추론 공급자에 걸 "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/reference/configuration/providers.md b/docs-site/src/content/docs/reference/configuration/providers.md index feebe4649b..65e57f19ab 100644 --- a/docs-site/src/content/docs/reference/configuration/providers.md +++ b/docs-site/src/content/docs/reference/configuration/providers.md @@ -921,7 +921,7 @@ ids with context `922000` and max input `922000`; OpenRouter seeds `openai/gpt-5 "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "glm-5.3", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "glm-5.3", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/ru/guides/providers.md b/docs-site/src/content/docs/ru/guides/providers.md index 8b1415f6b8..0811958d03 100644 --- a/docs-site/src/content/docs/ru/guides/providers.md +++ b/docs-site/src/content/docs/ru/guides/providers.md @@ -280,7 +280,7 @@ Volcengine Agent Plan использует нативную конечную т > каталог. У шлюза Agent Plan ресурса `/models` нет. Для pay-as-you-go модель по умолчанию — > `doubao-seed-2-1-pro-260628`; его статический каталог также включает актуальные текстовые модели > DeepSeek и GLM. Для Coding Plan модель по умолчанию — `ark-code-latest`, для Agent Plan — -> `deepseek-v4-pro`. +> `deepseek-v4-flash`. **Discovery для Chutes.** Пресет `chutes` использует фиксированный общий OpenAI-совместимый LLM gateway Chutes. Из публичного каталога `/v1/models` он оставляет только строки, где @@ -474,7 +474,7 @@ Ollama Cloud — это размещённая в облаке (не локал получает список моделей от провайдера, поэтому новые модели Ollama Cloud появляются без изменения конфигурации. opencodex классифицирует её облачную линейку по поддержке изображений, чтобы [vision-сайдкар](/ru/guides/sidecars/) включался -только для текстовых моделей. Текстовые модели (например, `glm-5.2`, `deepseek-v4-pro`, `gpt-oss`, +только для текстовых моделей. Текстовые модели (например, `glm-5.2`, `deepseek-v4-flash`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x`, `nemotron-3-*`) перечислены в `noVisionModels`; модели с нативной поддержкой изображений (например, `kimi-k2.6`, `minimax-m3`, `gemma4`, `qwen3.5`, `gemini-3-flash-preview`) — нет. Сопоставление терпимо к тегам Ollama вида `:size`, поэтому diff --git a/docs-site/src/content/docs/ru/guides/sidecars.md b/docs-site/src/content/docs/ru/guides/sidecars.md index cdb6dc7087..57b437c1e6 100644 --- a/docs-site/src/content/docs/ru/guides/sidecars.md +++ b/docs-site/src/content/docs/ru/guides/sidecars.md @@ -139,7 +139,7 @@ opencodex описывает каждое изображение **до** осн "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/ru/reference/configuration/providers.md b/docs-site/src/content/docs/ru/reference/configuration/providers.md index 37b00b3150..5966758e29 100644 --- a/docs-site/src/content/docs/ru/reference/configuration/providers.md +++ b/docs-site/src/content/docs/ru/reference/configuration/providers.md @@ -533,7 +533,7 @@ Pool/Direct рекламирует `922000`; синхронизированны "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/tr/guides/providers.md b/docs-site/src/content/docs/tr/guides/providers.md index 1c166afd9b..9b9b557d24 100644 --- a/docs-site/src/content/docs/tr/guides/providers.md +++ b/docs-site/src/content/docs/tr/guides/providers.md @@ -409,7 +409,7 @@ bitirir ancak son Responses olayını atlarsa opencodex beş saniyelik model kapsamlı bir yetkisiz kullanım onarımı uygular; hatalı biçimlendirilmiş veya kısmi akışlar başarılı olarak bildirilmek yerine tamamlanmamış olarak kapanır. -> **Üç Volcengine faturalandırma rotası:** `volcengine` kullandıkça öde Ark API'sidir, `volcengine-coding-plan` Coding Plan kotasını tüketir ve `volcengine-agent-plan` Agent Plan kotasını tüketir. Aynı ürün için verilen anahtarı ve uç noktayı kullanın; sıradan `/api/v3` uç noktası bir Plan aboneliği mevcut olduğunda bile kullandıkça öde ücretlerine neden olabilir. Önayarlar özenle seçilmiş statik model katalogları kullanır çünkü Ark'ın `/models` yanıtı yerleştirme, görsel, video ve 3D kaynaklarını da içerir, Coding ağ geçidi aynı geniş kataloğu döndürür ve Agent Plan ağ geçidinin `/models` kaynağı yoktur. Kullandıkça öde varsayılan olarak `doubao-seed-2-1-pro-260628`'dir; seçilmiş kataloğu güncel DeepSeek ve GLM metin modellerini de içerir. Coding Plan varsayılan olarak `ark-code-latest`, Agent Plan ise varsayılan olarak `deepseek-v4-pro`'dur. +> **Üç Volcengine faturalandırma rotası:** `volcengine` kullandıkça öde Ark API'sidir, `volcengine-coding-plan` Coding Plan kotasını tüketir ve `volcengine-agent-plan` Agent Plan kotasını tüketir. Aynı ürün için verilen anahtarı ve uç noktayı kullanın; sıradan `/api/v3` uç noktası bir Plan aboneliği mevcut olduğunda bile kullandıkça öde ücretlerine neden olabilir. Önayarlar özenle seçilmiş statik model katalogları kullanır çünkü Ark'ın `/models` yanıtı yerleştirme, görsel, video ve 3D kaynaklarını da içerir, Coding ağ geçidi aynı geniş kataloğu döndürür ve Agent Plan ağ geçidinin `/models` kaynağı yoktur. Kullandıkça öde varsayılan olarak `doubao-seed-2-1-pro-260628`'dir; seçilmiş kataloğu güncel DeepSeek ve GLM metin modellerini de içerir. Coding Plan varsayılan olarak `ark-code-latest`, Agent Plan ise varsayılan olarak `deepseek-v4-flash`'dur. > **Volcengine Plan kullanım kısıtlaması:** Volcengine, Coding Plan ve Agent Plan kotasını yalnızca desteklenen yapay zeka kodlama araçları içinde geçerli olarak belgeler ve genel API çağrıları için bir plan anahtarı kullanmanın aboneliği askıya alabileceği veya hesabı yasaklayabileceği konusunda uyarır. Codex veya Claude Code'u opencodex üzerinden yönlendirmek belgelenmiş kullanımdır; diğer otomasyonları bir plan anahtarına yönlendirmek değildir. Kullandıkça öde `volcengine` rotası böyle bir kısıtlama taşımaz. @@ -652,7 +652,7 @@ listesini sağlayıcıdan keşfeder; böylece yeni Ollama Cloud modelleri yapıl değişikliği olmadan görünür. opencodex, bulut serisini vizyon yeteneğine göre sınıflandırır, böylece [vizyon sidecar'ı](/tr/guides/sidecars/) yalnızca salt metin modeller için devreye girer. Salt metin modeller (örneğin -`glm-5.2`, `deepseek-v4-pro`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x`, +`glm-5.2`, `deepseek-v4-flash`, `gpt-oss`, `qwen3-coder`, `minimax-m2.x`, `nemotron-3-*`) `noVisionModels` içinde listelenir; vizyon yerel modeller (örneğin `kimi-k2.6`, `minimax-m3`, `gemma4`, `qwen3.5`, `gemini-3-flash-preview`) listelenmez. Eşleştirme Ollama'nın `:size` diff --git a/docs-site/src/content/docs/tr/guides/sidecars.md b/docs-site/src/content/docs/tr/guides/sidecars.md index 5205f0041f..6dc32cd15d 100644 --- a/docs-site/src/content/docs/tr/guides/sidecars.md +++ b/docs-site/src/content/docs/tr/guides/sidecars.md @@ -178,7 +178,7 @@ Bir model, sağlayıcı başına salt metin olarak işaretlenir: "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/tr/reference/configuration/providers.md b/docs-site/src/content/docs/tr/reference/configuration/providers.md index f0739bbc63..7b0c14cfcc 100644 --- a/docs-site/src/content/docs/tr/reference/configuration/providers.md +++ b/docs-site/src/content/docs/tr/reference/configuration/providers.md @@ -558,7 +558,7 @@ geçerli adı kontrol edin. "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/zh-cn/guides/providers.md b/docs-site/src/content/docs/zh-cn/guides/providers.md index bcb4e3f3e9..084f6cd38b 100644 --- a/docs-site/src/content/docs/zh-cn/guides/providers.md +++ b/docs-site/src/content/docs/zh-cn/guides/providers.md @@ -241,7 +241,7 @@ Cline IDE/CLI 中提供,不能通过 API 使用;`minimax/minimax-m2.5` 是 > 视频和 3D 资源,Coding 网关也会返回这份宽泛目录,Agent Plan 网关没有 `/models` 资源。 > 按量付费默认使用 `doubao-seed-2-1-pro-260628`,静态目录还包含当前 DeepSeek 和 GLM > 文本模型。Coding Plan 默认使用 `ark-code-latest`,Agent Plan 默认使用 -> `deepseek-v4-pro`。 +> `deepseek-v4-flash`。 **Chutes 发现:**`chutes` 预设使用 Chutes 固定的共享 OpenAI 兼容 LLM gateway。它读取公开的 `/v1/models` 目录,仅保留 `supported_features` 包含 `tools` 的记录,保留含 `/` 的原生 model id 与 @@ -438,7 +438,7 @@ Cursor OAuth 和 live model discovery 已在这个实验性 adapter 中启用; ### Ollama Cloud -Ollama Cloud 是托管(而非本地)的 Ollama,配置地址为 `https://ollama.com/v1`,密钥来自 [ollama.com/settings/keys](https://ollama.com/settings/keys)。opencodex 通过 Ollama 自身的 REST API(`POST /api/chat`)连接,而不是 OpenAI 兼容接口,并从提供方动态发现模型列表,因此新的 Ollama Cloud 模型无需改动配置即可出现。opencodex 按视觉能力对其云端阵容进行分类,使 [vision sidecar](/zh-cn/guides/sidecars/) 仅对纯文本模型生效。纯文本模型(例如 `glm-5.2`、`deepseek-v4-pro`、`gpt-oss`、`qwen3-coder`、`minimax-m2.x`、`nemotron-3-*`)列在 `noVisionModels` 中;原生支持视觉的模型(例如 `kimi-k2.6`、`minimax-m3`、`gemma4`、`qwen3.5`、`gemini-3-flash-preview`)则不在其中。匹配能容忍 Ollama 的 `:size` 标签,因此 `gpt-oss` 涵盖 `gpt-oss:120b` 和 `gpt-oss:20b`。 +Ollama Cloud 是托管(而非本地)的 Ollama,配置地址为 `https://ollama.com/v1`,密钥来自 [ollama.com/settings/keys](https://ollama.com/settings/keys)。opencodex 通过 Ollama 自身的 REST API(`POST /api/chat`)连接,而不是 OpenAI 兼容接口,并从提供方动态发现模型列表,因此新的 Ollama Cloud 模型无需改动配置即可出现。opencodex 按视觉能力对其云端阵容进行分类,使 [vision sidecar](/zh-cn/guides/sidecars/) 仅对纯文本模型生效。纯文本模型(例如 `glm-5.2`、`deepseek-v4-flash`、`gpt-oss`、`qwen3-coder`、`minimax-m2.x`、`nemotron-3-*`)列在 `noVisionModels` 中;原生支持视觉的模型(例如 `kimi-k2.6`、`minimax-m3`、`gemma4`、`qwen3.5`、`gemini-3-flash-preview`)则不在其中。匹配能容忍 Ollama 的 `:size` 标签,因此 `gpt-oss` 涵盖 `gpt-oss:120b` 和 `gpt-oss:20b`。 Ollama 目前在文档中说明结构化输出在 Ollama Cloud 上不受支持。因此对正典 `ollama-cloud`, opencodex 会以明确的错误拒绝结构化输出请求(`text.format`),而不是悄悄返回不受约束的自由 diff --git a/docs-site/src/content/docs/zh-cn/guides/sidecars.md b/docs-site/src/content/docs/zh-cn/guides/sidecars.md index f2dfec5c95..9d148c85b8 100644 --- a/docs-site/src/content/docs/zh-cn/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-cn/guides/sidecars.md @@ -113,7 +113,7 @@ Dashboard 和管理 API 都使用 `gpt-5.4-mini` 作为回退。启动时仍会 "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md index de314d8d8e..01f499e454 100644 --- a/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-cn/reference/configuration/providers.md @@ -434,7 +434,7 @@ Vercel AI Gateway 可以在多个底层推理提供者之间路由一个模型 "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/docs-site/src/content/docs/zh-tw/guides/providers.md b/docs-site/src/content/docs/zh-tw/guides/providers.md index 82ec32f008..6dd562ec93 100644 --- a/docs-site/src/content/docs/zh-tw/guides/providers.md +++ b/docs-site/src/content/docs/zh-tw/guides/providers.md @@ -320,7 +320,7 @@ incomplete 關閉,不會被誤報為成功。 > pay-as-you-go 費用。preset 使用 curated static model catalog,因為 Ark `/models` 也包含 embedding、 > image、video 與 3D resource,Coding gateway 會回傳相同 broad catalog,而 Agent Plan gateway 沒有 > `/models` resource。Pay-as-you-go 預設 `doubao-seed-2-1-pro-260628`,curated catalog 也包含目前的 -> DeepSeek 與 GLM text model。Coding Plan 預設 `ark-code-latest`;Agent Plan 預設 `deepseek-v4-pro`。 +> DeepSeek 與 GLM text model。Coding Plan 預設 `ark-code-latest`;Agent Plan 預設 `deepseek-v4-flash`。 > **Volcengine Plan 使用限制:** Volcengine 文件指出 Coding Plan 與 Agent Plan quota 只能在受支援的 > AI coding tool 內使用,並警告把 plan key 用於一般 API call 可能導致訂閱停權或帳號封鎖。透過 @@ -507,7 +507,7 @@ key 來自 [ollama.com/settings/keys](https://ollama.com/settings/keys)。openco REST API(`POST /api/chat`)連線,而非 OpenAI-compatible 介面,並向 provider 動態探索模型清單, 因此新的 Ollama Cloud 模型不需改設定就會出現。opencodex 依 vision capability 分類其 cloud lineup,讓 [vision sidecar](/zh-tw/guides/sidecars/) 只對純文字模型生效。純文字模型,例如 -`glm-5.2`、`deepseek-v4-pro`、`gpt-oss`、`qwen3-coder`、`minimax-m2.x`、`nemotron-3-*`,會列在 +`glm-5.2`、`deepseek-v4-flash`、`gpt-oss`、`qwen3-coder`、`minimax-m2.x`、`nemotron-3-*`,會列在 `noVisionModels`;原生 vision 模型,例如 `kimi-k2.6`、`minimax-m3`、`gemma4`、`qwen3.5`、 `gemini-3-flash-preview`,不會列入。matching 可容忍 Ollama 的 `:size` tag,因此 `gpt-oss` 同時涵蓋 `gpt-oss:120b` 與 `gpt-oss:20b`。 diff --git a/docs-site/src/content/docs/zh-tw/guides/sidecars.md b/docs-site/src/content/docs/zh-tw/guides/sidecars.md index 60131afd19..6462a5df2d 100644 --- a/docs-site/src/content/docs/zh-tw/guides/sidecars.md +++ b/docs-site/src/content/docs/zh-tw/guides/sidecars.md @@ -108,7 +108,7 @@ OAuth 帳號時使用 `anthropic`,否則使用 `openai`。明確選擇 `anthro "providers": { "ollama-cloud": { "baseUrl": "https://ollama.com/v1", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } } } diff --git a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md index 292b8e63dc..b47d92eb48 100644 --- a/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md +++ b/docs-site/src/content/docs/zh-tw/reference/configuration/providers.md @@ -387,7 +387,7 @@ Vercel AI Gateway 可在多個底層推論供應商之間路由一個模型。`v "baseUrl": "https://ollama.com/v1", "apiKey": "${OLLAMA_API_KEY}", "defaultModel": "glm-5.2", - "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-pro"] + "noVisionModels": ["glm-5.2", "gpt-oss", "qwen3-coder", "deepseek-v4-flash"] } }, "subagentModels": ["anthropic/claude-opus-5", "ollama-cloud/glm-5.2"], diff --git a/src/adapters/cline-pass-deepseek-v4-tool-replay.ts b/src/adapters/cline-pass-deepseek-v4-tool-replay.ts index e2d0369974..cf28da46fe 100644 --- a/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +++ b/src/adapters/cline-pass-deepseek-v4-tool-replay.ts @@ -2,7 +2,6 @@ import type { ProviderAdapter } from "./base"; const CLINE_PASS_DEEPSEEK_V4_MODELS = new Set([ "cline-pass/deepseek-v4-flash", - "cline-pass/deepseek-v4-pro", ]); function isRecord(value: unknown): value is Record { diff --git a/src/adapters/command-code.ts b/src/adapters/command-code.ts index c20dc88be6..4b7c707d7e 100644 --- a/src/adapters/command-code.ts +++ b/src/adapters/command-code.ts @@ -495,7 +495,6 @@ function supportedCommandCodeEffort(provider: OcxProviderConfig, modelId: string let wire = requested; const lower = canonicalId.toLowerCase(); const needsAlias = - lower === "deepseek/deepseek-v4-pro" || lower === "deepseek/deepseek-v4-flash" || lower === "zai-org/glm-5.2"; if (requested === "xhigh" && !supported.includes("xhigh") && supported.includes("max")) { diff --git a/src/codex/catalog/parsing.ts b/src/codex/catalog/parsing.ts index 8430622092..6ddf6512e6 100644 --- a/src/codex/catalog/parsing.ts +++ b/src/codex/catalog/parsing.ts @@ -183,6 +183,23 @@ export const ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS = new Set([ // Issue #2330: OpenCode Go models absent from current documentation or returning terminal HTTP 400 errors. "opencode-go/mimo-v2-omni", "opencode-go/mimo-v2-pro", + /* + * DeepSeek retired `deepseek-v4-pro` on 2026-09-14 04:00 UTC and routes its requests to + * V4.1-Flash (api-docs.deepseek.com/news/news260910). Deleting the registry rows removes + * the model on providers that publish a static roster, but every provider below discovers + * its models live — there, a deleted row does not remove anything, it only strips the + * context window, the effort ladder and the text-only hint, so the retired model would + * keep appearing with its capabilities broken. Excluding the slug is what actually takes + * it out of the routed catalog. + */ + "command-code/deepseek-deepseek-v4-pro", + "commandcode/deepseek-deepseek-v4-pro", + "orcarouter/deepseek-deepseek-v4-pro", + "cline-pass/cline-pass-deepseek-v4-pro", + "baseten/deepseek-ai-DeepSeek-V4-Pro", + "digitalocean/deepseek-v4-pro", + "qoder/DeepSeek-V4-Pro", + "codebuddy/deepseek-v4-pro", ]); export function isRoutedModelCompatibilityExcluded(slug: string): boolean { diff --git a/src/providers/codebuddy-models.ts b/src/providers/codebuddy-models.ts index edd6a30415..ceb7463ce8 100644 --- a/src/providers/codebuddy-models.ts +++ b/src/providers/codebuddy-models.ts @@ -35,7 +35,6 @@ export const CODEBUDDY_GLOBAL_MODELS = [ /** China (`internal`) session models from the official internal manifest (text/chat models only). */ export const CODEBUDDY_CN_MODELS = [ "default", - "deepseek-v4-pro", "deepseek-v4-flash", "minimax-m3", "minimax-m2.7", @@ -121,7 +120,6 @@ export const CODEBUDDY_GLOBAL_MODEL_DEFAULT_REASONING_EFFORTS: Record = { "default": 200_000, - "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "minimax-m3": 512_000, "minimax-m2.7": 200_000, @@ -142,7 +140,6 @@ export const CODEBUDDY_CN_MODEL_CONTEXT_WINDOWS: Record = { export const CODEBUDDY_CN_MODEL_MAX_OUTPUT_TOKENS: Record = { "default": 24_000, - "deepseek-v4-pro": 50_000, "deepseek-v4-flash": 50_000, "minimax-m3": 128_000, "minimax-m2.7": 48_000, diff --git a/src/providers/command-code-efforts.ts b/src/providers/command-code-efforts.ts index 621eb330bc..31a11e3af5 100644 --- a/src/providers/command-code-efforts.ts +++ b/src/providers/command-code-efforts.ts @@ -1,10 +1,6 @@ import { readBoundedResponseBody } from "../lib/bounded-body"; const COMMAND_CODE_MODEL_EFFORTS = { - "deepseek/deepseek-v4-pro": { - efforts: ["high", "max"], - profileUrl: "https://commandcode.ai/models/deepseek-v4-pro", - }, "deepseek/deepseek-v4-flash": { efforts: ["high", "max"], profileUrl: "https://commandcode.ai/models/deepseek-v4-flash", @@ -130,6 +126,29 @@ const COMMAND_CODE_MODEL_EFFORTS = { efforts: ["low", "medium", "high", "xhigh", "max"], profileUrl: "https://commandcode.ai/models/meta-muse-spark-1.1", }, + /* + * Two live routes that never gained a row here, so the adapter dropped every + * requested effort (a client's `max` left the wire as no reasoning parameter + * at all) and the preset advertised no effort control for them. + * + * PROVENANCE, stated plainly: both ladders are inferred from the same-family + * rows above — deepseek v4: high..max; the Qwen 3.8 family: low..max — NOT + * read from the profile pages. commandcode.ai renders those client-side and + * ships an empty reasoning payload, so the self-refresh below is as dead for + * these rows as the #2647 block above already documents. Measured live + * 2026-09-11: /alpha/generate accepts `reasoning_effort: "max"` on both + * routes (HTTP 200). `ultra` is deliberately not offered: the adapter would + * strip it, and no profile evidence backs an ultra→max alias the way it does + * for v4-pro/v4-flash above. + */ + "deepseek/deepseek-v4.1-flash": { + efforts: ["high", "max"], + profileUrl: "https://commandcode.ai/models/deepseek-v4-1-flash", + }, + "Qwen/Qwen3.8-Flash": { + efforts: ["low", "medium", "high", "max"], + profileUrl: "https://commandcode.ai/models/qwen3-8-flash", + }, } as const; /** diff --git a/src/providers/default-aliases.ts b/src/providers/default-aliases.ts index dffbd9eb3c..30128db749 100644 --- a/src/providers/default-aliases.ts +++ b/src/providers/default-aliases.ts @@ -51,7 +51,11 @@ export const DEFAULT_MODEL_ALIASES: ReadonlyArray<{ match: RegExp; alias: string { match: /^claude-haiku/, alias: "haiku" }, { match: /^gemini-3(?:\.\d+)?-pro/, alias: "g3p" }, { match: /^gemini-3(?:\.\d+)?-flash/, alias: "g3f" }, + // Ordered before the V4 rule on purpose: `builtinRule` takes the first match, and + // `/^deepseek-v4/` also matches `deepseek-v4.1-flash`. + { match: /^deepseek-v4\.1/, alias: "ds41" }, { match: /^deepseek-v4/, alias: "ds4" }, + { match: /^deepseek-flash/, alias: "dsf" }, { match: /^grok-4/, alias: "grok" }, ]; diff --git a/src/providers/qoder-models.ts b/src/providers/qoder-models.ts index 0f8d8c4350..bb127aedba 100644 --- a/src/providers/qoder-models.ts +++ b/src/providers/qoder-models.ts @@ -10,7 +10,6 @@ export const QODER_GLOBAL_MODELS = [ "Kimi-K2.7-Code", "GLM-5.3", "GLM-5.2", - "DeepSeek-V4-Pro", ] as const; /** Live Qoder CN roster captured from the official CLI on 2026-09-03. */ diff --git a/src/providers/registry.ts b/src/providers/registry.ts index f9cbcc598e..fb9db46101 100644 --- a/src/providers/registry.ts +++ b/src/providers/registry.ts @@ -616,7 +616,30 @@ const THINKING_BUDGET_MODELS = [ "qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus", ]; const OPENCODE_GO_THINKING_BUDGET_MODELS = ["qwen3.5-plus", "qwen3.6-plus", "qwen3.7-max", "qwen3.7-plus"]; -const DEEPSEEK_THINKING_MODELS = ["deepseek-v4-pro", "deepseek-v4-flash"]; +/* + * DeepSeek moved the whole V4 name set on 2026-09-10. V4.1-Flash ships as deepseek-flash + * on the first-party API; deepseek-v4-flash and the vision preview retire as models but + * keep routing there as compatibility aliases, and deepseek-v4-pro follows from + * 2026-09-14 04:00 UTC. Evidence: https://api-docs.deepseek.com/news/news260910/. + * + * The spelling differs by who serves it, so one shared list cannot express it: the + * first-party API answers to deepseek-flash, while the Zen gateway exposes the route as + * deepseek-v4.1-flash (issue #4253, PR #4258). Vendor-hosted rosters (Volcengine plan + * snapshots, Alibaba) publish on their own schedule and keep the legacy set until they say + * otherwise - a first-party retirement notice does not end their deployment. + */ +const DEEPSEEK_V4_LEGACY_MODELS = ["deepseek-v4-flash"]; +/* + * `deepseek-v4-pro` is deliberately absent from both live sets. DeepSeek retires it from + * 2026-09-14 04:00 UTC and routes its requests to V4.1-Flash until a V4.1 Pro exists, so a + * row here would advertise a Pro context window and Pro pricing for a route that serves + * Flash. The retirement is followed through every roster in this file, including the + * vendor-hosted ones; providers that discover their models live are handled by + * `ROUTED_MODEL_COMPATIBILITY_EXCLUSIONS` because deleting a row there removes the + * model's capabilities rather than the model. + */ +const DEEPSEEK_NATIVE_THINKING_MODELS = ["deepseek-flash", "deepseek-v4-flash"]; +const DEEPSEEK_GATEWAY_THINKING_MODELS = ["deepseek-v4.1-flash", "deepseek-v4-flash"]; /* * DeepSeek's experimental vision preview (released 2026-08-21, api-docs.deepseek.com): * text+image input on the V4 Flash base. DeepSeek positions it as a preview id; @@ -628,7 +651,7 @@ const DEEPSEEK_VISION_PREVIEW_MODEL = "deepseek-v4-flash-vision-exp"; * CommandCode routes verified to accept image input end-to-end (#2406). * * Verified-negative and therefore deliberately ABSENT: deepseek/deepseek-v4-flash, - * deepseek/deepseek-v4-pro, zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those + * zai-org/GLM-5.2, zai-org/GLM-5.3, xai/grok-4.6. Those * routes accept the request and drop the image, which is worse than declining it — the * model answers about an image it never saw. Do not add an id here on family resemblance; * capability intersection trusts this map. @@ -716,7 +739,7 @@ const DEEPSEEK_FLASH_REASONING_MAP: Record = { }; /** * Flash-versus-Pro classification for DeepSeek V4 model ids, including prefixed - * (`deepseek/deepseek-v4-pro`) and suffixed (`deepseek-v4-flash-free`) forms. + * (`deepseek/deepseek-v4.1-flash`) and suffixed (`deepseek-v4-flash-free`) forms. * `tests/providers/provider-registry-parity.test.ts` enumerates every id the registry * actually passes here, so a future id this substring test would misread cannot * land silently. @@ -733,7 +756,7 @@ const deepseekReasoningMapFor = (modelId: string): Record => // https://help.aliyun.com/en/model-studio/token-plan-quickstart const ALIBABA_TOKEN_PLAN_MODELS = [ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash", - "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", + "glm-5.3", "glm-5.3-flash", "glm-5.2", ]; const ALIBABA_TOKEN_PLAN_QWEN_MODELS = [ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash", @@ -746,7 +769,6 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record = { "glm-5.3": ["text"], "glm-5.3-flash": ["text", "image"], "glm-5.2": ["text"], - "deepseek-v4-pro": ["text"], }; // 260721 Alibaba Token Plan International (ap-southeast-1 / Singapore, hardened 260721). @@ -755,7 +777,7 @@ const ALIBABA_TOKEN_PLAN_INPUT_MODALITIES: Record = { // https://qwencloud.com/pricing/token-plan (qwen3.8 metadata) const ALIBABA_INTL_TOKEN_PLAN_MODELS = [ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash", - "deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", + "deepseek-v4-flash", "deepseek-v3.2", "kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "glm-5.3", "glm-5.3-flash", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5", @@ -788,7 +810,6 @@ const VOLCENGINE_ARK_MODELS = [ "doubao-seed-2-1-pro-260628", "doubao-seed-2-1-turbo-260628", "doubao-seed-evolving", - "deepseek-v4-pro-260425", "deepseek-v4-flash-260425", "deepseek-v3-2-251201", // No glm-5-3 row: Ark pins date-stamped snapshot ids (glm-5-2-260617) that cannot be @@ -804,7 +825,6 @@ const VOLCENGINE_DOUBAO_THINKING_MODELS = [ const VOLCENGINE_CODING_PLAN_MODELS = [ "ark-code-latest", "doubao-seed-2.0-code", - "deepseek-v4-pro", "deepseek-v4-flash", "glm-5.3", "glm-5.3-flash", @@ -813,7 +833,6 @@ const VOLCENGINE_CODING_PLAN_MODELS = [ "minimax-m3", ]; const VOLCENGINE_AGENT_PLAN_MODELS = [ - "deepseek-v4-pro", "deepseek-v4-flash", "glm-5.3", "glm-5.3-flash", @@ -835,7 +854,6 @@ const VOLCENGINE_PLAN_INPUT_MODALITIES: Record = { const VOLCENGINE_PLAN_TEXT_ONLY_MODELS = [ "ark-code-latest", "doubao-seed-2.0-code", - "deepseek-v4-pro", "deepseek-v4-flash", "glm-5.3", "glm-5.2", @@ -847,7 +865,6 @@ const ALIBABA_INTL_TOKEN_PLAN_INPUT_MODALITIES: Record = { "qwen3.7-plus": ["text", "image"], "qwen3.6-plus": ["text", "image"], "qwen3.6-flash": ["text", "image"], - "deepseek-v4-pro": ["text"], "deepseek-v4-flash": ["text"], "deepseek-v3.2": ["text"], "kimi-k2.7-code": ["text", "image"], @@ -966,7 +983,7 @@ const NVIDIA_NIM_VISION_INPUT_MODALITIES: Record = Object.from * reasoning suppression regardless of which list they appear in here. */ const NVIDIA_NIM_NO_VISION_MODELS = [ - "deepseek-ai/deepseek-v4-flash", "deepseek-ai/deepseek-v4-pro", + "deepseek-ai/deepseek-v4-flash", "google/codegemma-7b", "meta/llama-3.1-70b-instruct", "meta/llama-3.1-8b-instruct", "meta/llama-3.2-1b-instruct", "meta/llama-3.2-3b-instruct", @@ -1007,7 +1024,6 @@ const NEURALWATT_REASONING_HISTORY_MODELS = [ // https://docs.baseten.co/inference/model-apis/vision const BASETEN_FULL_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"]; const BASETEN_MODEL_REASONING_EFFORTS: Record = { - "deepseek-ai/DeepSeek-V4-Pro": BASETEN_FULL_REASONING_EFFORTS, "thinkingmachines/inkling": BASETEN_FULL_REASONING_EFFORTS, "openai/gpt-oss-120b": BASETEN_FULL_REASONING_EFFORTS, "moonshotai/Kimi-K3": ["low", "high", "max"], @@ -1018,7 +1034,6 @@ const BASETEN_MODEL_REASONING_EFFORTS: Record = { "zai-org/GLM-5.2-Fast": ["high", "max"], }; const BASETEN_MODEL_REASONING_EFFORT_MAP: Record> = { - "deepseek-ai/DeepSeek-V4-Pro": { none: "none", minimal: "minimal" }, "thinkingmachines/inkling": { none: "none", minimal: "minimal" }, "openai/gpt-oss-120b": { none: "none", minimal: "minimal" }, "moonshotai/Kimi-K3": { none: "none" }, @@ -1028,7 +1043,6 @@ const BASETEN_MODEL_REASONING_EFFORT_MAP: Record> "zai-org/GLM-5.2-Fast": { none: "none" }, }; const BASETEN_MODEL_DEFAULT_REASONING_EFFORTS: Record = { - "deepseek-ai/DeepSeek-V4-Pro": "medium", "thinkingmachines/inkling": "high", "openai/gpt-oss-120b": "medium", "moonshotai/Kimi-K3": "max", @@ -1056,7 +1070,6 @@ const DIGITALOCEAN_CHAT_COMPLETION_MODELS = [ "openai-gpt-5.6-luna", "qwen3-coder-flash", "qwen3.5-397b-a17b", - "deepseek-v4-pro", "deepseek-4-flash", "deepseek-3.2", "gemma-4-31B-it", @@ -1141,7 +1154,6 @@ const CLINE_PASS_MODELS = [ "cline-pass/kimi-k3", "cline-pass/kimi-k2.7-code", "cline-pass/kimi-k2.6", - "cline-pass/deepseek-v4-pro", "cline-pass/deepseek-v4-flash", "cline-pass/mimo-v2.5", "cline-pass/mimo-v2.5-pro", @@ -1177,17 +1189,11 @@ const ORCAROUTER_MODELS = [ "openai/gpt-5.5", "anthropic/claude-opus-4.8", "google/gemini-3.5-flash", - "deepseek/deepseek-v4-pro", "orcarouter/auto", ]; -const ORCAROUTER_TEXT_ONLY_MODELS = ["deepseek/deepseek-v4-pro"]; const ORCAROUTER_MODEL_REASONING_EFFORTS = { // Live /models currently exposes ids and modalities, not the accepted reasoning ladder. "openai/gpt-5.5": ["low", "medium", "high", "xhigh"], - "deepseek/deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek/deepseek-v4-pro"), -}; -const ORCAROUTER_MODEL_REASONING_EFFORT_MAP = { - "deepseek/deepseek-v4-pro": deepseekReasoningMapFor("deepseek/deepseek-v4-pro"), }; const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record = { "cline-pass/glm-5.3": 1_048_576, @@ -1196,7 +1202,6 @@ const CLINE_PASS_MODEL_CONTEXT_WINDOWS: Record = { "cline-pass/kimi-k3": 1_048_576, "cline-pass/kimi-k2.7-code": 262_144, "cline-pass/kimi-k2.6": 262_144, - "cline-pass/deepseek-v4-pro": 1_048_576, "cline-pass/deepseek-v4-flash": 1_048_576, "cline-pass/mimo-v2.5": 1_050_000, "cline-pass/mimo-v2.5-pro": 1_050_000, @@ -1440,10 +1445,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ models: ORCAROUTER_MODELS, liveModels: true, modelDiscovery: ORCAROUTER_MODEL_DISCOVERY, - noVisionModels: ORCAROUTER_TEXT_ONLY_MODELS, modelReasoningEfforts: ORCAROUTER_MODEL_REASONING_EFFORTS, - modelReasoningEffortMap: ORCAROUTER_MODEL_REASONING_EFFORT_MAP, - preserveReasoningContentModels: ORCAROUTER_TEXT_ONLY_MODELS, note: "Connect your OrcaRouter account with OAuth 2.0 + PKCE; the issued API key is stored in OpenCodex's existing credential store.", }, { @@ -1757,7 +1759,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ "kimi-k2.7-code-highspeed": [], ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_EFFORTS])), ...Object.fromEntries(OPENCODE_GO_THINKING_BUDGET_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])), - ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])), + ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])), }, modelDefaultReasoningEfforts: { "grok-4.6": "high", "kimi-k3": "max" }, // glm-5.2 uses identity labels now that `max` is a native Codex level (no alias map); @@ -1765,7 +1767,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelReasoningEffortMap: { "kimi-k3": KIMI_CODING_K3_REASONING_EFFORT_MAP, ...Object.fromEntries(OPENCODE_GO_THINKING_TOGGLE_MODELS.map(id => [id, THINKING_TOGGLE_MAP])), - ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])), + ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])), }, modelSupportsReasoningSummaries: { "glm-5.3": true, @@ -1773,7 +1775,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ "glm-5.2": true, "glm-5.1": true, "glm-5": true, - ...Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])), + ...Object.fromEntries(DEEPSEEK_GATEWAY_THINKING_MODELS.map(id => [id, true])), }, thinkingToggleModels: OPENCODE_GO_THINKING_TOGGLE_MODELS, /* @@ -1790,7 +1792,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // Kimi K2.7 Code accepts text+image+video: do NOT list it here. noVisionModels: [ "glm-5.3", "glm-5.2", "glm-5", "glm-5.1", - "deepseek-v4-flash", "deepseek-v4-pro", + "deepseek-v4.1-flash", "deepseek-v4-flash", "mimo-v2-pro", "mimo-v2.5-pro", "minimax-m2.5", "minimax-m2.7", "qwen3.7-max", @@ -1800,7 +1802,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ noPenaltyModels: ["kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed"], autoToolChoiceOnlyModels: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed"], // Issue #78: DeepSeek V4 thinking mode requires reasoning_content replay on tool-call turns. - preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_THINKING_MODELS], + preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "kimi-k3", "kimi-k2.7-code", "kimi-k2.7-code-highspeed", ...DEEPSEEK_GATEWAY_THINKING_MODELS], /* * Issues #1338 / #1415: this gateway answers a `response_format` of type * `json_schema` with HTTP 400 `This response_format type is unavailable now` @@ -1810,7 +1812,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ * The reported rejection is type-specific, so this narrower list downgrades the * request to `json_object` instead of claiming the whole field is unavailable. */ - noJsonSchemaModels: [...DEEPSEEK_THINKING_MODELS], + noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS], }, { id: "neuralwatt", @@ -1957,10 +1959,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelDiscovery: ORCAROUTER_MODEL_DISCOVERY, // Catalog discovery owns WHICH models exist. These entries only retain verified // request-shaping facts that the upstream catalog does not currently publish. - noVisionModels: ORCAROUTER_TEXT_ONLY_MODELS, modelReasoningEfforts: ORCAROUTER_MODEL_REASONING_EFFORTS, - modelReasoningEffortMap: ORCAROUTER_MODEL_REASONING_EFFORT_MAP, - preserveReasoningContentModels: ORCAROUTER_TEXT_ONLY_MODELS, note: "OpenAI-compatible adaptive router. Models and multimodal capabilities are discovered live from the public chat catalog. Use the OrcaRouter account entry for PKCE login.", }, { @@ -2042,18 +2041,20 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // verified 2026-08-08). jawcodeBundle: "deepseek", // deepseek-chat/deepseek-reasoner were deprecated upstream on 2026-07-24 15:59 UTC; - // official identifiers are now deepseek-v4-flash / deepseek-v4-pro. They stay in + // the current official identifier is deepseek-flash. They stay in // the list only as compatibility aliases so existing saved configs and requests // keep validating and routing (they previously mapped to v4-flash; devlog // _fin/260710_provider_hardening/002_research_cn.md). The current offerings are // the V4 ids — defaultModel and the model-specific wiring above use them. // deepseek-v4-flash-vision-exp: experimental vision preview (2026-08-21) — // expected to merge into deepseek-v4-flash later; see DEEPSEEK_VISION_PREVIEW_MODEL. - models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL], - defaultModel: "deepseek-v4-flash", + models: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_NATIVE_THINKING_MODELS, DEEPSEEK_VISION_PREVIEW_MODEL], + // V4.1-Flash is the current first-party offering; `deepseek-v4-flash` now routes there + // as a compatibility alias, so a new install should ask for the live id by name. + defaultModel: "deepseek-flash", // Official DeepSeek Codex setup (codex-deepseek-setup.sh) advertises 1,048,576 // for both V4 models; the older 1,000,000 figure was a rounded approximation. - modelContextWindows: { "deepseek-v4-flash": 1_048_576, "deepseek-v4-pro": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 }, + modelContextWindows: { "deepseek-flash": 1_048_576, "deepseek-v4-flash": 1_048_576, [DEEPSEEK_VISION_PREVIEW_MODEL]: 1_048_576 }, modelInputModalities: { [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"] }, // DeepSeek documents both V4 models as native Responses API models adapted for Codex // (model table marks Responses API ✓ for flash and pro; the /responses reference lists @@ -2067,7 +2068,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // translating them into Responses would add a hop onto our newest upstream path // for no gain. "deepseek-v4-flash": { wire: "openai-responses", inbound: ["responses"] }, - "deepseek-v4-pro": { wire: "openai-responses", inbound: ["responses"] }, + // Same Responses contract as the V4 ids it succeeds; without this row the new + // default would fall back to the provider-wide Chat wire. + "deepseek-flash": { wire: "openai-responses", inbound: ["responses"] }, }, // The #875-era bounded-JSON force (`modelResponsesUpstreamStreaming`) is retired // for this entry: the official guide documents a `response.completed` / @@ -2082,7 +2085,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // devlog/_fin/260807_deepseek_responses_streaming/000_plan.md. // Current official streams normally carry a real terminal; retain a narrow grace // repair for the historical shape that closes after a complete graph without one. - modelResponsesTerminalRepair: { "deepseek-v4-flash": { graceMs: 5_000 }, "deepseek-v4-pro": { graceMs: 5_000 } }, + modelResponsesTerminalRepair: { "deepseek-flash": { graceMs: 5_000 }, "deepseek-v4-flash": { graceMs: 5_000 } }, // DeepSeek's Responses route emits bare UUID item ids, which leave Codex // clients stuck on an uncommitted turn (#938). Client-facing only — raw // continuation snapshots keep the upstream ids. @@ -2118,14 +2121,14 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ - 대안 분석: Globally preserve reasoning_content for all OpenAI-compatible models; preserve it for legacy deepseek-reasoner too; mark only V4 thinking models in registry metadata. - 선택 근거: DeepSeek V4 thinking mode requires history replay, while older DeepSeek reasoner has different compatibility rules. A model-scoped registry flag fixes built-in and stale saved configs without broad provider regressions. */ - modelReasoningEfforts: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])), - modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])), - modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_THINKING_MODELS.map(id => [id, true])), - preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS, + modelReasoningEfforts: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)])), + modelReasoningEffortMap: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)])), + modelSupportsReasoningSummaries: Object.fromEntries(DEEPSEEK_NATIVE_THINKING_MODELS.map(id => [id, true])), + preserveReasoningContentModels: DEEPSEEK_NATIVE_THINKING_MODELS, // Issue #88: every DeepSeek API model is text-only input (no image support upstream) — the // vision sidecar describes attached images for them, and the catalog advertises image input // on their behalf (same treatment as opencode-go's DeepSeek V4 entries above). - noVisionModels: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_THINKING_MODELS], + noVisionModels: ["deepseek-chat", "deepseek-reasoner", ...DEEPSEEK_NATIVE_THINKING_MODELS], }, // llama-3.3-70b was deprecated by Cerebras on 2026-02-16. Evidence: devlog/_plan/260710_provider_hardening/003_research_aggregators.md. { id: "cerebras", label: "Cerebras", baseUrl: "https://api.cerebras.ai/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://cloud.cerebras.ai/platform/apikeys", defaultModel: "gpt-oss-120b" }, @@ -2309,7 +2312,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // Official Command Code model-profile reasoning facts (shared with the OAuth // `command-code` entry). Without them the API-key preset never advertises a // reasoning picker, and the router's known-ids decode source misses the native - // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-pro` + // slash ids — so a Codex-facing slug like `commandcode/deepseek-deepseek-v4-flash` // is sent upstream verbatim and rejected with `unsupported_model`. modelReasoningEfforts: COMMAND_CODE_MODEL_REASONING_EFFORTS, // The DeepSeek vision preview id is preemptive for when the catalog serves it @@ -2789,13 +2792,11 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ ), thinkingToggleModels: VOLCENGINE_DOUBAO_THINKING_MODELS, preserveReasoningContentModels: [ - "deepseek-v4-pro-260425", "deepseek-v4-flash-260425", "glm-5-2-260617", "glm-4-7-251222", ], noVisionModels: [ - "deepseek-v4-pro-260425", "deepseek-v4-flash-260425", "deepseek-v3-2-251201", "glm-5-2-260617", @@ -2817,12 +2818,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES, noVisionModels: VOLCENGINE_PLAN_TEXT_ONLY_MODELS, modelReasoningEfforts: Object.fromEntries( - DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]), + DEEPSEEK_V4_LEGACY_MODELS.map(id => [id, deepseekThinkingEffortsFor(id)]), ), modelReasoningEffortMap: Object.fromEntries( - DEEPSEEK_THINKING_MODELS.map(id => [id, deepseekReasoningMapFor(id)]), + DEEPSEEK_V4_LEGACY_MODELS.map(id => [id, deepseekReasoningMapFor(id)]), ), - preserveReasoningContentModels: DEEPSEEK_THINKING_MODELS, + preserveReasoningContentModels: DEEPSEEK_V4_LEGACY_MODELS, note: "Coding tools only. Volcengine restricts Coding Plan quota to supported AI coding tools and warns that using this key for general API calls may suspend the subscription or ban the account. Use the plan key issued by the Ark console.", }, { @@ -2836,7 +2837,9 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ supportsServiceTier: false, preserveCustomDestination: true, dashboardUrl: "https://console.volcengine.com/ark/region:ark+cn-beijing/overview", - defaultModel: "deepseek-v4-pro", + // Was `deepseek-v4-pro` until DeepSeek retired it; the plan roster's other DeepSeek + // entry takes over so a fresh install still lands on a working default. + defaultModel: "deepseek-v4-flash", models: VOLCENGINE_AGENT_PLAN_MODELS, liveModels: false, modelInputModalities: VOLCENGINE_PLAN_INPUT_MODALITIES, @@ -2861,7 +2864,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelInputModalities: ALIBABA_TOKEN_PLAN_INPUT_MODALITIES, modelContextWindows: { "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, - "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "deepseek-v4-pro": 1_000_000, + "qwen3.6-flash": 1_000_000, "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, }, modelReasoningEfforts: { ...Object.fromEntries(ALIBABA_TOKEN_PLAN_QWEN_MODELS.map(id => [id, THINKING_BUDGET_EFFORTS])), @@ -2869,14 +2872,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS, "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS, "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS, - "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"), }, modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" }, - modelReasoningEffortMap: { "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro") }, directReasoningEffortModels: ["qwen3.8-max"], thinkingBudgetModels: ALIBABA_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"), - preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"], - noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"], + preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash"], + noVisionModels: ["glm-5.3", "glm-5.2"], }, { id: "alibaba-token-plan-intl", @@ -2896,7 +2897,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelContextWindows: { "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, "qwen3.7-plus": 1_000_000, "qwen3.6-plus": 1_000_000, "qwen3.6-flash": 1_000_000, - "deepseek-v4-pro": 1_000_000, "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072, + "deepseek-v4-flash": 1_000_000, "deepseek-v3.2": 131_072, "kimi-k2.7-code": 262_144, "kimi-k2.6": 262_144, "kimi-k2.5": 262_144, "glm-5.3": 1_000_000, "glm-5.3-flash": 1_000_000, "glm-5.2": 1_000_000, "glm-5.1": 1_000_000, "glm-5": 1_000_000, "MiniMax-M2.5": 204_800, @@ -2907,17 +2908,15 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ "glm-5.3": ZAI_GLM_53_REASONING_EFFORTS, "glm-5.3-flash": ZAI_GLM_53_REASONING_EFFORTS, "glm-5.2": ZAI_GLM_52_REASONING_EFFORTS, - "deepseek-v4-pro": deepseekThinkingEffortsFor("deepseek-v4-pro"), "deepseek-v4-flash": deepseekThinkingEffortsFor("deepseek-v4-flash"), }, modelReasoningEffortMap: { - "deepseek-v4-pro": deepseekReasoningMapFor("deepseek-v4-pro"), "deepseek-v4-flash": deepseekReasoningMapFor("deepseek-v4-flash"), }, directReasoningEffortModels: ["qwen3.8-max"], thinkingBudgetModels: ALIBABA_INTL_TOKEN_PLAN_QWEN_MODELS.filter(id => id !== "qwen3.8-max"), - preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"], - noVisionModels: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"], + preserveReasoningContentModels: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-flash", "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-plus", "qwen3.6-flash"], + noVisionModels: ["deepseek-v4-flash", "deepseek-v3.2", "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "MiniMax-M2.5"], noReasoningModels: ["kimi-k2.7-code", "kimi-k2.6", "kimi-k2.5", "deepseek-v3.2", "glm-5.1", "glm-5", "MiniMax-M2.5"], modelDefaultReasoningEfforts: { "qwen3.8-max": "xhigh" }, }, @@ -2955,7 +2954,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ authKind: "key", dashboardUrl: "https://ollama.com/settings/keys", // Live IDs verified 2026-07-10; qwen3-coder:480b retires 2026-07-15. - models: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"], + models: ["glm-5.3", "glm-5.3-flash", "glm-5.2", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b"], defaultModel: "glm-5.3", // Owner-audited exact outage fallback: these current Ollama Cloud GLM-5.3 rows have // 1,048,576-token context windows. Live discovery and successful /api/show enrichment keep @@ -2967,7 +2966,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ "glm-5.3", "glm-5.2", "glm-5.1", "glm-5", "glm-4.7", "minimax-m2.7", "minimax-m2.5", "minimax-m2.1", "nemotron-3-ultra", "nemotron-3-super", - "deepseek-v4-pro", "deepseek-v4-flash", + "deepseek-v4-flash", "gpt-oss", "qwen3-coder:480b", ], // Ollama's native chat API has no `text.verbosity` equivalent and the ollama-native adapter @@ -3051,12 +3050,12 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // Zen DeepSeek thinking models — never serialize a bare tool-call turn. note: "Keyed OpenCode Zen gateway. Free models on this tier are often short-window rate-limited at roughly 15-20 requests/minute (community-measured; OpenCode does not publish RPM). Zen may return generic 429s without Retry-After / X-RateLimit headers; when Retry-After is omitted, opencodex adds a synthetic backoff hint (upstream Retry-After still wins). Distinct from the keyless opencode-free desktop quota (~200 Big Pickle/free-model requests per 5 hours). Docs: https://opencode.ai/docs/zen/. Free-model prompts may be retained for training — do not send confidential material.", modelReasoningEfforts: Object.fromEntries( - [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]), + [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekThinkingEffortsFor(id)]), ), modelReasoningEffortMap: Object.fromEntries( - [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]), + [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS].map(id => [id, deepseekReasoningMapFor(id)]), ), - preserveReasoningContentModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], + preserveReasoningContentModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], // Same Zen gateway as opencode-free: the DeepSeek vision preview id // (merges into deepseek-v4-flash later). modelContextWindows: { @@ -3065,10 +3064,10 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ modelInputModalities: { [DEEPSEEK_VISION_PREVIEW_MODEL]: ["text", "image"], }, - noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_THINKING_MODELS], + noVisionModels: [...OPENCODE_ZEN_TEXT_ONLY_MODELS, ...DEEPSEEK_GATEWAY_THINKING_MODELS], // Same DeepSeek routes as the Go preset above, behind the same vendor, so they carry // the same json_schema rejection (#1338 / #1415). - noJsonSchemaModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], + noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], }, { id: "vercel-ai-gateway", label: "Vercel AI Gateway", baseUrl: "https://ai-gateway.vercel.sh/v1", adapter: "openai-chat", authKind: "key", dashboardUrl: "https://vercel.com/dashboard" }, { @@ -3112,7 +3111,7 @@ export const PROVIDER_REGISTRY: readonly ProviderRegistryEntry[] = [ // Same reasoning: the free tier is the same Zen roster, so its DeepSeek members get // the keyed tier's json_schema treatment and its reasoning contract rather than a // narrower table that silently falls behind whenever the keyed one is updated. - noJsonSchemaModels: [...DEEPSEEK_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], + noJsonSchemaModels: [...DEEPSEEK_GATEWAY_THINKING_MODELS, ...OPENCODE_FREE_DEEPSEEK_MODELS], }, { id: "xiaomi", label: "Xiaomi MiMo", baseUrl: "https://api.xiaomimimo.com/anthropic", adapter: "anthropic", authKind: "key", dashboardUrl: "https://xiaomimimo.com", defaultModel: "mimo-v2.5-pro" }, // Xiaomi's public OpenAI-compatible endpoint is a distinct transport from both the Anthropic diff --git a/src/router.ts b/src/router.ts index c70a438fbe..55a0326fce 100644 --- a/src/router.ts +++ b/src/router.ts @@ -683,7 +683,7 @@ function routeModelInternal( } } - // 0. Explicit "/" namespace (e.g. "opencode-go/deepseek-v4-pro"). + // 0. Explicit "/" namespace (e.g. "opencode-go/deepseek-v4.1-flash"). // Only triggers when the prefix matches a CONFIGURED provider, so genuine // slash-containing model ids (e.g. "anthropic/claude-...") fall through when // no such provider exists. diff --git a/tests/codex-integration/codex-catalog-restore.test.ts b/tests/codex-integration/codex-catalog-restore.test.ts index c507b232c4..c268a55d19 100644 --- a/tests/codex-integration/codex-catalog-restore.test.ts +++ b/tests/codex-integration/codex-catalog-restore.test.ts @@ -92,7 +92,7 @@ describe("Codex catalog restore", () => { writeFileSync(catalogPath, JSON.stringify({ models: [ { slug: "gpt-5.5" }, - { slug: "opencode-go/deepseek-v4-pro" }, + { slug: "opencode-go/deepseek-v4.1-flash" }, { slug: "user-native" }, ], }, null, 2) + "\n"); diff --git a/tests/codex-integration/codex-catalog.test.ts b/tests/codex-integration/codex-catalog.test.ts index 2a8b52543e..7c92d0af5d 100644 --- a/tests/codex-integration/codex-catalog.test.ts +++ b/tests/codex-integration/codex-catalog.test.ts @@ -2224,16 +2224,16 @@ describe("configured CatalogModel displayName -> catalog display_name", () => { test("Command Code routed models relabel the picker row with distinguishable slugs", () => { const entries = buildCatalogEntries(nativeTemplate(), [], [ { provider: "command-code", id: "deepseek/deepseek-v4-flash", owned_by: "command-code" }, - { provider: "commandcode", id: "deepseek/deepseek-v4-pro", owned_by: "commandcode" }, + { provider: "commandcode", id: "deepseek/deepseek-v4.1-flash", owned_by: "commandcode" }, ]); const auth = entries.find(e => e.slug === "command-code/deepseek-deepseek-v4-flash"); - const api = entries.find(e => e.slug === "commandcode/deepseek-deepseek-v4-pro"); + const api = entries.find(e => e.slug === "commandcode/deepseek-deepseek-v4.1-flash"); // Display-only relabel + redundant vendor-prefix drop: routing slugs stay untouched. expect(auth?.display_name).toBe("commandcode-auth/deepseek-v4-flash"); expect(auth?.slug).toBe("command-code/deepseek-deepseek-v4-flash"); - expect(api?.display_name).toBe("commandcode-api/deepseek-v4-pro"); - expect(api?.slug).toBe("commandcode/deepseek-deepseek-v4-pro"); + expect(api?.display_name).toBe("commandcode-api/deepseek-deepseek-v4.1-flash"); + expect(api?.slug).toBe("commandcode/deepseek-deepseek-v4.1-flash"); }); test("Google Antigravity routed models relabel the picker row with compact agy prefix", () => { @@ -5987,9 +5987,9 @@ describe("Codex catalog routed normalization", () => { test("built-in DeepSeek and GLM effort models opt into Codex reasoning propagation (#1100)", async () => { const expected = [ { slug: "deepseek/deepseek-v4-flash", efforts: ["low", "high", "max", "ultra"] }, - { slug: "deepseek/deepseek-v4-pro", efforts: ["low", "high", "max", "ultra"] }, + { slug: "deepseek/deepseek-flash", efforts: ["low", "high", "max", "ultra"] }, { slug: "opencode-go/deepseek-v4-flash", efforts: ["low", "high", "max"] }, - { slug: "opencode-go/deepseek-v4-pro", efforts: ["low", "high", "max"] }, + { slug: "opencode-go/deepseek-v4.1-flash", efforts: ["low", "high", "max"] }, { slug: "opencode-go/glm-5.2", efforts: ["low", "medium", "high", "xhigh", "max"] }, { slug: "opencode-go/glm-5.1", efforts: ["low", "medium", "high", "xhigh", "max"] }, { slug: "opencode-go/glm-5", efforts: ["low", "medium", "high", "xhigh", "max"] }, @@ -6008,7 +6008,7 @@ describe("Codex catalog routed normalization", () => { authMode: "key", apiKey: "sk-test", liveModels: false, - models: ["deepseek-v4-flash", "deepseek-v4-pro"], + models: ["deepseek-v4-flash", "deepseek-flash"], }, "opencode-go": { adapter: "openai-chat", @@ -6016,7 +6016,7 @@ describe("Codex catalog routed normalization", () => { authMode: "key", apiKey: "sk-test", liveModels: false, - models: ["deepseek-v4-flash", "deepseek-v4-pro", "glm-5.2", "glm-5.1", "glm-5"], + models: ["deepseek-v4-flash", "deepseek-v4.1-flash", "glm-5.2", "glm-5.1", "glm-5"], }, zai: { adapter: "openai-chat", @@ -6174,7 +6174,7 @@ describe("Codex catalog routed normalization", () => { expect(provider.modelSupportsReasoningSummaries).toEqual({ "deepseek-v4-flash": false, - "deepseek-v4-pro": true, + "deepseek-flash": true, }); }); diff --git a/tests/codex-integration/reasoning-effort.test.ts b/tests/codex-integration/reasoning-effort.test.ts index a5d3cd1114..3f0b85da34 100644 --- a/tests/codex-integration/reasoning-effort.test.ts +++ b/tests/codex-integration/reasoning-effort.test.ts @@ -194,11 +194,11 @@ describe("provider-specific reasoning effort mapping", () => { adapter: "openai-chat", baseUrl: "https://api.deepseek.com", apiKey: "key", - models: ["deepseek-v4-pro"], + models: ["deepseek-flash"], }, }, }; - const route = routeModel(config, "deepseek/deepseek-v4-pro"); + const route = routeModel(config, "deepseek/deepseek-flash"); const req = createOpenAIChatAdapter(route.provider).buildRequest({ modelId: route.modelId, @@ -271,7 +271,8 @@ describe("provider-specific reasoning effort mapping", () => { }); const body = JSON.parse(req.body as string) as { messages: Record[] }; - expect(route.provider.preserveReasoningContentModels).toEqual(["deepseek-v4-pro", "deepseek-v4-flash"]); + expect(route.provider.preserveReasoningContentModels) + .toEqual(["deepseek-flash", "deepseek-v4-flash"]); expect(body.messages[1].reasoning_content).toBeUndefined(); }); diff --git a/tests/codex-integration/slug-codec.test.ts b/tests/codex-integration/slug-codec.test.ts index 611754ce18..5248b75020 100644 --- a/tests/codex-integration/slug-codec.test.ts +++ b/tests/codex-integration/slug-codec.test.ts @@ -241,8 +241,8 @@ describe("routeModel decode (proxy layer)", () => { test("commandcode API-key preset decodes its native slash ids from the registry effort table", () => { // Regression: the `commandcode` (API-key) registry entry must share the official // reasoning-facts table with the OAuth `command-code` entry. Without it the router's - // known-ids source misses `deepseek/deepseek-v4-pro` / `zai-org/GLM-5.3`, so the - // Codex-facing slugs (`commandcode/deepseek-deepseek-v4-pro`) pass through unchanged + // known-ids source misses `deepseek/deepseek-v4-flash` / `zai-org/GLM-5.3`, so the + // Codex-facing slugs (`commandcode/deepseek-deepseek-v4-flash`) pass through unchanged // and upstream rejects them with `unsupported_model`. const prov = { adapter: "openai-chat", @@ -252,9 +252,9 @@ describe("routeModel decode (proxy layer)", () => { liveModels: true, }; const ids = knownModelIdsForProvider("commandcode", prov); - expect(ids).toContain("deepseek/deepseek-v4-pro"); + expect(ids).toContain("deepseek/deepseek-v4-flash"); expect(ids).toContain("zai-org/GLM-5.3"); - expect(decodeRoutedModelId("deepseek-deepseek-v4-pro", ids)).toBe("deepseek/deepseek-v4-pro"); + expect(decodeRoutedModelId("deepseek-deepseek-v4-flash", ids)).toBe("deepseek/deepseek-v4-flash"); expect(decodeRoutedModelId("zai-org-GLM-5.3", ids)).toBe("zai-org/GLM-5.3"); }); }); diff --git a/tests/e2e-style/phase100-native-parity.test.ts b/tests/e2e-style/phase100-native-parity.test.ts index fa60965738..04088ace30 100644 --- a/tests/e2e-style/phase100-native-parity.test.ts +++ b/tests/e2e-style/phase100-native-parity.test.ts @@ -59,7 +59,7 @@ describe("Phase 100 Codex-native parity smoke", () => { adapter: "openai-chat", baseUrl: "https://routed.example/v1", apiKey: "routed-key", - noVisionModels: ["deepseek-v4-pro"], + noVisionModels: ["deepseek-v4-flash"], }; const forwardProvider: OcxProviderConfig = { adapter: "openai-responses", @@ -76,9 +76,9 @@ describe("Phase 100 Codex-native parity smoke", () => { }; const catalog = buildCatalogEntries(nativeTemplate(), ["gpt-5.5"], [ - { provider: "opencode-go", id: "deepseek-v4-pro" }, + { provider: "opencode-go", id: "deepseek-v4-flash" }, ], undefined, false); - const routed = catalog.find(entry => entry.slug === "opencode-go/deepseek-v4-pro"); + const routed = catalog.find(entry => entry.slug === "opencode-go/deepseek-v4-flash"); expect(routed).toMatchObject({ web_search_tool_type: "text_and_image", supports_search_tool: true, @@ -90,7 +90,7 @@ describe("Phase 100 Codex-native parity smoke", () => { expect(routed).not.toHaveProperty("supports_websockets"); const parsed = parseRequest({ - model: "opencode-go/deepseek-v4-pro", + model: "opencode-go/deepseek-v4-flash", stream: true, input: "Search current docs, then answer.", tools: [ @@ -106,7 +106,7 @@ describe("Phase 100 Codex-native parity smoke", () => { parsed, false, routedProvider, - "deepseek-v4-pro", + "deepseek-v4-flash", { providerName: "openai", provider: forwardProvider, @@ -125,7 +125,7 @@ describe("Phase 100 Codex-native parity smoke", () => { const frames = await collectSse(bridgeToResponsesSSE(replay([ { type: "error", message: "Your input exceeds the context window" }, - ]), "deepseek-v4-pro")); + ]), "deepseek-v4-flash")); const failed = frames.find(frame => frame.event === "response.failed")?.data.response as Record; expect(failed.error).toMatchObject({ code: "context_length_exceeded", diff --git a/tests/fixtures/baseten-models.json b/tests/fixtures/baseten-models.json index 7852c4374f..bd1795c81a 100644 --- a/tests/fixtures/baseten-models.json +++ b/tests/fixtures/baseten-models.json @@ -2,7 +2,7 @@ "object": "list", "data": [ { - "id": "deepseek-ai/DeepSeek-V4-Pro" + "id": "thinkingmachines/inkling" }, { "id": "moonshotai/Kimi-K2.6" diff --git a/tests/gui/alibaba-intl-token-plan.test.ts b/tests/gui/alibaba-intl-token-plan.test.ts index e5fb297e0d..8ff822e5e0 100644 --- a/tests/gui/alibaba-intl-token-plan.test.ts +++ b/tests/gui/alibaba-intl-token-plan.test.ts @@ -28,14 +28,14 @@ describe("alibaba-token-plan-intl registry entry", () => { test("model list includes multi-vendor lineup", () => { const entry = PROVIDER_REGISTRY.find(e => e.id === "alibaba-token-plan-intl"); expect(entry!.models).toContain("qwen3.7-max"); - expect(entry!.models).toContain("deepseek-v4-pro"); + expect(entry!.models).not.toContain("deepseek-v4-pro"); expect(entry!.models).toContain("kimi-k2.7-code"); expect(entry!.models).toContain("glm-5.2"); expect(entry!.models).toContain("glm-5.3"); expect(entry!.models).toContain("glm-5.3-flash"); expect(entry!.models).toContain("MiniMax-M2.5"); expect(entry!.models).toContain("qwen3.8-max"); - expect(entry!.models!.length).toBe(17); + expect(entry!.models!.length).toBe(16); }); test("MiniMax case-insensitive normalization is set", () => { diff --git a/tests/gui/volcengine-providers.test.ts b/tests/gui/volcengine-providers.test.ts index c6bcf2c743..868a5bbc89 100644 --- a/tests/gui/volcengine-providers.test.ts +++ b/tests/gui/volcengine-providers.test.ts @@ -34,7 +34,6 @@ describe("Volcengine Ark providers", () => { "doubao-seed-2-1-pro-260628", "doubao-seed-2-1-turbo-260628", "doubao-seed-evolving", - "deepseek-v4-pro-260425", "deepseek-v4-flash-260425", "deepseek-v3-2-251201", "glm-5-2-260617", @@ -57,7 +56,6 @@ describe("Volcengine Ark providers", () => { models: [ "ark-code-latest", "doubao-seed-2.0-code", - "deepseek-v4-pro", "deepseek-v4-flash", "glm-5.3", "glm-5.3-flash", @@ -73,14 +71,12 @@ describe("Volcengine Ark providers", () => { // #1057: per-model ladders. Since the V4 Pro GA (DeepSeek-V4-Pro-0813) the // vendor table is identical for both models; `xhigh` stays an unadvertised alias. modelReasoningEfforts: { - "deepseek-v4-pro": ["low", "high", "max"], "deepseek-v4-flash": ["low", "high", "max"], }, modelReasoningEffortMap: { - "deepseek-v4-pro": { low: "low", medium: "high", high: "high", xhigh: "high", max: "max" }, "deepseek-v4-flash": { low: "low", medium: "high", high: "high", xhigh: "high", max: "max" }, }, - preserveReasoningContentModels: ["deepseek-v4-pro", "deepseek-v4-flash"], + preserveReasoningContentModels: ["deepseek-v4-flash"], }); expect(PROVIDER_REGISTRY.find(provider => provider.id === "volcengine-agent-plan")).toMatchObject({ label: "Volcengine Ark Agent Plan", @@ -89,9 +85,8 @@ describe("Volcengine Ark providers", () => { adapter: "openai-responses", authKind: "key", preserveCustomDestination: true, - defaultModel: "deepseek-v4-pro", + defaultModel: "deepseek-v4-flash", models: [ - "deepseek-v4-pro", "deepseek-v4-flash", "glm-5.3", "glm-5.3-flash", @@ -118,13 +113,13 @@ describe("Volcengine Ark providers", () => { baseUrl: "https://ark.cn-beijing.volces.com/api/coding/v3", defaultModel: "ark-code-latest", liveModels: false, - preserveReasoningContentModels: ["deepseek-v4-pro", "deepseek-v4-flash"], + preserveReasoningContentModels: ["deepseek-v4-flash"], }); expect(KEY_LOGIN_PROVIDERS["volcengine-agent-plan"]).toMatchObject({ baseUrl: "https://ark.cn-beijing.volces.com/api/plan/v3", responsesPath: "/responses", adapter: "openai-responses", - defaultModel: "deepseek-v4-pro", + defaultModel: "deepseek-v4-flash", liveModels: false, }); for (const id of ["volcengine", "volcengine-coding-plan", "volcengine-agent-plan"]) { @@ -147,7 +142,7 @@ describe("Volcengine Ark providers", () => { }, }, }; - const route = routeModel(config, "volcengine-agent-plan/deepseek-v4-pro"); + const route = routeModel(config, "volcengine-agent-plan/deepseek-v4-flash"); expect(route.provider.responsesPath).toBe("/responses"); const request = createResponsesPassthroughAdapter(route.provider).buildRequest({ @@ -215,7 +210,7 @@ describe("Volcengine Ark providers", () => { expect(body).not.toHaveProperty("reasoning_effort"); }); - test.each(["deepseek-v4-pro", "deepseek-v4-flash"])( + test.each(["deepseek-v4-flash"])( "preserves %s tool-call reasoning and maps Codex efforts on Coding Plan", modelId => { const config: OcxConfig = { @@ -316,11 +311,11 @@ describe("Volcengine Ark providers", () => { }, }); const request = createResponsesPassthroughAdapter(postBody.provider).buildRequest({ - modelId: "deepseek-v4-pro", + modelId: "deepseek-v4-flash", context: { messages: [] }, stream: true, options: {}, - _rawBody: { model: "deepseek-v4-pro", input: "ping", stream: true }, + _rawBody: { model: "deepseek-v4-flash", input: "ping", stream: true }, }, { headers: new Headers() }); expect(request.url).toBe("https://ark.cn-beijing.volces.com/api/plan/v3/responses"); }); diff --git a/tests/providers/baseten-provider.test.ts b/tests/providers/baseten-provider.test.ts index 8f3da5ed25..721b569894 100644 --- a/tests/providers/baseten-provider.test.ts +++ b/tests/providers/baseten-provider.test.ts @@ -79,7 +79,6 @@ describe("Baseten Model APIs provider", () => { }, }); expect(basetenEntry().modelReasoningEfforts).toEqual({ - "deepseek-ai/DeepSeek-V4-Pro": ["low", "medium", "high", "xhigh", "max"], "thinkingmachines/inkling": ["low", "medium", "high", "xhigh", "max"], "openai/gpt-oss-120b": ["low", "medium", "high", "xhigh", "max"], "moonshotai/Kimi-K3": ["low", "high", "max"], @@ -89,7 +88,6 @@ describe("Baseten Model APIs provider", () => { "zai-org/GLM-5.2-Fast": ["high", "max"], }); expect(basetenEntry().modelReasoningEffortMap).toEqual({ - "deepseek-ai/DeepSeek-V4-Pro": { none: "none", minimal: "minimal" }, "thinkingmachines/inkling": { none: "none", minimal: "minimal" }, "openai/gpt-oss-120b": { none: "none", minimal: "minimal" }, "moonshotai/Kimi-K3": { none: "none" }, @@ -99,7 +97,6 @@ describe("Baseten Model APIs provider", () => { "zai-org/GLM-5.2-Fast": { none: "none" }, }); expect(basetenEntry().modelDefaultReasoningEfforts).toEqual({ - "deepseek-ai/DeepSeek-V4-Pro": "medium", "thinkingmachines/inkling": "high", "openai/gpt-oss-120b": "medium", "moonshotai/Kimi-K3": "max", @@ -137,7 +134,7 @@ describe("Baseten Model APIs provider", () => { parallelToolCalls: true, reasoningEfforts: [], }); - expect(seed.modelReasoningEfforts?.["deepseek-ai/DeepSeek-V4-Pro"]) + expect(seed.modelReasoningEfforts?.["thinkingmachines/inkling"]) .toEqual(["low", "medium", "high", "xhigh", "max"]); expect(seed.modelInputModalities?.["moonshotai/Kimi-K2.6"]) .toEqual(["text", "image"]); @@ -178,10 +175,10 @@ describe("Baseten Model APIs provider", () => { test("routes chat completions to the shared inference host with documented tool parallelism", () => { const route = routeModel( basetenConfig(), - "baseten/deepseek-ai/DeepSeek-V4-Pro", + "baseten/thinkingmachines/inkling", ); expect(route.provider.parallelToolCalls).toBe(true); - expect(route.modelId).toBe("deepseek-ai/DeepSeek-V4-Pro"); + expect(route.modelId).toBe("thinkingmachines/inkling"); const request = createOpenAIChatAdapter(route.provider).buildRequest({ modelId: route.modelId, @@ -196,12 +193,12 @@ describe("Baseten Model APIs provider", () => { expect(request.url).toBe("https://inference.baseten.co/v1/chat/completions"); expect(request.headers.Authorization).toBe("Bearer bt-test-key"); - expect(body.model).toBe("deepseek-ai/DeepSeek-V4-Pro"); + expect(body.model).toBe("thinkingmachines/inkling"); expect(body.parallel_tool_calls).toBe(true); }); test("forwards only the documented per-model reasoning effort ladders", () => { - const deepseekRoute = routeModel(basetenConfig(), "baseten/deepseek-ai/DeepSeek-V4-Pro"); + const deepseekRoute = routeModel(basetenConfig(), "baseten/thinkingmachines/inkling"); const deepseekBody = JSON.parse(String(createOpenAIChatAdapter(deepseekRoute.provider).buildRequest({ modelId: deepseekRoute.modelId, context: { messages: [{ role: "user", content: "ping", timestamp: 0 }] }, @@ -245,25 +242,25 @@ describe("Baseten Model APIs provider", () => { const config = withStubbedProviderFetch(basetenConfig()); const models = (await gatherRoutedModels(config)).filter(row => row.provider === "baseten"); expect(models.map(row => row.id)).toEqual([ - "deepseek-ai/DeepSeek-V4-Pro", "moonshotai/Kimi-K2.6", + "thinkingmachines/inkling", ]); expect(models[0]).toMatchObject({ - reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], - defaultReasoningEffort: "medium", + inputModalities: ["text", "image"], + reasoningEfforts: [], parallelToolCalls: true, }); expect(models[1]).toMatchObject({ - inputModalities: ["text", "image"], - reasoningEfforts: [], + reasoningEfforts: ["low", "medium", "high", "xhigh", "max"], + defaultReasoningEffort: "high", parallelToolCalls: true, }); - expect(routedSlug("baseten", models[0]!.id)).toBe("baseten/deepseek-ai-DeepSeek-V4-Pro"); + expect(routedSlug("baseten", models[1]!.id)).toBe("baseten/thinkingmachines-inkling"); - expect(routeModel(config, "baseten/deepseek-ai/DeepSeek-V4-Pro").modelId) - .toBe("deepseek-ai/DeepSeek-V4-Pro"); - expect(routeModel(config, "baseten/deepseek-ai-DeepSeek-V4-Pro").modelId) - .toBe("deepseek-ai/DeepSeek-V4-Pro"); + expect(routeModel(config, "baseten/thinkingmachines/inkling").modelId) + .toBe("thinkingmachines/inkling"); + expect(routeModel(config, "baseten/thinkingmachines-inkling").modelId) + .toBe("thinkingmachines/inkling"); }); test("does not retarget an older same-named custom provider", () => { diff --git a/tests/providers/cline-pass-deepseek-v4-tool-replay.test.ts b/tests/providers/cline-pass-deepseek-v4-tool-replay.test.ts index 0cba245597..6aa7af0880 100644 --- a/tests/providers/cline-pass-deepseek-v4-tool-replay.test.ts +++ b/tests/providers/cline-pass-deepseek-v4-tool-replay.test.ts @@ -9,7 +9,6 @@ import { createTestTranslatorBudget } from "../helpers/translator-budget"; const TARGET_MODELS = [ "cline-pass/deepseek-v4-flash", - "cline-pass/deepseek-v4-pro", ] as const; const provider = { diff --git a/tests/providers/cline-pass-provider.test.ts b/tests/providers/cline-pass-provider.test.ts index 3db66eccd6..81a56923e9 100644 --- a/tests/providers/cline-pass-provider.test.ts +++ b/tests/providers/cline-pass-provider.test.ts @@ -14,7 +14,6 @@ const OFFICIAL_CLINE_PASS_MODELS = [ "cline-pass/kimi-k3", "cline-pass/kimi-k2.7-code", "cline-pass/kimi-k2.6", - "cline-pass/deepseek-v4-pro", "cline-pass/deepseek-v4-flash", "cline-pass/mimo-v2.5", "cline-pass/mimo-v2.5-pro", @@ -73,7 +72,6 @@ describe("ClinePass provider", () => { expect(entry?.noVisionModels).toEqual([ "cline-pass/glm-5.3", "cline-pass/glm-5.2", - "cline-pass/deepseek-v4-pro", "cline-pass/deepseek-v4-flash", "cline-pass/mimo-v2.5-pro", "cline-pass/qwen3.7-max", diff --git a/tests/providers/cline-pass-reasoning-efforts.test.ts b/tests/providers/cline-pass-reasoning-efforts.test.ts index a0acff6078..cf45551060 100644 --- a/tests/providers/cline-pass-reasoning-efforts.test.ts +++ b/tests/providers/cline-pass-reasoning-efforts.test.ts @@ -9,7 +9,6 @@ const CLINE_PASS_MODELS = [ "cline-pass/kimi-k3", "cline-pass/kimi-k2.7-code", "cline-pass/kimi-k2.6", - "cline-pass/deepseek-v4-pro", "cline-pass/deepseek-v4-flash", "cline-pass/mimo-v2.5", "cline-pass/mimo-v2.5-pro", diff --git a/tests/providers/command-code-provider.test.ts b/tests/providers/command-code-provider.test.ts index a3b81e408d..d588c550ee 100644 --- a/tests/providers/command-code-provider.test.ts +++ b/tests/providers/command-code-provider.test.ts @@ -79,7 +79,6 @@ describe("Command Code provider", () => { // rejected with `unsupported_model`. expect(apiKey?.modelReasoningEfforts).toEqual(oauth?.modelReasoningEfforts); expect(apiKey?.modelReasoningEfforts).toMatchObject({ - "deepseek/deepseek-v4-pro": ["high", "max"], "zai-org/GLM-5": ["high", "max"], "zai-org/GLM-5.1": ["high", "max"], "zai-org/GLM-5.2-Fast": ["high", "max"], @@ -112,6 +111,39 @@ describe("Command Code provider", () => { expect(commandCodeReasoningEfforts("z-ai/glm-5.3-flash-vision")).toBeUndefined(); }); + /* + * deepseek/deepseek-v4.1-flash and Qwen/Qwen3.8-Flash are live routes that had + * no row in the official table, so `supportedCommandCodeEffort` dropped the + * field — a client's `max` reached /alpha/generate as no reasoning parameter + * at all. The two presets are constructed separately and must each carry the + * rows; the request assertions pin that the effort survives construction. + */ + test("the live v4.1-flash and Qwen3.8-Flash routes forward their own ladder", async () => { + const oauth = PROVIDER_REGISTRY.find(row => row.id === "command-code"); + const apiKey = PROVIDER_REGISTRY.find(row => row.id === "commandcode"); + for (const [label, entry] of [["oauth", oauth], ["api-key", apiKey]] as const) { + expect(entry?.modelReasoningEfforts?.["deepseek/deepseek-v4.1-flash"], `${label} preset ladder`) + .toEqual(["high", "max"]); + expect(entry?.modelReasoningEfforts?.["Qwen/Qwen3.8-Flash"], `${label} preset ladder`) + .toEqual(["low", "medium", "high", "max"]); + } + expect(commandCodeReasoningEfforts("deepseek/deepseek-v4.1-flash")).toEqual(["high", "max"]); + expect(commandCodeReasoningEfforts("Qwen/Qwen3.8-Flash")).toEqual(["low", "medium", "high", "max"]); + // The live-discovered id may arrive in any case; the lookup folds it. + expect(commandCodeReasoningEfforts("qwen/qwen3.8-flash")).toEqual(["low", "medium", "high", "max"]); + + const deepseekMax = await builtRequest({ + ...parsed("deepseek/deepseek-v4.1-flash"), + options: { reasoning: "max", maxOutputTokens: 100 }, + }); + expect(JSON.parse(deepseekMax.body).params.reasoning_effort).toBe("max"); + const qwenMax = await builtRequest({ + ...parsed("Qwen/Qwen3.8-Flash"), + options: { reasoning: "max", maxOutputTokens: 100 }, + }); + expect(JSON.parse(qwenMax.body).params.reasoning_effort).toBe("max"); + }); + test("OAuth and API-key presets share only verified image capabilities", () => { const oauth = PROVIDER_REGISTRY.find(row => row.id === "command-code"); const apiKey = PROVIDER_REGISTRY.find(row => row.id === "commandcode"); @@ -128,7 +160,6 @@ describe("Command Code provider", () => { ]; const verifiedTextOnlyModels = [ "deepseek/deepseek-v4-flash", - "deepseek/deepseek-v4-pro", "zai-org/GLM-5.2", "zai-org/GLM-5.3", "xai/grok-4.6", diff --git a/tests/providers/digitalocean-scaleway-provider.test.ts b/tests/providers/digitalocean-scaleway-provider.test.ts index 34464c65aa..edee14a87c 100644 --- a/tests/providers/digitalocean-scaleway-provider.test.ts +++ b/tests/providers/digitalocean-scaleway-provider.test.ts @@ -111,7 +111,7 @@ describe("DigitalOcean and Scaleway providers", () => { // the id to both the DigitalOcean and Scaleway lists and moved neither length // assertion; Scaleway's happened to still match, so only this one went red - and it // stayed red on dev, which is how a broken shard reached the branch that noticed it. - expect(digitaloceanModels).toHaveLength(28); + expect(digitaloceanModels).toHaveLength(27); expect(digitaloceanModels).toContain("glm-5.3-flash"); expect(digitaloceanModels).toContain("openai-gpt-5.6-sol"); expect(digitaloceanModels).toContain("meta-llama/Meta-Llama-3.1-8B-Instruct"); @@ -264,12 +264,11 @@ describe("DigitalOcean and Scaleway providers", () => { const scalewayModels = models.filter(row => row.provider === "scaleway"); expect(digitaloceanModels.map(row => row.id)).toEqual([ - "deepseek-v4-pro", "meta-llama/Meta-Llama-3.1-8B-Instruct", "openai-gpt-5.6-sol", ]); expect(digitaloceanModels[1]).toMatchObject({ - owned_by: "digitalocean", + owned_by: "openai", reasoningEfforts: [], }); expect(scalewayModels.map(row => row.id)).toEqual([ diff --git a/tests/providers/opencode-go-deepseek.test.ts b/tests/providers/opencode-go-deepseek.test.ts index 890ec0a92d..eead3f2c32 100644 --- a/tests/providers/opencode-go-deepseek.test.ts +++ b/tests/providers/opencode-go-deepseek.test.ts @@ -54,7 +54,7 @@ function buildToolCallBody(modelId: string, reasoning: string): { describe("opencode-go DeepSeek V4 thinking mode", () => { test("normalizes Desktop-style root composition schemas for Console Go", () => { - const route = routeModel(configFor("deepseek-v4-pro"), "opencode-go/deepseek-v4-pro"); + const route = routeModel(configFor("deepseek-v4.1-flash"), "opencode-go/deepseek-v4.1-flash"); const req = createOpenAIChatAdapter(route.provider).buildRequest({ modelId: route.modelId, context: { @@ -99,7 +99,7 @@ describe("opencode-go DeepSeek V4 thinking mode", () => { }); }); - test.each(["deepseek-v4-flash", "deepseek-v4-pro"])( + test.each(["deepseek-v4-flash", "deepseek-v4.1-flash"])( "%s replays tool-call reasoning and maps Codex efforts", modelId => { const xhighBody = buildToolCallBody(modelId, "xhigh"); @@ -157,12 +157,12 @@ describe("opencode-go DeepSeek json_schema downgrade", () => { test("the preset reaches the routed provider", () => { expect(buildWith("deepseek-v4-flash").provider.noJsonSchemaModels) - .toEqual(["deepseek-v4-pro", "deepseek-v4-flash"]); + .toEqual(["deepseek-v4.1-flash", "deepseek-v4-flash"]); }); test("a listed DeepSeek route is downgraded to json_object", () => { expect(buildWith("deepseek-v4-flash").body.response_format).toEqual({ type: "json_object" }); - expect(buildWith("deepseek-v4-pro").body.response_format).toEqual({ type: "json_object" }); + expect(buildWith("deepseek-v4.1-flash").body.response_format).toEqual({ type: "json_object" }); }); test("an unlisted sibling on the same gateway keeps its schema", () => { diff --git a/tests/providers/opencode-zen-deepseek-reasoning.test.ts b/tests/providers/opencode-zen-deepseek-reasoning.test.ts index 5074a510f1..9bfc0ac369 100644 --- a/tests/providers/opencode-zen-deepseek-reasoning.test.ts +++ b/tests/providers/opencode-zen-deepseek-reasoning.test.ts @@ -53,7 +53,7 @@ function buildToolCallBody(modelId: string, reasoning?: string): { } describe("opencode-zen DeepSeek thinking mode", () => { - test.each(["deepseek-v4-flash-free", "deepseek-v4-flash", "deepseek-v4-pro"])( + test.each(["deepseek-v4-flash-free", "deepseek-v4-flash", "deepseek-v4.1-flash"])( "%s replays tool-call reasoning_content and maps Codex efforts (issue #950/#994)", modelId => { const body = buildToolCallBody(modelId, "xhigh"); @@ -81,7 +81,7 @@ describe("opencode-zen DeepSeek thinking mode", () => { expect(body.messages[1]).toHaveProperty("tool_calls"); }); - test.each(["deepseek-v4-flash-free", "deepseek-v4-flash", "deepseek-v4-pro"])( + test.each(["deepseek-v4-flash-free", "deepseek-v4-flash", "deepseek-v4.1-flash"])( "%s is listed in opencode-zen noVisionModels for the vision sidecar", modelId => { const route = routeModel(configFor(modelId), `opencode-zen/${modelId}`); diff --git a/tests/providers/orcarouter-provider.test.ts b/tests/providers/orcarouter-provider.test.ts index 2b8cb02a54..a45ffde24a 100644 --- a/tests/providers/orcarouter-provider.test.ts +++ b/tests/providers/orcarouter-provider.test.ts @@ -136,7 +136,7 @@ describe("OrcaRouter dual authentication", () => { expect(entry.models).toContain("orcarouter/auto"); expect(entry.modelReasoningEfforts?.["openai/gpt-5.5"]) .toEqual(["low", "medium", "high", "xhigh"]); - expect(entry.modelReasoningEfforts?.["deepseek/deepseek-v4-pro"]).toBeArray(); + expect(entry.modelReasoningEfforts?.["openai/gpt-5.5"]).toBeArray(); } expect(KEY_LOGIN_PROVIDERS.orcarouter).toBeDefined(); expect(OAUTH_PROVIDERS["orcarouter-oauth"]).toBeDefined(); diff --git a/tests/providers/provider-registry-parity.test.ts b/tests/providers/provider-registry-parity.test.ts index b476ce1036..586c8a7735 100644 --- a/tests/providers/provider-registry-parity.test.ts +++ b/tests/providers/provider-registry-parity.test.ts @@ -73,7 +73,7 @@ describe("provider registry parity", () => { expect(KEY_LOGIN_PROVIDERS["opencode-go"].noVisionModels).toEqual([ "glm-5.3", "glm-5.2", "glm-5", "glm-5.1", - "deepseek-v4-flash", "deepseek-v4-pro", + "deepseek-v4.1-flash", "deepseek-v4-flash", "mimo-v2-pro", "mimo-v2.5-pro", "minimax-m2.5", "minimax-m2.7", "qwen3.7-max", @@ -84,13 +84,52 @@ describe("provider registry parity", () => { // an operator no longer has to disable structured output by hand. Registry-only means // it is asserted here against the raw entry, not the derived key-login map. const zenDeepseekJsonSchema: Record = { - "opencode-go": ["deepseek-v4-pro", "deepseek-v4-flash"], - "opencode-zen": ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v4-flash-free"], - "opencode-free": ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-v4-flash-free"], + "opencode-go": ["deepseek-v4.1-flash", "deepseek-v4-flash"], + "opencode-zen": ["deepseek-v4.1-flash", "deepseek-v4-flash", "deepseek-v4-flash-free"], + "opencode-free": ["deepseek-v4.1-flash", "deepseek-v4-flash", "deepseek-v4-flash-free"], }; for (const [id, expected] of Object.entries(zenDeepseekJsonSchema)) { expect(PROVIDER_REGISTRY.find(entry => entry.id === id)?.noJsonSchemaModels).toEqual(expected); } + /* + * DeepSeek's V4.1 transition (2026-09-10) split the spelling by who serves the route: + * the first-party API answers to `deepseek-flash`, the Zen gateway exposes + * `deepseek-v4.1-flash`. A single shared list cannot express that, and the earlier + * draft that tried it would have leaked the gateway spelling into the native preset. + * Pin both directions, including the negatives — a future edit that collapses the two + * constants back together fails here rather than in a user's request. + */ + const nativeDeepseek = PROVIDER_REGISTRY.find(entry => entry.id === "deepseek"); + expect(nativeDeepseek?.defaultModel).toBe("deepseek-flash"); + expect(nativeDeepseek?.models).toContain("deepseek-flash"); + for (const map of [ + nativeDeepseek?.modelReasoningEfforts, + nativeDeepseek?.modelReasoningEffortMap, + nativeDeepseek?.modelSupportsReasoningSummaries, + nativeDeepseek?.modelContextWindows, + ]) { + expect(Object.keys(map ?? {})).toContain("deepseek-flash"); + } + expect(nativeDeepseek?.preserveReasoningContentModels).toContain("deepseek-flash"); + expect(nativeDeepseek?.noVisionModels).toContain("deepseek-flash"); + // The new id keeps the Flash ladder, not the Pro one, through isDeepseekFlashModel. + expect(nativeDeepseek?.modelReasoningEfforts?.["deepseek-flash"]) + .toEqual(nativeDeepseek?.modelReasoningEfforts?.["deepseek-v4-flash"]); + + const zenGo = PROVIDER_REGISTRY.find(entry => entry.id === "opencode-go"); + expect(zenGo?.preserveReasoningContentModels).toContain("deepseek-v4.1-flash"); + expect(zenGo?.noVisionModels).toContain("deepseek-v4.1-flash"); + expect(Object.keys(zenGo?.modelReasoningEfforts ?? {})).toContain("deepseek-v4.1-flash"); + + // Negatives: neither spelling crosses into the other side. + expect(JSON.stringify(nativeDeepseek)).not.toContain("deepseek-v4.1-flash"); + for (const id of ["opencode-go", "opencode-zen", "opencode-free"]) { + expect(JSON.stringify(PROVIDER_REGISTRY.find(entry => entry.id === id))) + .not.toContain("\"deepseek-flash\""); + } + // Vendor-hosted rosters publish on their own schedule and keep the legacy set. + expect(PROVIDER_REGISTRY.find(entry => entry.id === "volcengine-coding-plan")?.preserveReasoningContentModels) + .toEqual(["deepseek-v4-flash"]); // A model can only be gated onto the thinking-budget or thinking-toggle wire if the same // preset also gives it an effort ladder — otherwise the adapter translates effort into a // wire field for a model whose picker is empty. opencode-go carried the shared budget list @@ -181,23 +220,26 @@ describe("provider registry parity", () => { expect(KEY_LOGIN_PROVIDERS.openrouter.modelContextWindows?.["openai/gpt-5.6-sol"]).toBe(1_050_000); expect(KEY_LOGIN_PROVIDERS.openrouter.modelContextWindows?.["openai/gpt-5.6-terra"]).toBe(1_050_000); expect(KEY_LOGIN_PROVIDERS.openrouter.modelContextWindows?.["openai/gpt-5.6-luna"]).toBe(1_050_000); - expect(KEY_LOGIN_PROVIDERS.deepseek.models).toContain("deepseek-v4-pro"); + // Retired from the first-party API on 2026-09-14; the vendor-hosted rosters keep it. + expect(KEY_LOGIN_PROVIDERS.deepseek.models).not.toContain("deepseek-v4-pro"); + expect(KEY_LOGIN_PROVIDERS.deepseek.models).toContain("deepseek-flash"); // #1057: DeepSeek's ladder is low/high/max and the two V4 models resolve it // differently (api-docs.deepseek.com/guides/thinking_mode, verified 2026-08-06). // `xhigh` is an alias, so it stays in the wire map but is not advertised. Pro // does not honor `low` (the vendor maps it to `high`), so Pro must not offer it. - expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEfforts?.["deepseek-v4-pro"]).toEqual(["low", "high", "max"]); + expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEfforts?.["deepseek-flash"]).toEqual(["low", "high", "max"]); expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEfforts?.["deepseek-v4-flash"]).toEqual(["low", "high", "max"]); - expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-pro"]?.low).toBe("low"); - expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-pro"]?.xhigh).toBe("high"); - expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-pro"]?.max).toBe("max"); + expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-flash"]?.low).toBe("low"); + expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-flash"]?.xhigh).toBe("high"); + expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-flash"]?.max).toBe("max"); expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-flash"]?.low).toBe("low"); expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-flash"]?.xhigh).toBe("high"); expect(KEY_LOGIN_PROVIDERS.deepseek.modelReasoningEffortMap?.["deepseek-v4-flash"]?.max).toBe("max"); - expect(KEY_LOGIN_PROVIDERS.deepseek.preserveReasoningContentModels).toEqual(["deepseek-v4-pro", "deepseek-v4-flash"]); + expect(KEY_LOGIN_PROVIDERS.deepseek.preserveReasoningContentModels) + .toEqual(["deepseek-flash", "deepseek-v4-flash"]); // Issue #88: every DeepSeek API model is text-only input — the vision sidecar covers them. expect(KEY_LOGIN_PROVIDERS.deepseek.noVisionModels).toEqual([ - "deepseek-chat", "deepseek-reasoner", "deepseek-v4-pro", "deepseek-v4-flash", + "deepseek-chat", "deepseek-reasoner", "deepseek-flash", "deepseek-v4-flash", ]); }); @@ -306,10 +348,9 @@ describe("provider registry parity", () => { expect(deepseek).toMatchObject({ adapter: "openai-chat", baseUrl: "https://api.deepseek.com", - defaultModel: "deepseek-v4-flash", + defaultModel: "deepseek-flash", modelContextWindows: { "deepseek-v4-flash": 1_048_576, - "deepseek-v4-pro": 1_048_576, }, }); @@ -358,7 +399,7 @@ describe("provider registry parity", () => { liveModels: false, models: [ "qwen3.8-max", "qwen3.7-max", "qwen3.7-plus", "qwen3.6-flash", - "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", + "glm-5.3", "glm-5.3-flash", "glm-5.2", ], modelInputModalities: { "qwen3.8-max": ["text", "image"], @@ -371,9 +412,8 @@ describe("provider registry parity", () => { modelContextWindows: { "qwen3.8-max": 983_616, "qwen3.7-max": 1_000_000, - "deepseek-v4-pro": 1_000_000, }, - noVisionModels: ["glm-5.3", "glm-5.2", "deepseek-v4-pro"], + noVisionModels: ["glm-5.3", "glm-5.2"], preserveReasoningContentModels: expect.arrayContaining(["qwen3.8-max", "qwen3.7-max", "qwen3.7-plus"]), }); expect(PROVIDER_REGISTRY.find(entry => entry.id === "alibaba-token-plan")?.directReasoningEffortModels) @@ -818,7 +858,7 @@ describe("provider registry parity", () => { const ollamaCloud = PROVIDER_REGISTRY.find(entry => entry.id === "ollama-cloud"); expect(ollamaCloud?.models).toEqual([ - "glm-5.3", "glm-5.3-flash", "glm-5.2", "deepseek-v4-pro", "qwen3-coder:480b", "gpt-oss:120b", + "glm-5.3", "glm-5.3-flash", "glm-5.2", "qwen3-coder:480b", "gpt-oss:120b", "kimi-k2.6", "minimax-m3", "qwen3.5:397b", "gemma4:31b", ]); expect(ollamaCloud?.models).not.toContain("qwen3-coder"); @@ -1422,15 +1462,11 @@ describe("free-provider directory isolation", () => { const flashLadder = ["low", "high", "max"]; const proLadder = ["low", "high", "max"]; const cases: Array<{ provider: string; model: string; flash: boolean }> = [ - { provider: "deepseek", model: "deepseek-v4-pro", flash: false }, + { provider: "deepseek", model: "deepseek-flash", flash: true }, { provider: "deepseek", model: "deepseek-v4-flash", flash: true }, - { provider: "opencode-go", model: "deepseek-v4-pro", flash: false }, + { provider: "opencode-go", model: "deepseek-v4.1-flash", flash: true }, { provider: "opencode-go", model: "deepseek-v4-flash", flash: true }, - { provider: "orcarouter", model: "deepseek/deepseek-v4-pro", flash: false }, - { provider: "volcengine-coding-plan", model: "deepseek-v4-pro", flash: false }, { provider: "volcengine-coding-plan", model: "deepseek-v4-flash", flash: true }, - { provider: "alibaba-token-plan", model: "deepseek-v4-pro", flash: false }, - { provider: "alibaba-token-plan-intl", model: "deepseek-v4-pro", flash: false }, { provider: "alibaba-token-plan-intl", model: "deepseek-v4-flash", flash: true }, { provider: "opencode-free", model: "deepseek-v4-flash-free", flash: true }, ]; diff --git a/tests/routing/fastwire-policy.test.ts b/tests/routing/fastwire-policy.test.ts index e9918dc6ce..36b64f6fb9 100644 --- a/tests/routing/fastwire-policy.test.ts +++ b/tests/routing/fastwire-policy.test.ts @@ -320,7 +320,7 @@ describe("resolveFastPolicy matrix", () => { { name: "DeepSeek V4 defaults", providerName: "deepseek", - modelIds: ["deepseek-v4-flash", "deepseek-v4-pro"], + modelIds: ["deepseek-flash", "deepseek-v4-flash"], provider: { adapter: "openai-chat", baseUrl: "https://api.deepseek.com", diff --git a/tests/routing/router.test.ts b/tests/routing/router.test.ts index 83be39818e..9b6eb68d4d 100644 --- a/tests/routing/router.test.ts +++ b/tests/routing/router.test.ts @@ -447,7 +447,7 @@ describe("routeModel registry effort defaults", () => { const route = routeModel(config, "deepseek/deepseek-v4-flash"); expect(route.provider.noVisionModels).toEqual([ - "deepseek-chat", "deepseek-reasoner", "deepseek-v4-pro", "deepseek-v4-flash", + "deepseek-chat", "deepseek-reasoner", "deepseek-flash", "deepseek-v4-flash", ]); }); diff --git a/tests/server/adapter-resolve.test.ts b/tests/server/adapter-resolve.test.ts index 1805ad7022..7d316a43c9 100644 --- a/tests/server/adapter-resolve.test.ts +++ b/tests/server/adapter-resolve.test.ts @@ -154,9 +154,9 @@ describe("registry per-model wire defaults", () => { test("routes the official V4 API ids through Responses", () => { expect(resolveWireProtocolOverride("deepseek", "deepseek-v4-flash", deepseek()).adapter) .toBe("openai-responses"); - // V4 Pro GA (DeepSeek-V4-Pro-0813) is officially on the Responses wire too — + // V4.1-Flash is officially on the Responses wire too — // the /responses reference lists both V4 ids as accepted `model` values. - expect(resolveWireProtocolOverride("deepseek", "deepseek-v4-pro", deepseek()).adapter) + expect(resolveWireProtocolOverride("deepseek", "deepseek-flash", deepseek()).adapter) .toBe("openai-responses"); // The dated release label is not the API model id and must not be silently rewritten. expect(resolveWireProtocolOverride("deepseek", "deepseek-v4-flash-0731", deepseek()).adapter)