fix(sub-agent): 修复传统模式 terminate 后子智能体仍继续调用工具的问题

- _call_model 中硬取消时直接抛出 CancelledError,避免返回半成品 tool_calls
- 软停止时仍优雅 break,后续由 _run_loop 丢弃半成品并进入 idle
- 保持 _mark_task_done 不覆盖 terminated 状态
- 侧边栏当前对话运行态标记修复保留
This commit is contained in:
JOJO 2026-07-14 20:37:57 +08:00
parent 0aa37b83ee
commit 571ed6c81f
4 changed files with 35 additions and 4 deletions

View File

@ -301,6 +301,17 @@ class SubAgentStateMixin:
task = self.tasks.get(task_id) task = self.tasks.get(task_id)
if not task: if not task:
return return
# 如果 terminate_sub_agent 已把任务标为 terminated下渗异步任务自然结束时
# 不要被“已完成/fulfilled覆盖为failed”值覆盖否则 task 被碰成 failed 但实际上
# 用户手动终止已发生状态语义错误且“successful=false”。
existing_status = task.get("status")
if existing_status in TERMINAL_STATUSES.union({"terminated"}):
ma_debug(
"mark_task_done_skip_already_terminal",
task_id=task_id,
existing_status=existing_status,
)
return
status = "completed" if success else "failed" status = "completed" if success else "failed"
ma_debug( ma_debug(
"mark_task_done", "mark_task_done",

View File

@ -540,8 +540,12 @@ class SubAgentTask:
usage = None usage = None
async for chunk in client.chat(self.messages, tools=tools, stream=True): async for chunk in client.chat(self.messages, tools=tools, stream=True):
if self._cancelled: if self._soft_stop:
# 软停止:优雅中断当前模型调用,后续由 _run_loop 丢弃半成品并进入 idle
break break
if self._cancelled:
# 硬取消:立即抛出 CancelledError确保不会返回半成品的 tool_calls 继续执行
raise asyncio.CancelledError()
if chunk.get("error"): if chunk.get("error"):
raise RuntimeError(f"API 调用失败: {chunk.get('error')}") raise RuntimeError(f"API 调用失败: {chunk.get('error')}")
choice = (chunk.get("choices") or [{}])[0] choice = (chunk.get("choices") or [{}])[0]

View File

@ -63,6 +63,8 @@
:icon-style="iconStyle" :icon-style="iconStyle"
:format-time="formatTime" :format-time="formatTime"
:running-tasks="runningWorkspaceTasks" :running-tasks="runningWorkspaceTasks"
:current-task-in-progress="taskInProgress"
:current-conversation-id="currentConversationId"
:current-workspace-id="currentHostWorkspaceId" :current-workspace-id="currentHostWorkspaceId"
:display-mode="chatDisplayMode" :display-mode="chatDisplayMode"
:display-mode-disabled="displayModeSwitchDisabled" :display-mode-disabled="displayModeSwitchDisabled"
@ -765,6 +767,8 @@
:icon-style="iconStyle" :icon-style="iconStyle"
:format-time="formatTime" :format-time="formatTime"
:running-tasks="runningWorkspaceTasks" :running-tasks="runningWorkspaceTasks"
:current-task-in-progress="taskInProgress"
:current-conversation-id="currentConversationId"
:current-workspace-id="currentHostWorkspaceId" :current-workspace-id="currentHostWorkspaceId"
:display-mode="chatDisplayMode" :display-mode="chatDisplayMode"
:display-mode-disabled="displayModeSwitchDisabled" :display-mode-disabled="displayModeSwitchDisabled"

View File

@ -547,6 +547,9 @@ const props = withDefaults(
hostWorkspaceEnabled?: boolean; hostWorkspaceEnabled?: boolean;
groupByWorkspace?: boolean; groupByWorkspace?: boolean;
versioningHostMode?: boolean; versioningHostMode?: boolean;
/** 当前会话是否仍在运行(用于标记当前正在查看的对话也显示运行中转圈) */
currentTaskInProgress?: boolean;
currentConversationId?: string;
}>(), }>(),
{ {
showCollapseButton: true, showCollapseButton: true,
@ -559,7 +562,9 @@ const props = withDefaults(
workspaceKind: 'workspace', workspaceKind: 'workspace',
hostWorkspaceEnabled: false, hostWorkspaceEnabled: false,
groupByWorkspace: false, groupByWorkspace: false,
versioningHostMode: false versioningHostMode: false,
currentTaskInProgress: false,
currentConversationId: ''
} }
); );
@ -890,8 +895,15 @@ const completedConversationIds = computed(
.filter(Boolean) .filter(Boolean)
) )
); );
const isConversationActive = (conversationId: string) => const isConversationActive = (conversationId: string) => {
activeConversationIds.value.has(String(conversationId || '')); if (activeConversationIds.value.has(String(conversationId || ''))) return true;
// runningTasks
// currentTaskInProgress
if (props.currentTaskInProgress && String(conversationId || '') === String(props.currentConversationId || '')) {
return true;
}
return false;
};
const isConversationCompleted = (conversationId: string) => const isConversationCompleted = (conversationId: string) =>
completedConversationIds.value.has(String(conversationId || '')); completedConversationIds.value.has(String(conversationId || ''));
const isWorkspaceRunning = (workspaceId: string) => const isWorkspaceRunning = (workspaceId: string) =>