fix(prompts): prioritize user language for reasoning (#1137)
This commit is contained in:
@@ -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
|
||||
|
||||
Generated
+14
-14
@@ -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"
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+5
-3
@@ -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)。
|
||||
|
||||
### 切换为中文界面
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user