feat:用户修改步骤后重新执行实现

This commit is contained in:
liailing1026
2026-01-26 15:06:17 +08:00
parent b287867069
commit 641d70033d
7 changed files with 581 additions and 24 deletions

View File

@@ -428,7 +428,7 @@ def executePlan_streaming_dynamic(
if execution_id:
# 动态模式:循环获取下一个步骤
# 等待新步骤的最大次数(避免无限等待)
max_empty_wait_cycles = 60 # 最多等待60次每次等待1秒
max_empty_wait_cycles = 5 # 最多等待60次每次等待1秒
empty_wait_count = 0
while True:

View File

@@ -18,6 +18,7 @@ from AgentCoord.PlanEngine.AgentSelectModify import (
import os
import yaml
import argparse
from typing import List, Dict
# initialize global variables
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
@@ -39,6 +40,40 @@ app.config['SECRET_KEY'] = 'agentcoord-secret-key'
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')
def truncate_rehearsal_log(RehearsalLog: List, restart_from_step_index: int) -> List:
"""
截断 RehearsalLog只保留指定索引之前的步骤结果
Args:
RehearsalLog: 原始日志列表
restart_from_step_index: 重新执行的起始步骤索引例如1 表示保留步骤0从步骤1重新执行
Returns:
截断后的 RehearsalLog
示例:
restart_from_step_index = 1
RehearsalLog = [step0, object0, step1, object1, step2, object2]
返回 = [step0, object0] # 只保留步骤0的结果
"""
truncated_log = []
step_count = 0
for logNode in RehearsalLog:
if logNode.get("LogNodeType") == "step":
# 只保留 restart_from_step_index 之前的步骤
if step_count < restart_from_step_index:
truncated_log.append(logNode)
step_count += 1
elif logNode.get("LogNodeType") == "object":
# object 节点:如果对应的 step 在保留范围内,保留它
# 策略:保留所有在截断点之前的 object
if step_count <= restart_from_step_index:
truncated_log.append(logNode)
return truncated_log
@app.route("/fill_stepTask_TaskProcess", methods=["post"])
def Handle_fill_stepTask_TaskProcess():
incoming_data = request.get_json()
@@ -412,7 +447,7 @@ def handle_ping():
def handle_execute_plan_optimized_ws(data):
"""
WebSocket版本优化版流式执行计划
支持步骤级流式 + 动作级智能并行 + 动态追加步骤
支持步骤级流式 + 动作级智能并行 + 动态追加步骤 + 从指定步骤重新执行
请求格式:
{
@@ -422,7 +457,8 @@ def handle_execute_plan_optimized_ws(data):
"plan": {...},
"num_StepToRun": null,
"RehearsalLog": [],
"enable_dynamic": true # 是否启用动态追加步骤
"enable_dynamic": true, # 是否启用动态追加步骤
"restart_from_step_index": 1 # 可选从指定步骤重新执行例如1表示从步骤2重新执行
}
}
"""
@@ -434,6 +470,13 @@ def handle_execute_plan_optimized_ws(data):
num_StepToRun = incoming_data.get("num_StepToRun")
RehearsalLog = incoming_data.get("RehearsalLog", [])
enable_dynamic = incoming_data.get("enable_dynamic", False)
restart_from_step_index = incoming_data.get("restart_from_step_index") # 新增:支持从指定步骤重新执行
# 如果指定了重新执行起始步骤,截断 RehearsalLog
if restart_from_step_index is not None:
print(f"🔄 从步骤 {restart_from_step_index + 1} 重新执行,正在截断 RehearsalLog...")
RehearsalLog = truncate_rehearsal_log(RehearsalLog, restart_from_step_index)
print(f"✅ RehearsalLog 已截断,保留 {sum(1 for node in RehearsalLog if node.get('LogNodeType') == 'step')} 个步骤的结果")
# 如果前端传入了execution_id使用前端的否则生成新的
execution_id = incoming_data.get("execution_id")
@@ -1650,6 +1693,66 @@ def handle_resume_execution(data):
})
@socketio.on('stop_execution')
def handle_stop_execution(data):
"""
WebSocket版本停止任务执行
请求格式:
{
"id": "request-id",
"action": "stop_execution",
"data": {
"goal": "任务描述"
}
}
"""
request_id = data.get('id')
incoming_data = data.get('data', {})
try:
from AgentCoord.RehearsalEngine_V2.execution_state import execution_state_manager
goal = incoming_data.get('goal', '')
# 检查当前执行的任务是否匹配
current_goal = execution_state_manager.get_goal()
if current_goal and current_goal != goal:
print(f"⚠️ 任务目标不匹配: 当前={current_goal}, 请求={goal}")
emit('response', {
'id': request_id,
'status': 'error',
'error': '任务目标不匹配'
})
return
# 调用执行状态管理器停止
success = execution_state_manager.stop_execution()
if success:
print(f"🛑 [DEBUG] 停止成功! 当前状态: {execution_state_manager.get_status().value}")
print(f"🛑 [DEBUG] should_stop: {execution_state_manager._should_stop}")
emit('response', {
'id': request_id,
'status': 'success',
'data': {"message": "已停止执行"}
})
else:
print(f"⚠️ [DEBUG] 停止失败,当前状态: {execution_state_manager.get_status().value}")
emit('response', {
'id': request_id,
'status': 'error',
'error': f'无法停止,当前状态: {execution_state_manager.get_status().value}'
})
except Exception as e:
print(f"❌ 停止执行失败: {str(e)}")
emit('response', {
'id': request_id,
'status': 'error',
'error': str(e)
})
if __name__ == "__main__":
parser = argparse.ArgumentParser(

View File

@@ -16,15 +16,16 @@ declare module 'vue' {
ElCard: typeof import('element-plus/es')['ElCard']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
Notification: typeof import('./src/components/Notification/Notification.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']

View File

@@ -180,15 +180,18 @@ class Api {
enableDynamic?: boolean,
onExecutionStarted?: (executionId: string) => void,
executionId?: string,
restartFromStepIndex?: number, // 新增:从指定步骤重新执行的索引
rehearsalLog?: any[], // 新增:传递截断后的 RehearsalLog
) => {
const useWs = useWebSocket !== undefined ? useWebSocket : this.useWebSocketDefault
const data = {
RehearsalLog: [],
RehearsalLog: rehearsalLog || [], // ✅ 使用传递的 RehearsalLog
num_StepToRun: null,
existingKeyObjects: existingKeyObjects || {},
enable_dynamic: enableDynamic || false,
execution_id: executionId || null,
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
plan: {
'Initial Input Object': plan['Initial Input Object'],
'General Goal': plan['General Goal'],

View File

@@ -2,7 +2,6 @@
import { computed, onUnmounted, ref, reactive, nextTick, watch, onMounted } from 'vue'
import { throttle } from 'lodash'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
import { ElMessageBox } from 'element-plus'
import AdditionalOutputCard from './AdditionalOutputCard.vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
@@ -42,6 +41,7 @@ const stepExecutionStatus = ref<Record<string, StepExecutionStatus>>({})
// 用于标记暂停时的"最后动作完成"状态
const isPausing = ref(false) // 正在请求暂停(等待当前动作完成)
const isRestarting = ref(false) // 正在重新执行(停止旧执行导致的错误不应显示)
// Check if step is ready to execute (has TaskProcess data)
const isStepReady = (step: IRawStepTask) => {
@@ -211,7 +211,23 @@ function handleSaveEdit(stepId: string, processId: string, value: string) {
if (step) {
const process = step.TaskProcess.find(p => p.ID === processId)
if (process) {
process.Description = value
// 保存旧值用于比较
const oldValue = process.Description
// 只有内容真正变化时才更新和记录修改
if (value !== oldValue) {
process.Description = value
// 记录修改过的步骤索引到 store
const stepIndex = collaborationProcess.value.findIndex(s => s.Id === stepId)
if (stepIndex >= 0) {
agentsStore.addModifiedStep(stepIndex)
console.log(
`📝 步骤 ${stepIndex + 1} (${step.StepName}) 的描述已被修改,将从该步骤重新执行`
)
}
} else {
console.log(` 步骤 ${step.StepName} 的描述未发生变化,不记录修改`)
}
}
}
editMap[key] = false
@@ -341,7 +357,6 @@ const executionProgress = ref({
const {
notifications,
progress: showProgress,
updateProgress,
updateProgressDetail,
success,
info,
@@ -457,9 +472,12 @@ async function executeNextReadyBatch() {
switch (event.type) {
case 'step_start':
// 使用全局步骤索引计算当前步骤
const globalStepIndex = collaborationProcess.value.findIndex(s => s.StepName === event.step_name)
const globalStepIndex = collaborationProcess.value.findIndex(
s => s.StepName === event.step_name
)
executionProgress.value = {
currentStep: globalStepIndex >= 0 ? globalStepIndex + 1 : (event.step_index || 0) + 1,
currentStep:
globalStepIndex >= 0 ? globalStepIndex + 1 : (event.step_index || 0) + 1,
totalSteps: collaborationProcess.value.length,
currentAction: 0,
totalActions: 0,
@@ -495,8 +513,13 @@ async function executeNextReadyBatch() {
: ''
// 使用全局步骤索引
const globalStepIndexForAction = collaborationProcess.value.findIndex(s => s.StepName === event.step_name)
const stepIndexForAction = globalStepIndexForAction >= 0 ? globalStepIndexForAction + 1 : (event.step_index || 0) + 1
const globalStepIndexForAction = collaborationProcess.value.findIndex(
s => s.StepName === event.step_name
)
const stepIndexForAction =
globalStepIndexForAction >= 0
? globalStepIndexForAction + 1
: (event.step_index || 0) + 1
const totalStepsValue = collaborationProcess.value.length
executionProgress.value = {
@@ -594,6 +617,13 @@ async function executeNextReadyBatch() {
const errorMessage = event.message || event.error || '未知错误'
console.error('执行错误:', errorMessage)
// 如果正在重新执行,不显示错误通知(因为旧执行的步骤中断是预期的)
if (isRestarting.value) {
console.log(' 正在重新执行,忽略旧执行的中断错误')
resolve() // 正常完成,不显示错误
return
}
// 关闭进度通知并显示错误通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
@@ -614,6 +644,13 @@ async function executeNextReadyBatch() {
(err: Error) => {
console.error('流式执行错误:', err)
// 如果正在重新执行,不显示错误通知(因为旧执行的步骤中断是预期的)
if (isRestarting.value) {
console.log(' 正在重新执行,忽略旧执行的中断错误')
resolve() // 正常完成,不显示错误
return
}
// 关闭进度通知并显示错误通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
@@ -695,7 +732,25 @@ function removeNotification(id: string) {
// Pause/Resume handler
async function handlePauseResume() {
if (isPaused.value) {
// Resume execution - 正常恢复执行
// Resume execution - 检查是否有修改的步骤
if (agentsStore.hasModifiedSteps()) {
// 有修改的步骤,必须从最早修改的步骤重新执行
// 因为步骤之间是串行依赖的修改了步骤2会导致步骤2-6都需要重新执行
const earliestModifiedIndex = Math.min(...agentsStore.modifiedSteps)
const modifiedStepName =
collaborationProcess.value[earliestModifiedIndex]?.StepName ||
`步骤${earliestModifiedIndex + 1}`
console.log(`🔄 从步骤 ${earliestModifiedIndex + 1} (${modifiedStepName}) 重新执行`)
success('开始重新执行', `检测到步骤修改,从步骤 ${earliestModifiedIndex + 1} 重新执行`)
// 直接调用重新执行函数
await restartFromStep(earliestModifiedIndex)
return
}
// 正常恢复执行(没有修改)
try {
if (websocket.connected) {
await websocket.send('resume_execution', {
@@ -739,6 +794,371 @@ async function handlePauseResume() {
}
}
// ==================== 重新执行功能 ====================
/**
* 从RehearsalLog中提取KeyObjects
* @param rehearsalLog RehearsalLog数组
* @returns KeyObjects字典
*/
function buildKeyObjectsFromLog(rehearsalLog: any[]): Record<string, any> {
const keyObjects: Record<string, any> = {}
for (const logNode of rehearsalLog) {
if (logNode.LogNodeType === 'object' && logNode.content) {
keyObjects[logNode.NodeId] = logNode.content
}
}
return keyObjects
}
/**
* 构建截断后的 RehearsalLog使用步骤名称匹配
* @param restartFromStepIndex 重新执行的起始步骤索引例如1 表示从步骤2重新执行
* @returns 截断后的 RehearsalLog
*/
function buildTruncatedRehearsalLog(restartFromStepIndex: number) {
const steps = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
const truncatedLog: any[] = []
for (const logNode of agentsStore.executePlan) {
if (logNode.LogNodeType === 'step') {
const stepIndex = steps.findIndex((s: any) => s.StepName === logNode.NodeId)
if (stepIndex >= 0 && stepIndex < restartFromStepIndex) {
truncatedLog.push(logNode)
}
} else if (logNode.LogNodeType === 'object') {
const stepIndex = steps.findIndex((s: any) => s.OutputObject === logNode.NodeId)
if (stepIndex >= 0 && stepIndex < restartFromStepIndex) {
truncatedLog.push(logNode)
}
}
}
return truncatedLog
}
/**
* 清除执行结果中指定步骤及之后的记录
* @param fromStepIndex 起始步骤索引
*/
function clearExecutionResults(fromStepIndex: number) {
const steps = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
// 过滤掉要重新执行的步骤及其之后的所有步骤
agentsStore.executePlan = agentsStore.executePlan.filter(logNode => {
if (logNode.LogNodeType === 'step') {
// 找到该步骤在原始步骤列表中的索引
const stepIndex = steps.findIndex((s: any) => s.StepName === logNode.NodeId)
return stepIndex < fromStepIndex
}
// 对于 object 节点,也需要判断
if (logNode.LogNodeType === 'object') {
// 找到该 object 对应的步骤索引
const stepIndex = steps.findIndex((s: any) => s.OutputObject === logNode.NodeId)
return stepIndex < fromStepIndex
}
return true
})
}
/**
* 重置步骤执行状态
* @param fromStepIndex 起始步骤索引
*/
function resetStepStatus(fromStepIndex: number) {
const steps = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
for (let i = fromStepIndex; i < steps.length; i++) {
const step = steps[i]
if (!step) continue
const stepName = step.StepName || step.Id || ''
stepExecutionStatus.value[stepName] = StepExecutionStatus.READY
}
}
/**
* 从指定步骤重新执行
* @param stepIndex 要重新执行的步骤索引例如1 表示从步骤2重新执行
*/
async function restartFromStep(stepIndex: number) {
try {
loading.value = true
isRestarting.value = true // 标记正在重新执行
// 清空修改记录(避免重复执行)
agentsStore.clearModifiedSteps()
// 0. 先停止旧的执行(避免双重执行)
if (websocket.connected && currentExecutionId.value) {
try {
console.log('🛑 停止旧的执行任务,避免双重执行')
const stopResponse = await websocket.send('stop_execution', {
goal: agentsStore.agentRawPlan.data?.['General Goal'] || ''
})
console.log('✅ 后端确认停止:', stopResponse)
// 等待一下确保后端完全停止
await new Promise(resolve => setTimeout(resolve, 1000))
} catch (err) {
console.warn('⚠️ 停止旧执行失败(可能已经停止):', err)
}
}
// 1. 构建截断后的 RehearsalLog
const truncatedLog = buildTruncatedRehearsalLog(stepIndex)
// 2. 从截断日志中提取 KeyObjects
const existingKeyObjects = buildKeyObjectsFromLog(truncatedLog)
console.log('📦 传递给后端的数据:', {
truncatedLogLength: truncatedLog.length,
keyObjectsCount: Object.keys(existingKeyObjects).length,
keyObjectsKeys: Object.keys(existingKeyObjects)
})
// 3. 清除前端执行结果中指定步骤及之后的记录
clearExecutionResults(stepIndex)
// 3.5 关闭旧的进度通知(避免通知混乱)
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
// 4. 重置步骤状态
resetStepStatus(stepIndex)
// 4.5 强制触发 Vue 响应式更新
nextTick(() => {
// 触发 UI 更新
})
// 5. 重置执行状态为执行中
isPaused.value = false
isStreaming.value = true
loading.value = true
// 6. 调用执行 API传递截断后的 RehearsalLog 和 KeyObjects
api.executePlanOptimized(
agentsStore.agentRawPlan.data!,
// onMessage - 使用内联函数处理事件
(event: StreamingEvent) => {
// 如果正在暂停isPausing或已暂停isPaused只允许特定事件
// 这样可以确保当前正在完成的动作的结果能够被正确保存
if (isPausing.value || isPaused.value) {
if (
event.type !== 'action_complete' &&
event.type !== 'step_complete' &&
event.type !== 'error'
) {
return
}
}
switch (event.type) {
case 'step_start':
stepExecutionStatus.value[event.step_name] = StepExecutionStatus.RUNNING
// 使用全局步骤索引计算当前步骤
const globalStepIndex = collaborationProcess.value.findIndex(
s => s.StepName === event.step_name
)
const currentStepNumber =
globalStepIndex >= 0 ? globalStepIndex + 1 : (event.step_index || 0) + 1
// 创建或更新进度通知
if (!currentProgressNotificationId.value) {
currentProgressNotificationId.value = showProgress(
'重新执行中',
currentStepNumber,
collaborationProcess.value.length
)
updateProgressDetail(
currentProgressNotificationId.value,
`步骤 ${currentStepNumber}/${collaborationProcess.value.length}`,
`正在执行: ${event.step_name}`
)
} else {
updateProgressDetail(
currentProgressNotificationId.value,
`步骤 ${currentStepNumber}/${collaborationProcess.value.length}`,
`正在执行: ${event.step_name}`,
currentStepNumber,
collaborationProcess.value.length
)
}
break
case 'action_complete':
// 检测是否正在暂停(等待当前动作完成)
if (isPausing.value) {
// 当前动作完成,完成暂停
isPaused.value = true
isPausing.value = false
success('已暂停', '已暂停执行,可稍后继续')
}
// 实时更新store
const existingStep = collaborationProcess.value.find(
s => s.StepName === event.step_name
)
if (existingStep) {
const currentResults = agentsStore.executePlan
const stepLogNode = currentResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (!stepLogNode) {
const newStepLog = {
LogNodeType: 'step',
NodeId: event.step_name,
InputName_List: existingStep.InputObject_List || [],
OutputName: existingStep.OutputObject || '',
chatLog: [],
inputObject_Record: [],
ActionHistory: [event.action_result]
}
agentsStore.setExecutePlan([...currentResults, newStepLog])
} else {
stepLogNode.ActionHistory.push(event.action_result)
agentsStore.setExecutePlan([...currentResults])
}
}
// 使用全局步骤索引
const globalStepIndexForAction = collaborationProcess.value.findIndex(
s => s.StepName === event.step_name
)
const stepNumberForAction =
globalStepIndexForAction >= 0
? globalStepIndexForAction + 1
: (event.step_index || 0) + 1
// 更新进度通知
if (currentProgressNotificationId.value) {
const parallelInfo = event.batch_info?.is_parallel
? ` [并行 ${event.batch_info!.batch_size} 个动作]`
: ''
updateProgressDetail(
currentProgressNotificationId.value,
`步骤 ${stepNumberForAction}/${collaborationProcess.value.length}`,
`${event.step_name} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`,
stepNumberForAction,
collaborationProcess.value.length
)
}
break
case 'step_complete':
stepExecutionStatus.value[event.step_name] = StepExecutionStatus.COMPLETED
// 更新完整步骤日志
const currentResults = agentsStore.executePlan
const existingLog = currentResults.find(
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
)
if (existingLog) {
existingLog.ActionHistory = event.step_log_node.ActionHistory
agentsStore.setExecutePlan([...currentResults])
} else if (event.step_log_node) {
agentsStore.setExecutePlan([...currentResults, event.step_log_node])
}
// 添加object_log_node
const updatedResults = agentsStore.executePlan
if (event.object_log_node) {
agentsStore.setExecutePlan([...updatedResults, event.object_log_node])
}
break
case 'execution_complete':
const steps = collaborationProcess.value
steps.forEach(step => {
const stepName = step.StepName || step.Id || ''
if (stepExecutionStatus.value[stepName] !== StepExecutionStatus.COMPLETED) {
stepExecutionStatus.value[stepName] = StepExecutionStatus.COMPLETED
}
})
// 关闭进度通知并显示完成通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
success('重新执行完成', `所有步骤已执行完成`, { duration: 3000 })
loading.value = false
isPaused.value = false
isStreaming.value = false
break
case 'error':
const errorMessage = event.message || '未知错误'
console.error('重新执行错误:', errorMessage)
// 关闭进度通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
// 静默处理所有错误,不显示任何通知
loading.value = false
isPaused.value = false
isStreaming.value = false
isRestarting.value = false // 重新执行结束
break
}
},
// onError
(err: Error) => {
console.error('重新执行错误:', err)
// 关闭进度通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
// 静默处理所有错误,不显示任何通知
loading.value = false
isPaused.value = false
isStreaming.value = false
isRestarting.value = false // 重新执行结束
},
// onComplete
() => {
// 关闭进度通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
loading.value = false
isPaused.value = false
isStreaming.value = false
isRestarting.value = false // 重新执行结束
},
true, // useWebSocket
existingKeyObjects, // ✅ 传递从截断日志中提取的KeyObjects
true, // enableDynamic
(executionId: string) => {
currentExecutionId.value = executionId
},
undefined, // executionId
stepIndex, // restartFromStepIndex
truncatedLog // ✅ 传递截断后的 RehearsalLog
)
success('重新执行', `正在从步骤 ${stepIndex + 1} 重新执行...`)
} catch (err) {
console.error('重新执行失败:', err)
error('重新执行失败', '无法启动重新执行')
loading.value = false
isRestarting.value = false // 重新执行失败,也要重置标记
}
}
// Handle execute button click
async function handleExecuteButtonClick() {
// If streaming, show pause/resume functionality
@@ -757,16 +1177,12 @@ async function handleRun() {
const waitingSteps = stepsReadyStatus.value.waiting
if (readySteps.length === 0 && waitingSteps.length > 0) {
ElMessageBox.confirm(
`All ${waitingSteps.length} steps的数据还在填充中\n\n${waitingSteps.join(
warning(
'步骤数据未就绪',
`${waitingSteps.length} 个步骤的数据还在填充中:${waitingSteps.join(
'、'
)}\n\n建议等待数据填充完成后再执行。`,
'Step data not ready',
{
confirmButtonText: 'I Understand',
cancelButtonText: 'Close',
type: 'warning'
}
)}建议等待数据填充完成后再执行。`,
{ duration: 5000 }
)
return
}

View File

@@ -201,7 +201,23 @@ const saveEditing = () => {
if (editingTaskId.value && editingContent.value.trim()) {
const taskToUpdate = collaborationProcess.value.find(item => item.Id === editingTaskId.value)
if (taskToUpdate) {
taskToUpdate.TaskContent = editingContent.value.trim()
// 保存旧值用于比较
const oldValue = taskToUpdate.TaskContent
const newValue = editingContent.value.trim()
// 只有内容真正变化时才更新和记录修改
if (newValue !== oldValue) {
taskToUpdate.TaskContent = newValue
// 记录修改过的步骤索引到 store用于重新执行
const stepIndex = collaborationProcess.value.findIndex(item => item.Id === editingTaskId.value)
if (stepIndex >= 0) {
agentsStore.addModifiedStep(stepIndex)
console.log(`📝 步骤 ${stepIndex + 1} (${taskToUpdate.StepName}) 的 TaskContent 已被修改,将从该步骤重新执行`)
}
} else {
console.log(` 步骤 ${taskToUpdate.StepName} 的 TaskContent 未发生变化,不记录修改`)
}
}
}
editingTaskId.value = null

View File

@@ -221,6 +221,19 @@ export const useAgentsStore = defineStore('agents', () => {
// 当前的展示的任务流程
const currentTask = ref<IRawStepTask>()
// 记录用户修改过的步骤索引(用于重新执行)
const modifiedSteps = ref<Set<number>>(new Set())
function addModifiedStep(stepIndex: number) {
modifiedSteps.value.add(stepIndex)
}
function clearModifiedSteps() {
modifiedSteps.value.clear()
}
function hasModifiedSteps() {
return modifiedSteps.value.size > 0
}
function setCurrentTask(task: IRawStepTask) {
const existingTask = currentTask.value
@@ -472,6 +485,11 @@ export const useAgentsStore = defineStore('agents', () => {
// 停止填充状态
hasStoppedFilling,
setHasStoppedFilling,
// 重新执行相关
modifiedSteps,
addModifiedStep,
clearModifiedSteps,
hasModifiedSteps,
}
})