修复: 上下文流式与工具签名域清债

- tool_signature 归一化补参,合法换参不再误触探索熔断
- SSE 超限截头改对齐事件边界保尾,流不再永久破损
- content_filter 纳入终态判定,无 DONE 流也收敛
- sanitize 占位补头前缀短路只吞 orphan 的盲区消除
- intent 关键词加词边界,history 不再误判
- 恢复重放后重置话题锚点,URL 图 token 地板不被稀释
This commit is contained in:
lxy
2026-10-03 15:35:05 +08:00
parent 499aa49e49
commit c1e5b96d75
10 changed files with 638 additions and 73 deletions
+29 -1
View File
@@ -51,7 +51,7 @@ fn estimate_message_counts_parts_tokens() {
mm_tokens
);
// url 模式(无字节)也按 URL 长度估算,不爆
// url 模式(无字节)按每图 ~1600 token 地板估(生效于 ×0.35 折扣之后,防预算漏裁)
let url_msg = ChatMessage::user_parts(
"t",
vec![crate::provider::ContentPart::image_url("https://example.com/x.png")],
@@ -777,6 +777,34 @@ fn topic_marker_take_is_idempotent_and_clears() {
assert!(mgr.take_topic_marker().is_none(), "marker 一次性消费, 二次 take 必空");
}
#[test]
fn topic_marker_reset_after_restore_from_messages() {
// restore_from_messages(DB 加载)重放历史时 push 会照常跑主题切换检测,
// 历史里"当时已发生"的切换不得残留成 pending marker——否则加载后首轮 take
// 拿到假话题锚点,后续话题归属被误判为切换、多插一次系统标记。
let history = vec![
ChatMessage::user("帮我重构这段代码"), // topic=code(高置信)
ChatMessage::assistant("好的"),
ChatMessage::user("创建项目并绑定目录"), // topic=project(高置信)→ 重放中会置位 marker
];
let mut mgr = ContextManager::new(cfg(100_000));
mgr.restore_from_messages(history);
assert!(
mgr.take_topic_marker().is_none(),
"重放历史不得残留 pending_topic_marker(假话题锚点)"
);
// restore 之后的实时 push 检测不受影响:末条 user topic=project,新 push code → 触发
mgr.push(ChatMessage::user("帮我重构这段代码"));
let marker = mgr
.take_topic_marker()
.expect("restore 后的实时切换仍应触发");
assert!(
marker.contains("project") && marker.contains("code"),
"marker: {}",
marker
);
}
// ============================================================
// msg-split-phase1:增量写路径基线(persisted_msg_count)+ 全量重写标志(needs_full_rewrite)
// ============================================================