标题生成强制关闭 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:
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
+41
-2
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user