fix: topic_id 穿透到 ToolContext,统一 todo scope_key 计算
- AgentBuildRequest 加 topic_id 字段 - Session.create_agent 传入 current_topic - ToolContext.topic_id 不再硬编码 None - 删除已废弃的 intercept_todo_write_results - 工具/emitter/handler 三处 scope_key 计算全部统一 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
ec5ddf644a
commit
eef0d24dcd
@ -24,6 +24,8 @@ pub(crate) struct AgentBuildRequest<'a> {
|
||||
pub(crate) sender_id: Option<&'a str>,
|
||||
pub(crate) message_id: Option<&'a str>,
|
||||
pub(crate) provider_config: LLMProviderConfig,
|
||||
/// 当前话题 ID(可选):用于 todo 等按 topic 隔离的工具
|
||||
pub(crate) topic_id: Option<String>,
|
||||
/// 取消信号接收端(可选):Agent 在每次迭代时检查是否被取消
|
||||
pub(crate) cancel_token: Option<tokio::sync::watch::Receiver<()>>,
|
||||
}
|
||||
@ -73,7 +75,7 @@ impl AgentFactory {
|
||||
sender_id: request.sender_id.map(str::to_string),
|
||||
chat_id: Some(tool_chat_id.to_string()),
|
||||
session_id: Some(session_id),
|
||||
topic_id: None,
|
||||
topic_id: request.topic_id.clone(),
|
||||
message_id: request.message_id.map(str::to_string),
|
||||
message_seq: None,
|
||||
subagent_description: None,
|
||||
|
||||
@ -445,89 +445,6 @@ impl Session {
|
||||
let _ = self.user_tx.send(msg).await;
|
||||
}
|
||||
|
||||
/// 扫描 agent 结果中的 todo_write 工具消息,
|
||||
/// 提取 todos 并做持久化 + 前端推送(同步版本)。
|
||||
pub(crate) fn intercept_todo_write_results(
|
||||
&self,
|
||||
emitted_messages: &[ChatMessage],
|
||||
chat_id: &str,
|
||||
) {
|
||||
for msg in emitted_messages {
|
||||
if msg.role != "tool" {
|
||||
continue;
|
||||
}
|
||||
if msg.tool_name.as_deref() != Some("todo_write") {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析工具返回的 JSON
|
||||
let parsed: serde_json::Value = match serde_json::from_str(&msg.content) {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let Some(todos_array) = parsed
|
||||
.get("current_todos")
|
||||
.and_then(|v| v.as_array())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// 计算持久化所需的 key
|
||||
let session_id = crate::storage::persistent_session_id(&self.channel_name, chat_id);
|
||||
let topic_id = self.current_topic(chat_id);
|
||||
let scope_key = topic_id.map(|t| t.to_string()).unwrap_or_else(|| session_id.clone());
|
||||
|
||||
// 转换为 TodoRecord 并持久化
|
||||
let records: Vec<crate::storage::TodoRecord> = todos_array
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
Some(crate::storage::TodoRecord {
|
||||
id: item.get("id")?.as_str()?.to_string(),
|
||||
scope_key: scope_key.clone(),
|
||||
session_id: session_id.clone(),
|
||||
topic_id: topic_id.map(|t| t.to_string()),
|
||||
content: item.get("content")?.as_str()?.to_string(),
|
||||
status: item.get("status")?.as_str()?.to_string(),
|
||||
priority: item.get("priority")?.as_str()?.to_string(),
|
||||
created_at: item.get("created_at")?.as_i64()?,
|
||||
updated_at: item.get("updated_at")?.as_i64()?,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
// 持久化到 SQLite
|
||||
tracing::info!(
|
||||
scope_key = %scope_key,
|
||||
todo_count = records.len(),
|
||||
"intercept_todo_write_results: persisting todos"
|
||||
);
|
||||
if let Err(e) = self.store.replace_todos(&scope_key, &records) {
|
||||
tracing::warn!(error = %e, scope_key = %scope_key, "Failed to persist todo list");
|
||||
}
|
||||
|
||||
// 推送到前端(使用 try_send 避免异步)
|
||||
let summaries: Vec<crate::protocol::TodoItemSummary> = records
|
||||
.iter()
|
||||
.map(|r| crate::protocol::TodoItemSummary {
|
||||
id: r.id.clone(),
|
||||
content: r.content.clone(),
|
||||
status: r.status.clone(),
|
||||
priority: r.priority.clone(),
|
||||
created_at: r.created_at,
|
||||
updated_at: r.updated_at,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let _ = self.user_tx.try_send(crate::protocol::WsOutbound::TodoList {
|
||||
todos: summaries,
|
||||
scope_key: scope_key.clone(),
|
||||
});
|
||||
|
||||
break; // 只处理第一个成功的 todo_write
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取 provider_config 引用
|
||||
pub fn provider_config(&self) -> &LLMProviderConfig {
|
||||
&self.provider_config
|
||||
@ -604,9 +521,11 @@ impl Session {
|
||||
) -> Result<AgentLoop, AgentError> {
|
||||
// 消费 pending 的取消信号接收端(如果存在)
|
||||
let cancel_token = self.pending_cancel_tokens.remove(session_chat_id);
|
||||
let topic_id = self.current_topic(session_chat_id).map(|s| s.to_string());
|
||||
self.agent_factory.create(AgentBuildRequest {
|
||||
channel_name: &self.channel_name,
|
||||
session_chat_id,
|
||||
topic_id,
|
||||
notification_chat_id,
|
||||
sender_id,
|
||||
message_id,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user