diff --git a/CHANGELOG.md b/CHANGELOG.md index c3cab001..96798298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.20] - 2026-05-08 + +### Fixed +- **Chinese reasoning stays Chinese** - restore the #588 language contract after + the deterministic environment prompt regressed it. The latest user message now + chooses the natural language for both `reasoning_content` and the final reply; + the resolved `lang` field is only a fallback when the user turn is ambiguous. + ## [0.8.19] - 2026-05-08 ### Fixed diff --git a/Cargo.lock b/Cargo.lock index 494597f6..4d6e376c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1152,7 +1152,7 @@ dependencies = [ [[package]] name = "deepseek-agent" -version = "0.8.19" +version = "0.8.20" dependencies = [ "deepseek-config", "serde", @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "deepseek-app-server" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "axum", @@ -1182,7 +1182,7 @@ dependencies = [ [[package]] name = "deepseek-config" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "deepseek-secrets", @@ -1194,7 +1194,7 @@ dependencies = [ [[package]] name = "deepseek-core" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "chrono", @@ -1212,7 +1212,7 @@ dependencies = [ [[package]] name = "deepseek-execpolicy" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "deepseek-protocol", @@ -1221,7 +1221,7 @@ dependencies = [ [[package]] name = "deepseek-hooks" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "async-trait", @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "deepseek-mcp" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "serde", @@ -1244,7 +1244,7 @@ dependencies = [ [[package]] name = "deepseek-protocol" -version = "0.8.19" +version = "0.8.20" dependencies = [ "serde", "serde_json", @@ -1252,7 +1252,7 @@ dependencies = [ [[package]] name = "deepseek-secrets" -version = "0.8.19" +version = "0.8.20" dependencies = [ "dirs", "keyring", @@ -1265,7 +1265,7 @@ dependencies = [ [[package]] name = "deepseek-state" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "chrono", @@ -1277,7 +1277,7 @@ dependencies = [ [[package]] name = "deepseek-tools" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "async-trait", @@ -1290,7 +1290,7 @@ dependencies = [ [[package]] name = "deepseek-tui" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "arboard", @@ -1351,7 +1351,7 @@ dependencies = [ [[package]] name = "deepseek-tui-cli" -version = "0.8.19" +version = "0.8.20" dependencies = [ "anyhow", "chrono", @@ -1375,7 +1375,7 @@ dependencies = [ [[package]] name = "deepseek-tui-core" -version = "0.8.19" +version = "0.8.20" [[package]] name = "deranged" diff --git a/Cargo.toml b/Cargo.toml index df918b46..0444787b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ default-members = ["crates/cli", "crates/app-server", "crates/tui"] resolver = "2" [workspace.package] -version = "0.8.19" +version = "0.8.20" edition = "2024" # Rust 1.88 stabilized `let_chains` in `if`/`while` conditions, which the # codebase relies on extensively. Cargo enforces this so users on older diff --git a/README.md b/README.md index 35decfce..ab282d98 100644 --- a/README.md +++ b/README.md @@ -225,12 +225,15 @@ deepseek --provider ollama --model deepseek-coder:1.3b --- -## What's New In v0.8.19 +## What's New In v0.8.20 -A hotfix release for DeepSeek endpoint defaults, plus the v0.8.18 -TUI/runtime/install polish. +A hotfix release for Chinese reasoning language, DeepSeek endpoint defaults, +and the v0.8.18 TUI/runtime/install polish. [Full changelog](CHANGELOG.md). +- **Chinese reasoning stays Chinese** - when the latest user message is in + Simplified Chinese, V4 `reasoning_content` and the final reply are prompted + to stay in Simplified Chinese even on an English system locale. - **DeepSeek beta endpoint stays default worldwide** - Chinese locales and legacy `deepseek-cn` configs now use `https://api.deepseek.com/beta`, so strict tool mode and other beta-gated features remain available. @@ -371,7 +374,7 @@ Key environment variables: | `NO_ANIMATIONS=1` | Force accessibility mode at startup | | `SSL_CERT_FILE` | Custom CA bundle for corporate proxies | -Set `locale` in `settings.toml`, use `/config locale zh-Hans`, or rely on `LC_ALL`/`LANG` to choose UI chrome and the default natural language sent to V4 models. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) and [docs/MCP.md](docs/MCP.md). +Set `locale` in `settings.toml`, use `/config locale zh-Hans`, or rely on `LC_ALL`/`LANG` to choose UI chrome and the fallback language sent to V4 models. The latest user message still wins for natural-language reasoning and replies, so Chinese user turns stay Chinese even on an English system locale. See [docs/CONFIGURATION.md](docs/CONFIGURATION.md) and [docs/MCP.md](docs/MCP.md). --- diff --git a/README.zh-CN.md b/README.zh-CN.md index 57a22350..a2970e50 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -192,10 +192,12 @@ deepseek --provider ollama --model deepseek-coder:1.3b --- -## v0.8.18 新功能 +## v0.8.20 新功能 -面向 TUI、运行时和安装体验的跟进版本。[完整更新日志](CHANGELOG.md)。 +面向中文思考语言、端点默认值、TUI、运行时和安装体验的热修复版本。[完整更新日志](CHANGELOG.md)。 +- **中文思考保持中文** —— 当最新用户消息是简体中文时,即使系统 locale + 是英文,V4 的 `reasoning_content` 和最终回复也会被提示保持简体中文。 - **直接运行 `deepseek` 会启动新会话** —— 同一目录开第二个终端时,不再静默进入 同一个中断检查点;需要恢复时请显式使用 `deepseek --continue`。 - **Docker 成为受支持安装方式** —— 发布流程会推送 @@ -288,7 +290,7 @@ deepseek update # 检查并应用二进制更新 | `NO_ANIMATIONS=1` | 启动时强制无障碍模式 | | `SSL_CERT_FILE` | 企业代理的自定义 CA 包 | -UI 语言与模型输出语言相互独立——在 `config.toml` 中设置 `locale`、使用 `/config locale zh-Hans`、或依赖 `LC_ALL`/`LANG`。详见 [docs/LOCALIZATION.md](docs/LOCALIZATION.md) 和 [docs/CONFIGURATION.md](docs/CONFIGURATION.md)。 +`locale` 会控制界面语言,并作为模型自然语言的兜底设置;最新用户消息的语言优先级更高。也就是说,即使系统 locale 是英文,用户用中文提问时,V4 的 `reasoning_content` 和最终回复也应该使用中文。可在 `config.toml` 中设置 `locale`、使用 `/config locale zh-Hans`、或依赖 `LC_ALL`/`LANG`。详见 [docs/LOCALIZATION.md](docs/LOCALIZATION.md) 和 [docs/CONFIGURATION.md](docs/CONFIGURATION.md)。 ### 切换为中文界面 diff --git a/crates/agent/Cargo.toml b/crates/agent/Cargo.toml index ffecb63c..d05ff0fc 100644 --- a/crates/agent/Cargo.toml +++ b/crates/agent/Cargo.toml @@ -7,5 +7,5 @@ repository.workspace = true description = "Model/provider registry and fallback strategy for DeepSeek workspace architecture" [dependencies] -deepseek-config = { path = "../config", version = "0.8.19" } +deepseek-config = { path = "../config", version = "0.8.20" } serde.workspace = true diff --git a/crates/app-server/Cargo.toml b/crates/app-server/Cargo.toml index 4b8f1cf8..b46f5fef 100644 --- a/crates/app-server/Cargo.toml +++ b/crates/app-server/Cargo.toml @@ -10,15 +10,15 @@ description = "Codex-style app-server transport for DeepSeek workspace architect anyhow.workspace = true axum.workspace = true clap.workspace = true -deepseek-agent = { path = "../agent", version = "0.8.19" } -deepseek-config = { path = "../config", version = "0.8.19" } -deepseek-core = { path = "../core", version = "0.8.19" } -deepseek-execpolicy = { path = "../execpolicy", version = "0.8.19" } -deepseek-hooks = { path = "../hooks", version = "0.8.19" } -deepseek-mcp = { path = "../mcp", version = "0.8.19" } -deepseek-protocol = { path = "../protocol", version = "0.8.19" } -deepseek-state = { path = "../state", version = "0.8.19" } -deepseek-tools = { path = "../tools", version = "0.8.19" } +deepseek-agent = { path = "../agent", version = "0.8.20" } +deepseek-config = { path = "../config", version = "0.8.20" } +deepseek-core = { path = "../core", version = "0.8.20" } +deepseek-execpolicy = { path = "../execpolicy", version = "0.8.20" } +deepseek-hooks = { path = "../hooks", version = "0.8.20" } +deepseek-mcp = { path = "../mcp", version = "0.8.20" } +deepseek-protocol = { path = "../protocol", version = "0.8.20" } +deepseek-state = { path = "../state", version = "0.8.20" } +deepseek-tools = { path = "../tools", version = "0.8.20" } serde.workspace = true serde_json.workspace = true tokio.workspace = true diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index ef65eec6..724988ad 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -14,13 +14,13 @@ path = "src/main.rs" anyhow.workspace = true clap.workspace = true clap_complete.workspace = true -deepseek-agent = { path = "../agent", version = "0.8.19" } -deepseek-app-server = { path = "../app-server", version = "0.8.19" } -deepseek-config = { path = "../config", version = "0.8.19" } -deepseek-execpolicy = { path = "../execpolicy", version = "0.8.19" } -deepseek-mcp = { path = "../mcp", version = "0.8.19" } -deepseek-secrets = { path = "../secrets", version = "0.8.19" } -deepseek-state = { path = "../state", version = "0.8.19" } +deepseek-agent = { path = "../agent", version = "0.8.20" } +deepseek-app-server = { path = "../app-server", version = "0.8.20" } +deepseek-config = { path = "../config", version = "0.8.20" } +deepseek-execpolicy = { path = "../execpolicy", version = "0.8.20" } +deepseek-mcp = { path = "../mcp", version = "0.8.20" } +deepseek-secrets = { path = "../secrets", version = "0.8.20" } +deepseek-state = { path = "../state", version = "0.8.20" } chrono.workspace = true dirs.workspace = true serde.workspace = true diff --git a/crates/config/Cargo.toml b/crates/config/Cargo.toml index 1438a52a..1043d1e6 100644 --- a/crates/config/Cargo.toml +++ b/crates/config/Cargo.toml @@ -8,7 +8,7 @@ description = "Config schema and precedence model for DeepSeek workspace archite [dependencies] anyhow.workspace = true -deepseek-secrets = { path = "../secrets", version = "0.8.19" } +deepseek-secrets = { path = "../secrets", version = "0.8.20" } dirs.workspace = true serde.workspace = true toml.workspace = true diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b3e787bc..844e8c85 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -9,13 +9,13 @@ description = "Core runtime boundaries for DeepSeek workspace architecture" [dependencies] anyhow.workspace = true chrono.workspace = true -deepseek-agent = { path = "../agent", version = "0.8.19" } -deepseek-config = { path = "../config", version = "0.8.19" } -deepseek-execpolicy = { path = "../execpolicy", version = "0.8.19" } -deepseek-hooks = { path = "../hooks", version = "0.8.19" } -deepseek-mcp = { path = "../mcp", version = "0.8.19" } -deepseek-protocol = { path = "../protocol", version = "0.8.19" } -deepseek-state = { path = "../state", version = "0.8.19" } -deepseek-tools = { path = "../tools", version = "0.8.19" } +deepseek-agent = { path = "../agent", version = "0.8.20" } +deepseek-config = { path = "../config", version = "0.8.20" } +deepseek-execpolicy = { path = "../execpolicy", version = "0.8.20" } +deepseek-hooks = { path = "../hooks", version = "0.8.20" } +deepseek-mcp = { path = "../mcp", version = "0.8.20" } +deepseek-protocol = { path = "../protocol", version = "0.8.20" } +deepseek-state = { path = "../state", version = "0.8.20" } +deepseek-tools = { path = "../tools", version = "0.8.20" } serde_json.workspace = true uuid.workspace = true diff --git a/crates/execpolicy/Cargo.toml b/crates/execpolicy/Cargo.toml index 501a62b4..c888ab0f 100644 --- a/crates/execpolicy/Cargo.toml +++ b/crates/execpolicy/Cargo.toml @@ -8,5 +8,5 @@ description = "Execution policy and approval model parity for DeepSeek workspace [dependencies] anyhow.workspace = true -deepseek-protocol = { path = "../protocol", version = "0.8.19" } +deepseek-protocol = { path = "../protocol", version = "0.8.20" } serde.workspace = true diff --git a/crates/hooks/Cargo.toml b/crates/hooks/Cargo.toml index 2132047a..620f6761 100644 --- a/crates/hooks/Cargo.toml +++ b/crates/hooks/Cargo.toml @@ -10,7 +10,7 @@ description = "Hook dispatch and notifications parity for DeepSeek workspace arc anyhow.workspace = true async-trait.workspace = true chrono.workspace = true -deepseek-protocol = { path = "../protocol", version = "0.8.19" } +deepseek-protocol = { path = "../protocol", version = "0.8.20" } reqwest.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml index cbb8ebd6..98c0b013 100644 --- a/crates/tools/Cargo.toml +++ b/crates/tools/Cargo.toml @@ -9,7 +9,7 @@ description = "Tool invocation lifecycle, schema validation, and scheduler paral [dependencies] anyhow.workspace = true async-trait.workspace = true -deepseek-protocol = { path = "../protocol", version = "0.8.19" } +deepseek-protocol = { path = "../protocol", version = "0.8.20" } serde.workspace = true serde_json.workspace = true tokio.workspace = true diff --git a/crates/tui/Cargo.toml b/crates/tui/Cargo.toml index 77da1c5f..55a44274 100644 --- a/crates/tui/Cargo.toml +++ b/crates/tui/Cargo.toml @@ -21,8 +21,8 @@ path = "src/main.rs" [dependencies] anyhow = "1.0.100" arboard = "3.4" -deepseek-secrets = { path = "../secrets", version = "0.8.19" } -deepseek-tools = { path = "../tools", version = "0.8.19" } +deepseek-secrets = { path = "../secrets", version = "0.8.20" } +deepseek-tools = { path = "../tools", version = "0.8.20" } schemaui = { version = "0.12.0", default-features = false, optional = true } async-stream = "0.3.6" async-trait = "0.1" diff --git a/crates/tui/src/prompts.rs b/crates/tui/src/prompts.rs index 4441018f..b57e43cb 100644 --- a/crates/tui/src/prompts.rs +++ b/crates/tui/src/prompts.rs @@ -618,8 +618,8 @@ mod tests { fn package_version_is_current_hotfix_release() { assert_eq!( env!("CARGO_PKG_VERSION"), - "0.8.19", - "0.8.19 release branch must report the release version before publishing" + "0.8.20", + "0.8.20 release branch must report the release version before publishing" ); } @@ -775,6 +775,24 @@ mod tests { } } + #[test] + fn language_mirroring_prioritizes_latest_user_message_over_locale_default() { + let prompt = compose_prompt(AppMode::Agent, Personality::Calm); + assert!( + prompt.contains("latest user message first"), + "the language directive must choose the turn language from the user message before \ + falling back to the environment locale" + ); + assert!( + prompt.contains("even when the `lang` field in `## Environment` is `en`"), + "Chinese user text must override an English resolved locale for reasoning_content" + ); + assert!( + prompt.contains("Use the `lang` field only when"), + "environment locale should be an ambiguity fallback, not the primary language source" + ); + } + /// #358: rlm guidance was reframed from "first-class" to "specialty /// tool" — verify the structural markers are present so a future /// change doesn't silently remove the RLM section entirely. diff --git a/crates/tui/src/prompts/base.md b/crates/tui/src/prompts/base.md index 51e5a94c..501b83e1 100644 --- a/crates/tui/src/prompts/base.md +++ b/crates/tui/src/prompts/base.md @@ -2,7 +2,7 @@ You are DeepSeek TUI. You're already running inside it — don't try to launch a ## Language -Use the language indicated by the `lang` field in the `## Environment` section as your default — both for `reasoning_content` and for the final reply. For example, when `lang` resolves to a Simplified Chinese tag (`zh-Hans`, `zh-CN`, …) reason and reply in Simplified Chinese; when it is `ja` use Japanese. If the user writes in a different language during the session, switch with them. When `lang` is missing or ambiguous, fall back to detecting the user's writing. +Choose the natural language for each turn from the latest user message first — both for `reasoning_content` and for the final reply. If the latest user message is Simplified Chinese (简体中文), your `reasoning_content` and final reply must both be in Simplified Chinese, even when the `lang` field in `## Environment` is `en`. If the user switches languages mid-session, switch with them. Use the `lang` field only when the latest user message is missing, mostly code/logs, or otherwise ambiguous. Code, file paths, identifiers, tool names, environment variables, command-line flags, URLs, and log lines stay in their original form — translating `read_file` to `读取文件` would break tool calls. Only natural-language prose mirrors the user. diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 8cf39388..9a0b5bb4 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -288,8 +288,10 @@ Common settings keys: - `locale` (`auto`, `en`, `ja`, `zh-Hans`, `pt-BR`; default `auto`): UI chrome locale. `auto` checks `LC_ALL`, `LC_MESSAGES`, then `LANG`; unsupported or missing locales fall back to English. The runtime also exposes the resolved - locale in the system prompt so V4 models use it as the default natural - language for reasoning and replies. + locale in the system prompt as the fallback natural language for V4 reasoning + and replies when the latest user message is ambiguous. Clear user language + still takes priority; Chinese turns should produce Chinese `reasoning_content` + and Chinese final replies even when the resolved locale is English. - `background_color` (`#RRGGBB`, `RRGGBB`, or `default`): optional main TUI background color applied to the root, header, transcript, and footer surfaces while preserving panel contrast. diff --git a/docs/DOCKER.md b/docs/DOCKER.md index 1a23c7b2..adfa51ba 100644 --- a/docs/DOCKER.md +++ b/docs/DOCKER.md @@ -24,7 +24,7 @@ Use a pinned release tag for reproducible installs: docker run --rm -it \ -e DEEPSEEK_API_KEY="$DEEPSEEK_API_KEY" \ -v ~/.deepseek:/home/deepseek/.deepseek \ - ghcr.io/hmbown/deepseek-tui:v0.8.19 + ghcr.io/hmbown/deepseek-tui:v0.8.20 ``` ## Local build diff --git a/docs/LOCALIZATION.md b/docs/LOCALIZATION.md index 9c04a2ad..0182055c 100644 --- a/docs/LOCALIZATION.md +++ b/docs/LOCALIZATION.md @@ -37,9 +37,10 @@ Fallback: - Missing or unsupported configured locales fall back to English. - `auto` falls back to English when no supported environment locale is detected. -- The resolved locale is included in the system prompt and used as the default - natural language for V4 reasoning and replies. Users can still switch - languages mid-session by writing in a different language. +- The resolved locale is included in the system prompt as the fallback natural + language for V4 reasoning and replies. The latest user message takes priority, + including for `reasoning_content`, so a Chinese turn should remain Chinese + even when the resolved locale is English. ## Planned Global South QA Matrix diff --git a/npm/deepseek-tui/package.json b/npm/deepseek-tui/package.json index e8451647..eba518a1 100644 --- a/npm/deepseek-tui/package.json +++ b/npm/deepseek-tui/package.json @@ -1,7 +1,7 @@ { "name": "deepseek-tui", - "version": "0.8.19", - "deepseekBinaryVersion": "0.8.19", + "version": "0.8.20", + "deepseekBinaryVersion": "0.8.20", "description": "Install and run deepseek and deepseek-tui binaries from GitHub release artifacts.", "author": "Hmbown", "license": "MIT",