后端核心实现: - 新增 wait_coordinator:释放/重获取 serial_lock 的 in-tool waiting 模式,替代旧的 break-exit 方案 - 新增 wait_for_subagents 工具:select! 等待子代理完成/用户消息/超时,支持 try_drain 批量消费 - task 工具异步 spawn 路径:CancellationToken 注册表 + RAII guard + Semaphore 并发限流 - process exit 安全检查:pending 子代理存在时抑制 ExecutionCompleted - 崩溃恢复:启动时标记 running->interrupted,history 加载时对账占位 /stop 取消流程修复: - wait_coordinator select! 添加 cancel 分支,完整清理状态 - ToolContext 注入 cancel_rx(watch::Receiver clone) - agent_loop 工具执行 select! 对 wait 工具跳过竞速,防止 drop coordinator 清理逻辑 - 子代理完成状态通过 execution_completed metadata 传播 存储层: - pending_subagents 表 + 条件 UPDATE - mark_all_running_as_interrupted 崩溃恢复
128 lines
4.6 KiB
Rust
128 lines
4.6 KiB
Rust
use async_trait::async_trait;
|
||
use std::sync::Arc;
|
||
|
||
use crate::command::Command;
|
||
use crate::command::context::CommandContext;
|
||
use crate::command::handler::{CommandHandler, CommandMetadata};
|
||
use crate::command::response::{CommandError, CommandResponse, MessageKind};
|
||
use crate::gateway::cancel_manager::CancelManager;
|
||
use crate::gateway::session::SessionManager;
|
||
use crate::tools::SubAgentRuntime;
|
||
|
||
/// 处理 StopExecution 命令:按话题取消当前正在执行的 Agent。
|
||
///
|
||
/// 取消传播:同时取消该 topic 下所有正在运行的异步子代理(通过 CancellationToken)。
|
||
pub struct StopExecutionCommandHandler {
|
||
cancel_manager: CancelManager,
|
||
session_manager: SessionManager,
|
||
subagent_executor: Option<Arc<dyn SubAgentRuntime>>,
|
||
}
|
||
|
||
impl StopExecutionCommandHandler {
|
||
pub fn new(
|
||
cancel_manager: CancelManager,
|
||
session_manager: SessionManager,
|
||
subagent_executor: Option<Arc<dyn SubAgentRuntime>>,
|
||
) -> Self {
|
||
Self {
|
||
cancel_manager,
|
||
session_manager,
|
||
subagent_executor,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[async_trait]
|
||
impl CommandHandler for StopExecutionCommandHandler {
|
||
fn can_handle(&self, cmd: &Command) -> bool {
|
||
matches!(cmd, Command::StopExecution)
|
||
}
|
||
|
||
fn metadata(&self) -> Option<CommandMetadata> {
|
||
Some(CommandMetadata {
|
||
name: "stop",
|
||
description: "停止当前话题正在执行的 Agent",
|
||
usage: "/stop",
|
||
})
|
||
}
|
||
|
||
async fn handle(
|
||
&self,
|
||
_cmd: Command,
|
||
ctx: CommandContext,
|
||
) -> Result<CommandResponse, CommandError> {
|
||
// 优先使用 ctx.topic_id,如果没有则从 session_manager 获取真实的 topic_id
|
||
let topic_id = match ctx.topic_id.as_deref() {
|
||
Some(id) => {
|
||
tracing::info!(
|
||
channel = %ctx.channel_name,
|
||
chat_id = ?ctx.chat_id,
|
||
topic_id = %id,
|
||
source = "ctx",
|
||
"Stop execution command received"
|
||
);
|
||
id.to_string()
|
||
}
|
||
None => {
|
||
// 从 SessionManager 获取真实的 current topic
|
||
let chat_id = ctx.chat_id.as_deref().unwrap_or("");
|
||
match self
|
||
.session_manager
|
||
.get_current_topic(&ctx.channel_name, chat_id)
|
||
.await
|
||
{
|
||
Ok(Some(id)) => {
|
||
tracing::info!(
|
||
channel = %ctx.channel_name,
|
||
chat_id = %chat_id,
|
||
topic_id = %id,
|
||
source = "session_manager",
|
||
"Stop execution command received (resolved from session)"
|
||
);
|
||
id
|
||
}
|
||
Ok(None) => {
|
||
return Ok(CommandResponse::success(ctx.request_id).with_message(
|
||
MessageKind::Notification,
|
||
"当前没有活跃的话题,无法停止",
|
||
));
|
||
}
|
||
Err(e) => {
|
||
return Ok(CommandResponse::error(
|
||
ctx.request_id,
|
||
CommandError::new("QUERY_TOPIC_ERROR", e.to_string()),
|
||
));
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
let cancelled = self.cancel_manager.cancel_by_topic(&topic_id).await;
|
||
|
||
// 取消传播:同时取消该 topic 下所有正在运行的异步子代理
|
||
let cancelled_subagents = if let Some(ref executor) = self.subagent_executor {
|
||
executor.cancel_pending_for_topic(&topic_id).await
|
||
} else {
|
||
0
|
||
};
|
||
|
||
if cancelled || cancelled_subagents > 0 {
|
||
let msg = if cancelled && cancelled_subagents > 0 {
|
||
format!(
|
||
"正在停止当前任务及 {} 个后台子代理...",
|
||
cancelled_subagents
|
||
)
|
||
} else if cancelled {
|
||
"正在停止当前任务...".to_string()
|
||
} else {
|
||
format!("正在停止 {} 个后台子代理...", cancelled_subagents)
|
||
};
|
||
Ok(CommandResponse::success(ctx.request_id)
|
||
.with_message(MessageKind::Notification, msg))
|
||
} else {
|
||
Ok(CommandResponse::success(ctx.request_id)
|
||
.with_message(MessageKind::Notification, "当前没有正在执行的任务"))
|
||
}
|
||
}
|
||
}
|