feat:执行状态单例状态bug修复

This commit is contained in:
liailing1026
2026-02-02 17:09:20 +08:00
parent f272ccc390
commit 328f8e7ec6
12 changed files with 899 additions and 278 deletions

View File

@@ -426,6 +426,7 @@ class Api {
fillStepTask = async (data: {
goal: string
stepTask: any
generation_id?: string
useWebSocket?: boolean
onProgress?: (progress: {
status: string
@@ -444,6 +445,7 @@ class Api {
{
'General Goal': data.goal,
stepTask: data.stepTask,
generation_id: data.generation_id || '',
},
undefined,
data.onProgress,
@@ -484,7 +486,7 @@ class Api {
}
// 使用重试机制执行请求
const response = await withRetry(executeRequest, {
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
@@ -492,6 +494,10 @@ class Api {
},
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
const vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
@@ -606,14 +612,21 @@ class Api {
}
// 使用重试机制执行请求
const response = await withRetry(executeRequest, {
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(`⚠️ [fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
console.warn(
`⚠️ [fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
const vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
@@ -688,7 +701,9 @@ class Api {
})
// 定义实际的 API 调用逻辑
const executeRequest = async (): Promise<Record<string, Record<string, { Reason: string; Score: number }>>> => {
const executeRequest = async (): Promise<
Record<string, Record<string, { Reason: string; Score: number }>>
> => {
if (useWs && websocket.connected) {
return await websocket.send(
'agent_select_modify_init',
@@ -712,18 +727,31 @@ class Api {
}
// 使用重试机制执行请求
const response = await withRetry(executeRequest, {
const rawResponse = await withRetry(executeRequest, {
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(`⚠️ [agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
console.warn(
`⚠️ [agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = rawResponse.data || rawResponse
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
// 确保 response 存在且是有效对象
if (!response || typeof response !== 'object' || Array.isArray(response)) {
console.warn('[agentSelectModifyInit] 后端返回数据格式异常:', response)
return transformedData
}
for (const [aspect, agents] of Object.entries(response)) {
for (const [agentName, scoreInfo] of Object.entries(agents)) {
for (const [agentName, scoreInfo] of Object.entries(agents as Record<string, { Reason: string; Score: number }> || {})) {
if (!transformedData[agentName]) {
transformedData[agentName] = {}
}
@@ -758,7 +786,7 @@ class Api {
// 如果启用WebSocket且已连接使用WebSocket
if (useWs && websocket.connected) {
response = await websocket.send(
const rawResponse = await websocket.send(
'agent_select_modify_add_aspect',
{
aspectList: data.aspectList,
@@ -766,6 +794,8 @@ class Api {
undefined,
data.onProgress,
)
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
response = rawResponse.data || rawResponse
} else {
// 否则使用REST API
response = await request<
@@ -818,7 +848,7 @@ class Api {
throw new Error('WebSocket未连接')
}
const response = (await websocket.send('add_steps_to_execution', {
const rawResponse = await websocket.send('add_steps_to_execution', {
execution_id: executionId,
new_steps: newSteps.map((step) => ({
StepName: step.StepName,
@@ -835,7 +865,11 @@ class Api {
ImportantInput: action.ImportantInput,
})),
})),
})) as { added_count: number }
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const response = (rawResponse.data || rawResponse) as { added_count: number }
return response?.added_count || 0
}

View File

@@ -25,6 +25,7 @@ const isFillingSteps = ref(false)
const isStopping = ref(false)
const isStopPending = ref(false)
const currentStepAbortController = ref<{ cancel: () => void } | null>(null)
const currentGenerationId = ref('')
// 解析URL参数
function getUrlParam(param: string): string | null {
@@ -135,35 +136,36 @@ function resetTextareaHeight() {
// 停止填充数据的处理函数
async function handleStop() {
try {
if (websocket.connected) {
await websocket.send('stop_generation', {
goal: searchValue.value
})
// 标记正在停止中,按钮显示 loading 状态
isStopping.value = true
isStopPending.value = true
agentsStore.setIsStopping(true)
success('提示', '正在停止,请稍候...')
} else {
warning('警告', 'WebSocket 未连接,无法停止')
// 未连接时直接重置状态
isFillingSteps.value = false
currentStepAbortController.value = null
agentsStore.setHasStoppedFilling(true)
}
} catch (error) {
notifyError('错误', '停止生成失败')
isFillingSteps.value = false
currentStepAbortController.value = null
agentsStore.setHasStoppedFilling(true)
// 检查是否有正在进行的生成任务
if (!isFillingSteps.value) {
warning('提示', '没有正在进行的生成任务')
return
}
// 先设置停止状态(立即显示"停止中..."
agentsStore.setIsStopping(true)
isStopping.value = true
isStopPending.value = true
success('提示', '正在停止,请稍候...')
// 发送停止请求(不等待响应,后端设置 should_stop = True
if (websocket.connected && currentGenerationId.value) {
websocket.send('stop_generation', {
generation_id: currentGenerationId.value
}).then((result: any) => {
console.log('停止生成响应:', result)
}).catch((error: any) => {
console.log('停止生成请求失败(可能已经停止):', error?.message)
})
}
// 不清空 currentGenerationId让 fillStepTask 循环检查 isStopping 来停止
}
// 监听后端发送的停止完成事件
// 监听后端发送的停止完成事件(备用,如果后端有发送)
function onGenerationStopped() {
isStopping.value = false
isStopPending.value = false
currentGenerationId.value = ''
success('成功', '已停止生成')
}
@@ -185,16 +187,34 @@ async function handleSearch() {
}
emit('search-start')
agentsStore.resetAgent()
agentsStore.setAgentRawPlan({ loading: true })
// 重置所有状态(处理可能的上一次未完成的停止操作)
isStopping.value = false
isStopPending.value = false
agentsStore.setIsStopping(false)
agentsStore.setHasStoppedFilling(false)
agentsStore.resetAgent()
agentsStore.setAgentRawPlan({ loading: true })
// 重置 generation_id
currentGenerationId.value = ''
// 获取大纲
const outlineData = await api.generateBasePlan({
const response = await api.generateBasePlan({
goal: searchValue.value,
inputs: []
})
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
// REST API 返回格式: {...}
const outlineData = response.data || response
// 保存 generation_id
if (response && response.generation_id) {
currentGenerationId.value = response.generation_id
console.log('📋 保存 generation_id:', currentGenerationId.value)
}
// 处理简报数据格式
outlineData['Collaboration Process'] = changeBriefs(outlineData['Collaboration Process'])
@@ -209,44 +229,55 @@ async function handleSearch() {
isFillingSteps.value = true
const steps = outlineData['Collaboration Process'] || []
// 保存 generation_id 到本地变量,用于 fillStepTask 调用
// 这样即使前端停止时清空了 currentGenerationId当前的 fillStepTask 仍能正确停止
const fillTaskGenerationId = currentGenerationId.value
// 串行填充所有步骤的详情
for (const step of steps) {
// 检查是否已停止
if (!isFillingSteps.value || agentsStore.isStopping) {
break
try {
for (const step of steps) {
// 检查是否已停止
if (!isFillingSteps.value || agentsStore.isStopping) {
break
}
await withRetry(
async () => {
const detailedStep = await api.fillStepTask({
goal: searchValue.value,
stepTask: {
StepName: step.StepName,
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List,
OutputObject: step.OutputObject,
},
generation_id: fillTaskGenerationId,
})
updateStepDetail(step.StepName, detailedStep)
},
{
maxRetries: 2, // 减少重试次数,因为是串行填充
initialDelayMs: 1000, // 使用较小的延迟
shouldRetry: () => isFillingSteps.value && !agentsStore.isStopping, // 可取消的重试
},
)
}
} finally {
// 重置状态(确保即使出错也会执行)
triggerOnFocus.value = true
if (isStopPending.value) {
isStopping.value = false
isStopPending.value = false
agentsStore.setIsStopping(false)
agentsStore.setHasStoppedFilling(true)
}
isFillingSteps.value = false
currentStepAbortController.value = null
// 只有在没有停止请求时才清空 generation_id
if (!isStopPending.value) {
currentGenerationId.value = ''
}
await withRetry(
async () => {
const detailedStep = await api.fillStepTask({
goal: searchValue.value,
stepTask: {
StepName: step.StepName,
TaskContent: step.TaskContent,
InputObject_List: step.InputObject_List,
OutputObject: step.OutputObject,
},
})
updateStepDetail(step.StepName, detailedStep)
},
{
maxRetries: 2, // 减少重试次数,因为是串行填充
initialDelayMs: 1000, // 使用较小的延迟
shouldRetry: () => isFillingSteps.value && !agentsStore.isStopping, // 可取消的重试
},
)
}
// 重置状态
triggerOnFocus.value = true
if (isStopPending.value) {
isStopping.value = false
isStopPending.value = false
agentsStore.setIsStopping(false)
agentsStore.setHasStoppedFilling(true)
}
isFillingSteps.value = false
currentStepAbortController.value = null
}
//更新单个步骤的详情

View File

@@ -679,10 +679,13 @@ const submitBranch = async () => {
goal: generalGoal
})
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {
@@ -974,10 +977,13 @@ const submitBranch = async () => {
goal: generalGoal
})
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
// REST API 返回格式: [[action1, action2], [action3, action4]]
const responseData = response.data || response
// 后端返回格式: [[action1, action2], [action3, action4]]
// 取第一个分支
if (response && response.length > 0) {
const firstBranch = response[0]
if (responseData && responseData.length > 0) {
const firstBranch = responseData[0]
// 直接遍历 action 数组
firstBranch.forEach((action: any) => {

View File

@@ -480,8 +480,14 @@ async function handlePauseResume() {
// 正常恢复执行
try {
if (websocket.connected) {
// 检查 execution_id 是否存在
if (!currentExecutionId.value) {
warning('无法恢复', '执行ID不存在请等待执行开始')
return
}
await websocket.send('resume_execution', {
execution_id: currentExecutionId.value || ''
execution_id: currentExecutionId.value
})
isPaused.value = false
@@ -498,12 +504,19 @@ async function handlePauseResume() {
// 暂停执行
try {
if (websocket.connected) {
// 检查 execution_id 是否存在
if (!currentExecutionId.value) {
warning('无法暂停', '执行ID不存在请等待执行开始')
isPausing.value = false
return
}
// 先设置 isPausing允许接收当前正在执行的动作的结果
isPausing.value = true
info('暂停中', '正在等待当前动作完成')
await websocket.send('pause_execution', {
execution_id: currentExecutionId.value || ''
execution_id: currentExecutionId.value
})
/*不立即设置 isPaused = true
@@ -881,11 +894,14 @@ async function restartFromStep(stepIndex: number) {
// 清空修改记录
agentsStore.clearModifiedSteps()
// 保存旧的 execution_id 用于停止
const oldExecutionId = currentExecutionId.value
// 停止旧的执行
if (websocket.connected && currentExecutionId.value) {
if (websocket.connected && oldExecutionId) {
try {
const stopResponse = await websocket.send('stop_execution', {
execution_id: currentExecutionId.value || ''
await websocket.send('stop_execution', {
execution_id: oldExecutionId
})
// 等待一下确保后端完全停止
await new Promise(resolve => setTimeout(resolve, 1000))
@@ -893,6 +909,13 @@ async function restartFromStep(stepIndex: number) {
console.warn('⚠️ 停止旧执行失败(可能已经停止):', err)
}
}
// 前端生成新的 execution_id确保前端和后端使用同一个 ID
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
const newExecutionId = `${generalGoal.replace(/\s+/g, '_')}_${Date.now()}`
currentExecutionId.value = newExecutionId
console.log('🔄 [DEBUG] restartFromStep: 生成新的 execution_id =', newExecutionId)
// 构建截断后的 RehearsalLog
const truncatedLog = buildTruncatedRehearsalLog(stepIndex)
@@ -936,7 +959,7 @@ async function restartFromStep(stepIndex: number) {
isStreaming.value = true
currentExecutionId.value = executionId
},
undefined,
newExecutionId, // 传入前端生成的 execution_id
stepIndex,
truncatedLog
)
@@ -1007,8 +1030,41 @@ async function handleTaskProcess() {
}
// 重置执行结果
function handleRefresh() {
async function handleRefresh() {
// 如果有正在执行的任务,先通知后端停止
if (websocket.connected && currentExecutionId.value) {
try {
await websocket.send('stop_execution', {
execution_id: currentExecutionId.value
})
// 等待一下确保后端完全停止
await new Promise(resolve => setTimeout(resolve, 500))
} catch (err) {
console.warn('⚠️ 停止执行失败(可能已经停止):', err)
}
}
// 重置所有状态
agentsStore.setExecutePlan([])
stepExecutionStatus.value = {}
sentStepIds.value.clear()
currentExecutionId.value = null
isPaused.value = false
isStreaming.value = false
isPausing.value = false
loading.value = false
isRestarting.value = false
// 重置进度通知标题
currentProgressTitle.value = '任务执行中'
// 关闭进度通知
if (currentProgressNotificationId.value) {
removeNotification(currentProgressNotificationId.value)
currentProgressNotificationId.value = null
}
success('已重置', '执行状态已重置')
}
// 添加滚动状态指示器

View File

@@ -797,13 +797,16 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
goal: generalGoal
})
// WebSocket 返回格式: { data: [[{...}]], ... }
// REST API 返回格式: [[{...}]]
const responseData = response.data || response
// 直接获取协作流程数据
if (Array.isArray(response)) {
if (Array.isArray(responseData)) {
// 可能是二维数组
newTasks = (response as any[])[0] || []
} else if (response && (response as any)['Collaboration Process']) {
newTasks = responseData[0] || []
} else if (responseData && responseData['Collaboration Process']) {
// 如果返回的是对象,尝试读取 Collaboration Process 字段
newTasks = (response as any)['Collaboration Process'] || []
newTasks = responseData['Collaboration Process'] || []
} else {
newTasks = []
}
@@ -1136,14 +1139,17 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
initialInputs: Array.isArray(initialInput) ? initialInput : [initialInput],
goal: generalGoal
})
// WebSocket 返回格式: { data: [[{...}]], ... }
// REST API 返回格式: [[{...}]]
const responseData = response.data || response
// 直接获取协作流程数据
// newTasks = response?.[0] || []
if (Array.isArray(response)) {
if (Array.isArray(responseData)) {
// 可能是二维数组
newTasks = (response as any[])[0] || []
} else if (response && (response as any)['Collaboration Process']) {
newTasks = responseData[0] || []
} else if (responseData && responseData['Collaboration Process']) {
// 如果返回的是对象,尝试读取 Collaboration Process 字段
newTasks = (response as any)['Collaboration Process'] || []
newTasks = responseData['Collaboration Process'] || []
} else {
newTasks = []
}

View File

@@ -78,17 +78,17 @@ class WebSocketClient {
reject(error)
})
this.socket.on('disconnect', (reason) => {
this.socket.on('disconnect', () => {
this.isConnected = false
})
this.socket.on('connected', (data) => {
this.socket.on('connected', () => {
// Server connected message
})
// 监听响应消息
this.socket.on('response', (response: ResponseMessage) => {
const { id, status, data, error } = response
const { id, status, data, error, generation_id, execution_id } = response
const handler = this.requestHandlers.get(id)
if (handler) {
@@ -98,7 +98,19 @@ class WebSocketClient {
}
if (status === 'success') {
handler.resolve(data)
// 返回完整响应,包含 data、generation_id、execution_id 等
// 注意:需要检查 data 是否为 null因为 typeof null === 'object'
// generation_id/execution_id 可能放在 data 中,需要兼容处理
// 注意:如果 data 是数组,不能展开,否则会破坏数组结构
const resolvedGenerationId = generation_id || (data && typeof data === 'object' && !Array.isArray(data) && data.generation_id)
const resolvedExecutionId = execution_id || (data && typeof data === 'object' && !Array.isArray(data) && data.execution_id)
// 直接返回 data保持原始数据结构数组或对象
handler.resolve({
data,
generation_id: resolvedGenerationId,
execution_id: resolvedExecutionId
})
} else {
handler.reject(new Error(error || 'Unknown error'))
}