feat:任务执行结果性能优化
This commit is contained in:
@@ -137,5 +137,6 @@ def AgentSelectModify_init(stepTask, General_Goal, Agent_Board):
|
|||||||
|
|
||||||
|
|
||||||
def AgentSelectModify_addAspect(aspectList, Agent_Board):
|
def AgentSelectModify_addAspect(aspectList, Agent_Board):
|
||||||
scoreTable = agentAbilityScoring(Agent_Board, aspectList)
|
newAspect = aspectList[-1]
|
||||||
|
scoreTable = agentAbilityScoring(Agent_Board, [newAspect])
|
||||||
return scoreTable
|
return scoreTable
|
||||||
|
|||||||
@@ -1,55 +1,53 @@
|
|||||||
from AgentCoord.PlanEngine.planOutline_Generator import generate_PlanOutline
|
from AgentCoord.PlanEngine.planOutline_Generator import generate_PlanOutline
|
||||||
from AgentCoord.PlanEngine.AgentSelection_Generator import (
|
# from AgentCoord.PlanEngine.AgentSelection_Generator import (
|
||||||
generate_AgentSelection,
|
# generate_AgentSelection,
|
||||||
)
|
# )
|
||||||
from AgentCoord.PlanEngine.taskProcess_Generator import generate_TaskProcess
|
|
||||||
import AgentCoord.util as util
|
|
||||||
|
|
||||||
|
|
||||||
def generate_basePlan(
|
def generate_basePlan(
|
||||||
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List
|
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List
|
||||||
):
|
):
|
||||||
basePlan = {
|
"""
|
||||||
"Initial Input Object": InitialObject_List,
|
优化模式:生成大纲 + 智能体选择,但不生成任务流程
|
||||||
"Collaboration Process": [],
|
优化用户体验:
|
||||||
}
|
1. 快速生成大纲和分配智能体
|
||||||
|
2. 用户可以看到完整的大纲和智能体图标
|
||||||
|
3. TaskProcess由前端通过 fillStepTask API 异步填充
|
||||||
|
|
||||||
|
"""
|
||||||
|
# 参数保留以保持接口兼容性
|
||||||
|
_ = AgentProfile_Dict
|
||||||
PlanOutline = generate_PlanOutline(
|
PlanOutline = generate_PlanOutline(
|
||||||
InitialObject_List=[], General_Goal=General_Goal
|
InitialObject_List=InitialObject_List, General_Goal=General_Goal
|
||||||
)
|
)
|
||||||
|
|
||||||
|
basePlan = {
|
||||||
|
"General Goal": General_Goal,
|
||||||
|
"Initial Input Object": InitialObject_List,
|
||||||
|
"Collaboration Process": []
|
||||||
|
}
|
||||||
|
|
||||||
for stepItem in PlanOutline:
|
for stepItem in PlanOutline:
|
||||||
Current_Task = {
|
# # 为每个步骤分配智能体
|
||||||
"TaskName": stepItem["StepName"],
|
# Current_Task = {
|
||||||
"InputObject_List": stepItem["InputObject_List"],
|
# "TaskName": stepItem["StepName"],
|
||||||
"OutputObject": stepItem["OutputObject"],
|
# "InputObject_List": stepItem["InputObject_List"],
|
||||||
"TaskContent": stepItem["TaskContent"],
|
# "OutputObject": stepItem["OutputObject"],
|
||||||
|
# "TaskContent": stepItem["TaskContent"],
|
||||||
|
# }
|
||||||
|
# AgentSelection = generate_AgentSelection(
|
||||||
|
# General_Goal=General_Goal,
|
||||||
|
# Current_Task=Current_Task,
|
||||||
|
# Agent_Board=Agent_Board,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# 添加智能体选择,但不添加任务流程
|
||||||
|
stepItem["AgentSelection"] = []
|
||||||
|
stepItem["TaskProcess"] = [] # 空数组,由前端异步填充
|
||||||
|
stepItem["Collaboration_Brief_frontEnd"] = {
|
||||||
|
"template": "",
|
||||||
|
"data": {}
|
||||||
}
|
}
|
||||||
AgentSelection = generate_AgentSelection(
|
|
||||||
General_Goal=General_Goal,
|
|
||||||
Current_Task=Current_Task,
|
|
||||||
Agent_Board=Agent_Board,
|
|
||||||
)
|
|
||||||
Current_Task_Description = {
|
|
||||||
"TaskName": stepItem["StepName"],
|
|
||||||
"AgentInvolved": [
|
|
||||||
{"Name": name, "Profile": AgentProfile_Dict[name]}
|
|
||||||
for name in AgentSelection
|
|
||||||
],
|
|
||||||
"InputObject_List": stepItem["InputObject_List"],
|
|
||||||
"OutputObject": stepItem["OutputObject"],
|
|
||||||
"CurrentTaskDescription": util.generate_template_sentence_for_CollaborationBrief(
|
|
||||||
stepItem["InputObject_List"],
|
|
||||||
stepItem["OutputObject"],
|
|
||||||
AgentSelection,
|
|
||||||
stepItem["TaskContent"],
|
|
||||||
),
|
|
||||||
}
|
|
||||||
TaskProcess = generate_TaskProcess(
|
|
||||||
General_Goal=General_Goal,
|
|
||||||
Current_Task_Description=Current_Task_Description,
|
|
||||||
)
|
|
||||||
# add the generated AgentSelection and TaskProcess to the stepItem
|
|
||||||
stepItem["AgentSelection"] = AgentSelection
|
|
||||||
stepItem["TaskProcess"] = TaskProcess
|
|
||||||
basePlan["Collaboration Process"].append(stepItem)
|
basePlan["Collaboration Process"].append(stepItem)
|
||||||
basePlan["General Goal"] = General_Goal
|
|
||||||
return basePlan
|
return basePlan
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from flask import Flask, request, jsonify
|
from flask import Flask, request, jsonify, Response, stream_with_context
|
||||||
import json
|
import json
|
||||||
from DataProcess import Add_Collaboration_Brief_FrontEnd
|
from DataProcess import Add_Collaboration_Brief_FrontEnd
|
||||||
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
|
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
|
||||||
|
from AgentCoord.RehearsalEngine_V2.ExecutePlan_Optimized import executePlan_streaming
|
||||||
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
|
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
|
||||||
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
|
from AgentCoord.PlanEngine.fill_stepTask import fill_stepTask
|
||||||
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
|
from AgentCoord.PlanEngine.fill_stepTask_TaskProcess import (
|
||||||
@@ -257,6 +258,45 @@ def Handle_executePlan():
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/executePlanOptimized", methods=["post"])
|
||||||
|
def Handle_executePlanOptimized():
|
||||||
|
"""
|
||||||
|
优化版流式执行计划(阶段1+2:步骤级流式 + 动作级智能并行)
|
||||||
|
|
||||||
|
返回 SSE 流,每完成一个动作就返回结果
|
||||||
|
- 无依赖关系的动作并行执行
|
||||||
|
- 有依赖关系的动作串行执行
|
||||||
|
|
||||||
|
前端使用 EventSource 接收
|
||||||
|
"""
|
||||||
|
incoming_data = request.get_json()
|
||||||
|
|
||||||
|
def generate():
|
||||||
|
try:
|
||||||
|
for chunk in executePlan_streaming(
|
||||||
|
plan=incoming_data["plan"],
|
||||||
|
num_StepToRun=incoming_data.get("num_StepToRun"),
|
||||||
|
RehearsalLog=incoming_data.get("RehearsalLog", []),
|
||||||
|
AgentProfile_Dict=AgentProfile_Dict,
|
||||||
|
):
|
||||||
|
yield chunk
|
||||||
|
except Exception as e:
|
||||||
|
error_event = json.dumps({
|
||||||
|
"type": "error",
|
||||||
|
"message": str(e)
|
||||||
|
}, ensure_ascii=False)
|
||||||
|
yield f"data: {error_event}\n\n"
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
stream_with_context(generate()),
|
||||||
|
mimetype="text/event-stream",
|
||||||
|
headers={
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
"X-Accel-Buffering": "no",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/_saveRequestCashe", methods=["post"])
|
@app.route("/_saveRequestCashe", methods=["post"])
|
||||||
def Handle_saveRequestCashe():
|
def Handle_saveRequestCashe():
|
||||||
with open(
|
with open(
|
||||||
|
|||||||
@@ -34,6 +34,47 @@ export type IExecuteRawResponse = {
|
|||||||
ActionHistory: ActionHistory[]
|
ActionHistory: ActionHistory[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SSE 流式事件类型
|
||||||
|
*/
|
||||||
|
export type StreamingEvent =
|
||||||
|
| {
|
||||||
|
type: 'step_start'
|
||||||
|
step_index: number
|
||||||
|
total_steps: number
|
||||||
|
step_name: string
|
||||||
|
task_description?: string
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'action_complete'
|
||||||
|
step_index: number
|
||||||
|
step_name: string
|
||||||
|
action_index: number
|
||||||
|
total_actions: number
|
||||||
|
completed_actions: number
|
||||||
|
action_result: ActionHistory
|
||||||
|
batch_info?: {
|
||||||
|
batch_index: number
|
||||||
|
batch_size: number
|
||||||
|
is_parallel: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'step_complete'
|
||||||
|
step_index: number
|
||||||
|
step_name: string
|
||||||
|
step_log_node: any
|
||||||
|
object_log_node: any
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'execution_complete'
|
||||||
|
total_steps: number
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'error'
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
|
||||||
export interface IFillAgentSelectionRequest {
|
export interface IFillAgentSelectionRequest {
|
||||||
goal: string
|
goal: string
|
||||||
stepTask: IApiStepTask
|
stepTask: IApiStepTask
|
||||||
@@ -99,7 +140,95 @@ class Api {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分支任务大纲
|
/**
|
||||||
|
* 优化版流式执行计划(阶段1+2:步骤级流式 + 动作级智能并行)
|
||||||
|
* 无依赖关系的动作并行执行,有依赖关系的动作串行执行
|
||||||
|
*/
|
||||||
|
executePlanOptimized = (
|
||||||
|
plan: IRawPlanResponse,
|
||||||
|
onMessage: (event: StreamingEvent) => void,
|
||||||
|
onError?: (error: Error) => void,
|
||||||
|
onComplete?: () => void,
|
||||||
|
) => {
|
||||||
|
const data = {
|
||||||
|
RehearsalLog: [],
|
||||||
|
num_StepToRun: null,
|
||||||
|
plan: {
|
||||||
|
'Initial Input Object': plan['Initial Input Object'],
|
||||||
|
'General Goal': plan['General Goal'],
|
||||||
|
'Collaboration Process': plan['Collaboration Process']?.map((step) => ({
|
||||||
|
StepName: step.StepName,
|
||||||
|
TaskContent: step.TaskContent,
|
||||||
|
InputObject_List: step.InputObject_List,
|
||||||
|
OutputObject: step.OutputObject,
|
||||||
|
AgentSelection: step.AgentSelection,
|
||||||
|
Collaboration_Brief_frontEnd: step.Collaboration_Brief_frontEnd,
|
||||||
|
TaskProcess: step.TaskProcess.map((action) => ({
|
||||||
|
ActionType: action.ActionType,
|
||||||
|
AgentName: action.AgentName,
|
||||||
|
Description: action.Description,
|
||||||
|
ID: action.ID,
|
||||||
|
ImportantInput: action.ImportantInput,
|
||||||
|
})),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('/api/executePlanOptimized', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
})
|
||||||
|
.then(async (response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = response.body?.getReader()
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
|
||||||
|
if (!reader) {
|
||||||
|
throw new Error('Response body is null')
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = ''
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
onComplete?.()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
|
||||||
|
const lines = buffer.split('\n')
|
||||||
|
buffer = lines.pop() || ''
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.slice(6)
|
||||||
|
try {
|
||||||
|
const event = JSON.parse(data)
|
||||||
|
onMessage(event)
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to parse SSE data:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
onError?.(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分支任务大纲
|
||||||
|
*/
|
||||||
branchPlanOutline = (data: {
|
branchPlanOutline = (data: {
|
||||||
branch_Number: number
|
branch_Number: number
|
||||||
Modification_Requirement: string
|
Modification_Requirement: string
|
||||||
@@ -122,7 +251,9 @@ class Api {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分支任务流程
|
/**
|
||||||
|
* 分支任务流程
|
||||||
|
*/
|
||||||
branchTaskProcess = (data: {
|
branchTaskProcess = (data: {
|
||||||
branch_Number: number
|
branch_Number: number
|
||||||
Modification_Requirement: string
|
Modification_Requirement: string
|
||||||
@@ -146,7 +277,6 @@ class Api {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fillStepTask = async (data: { goal: string; stepTask: any }): Promise<IRawStepTask> => {
|
fillStepTask = async (data: { goal: string; stepTask: any }): Promise<IRawStepTask> => {
|
||||||
// 后端返回格式:包含 Collaboration_Brief_FrontEnd(大写 FrontEnd)
|
|
||||||
const response = await request<
|
const response = await request<
|
||||||
{
|
{
|
||||||
'General Goal': string
|
'General Goal': string
|
||||||
@@ -179,14 +309,11 @@ class Api {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 数据转换:后端的 Collaboration_Brief_FrontEnd → 前端的 Collaboration_Brief_frontEnd
|
|
||||||
|
|
||||||
const vec2Hsl = (color: number[]): string => {
|
const vec2Hsl = (color: number[]): string => {
|
||||||
const [h, s, l] = color
|
const [h, s, l] = color
|
||||||
return `hsl(${h}, ${s}%, ${l}%)`
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换 brief.data:后端格式 { "0": { text, color: [h,s,l] } } → 前端格式 { "0": { text, style: { background } } }
|
|
||||||
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
|
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
|
||||||
if (response.Collaboration_Brief_FrontEnd?.data) {
|
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||||
@@ -199,7 +326,9 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建前端格式的 IRawStepTask
|
/**
|
||||||
|
* 构建前端格式的 IRawStepTask
|
||||||
|
*/
|
||||||
return {
|
return {
|
||||||
StepName: response.StepName || '',
|
StepName: response.StepName || '',
|
||||||
TaskContent: response.TaskContent || '',
|
TaskContent: response.TaskContent || '',
|
||||||
@@ -219,7 +348,6 @@ class Api {
|
|||||||
stepTask: IApiStepTask
|
stepTask: IApiStepTask
|
||||||
agents: string[]
|
agents: string[]
|
||||||
}): Promise<IApiStepTask> => {
|
}): Promise<IApiStepTask> => {
|
||||||
// 后端返回格式: IRawStepTask
|
|
||||||
const response = await request<
|
const response = await request<
|
||||||
{
|
{
|
||||||
'General Goal': string
|
'General Goal': string
|
||||||
@@ -264,16 +392,11 @@ class Api {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 数据转换:后端格式 (IRawStepTask) → 前端格式 (IApiStepTask)
|
|
||||||
// 注意:此转换逻辑与Mock API完全一致
|
|
||||||
|
|
||||||
// 1. 转换颜色格式:[h, s, l] → "hsl(h, s%, l%)"
|
|
||||||
const vec2Hsl = (color: number[]): string => {
|
const vec2Hsl = (color: number[]): string => {
|
||||||
const [h, s, l] = color
|
const [h, s, l] = color
|
||||||
return `hsl(${h}, ${s}%, ${l}%)`
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 转换 brief.data: { "0": { text, color: [h,s,l] } } → { "0": { text, style: { background } } }
|
|
||||||
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||||
if (response.Collaboration_Brief_FrontEnd?.data) {
|
if (response.Collaboration_Brief_FrontEnd?.data) {
|
||||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
|
||||||
@@ -286,7 +409,6 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 转换 process: { ID, ActionType, AgentName, Description, ImportantInput } → { id, type, agent, description, inputs }
|
|
||||||
const process = (response.TaskProcess || []).map((action) => ({
|
const process = (response.TaskProcess || []).map((action) => ({
|
||||||
id: action.ID,
|
id: action.ID,
|
||||||
type: action.ActionType,
|
type: action.ActionType,
|
||||||
@@ -295,7 +417,6 @@ class Api {
|
|||||||
inputs: action.ImportantInput,
|
inputs: action.ImportantInput,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 4. 构建前端格式的 IApiStepTask
|
|
||||||
return {
|
return {
|
||||||
name: response.StepName || '',
|
name: response.StepName || '',
|
||||||
content: response.TaskContent || '',
|
content: response.TaskContent || '',
|
||||||
@@ -310,12 +431,13 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 为每个智能体评分
|
/**
|
||||||
|
* 为每个智能体评分
|
||||||
|
*/
|
||||||
agentSelectModifyInit = async (data: {
|
agentSelectModifyInit = async (data: {
|
||||||
goal: string
|
goal: string
|
||||||
stepTask: any
|
stepTask: any
|
||||||
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
|
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
|
||||||
// 后端返回:维度 -> agent -> { Reason, Score }
|
|
||||||
const response = await request<
|
const response = await request<
|
||||||
{
|
{
|
||||||
'General Goal': string
|
'General Goal': string
|
||||||
@@ -336,11 +458,9 @@ class Api {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 数据转换:后端格式 (维度->agent) → 前端格式 (agent->维度)
|
|
||||||
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||||
|
|
||||||
for (const [aspect, agents] of Object.entries(response)) {
|
for (const [aspect, agents] of Object.entries(response)) {
|
||||||
// aspect: 维度名称, agents: { agentName: { Reason, Score } }
|
|
||||||
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
||||||
if (!transformedData[agentName]) {
|
if (!transformedData[agentName]) {
|
||||||
transformedData[agentName] = {}
|
transformedData[agentName] = {}
|
||||||
@@ -355,15 +475,15 @@ class Api {
|
|||||||
return transformedData
|
return transformedData
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加新的评估维度
|
/**
|
||||||
// 定义返回类型(与 Mock 数据格式一致)
|
* 添加新的评估维度
|
||||||
|
*/
|
||||||
agentSelectModifyAddAspect = async (data: {
|
agentSelectModifyAddAspect = async (data: {
|
||||||
aspectList: string[]
|
aspectList: string[]
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
aspectName: string
|
aspectName: string
|
||||||
agentScores: Record<string, { score: number; reason: string }>
|
agentScores: Record<string, { score: number; reason: string }>
|
||||||
}> => {
|
}> => {
|
||||||
// 后端返回:维度 -> agent -> { Reason, Score }
|
|
||||||
const response = await request<
|
const response = await request<
|
||||||
{
|
{
|
||||||
aspectList: string[]
|
aspectList: string[]
|
||||||
@@ -377,13 +497,14 @@ class Api {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取新添加的维度(最后一个)
|
/**
|
||||||
|
* 获取新添加的维度
|
||||||
|
*/
|
||||||
const newAspect = data.aspectList[data.aspectList.length - 1]
|
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||||
if (!newAspect) {
|
if (!newAspect) {
|
||||||
throw new Error('aspectList is empty')
|
throw new Error('aspectList is empty')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取该维度的数据:维度 -> agent -> { Reason, Score } → agent -> { score, reason }
|
|
||||||
const newAspectAgents = response[newAspect]
|
const newAspectAgents = response[newAspect]
|
||||||
const agentScores: Record<string, { score: number; reason: string }> = {}
|
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||||
|
|
||||||
@@ -401,23 +522,18 @@ class Api {
|
|||||||
agentScores,
|
agentScores,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
// ==================== Mock API(开发阶段使用)====================
|
* ==================== Mock API(开发阶段使用)====================
|
||||||
// Mock API 使用与真实 API 相同的数据转换逻辑,确保将来切换无缝
|
*为每个智能体评分
|
||||||
|
*/
|
||||||
// Mock: 为每个智能体评分
|
|
||||||
mockAgentSelectModifyInit = async (): Promise<
|
mockAgentSelectModifyInit = async (): Promise<
|
||||||
Record<string, Record<string, { reason: string; score: number }>>
|
Record<string, Record<string, { reason: string; score: number }>>
|
||||||
> => {
|
> => {
|
||||||
// 调用Mock后端数据(维度 -> agent -> { Reason, Score })
|
|
||||||
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyInit()
|
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyInit()
|
||||||
|
|
||||||
// 数据转换:后端格式 (维度->agent) → 前端格式 (agent->维度)
|
|
||||||
// 注意:此转换逻辑与真实API完全一致
|
|
||||||
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
|
||||||
|
|
||||||
for (const [aspect, agents] of Object.entries(response)) {
|
for (const [aspect, agents] of Object.entries(response)) {
|
||||||
// aspect: 维度名称, agents: { agentName: { Reason, Score } }
|
|
||||||
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
for (const [agentName, scoreInfo] of Object.entries(agents)) {
|
||||||
if (!transformedData[agentName]) {
|
if (!transformedData[agentName]) {
|
||||||
transformedData[agentName] = {}
|
transformedData[agentName] = {}
|
||||||
@@ -432,26 +548,21 @@ class Api {
|
|||||||
return transformedData
|
return transformedData
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock: 添加新的评估维度
|
|
||||||
mockAgentSelectModifyAddAspect = async (data: {
|
mockAgentSelectModifyAddAspect = async (data: {
|
||||||
aspectList: string[]
|
aspectList: string[]
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
aspectName: string
|
aspectName: string
|
||||||
agentScores: Record<string, { score: number; reason: string }>
|
agentScores: Record<string, { score: number; reason: string }>
|
||||||
}> => {
|
}> => {
|
||||||
// 调用Mock后端数据(维度 -> agent -> { Reason, Score })
|
|
||||||
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyAddAspect(
|
const response: BackendAgentScoreResponse = await mockBackendAgentSelectModifyAddAspect(
|
||||||
data.aspectList,
|
data.aspectList,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 获取新添加的维度(最后一个)
|
|
||||||
const newAspect = data.aspectList[data.aspectList.length - 1]
|
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||||
if (!newAspect) {
|
if (!newAspect) {
|
||||||
throw new Error('aspectList is empty')
|
throw new Error('aspectList is empty')
|
||||||
}
|
}
|
||||||
|
|
||||||
// 提取该维度的数据:维度 -> agent -> { Reason, Score } → agent -> { score, reason }
|
|
||||||
// 注意:此转换逻辑与真实API完全一致
|
|
||||||
const newAspectAgents = response[newAspect]
|
const newAspectAgents = response[newAspect]
|
||||||
const agentScores: Record<string, { score: number; reason: string }> = {}
|
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||||
|
|
||||||
@@ -470,29 +581,22 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock: 填充智能体任务流程
|
|
||||||
mockFillStepTaskTaskProcess = async (data: {
|
mockFillStepTaskTaskProcess = async (data: {
|
||||||
goal: string
|
goal: string
|
||||||
stepTask: IApiStepTask
|
stepTask: IApiStepTask
|
||||||
agents: string[]
|
agents: string[]
|
||||||
}): Promise<IApiStepTask> => {
|
}): Promise<IApiStepTask> => {
|
||||||
// 调用Mock后端数据(后端原始格式)
|
|
||||||
const response: RawAgentTaskProcessResponse = await mockBackendFillAgentTaskProcess(
|
const response: RawAgentTaskProcessResponse = await mockBackendFillAgentTaskProcess(
|
||||||
data.goal,
|
data.goal,
|
||||||
data.stepTask,
|
data.stepTask,
|
||||||
data.agents,
|
data.agents,
|
||||||
)
|
)
|
||||||
|
|
||||||
// 数据转换:后端格式 → 前端格式
|
|
||||||
// 注意:此转换逻辑与真实API完全一致
|
|
||||||
|
|
||||||
// 1. 转换颜色格式:[h, s, l] → "hsl(h, s%, l%)"
|
|
||||||
const vec2Hsl = (color: number[]): string => {
|
const vec2Hsl = (color: number[]): string => {
|
||||||
const [h, s, l] = color
|
const [h, s, l] = color
|
||||||
return `hsl(${h}, ${s}%, ${l}%)`
|
return `hsl(${h}, ${s}%, ${l}%)`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 转换 brief.data: { "0": { text, color: [h,s,l] } } → { "0": { text, style: { background } } }
|
|
||||||
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||||
if (response.Collaboration_Brief_frontEnd?.data) {
|
if (response.Collaboration_Brief_frontEnd?.data) {
|
||||||
for (const [key, value] of Object.entries(response.Collaboration_Brief_frontEnd.data)) {
|
for (const [key, value] of Object.entries(response.Collaboration_Brief_frontEnd.data)) {
|
||||||
@@ -505,7 +609,6 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 转换 process: { ID, ActionType, AgentName, Description, ImportantInput } → { id, type, agent, description, inputs }
|
|
||||||
const process = (response.TaskProcess || []).map((action) => ({
|
const process = (response.TaskProcess || []).map((action) => ({
|
||||||
id: action.ID,
|
id: action.ID,
|
||||||
type: action.ActionType,
|
type: action.ActionType,
|
||||||
@@ -514,7 +617,6 @@ class Api {
|
|||||||
inputs: action.ImportantInput,
|
inputs: action.ImportantInput,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// 4. 构建前端格式的 IApiStepTask
|
|
||||||
return {
|
return {
|
||||||
name: response.StepName || '',
|
name: response.StepName || '',
|
||||||
content: response.TaskContent || '',
|
content: response.TaskContent || '',
|
||||||
@@ -529,7 +631,6 @@ class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock: 分支任务大纲
|
|
||||||
mockBranchPlanOutline = async (data: {
|
mockBranchPlanOutline = async (data: {
|
||||||
branch_Number: number
|
branch_Number: number
|
||||||
Modification_Requirement: string
|
Modification_Requirement: string
|
||||||
@@ -538,7 +639,6 @@ class Api {
|
|||||||
initialInputs: string[]
|
initialInputs: string[]
|
||||||
goal: string
|
goal: string
|
||||||
}): Promise<IRawPlanResponse> => {
|
}): Promise<IRawPlanResponse> => {
|
||||||
// 直接调用 Mock API(已经返回 IRawPlanResponse 格式)
|
|
||||||
const response = await mockBranchPlanOutlineAPI({
|
const response = await mockBranchPlanOutlineAPI({
|
||||||
branch_Number: data.branch_Number,
|
branch_Number: data.branch_Number,
|
||||||
Modification_Requirement: data.Modification_Requirement,
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
@@ -551,9 +651,7 @@ class Api {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock: 填充任务流程
|
|
||||||
mockFillStepTask = async (data: { goal: string; stepTask: any }): Promise<any> => {
|
mockFillStepTask = async (data: { goal: string; stepTask: any }): Promise<any> => {
|
||||||
// 直接调用 Mock API(已经返回 IRawStepTask 格式)
|
|
||||||
const response = await mockFillStepTaskAPI({
|
const response = await mockFillStepTaskAPI({
|
||||||
General_Goal: data.goal,
|
General_Goal: data.goal,
|
||||||
stepTask: data.stepTask,
|
stepTask: data.stepTask,
|
||||||
@@ -562,7 +660,6 @@ class Api {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock: 分支任务流程
|
|
||||||
mockBranchTaskProcess = async (data: {
|
mockBranchTaskProcess = async (data: {
|
||||||
branch_Number: number
|
branch_Number: number
|
||||||
Modification_Requirement: string
|
Modification_Requirement: string
|
||||||
@@ -571,7 +668,6 @@ class Api {
|
|||||||
stepTaskExisting: any
|
stepTaskExisting: any
|
||||||
goal: string
|
goal: string
|
||||||
}): Promise<BranchAction[][]> => {
|
}): Promise<BranchAction[][]> => {
|
||||||
// 直接调用 Mock API(已经返回 BranchAction[][] 格式,与后端完全一致)
|
|
||||||
const response = await mockBranchTaskProcessAPI({
|
const response = await mockBranchTaskProcessAPI({
|
||||||
branch_Number: data.branch_Number,
|
branch_Number: data.branch_Number,
|
||||||
Modification_Requirement: data.Modification_Requirement,
|
Modification_Requirement: data.Modification_Requirement,
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ function resetTextareaHeight() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleSearch() {
|
async function handleSearch() {
|
||||||
|
// 用于标记大纲是否成功加载
|
||||||
|
let outlineLoaded = false
|
||||||
|
|
||||||
try {
|
try {
|
||||||
triggerOnFocus.value = false
|
triggerOnFocus.value = false
|
||||||
if (!searchValue.value) {
|
if (!searchValue.value) {
|
||||||
@@ -93,19 +96,104 @@ async function handleSearch() {
|
|||||||
emit('search-start')
|
emit('search-start')
|
||||||
agentsStore.resetAgent()
|
agentsStore.resetAgent()
|
||||||
agentsStore.setAgentRawPlan({ loading: true })
|
agentsStore.setAgentRawPlan({ loading: true })
|
||||||
const data = await api.generateBasePlan({
|
|
||||||
|
// 获取大纲
|
||||||
|
const outlineData = await api.generateBasePlan({
|
||||||
goal: searchValue.value,
|
goal: searchValue.value,
|
||||||
inputs: []
|
inputs: []
|
||||||
})
|
})
|
||||||
data['Collaboration Process'] = changeBriefs(data['Collaboration Process'])
|
|
||||||
agentsStore.setAgentRawPlan({ data })
|
|
||||||
|
|
||||||
|
// 处理简报数据格式
|
||||||
|
outlineData['Collaboration Process'] = changeBriefs(outlineData['Collaboration Process'])
|
||||||
|
|
||||||
|
// 立即显示大纲
|
||||||
|
agentsStore.setAgentRawPlan({ data: outlineData, loading: false })
|
||||||
|
outlineLoaded = true
|
||||||
emit('search', searchValue.value)
|
emit('search', searchValue.value)
|
||||||
|
|
||||||
|
// 并行填充所有步骤的详情
|
||||||
|
const steps = outlineData['Collaboration Process'] || []
|
||||||
|
|
||||||
|
// 带重试的填充函数
|
||||||
|
const fillStepWithRetry = async (step: any, retryCount = 0): Promise<void> => {
|
||||||
|
const maxRetries = 2 // 最多重试2次
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!step.StepName) {
|
||||||
|
console.warn('步骤缺少 StepName,跳过填充详情')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用现有的 fillStepTask API 填充每个步骤的详情
|
||||||
|
const detailedStep = await api.fillStepTask({
|
||||||
|
goal: searchValue.value,
|
||||||
|
stepTask: {
|
||||||
|
StepName: step.StepName,
|
||||||
|
TaskContent: step.TaskContent,
|
||||||
|
InputObject_List: step.InputObject_List,
|
||||||
|
OutputObject: step.OutputObject
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新该步骤的详情到 store
|
||||||
|
updateStepDetail(step.StepName, detailedStep)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
`填充步骤 ${step.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
|
||||||
|
// 如果未达到最大重试次数,延迟后重试
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
console.log(`正在重试步骤 ${step.StepName}...`)
|
||||||
|
// 延迟1秒后重试,避免立即重试导致同样的问题
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return fillStepWithRetry(step, retryCount + 1)
|
||||||
|
} else {
|
||||||
|
console.error(`步骤 ${step.StepName} 在 ${maxRetries + 1} 次尝试后仍然失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 为每个步骤并行填充详情(选人+过程)
|
||||||
|
// const fillPromises = steps.map(step => fillStepWithRetry(step))
|
||||||
|
// // 等待所有步骤填充完成(包括重试)
|
||||||
|
// await Promise.all(fillPromises)
|
||||||
|
|
||||||
|
// 串行填充所有步骤的详情(避免字段混乱)
|
||||||
|
for (const step of steps) {
|
||||||
|
await fillStepWithRetry(step)
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
triggerOnFocus.value = true
|
triggerOnFocus.value = true
|
||||||
|
// 如果大纲加载失败,确保关闭loading
|
||||||
|
if (!outlineLoaded) {
|
||||||
agentsStore.setAgentRawPlan({ loading: false })
|
agentsStore.setAgentRawPlan({ loading: false })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:更新单个步骤的详情
|
||||||
|
function updateStepDetail(stepId: string, detailedStep: any) {
|
||||||
|
const planData = agentsStore.agentRawPlan.data
|
||||||
|
if (!planData) return
|
||||||
|
|
||||||
|
const collaborationProcess = planData['Collaboration Process']
|
||||||
|
if (!collaborationProcess) return
|
||||||
|
|
||||||
|
const index = collaborationProcess.findIndex((s: any) => s.StepName === stepId)
|
||||||
|
if (index !== -1 && collaborationProcess[index]) {
|
||||||
|
// 保持响应式更新 - 使用 Vue 的响应式系统
|
||||||
|
Object.assign(collaborationProcess[index], {
|
||||||
|
AgentSelection: detailedStep.AgentSelection || [],
|
||||||
|
TaskProcess: detailedStep.TaskProcess || [],
|
||||||
|
Collaboration_Brief_frontEnd: detailedStep.Collaboration_Brief_frontEnd || {
|
||||||
|
template: '',
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
|
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
|
||||||
const results = queryString
|
const results = queryString
|
||||||
@@ -215,8 +303,6 @@ onMounted(() => {
|
|||||||
:deep(.el-autocomplete .el-textarea .el-textarea__inner) {
|
:deep(.el-autocomplete .el-textarea .el-textarea__inner) {
|
||||||
overflow-y: auto !important;
|
overflow-y: auto !important;
|
||||||
min-height: 56px !important;
|
min-height: 56px !important;
|
||||||
// overflow-y: hidden;
|
|
||||||
// background-color: black;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/confi
|
|||||||
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
||||||
import variables from '@/styles/variables.module.scss'
|
import variables from '@/styles/variables.module.scss'
|
||||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||||
import api from '@/api'
|
import api, { type StreamingEvent } from '@/api'
|
||||||
import ProcessCard from '../TaskProcess/ProcessCard.vue'
|
import ProcessCard from '../TaskProcess/ProcessCard.vue'
|
||||||
import ExecutePlan from './ExecutePlan.vue'
|
import ExecutePlan from './ExecutePlan.vue'
|
||||||
|
|
||||||
@@ -182,15 +182,149 @@ function createInternalLine(id?: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const executionProgress = ref({
|
||||||
|
currentStep: 0,
|
||||||
|
totalSteps: 0,
|
||||||
|
currentAction: 0,
|
||||||
|
totalActions: 0,
|
||||||
|
currentStepName: '',
|
||||||
|
message: '正在执行...'
|
||||||
|
})
|
||||||
|
|
||||||
async function handleRun() {
|
async function handleRun() {
|
||||||
|
// 清空之前的执行结果
|
||||||
|
agentsStore.setExecutePlan([])
|
||||||
|
const tempResults: any[] = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
const d = await api.executePlan(agentsStore.agentRawPlan.data!)
|
|
||||||
agentsStore.setExecutePlan(d)
|
// 使用优化版流式API(阶段1+2:步骤级流式 + 动作级智能并行)
|
||||||
} finally {
|
api.executePlanOptimized(
|
||||||
|
agentsStore.agentRawPlan.data!,
|
||||||
|
// onMessage: 处理每个事件
|
||||||
|
(event: StreamingEvent) => {
|
||||||
|
switch (event.type) {
|
||||||
|
case 'step_start':
|
||||||
|
// 步骤开始
|
||||||
|
executionProgress.value = {
|
||||||
|
currentStep: event.step_index + 1,
|
||||||
|
totalSteps: event.total_steps,
|
||||||
|
currentAction: 0,
|
||||||
|
totalActions: 0,
|
||||||
|
currentStepName: event.step_name,
|
||||||
|
message: `正在执行步骤 ${event.step_index + 1}/${event.total_steps}: ${
|
||||||
|
event.step_name
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
console.log(
|
||||||
|
`📋 步骤 ${event.step_index + 1}/${event.total_steps} 开始: ${event.step_name}`
|
||||||
|
)
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'action_complete':
|
||||||
|
// 动作完成
|
||||||
|
const parallelInfo = event.batch_info?.is_parallel
|
||||||
|
? ` [批次 ${event.batch_info!.batch_index + 1}, 并行 ${
|
||||||
|
event.batch_info!.batch_size
|
||||||
|
} 个]`
|
||||||
|
: ''
|
||||||
|
|
||||||
|
executionProgress.value = {
|
||||||
|
...executionProgress.value,
|
||||||
|
currentAction: event.completed_actions,
|
||||||
|
totalActions: event.total_actions,
|
||||||
|
message: `步骤 ${event.step_index + 1}/${executionProgress.value.totalSteps}: ${
|
||||||
|
event.step_name
|
||||||
|
} - 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`✅ 动作 ${event.completed_actions}/${event.total_actions} 完成${parallelInfo}: ${event.action_result.ActionType} by ${event.action_result.AgentName}`
|
||||||
|
)
|
||||||
|
|
||||||
|
// 实时更新到 store(找到对应的步骤并添加 ActionHistory)
|
||||||
|
const step = collaborationProcess.value.find(s => s.StepName === event.step_name)
|
||||||
|
if (step) {
|
||||||
|
const stepLogNode = tempResults.find(
|
||||||
|
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
|
||||||
|
)
|
||||||
|
if (!stepLogNode) {
|
||||||
|
// 创建步骤日志节点
|
||||||
|
const newStepLog = {
|
||||||
|
LogNodeType: 'step',
|
||||||
|
NodeId: event.step_name,
|
||||||
|
InputName_List: step.InputObject_List || [],
|
||||||
|
OutputName: step.OutputObject || '',
|
||||||
|
chatLog: [],
|
||||||
|
inputObject_Record: [],
|
||||||
|
ActionHistory: [event.action_result]
|
||||||
|
}
|
||||||
|
tempResults.push(newStepLog)
|
||||||
|
} else {
|
||||||
|
// 追加动作结果
|
||||||
|
stepLogNode.ActionHistory.push(event.action_result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 store
|
||||||
|
agentsStore.setExecutePlan([...tempResults])
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'step_complete':
|
||||||
|
// 步骤完成
|
||||||
|
console.log(`🎯 步骤完成: ${event.step_name}`)
|
||||||
|
|
||||||
|
// 更新步骤日志节点
|
||||||
|
const existingStepLog = tempResults.find(
|
||||||
|
r => r.NodeId === event.step_name && r.LogNodeType === 'step'
|
||||||
|
)
|
||||||
|
if (existingStepLog) {
|
||||||
|
existingStepLog.ActionHistory = event.step_log_node.ActionHistory
|
||||||
|
} else {
|
||||||
|
tempResults.push(event.step_log_node)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加对象日志节点
|
||||||
|
tempResults.push(event.object_log_node)
|
||||||
|
|
||||||
|
// 更新 store
|
||||||
|
agentsStore.setExecutePlan([...tempResults])
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'execution_complete':
|
||||||
|
// 执行完成
|
||||||
|
executionProgress.value.message = `执行完成!共 ${event.total_steps} 个步骤`
|
||||||
|
console.log(`🎉 执行完成,共 ${event.total_steps} 个步骤`)
|
||||||
|
|
||||||
|
// 确保所有结果都保存到 store
|
||||||
|
agentsStore.setExecutePlan([...tempResults])
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
// 错误
|
||||||
|
console.error('❌ 执行错误:', event.message)
|
||||||
|
executionProgress.value.message = `执行错误: ${event.message}`
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// onError: 处理错误
|
||||||
|
(error: Error) => {
|
||||||
|
console.error('❌ 流式执行错误:', error)
|
||||||
|
executionProgress.value.message = `执行失败: ${error.message}`
|
||||||
|
},
|
||||||
|
// onComplete: 执行完成
|
||||||
|
() => {
|
||||||
|
console.log('✅ 流式执行完成')
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('执行失败:', error)
|
||||||
|
executionProgress.value.message = '执行失败,请重试'
|
||||||
|
} finally {
|
||||||
|
// loading 会在 onComplete 中设置为 false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查看任务过程
|
// 查看任务过程
|
||||||
@@ -381,6 +515,15 @@ defineExpose({
|
|||||||
id="task-results"
|
id="task-results"
|
||||||
:class="{ 'is-running': agentsStore.executePlan.length > 0 }"
|
:class="{ 'is-running': agentsStore.executePlan.length > 0 }"
|
||||||
>
|
>
|
||||||
|
<!-- 执行进度提示 -->
|
||||||
|
<div v-if="loading" class="execution-progress-hint">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>{{ executionProgress.message }}</span>
|
||||||
|
<span v-if="executionProgress.totalSteps > 0" class="progress">
|
||||||
|
{{ executionProgress.currentStep }}/{{ executionProgress.totalSteps }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 标题与执行按钮 -->
|
<!-- 标题与执行按钮 -->
|
||||||
<div class="text-[18px] font-bold mb-[7px] flex justify-between items-center px-[20px]">
|
<div class="text-[18px] font-bold mb-[7px] flex justify-between items-center px-[20px]">
|
||||||
<span class="text-[var(--color-text-title-header)]">执行结果</span>
|
<span class="text-[var(--color-text-title-header)]">执行结果</span>
|
||||||
@@ -606,6 +749,48 @@ defineExpose({
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
// 执行进度提示样式
|
||||||
|
.execution-progress-hint {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 20px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
max-width: 400px;
|
||||||
|
|
||||||
|
.message {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#task-results.is-running {
|
#task-results.is-running {
|
||||||
--color-bg-detail-list: var(--color-bg-detail-list-run); // 直接指向 100 % 版本
|
--color-bg-detail-list: var(--color-bg-detail-list-run); // 直接指向 100 % 版本
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,20 +25,20 @@ defineEmits<{
|
|||||||
<!-- 背景那一根线 -->
|
<!-- 背景那一根线 -->
|
||||||
<div class="h-full bg-[var(--color-bg-flow)] w-[5px]">
|
<div class="h-full bg-[var(--color-bg-flow)] w-[5px]">
|
||||||
<!-- 顶部加号区域 -->
|
<!-- 顶部加号区域 -->
|
||||||
<div
|
<!-- <div
|
||||||
v-if="!isAdding"
|
v-if="!isAdding"
|
||||||
v-dev-only
|
v-dev-only
|
||||||
class="plus-area mt-[35px] ml-[-15px] w-[34px] h-[34px] flex items-center justify-center cursor-pointer rounded-full"
|
class="plus-area mt-[35px] ml-[-15px] w-[34px] h-[34px] flex items-center justify-center cursor-pointer rounded-full"
|
||||||
@click="$emit('start-add-output')"
|
@click="$emit('start-add-output')"
|
||||||
>
|
> -->
|
||||||
<!-- 加号图标 -->
|
<!-- 加号图标 -->
|
||||||
<svg-icon
|
<!-- <svg-icon
|
||||||
icon-class="plus"
|
icon-class="plus"
|
||||||
color="var(--color-text)"
|
color="var(--color-text)"
|
||||||
size="20px"
|
size="20px"
|
||||||
class="plus-icon opacity-0 transition-opacity duration-200"
|
class="plus-icon opacity-0 transition-opacity duration-200"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div> -->
|
||||||
<!-- 线底部的小圆球 -->
|
<!-- 线底部的小圆球 -->
|
||||||
<div
|
<div
|
||||||
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-flow)] w-[15px] h-[15px] rounded-full"
|
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-flow)] w-[15px] h-[15px] rounded-full"
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="plan-modification">
|
<div class="plan-modification">
|
||||||
|
<!-- 全局加载提示 -->
|
||||||
|
<div v-if="branchFillingStatus.isFilling" class="branch-loading-hint">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>{{ branchFillingStatus.message }}</span>
|
||||||
|
<span class="progress">{{ branchFillingStatus.current }}/{{ branchFillingStatus.total }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-loading="agentsStore.agentRawPlan.loading || branchLoading"
|
v-loading="agentsStore.agentRawPlan.loading || branchLoading"
|
||||||
class="flow-wrapper"
|
class="flow-wrapper"
|
||||||
@@ -49,6 +56,7 @@ import { VueFlow, useVueFlow } from '@vue-flow/core'
|
|||||||
import type { Node, Edge } from '@vue-flow/core'
|
import type { Node, Edge } from '@vue-flow/core'
|
||||||
import { useAgentsStore, useSelectionStore, type IRawStepTask } from '@/stores'
|
import { useAgentsStore, useSelectionStore, type IRawStepTask } from '@/stores'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
import '@vue-flow/core/dist/style.css'
|
import '@vue-flow/core/dist/style.css'
|
||||||
import '@vue-flow/core/dist/theme-default.css'
|
import '@vue-flow/core/dist/theme-default.css'
|
||||||
@@ -65,6 +73,14 @@ const selectionStore = useSelectionStore()
|
|||||||
// Mock 数据开关
|
// Mock 数据开关
|
||||||
const USE_MOCK_DATA = false
|
const USE_MOCK_DATA = false
|
||||||
|
|
||||||
|
// 分支详情填充状态
|
||||||
|
const branchFillingStatus = ref({
|
||||||
|
isFilling: false,
|
||||||
|
current: 0,
|
||||||
|
total: 0,
|
||||||
|
message: ''
|
||||||
|
})
|
||||||
|
|
||||||
// 获取协作流程数据
|
// 获取协作流程数据
|
||||||
const collaborationProcess = computed(() => {
|
const collaborationProcess = computed(() => {
|
||||||
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
||||||
@@ -782,8 +798,6 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// 直接获取协作流程数据
|
// 直接获取协作流程数据
|
||||||
// newTasks = response?.[0] || []
|
|
||||||
// 直接获取协作流程数据:兼容返回数组或对象的情况,优先处理二维数组返回
|
|
||||||
if (Array.isArray(response)) {
|
if (Array.isArray(response)) {
|
||||||
// 可能是二维数组
|
// 可能是二维数组
|
||||||
newTasks = (response as any[])[0] || []
|
newTasks = (response as any[])[0] || []
|
||||||
@@ -793,46 +807,22 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
} else {
|
} else {
|
||||||
newTasks = []
|
newTasks = []
|
||||||
}
|
}
|
||||||
// ========== 填充每个任务的 TaskProcess ==========
|
|
||||||
for (let i = 0; i < newTasks.length; i++) {
|
|
||||||
const task = newTasks[i]
|
|
||||||
if (!task) continue
|
|
||||||
|
|
||||||
try {
|
// ========== 立即创建节点显示大纲 ==========
|
||||||
const filledTask = await api.fillStepTask({
|
|
||||||
goal: generalGoal,
|
|
||||||
stepTask: task
|
|
||||||
})
|
|
||||||
// 更新任务的 AgentSelection 和 TaskProcess
|
|
||||||
newTasks[i]!.AgentSelection = filledTask.AgentSelection
|
|
||||||
newTasks[i]!.TaskProcess = filledTask.TaskProcess
|
|
||||||
newTasks[i]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ElMessage.success('任务大纲分支创建成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建多个任务节点,每个任务显示为单独的流程卡片
|
|
||||||
if (newTasks.length > 0) {
|
if (newTasks.length > 0) {
|
||||||
// 分支任务与主流程任务对齐:从第一个主流程任务的位置开始
|
|
||||||
const branchBaseX = START_X + ROOT_WIDTH + NODE_GAP
|
const branchBaseX = START_X + ROOT_WIDTH + NODE_GAP
|
||||||
const branchBaseY = START_Y + 300 // 向下偏移,与主流程垂直排列
|
const branchBaseY = START_Y + 300
|
||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const taskNodeIds: string[] = []
|
const taskNodeIds: string[] = []
|
||||||
|
|
||||||
// 为每个任务创建节点
|
// 为每个任务创建节点(只显示大纲)
|
||||||
newTasks.forEach((task, index) => {
|
newTasks.forEach((task, index) => {
|
||||||
const taskNodeId = `branch-task-${taskId}-${timestamp}-${index}`
|
const taskNodeId = `branch-task-${taskId}-${timestamp}-${index}`
|
||||||
taskNodeIds.push(taskNodeId)
|
taskNodeIds.push(taskNodeId)
|
||||||
|
|
||||||
// 计算位置:横向排列,与主流程任务对齐
|
|
||||||
const taskX = branchBaseX + index * (NODE_WIDTH + NODE_GAP)
|
const taskX = branchBaseX + index * (NODE_WIDTH + NODE_GAP)
|
||||||
const taskY = branchBaseY
|
const taskY = branchBaseY
|
||||||
|
|
||||||
// 创建任务节点(只显示 StepName)
|
|
||||||
const newTaskNode: Node = {
|
const newTaskNode: Node = {
|
||||||
id: taskNodeId,
|
id: taskNodeId,
|
||||||
type: 'task',
|
type: 'task',
|
||||||
@@ -841,9 +831,9 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
task,
|
task,
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
editingContent: '',
|
editingContent: '',
|
||||||
isBranchTask: true, // 标记为分支任务,可用于特殊样式
|
isBranchTask: true,
|
||||||
branchId: String(timestamp), // 添加分支ID,用于识别和分组
|
branchId: String(timestamp),
|
||||||
updateKey: Date.now() // 添加更新时间戳,用于强制组件重新渲染
|
updateKey: Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -852,7 +842,6 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
|
|
||||||
// 创建连接边
|
// 创建连接边
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
// 连接到父节点
|
|
||||||
const newEdge: Edge = {
|
const newEdge: Edge = {
|
||||||
id: `edge-${taskId}-${taskNodeId}`,
|
id: `edge-${taskId}-${taskNodeId}`,
|
||||||
source: taskId,
|
source: taskId,
|
||||||
@@ -873,7 +862,6 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
edges.value.push(newEdge)
|
edges.value.push(newEdge)
|
||||||
newBranchEdges.push(newEdge)
|
newBranchEdges.push(newEdge)
|
||||||
} else {
|
} else {
|
||||||
// 后续任务连接到前一个任务(左右连接)
|
|
||||||
const prevTaskNodeId = taskNodeIds[index - 1]
|
const prevTaskNodeId = taskNodeIds[index - 1]
|
||||||
const newEdge: Edge = {
|
const newEdge: Edge = {
|
||||||
id: `edge-${prevTaskNodeId}-${taskNodeId}`,
|
id: `edge-${prevTaskNodeId}-${taskNodeId}`,
|
||||||
@@ -896,10 +884,8 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
newBranchEdges.push(newEdge)
|
newBranchEdges.push(newEdge)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// 保存分支到 store
|
// 保存分支到 store(此时只有大纲)
|
||||||
if (newBranchNodes.length > 0) {
|
|
||||||
selectionStore.addFlowBranch({
|
selectionStore.addFlowBranch({
|
||||||
parentNodeId: taskId,
|
parentNodeId: taskId,
|
||||||
branchContent: branchContent,
|
branchContent: branchContent,
|
||||||
@@ -908,6 +894,82 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
edges: newBranchEdges,
|
edges: newBranchEdges,
|
||||||
tasks: JSON.parse(JSON.stringify(newTasks))
|
tasks: JSON.parse(JSON.stringify(newTasks))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ElMessage.success('任务大纲分支创建成功')
|
||||||
|
|
||||||
|
// 适应视图,让用户先看到大纲
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
fit({ padding: 100, duration: 300 })
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ========== 关闭遮罩层Loading ==========
|
||||||
|
branchLoading.value = false
|
||||||
|
|
||||||
|
// ========== 异步填充详情(后台进行)==========
|
||||||
|
branchFillingStatus.value = {
|
||||||
|
isFilling: true,
|
||||||
|
current: 0,
|
||||||
|
total: newTasks.length,
|
||||||
|
message: '正在生成分支任务协作流程...'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带重试的填充函数
|
||||||
|
const fillStepWithRetry = async (task: IRawStepTask, index: number, retryCount = 0): Promise<void> => {
|
||||||
|
const maxRetries = 2
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filledTask = await api.fillStepTask({
|
||||||
|
goal: generalGoal,
|
||||||
|
stepTask: task
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新任务的 AgentSelection 和 TaskProcess
|
||||||
|
newTasks[index]!.AgentSelection = filledTask.AgentSelection
|
||||||
|
newTasks[index]!.TaskProcess = filledTask.TaskProcess
|
||||||
|
newTasks[index]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
|
||||||
|
|
||||||
|
// 更新节点数据
|
||||||
|
const taskNodeId = taskNodeIds[index]
|
||||||
|
updateNode(taskNodeId, {
|
||||||
|
data: {
|
||||||
|
...findNode(taskNodeId)?.data,
|
||||||
|
task: { ...newTasks[index] },
|
||||||
|
updateKey: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
branchFillingStatus.value.current = index + 1
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, error)
|
||||||
|
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
// 延迟后重试
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return fillStepWithRetry(task, index, retryCount + 1)
|
||||||
|
} else {
|
||||||
|
console.error(`分支任务 ${task.StepName} 在 ${maxRetries + 1} 次尝试后仍然失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 串行填充所有任务的详情(避免并发问题导致字段混乱)
|
||||||
|
for (let i = 0; i < newTasks.length; i++) {
|
||||||
|
await fillStepWithRetry(newTasks[i], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 store 中的任务数据
|
||||||
|
const allBranches = selectionStore.getAllFlowBranches()
|
||||||
|
const currentBranch = allBranches.find(b => b.branchContent === branchContent && b.parentNodeId === taskId)
|
||||||
|
if (currentBranch) {
|
||||||
|
currentBranch.tasks = JSON.parse(JSON.stringify(newTasks))
|
||||||
|
}
|
||||||
|
|
||||||
|
branchFillingStatus.value.isFilling = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ========== 任务节点级别分支 ==========
|
// ========== 任务节点级别分支 ==========
|
||||||
@@ -1085,28 +1147,8 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
} else {
|
} else {
|
||||||
newTasks = []
|
newTasks = []
|
||||||
}
|
}
|
||||||
for (let i = 0; i < newTasks.length; i++) {
|
|
||||||
const task = newTasks[i]
|
|
||||||
if (!task) continue
|
|
||||||
|
|
||||||
try {
|
// ========== 立即创建节点显示大纲 ==========
|
||||||
const filledTask = await api.fillStepTask({
|
|
||||||
goal: generalGoal,
|
|
||||||
stepTask: task
|
|
||||||
})
|
|
||||||
// 更新任务的 AgentSelection 和 TaskProcess
|
|
||||||
newTasks[i]!.AgentSelection = filledTask.AgentSelection
|
|
||||||
newTasks[i]!.TaskProcess = filledTask.TaskProcess
|
|
||||||
newTasks[i]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ElMessage.success('任务大纲分支创建成功')
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建多个任务节点,每个任务显示为单独的流程卡片
|
|
||||||
if (newTasks.length > 0) {
|
if (newTasks.length > 0) {
|
||||||
// 计算分支起始位置:在父任务节点的右下方
|
// 计算分支起始位置:在父任务节点的右下方
|
||||||
const branchBaseX = parentNode.position.x + NODE_WIDTH + NODE_GAP
|
const branchBaseX = parentNode.position.x + NODE_WIDTH + NODE_GAP
|
||||||
@@ -1114,7 +1156,7 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
const timestamp = Date.now()
|
const timestamp = Date.now()
|
||||||
const taskNodeIds: string[] = []
|
const taskNodeIds: string[] = []
|
||||||
|
|
||||||
// 为每个任务创建节点
|
// 为每个任务创建节点(只显示大纲)
|
||||||
newTasks.forEach((task, index) => {
|
newTasks.forEach((task, index) => {
|
||||||
const taskNodeId = `branch-task-${taskId}-${timestamp}-${index}`
|
const taskNodeId = `branch-task-${taskId}-${timestamp}-${index}`
|
||||||
taskNodeIds.push(taskNodeId)
|
taskNodeIds.push(taskNodeId)
|
||||||
@@ -1123,7 +1165,7 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
const taskX = branchBaseX + index * (NODE_WIDTH + NODE_GAP)
|
const taskX = branchBaseX + index * (NODE_WIDTH + NODE_GAP)
|
||||||
const taskY = branchBaseY
|
const taskY = branchBaseY
|
||||||
|
|
||||||
// 创建任务节点(只显示 StepName)
|
// 创建任务节点
|
||||||
const newTaskNode: Node = {
|
const newTaskNode: Node = {
|
||||||
id: taskNodeId,
|
id: taskNodeId,
|
||||||
type: 'task',
|
type: 'task',
|
||||||
@@ -1132,8 +1174,8 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
task,
|
task,
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
editingContent: '',
|
editingContent: '',
|
||||||
isBranchTask: true, // 标记为分支任务,可用于特殊样式
|
isBranchTask: true,
|
||||||
updateKey: Date.now() // 添加更新时间戳,用于强制组件重新渲染
|
updateKey: Date.now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1186,10 +1228,8 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
newBranchEdges.push(newEdge)
|
newBranchEdges.push(newEdge)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
// 保存分支数据到 store
|
// 保存分支数据到 store(此时只有大纲)
|
||||||
if (newBranchNodes.length > 0) {
|
|
||||||
selectionStore.addFlowBranch({
|
selectionStore.addFlowBranch({
|
||||||
parentNodeId: taskId,
|
parentNodeId: taskId,
|
||||||
branchContent: branchContent,
|
branchContent: branchContent,
|
||||||
@@ -1198,6 +1238,82 @@ const handleAddBranch = async (taskId: string, branchContent: string) => {
|
|||||||
edges: newBranchEdges,
|
edges: newBranchEdges,
|
||||||
tasks: JSON.parse(JSON.stringify(newTasks))
|
tasks: JSON.parse(JSON.stringify(newTasks))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ElMessage.success('任务大纲分支创建成功')
|
||||||
|
|
||||||
|
// 适应视图,让用户先看到大纲
|
||||||
|
nextTick(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
fit({ padding: 100, duration: 300 })
|
||||||
|
}, 100)
|
||||||
|
})
|
||||||
|
|
||||||
|
// ========== 关闭遮罩层Loading ==========
|
||||||
|
branchLoading.value = false
|
||||||
|
|
||||||
|
// ========== 异步填充详情(后台进行)==========
|
||||||
|
branchFillingStatus.value = {
|
||||||
|
isFilling: true,
|
||||||
|
current: 0,
|
||||||
|
total: newTasks.length,
|
||||||
|
message: '正在生成分支任务协作流程...'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带重试的填充函数
|
||||||
|
const fillStepWithRetry = async (task: IRawStepTask, index: number, retryCount = 0): Promise<void> => {
|
||||||
|
const maxRetries = 2
|
||||||
|
|
||||||
|
try {
|
||||||
|
const filledTask = await api.fillStepTask({
|
||||||
|
goal: generalGoal,
|
||||||
|
stepTask: task
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新任务的 AgentSelection 和 TaskProcess
|
||||||
|
newTasks[index]!.AgentSelection = filledTask.AgentSelection
|
||||||
|
newTasks[index]!.TaskProcess = filledTask.TaskProcess
|
||||||
|
newTasks[index]!.Collaboration_Brief_frontEnd = filledTask.Collaboration_Brief_frontEnd
|
||||||
|
|
||||||
|
// 更新节点数据
|
||||||
|
const taskNodeId = taskNodeIds[index]
|
||||||
|
updateNode(taskNodeId, {
|
||||||
|
data: {
|
||||||
|
...findNode(taskNodeId)?.data,
|
||||||
|
task: { ...newTasks[index] },
|
||||||
|
updateKey: Date.now()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
branchFillingStatus.value.current = index + 1
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`填充分支任务 ${task.StepName} 详情失败 (尝试 ${retryCount + 1}/${maxRetries + 1}):`, error)
|
||||||
|
|
||||||
|
if (retryCount < maxRetries) {
|
||||||
|
// 延迟后重试
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
return fillStepWithRetry(task, index, retryCount + 1)
|
||||||
|
} else {
|
||||||
|
console.error(`分支任务 ${task.StepName} 在 ${maxRetries + 1} 次尝试后仍然失败`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 串行填充所有任务的详情(避免并发问题导致字段混乱)
|
||||||
|
for (let i = 0; i < newTasks.length; i++) {
|
||||||
|
await fillStepWithRetry(newTasks[i], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 store 中的任务数据
|
||||||
|
const allBranches = selectionStore.getAllFlowBranches()
|
||||||
|
const currentBranch = allBranches.find(b => b.branchContent === branchContent && b.parentNodeId === taskId)
|
||||||
|
if (currentBranch) {
|
||||||
|
currentBranch.tasks = JSON.parse(JSON.stringify(newTasks))
|
||||||
|
}
|
||||||
|
|
||||||
|
branchFillingStatus.value.isFilling = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1297,4 +1413,38 @@ defineExpose({
|
|||||||
:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
|
:deep(.vue-flow__edge.selected .vue-flow__edge-path) {
|
||||||
stroke: #409eff;
|
stroke: #409eff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分支加载提示样式
|
||||||
|
.branch-loading-hint {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 20px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/
|
|||||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||||
import { computed, ref, nextTick, watch, onMounted } from 'vue'
|
import { computed, ref, nextTick, watch, onMounted } from 'vue'
|
||||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||||
|
import { Loading } from '@element-plus/icons-vue'
|
||||||
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
|
||||||
import Bg from './Bg.vue'
|
import Bg from './Bg.vue'
|
||||||
import BranchButton from './components/BranchButton.vue'
|
import BranchButton from './components/BranchButton.vue'
|
||||||
@@ -37,6 +38,22 @@ const collaborationProcess = computed(() => {
|
|||||||
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 检测是否正在填充详情(有步骤但没有 AgentSelection)
|
||||||
|
const isFillingDetails = computed(() => {
|
||||||
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||||
|
return process.length > 0 && process.some(step => !step.AgentSelection || step.AgentSelection.length === 0)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 计算填充进度
|
||||||
|
const completedSteps = computed(() => {
|
||||||
|
const process = agentsStore.agentRawPlan.data?.['Collaboration Process'] || []
|
||||||
|
return process.filter(step => step.AgentSelection && step.AgentSelection.length > 0).length
|
||||||
|
})
|
||||||
|
|
||||||
|
const totalSteps = computed(() => {
|
||||||
|
return agentsStore.agentRawPlan.data?.['Collaboration Process']?.length || 0
|
||||||
|
})
|
||||||
|
|
||||||
// 编辑状态管理
|
// 编辑状态管理
|
||||||
const editingTaskId = ref<string | null>(null)
|
const editingTaskId = ref<string | null>(null)
|
||||||
const editingContent = ref('')
|
const editingContent = ref('')
|
||||||
@@ -258,6 +275,13 @@ defineExpose({
|
|||||||
任务大纲
|
任务大纲
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 加载详情提示 -->
|
||||||
|
<div v-if="isFillingDetails" class="detail-loading-hint">
|
||||||
|
<el-icon class="is-loading"><Loading /></el-icon>
|
||||||
|
<span>正在生成任务协作流程...</span>
|
||||||
|
<span class="progress">{{ completedSteps }}/{{ totalSteps }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
v-loading="agentsStore.agentRawPlan.loading"
|
v-loading="agentsStore.agentRawPlan.loading"
|
||||||
class="flex-1 w-full overflow-y-auto relative"
|
class="flex-1 w-full overflow-y-auto relative"
|
||||||
@@ -556,6 +580,40 @@ defineExpose({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载详情提示样式
|
||||||
|
.detail-loading-hint {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
right: 20px;
|
||||||
|
background: var(--el-bg-color);
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
z-index: 1000;
|
||||||
|
animation: slideInRight 0.3s ease-out;
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideInRight {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 输入框样式
|
// 输入框样式
|
||||||
:deep(.el-input__wrapper) {
|
:deep(.el-input__wrapper) {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
Reference in New Issue
Block a user