优化: 子代理截断信号与缓存失效
- max_tokens 截断标记穿透到完成卡 - 缓存写后失效补测试锁定 - 派发参数卡头信息补全
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
//! `resolve_all` 按 [`MentionRef::kind_str`] 分发到对应 Resolver,单条 resolve 失败
|
||||
//! 收集进 errors 列表不阻断整批注入(返回 `(Vec<Augmentation>, Vec<ResolveError>)`)。
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
use df_types::augmentation::{Augmentation, MentionRef, ResolveError};
|
||||
@@ -45,6 +45,10 @@ impl ResolverRegistry {
|
||||
/// - 失败(Resolver 返 Err / kind 无对应 Resolver)→ push 进 `errors`,**不中断**
|
||||
/// 后续 mention(整批注入最大化保留可用上下文)。
|
||||
///
|
||||
/// 同一引用去重(先到先得):`/技能` 前导与 @mention span 可指向同一技能,按
|
||||
/// `kind + ref_id`(skill 的 ref_id 即 name)跳过重复,避免同一正文重复 resolve
|
||||
/// 并在 prompt 注入两份。首现位置决定保留哪条,顺序不变。
|
||||
///
|
||||
/// 返回 `(成功列表, 失败列表)`,调用方据 errors 数决定是否提示用户某 mention 跳过。
|
||||
pub async fn resolve_all(
|
||||
&self,
|
||||
@@ -53,7 +57,12 @@ impl ResolverRegistry {
|
||||
) -> (Vec<Augmentation>, Vec<ResolveError>) {
|
||||
let mut augs = Vec::with_capacity(refs.len());
|
||||
let mut errors = Vec::new();
|
||||
let mut seen: HashSet<String> = HashSet::with_capacity(refs.len());
|
||||
for rf in refs {
|
||||
// 去重键 = kind + ref_id(id 或 skill 的 name):同键 mention 只 resolve 首条
|
||||
if !seen.insert(format!("{}:{}", rf.kind_str(), rf.ref_id())) {
|
||||
continue;
|
||||
}
|
||||
let kind = rf.kind_str();
|
||||
match self.resolvers.get(kind) {
|
||||
Some(resolver) => match resolver.resolve(rf, loc).await {
|
||||
@@ -139,4 +148,48 @@ mod tests {
|
||||
assert!(augs.is_empty(), "两条 mention kind 均≠test 应全 KindMismatch");
|
||||
assert_eq!(errors.len(), 2, "两条失败都应收集");
|
||||
}
|
||||
|
||||
/// 测试用 Resolver:kind 固定 "skill",resolve 固定返 Skill(模拟成功注入)。
|
||||
struct OkSkillResolver;
|
||||
#[async_trait]
|
||||
impl MentionResolver for OkSkillResolver {
|
||||
fn kind(&self) -> &'static str {
|
||||
"skill"
|
||||
}
|
||||
async fn resolve(
|
||||
&self,
|
||||
rf: &MentionRef,
|
||||
_loc: ProviderLocality,
|
||||
) -> Result<Augmentation, ResolveError> {
|
||||
let name = match rf {
|
||||
MentionRef::Skill { name } => name.clone(),
|
||||
_ => String::new(),
|
||||
};
|
||||
Ok(Augmentation::Skill {
|
||||
name,
|
||||
source: "test".into(),
|
||||
body: "BODY".into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_all_dedups_same_ref_first_wins() {
|
||||
// 去重(先到先得):/技能 前导 + @mention span 指向同一技能时只 resolve/注入一份;
|
||||
// 不同技能不受影响;首现位置决定保留顺序。
|
||||
let mut reg = ResolverRegistry::new();
|
||||
reg.register(Arc::new(OkSkillResolver));
|
||||
let refs = vec![
|
||||
MentionRef::Skill { name: "review".into() },
|
||||
MentionRef::Skill { name: "review".into() },
|
||||
MentionRef::Skill { name: "idea".into() },
|
||||
];
|
||||
let (augs, errors) = reg.resolve_all(&refs, ProviderLocality::Local).await;
|
||||
assert!(errors.is_empty());
|
||||
assert_eq!(augs.len(), 2, "同名技能两条应去重为一份");
|
||||
match &augs[0] {
|
||||
Augmentation::Skill { name, .. } => assert_eq!(name, "review", "先到先得保留首条"),
|
||||
other => panic!("expected Skill, got {:?}", other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user