修复: 协议层与体验缺陷六件

- temperature 按模型适配,仅收 1 的模型不再 400
- 流式 error 帧不再被 choices 必填整帧丢弃
- 只读缓存写后失效
- git diff 中文截断撞 UTF-8 边界 panic
- 非视觉模型纯图片直接拒绝
- 全局快捷键补输入焦点守卫
This commit is contained in:
lxy
2026-10-04 16:27:31 +08:00
parent 01d231c39d
commit 7aa0da32f2
28 changed files with 491 additions and 52 deletions
+23 -8
View File
@@ -841,12 +841,12 @@ fn phase1_push_only_keeps_clean_no_dirty_flag() {
#[test]
fn phase1_advance_persisted_count_sets_baseline_and_clears_dirty() {
// advance_persisted_count(new_count):推进基线 + 清 needs_full_rewrite(DB 已与内存一致)
// advance_persisted_count(new_count, full):推进基线;全量重写才清 needs_full_rewrite
let mut mgr = ContextManager::new(cfg(100_000));
mgr.push(ChatMessage::user("x"));
mgr.push(ChatMessage::user("y"));
// 模拟 save 成功后推进
mgr.advance_persisted_count(2);
// 模拟 save(全量重写)成功后推进
mgr.advance_persisted_count(2, true);
assert_eq!(mgr.persisted_msg_count(), 2);
assert!(!mgr.needs_full_rewrite());
}
@@ -855,11 +855,26 @@ fn phase1_advance_persisted_count_sets_baseline_and_clears_dirty() {
fn phase1_advance_persisted_count_max_no_regression() {
// 防回退:advance 传更小值不回退基线(防误传 / 并发致基线丢失 → 下次增量漏写)
let mut mgr = ContextManager::new(cfg(100_000));
mgr.advance_persisted_count(5);
mgr.advance_persisted_count(3); // 误传更小
mgr.advance_persisted_count(5, true);
mgr.advance_persisted_count(3, true); // 误传更小
assert_eq!(mgr.persisted_msg_count(), 5, "基线不可回退");
}
#[test]
fn phase1_advance_incremental_keeps_dirty() {
// 增量/无变化路径(full=false)不得清 dirty:写库期间新置的 dirty(如压缩链 mark)
// 与本次 append 写库无关,误清会致改动滞留内存永不落库
let mut mgr = ContextManager::new(cfg(100_000));
mgr.push(ChatMessage::user("a"));
mgr.advance_persisted_count(1, false); // 增量 save 成功
assert!(!mgr.needs_full_rewrite());
mgr.mark_needs_full_rewrite(); // 模拟写库期间压缩 mark
mgr.advance_persisted_count(1, false); // 又一次增量 save
assert!(mgr.needs_full_rewrite(), "增量路径 advance 不得清 dirty");
mgr.advance_persisted_count(1, true); // 直到全量重写才清
assert!(!mgr.needs_full_rewrite());
}
#[test]
fn phase1_compress_old_messages_sets_dirty() {
// compress 改既有消息 status(active→compressed) → 必须全量重写收敛
@@ -881,7 +896,7 @@ fn phase1_compress_no_op_does_not_set_dirty() {
mgr.push(ChatMessage::user("a"));
let _ = mgr.compress_old_messages(1);
assert!(mgr.needs_full_rewrite(), "首次压缩应置 dirty");
mgr.advance_persisted_count(1); // 模拟 save 后清 dirty
mgr.advance_persisted_count(1, true); // 模拟 save(全量)后清 dirty
// 二次压缩同范围:全已 compressed,无新标 → 不应再置 dirty
let compressed = mgr.compress_old_messages(1);
assert!(compressed.is_empty());
@@ -974,7 +989,7 @@ fn phase1_clear_resets_baseline_and_sets_dirty() {
// clear:基线重置 0 + 置 dirty(下次 save 全量重写空列表清 DB)
let mut mgr = ContextManager::new(cfg(100_000));
mgr.push(ChatMessage::user("a"));
mgr.advance_persisted_count(1);
mgr.advance_persisted_count(1, true);
assert_eq!(mgr.persisted_msg_count(), 1);
mgr.clear();
assert_eq!(mgr.persisted_msg_count(), 0, "clear 应重置基线到 0");
@@ -1021,7 +1036,7 @@ fn phase1_append_after_dirty_stays_dirty_until_save() {
mgr.push(ChatMessage::user("b")); // 追加
assert!(mgr.needs_full_rewrite(), "dirty 后 push 不应清 dirty");
// 模拟 save(全量重写)成功后推进 + 清
mgr.advance_persisted_count(2);
mgr.advance_persisted_count(2, true);
assert!(!mgr.needs_full_rewrite());
}