修复: namespace 引用按真实体量入账估算
- ref 尾段 ?len= 携带原文字节数, 估算器解析入账(原 ~15 token 低估致预算/裁剪/压缩三门失效) - 走查五线复核通过: 入口唯一/展开在发送链/估算无旁路/压缩扣减闭环/子代理同效 - 归一方案否决留档: 近度豁免失效等四破坏, 重consider 条件三条 - 已知取舍: 字节直入字符口径中文 3x 高估, 方向安全
This commit is contained in:
@@ -60,9 +60,20 @@ pub fn is_namespace_ref(content: &str) -> bool {
|
||||
/// 从引用路径中提取内部 key
|
||||
pub fn parse_namespace_key(full_path: &str) -> Option<&str> {
|
||||
full_path.strip_prefix(NAMESPACE_REF_PREFIX)
|
||||
// 939①: 先剥 ?len= 长度尾段(估算入账用),否则尾段并入 key 致全线查空
|
||||
.and_then(|s| s.split("?len=").next())
|
||||
.and_then(|s| s.split('/').nth(1))
|
||||
}
|
||||
|
||||
/// 从引用路径提取原文字节数(939①)。带 `?len=N` 尾段的引用返回 Some(N);
|
||||
/// 旧格式/放弃入库的无尾段引用返回 None(调用方按字面字符保守估算)。
|
||||
pub fn parse_ref_content_len(full_path: &str) -> Option<usize> {
|
||||
full_path.rsplit("?len=")
|
||||
.next()
|
||||
.and_then(|s| s.parse::<usize>().ok())
|
||||
.filter(|_| full_path.contains("?len="))
|
||||
}
|
||||
|
||||
/// namespace 条目
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NamespaceEntry {
|
||||
@@ -138,6 +149,12 @@ impl NamespaceStore {
|
||||
|
||||
fn store_inner(&mut self, tool_name: &str, content: &str, owner: Option<&str>, pinned: bool) -> String {
|
||||
let key = simple_hash(content);
|
||||
// 939① 估算入账:ref 尾段携带原文字节数,TokenEstimator 解析即按真实体量入账
|
||||
// (旧口径 ref 仅 ~30 字符被记 ~15 token,预算/裁剪/压缩三重门对其失效)。
|
||||
// parse_namespace_key 取 nth(1) 不受尾段影响,展开/淘汰提示零改动。
|
||||
// 放弃入库(超容量/淘汰后仍放不下)返回**不带长度**的 path:原文不在池,展开点
|
||||
// 落占位提示,预算按轻量字符计——已淘汰内容不应继续虚占预算。
|
||||
let path_with_len = format!("{}{}/{}?len={}", NAMESPACE_REF_PREFIX, tool_name, key, content.len());
|
||||
let path = format!("{}{}/{}", NAMESPACE_REF_PREFIX, tool_name, key);
|
||||
|
||||
// 已存在 → 刷新 access_count,不重复占用空间;若本次要求 pin,升级既有条目
|
||||
@@ -148,7 +165,7 @@ impl NamespaceStore {
|
||||
entry.pinned = true;
|
||||
}
|
||||
}
|
||||
return path;
|
||||
return path_with_len;
|
||||
}
|
||||
|
||||
let new_bytes = content.len();
|
||||
@@ -190,7 +207,7 @@ impl NamespaceStore {
|
||||
|
||||
self.current_bytes += new_bytes;
|
||||
self.entries.insert(key, entry);
|
||||
path
|
||||
path_with_len
|
||||
}
|
||||
|
||||
/// pin/取消 pin(事后)。pin 总量超 50% 容量时本调用拒绝并返回 false(防失控,拍板 C1)。
|
||||
@@ -532,4 +549,18 @@ mod tests {
|
||||
assert!(ns.contains(&p_old) == false || ns.pinned_bytes() <= 50,
|
||||
"降级机制应使 pin 总量可被压缩");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_carries_content_len_for_budget() {
|
||||
// 939①: 成功入库的 ref 带 ?len= 原文字节数,parse 往返一致;
|
||||
// parse_namespace_key 不受尾段影响(展开点零改动)
|
||||
let mut ns = NamespaceStore::new(100_000);
|
||||
let content = "x".repeat(5000);
|
||||
let path = ns.store("read_file", &content);
|
||||
assert!(path.contains(&format!("?len={}", content.len())), "ref 应带 len: {path}");
|
||||
assert_eq!(parse_ref_content_len(&path), Some(content.len()));
|
||||
assert_eq!(parse_namespace_key(&path), parse_namespace_key(&format!("namespace://read_file/{}", crate::namespace_store::simple_hash(&content))));
|
||||
// 旧格式/无尾段 → None(调用方保守按字面计)
|
||||
assert_eq!(parse_ref_content_len("namespace://read_file/abc"), None);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user