feat: DeepSeek V4 support with reasoning-effort control (0.4.0) - #17
Conversation
Pivot the client to DeepSeek's documented OpenAI-compatible Chat Completions API as the primary path. The Responses API probe is now gated behind DEEPSEEK_EXPERIMENTAL_RESPONSES_API for local compatibility experiments only. - Update pricing to DeepSeek's cache-hit vs cache-miss input tiers and wire cached-token fields from usage into cost tracking. - Accept /v1 and /beta base URLs for OpenAI SDK compatibility and document /v1/models for live discovery and health checks. - Refresh README, AGENTS, CONFIGURATION, ARCHITECTURE, and npm README to describe the documented endpoints, cache behavior, and env overrides. - Extend engine, turn, session, UI, and client tests to cover the new path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds first-class support for deepseek-v4-pro and deepseek-v4-flash with 1M context windows and new per-model pricing. Introduces a reasoning-effort tier (off/low/medium/high/max) plumbed through the request layer and a Shift+Tab cycler with a header chip for live switching. - Model registry: v4-pro (flagship) and v4-flash first-class; legacy deepseek-chat/reasoner/v3.2 kept as silent aliases of v4-flash. - Defaults flipped to deepseek-v4-pro. - Pricing: v4-pro \$0.145/\$1.74/\$3.48, v4-flash \$0.028/\$0.14/\$0.28 per 1M (cache-hit / cache-miss / output). - 1M context window recognized for v4 models; MAX_COMPACTION_MESSAGE_THRESHOLD raised 150 → 500 so large-context runs don't prematurely compact. - reasoning_effort config field + Op::SendMessage + Session plumbing. - Client translates effort into reasoning_effort + extra_body.thinking for chat/completions, streaming, and Responses. - Shift+Tab cycles off → high → max; header shows ⚡ chip. - npm publish job added to release.yml (OIDC trusted publisher). - Version bump 0.3.33 → 0.4.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request updates the workspace to version 0.4.0, introducing comprehensive support for DeepSeek V4 models, including deepseek-v4-pro and deepseek-v4-flash. Key enhancements include the implementation of reasoning-effort tiers (off, high, max) with TUI integration, updated pricing calculations for context caching, and a refactored CLI that provides better command delegation to the TUI. The default model is transitioned to deepseek-v4-pro. Review feedback highlights several locations in the CLI where hardcoded legacy model IDs and test assertions need to be updated to maintain consistency with the new V4 defaults.
| .deepseek | ||
| .model | ||
| .clone() | ||
| .unwrap_or_else(|| "deepseek-reasoner".to_string()), |
There was a problem hiding this comment.
The fallback model here is still hardcoded to deepseek-reasoner, which is now a legacy alias for deepseek-v4-flash. Since the flagship model has been updated to deepseek-v4-pro across the rest of the workspace (e.g., in crates/config/src/lib.rs and crates/tui/src/config.rs), this should be updated to maintain consistency for new logins.
| .unwrap_or_else(|| "deepseek-reasoner".to_string()), | |
| .unwrap_or_else(|| "deepseek-v4-pro".to_string()), |
| assert_eq!(store.config.api_key.as_deref(), Some("sk-test")); | ||
| assert_eq!( | ||
| store.config.default_text_model.as_deref(), | ||
| Some("deepseek-reasoner") |
| ); | ||
| let saved = std::fs::read_to_string(&path).expect("config should be written"); | ||
| assert!(saved.contains("api_key = \"sk-test\"")); | ||
| assert!(saved.contains("default_text_model = \"deepseek-reasoner\"")); |
Add override in both integrations/feishu-bridge and web package.json to resolve GHSA-q8mj-m7cp-5q26 (moderate DoS vulnerability in qs < 6.15.2). Regenerated both package-lock.json files. Dependabot alerts #16 (web) and #17 (feishu-bridge) are the two vulnerabilities flagged during push.
…room, fleet roles Launch / splash - The launch stage now samples the ambient clock and asks for frames while the mark surfaces, the card dissolves, or the water is alive. Before, the only clock driver was the transcript widget, which the launch screen never builds: surface_progress stayed at 0 and the braille whale was painted in the field colour forever (Hmbown#1), and the ocean ramp / ambient life were never wired into the stage (Hmbown#2, Hmbown#3). The stage paints the same ramp and life the transcript does; the life field ends at the composer dock. - Recent rows resume (Hmbown#4): AppAction::LoadSession never dismissed the launch stage, so a resumed session loaded under a still-visible card. - Tab cycles the mode on the launch screen (Hmbown#5): ModeCycle is AnyShell. The card's rule now reads `model (effort) · mode · permission` so the cycle is legible and matches the posture bar (Hmbown#7). Work bar - One-time migration of a persisted `work_surface_placement = "top"` to `bottom` (Hmbown#9), recorded in `work_surface_bottom_migrated`. - Idle scheduled automations no longer count as work that auto-opens the dock (Hmbown#10); live shells, tasks, or a running automation still do. - ORDER / AUTO_ORDER lead with TODO, then AGENTS (Hmbown#11). The dismissed dock re-opens on new work in any auto view, and `← for agents` works from the TODO view. Golden dock_80x24 re-blessed for the order. - The posture bar's live counts are click targets that open the dock view they count; with nothing live a dim `todo` chip is the bottom affordance. /mcp, /plugin, Extensions - /mcp and every MCP action open Extensions → MCP rebuilt from the live pool snapshot; the text pager and its formatter are deleted (Hmbown#17–Hmbown#19, Hmbown#25 — the `tools: 0` rows were the passive config snapshot). - The markdown inline parser honours backslash escapes, so `computer\-use` renders as `computer-use` (Hmbown#21). - Extensions keyboard follows grokbuild: Tab / Shift+Tab move across tabs, including mid-search with the query kept (Hmbown#22, Hmbown#23). Automations - New AutomationsView (`/automation`, `/automation show <id>`, the dock's background row): list + detail, p pause/resume, r run, x cancel the live run (via /task cancel), d delete (two-step confirm), Tab list↔detail. Every action is the typed command, so receipts match (Hmbown#14, Hmbown#16). `/automation list` and `print <id>` keep the text forms. Fleet - Roles are members. `add_fleet_model` requires a role (NeedsRole); the auto-enroll that turned every selected model into a role-less member is deleted with its 7 call sites; bare model pins are dropped on read so 27 → 12 members (Hmbown#26). Picker ⇧F enrolls under `general`. - Roster detail is role-first and compact (Hmbown#27, Hmbown#28); footer is ↑↓ · Enter · Tab workers · f · Esc (Hmbown#29). `m`, `s` and the model-picker shortcut event are removed. Gates (run): - cargo fmt --all -- --check: clean - cargo clippy -p codewhale-tui -p codewhale-config --all-targets: clean - scripts/dev-test.sh tui: 11658 tests run: 11658 passed, 13 skipped - scripts/dev-test.sh tui-integration: 280 passed - cargo test -p codewhale-config --lib: 634 passed; 0 failed; 1 ignored - budget checks: dead-code 418/425, runtime-contract, provider-registry, command boundaries, migration manifest, locale parity (15 packs complete), product vocabulary: all PASS - release build + tmux dogfood at 120x34: whale surfaces, ramp + fish on launch, Tab cycles mode, Enter on recent row resumes, dock closed at start and bottom/TODO-first when opened, /mcp opens Extensions with live tool counts, /automation opens the room, fleet shows 12 role members. Not run: cargo nextest --workspace, tui-cucumber, web rung (no web change). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Summary
deepseek-v4-proanddeepseek-v4-flashwith 1M context windows and current DeepSeek alias handling.thinkingin raw Chat Completions JSON, not SDK-onlyextra_body.reasoning_contentfor all thinking-mode tool-call turns so multi-turn tool conversations do not hit DeepSeek's HTTP 400 replay error.Model & pricing
deepseek-v4-prodeepseek-v4-flashLegacy
deepseek-chat,deepseek-reasoner,deepseek-r1,deepseek-v3, anddeepseek-v3.2remain accepted local aliases and now budget against the current V4 1M context unless an explicit context suffix is provided.Reasoning effort
Config field
reasoning_effortin~/.deepseek/config.toml:off->thinking.type = "disabled"low/medium/high->reasoning_effort: "high",thinking.type = "enabled"max/xhigh->reasoning_effort: "max",thinking.type = "enabled"Test plan
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --workspace --all-features --lockedcargo build --release --locked -p deepseek-tui-cli -p deepseek-tuitarget/release/deepseek-tui doctorDEEPSEEK_MODEL=deepseek-v4-pro target/release/deepseek-tui --no-alt-screen --skip-onboarding -p "Reply with exactly OK."npm run release:check,npm pack, temp-installnpx deepseek/npx deepseek-tuismoke