重构: 巨函数拆分 + 清理历史标记注释 + custom_prompt/停止按钮/tunnel 改进

This commit is contained in:
lxy
2026-07-31 21:36:12 +08:00
parent 365af554da
commit bd9031d35d
73 changed files with 1421 additions and 1064 deletions
+10 -10
View File
@@ -47,7 +47,7 @@ impl Default for TokenEstimator {
impl TokenEstimator {
/// 估算单条消息的 token 数(保守估计)
///
/// F-260614-05 多模态回归修正:`msg.parts` 中的 Image.base64 与 Text.text 同样计入预算。
/// 多模态回归修正:`msg.parts` 中的 Image.base64 与 Text.text 同样计入预算。
/// 此前只算 `content`,含图消息的大段 base64(可达 25 万 tokens)被完全忽略,致
/// `history_tokens` 严重低估 → build_for_request 误判未超预算 → provider 超限 400/500。
/// 这里把 parts 的文本/base64 按同一 chars_ratio 粗估累加(base64 视为密集字符,0.35 偏保守)。
@@ -58,7 +58,7 @@ impl TokenEstimator {
match p {
crate::provider::ContentPart::Text { text } => char_count += text.chars().count(),
crate::provider::ContentPart::Image { base64, url, .. } => {
// CR-260618-11#2:0.35 按 base64 字节数粗估,显著高于厂商实际(OpenAI 按像素非字节)。
// 0.35 按 base64 字节数粗估,显著高于厂商实际(OpenAI 按像素非字节)。
// 偏保守致含图消息 token 高估、过度裁剪;降值(如 0.10~0.15)需独立评估裁剪边界,本次不改值仅标注。
// base64 优先(多模态主载荷),url 次之;url 模式无字节,仅按 URL 长度估
if let Some(b) = base64 {
@@ -145,7 +145,7 @@ pub enum MessageGroup {
/// 带有 token 缓存和分组信息的消息条目
///
/// 字段 `pub`:供阶段2 IPC 经 `ContextManager::messages_mut()` 拿到可变切片后,
/// 字段 `pub`:供 IPC 经 `ContextManager::messages_mut()` 拿到可变切片后,
/// 直接改 `message.status` / 读 `token_count` 做 token 重算(Mutex 单线程访问,
/// 同 crate 内安全)。结构体本身也 `pub`(返回类型对外可见)。
pub struct TrackedMessage {
@@ -408,7 +408,7 @@ pub fn extract_key_info(content: &str, tool_name: &str) -> String {
*field = serde_json::Value::String(out.join("\n"));
truncated = true;
} else if s.chars().count() > TOOL_RESULT_JSON_STR_FIELD_MAX {
// BUG-260628-01:单行/少行大字符串绕过行级截断(实测 53/94 次零效果)。
// 单行/少行大字符串绕过行级截断(实测 53/94 次零效果)。
// 按字符数截断保留头尾,保证压缩至少生效。
let head: String = s.chars().take(TOOL_RESULT_JSON_STR_FIELD_MAX / 2).collect();
let tail: String = s.chars().skip(s.chars().count().saturating_sub(TOOL_RESULT_JSON_STR_FIELD_MAX / 2)).collect();
@@ -439,7 +439,7 @@ pub fn extract_key_info(content: &str, tool_name: &str) -> String {
let total = lines.len();
let kept_boundary = TOOL_RESULT_HEAD_LINES + TOOL_RESULT_TAIL_LINES;
if total <= kept_boundary {
// BUG-260628-01:行数少但内容超大的情况(单行 50KB),行级截断无效。
// 行数少但内容超大的情况(单行 50KB),行级截断无效。
// 按字符数截断保证压缩至少生效。
let char_count = content.chars().count();
if char_count > TOOL_RESULT_CHAR_LIMIT {
@@ -524,7 +524,7 @@ fn is_error_line(line: &str) -> bool {
/// 淘汰单元:连续消息范围 [..end) + token 总和
///
/// `pub` 供 `build_eviction_units` 的返回类型对外可见(阶段2/3 调用方读 `end` / `token_sum`)。
/// `pub` 供 `build_eviction_units` 的返回类型对外可见(调用方读 `end` / `token_sum`)。
pub struct EvictionUnit {
pub end: usize,
pub token_sum: u32,
@@ -541,7 +541,7 @@ pub const PROTECT_COUNT: usize = 6;
/// 此类 id 必然无匹配 tool_result,是历史中毒的标志,sanitize 时据此剔除畸形三元组。
pub const TOOL_MISSING_PREFIX: &str = "tool_missing_";
/// 阶段2(path_auth 审批链重构):占位配对完整性(解 400 orphan)常量开关。
/// 占位配对完整性(解 400 orphan)常量开关。
///
/// 根因:审批挂起占位 tool_result(内容为 audit/cache.rs:PENDING_APPROVAL_PLACEHOLDER)
/// 与其 tool_call 头经 sanitize/compress 裁剪后丢配对头 → orphan tool_result(无头)→
@@ -1118,7 +1118,7 @@ mod tests {
#[test]
fn extract_key_info_single_huge_line_no_newline_compressed() {
// BUG-260628-01:单行超大内容(50KB)原本逃逸压缩,现按字符数截断保留头尾。
// 单行超大内容(50KB)原本逃逸压缩,现按字符数截断保留头尾。
let content = "x".repeat(50_000);
let result = extract_key_info(&content, "read_file");
assert!(result.len() < content.len(), "单行超长应压缩: {} >= {}", result.len(), content.len());
@@ -1130,7 +1130,7 @@ mod tests {
#[test]
fn extract_key_info_json_huge_string_field_truncated() {
// BUG-260628-01:JSON 对象中大字符串字段(单行少行)逃逸压缩。
// JSON 对象中大字符串字段(单行少行)逃逸压缩。
// 如 `{"path":"src/main.rs","content":"单行超大文本..."}`。
let large = "z".repeat(10_000);
let content = format!("{{\"path\":\"src/main.rs\",\"content\":\"{}\"}}", large);
@@ -1243,7 +1243,7 @@ mod tests {
assert!(toks.contains(&"now".to_string()));
}
// ── 阶段2 占位配对完整性:is_pending_placeholder / extract_pending_tc_id ──
// ── 占位配对完整性:is_pending_placeholder / extract_pending_tc_id ──
#[test]
fn is_pending_placeholder_new_with_marker() {