标题生成强制关闭 think 模式,回答速度指示器默认开启

- TitleMiddleware 给模型实例注入 extra_body 关 think(覆盖 Zhipu GLM 与 Qwen/vLLM 两套写法),并兜底剥离不闭合/残留的 <think> 标签,避免 GLM5 起标题时长时间思考与标签泄漏。
- 前端 streamSpeedMeterEnabled 默认改为 true,设置项描述同步为"默认开启"。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
吉鑫升andClaude Opus 4.7 committed 2026-05-22 18:03:24 +08:00
1 parent f8e3a3e5b4
commit 67d5e43ec4
3 files changed
+50 -11

No files matched your search

@@ -122,7 +122,7 @@ export function AppearanceSettingsPage() {
<div className="text-sm font-medium">回答速度指示器</div>
<p className="text-muted-foreground mt-1 max-w-md text-xs leading-relaxed">
在生成回答时显示一个小胶囊,实时估算当前 token/s
并用绿到红的渐变条直观反映快慢。默认关闭。
并用绿到红的渐变条直观反映快慢。默认开启。
</p>
</div>
<Switch
+8 -8
View File
@@ -5,15 +5,15 @@ export const DEFAULT_LOCAL_SETTINGS: LocalSettings = {
enabled: true,
},
display: {
streamSpeedMeterEnabled: false,
streamSpeedMeterEnabled: true,
},
context: {
model_name: undefined,
mode: undefined,
reasoning_effort: undefined,
writing_mode: false,
},
};
model_name: undefined,
mode: undefined,
reasoning_effort: undefined,
writing_mode: false,
},
};
export const LOCAL_SETTINGS_KEY = "deerflow.local-settings";
export const THREAD_MODEL_KEY_PREFIX = "deerflow.thread-model.";
@@ -29,7 +29,7 @@ export interface LocalSettings {
display: {
/**
* Whether to render a small token/s speed meter while a streaming
* AI reply is in flight. Default off; controlled by the user from
* AI reply is in flight. Default on; controlled by the user from
* the appearance settings page.
*/
streamSpeedMeterEnabled: boolean;
@@ -105,8 +105,16 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
return prompt, user_msg
def _strip_think_tags(self, text: str) -> str:
"""Remove <think>...</think> blocks emitted by reasoning models (e.g. minimax, DeepSeek-R1)."""
return re.sub(r"<think>[\s\S]*?</think>", "", text, flags=re.IGNORECASE).strip()
"""Remove <think>...</think> blocks emitted by reasoning models (e.g. minimax, GLM, DeepSeek-R1).
Also handles malformed cases: an unclosed `<think>` (response truncated mid-reasoning)
or a stray `</think>` (open tag eaten by upstream parsing) — both can otherwise leak
raw reasoning into the title.
"""
text = re.sub(r"<think>[\s\S]*?</think>", "", text, flags=re.IGNORECASE)
text = re.sub(r"<think>[\s\S]*$", "", text, flags=re.IGNORECASE)
text = re.sub(r"^[\s\S]*?</think>", "", text, flags=re.IGNORECASE)
return text.strip()
def _parse_title(self, content: object) -> str:
"""Normalize model output into a clean title string."""
@@ -146,6 +154,36 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
_, user_msg = self._build_title_prompt(state)
return {"title": self._fallback_title(user_msg)}
def _force_disable_thinking(self, model: object) -> None:
"""Force-disable thinking on the title model via `extra_body` overrides.
Title generation never needs reasoning. The model factory only emits
disable params when the model config declares `when_thinking_disabled`
or a matching `when_thinking_enabled` shape — many user-added reasoning
models (e.g. GLM-4.5/5) only set `supports_thinking: true` and would
otherwise keep thinking on, which is slow and leaks <think> tags into
the title prompt response. Injecting both common OpenAI-compatible
disable shapes covers Zhipu GLM (`thinking.type`) and Qwen/vLLM
(`chat_template_kwargs.enable_thinking`); other providers ignore
unknown body fields.
"""
if not hasattr(model, "extra_body"):
return
current = getattr(model, "extra_body", None) or {}
merged_chat_template_kwargs = {
**(current.get("chat_template_kwargs") or {}),
"enable_thinking": False,
}
merged = {
**current,
"thinking": {**(current.get("thinking") or {}), "type": "disabled"},
"chat_template_kwargs": merged_chat_template_kwargs,
}
try:
model.extra_body = merged
except Exception:
logger.debug("Could not override extra_body to disable thinking on title model", exc_info=True)
async def _agenerate_title_result(self, state: TitleMiddlewareState) -> dict | None:
"""Generate a title asynchronously and fall back locally on failure."""
if not self._should_generate_title(state):
@@ -162,6 +200,7 @@ class TitleMiddleware(AgentMiddleware[TitleMiddlewareState]):
model = create_chat_model(name=config.model_name, **model_kwargs)
else:
model = create_chat_model(**model_kwargs)
self._force_disable_thinking(model)
response = await model.ainvoke(prompt, config=self._get_runnable_config())
title = self._parse_title(response.content)
if title: