feat:1.数据库存储功能添加(初版)2.后端REST API版本代码清理
This commit is contained in:
@@ -49,7 +49,6 @@ def LLM_Completion(
|
||||
messages: list[dict], stream: bool = True, useGroq: bool = True,model_config: dict = None
|
||||
) -> str:
|
||||
if model_config:
|
||||
print_colored(f"Using model config: {model_config}", "blue")
|
||||
return _call_with_custom_config(messages,stream,model_config)
|
||||
if not useGroq or not FAST_DESIGN_MODE:
|
||||
force_gpt4 = True
|
||||
@@ -125,7 +124,7 @@ def _call_with_custom_config(messages: list[dict], stream: bool, model_config: d
|
||||
#print(colored(full_reply_content, "blue", "on_white"), end="")
|
||||
return full_reply_content
|
||||
except Exception as e:
|
||||
print_colored(f"Custom API error for model {api_model} :{str(e)}","red")
|
||||
print_colored(f"[API Error] Custom API error: {str(e)}", "red")
|
||||
raise
|
||||
|
||||
|
||||
@@ -166,11 +165,11 @@ async def _achat_completion_stream_custom(messages:list[dict], temp_async_client
|
||||
except httpx.RemoteProtocolError as e:
|
||||
if attempt < max_retries - 1:
|
||||
wait_time = (attempt + 1) *2
|
||||
print_colored(f"⚠️ Stream connection interrupted (attempt {attempt+1}/{max_retries}). Retrying in {wait_time}s...", text_color="yellow")
|
||||
print_colored(f"[API Warn] Stream connection interrupted, retrying in {wait_time}s...", "yellow")
|
||||
await asyncio.sleep(wait_time)
|
||||
continue
|
||||
except Exception as e:
|
||||
print_colored(f"Custom API stream error for model {api_model} :{str(e)}","red")
|
||||
print_colored(f"[API Error] Custom API stream error: {str(e)}", "red")
|
||||
raise
|
||||
|
||||
|
||||
@@ -181,7 +180,6 @@ async def _achat_completion_stream_groq(messages: list[dict]) -> str:
|
||||
max_attempts = 5
|
||||
|
||||
for attempt in range(max_attempts):
|
||||
print("Attempt to use Groq (Fase Design Mode):")
|
||||
try:
|
||||
response = await groq_client.chat.completions.create(
|
||||
messages=messages,
|
||||
@@ -363,7 +361,7 @@ async def _achat_completion_stream(messages: list[dict]) -> str:
|
||||
|
||||
return full_reply_content
|
||||
except Exception as e:
|
||||
print_colored(f"OpenAI API error in _achat_completion_stream: {str(e)}", "red")
|
||||
print_colored(f"[API Error] OpenAI API stream error: {str(e)}", "red")
|
||||
raise
|
||||
|
||||
|
||||
@@ -383,7 +381,7 @@ def _chat_completion(messages: list[dict]) -> str:
|
||||
|
||||
return content
|
||||
except Exception as e:
|
||||
print_colored(f"OpenAI API error in _chat_completion: {str(e)}", "red")
|
||||
print_colored(f"[API Error] OpenAI API error: {str(e)}", "red")
|
||||
raise
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ def generate_AbilityRequirement(General_Goal, Current_Task):
|
||||
),
|
||||
},
|
||||
]
|
||||
print(messages[1]["content"])
|
||||
return read_LLM_Completion(messages)["AbilityRequirement"]
|
||||
|
||||
|
||||
@@ -106,6 +105,7 @@ def generate_AgentSelection(General_Goal, Current_Task, Agent_Board):
|
||||
|
||||
while True:
|
||||
candidate = read_LLM_Completion(messages)["AgentSelectionPlan"]
|
||||
# 添加调试打印
|
||||
if len(candidate) > MAX_TEAM_SIZE:
|
||||
teamSize = random.randint(2, MAX_TEAM_SIZE)
|
||||
candidate = candidate[0:teamSize]
|
||||
@@ -113,7 +113,6 @@ def generate_AgentSelection(General_Goal, Current_Task, Agent_Board):
|
||||
continue
|
||||
AgentSelectionPlan = sorted(candidate)
|
||||
AgentSelectionPlan_set = set(AgentSelectionPlan)
|
||||
|
||||
# Check if every item in AgentSelectionPlan is in agentboard
|
||||
if AgentSelectionPlan_set.issubset(agentboard_set):
|
||||
break # If all items are in agentboard, break the loop
|
||||
|
||||
@@ -85,7 +85,6 @@ class BaseAction():
|
||||
# Handle missing agent profiles gracefully
|
||||
model_config = None
|
||||
if agentName not in AgentProfile_Dict:
|
||||
print_colored(text=f"Warning: Agent '{agentName}' not found in AgentProfile_Dict. Using default profile.", text_color="yellow")
|
||||
agentProfile = f"AI Agent named {agentName}"
|
||||
else:
|
||||
# agentProfile = AgentProfile_Dict[agentName]
|
||||
|
||||
@@ -83,7 +83,6 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
|
||||
}
|
||||
|
||||
# start the group chat
|
||||
util.print_colored(TaskDescription, text_color="green")
|
||||
ActionHistory = []
|
||||
action_count = 0
|
||||
total_actions = len(TaskProcess)
|
||||
@@ -93,9 +92,6 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
|
||||
actionType = ActionInfo["ActionType"]
|
||||
agentName = ActionInfo["AgentName"]
|
||||
|
||||
# 添加进度日志
|
||||
util.print_colored(f"🔄 Executing action {action_count}/{total_actions}: {actionType} by {agentName}", text_color="yellow")
|
||||
|
||||
if actionType in Action.customAction_Dict:
|
||||
currentAction = Action.customAction_Dict[actionType](
|
||||
info=ActionInfo,
|
||||
@@ -126,11 +122,4 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
|
||||
stepLogNode["ActionHistory"] = ActionHistory
|
||||
|
||||
# Return Output
|
||||
print(
|
||||
colored(
|
||||
"$Run " + str(StepRun_count) + "step$",
|
||||
color="black",
|
||||
on_color="on_white",
|
||||
)
|
||||
)
|
||||
return RehearsalLog
|
||||
|
||||
@@ -107,7 +107,6 @@ def get_parallel_batches(TaskProcess: List[Dict], dependency_map: Dict[int, List
|
||||
# 避免死循环
|
||||
remaining = [i for i in range(len(TaskProcess)) if i not in completed]
|
||||
if remaining:
|
||||
print(colored(f"警告: 检测到循环依赖,强制串行执行: {remaining}", "yellow"))
|
||||
ready_to_run = remaining[:1]
|
||||
else:
|
||||
break
|
||||
@@ -188,7 +187,8 @@ async def execute_step_async_streaming(
|
||||
KeyObjects: Dict,
|
||||
step_index: int,
|
||||
total_steps: int,
|
||||
execution_id: str = None
|
||||
execution_id: str = None,
|
||||
RehearsalLog: List = None # 用于追加日志到历史记录
|
||||
) -> Generator[Dict, None, None]:
|
||||
"""
|
||||
异步执行单个步骤,支持流式返回
|
||||
@@ -276,8 +276,9 @@ async def execute_step_async_streaming(
|
||||
total_actions = len(TaskProcess)
|
||||
completed_actions = 0
|
||||
|
||||
# 步骤开始日志
|
||||
util.print_colored(
|
||||
f"📋 步骤 {step_index + 1}/{total_steps}: {StepName} ({total_actions} 个动作, 分 {len(batches)} 批并行执行)",
|
||||
f"📋 步骤 {step_index + 1}/{total_steps}: {StepName} ({total_actions} 个动作, 分 {len(batches)} 批执行)",
|
||||
text_color="cyan"
|
||||
)
|
||||
|
||||
@@ -286,11 +287,11 @@ async def execute_step_async_streaming(
|
||||
# 在每个批次执行前检查暂停状态
|
||||
should_continue = await execution_state_manager.async_check_pause(execution_id)
|
||||
if not should_continue:
|
||||
util.print_colored("🛑 用户请求停止执行", "red")
|
||||
return
|
||||
|
||||
batch_size = len(batch_indices)
|
||||
|
||||
# 批次执行日志
|
||||
if batch_size > 1:
|
||||
util.print_colored(
|
||||
f"🚦 批次 {batch_index + 1}/{len(batches)}: 并行执行 {batch_size} 个动作",
|
||||
@@ -298,7 +299,7 @@ async def execute_step_async_streaming(
|
||||
)
|
||||
else:
|
||||
util.print_colored(
|
||||
f"🔄 动作 {completed_actions + 1}/{total_actions}: 串行执行",
|
||||
f"🔄 批次 {batch_index + 1}/{len(batches)}: 串行执行",
|
||||
text_color="yellow"
|
||||
)
|
||||
|
||||
@@ -353,12 +354,21 @@ async def execute_step_async_streaming(
|
||||
objectLogNode["content"] = KeyObjects[OutputName]
|
||||
stepLogNode["ActionHistory"] = ActionHistory
|
||||
|
||||
# 收集该步骤使用的 agent(去重)
|
||||
assigned_agents_in_step = list(set(Agent_List)) if Agent_List else []
|
||||
|
||||
# 追加到 RehearsalLog(因为 RehearsalLog 是可变对象,会反映到原列表)
|
||||
if RehearsalLog is not None:
|
||||
RehearsalLog.append(stepLogNode)
|
||||
RehearsalLog.append(objectLogNode)
|
||||
|
||||
yield {
|
||||
"type": "step_complete",
|
||||
"step_index": step_index,
|
||||
"step_name": StepName,
|
||||
"step_log_node": stepLogNode,
|
||||
"object_log_node": objectLogNode,
|
||||
"assigned_agents": {StepName: assigned_agents_in_step}, # 该步骤使用的 agent
|
||||
}
|
||||
|
||||
|
||||
@@ -394,8 +404,6 @@ def executePlan_streaming_dynamic(
|
||||
|
||||
execution_state_manager.start_execution(execution_id, general_goal)
|
||||
|
||||
print(colored(f"⏸️ 执行状态管理器已启动,支持暂停/恢复,execution_id={execution_id}", "green"))
|
||||
|
||||
# 准备执行
|
||||
KeyObjects = existingKeyObjects.copy() if existingKeyObjects else {}
|
||||
finishedStep_index = -1
|
||||
@@ -406,9 +414,6 @@ def executePlan_streaming_dynamic(
|
||||
if logNode["LogNodeType"] == "object":
|
||||
KeyObjects[logNode["NodeId"]] = logNode["content"]
|
||||
|
||||
if existingKeyObjects:
|
||||
print(colored(f"📦 使用已存在的 KeyObjects: {list(existingKeyObjects.keys())}", "cyan"))
|
||||
|
||||
# 确定要运行的步骤范围
|
||||
if num_StepToRun is None:
|
||||
run_to = len(plan["Collaboration Process"])
|
||||
@@ -421,9 +426,6 @@ def executePlan_streaming_dynamic(
|
||||
if execution_id:
|
||||
# 初始化执行管理器,使用传入的execution_id
|
||||
actual_execution_id = dynamic_execution_manager.start_execution(general_goal, steps_to_run, execution_id)
|
||||
print(colored(f"🚀 开始执行计划(动态模式),共 {len(steps_to_run)} 个步骤,执行ID: {actual_execution_id}", "cyan"))
|
||||
else:
|
||||
print(colored(f"🚀 开始执行计划(流式推送),共 {len(steps_to_run)} 个步骤", "cyan"))
|
||||
|
||||
total_steps = len(steps_to_run)
|
||||
|
||||
@@ -443,7 +445,7 @@ def executePlan_streaming_dynamic(
|
||||
# 检查暂停状态
|
||||
should_continue = await execution_state_manager.async_check_pause(execution_id)
|
||||
if not should_continue:
|
||||
print(colored("🛑 用户请求停止执行", "red"))
|
||||
util.print_colored("🛑 用户请求停止执行", "red")
|
||||
await queue.put({
|
||||
"type": "error",
|
||||
"message": "执行已被用户停止"
|
||||
@@ -466,29 +468,24 @@ def executePlan_streaming_dynamic(
|
||||
|
||||
# 如果没有步骤在队列中(queue_total_steps为0),立即退出
|
||||
if queue_total_steps == 0:
|
||||
print(colored(f"⚠️ 没有步骤在队列中,退出执行", "yellow"))
|
||||
break
|
||||
|
||||
# 如果所有步骤都已完成,等待可能的新步骤
|
||||
if completed_steps >= queue_total_steps:
|
||||
if empty_wait_count >= max_empty_wait_cycles:
|
||||
# 等待超时,退出执行
|
||||
print(colored(f"✅ 所有步骤执行完成,等待超时", "green"))
|
||||
break
|
||||
else:
|
||||
# 等待新步骤追加
|
||||
print(colored(f"⏳ 等待新步骤追加... ({empty_wait_count}/{max_empty_wait_cycles})", "cyan"))
|
||||
await asyncio.sleep(1)
|
||||
continue
|
||||
else:
|
||||
# 还有步骤未完成,继续尝试获取
|
||||
print(colored(f"⏳ 等待步骤就绪... ({completed_steps}/{queue_total_steps})", "cyan"))
|
||||
await asyncio.sleep(0.5)
|
||||
empty_wait_count = 0 # 重置等待计数
|
||||
continue
|
||||
else:
|
||||
# 执行信息不存在,退出
|
||||
print(colored(f"⚠️ 执行信息不存在,退出执行", "yellow"))
|
||||
break
|
||||
|
||||
# 重置等待计数
|
||||
@@ -506,7 +503,8 @@ def executePlan_streaming_dynamic(
|
||||
KeyObjects,
|
||||
step_index,
|
||||
current_total_steps, # 使用动态更新的总步骤数
|
||||
execution_id
|
||||
execution_id,
|
||||
RehearsalLog # 传递 RehearsalLog 用于追加日志
|
||||
):
|
||||
if execution_state_manager.is_stopped(execution_id):
|
||||
await queue.put({
|
||||
@@ -533,7 +531,7 @@ def executePlan_streaming_dynamic(
|
||||
for step_index, stepDescrip in enumerate(steps_to_run):
|
||||
should_continue = await execution_state_manager.async_check_pause(execution_id)
|
||||
if not should_continue:
|
||||
print(colored("🛑 用户请求停止执行", "red"))
|
||||
util.print_colored("🛑 用户请求停止执行", "red")
|
||||
await queue.put({
|
||||
"type": "error",
|
||||
"message": "执行已被用户停止"
|
||||
@@ -547,7 +545,8 @@ def executePlan_streaming_dynamic(
|
||||
KeyObjects,
|
||||
step_index,
|
||||
total_steps,
|
||||
execution_id
|
||||
execution_id,
|
||||
RehearsalLog # 传递 RehearsalLog 用于追加日志
|
||||
):
|
||||
if execution_state_manager.is_stopped(execution_id):
|
||||
await queue.put({
|
||||
|
||||
@@ -63,10 +63,7 @@ class DynamicExecutionManager:
|
||||
# 初始化待执行步骤索引
|
||||
self._pending_steps[execution_id] = list(range(len(initial_steps)))
|
||||
|
||||
print(f"🚀 启动执行: {execution_id}")
|
||||
print(f"📊 初始步骤数: {len(initial_steps)}")
|
||||
print(f"📋 待执行步骤索引: {self._pending_steps[execution_id]}")
|
||||
|
||||
print(f"[Execution] 启动执行: {execution_id}, 步骤数={len(initial_steps)}")
|
||||
return execution_id
|
||||
|
||||
def add_steps(self, execution_id: str, new_steps: List[Dict]) -> int:
|
||||
@@ -82,7 +79,6 @@ class DynamicExecutionManager:
|
||||
"""
|
||||
with self._lock:
|
||||
if execution_id not in self._step_queues:
|
||||
print(f"⚠️ 警告: 执行ID {execution_id} 不存在,无法追加步骤")
|
||||
return 0
|
||||
|
||||
current_count = len(self._step_queues[execution_id])
|
||||
@@ -95,14 +91,9 @@ class DynamicExecutionManager:
|
||||
self._pending_steps[execution_id].extend(new_indices)
|
||||
|
||||
# 更新总步骤数
|
||||
old_total = self._executions[execution_id]["total_steps"]
|
||||
self._executions[execution_id]["total_steps"] = len(self._step_queues[execution_id])
|
||||
new_total = self._executions[execution_id]["total_steps"]
|
||||
|
||||
print(f"➕ 追加了 {len(new_steps)} 个步骤到 {execution_id}")
|
||||
print(f"📊 步骤总数: {old_total} -> {new_total}")
|
||||
print(f"📋 待执行步骤索引: {self._pending_steps[execution_id]}")
|
||||
|
||||
print(f"[Execution] 追加步骤: +{len(new_steps)}, 总计={self._executions[execution_id]['total_steps']}")
|
||||
return len(new_steps)
|
||||
|
||||
def get_next_step(self, execution_id: str) -> Optional[Dict]:
|
||||
@@ -117,7 +108,6 @@ class DynamicExecutionManager:
|
||||
"""
|
||||
with self._lock:
|
||||
if execution_id not in self._pending_steps:
|
||||
print(f"⚠️ 警告: 执行ID {execution_id} 不存在")
|
||||
return None
|
||||
|
||||
# 获取第一个待执行步骤的索引
|
||||
@@ -128,7 +118,6 @@ class DynamicExecutionManager:
|
||||
|
||||
# 从队列中获取步骤
|
||||
if step_index >= len(self._step_queues[execution_id]):
|
||||
print(f"⚠️ 警告: 步骤索引 {step_index} 超出范围")
|
||||
return None
|
||||
|
||||
step = self._step_queues[execution_id][step_index]
|
||||
@@ -137,9 +126,7 @@ class DynamicExecutionManager:
|
||||
self._executed_steps[execution_id].add(step_index)
|
||||
|
||||
step_name = step.get("StepName", "未知")
|
||||
print(f"🎯 获取下一个步骤: {step_name} (索引: {step_index})")
|
||||
print(f"📋 剩余待执行步骤: {len(self._pending_steps[execution_id])}")
|
||||
|
||||
print(f"[Execution] 获取步骤: {step_name} (索引: {step_index})")
|
||||
return step
|
||||
|
||||
def mark_step_completed(self, execution_id: str):
|
||||
@@ -154,9 +141,7 @@ class DynamicExecutionManager:
|
||||
self._executions[execution_id]["completed_steps"] += 1
|
||||
completed = self._executions[execution_id]["completed_steps"]
|
||||
total = self._executions[execution_id]["total_steps"]
|
||||
print(f"📊 步骤完成进度: {completed}/{total}")
|
||||
else:
|
||||
print(f"⚠️ 警告: 执行ID {execution_id} 不存在")
|
||||
print(f"[Execution] 步骤完成: {completed}/{total}")
|
||||
|
||||
def get_execution_info(self, execution_id: str) -> Optional[Dict]:
|
||||
"""
|
||||
|
||||
@@ -129,7 +129,6 @@ class ExecutionStateManager:
|
||||
state['goal'] = goal
|
||||
state['should_pause'] = False
|
||||
state['should_stop'] = False
|
||||
print(f"🚀 [DEBUG] start_execution: execution_id={execution_id}, 状态设置为 RUNNING, goal={goal}")
|
||||
|
||||
def pause_execution(self, execution_id: str) -> bool:
|
||||
"""
|
||||
@@ -143,19 +142,13 @@ class ExecutionStateManager:
|
||||
"""
|
||||
state = self._get_state(execution_id)
|
||||
if state is None:
|
||||
# 打印当前所有活跃的 execution_id,帮助调试
|
||||
active_ids = list(self._states.keys())
|
||||
print(f"⚠️ [DEBUG] pause_execution: execution_id={execution_id} 不存在")
|
||||
print(f" 当前活跃的 execution_id 列表: {active_ids}")
|
||||
return False
|
||||
|
||||
with self._get_lock(execution_id):
|
||||
if state['status'] != ExecutionStatus.RUNNING:
|
||||
print(f"⚠️ [DEBUG] pause_execution: execution_id={execution_id}, 当前状态是 {state['status']},无法暂停")
|
||||
return False
|
||||
state['status'] = ExecutionStatus.PAUSED
|
||||
state['should_pause'] = True
|
||||
print(f"⏸️ [DEBUG] pause_execution: execution_id={execution_id}, 状态设置为PAUSED")
|
||||
return True
|
||||
|
||||
def resume_execution(self, execution_id: str) -> bool:
|
||||
@@ -170,16 +163,13 @@ class ExecutionStateManager:
|
||||
"""
|
||||
state = self._get_state(execution_id)
|
||||
if state is None:
|
||||
print(f"⚠️ [DEBUG] resume_execution: execution_id={execution_id} 不存在")
|
||||
return False
|
||||
|
||||
with self._get_lock(execution_id):
|
||||
if state['status'] != ExecutionStatus.PAUSED:
|
||||
print(f"⚠️ [DEBUG] resume_execution: 当前状态不是PAUSED,而是 {state['status']}")
|
||||
return False
|
||||
state['status'] = ExecutionStatus.RUNNING
|
||||
state['should_pause'] = False
|
||||
print(f"▶️ [DEBUG] resume_execution: execution_id={execution_id}, 状态设置为RUNNING, should_pause=False")
|
||||
return True
|
||||
|
||||
def stop_execution(self, execution_id: str) -> bool:
|
||||
@@ -194,17 +184,14 @@ class ExecutionStateManager:
|
||||
"""
|
||||
state = self._get_state(execution_id)
|
||||
if state is None:
|
||||
print(f"⚠️ [DEBUG] stop_execution: execution_id={execution_id} 不存在")
|
||||
return False
|
||||
|
||||
with self._get_lock(execution_id):
|
||||
if state['status'] in [ExecutionStatus.IDLE, ExecutionStatus.STOPPED]:
|
||||
print(f"⚠️ [DEBUG] stop_execution: 当前状态是 {state['status']}, 无法停止")
|
||||
return False
|
||||
state['status'] = ExecutionStatus.STOPPED
|
||||
state['should_stop'] = True
|
||||
state['should_pause'] = False
|
||||
print(f"🛑 [DEBUG] stop_execution: execution_id={execution_id}, 状态设置为STOPPED")
|
||||
return True
|
||||
|
||||
def reset(self, execution_id: str):
|
||||
@@ -215,12 +202,10 @@ class ExecutionStateManager:
|
||||
state['goal'] = None
|
||||
state['should_pause'] = False
|
||||
state['should_stop'] = False
|
||||
print(f"🔄 [DEBUG] reset: execution_id={execution_id}, 状态重置为IDLE")
|
||||
|
||||
def cleanup(self, execution_id: str):
|
||||
"""清理指定 execution_id 的所有状态"""
|
||||
self._cleanup_state(execution_id)
|
||||
print(f"🧹 [DEBUG] cleanup: execution_id={execution_id} 的状态已清理")
|
||||
|
||||
async def async_check_pause(self, execution_id: str):
|
||||
"""
|
||||
@@ -248,7 +233,6 @@ class ExecutionStateManager:
|
||||
|
||||
# 检查停止标志
|
||||
if should_stop:
|
||||
print("🛑 [DEBUG] async_check_pause: execution_id={}, 检测到停止信号".format(execution_id))
|
||||
return False
|
||||
|
||||
# 检查暂停状态
|
||||
@@ -262,7 +246,6 @@ class ExecutionStateManager:
|
||||
should_stop = state['should_stop']
|
||||
|
||||
if not should_pause:
|
||||
print("▶️ [DEBUG] async_check_pause: execution_id={}, 从暂停中恢复!".format(execution_id))
|
||||
continue
|
||||
if should_stop:
|
||||
return False
|
||||
|
||||
@@ -113,7 +113,6 @@ class GenerationStateManager:
|
||||
state['status'] = GenerationStatus.GENERATING
|
||||
state['goal'] = goal
|
||||
state['should_stop'] = False
|
||||
print(f"🚀 [GenerationState] start_generation: generation_id={generation_id}, 状态设置为 GENERATING")
|
||||
|
||||
def stop_generation(self, generation_id: str) -> bool:
|
||||
"""
|
||||
@@ -127,26 +126,21 @@ class GenerationStateManager:
|
||||
"""
|
||||
state = self._get_state(generation_id)
|
||||
if state is None:
|
||||
print(f"⚠️ [GenerationState] stop_generation: generation_id={generation_id} 不存在")
|
||||
return True # 不存在也算停止成功
|
||||
|
||||
with self._get_lock(generation_id):
|
||||
if state['status'] == GenerationStatus.STOPPED:
|
||||
print(f"✅ [GenerationState] stop_generation: generation_id={generation_id} 已经是 STOPPED 状态")
|
||||
return True # 已经停止也算成功
|
||||
|
||||
if state['status'] == GenerationStatus.COMPLETED:
|
||||
print(f"✅ [GenerationState] stop_generation: generation_id={generation_id} 已经 COMPLETED,视为停止成功")
|
||||
return True # 已完成也视为停止成功
|
||||
|
||||
if state['status'] == GenerationStatus.IDLE:
|
||||
print(f"⚠️ [GenerationState] stop_generation: generation_id={generation_id} 是 IDLE 状态,无需停止")
|
||||
return True # 空闲状态也视为无需停止
|
||||
|
||||
# 真正需要停止的情况
|
||||
state['status'] = GenerationStatus.STOPPED
|
||||
state['should_stop'] = True
|
||||
print(f"🛑 [GenerationState] stop_generation: generation_id={generation_id}, 状态设置为STOPPED")
|
||||
return True
|
||||
|
||||
def complete_generation(self, generation_id: str):
|
||||
@@ -154,12 +148,10 @@ class GenerationStateManager:
|
||||
state = self._ensure_state(generation_id)
|
||||
with self._get_lock(generation_id):
|
||||
state['status'] = GenerationStatus.COMPLETED
|
||||
print(f"✅ [GenerationState] complete_generation: generation_id={generation_id}")
|
||||
|
||||
def cleanup(self, generation_id: str):
|
||||
"""清理指定 generation_id 的所有状态"""
|
||||
self._cleanup_state(generation_id)
|
||||
print(f"🧹 [GenerationState] cleanup: generation_id={generation_id} 的状态已清理")
|
||||
|
||||
def should_stop(self, generation_id: str) -> bool:
|
||||
"""检查是否应该停止"""
|
||||
|
||||
@@ -94,6 +94,7 @@ def remove_render_spec(duty_spec):
|
||||
return duty_spec
|
||||
|
||||
|
||||
|
||||
def read_LLM_Completion(messages, useGroq=True):
|
||||
for _ in range(3):
|
||||
text = LLM_Completion(messages, useGroq=useGroq)
|
||||
|
||||
25
backend/db/__init__.py
Normal file
25
backend/db/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
AgentCoord 数据库模块
|
||||
提供 PostgreSQL 数据库连接、模型和 CRUD 操作
|
||||
基于 DATABASE_DESIGN.md 设计
|
||||
"""
|
||||
|
||||
from .database import get_db, get_db_context, test_connection, engine, text
|
||||
from .models import MultiAgentTask, UserAgent, TaskStatus
|
||||
from .crud import MultiAgentTaskCRUD, UserAgentCRUD
|
||||
|
||||
__all__ = [
|
||||
# 连接管理
|
||||
"get_db",
|
||||
"get_db_context",
|
||||
"test_connection",
|
||||
"engine",
|
||||
"text",
|
||||
# 模型
|
||||
"MultiAgentTask",
|
||||
"UserAgent",
|
||||
"TaskStatus",
|
||||
# CRUD
|
||||
"MultiAgentTaskCRUD",
|
||||
"UserAgentCRUD",
|
||||
]
|
||||
404
backend/db/crud.py
Normal file
404
backend/db/crud.py
Normal file
@@ -0,0 +1,404 @@
|
||||
"""
|
||||
数据库 CRUD 操作
|
||||
封装所有数据库操作方法 (基于 DATABASE_DESIGN.md)
|
||||
"""
|
||||
|
||||
import copy
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from .models import MultiAgentTask, UserAgent
|
||||
|
||||
|
||||
class MultiAgentTaskCRUD:
|
||||
"""多智能体任务 CRUD 操作"""
|
||||
|
||||
@staticmethod
|
||||
def create(
|
||||
db: Session,
|
||||
task_id: Optional[str] = None, # 可选,如果为 None 则自动生成
|
||||
user_id: str = "",
|
||||
query: str = "",
|
||||
agents_info: list = [],
|
||||
task_outline: Optional[dict] = None,
|
||||
assigned_agents: Optional[list] = None,
|
||||
agent_scores: Optional[dict] = None,
|
||||
result: Optional[str] = None,
|
||||
) -> MultiAgentTask:
|
||||
"""创建任务记录"""
|
||||
task = MultiAgentTask(
|
||||
task_id=task_id or str(uuid.uuid4()), # 如果没传则生成新的
|
||||
user_id=user_id,
|
||||
query=query,
|
||||
agents_info=agents_info,
|
||||
task_outline=task_outline,
|
||||
assigned_agents=assigned_agents,
|
||||
agent_scores=agent_scores,
|
||||
result=result,
|
||||
)
|
||||
db.add(task)
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(db: Session, task_id: str) -> Optional[MultiAgentTask]:
|
||||
"""根据任务 ID 获取记录"""
|
||||
return db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
|
||||
@staticmethod
|
||||
def get_by_user_id(
|
||||
db: Session, user_id: str, limit: int = 50, offset: int = 0
|
||||
) -> List[MultiAgentTask]:
|
||||
"""根据用户 ID 获取任务记录"""
|
||||
return (
|
||||
db.query(MultiAgentTask)
|
||||
.filter(MultiAgentTask.user_id == user_id)
|
||||
.order_by(MultiAgentTask.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_recent(
|
||||
db: Session, limit: int = 20, offset: int = 0
|
||||
) -> List[MultiAgentTask]:
|
||||
"""获取最近的任务记录"""
|
||||
return (
|
||||
db.query(MultiAgentTask)
|
||||
.order_by(MultiAgentTask.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_result(
|
||||
db: Session, task_id: str, result: list
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新任务结果"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.result = result if result else []
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_task_outline(
|
||||
db: Session, task_id: str, task_outline: dict
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新任务大纲"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.task_outline = task_outline
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_assigned_agents(
|
||||
db: Session, task_id: str, assigned_agents: dict
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新分配的智能体(步骤名 -> agent列表)"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.assigned_agents = assigned_agents
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_agent_scores(
|
||||
db: Session, task_id: str, agent_scores: dict
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新智能体评分(合并模式,追加新步骤的评分)"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
# 合并现有评分数据和新评分数据
|
||||
existing_scores = task.agent_scores or {}
|
||||
merged_scores = {**existing_scores, **agent_scores} # 新数据覆盖/追加旧数据
|
||||
task.agent_scores = merged_scores
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_status(
|
||||
db: Session, task_id: str, status: str
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新任务状态"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.status = status
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def increment_execution_count(db: Session, task_id: str) -> Optional[MultiAgentTask]:
|
||||
"""增加任务执行次数"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.execution_count = (task.execution_count or 0) + 1
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_generation_id(
|
||||
db: Session, task_id: str, generation_id: str
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新生成 ID"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.generation_id = generation_id
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_execution_id(
|
||||
db: Session, task_id: str, execution_id: str
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新执行 ID"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.execution_id = execution_id
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_rehearsal_log(
|
||||
db: Session, task_id: str, rehearsal_log: list
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新排练日志"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
task.rehearsal_log = rehearsal_log if rehearsal_log else []
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def append_rehearsal_log(
|
||||
db: Session, task_id: str, log_entry: dict
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""追加排练日志条目"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
current_log = task.rehearsal_log or []
|
||||
if isinstance(current_log, list):
|
||||
current_log.append(log_entry)
|
||||
else:
|
||||
current_log = [log_entry]
|
||||
task.rehearsal_log = current_log
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def update_branches(
|
||||
db: Session, task_id: str, branches
|
||||
) -> Optional[MultiAgentTask]:
|
||||
"""更新任务分支数据
|
||||
支持两种格式:
|
||||
- list: 旧格式,直接覆盖
|
||||
- dict: 新格式 { flow_branches: [...], task_process_branches: {...} }
|
||||
两个 key 独立保存,互不干扰。
|
||||
"""
|
||||
import copy
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
if isinstance(branches, dict):
|
||||
# 新格式:字典,独立保存两个 key,互不干扰
|
||||
# 使用深拷贝避免引用共享问题
|
||||
existing = copy.deepcopy(task.branches) if task.branches else {}
|
||||
if isinstance(existing, dict):
|
||||
# 如果只更新 flow_branches,保留已有的 task_process_branches
|
||||
if 'flow_branches' in branches and 'task_process_branches' not in branches:
|
||||
branches['task_process_branches'] = existing.get('task_process_branches', {})
|
||||
# 如果只更新 task_process_branches,保留已有的 flow_branches
|
||||
if 'task_process_branches' in branches and 'flow_branches' not in branches:
|
||||
branches['flow_branches'] = existing.get('flow_branches', [])
|
||||
task.branches = branches
|
||||
else:
|
||||
# 旧格式:列表
|
||||
task.branches = branches if branches else []
|
||||
db.commit()
|
||||
db.refresh(task)
|
||||
return task
|
||||
|
||||
@staticmethod
|
||||
def get_branches(db: Session, task_id: str) -> Optional[list]:
|
||||
"""获取任务分支数据"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
return task.branches or []
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def get_by_status(
|
||||
db: Session, status: str, limit: int = 50, offset: int = 0
|
||||
) -> List[MultiAgentTask]:
|
||||
"""根据状态获取任务记录"""
|
||||
return (
|
||||
db.query(MultiAgentTask)
|
||||
.filter(MultiAgentTask.status == status)
|
||||
.order_by(MultiAgentTask.created_at.desc())
|
||||
.offset(offset)
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_by_generation_id(
|
||||
db: Session, generation_id: str
|
||||
) -> List[MultiAgentTask]:
|
||||
"""根据生成 ID 获取任务记录"""
|
||||
return (
|
||||
db.query(MultiAgentTask)
|
||||
.filter(MultiAgentTask.generation_id == generation_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_by_execution_id(
|
||||
db: Session, execution_id: str
|
||||
) -> List[MultiAgentTask]:
|
||||
"""根据执行 ID 获取任务记录"""
|
||||
return (
|
||||
db.query(MultiAgentTask)
|
||||
.filter(MultiAgentTask.execution_id == execution_id)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def delete(db: Session, task_id: str) -> bool:
|
||||
"""删除任务记录"""
|
||||
task = db.query(MultiAgentTask).filter(MultiAgentTask.task_id == task_id).first()
|
||||
if task:
|
||||
db.delete(task)
|
||||
db.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class UserAgentCRUD:
|
||||
"""用户智能体配置 CRUD 操作"""
|
||||
|
||||
@staticmethod
|
||||
def create(
|
||||
db: Session,
|
||||
user_id: str,
|
||||
agent_name: str,
|
||||
agent_config: dict,
|
||||
) -> UserAgent:
|
||||
"""创建用户智能体配置"""
|
||||
agent = UserAgent(
|
||||
id=str(uuid.uuid4()),
|
||||
user_id=user_id,
|
||||
agent_name=agent_name,
|
||||
agent_config=agent_config,
|
||||
)
|
||||
db.add(agent)
|
||||
db.commit()
|
||||
db.refresh(agent)
|
||||
return agent
|
||||
|
||||
@staticmethod
|
||||
def get_by_id(db: Session, agent_id: str) -> Optional[UserAgent]:
|
||||
"""根据 ID 获取配置"""
|
||||
return db.query(UserAgent).filter(UserAgent.id == agent_id).first()
|
||||
|
||||
@staticmethod
|
||||
def get_by_user_id(
|
||||
db: Session, user_id: str, limit: int = 50
|
||||
) -> List[UserAgent]:
|
||||
"""根据用户 ID 获取所有智能体配置"""
|
||||
return (
|
||||
db.query(UserAgent)
|
||||
.filter(UserAgent.user_id == user_id)
|
||||
.order_by(UserAgent.created_at.desc())
|
||||
.limit(limit)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_by_name(
|
||||
db: Session, user_id: str, agent_name: str
|
||||
) -> List[UserAgent]:
|
||||
"""根据用户 ID 和智能体名称获取配置"""
|
||||
return (
|
||||
db.query(UserAgent)
|
||||
.filter(
|
||||
UserAgent.user_id == user_id,
|
||||
UserAgent.agent_name == agent_name,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def update_config(
|
||||
db: Session, agent_id: str, agent_config: dict
|
||||
) -> Optional[UserAgent]:
|
||||
"""更新智能体配置"""
|
||||
agent = db.query(UserAgent).filter(UserAgent.id == agent_id).first()
|
||||
if agent:
|
||||
agent.agent_config = agent_config
|
||||
db.commit()
|
||||
db.refresh(agent)
|
||||
return agent
|
||||
|
||||
@staticmethod
|
||||
def delete(db: Session, agent_id: str) -> bool:
|
||||
"""删除智能体配置"""
|
||||
agent = db.query(UserAgent).filter(UserAgent.id == agent_id).first()
|
||||
if agent:
|
||||
db.delete(agent)
|
||||
db.commit()
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def upsert(
|
||||
db: Session,
|
||||
user_id: str,
|
||||
agent_name: str,
|
||||
agent_config: dict,
|
||||
) -> UserAgent:
|
||||
"""更新或插入用户智能体配置(根据 user_id + agent_name 判断唯一性)
|
||||
|
||||
如果已存在相同 user_id 和 agent_name 的记录,则更新配置;
|
||||
否则创建新记录。
|
||||
"""
|
||||
existing = (
|
||||
db.query(UserAgent)
|
||||
.filter(
|
||||
UserAgent.user_id == user_id,
|
||||
UserAgent.agent_name == agent_name,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if existing:
|
||||
# 更新现有记录
|
||||
existing.agent_config = agent_config
|
||||
db.commit()
|
||||
db.refresh(existing)
|
||||
return existing
|
||||
else:
|
||||
# 创建新记录
|
||||
agent = UserAgent(
|
||||
id=str(uuid.uuid4()),
|
||||
user_id=user_id,
|
||||
agent_name=agent_name,
|
||||
agent_config=agent_config,
|
||||
)
|
||||
db.add(agent)
|
||||
db.commit()
|
||||
db.refresh(agent)
|
||||
return agent
|
||||
95
backend/db/database.py
Normal file
95
backend/db/database.py
Normal file
@@ -0,0 +1,95 @@
|
||||
"""
|
||||
数据库连接管理模块
|
||||
使用 SQLAlchemy ORM,支持同步操作
|
||||
"""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
from typing import Generator
|
||||
from contextlib import contextmanager
|
||||
import json
|
||||
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.orm import sessionmaker, declarative_base
|
||||
from sqlalchemy.pool import QueuePool
|
||||
from sqlalchemy.dialects.postgresql import dialect as pg_dialect
|
||||
|
||||
# 读取配置
|
||||
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
|
||||
try:
|
||||
with open(yaml_file, "r", encoding="utf-8") as file:
|
||||
config = yaml.safe_load(file).get("database", {})
|
||||
except Exception:
|
||||
config = {}
|
||||
|
||||
|
||||
def get_database_url() -> str:
|
||||
"""获取数据库连接 URL"""
|
||||
# 优先使用环境变量
|
||||
host = os.getenv("DB_HOST", config.get("host", "localhost"))
|
||||
port = os.getenv("DB_PORT", config.get("port", "5432"))
|
||||
user = os.getenv("DB_USER", config.get("username", "postgres"))
|
||||
password = os.getenv("DB_PASSWORD", config.get("password", ""))
|
||||
dbname = os.getenv("DB_NAME", config.get("name", "agentcoord"))
|
||||
|
||||
return f"postgresql://{user}:{password}@{host}:{port}/{dbname}"
|
||||
|
||||
|
||||
# 创建引擎
|
||||
DATABASE_URL = get_database_url()
|
||||
engine = create_engine(
|
||||
DATABASE_URL,
|
||||
poolclass=QueuePool,
|
||||
pool_size=config.get("pool_size", 10),
|
||||
max_overflow=config.get("max_overflow", 20),
|
||||
pool_pre_ping=True,
|
||||
echo=False,
|
||||
# JSONB 类型处理器配置
|
||||
json_serializer=lambda obj: json.dumps(obj, ensure_ascii=False),
|
||||
json_deserializer=lambda s: json.loads(s) if isinstance(s, str) else s
|
||||
)
|
||||
|
||||
# 创建会话工厂
|
||||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
# 基础类
|
||||
Base = declarative_base()
|
||||
|
||||
|
||||
def get_db() -> Generator:
|
||||
"""
|
||||
获取数据库会话
|
||||
用法: for db in get_db(): ...
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def get_db_context() -> Generator:
|
||||
"""
|
||||
上下文管理器方式获取数据库会话
|
||||
用法: with get_db_context() as db: ...
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
yield db
|
||||
db.commit()
|
||||
except Exception as e:
|
||||
db.rollback()
|
||||
raise
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def test_connection() -> bool:
|
||||
"""测试数据库连接"""
|
||||
try:
|
||||
with engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
22
backend/db/init_db.py
Normal file
22
backend/db/init_db.py
Normal file
@@ -0,0 +1,22 @@
|
||||
"""
|
||||
数据库初始化脚本
|
||||
运行此脚本创建所有表结构
|
||||
基于 DATABASE_DESIGN.md 设计
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from db.database import engine, Base
|
||||
from db.models import MultiAgentTask, UserAgent
|
||||
|
||||
|
||||
def init_database():
|
||||
"""初始化数据库表结构"""
|
||||
Base.metadata.create_all(bind=engine)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
init_database()
|
||||
104
backend/db/models.py
Normal file
104
backend/db/models.py
Normal file
@@ -0,0 +1,104 @@
|
||||
"""
|
||||
SQLAlchemy ORM 数据模型
|
||||
对应数据库表结构 (基于 DATABASE_DESIGN.md)
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum as PyEnum
|
||||
from sqlalchemy import Column, String, Text, DateTime, Integer, Enum, Index, ForeignKey
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from .database import Base
|
||||
|
||||
|
||||
class TaskStatus(str, PyEnum):
|
||||
"""任务状态枚举"""
|
||||
GENERATING = "generating" # 生成中 - TaskProcess 生成阶段
|
||||
EXECUTING = "executing" # 执行中 - 任务执行阶段
|
||||
STOPPED = "stopped" # 已停止 - 用户手动停止执行
|
||||
COMPLETED = "completed" # 已完成 - 任务正常完成
|
||||
|
||||
|
||||
def utc_now():
|
||||
"""获取当前 UTC 时间"""
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
class MultiAgentTask(Base):
|
||||
"""多智能体任务记录模型"""
|
||||
__tablename__ = "multi_agent_tasks"
|
||||
|
||||
task_id = Column(String(64), primary_key=True)
|
||||
user_id = Column(String(64), nullable=False, index=True)
|
||||
query = Column(Text, nullable=False)
|
||||
agents_info = Column(JSONB, nullable=False)
|
||||
task_outline = Column(JSONB)
|
||||
assigned_agents = Column(JSONB)
|
||||
agent_scores = Column(JSONB)
|
||||
result = Column(JSONB)
|
||||
status = Column(
|
||||
Enum(TaskStatus, name="task_status_enum", create_type=False),
|
||||
default=TaskStatus.GENERATING,
|
||||
nullable=False
|
||||
)
|
||||
execution_count = Column(Integer, default=0, nullable=False)
|
||||
generation_id = Column(String(64))
|
||||
execution_id = Column(String(64))
|
||||
rehearsal_log = Column(JSONB)
|
||||
branches = Column(JSONB) # 任务大纲探索分支数据
|
||||
created_at = Column(DateTime(timezone=True), default=utc_now)
|
||||
updated_at = Column(DateTime(timezone=True), default=utc_now, onupdate=utc_now)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_multi_agent_tasks_status", "status"),
|
||||
Index("idx_multi_agent_tasks_generation_id", "generation_id"),
|
||||
Index("idx_multi_agent_tasks_execution_id", "execution_id"),
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""转换为字典"""
|
||||
return {
|
||||
"task_id": self.task_id,
|
||||
"user_id": self.user_id,
|
||||
"query": self.query,
|
||||
"agents_info": self.agents_info,
|
||||
"task_outline": self.task_outline,
|
||||
"assigned_agents": self.assigned_agents,
|
||||
"agent_scores": self.agent_scores,
|
||||
"result": self.result,
|
||||
"status": self.status.value if self.status else None,
|
||||
"execution_count": self.execution_count,
|
||||
"generation_id": self.generation_id,
|
||||
"execution_id": self.execution_id,
|
||||
"rehearsal_log": self.rehearsal_log,
|
||||
"branches": self.branches,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
|
||||
}
|
||||
|
||||
|
||||
class UserAgent(Base):
|
||||
"""用户保存的智能体配置模型 (可选表)"""
|
||||
__tablename__ = "user_agents"
|
||||
|
||||
id = Column(String(64), primary_key=True)
|
||||
user_id = Column(String(64), nullable=False, index=True)
|
||||
agent_name = Column(String(100), nullable=False)
|
||||
agent_config = Column(JSONB, nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), default=utc_now)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_user_agents_user_created", "user_id", "created_at"),
|
||||
)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""转换为字典"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"user_id": self.user_id,
|
||||
"agent_name": self.agent_name,
|
||||
"agent_config": self.agent_config,
|
||||
"created_at": self.created_at.isoformat() if self.created_at else None,
|
||||
}
|
||||
70
backend/db/schema.sql
Normal file
70
backend/db/schema.sql
Normal file
@@ -0,0 +1,70 @@
|
||||
-- AgentCoord 数据库表结构
|
||||
-- 基于 DATABASE_DESIGN.md 设计
|
||||
-- 执行方式: psql -U postgres -d agentcoord -f schema.sql
|
||||
|
||||
-- =============================================================================
|
||||
-- 表1: multi_agent_tasks (多智能体任务记录)
|
||||
-- 状态枚举: pending/planning/generating/executing/completed/failed
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS multi_agent_tasks (
|
||||
task_id VARCHAR(64) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
query TEXT NOT NULL,
|
||||
agents_info JSONB NOT NULL,
|
||||
task_outline JSONB,
|
||||
assigned_agents JSONB,
|
||||
agent_scores JSONB,
|
||||
result JSONB,
|
||||
status VARCHAR(20) DEFAULT 'pending',
|
||||
execution_count INTEGER DEFAULT 0,
|
||||
generation_id VARCHAR(64),
|
||||
execution_id VARCHAR(64),
|
||||
rehearsal_log JSONB,
|
||||
branches JSONB, -- 任务大纲探索分支数据
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_user_id ON multi_agent_tasks(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_created_at ON multi_agent_tasks(created_at DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_status ON multi_agent_tasks(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_generation_id ON multi_agent_tasks(generation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_multi_agent_tasks_execution_id ON multi_agent_tasks(execution_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- 表2: user_agents (用户保存的智能体配置) - 可选表
|
||||
-- =============================================================================
|
||||
CREATE TABLE IF NOT EXISTS user_agents (
|
||||
id VARCHAR(64) PRIMARY KEY,
|
||||
user_id VARCHAR(64) NOT NULL,
|
||||
agent_name VARCHAR(100) NOT NULL,
|
||||
agent_config JSONB NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_user_agents_user_id ON user_agents(user_id);
|
||||
|
||||
-- =============================================================================
|
||||
-- 更新时间触发器函数
|
||||
-- =============================================================================
|
||||
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.updated_at = CURRENT_TIMESTAMP;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ language 'plpgsql';
|
||||
|
||||
-- 为 multi_agent_tasks 表创建触发器
|
||||
CREATE TRIGGER update_multi_agent_tasks_updated_at
|
||||
BEFORE UPDATE ON multi_agent_tasks
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE '✅ PostgreSQL 数据库表结构创建完成!';
|
||||
RAISE NOTICE '表: multi_agent_tasks (多智能体任务记录)';
|
||||
RAISE NOTICE '表: user_agents (用户智能体配置)';
|
||||
END $$;
|
||||
1386
backend/server.py
1386
backend/server.py
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,3 @@
|
||||
import request from '@/utils/request'
|
||||
import websocket from '@/utils/websocket'
|
||||
import type { Agent, IApiStepTask, IRawPlanResponse, IRawStepTask } from '@/stores'
|
||||
import { withRetry } from '@/utils/retry'
|
||||
@@ -30,7 +29,7 @@ export type IExecuteRawResponse = {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSE 流式事件类型
|
||||
* WebSocket 流式事件类型
|
||||
*/
|
||||
export type StreamingEvent =
|
||||
| {
|
||||
@@ -77,101 +76,47 @@ export interface IFillAgentSelectionRequest {
|
||||
}
|
||||
|
||||
class Api {
|
||||
// 默认使用WebSocket
|
||||
private useWebSocketDefault = true
|
||||
|
||||
setAgents = (
|
||||
data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[],
|
||||
useWebSocket: boolean = this.useWebSocketDefault,
|
||||
) => {
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWebSocket && websocket.connected) {
|
||||
return websocket.send('set_agents', data)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
return request({
|
||||
url: '/setAgents',
|
||||
data,
|
||||
method: 'POST',
|
||||
})
|
||||
// 提取响应数据的公共方法
|
||||
private extractResponse<T>(raw: any): T {
|
||||
return (raw.data || raw) as T
|
||||
}
|
||||
|
||||
// 颜色向量转 HSL 字符串
|
||||
private vec2Hsl = (color: number[]): string => {
|
||||
const [h, s, l] = color
|
||||
return `hsl(${h}, ${s}%, ${l}%)`
|
||||
}
|
||||
|
||||
setAgents = (data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[]) =>
|
||||
websocket.send('set_agents', data)
|
||||
|
||||
getAgents = (user_id: string = 'default_user') => websocket.send('get_agents', { user_id })
|
||||
|
||||
generateBasePlan = (data: {
|
||||
goal: string
|
||||
inputs: string[]
|
||||
apiUrl?: string
|
||||
apiKey?: string
|
||||
apiModel?: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send(
|
||||
'generate_base_plan',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
'Initial Input Object': data.inputs,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
apiModel: data.apiModel,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
return request<unknown, IRawPlanResponse>({
|
||||
url: '/generate_basePlan',
|
||||
method: 'POST',
|
||||
data: {
|
||||
}) =>
|
||||
websocket.send(
|
||||
'generate_base_plan',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
'Initial Input Object': data.inputs,
|
||||
apiUrl: data.apiUrl,
|
||||
apiKey: data.apiKey,
|
||||
apiModel: data.apiModel,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
executePlan = (plan: IRawPlanResponse) => {
|
||||
return request<unknown, IExecuteRawResponse[]>({
|
||||
url: '/executePlan',
|
||||
method: 'POST',
|
||||
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,
|
||||
})),
|
||||
})),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
|
||||
/**
|
||||
* 优化版流式执行计划(支持动态追加步骤)
|
||||
@@ -182,23 +127,25 @@ class Api {
|
||||
onMessage: (event: StreamingEvent) => void,
|
||||
onError?: (error: Error) => void,
|
||||
onComplete?: () => void,
|
||||
useWebSocket?: boolean,
|
||||
_useWebSocket?: boolean,
|
||||
existingKeyObjects?: Record<string, any>,
|
||||
enableDynamic?: boolean,
|
||||
onExecutionStarted?: (executionId: string) => void,
|
||||
executionId?: string,
|
||||
restartFromStepIndex?: number, // 新增:从指定步骤重新执行的索引
|
||||
rehearsalLog?: any[], // 新增:传递截断后的 RehearsalLog
|
||||
restartFromStepIndex?: number,
|
||||
rehearsalLog?: any[],
|
||||
TaskID?: string,
|
||||
) => {
|
||||
const useWs = useWebSocket !== undefined ? useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
void _useWebSocket // 保留参数位置以保持兼容性
|
||||
const data = {
|
||||
RehearsalLog: rehearsalLog || [], // ✅ 使用传递的 RehearsalLog
|
||||
RehearsalLog: rehearsalLog || [], // 使用传递的 RehearsalLog
|
||||
num_StepToRun: null,
|
||||
existingKeyObjects: existingKeyObjects || {},
|
||||
enable_dynamic: enableDynamic || false,
|
||||
execution_id: executionId || null,
|
||||
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
|
||||
task_id: TaskID || null, // 任务唯一标识,用于写入数据库
|
||||
plan: {
|
||||
'Initial Input Object': plan['Initial Input Object'],
|
||||
'General Goal': plan['General Goal'],
|
||||
@@ -220,103 +167,46 @@ class Api {
|
||||
},
|
||||
}
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
websocket.subscribe(
|
||||
'execute_plan_optimized',
|
||||
data,
|
||||
// onProgress
|
||||
(progressData) => {
|
||||
try {
|
||||
let event: StreamingEvent
|
||||
websocket.subscribe(
|
||||
'execute_plan_optimized',
|
||||
data,
|
||||
// onProgress
|
||||
(progressData) => {
|
||||
try {
|
||||
let event: StreamingEvent
|
||||
|
||||
// 处理不同类型的progress数据
|
||||
if (typeof progressData === 'string') {
|
||||
event = JSON.parse(progressData)
|
||||
} else {
|
||||
event = progressData as StreamingEvent
|
||||
}
|
||||
|
||||
// 处理特殊事件类型
|
||||
if (event && typeof event === 'object') {
|
||||
// 检查是否是execution_started事件
|
||||
if ('status' in event && event.status === 'execution_started') {
|
||||
if ('execution_id' in event && onExecutionStarted) {
|
||||
onExecutionStarted(event.execution_id as string)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
onMessage(event)
|
||||
} catch (e) {
|
||||
// Failed to parse WebSocket data
|
||||
// 处理不同类型的progress数据
|
||||
if (typeof progressData === 'string') {
|
||||
event = JSON.parse(progressData)
|
||||
} else {
|
||||
event = progressData as StreamingEvent
|
||||
}
|
||||
},
|
||||
// onComplete
|
||||
() => {
|
||||
onComplete?.()
|
||||
},
|
||||
// onError
|
||||
(error) => {
|
||||
onError?.(error)
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// 否则使用原有的SSE方式
|
||||
// 处理特殊事件类型
|
||||
if (event && typeof event === 'object') {
|
||||
// 检查是否是execution_started事件
|
||||
if ('status' in event && event.status === 'execution_started') {
|
||||
if ('execution_id' in event && onExecutionStarted) {
|
||||
onExecutionStarted(event.execution_id as string)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fetch('/api/executePlanOptimized', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
onMessage(event)
|
||||
} catch (e) {
|
||||
// Failed to parse WebSocket data
|
||||
}
|
||||
},
|
||||
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) {
|
||||
// Failed to parse SSE data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
// onComplete
|
||||
() => {
|
||||
onComplete?.()
|
||||
},
|
||||
// onError
|
||||
(error) => {
|
||||
onError?.(error)
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,38 +219,16 @@ class Api {
|
||||
Baseline_Completion: number
|
||||
initialInputs: string[]
|
||||
goal: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send(
|
||||
'branch_plan_outline',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
'Initial Input Object': data.initialInputs,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
return request<unknown, IRawPlanResponse>({
|
||||
url: '/branch_PlanOutline',
|
||||
method: 'POST',
|
||||
data: {
|
||||
}) =>
|
||||
websocket.send(
|
||||
'branch_plan_outline',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
@@ -368,8 +236,9 @@ class Api {
|
||||
'Initial Input Object': data.initialInputs,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
})
|
||||
}
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
|
||||
/**
|
||||
* 分支任务流程
|
||||
@@ -381,38 +250,16 @@ class Api {
|
||||
Baseline_Completion: number
|
||||
stepTaskExisting: any
|
||||
goal: string
|
||||
useWebSocket?: boolean
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
message?: string
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}) => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
return websocket.send(
|
||||
'branch_task_process',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
Baseline_Completion: data.Baseline_Completion,
|
||||
stepTaskExisting: data.stepTaskExisting,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
|
||||
// 否则使用REST API
|
||||
return request<unknown, BranchAction[][]>({
|
||||
url: '/branch_TaskProcess',
|
||||
method: 'POST',
|
||||
data: {
|
||||
}) =>
|
||||
websocket.send(
|
||||
'branch_task_process',
|
||||
{
|
||||
branch_Number: data.branch_Number,
|
||||
Modification_Requirement: data.Modification_Requirement,
|
||||
Existing_Steps: data.Existing_Steps,
|
||||
@@ -420,14 +267,15 @@ class Api {
|
||||
stepTaskExisting: data.stepTaskExisting,
|
||||
'General Goal': data.goal,
|
||||
},
|
||||
})
|
||||
}
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
|
||||
fillStepTask = async (data: {
|
||||
goal: string
|
||||
stepTask: any
|
||||
generation_id?: string
|
||||
useWebSocket?: boolean
|
||||
TaskID?: string
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
@@ -435,72 +283,31 @@ class Api {
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<IRawStepTask> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<any> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
const rawResponse = await withRetry(
|
||||
() =>
|
||||
websocket.send(
|
||||
'fill_step_task',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
stepTask: data.stepTask,
|
||||
generation_id: data.generation_id || '',
|
||||
task_id: data.TaskID || '',
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask: any
|
||||
),
|
||||
{
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(` [fillStepTask] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
|
||||
},
|
||||
{
|
||||
AgentSelection?: string[]
|
||||
Collaboration_Brief_FrontEnd?: {
|
||||
template: string
|
||||
data: Record<string, { text: string; color: number[] }>
|
||||
}
|
||||
InputObject_List?: string[]
|
||||
OutputObject?: string
|
||||
StepName?: string
|
||||
TaskContent?: string
|
||||
TaskProcess?: Array<{
|
||||
ID: string
|
||||
ActionType: string
|
||||
AgentName: string
|
||||
Description: string
|
||||
ImportantInput: string[]
|
||||
}>
|
||||
}
|
||||
>({
|
||||
url: '/fill_stepTask',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'General Goal': data.goal,
|
||||
stepTask: data.stepTask,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const rawResponse = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(`⚠️ [fillStepTask] 第${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}%)`
|
||||
let response = this.extractResponse<any>(rawResponse)
|
||||
if (response?.filled_stepTask) {
|
||||
response = response.filled_stepTask
|
||||
}
|
||||
|
||||
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
|
||||
@@ -509,15 +316,12 @@ class Api {
|
||||
briefData[key] = {
|
||||
text: (value as { text: string; color: number[] }).text,
|
||||
style: {
|
||||
background: vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建前端格式的 IRawStepTask
|
||||
*/
|
||||
return {
|
||||
StepName: response.StepName || '',
|
||||
TaskContent: response.TaskContent || '',
|
||||
@@ -536,7 +340,7 @@ class Api {
|
||||
goal: string
|
||||
stepTask: IApiStepTask
|
||||
agents: string[]
|
||||
useWebSocket?: boolean
|
||||
TaskID?: string
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
@@ -544,15 +348,13 @@ class Api {
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<IApiStepTask> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<any> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
const rawResponse = await withRetry(
|
||||
() =>
|
||||
websocket.send(
|
||||
'fill_step_task_process',
|
||||
{
|
||||
'General Goal': data.goal,
|
||||
task_id: data.TaskID || undefined,
|
||||
stepTask_lackTaskProcess: {
|
||||
StepName: data.stepTask.name,
|
||||
TaskContent: data.stepTask.content,
|
||||
@@ -560,76 +362,26 @@ class Api {
|
||||
OutputObject: data.stepTask.output,
|
||||
AgentSelection: data.agents,
|
||||
},
|
||||
agents: data.agents,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask_lackTaskProcess: {
|
||||
StepName: string
|
||||
TaskContent: string
|
||||
InputObject_List: string[]
|
||||
OutputObject: string
|
||||
AgentSelection: string[]
|
||||
}
|
||||
),
|
||||
{
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(
|
||||
`[fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`,
|
||||
error?.message,
|
||||
)
|
||||
},
|
||||
{
|
||||
StepName?: string
|
||||
TaskContent?: string
|
||||
InputObject_List?: string[]
|
||||
OutputObject?: string
|
||||
AgentSelection?: string[]
|
||||
TaskProcess?: Array<{
|
||||
ID: string
|
||||
ActionType: string
|
||||
AgentName: string
|
||||
Description: string
|
||||
ImportantInput: string[]
|
||||
}>
|
||||
Collaboration_Brief_FrontEnd?: {
|
||||
template: string
|
||||
data: Record<string, { text: string; color: number[] }>
|
||||
}
|
||||
}
|
||||
>({
|
||||
url: '/fill_stepTask_TaskProcess',
|
||||
method: 'POST',
|
||||
data: {
|
||||
'General Goal': data.goal,
|
||||
stepTask_lackTaskProcess: {
|
||||
StepName: data.stepTask.name,
|
||||
TaskContent: data.stepTask.content,
|
||||
InputObject_List: data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.output,
|
||||
AgentSelection: data.agents,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const rawResponse = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
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}%)`
|
||||
let response = this.extractResponse<any>(rawResponse)
|
||||
if (response?.filled_stepTask) {
|
||||
response = response.filled_stepTask
|
||||
}
|
||||
|
||||
const briefData: Record<string, { text: string; style: { background: string } }> = {}
|
||||
@@ -638,7 +390,7 @@ class Api {
|
||||
briefData[key] = {
|
||||
text: (value as { text: string; color: number[] }).text,
|
||||
style: {
|
||||
background: vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -672,7 +424,7 @@ class Api {
|
||||
agentSelectModifyInit = async (data: {
|
||||
goal: string
|
||||
stepTask: any
|
||||
useWebSocket?: boolean
|
||||
TaskID?: string
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
@@ -680,78 +432,54 @@ class Api {
|
||||
[key: string]: any
|
||||
}) => void
|
||||
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
|
||||
// 调试日志:打印请求参数
|
||||
const requestPayload = {
|
||||
'General Goal': data.goal,
|
||||
stepTask: {
|
||||
Id: data.stepTask.Id || data.stepTask.id,
|
||||
StepName: data.stepTask.StepName || data.stepTask.name,
|
||||
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
|
||||
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
|
||||
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
|
||||
},
|
||||
task_id: data.TaskID || '',
|
||||
}
|
||||
console.log('🔍 [agentSelectModifyInit] 请求参数:', {
|
||||
goal: requestPayload['General Goal'],
|
||||
stepTaskName: requestPayload.stepTask.StepName,
|
||||
stepTaskContentLength: requestPayload.stepTask.TaskContent?.length,
|
||||
useWebSocket: useWs && websocket.connected,
|
||||
wsConnected: websocket.connected,
|
||||
})
|
||||
|
||||
// 定义实际的 API 调用逻辑
|
||||
const executeRequest = async (): Promise<
|
||||
Record<string, Record<string, { Reason: string; Score: number }>>
|
||||
> => {
|
||||
if (useWs && websocket.connected) {
|
||||
return await websocket.send(
|
||||
const rawResponse = await withRetry(
|
||||
() =>
|
||||
websocket.send(
|
||||
'agent_select_modify_init',
|
||||
requestPayload,
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
}
|
||||
// 否则使用REST API
|
||||
return await request<
|
||||
{
|
||||
'General Goal': string
|
||||
stepTask: any
|
||||
),
|
||||
{
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(
|
||||
`[agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
|
||||
error?.message,
|
||||
)
|
||||
},
|
||||
Record<string, Record<string, { Reason: string; Score: number }>>
|
||||
>({
|
||||
url: '/agentSelectModify_init',
|
||||
method: 'POST',
|
||||
data: requestPayload,
|
||||
})
|
||||
}
|
||||
|
||||
// 使用重试机制执行请求
|
||||
const rawResponse = await withRetry(executeRequest, {
|
||||
maxRetries: 3,
|
||||
initialDelayMs: 2000,
|
||||
onRetry: (error, attempt, delay) => {
|
||||
console.warn(
|
||||
`⚠️ [agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
|
||||
error?.message,
|
||||
)
|
||||
},
|
||||
})
|
||||
)
|
||||
|
||||
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
|
||||
// REST API 返回格式: {...}
|
||||
const response = rawResponse.data || rawResponse
|
||||
let response = this.extractResponse<any>(rawResponse)
|
||||
if (response?.scoreTable) {
|
||||
response = response.scoreTable
|
||||
}
|
||||
|
||||
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 as Record<string, { Reason: string; Score: number }> || {})) {
|
||||
for (const [agentName, scoreInfo] of Object.entries(
|
||||
(agents as Record<string, { Reason: string; Score: number }>) || {},
|
||||
)) {
|
||||
if (!transformedData[agentName]) {
|
||||
transformedData[agentName] = {}
|
||||
}
|
||||
@@ -770,7 +498,14 @@ class Api {
|
||||
*/
|
||||
agentSelectModifyAddAspect = async (data: {
|
||||
aspectList: string[]
|
||||
useWebSocket?: boolean
|
||||
stepTask?: {
|
||||
Id?: string
|
||||
StepName?: string
|
||||
TaskContent?: string
|
||||
InputObject_List?: string[]
|
||||
OutputObject?: string
|
||||
}
|
||||
TaskID?: string
|
||||
onProgress?: (progress: {
|
||||
status: string
|
||||
stage?: string
|
||||
@@ -781,53 +516,33 @@ class Api {
|
||||
aspectName: string
|
||||
agentScores: Record<string, { score: number; reason: string }>
|
||||
}> => {
|
||||
const useWs = data.useWebSocket !== undefined ? data.useWebSocket : this.useWebSocketDefault
|
||||
let response: Record<string, Record<string, { Reason: string; Score: number }>>
|
||||
const rawResponse = await websocket.send(
|
||||
'agent_select_modify_add_aspect',
|
||||
{
|
||||
aspectList: data.aspectList,
|
||||
stepTask: data.stepTask,
|
||||
task_id: data.TaskID || '',
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
|
||||
// 如果启用WebSocket且已连接,使用WebSocket
|
||||
if (useWs && websocket.connected) {
|
||||
const rawResponse = await websocket.send(
|
||||
'agent_select_modify_add_aspect',
|
||||
{
|
||||
aspectList: data.aspectList,
|
||||
},
|
||||
undefined,
|
||||
data.onProgress,
|
||||
)
|
||||
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
|
||||
response = rawResponse.data || rawResponse
|
||||
} else {
|
||||
// 否则使用REST API
|
||||
response = await request<
|
||||
{
|
||||
aspectList: string[]
|
||||
},
|
||||
Record<string, Record<string, { Reason: string; Score: number }>>
|
||||
>({
|
||||
url: '/agentSelectModify_addAspect',
|
||||
method: 'POST',
|
||||
data: {
|
||||
aspectList: data.aspectList,
|
||||
},
|
||||
})
|
||||
}
|
||||
const response = this.extractResponse<any>(rawResponse)
|
||||
|
||||
/**
|
||||
* 获取新添加的维度
|
||||
*/
|
||||
const newAspect = data.aspectList[data.aspectList.length - 1]
|
||||
if (!newAspect) {
|
||||
throw new Error('aspectList is empty')
|
||||
}
|
||||
|
||||
const newAspectAgents = response[newAspect]
|
||||
const scoreTable = response.scoreTable || response
|
||||
const newAspectAgents = scoreTable[newAspect]
|
||||
const agentScores: Record<string, { score: number; reason: string }> = {}
|
||||
|
||||
if (newAspectAgents) {
|
||||
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents)) {
|
||||
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents as Record<string, { Score?: number; score?: number; Reason?: string; reason?: string }>)) {
|
||||
agentScores[agentName] = {
|
||||
score: scoreInfo.Score,
|
||||
reason: scoreInfo.Reason,
|
||||
score: scoreInfo.Score || scoreInfo.score || 0,
|
||||
reason: scoreInfo.Reason || scoreInfo.reason || '',
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -867,12 +582,126 @@ class Api {
|
||||
})),
|
||||
})
|
||||
|
||||
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
|
||||
// REST API 返回格式: {...}
|
||||
const response = (rawResponse.data || rawResponse) as { added_count: number }
|
||||
const response = this.extractResponse<{ added_count: number }>(rawResponse)
|
||||
|
||||
return response?.added_count || 0
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务分支数据
|
||||
* @param taskId 任务ID
|
||||
* @param branches 分支数据数组
|
||||
* @returns 是否保存成功
|
||||
*/
|
||||
saveBranches = async (taskId: string, branches: any[]): Promise<boolean> => {
|
||||
if (!websocket.connected) {
|
||||
throw new Error('WebSocket未连接')
|
||||
}
|
||||
|
||||
try {
|
||||
const rawResponse = await websocket.send('save_branches', {
|
||||
task_id: taskId,
|
||||
branches,
|
||||
})
|
||||
|
||||
const response = this.extractResponse<{ status: string }>(rawResponse)
|
||||
return response?.status === 'success' || false
|
||||
} catch (error) {
|
||||
console.error('保存分支数据失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务过程分支数据
|
||||
* @param TaskID 大任务ID(数据库主键)
|
||||
* @param branches 任务过程分支数据(Map结构转对象)
|
||||
* @returns 是否保存成功
|
||||
*/
|
||||
saveTaskProcessBranches = async (
|
||||
TaskID: string,
|
||||
branches: Record<string, Record<string, any[]>>,
|
||||
): Promise<boolean> => {
|
||||
if (!websocket.connected) {
|
||||
throw new Error('WebSocket未连接')
|
||||
}
|
||||
|
||||
try {
|
||||
const rawResponse = await websocket.send('save_task_process_branches', {
|
||||
task_id: TaskID,
|
||||
branches,
|
||||
})
|
||||
|
||||
const response = this.extractResponse<{ status: string }>(rawResponse)
|
||||
return response?.status === 'success' || false
|
||||
} catch (error) {
|
||||
console.error('保存任务过程分支数据失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新任务大纲数据
|
||||
* @param taskId 任务ID
|
||||
* @param taskOutline 完整的大纲数据
|
||||
* @returns 是否更新成功
|
||||
*/
|
||||
updateTaskOutline = async (taskId: string, taskOutline: any): Promise<boolean> => {
|
||||
if (!websocket.connected) {
|
||||
throw new Error('WebSocket未连接')
|
||||
}
|
||||
|
||||
try {
|
||||
const rawResponse = await websocket.send('save_task_outline', {
|
||||
task_id: taskId,
|
||||
task_outline: taskOutline,
|
||||
})
|
||||
|
||||
const response = this.extractResponse<{ status: string }>(rawResponse)
|
||||
return response?.status === 'success' || false
|
||||
} catch (error) {
|
||||
console.error('更新任务大纲失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新指定步骤的 assigned_agents
|
||||
* @param params 参数对象
|
||||
* @returns 是否更新成功
|
||||
*/
|
||||
updateAssignedAgents = async (params: {
|
||||
task_id: string // 大任务ID(数据库主键)
|
||||
step_id: string // 步骤级ID(小任务UUID)
|
||||
agents: string[] // 选中的 agent 列表
|
||||
confirmed_groups?: string[][] // 可选:确认的 agent 组合列表
|
||||
agent_combinations?: Record<string, { process: any; brief: any }> // 可选:agent 组合的 TaskProcess 数据
|
||||
}): Promise<boolean> => {
|
||||
if (!websocket.connected) {
|
||||
throw new Error('WebSocket未连接')
|
||||
}
|
||||
|
||||
try {
|
||||
const rawResponse = await websocket.send('update_assigned_agents', {
|
||||
task_id: params.task_id,
|
||||
step_id: params.step_id,
|
||||
agents: params.agents,
|
||||
confirmed_groups: params.confirmed_groups,
|
||||
agent_combinations: params.agent_combinations,
|
||||
})
|
||||
|
||||
const response = this.extractResponse<{ status: string; error?: string }>(rawResponse)
|
||||
if (response?.status === 'success') {
|
||||
console.log('更新 assigned_agents 成功:', params)
|
||||
return true
|
||||
}
|
||||
console.warn('更新 assigned_agents 失败:', response?.error)
|
||||
return false
|
||||
} catch (error) {
|
||||
console.error('更新 assigned_agents 失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new Api()
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, computed, reactive, nextTick } from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import { useAgentsStore, useConfigStore } from '@/stores'
|
||||
import { useAgentsStore, useConfigStore, useSelectionStore } from '@/stores'
|
||||
import api from '@/api'
|
||||
import websocket from '@/utils/websocket'
|
||||
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
|
||||
import { useNotification } from '@/composables/useNotification'
|
||||
import AssignmentButton from './TaskTemplate/TaskSyllabus/components/AssignmentButton.vue'
|
||||
import HistoryList from './TaskTemplate/HistoryList/index.vue'
|
||||
import { withRetry } from '@/utils/retry'
|
||||
import { Clock } from '@element-plus/icons-vue'
|
||||
const emit = defineEmits<{
|
||||
(e: 'search-start'): void
|
||||
(e: 'search', value: string): void
|
||||
@@ -15,6 +17,7 @@ const emit = defineEmits<{
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
const configStore = useConfigStore()
|
||||
const selectionStore = useSelectionStore()
|
||||
const { success, warning, error: notifyError } = useNotification()
|
||||
const searchValue = ref('')
|
||||
const triggerOnFocus = ref(true)
|
||||
@@ -26,6 +29,16 @@ const isStopping = ref(false)
|
||||
const isStopPending = ref(false)
|
||||
const currentStepAbortController = ref<{ cancel: () => void } | null>(null)
|
||||
const currentGenerationId = ref('')
|
||||
const currentTaskID = ref('') // 后端数据库主键
|
||||
|
||||
// 监听 currentTaskID 变化,同步到全局变量(供分支保存使用)
|
||||
watch(currentTaskID, (newVal) => {
|
||||
;(window as any).__CURRENT_TASK_ID__ = newVal || ''
|
||||
console.log('♻️ [Task] currentTaskID 同步到全局变量:', (window as any).__CURRENT_TASK_ID__)
|
||||
})
|
||||
|
||||
const currentPlanOutline = ref<any>(null) // 用于恢复的完整大纲数据
|
||||
const historyDialogVisible = ref(false) // 历史记录弹窗控制
|
||||
|
||||
// 解析URL参数
|
||||
function getUrlParam(param: string): string | null {
|
||||
@@ -73,7 +86,7 @@ function handleBlur() {
|
||||
}
|
||||
|
||||
// 预加载所有任务的智能体评分数据
|
||||
async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
|
||||
async function preloadAllTaskAgentScores(outlineData: any, goal: string, TaskID: string) {
|
||||
const tasks = outlineData['Collaboration Process'] || []
|
||||
|
||||
if (tasks.length === 0) {
|
||||
@@ -96,11 +109,13 @@ async function preloadAllTaskAgentScores(outlineData: any, goal: string) {
|
||||
const agentScores = await api.agentSelectModifyInit({
|
||||
goal: goal,
|
||||
stepTask: {
|
||||
Id: task.Id, // 小任务步骤ID,用于数据库存储
|
||||
StepName: task.StepName,
|
||||
TaskContent: task.TaskContent,
|
||||
InputObject_List: task.InputObject_List,
|
||||
OutputObject: task.OutputObject
|
||||
}
|
||||
},
|
||||
TaskID: TaskID // 后端数据库主键(大任务ID)
|
||||
})
|
||||
|
||||
// 提取维度列表并存储
|
||||
@@ -205,14 +220,26 @@ async function handleSearch() {
|
||||
inputs: []
|
||||
})
|
||||
|
||||
// WebSocket 返回格式: { data: {...}, generation_id, execution_id }
|
||||
// WebSocket 返回格式: { data: { task_id, generation_id, basePlan }, 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)
|
||||
// 提取真正的outline数据
|
||||
let outlineData: any
|
||||
if (response?.data?.basePlan) {
|
||||
// WebSocket新格式:数据嵌套在basePlan中
|
||||
outlineData = response.data.basePlan
|
||||
currentGenerationId.value = response.data.generation_id || ''
|
||||
currentTaskID.value = response.data.task_id || ''
|
||||
console.log('📋 WebSocket格式: 从basePlan提取数据, generation_id:', currentGenerationId.value, 'TaskID:', currentTaskID.value)
|
||||
} else if (response?.data) {
|
||||
// 可能是WebSocket旧格式或REST API格式
|
||||
outlineData = response.data
|
||||
currentGenerationId.value = response.generation_id || ''
|
||||
currentTaskID.value = ''
|
||||
console.log('📋 直接格式: generation_id:', currentGenerationId.value)
|
||||
} else {
|
||||
outlineData = response
|
||||
currentGenerationId.value = ''
|
||||
currentTaskID.value = ''
|
||||
}
|
||||
|
||||
// 处理简报数据格式
|
||||
@@ -223,15 +250,22 @@ async function handleSearch() {
|
||||
emit('search', searchValue.value)
|
||||
|
||||
// 预加载所有任务的智能体评分数据
|
||||
preloadAllTaskAgentScores(outlineData, searchValue.value)
|
||||
preloadAllTaskAgentScores(outlineData, searchValue.value, currentTaskID.value)
|
||||
|
||||
// 填充步骤详情
|
||||
isFillingSteps.value = true
|
||||
const steps = outlineData['Collaboration Process'] || []
|
||||
|
||||
// 保存 generation_id 到本地变量,用于 fillStepTask 调用
|
||||
// 保存 generation_id 和 TaskID 到本地变量,用于 fillStepTask 调用
|
||||
// 这样即使前端停止时清空了 currentGenerationId,当前的 fillStepTask 仍能正确停止
|
||||
const fillTaskGenerationId = currentGenerationId.value
|
||||
const fillTaskTaskID = currentTaskID.value
|
||||
|
||||
console.log('📋 开始填充步骤详情', {
|
||||
generationId: fillTaskGenerationId,
|
||||
TaskID: fillTaskTaskID,
|
||||
stepsCount: steps.length
|
||||
})
|
||||
|
||||
// 串行填充所有步骤的详情
|
||||
try {
|
||||
@@ -243,6 +277,7 @@ async function handleSearch() {
|
||||
|
||||
await withRetry(
|
||||
async () => {
|
||||
console.log(`📤 调用 fillStepTask: 步骤=${step.StepName}, TaskID=${fillTaskTaskID}`)
|
||||
const detailedStep = await api.fillStepTask({
|
||||
goal: searchValue.value,
|
||||
stepTask: {
|
||||
@@ -250,9 +285,12 @@ async function handleSearch() {
|
||||
TaskContent: step.TaskContent,
|
||||
InputObject_List: step.InputObject_List,
|
||||
OutputObject: step.OutputObject,
|
||||
Id: step.Id, // 传递步骤ID用于后端定位
|
||||
},
|
||||
generation_id: fillTaskGenerationId,
|
||||
TaskID: fillTaskTaskID, // 后端数据库主键
|
||||
})
|
||||
console.log(`📥 fillStepTask 返回完成: 步骤=${step.StepName}`)
|
||||
updateStepDetail(step.StepName, detailedStep)
|
||||
},
|
||||
{
|
||||
@@ -326,6 +364,145 @@ onMounted(() => {
|
||||
onUnmounted(() => {
|
||||
websocket.off('generation_stopped', onGenerationStopped)
|
||||
})
|
||||
|
||||
// 打开历史记录弹窗
|
||||
const openHistoryDialog = () => {
|
||||
historyDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 处理历史任务恢复
|
||||
const handleRestorePlan = (plan: any) => {
|
||||
console.log('♻️ [Task] 恢复历史任务:', plan)
|
||||
|
||||
// 1. 关闭弹窗
|
||||
historyDialogVisible.value = false
|
||||
|
||||
// 2. 设置搜索值为历史任务的目标
|
||||
searchValue.value = plan.general_goal
|
||||
|
||||
// 3. 设置当前任务的 task_id(供分支保存使用)
|
||||
currentTaskID.value = plan.id || ''
|
||||
if (plan.id) {
|
||||
;(window as any).__CURRENT_TASK_ID__ = plan.id
|
||||
console.log('♻️ [Task] 已设置 currentTaskID:', currentTaskID.value)
|
||||
}
|
||||
|
||||
// 4. 设置分支初始化标记,告诉 PlanModification.vue 这是从历史记录恢复
|
||||
// 这样 initializeFlow 会走"恢复分支"逻辑,而不是"首次初始化"
|
||||
sessionStorage.setItem('plan-modification-branches-initialized', 'true')
|
||||
|
||||
// 4. 保存完整的任务大纲数据
|
||||
currentPlanOutline.value = plan.task_outline || null
|
||||
|
||||
// 4. 如果有 agents_info,更新到 store
|
||||
if (plan.agents_info && Array.isArray(plan.agents_info)) {
|
||||
agentsStore.setAgents(plan.agents_info)
|
||||
}
|
||||
|
||||
// 5. 如果有智能体评分数据,更新到 store
|
||||
if (plan.agent_scores && currentPlanOutline.value) {
|
||||
const tasks = currentPlanOutline.value['Collaboration Process'] || []
|
||||
console.log('🔍 [Task] 所有步骤名称:', tasks.map((t: any) => t.StepName))
|
||||
console.log('🔍 [Task] agent_scores 的 key:', Object.keys(plan.agent_scores))
|
||||
|
||||
for (const task of tasks) {
|
||||
// 使用 task.Id 作为 key
|
||||
const taskId = task.Id
|
||||
console.log(`🔍 [Task] 步骤使用 taskId: ${taskId}`)
|
||||
console.log(`🔍 [Task] plan.agent_scores[taskId] =`, plan.agent_scores[taskId])
|
||||
|
||||
if (taskId && plan.agent_scores[taskId]) {
|
||||
const stepScores = plan.agent_scores[taskId]
|
||||
console.log(`✅ [Task] 找到步骤 ${taskId} 的评分数据`)
|
||||
|
||||
// 后端返回格式: { aspectList: [...], agentScores: { "Agent1": { "Aspect1": {...} } } }
|
||||
// 解构获取 aspectList 和 agentScores
|
||||
const { aspectList, agentScores } = stepScores
|
||||
|
||||
// agentScores 格式: { "Agent1": { "Aspect1": { score, reason } } }
|
||||
// 直接使用,无需转换(已经是前端期望的格式)
|
||||
console.log(`🔍 [Task] 步骤 ${taskId} 的 agentScores:`, agentScores)
|
||||
console.log(`🔍 [Task] 步骤 ${taskId} 的维度列表:`, aspectList)
|
||||
|
||||
// 使用 taskId 作为 key 存储
|
||||
console.log(`🔍 [Task] 调用 setTaskScoreData: taskId=${taskId}`)
|
||||
agentsStore.setTaskScoreData(taskId, {
|
||||
aspectList,
|
||||
agentScores
|
||||
})
|
||||
console.log(`🔍 [Task] setTaskScoreData 完成,检查存储结果:`)
|
||||
const storedData = agentsStore.getTaskScoreData(taskId)
|
||||
console.log(` taskId=${taskId} 的存储结果:`, storedData)
|
||||
} else {
|
||||
console.log(`❌ [Task] 未找到步骤 "${taskId}" 的评分数据,尝试用 StepName 查找`)
|
||||
// 兼容处理:如果找不到,尝试用 StepName
|
||||
const stepName = task.StepName
|
||||
if (stepName && plan.agent_scores[stepName]) {
|
||||
console.log(`✅ [Task] 兼容模式:使用 StepName ${stepName} 找到评分数据`)
|
||||
const stepScores = plan.agent_scores[stepName]
|
||||
// 后端返回格式: { aspectList: [...], agentScores: {...} }
|
||||
const { aspectList, agentScores } = stepScores
|
||||
agentsStore.setTaskScoreData(taskId, {
|
||||
aspectList,
|
||||
agentScores
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 将完整的大纲数据设置到 store(跳过重新生成)
|
||||
if (currentPlanOutline.value) {
|
||||
agentsStore.setAgentRawPlan({
|
||||
data: currentPlanOutline.value,
|
||||
loading: false
|
||||
})
|
||||
}
|
||||
|
||||
// 7. 如果有分支数据,恢复到 selection store
|
||||
if (plan.branches && Array.isArray(plan.branches)) {
|
||||
// 旧格式:数组,恢复任务大纲探索分支
|
||||
console.log('♻️ [Task] 恢复分支数据:', { count: plan.branches.length })
|
||||
selectionStore.restoreBranchesFromDB(plan.branches)
|
||||
} else if (plan.branches && typeof plan.branches === 'object') {
|
||||
// 新格式:对象,可能包含 flow_branches 和 task_process_branches
|
||||
// 恢复任务大纲探索分支
|
||||
if (plan.branches.flow_branches && Array.isArray(plan.branches.flow_branches)) {
|
||||
console.log('♻️ [Task] 恢复任务大纲探索分支:', { count: plan.branches.flow_branches.length })
|
||||
selectionStore.restoreBranchesFromDB(plan.branches.flow_branches)
|
||||
}
|
||||
|
||||
// 恢复任务过程分支
|
||||
if (plan.branches.task_process_branches && typeof plan.branches.task_process_branches === 'object') {
|
||||
console.log('♻️ [Task] 恢复任务过程分支:', { stepCount: Object.keys(plan.branches.task_process_branches).length })
|
||||
selectionStore.restoreTaskProcessBranchesFromDB(plan.branches.task_process_branches)
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 如果有 agent 组合数据,恢复到 selection store
|
||||
if (plan.assigned_agents && typeof plan.assigned_agents === 'object') {
|
||||
console.log('♻️ [Task] 恢复 agent 组合数据:', plan.assigned_agents)
|
||||
selectionStore.restoreAgentCombinationsFromDB(plan.assigned_agents, plan.id)
|
||||
}
|
||||
|
||||
// 9. 如果有 rehearsal_log,恢复执行结果
|
||||
if (plan.rehearsal_log && Array.isArray(plan.rehearsal_log)) {
|
||||
console.log('♻️ [Task] 恢复执行结果:', { count: plan.rehearsal_log.length })
|
||||
// rehearsal_log 本身就是 IExecuteRawResponse 格式的数组
|
||||
agentsStore.setExecutePlan(plan.rehearsal_log)
|
||||
}
|
||||
|
||||
// 10. 触发搜索事件
|
||||
emit('search', plan.general_goal)
|
||||
|
||||
success('成功', '已恢复历史任务')
|
||||
}
|
||||
|
||||
// 暴露给父组件
|
||||
defineExpose({
|
||||
currentTaskID,
|
||||
openHistoryDialog
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -389,8 +566,29 @@ onUnmounted(() => {
|
||||
</el-button>
|
||||
</div>
|
||||
<AssignmentButton v-if="planReady" @click="openAgentAllocationDialog" />
|
||||
<!-- 历史记录按钮 -->
|
||||
<el-button
|
||||
class="history-button"
|
||||
circle
|
||||
:title="'历史记录'"
|
||||
@click.stop="openHistoryDialog"
|
||||
>
|
||||
<el-icon size="18px"><Clock /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- 历史记录弹窗 -->
|
||||
<el-dialog
|
||||
v-model="historyDialogVisible"
|
||||
title="历史任务"
|
||||
width="600px"
|
||||
:close-on-click-modal="true"
|
||||
destroy-on-close
|
||||
append-to-body
|
||||
>
|
||||
<HistoryList @restore="handleRestorePlan" />
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@@ -529,4 +727,21 @@ onUnmounted(() => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 历史记录按钮
|
||||
.history-button {
|
||||
position: absolute;
|
||||
right: 70px;
|
||||
top: 28px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 999;
|
||||
background: var(--color-bg-taskbar);
|
||||
border: 1px solid var(--color-border);
|
||||
color: var(--color-text-taskbar);
|
||||
|
||||
&:hover {
|
||||
background: var(--color-bg-hover);
|
||||
color: var(--color-text-hover);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -36,6 +36,7 @@ const handleFileSelect = (event: Event) => {
|
||||
if (input.files && input.files[0]) {
|
||||
const file = input.files[0]
|
||||
readFileContent(file)
|
||||
input.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<div class="history-list">
|
||||
<div class="header">
|
||||
<h3>📋 历史任务</h3>
|
||||
<el-button type="primary" link @click="fetchPlans">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-if="loading" class="loading">
|
||||
<el-icon class="is-loading"><Loading /></el-icon>
|
||||
<span>加载中...</span>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<el-empty v-else-if="plans.length === 0" description="暂无历史任务" />
|
||||
|
||||
<!-- 任务列表 -->
|
||||
<div v-else class="plan-list">
|
||||
<div
|
||||
v-for="plan in plans"
|
||||
:key="plan.id"
|
||||
class="plan-item"
|
||||
:class="{ active: selectedPlanId === plan.id }"
|
||||
@click="selectPlan(plan)"
|
||||
>
|
||||
<div class="plan-info">
|
||||
<div class="plan-goal">{{ plan.general_goal || '未知任务' }}</div>
|
||||
<div class="plan-meta">
|
||||
<el-tag size="small" :type="getStatusType(plan.status)">
|
||||
{{ getStatusText(plan.status) }}
|
||||
</el-tag>
|
||||
<span class="plan-time">{{ formatTime(plan.created_at) }}</span>
|
||||
</div>
|
||||
<div class="plan-stats">
|
||||
<span>执行次数: {{ plan.execution_count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plan-actions">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="restorePlan(plan)"
|
||||
:disabled="restoring"
|
||||
>
|
||||
恢复
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
link
|
||||
@click.stop="deletePlan(plan)"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div v-if="plans.length > 0" class="pagination">
|
||||
<span>共 {{ total }} 个任务</span>
|
||||
</div>
|
||||
|
||||
<!-- 删除确认对话框 -->
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="删除确认"
|
||||
width="400px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<span>确定要删除任务 "{{ planToDelete?.general_goal }}" 吗?此操作不可恢复。</span>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="danger" @click="confirmDelete" :loading="deleting">确定删除</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Refresh, Loading, Delete } from '@element-plus/icons-vue'
|
||||
import websocket from '@/utils/websocket'
|
||||
|
||||
// 事件定义
|
||||
const emit = defineEmits<{
|
||||
(e: 'restore', plan: PlanInfo): void
|
||||
(e: 'close'): void
|
||||
}>()
|
||||
|
||||
// 数据类型
|
||||
interface PlanInfo {
|
||||
id: string // 对应数据库 task_id
|
||||
general_goal: string // 对应数据库 query
|
||||
status: string
|
||||
execution_count: number
|
||||
created_at: string
|
||||
// 完整恢复数据
|
||||
task_outline?: any
|
||||
assigned_agents?: any
|
||||
agent_scores?: any
|
||||
agents_info?: any[]
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const plans = ref<PlanInfo[]>([])
|
||||
const loading = ref(false)
|
||||
const restoring = ref(false)
|
||||
const selectedPlanId = ref<string | null>(null)
|
||||
const total = ref(0)
|
||||
const isConnected = ref(false)
|
||||
|
||||
// 删除对话框相关
|
||||
const dialogVisible = ref(false)
|
||||
const planToDelete = ref<PlanInfo | null>(null)
|
||||
const deleting = ref(false)
|
||||
|
||||
// 生成唯一请求ID
|
||||
let requestIdCounter = 0
|
||||
const generateRequestId = () => `ws_req_${Date.now()}_${++requestIdCounter}`
|
||||
|
||||
// WebSocket 监听器引用(用于清理)
|
||||
const historyUpdatedHandler = () => {
|
||||
console.log('📡 收到历史列表更新通知,自动刷新')
|
||||
fetchPlans()
|
||||
}
|
||||
|
||||
// 获取任务列表
|
||||
const fetchPlans = async () => {
|
||||
loading.value = true
|
||||
const reqId = generateRequestId()
|
||||
|
||||
try {
|
||||
const result = await websocket.send('get_plans', { id: reqId })
|
||||
plans.value = (result.data || []) as PlanInfo[]
|
||||
total.value = plans.value.length
|
||||
isConnected.value = true
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', error)
|
||||
ElMessage.error('获取任务列表失败')
|
||||
isConnected.value = false
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 选择任务
|
||||
const selectPlan = (plan: PlanInfo) => {
|
||||
selectedPlanId.value = plan.id
|
||||
}
|
||||
|
||||
// 恢复任务
|
||||
const restorePlan = async (plan: PlanInfo) => {
|
||||
if (restoring.value) return
|
||||
|
||||
console.log('🔍 [HistoryList] 恢复计划:', plan)
|
||||
console.log('🔍 [HistoryList] plan.id:', plan.id)
|
||||
|
||||
if (!plan.id) {
|
||||
ElMessage.error('任务 ID 为空,无法恢复')
|
||||
return
|
||||
}
|
||||
|
||||
restoring.value = true
|
||||
selectedPlanId.value = plan.id
|
||||
|
||||
const reqId = generateRequestId()
|
||||
|
||||
try {
|
||||
const result = await websocket.send('restore_plan', {
|
||||
id: reqId,
|
||||
data: { plan_id: plan.id }
|
||||
})
|
||||
const planData = (result.data || result) as any
|
||||
|
||||
// 发送恢复事件
|
||||
emit('restore', {
|
||||
...plan,
|
||||
...planData
|
||||
})
|
||||
|
||||
ElMessage.success('任务已恢复')
|
||||
} catch (error) {
|
||||
console.error('恢复任务失败:', error)
|
||||
ElMessage.error('恢复任务失败')
|
||||
} finally {
|
||||
restoring.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 删除任务
|
||||
const deletePlan = (plan: PlanInfo) => {
|
||||
planToDelete.value = plan
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
// 确认删除
|
||||
const confirmDelete = async () => {
|
||||
if (!planToDelete.value) return
|
||||
|
||||
deleting.value = true
|
||||
const reqId = generateRequestId()
|
||||
|
||||
try {
|
||||
await websocket.send('delete_plan', {
|
||||
id: reqId,
|
||||
data: { plan_id: planToDelete.value.id }
|
||||
})
|
||||
|
||||
dialogVisible.value = false
|
||||
ElMessage.success('删除成功')
|
||||
// 删除成功后会自动通过 history_updated 事件刷新列表
|
||||
} catch (error) {
|
||||
console.error('删除任务失败:', error)
|
||||
ElMessage.error('删除任务失败')
|
||||
} finally {
|
||||
deleting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (timeStr: string | undefined) => {
|
||||
if (!timeStr) return ''
|
||||
try {
|
||||
const date = new Date(timeStr)
|
||||
return date.toLocaleString('zh-CN', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
})
|
||||
} catch {
|
||||
return timeStr
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态类型
|
||||
const getStatusType = (status: string): string => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'generating': 'warning',
|
||||
'executing': 'warning',
|
||||
'completed': 'success',
|
||||
'stopped': 'danger'
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: string): string => {
|
||||
const statusMap: Record<string, string> = {
|
||||
'generating': '生成中',
|
||||
'executing': '执行中',
|
||||
'completed': '已完成',
|
||||
'stopped': '已停止'
|
||||
}
|
||||
return statusMap[status] || status
|
||||
}
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
fetchPlans()
|
||||
// 监听历史列表更新事件(多标签页实时同步)
|
||||
websocket.on('history_updated', historyUpdatedHandler)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 移除事件监听
|
||||
websocket.off('history_updated', historyUpdatedHandler)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.history-list {
|
||||
padding: 16px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 40px;
|
||||
color: #909399;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.plan-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.plan-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 8px;
|
||||
background: #f5f7fa;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #ecf5ff;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
}
|
||||
|
||||
.plan-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.plan-goal {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.plan-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.plan-time {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.plan-stats {
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.plan-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
useAgentsStore,
|
||||
useSelectionStore,
|
||||
type IRawStepTask,
|
||||
type TaskProcess,
|
||||
type IApiAgentAction
|
||||
} from '@/stores'
|
||||
import { getAgentMapIcon, getActionTypeDisplay } from '@/layout/components/config.ts'
|
||||
@@ -32,9 +33,6 @@ const branchInputRef = ref<InstanceType<typeof HTMLInputElement>>()
|
||||
// 分支加载状态
|
||||
const branchLoading = ref(false)
|
||||
|
||||
// Mock 数据配置
|
||||
const USE_MOCK_DATA = false
|
||||
|
||||
// 节点和边数据
|
||||
const nodes = ref<Node[]>([])
|
||||
const edges = ref<Edge[]>([])
|
||||
@@ -51,6 +49,191 @@ const BRANCHES_INIT_KEY_PREFIX = 'plan-task-branches-initialized-'
|
||||
//最后选中的分支ID
|
||||
const LAST_SELECTED_BRANCH_KEY = 'plan-task-last-selected-branch'
|
||||
|
||||
// ==================== 公共函数提取 ====================
|
||||
|
||||
// 获取 agent 个人简介
|
||||
const getAgentProfile = (agentName: string): string => {
|
||||
return (
|
||||
agentsStore.agents.find((agent: any) => agent.Name === agentName)?.Profile || '暂无个人简介'
|
||||
)
|
||||
}
|
||||
|
||||
// 创建 VueFlow 边的通用函数
|
||||
const createFlowEdge = (
|
||||
source: string,
|
||||
target: string,
|
||||
sourceHandle: string = 'right',
|
||||
targetHandle: string = 'left',
|
||||
strokeColor: string = '#43a8aa',
|
||||
isFirstNode: boolean = false
|
||||
): Edge => {
|
||||
return {
|
||||
id: `edge-${source}-${target}`,
|
||||
source,
|
||||
target,
|
||||
sourceHandle,
|
||||
targetHandle,
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
style: {
|
||||
stroke: strokeColor,
|
||||
strokeWidth: 2,
|
||||
...(isFirstNode ? { strokeDasharray: '5,5' } : {})
|
||||
},
|
||||
markerEnd: {
|
||||
type: 'arrow' as any,
|
||||
color: '#43a8aa',
|
||||
width: 20,
|
||||
height: 20,
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建分支 agent 节点的通用函数
|
||||
const createBranchAgentNode = (
|
||||
agentNodeId: string,
|
||||
action: IApiAgentAction,
|
||||
position: { x: number; y: number }
|
||||
): Node => {
|
||||
const agentInfo = getAgentMapIcon(action.agent)
|
||||
const actionTypeInfo = getActionTypeDisplay(action.type)
|
||||
const agentProfile = getAgentProfile(action.agent)
|
||||
|
||||
return {
|
||||
id: agentNodeId,
|
||||
type: 'agent',
|
||||
position,
|
||||
data: {
|
||||
agentName: action.agent,
|
||||
agentIcon: agentInfo.icon,
|
||||
agentColor: agentInfo.color,
|
||||
actionTypeColor: actionTypeInfo?.color || '#909399',
|
||||
agentDescription: action.description || '暂无描述',
|
||||
agentProfile: agentProfile,
|
||||
actionTypeName: actionTypeInfo?.name || '未知职责',
|
||||
actionTypeKey: actionTypeInfo?.key || 'unknown',
|
||||
isRoot: false,
|
||||
isBranchTask: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存分支到 store 的通用函数
|
||||
const saveBranchToStore = (
|
||||
branchType: 'root' | 'task',
|
||||
newBranchNodes: Node[],
|
||||
newBranchEdges: Edge[],
|
||||
branchTasks: any[]
|
||||
) => {
|
||||
if (newBranchNodes.length === 0) return
|
||||
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
|
||||
isSyncing = true
|
||||
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
|
||||
parentNodeId: addingBranchNodeId.value!,
|
||||
branchContent: branchInput.value.trim(),
|
||||
branchType,
|
||||
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
|
||||
edges: JSON.parse(JSON.stringify(newBranchEdges)),
|
||||
tasks: JSON.parse(JSON.stringify(branchTasks))
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
isSyncing = false
|
||||
}, 100)
|
||||
|
||||
saveTaskProcessBranchesToDB()
|
||||
}
|
||||
|
||||
// 解析分支 API 响应的通用函数
|
||||
const parseBranchResponse = (response: any): IApiAgentAction[] => {
|
||||
const newAgentActions: IApiAgentAction[] = []
|
||||
const responseData = response.data || response
|
||||
|
||||
if (responseData && responseData.length > 0) {
|
||||
const firstBranch = responseData[0]
|
||||
firstBranch.forEach((action: any) => {
|
||||
newAgentActions.push({
|
||||
id: action.ID || uuidv4(),
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return newAgentActions
|
||||
}
|
||||
|
||||
// 获取主流程节点 ID 列表的通用函数
|
||||
const getMainProcessNodeIds = (nodeList?: Node[]): string[] => {
|
||||
const targetNodes = nodeList || nodes.value
|
||||
return targetNodes.filter(n => !n.data.isBranchTask && n.id !== 'root').map(n => n.id)
|
||||
}
|
||||
|
||||
// 设置主流程高亮的通用函数
|
||||
const highlightMainProcess = (nodeList?: Node[]) => {
|
||||
const mainProcessNodes = getMainProcessNodeIds(nodeList)
|
||||
selectedNodeIds.value = new Set(mainProcessNodes)
|
||||
}
|
||||
|
||||
// 创建分支节点和边的通用函数
|
||||
const createBranchNodesAndEdges = (
|
||||
newAgentActions: IApiAgentAction[],
|
||||
branchStartX: number,
|
||||
branchStartY: number,
|
||||
parentNodeId: string,
|
||||
strokeColor: string = '#67c23a'
|
||||
): { nodes: Node[]; edges: Edge[] } => {
|
||||
const newBranchNodes: Node[] = []
|
||||
const newBranchEdges: Edge[] = []
|
||||
const timestamp = Date.now()
|
||||
const agentNodeIds: string[] = []
|
||||
|
||||
newAgentActions.forEach((action, index) => {
|
||||
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
|
||||
agentNodeIds.push(agentNodeId)
|
||||
|
||||
// 计算位置:横向排列
|
||||
const nodeX = branchStartX + (parentNodeId === 'root' ? 100 : 0) + index * 120
|
||||
const nodeY = branchStartY
|
||||
|
||||
// 创建 agent 节点
|
||||
const newAgentNode = createBranchAgentNode(agentNodeId, action, { x: nodeX, y: nodeY })
|
||||
nodes.value.push(newAgentNode)
|
||||
newBranchNodes.push(newAgentNode)
|
||||
|
||||
// 创建连接边
|
||||
if (index === 0) {
|
||||
// 第一个 agent 连接到父节点
|
||||
const newEdge = createFlowEdge(parentNodeId, agentNodeId, 'bottom', 'left', strokeColor, true)
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
} else {
|
||||
// 后续 agent 连接到前一个 agent
|
||||
const prevAgentNodeId = agentNodeIds[index - 1]!
|
||||
const newEdge = createFlowEdge(
|
||||
prevAgentNodeId,
|
||||
agentNodeId,
|
||||
'right',
|
||||
'left',
|
||||
strokeColor,
|
||||
false
|
||||
)
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
}
|
||||
})
|
||||
|
||||
return { nodes: newBranchNodes, edges: newBranchEdges }
|
||||
}
|
||||
|
||||
// ==================== 原有函数保持不变 ====================
|
||||
|
||||
// 获取分支的所有节点
|
||||
const getAllBranchNodes = (startNodeId: string): string[] => {
|
||||
const visited = new Set<string>()
|
||||
@@ -272,14 +455,44 @@ const initializeFlow = () => {
|
||||
)}`
|
||||
const branchesInitialized = sessionStorage.getItem(branchesInitKey) === 'true'
|
||||
|
||||
if (branchesInitialized && savedBranches.length > 0) {
|
||||
nodes.value = []
|
||||
edges.value = []
|
||||
// 【修复】优先检查 store 中是否有数据,而不是依赖 sessionStorage 标记
|
||||
// 原逻辑:if (branchesInitialized && savedBranches.length > 0)
|
||||
// 问题:从历史记录恢复时,store 有数据但 sessionStorage 无标记,导致重复创建"初始流程"分支
|
||||
if (savedBranches.length > 0) {
|
||||
// 先创建根节点和主流程节点(作为分支的基础)
|
||||
nodes.value = newNodes
|
||||
edges.value = newEdges
|
||||
|
||||
savedBranches.forEach(branch => {
|
||||
// 恢复节点
|
||||
branch.nodes.forEach(node => {
|
||||
nodes.value.push(JSON.parse(JSON.stringify(node)))
|
||||
// 找出非初始流程的分支(只处理真正需要偏移的分支)
|
||||
const actualBranches = savedBranches.filter(branch => branch.branchContent !== '初始流程')
|
||||
|
||||
// 统计每个父节点下有多少个分支,用于计算垂直偏移
|
||||
const parentBranchIndex: Record<string, number> = {}
|
||||
actualBranches.forEach(branch => {
|
||||
parentBranchIndex[branch.parentNodeId] = parentBranchIndex[branch.parentNodeId] || 0
|
||||
})
|
||||
|
||||
// 恢复分支时,按顺序处理,同一父节点的分支依次向下偏移
|
||||
actualBranches.forEach(branch => {
|
||||
const parentNodeId = branch.parentNodeId
|
||||
|
||||
// 获取父节点的Y坐标
|
||||
const parentNode = nodes.value.find(n => n.id === parentNodeId)
|
||||
const parentNodeY = parentNode?.position.y || 150
|
||||
|
||||
// 获取该分支在同一父节点下的索引
|
||||
const branchIndex = parentBranchIndex[parentNodeId] ?? 0
|
||||
parentBranchIndex[parentNodeId] = branchIndex + 1
|
||||
|
||||
// 计算分支起始Y坐标:父节点Y + 200 + 分支索引 * 250
|
||||
const branchStartY = parentNodeY + 200 + branchIndex * 250
|
||||
|
||||
// 恢复节点(计算新的Y坐标)
|
||||
branch.nodes.forEach((node, nodeIndex) => {
|
||||
const restoredNode = JSON.parse(JSON.stringify(node))
|
||||
// 计算新位置:分支起始Y + 节点索引 * 120(横向排列)
|
||||
restoredNode.position.y = branchStartY
|
||||
nodes.value.push(restoredNode)
|
||||
})
|
||||
// 恢复边
|
||||
branch.edges.forEach(edge => {
|
||||
@@ -287,6 +500,11 @@ const initializeFlow = () => {
|
||||
})
|
||||
})
|
||||
|
||||
// 【修复】补充标记已初始化,避免后续重复创建"初始流程"分支
|
||||
if (!branchesInitialized) {
|
||||
sessionStorage.setItem(branchesInitKey, 'true')
|
||||
}
|
||||
|
||||
// 恢复最后选中的分支
|
||||
const lastSelectedBranchId = sessionStorage.getItem(LAST_SELECTED_BRANCH_KEY)
|
||||
if (lastSelectedBranchId) {
|
||||
@@ -297,9 +515,7 @@ const initializeFlow = () => {
|
||||
|
||||
if (lastSelectedBranch.branchContent === '初始流程') {
|
||||
// 初始流程:高亮所有主流程节点(从恢复的nodes中获取)
|
||||
nodesToHighlight = nodes.value
|
||||
.filter(n => !n.data.isBranchTask && n.id !== 'root')
|
||||
.map(n => n.id)
|
||||
nodesToHighlight = getMainProcessNodeIds()
|
||||
} else {
|
||||
// 其他分支:高亮主流程路径 + 分支节点(支持多级分支)
|
||||
const firstBranchNode = lastSelectedBranch.nodes[0]
|
||||
@@ -311,7 +527,7 @@ const initializeFlow = () => {
|
||||
let currentNode = nodes.value.find(n => n.id === incomingEdge.source)
|
||||
while (currentNode && currentNode.id !== 'root') {
|
||||
nodesToHighlight.unshift(currentNode.id)
|
||||
const prevEdge = edges.value.find(e => e.target === currentNode.id)
|
||||
const prevEdge = edges.value.find(e => e.target === currentNode?.id)
|
||||
currentNode = prevEdge
|
||||
? nodes.value.find(n => n.id === prevEdge.source)
|
||||
: undefined
|
||||
@@ -330,17 +546,11 @@ const initializeFlow = () => {
|
||||
selectedNodeIds.value = new Set(nodesToHighlight)
|
||||
} else {
|
||||
// 找不到最后选中的分支,默认选中初始流程的高亮状态
|
||||
const mainProcessNodes = nodes.value
|
||||
.filter(n => !n.data.isBranchTask && n.id !== 'root')
|
||||
.map(n => n.id)
|
||||
selectedNodeIds.value = new Set(mainProcessNodes)
|
||||
highlightMainProcess()
|
||||
}
|
||||
} else {
|
||||
// 没有保存的选中分支,默认选中初始流程的高亮状态
|
||||
const mainProcessNodes = nodes.value
|
||||
.filter(n => !n.data.isBranchTask && n.id !== 'root')
|
||||
.map(n => n.id)
|
||||
selectedNodeIds.value = new Set(mainProcessNodes)
|
||||
highlightMainProcess()
|
||||
}
|
||||
} else {
|
||||
// 首次初始化:设置节点和边,并保存为"初始流程"分支
|
||||
@@ -362,14 +572,14 @@ const initializeFlow = () => {
|
||||
// 标记已初始化(针对该任务步骤和 agent 组合)
|
||||
sessionStorage.setItem(branchesInitKey, 'true')
|
||||
|
||||
// 保存任务过程分支到数据库
|
||||
saveTaskProcessBranchesToDB()
|
||||
|
||||
// 首次初始化时,设置初始流程为当前选中分支
|
||||
selectionStore.setActiveTaskProcessBranch(taskStepId, currentAgents, initialBranchId)
|
||||
|
||||
// 默认选中"初始流程"的高亮状态
|
||||
const mainProcessNodes = newNodes
|
||||
.filter(n => !n.data.isBranchTask && n.id !== 'root')
|
||||
.map(n => n.id)
|
||||
selectedNodeIds.value = new Set(mainProcessNodes)
|
||||
highlightMainProcess(newNodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,7 +636,7 @@ const onNodeClick = (event: any) => {
|
||||
let topBranchNodeId: string | null = null
|
||||
if (branchParentChain.length > 0) {
|
||||
// 取父节点链的最后一个(最顶层的分支节点)
|
||||
topBranchNodeId = branchParentChain[branchParentChain.length - 1]
|
||||
topBranchNodeId = branchParentChain[branchParentChain.length - 1]!
|
||||
} else {
|
||||
// 如果没有分支父节点,当前节点就是最顶层
|
||||
topBranchNodeId = nodeId
|
||||
@@ -460,8 +670,12 @@ const onNodeClick = (event: any) => {
|
||||
if (currentTask.value) {
|
||||
const completeTaskProcess: any[] = []
|
||||
|
||||
// 类型守卫:检查是否为 TaskProcess 类型(有 Description 字段)
|
||||
const isTaskProcess = (data: any): data is TaskProcess =>
|
||||
data && 'Description' in data
|
||||
|
||||
// 从 store 中获取"初始流程"副本(而不是使用 taskProcess.value)
|
||||
const taskStepId = currentTask.value.Id
|
||||
const taskStepId = currentTask.value.Id!
|
||||
const currentAgents = currentTask.value.AgentSelection || []
|
||||
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||
const initialBranch = branches.find(branch => branch.branchContent === '初始流程')
|
||||
@@ -478,7 +692,7 @@ const onNodeClick = (event: any) => {
|
||||
// 主流程节点:从初始流程数据中获取
|
||||
const originalIndex = node.data.originalIndex
|
||||
const processData = mainProcessData[originalIndex]
|
||||
if (processData && processData.ID && processData.AgentName && processData.Description) {
|
||||
if (processData && isTaskProcess(processData) && processData.ID && processData.AgentName && processData.Description) {
|
||||
completeTaskProcess.push(processData)
|
||||
}
|
||||
} else if (node.data.isBranchTask) {
|
||||
@@ -490,7 +704,7 @@ const onNodeClick = (event: any) => {
|
||||
const nodeIndex = parentBranch.nodes.findIndex(n => n.id === nodeId)
|
||||
if (nodeIndex !== -1 && parentBranch.tasks[nodeIndex]) {
|
||||
const taskData = parentBranch.tasks[nodeIndex]
|
||||
if (taskData.ID && taskData.AgentName && taskData.Description) {
|
||||
if (isTaskProcess(taskData) && taskData.ID && taskData.AgentName && taskData.Description) {
|
||||
completeTaskProcess.push(taskData)
|
||||
}
|
||||
}
|
||||
@@ -508,7 +722,7 @@ const onNodeClick = (event: any) => {
|
||||
}
|
||||
|
||||
selectionStore.setActiveTaskProcessData(
|
||||
currentTask.value.Id,
|
||||
taskStepId,
|
||||
currentAgents,
|
||||
completeTaskProcess
|
||||
)
|
||||
@@ -521,11 +735,7 @@ const onNodeClick = (event: any) => {
|
||||
}
|
||||
} else {
|
||||
// 点击的是主流程节点,高亮所有主流程节点(初始流程)
|
||||
const mainProcessNodes = nodes.value
|
||||
.filter(n => !n.data.isBranchTask && n.id !== 'root')
|
||||
.map(n => n.id)
|
||||
|
||||
selectedNodeIds.value = new Set(mainProcessNodes)
|
||||
highlightMainProcess()
|
||||
|
||||
// 点击主流程节点时,从 store 读取"初始流程"分支的副本
|
||||
if (currentTask.value) {
|
||||
@@ -542,7 +752,7 @@ const onNodeClick = (event: any) => {
|
||||
selectionStore.setActiveTaskProcessData(taskStepId, currentAgents, initialBranch.tasks)
|
||||
|
||||
// 同步更新 currentTask.TaskProcess(实现全局数据联动)
|
||||
agentsStore.setCurrentTaskProcess(initialBranch.tasks)
|
||||
agentsStore.setCurrentTaskProcess(initialBranch.tasks as unknown as TaskProcess[])
|
||||
|
||||
// 保存选中的分支ID到 sessionStorage
|
||||
sessionStorage.setItem(LAST_SELECTED_BRANCH_KEY, initialBranch.id)
|
||||
@@ -626,181 +836,48 @@ const submitBranch = async () => {
|
||||
// 判断是根节点还是 agent 节点
|
||||
if (parentNodeId === 'root') {
|
||||
// 根节点分支
|
||||
let newAgentActions: IApiAgentAction[] = []
|
||||
|
||||
if (USE_MOCK_DATA) {
|
||||
// 使用 Mock API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
// 调用真实 API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
|
||||
// 根节点分支:从零开始生成完整方案
|
||||
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
|
||||
// Existing_Steps 传空数组,不传递初始流程信息
|
||||
const response = await api.mockBranchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: [], // ← 根节点分支不传递现有步骤
|
||||
Baseline_Completion: 0, // ← 从零开始
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
// 根节点分支:从零开始生成完整方案
|
||||
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
|
||||
// Existing_Steps 传空数组,不传递初始流程信息
|
||||
const response = await api.branchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: [], // ← 根节点分支不传递现有步骤
|
||||
Baseline_Completion: 0, // ← 从零开始
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
|
||||
// 后端返回格式: [[action1, action2], [action3, action4]]
|
||||
// 取第一个分支
|
||||
if (response && response.length > 0) {
|
||||
const firstBranch = response[0]
|
||||
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
|
||||
// REST API 返回格式: [[action1, action2], [action3, action4]]
|
||||
// 使用通用函数解析 API 响应
|
||||
const newAgentActions = parseBranchResponse(response)
|
||||
|
||||
// 直接遍历 action 数组
|
||||
firstBranch.forEach((action: any) => {
|
||||
// 直接使用接口返回的 ActionType
|
||||
newAgentActions.push({
|
||||
id: action.ID || uuidv4(),
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ElMessage.success('[Mock] 任务流程分支创建成功')
|
||||
} else {
|
||||
// 调用真实 API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
|
||||
// 根节点分支:从零开始生成完整方案
|
||||
// Baseline_Completion = 0 表示没有已完成的部分,需要生成所有阶段
|
||||
// Existing_Steps 传空数组,不传递初始流程信息
|
||||
const response = await api.branchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: [], // ← 根节点分支不传递现有步骤
|
||||
Baseline_Completion: 0, // ← 从零开始
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
|
||||
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
|
||||
// REST API 返回格式: [[action1, action2], [action3, action4]]
|
||||
const responseData = response.data || response
|
||||
// 后端返回格式: [[action1, action2], [action3, action4]]
|
||||
// 取第一个分支
|
||||
if (responseData && responseData.length > 0) {
|
||||
const firstBranch = responseData[0]
|
||||
|
||||
// 直接遍历 action 数组
|
||||
firstBranch.forEach((action: any) => {
|
||||
// 直接使用接口返回的 ActionType
|
||||
newAgentActions.push({
|
||||
id: action.ID || uuidv4(),
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ElMessage.success('任务流程分支创建成功')
|
||||
}
|
||||
ElMessage.success('任务流程分支创建成功')
|
||||
|
||||
// 创建新的 agent 节点
|
||||
if (newAgentActions.length > 0) {
|
||||
// 计算分支起始位置:在根节点下方(固定位置)
|
||||
const branchStartX = parentNode.position.x
|
||||
const branchStartY = parentNode.position.y + 200 // 固定位置:父节点下方200px
|
||||
const timestamp = Date.now()
|
||||
const agentNodeIds: string[] = []
|
||||
const branchStartY = parentNode.position.y + 200
|
||||
|
||||
// 为每个动作创建一个 agent 节点
|
||||
newAgentActions.forEach((action, index) => {
|
||||
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
|
||||
agentNodeIds.push(agentNodeId)
|
||||
|
||||
const agentInfo = getAgentMapIcon(action.agent)
|
||||
const actionTypeInfo = getActionTypeDisplay(action.type)
|
||||
|
||||
// 从 agentsStore 中获取 agent 的个人简介
|
||||
const agentProfile =
|
||||
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
|
||||
'暂无个人简介'
|
||||
|
||||
// 计算位置:横向排列
|
||||
const nodeX = branchStartX + 100 + index * 120
|
||||
const nodeY = branchStartY
|
||||
|
||||
// 创建 agent 节点
|
||||
const newAgentNode: Node = {
|
||||
id: agentNodeId,
|
||||
type: 'agent',
|
||||
position: { x: nodeX, y: nodeY },
|
||||
data: {
|
||||
agentName: action.agent,
|
||||
agentIcon: agentInfo.icon,
|
||||
agentColor: agentInfo.color,
|
||||
actionTypeColor: actionTypeInfo?.color || '#909399',
|
||||
agentDescription: action.description || '暂无描述',
|
||||
agentProfile: agentProfile,
|
||||
actionTypeName: actionTypeInfo?.name || '未知职责',
|
||||
actionTypeKey: actionTypeInfo?.key || 'unknown',
|
||||
isRoot: false,
|
||||
isBranchTask: true // 标记为分支任务
|
||||
}
|
||||
}
|
||||
|
||||
nodes.value.push(newAgentNode)
|
||||
newBranchNodes.push(newAgentNode)
|
||||
|
||||
// 创建连接边
|
||||
if (index === 0) {
|
||||
// 第一个 agent 连接到父节点(根节点)
|
||||
const newEdge: Edge = {
|
||||
id: `edge-${parentNodeId}-${agentNodeId}`,
|
||||
source: parentNodeId,
|
||||
target: agentNodeId,
|
||||
sourceHandle: 'bottom',
|
||||
targetHandle: 'left',
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
style: { stroke: '#67c23a', strokeWidth: 2, strokeDasharray: '5,5' },
|
||||
markerEnd: {
|
||||
type: 'arrow' as any,
|
||||
color: '#43a8aa',
|
||||
width: 20,
|
||||
height: 20,
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
} else {
|
||||
// 后续 agent 连接到前一个 agent
|
||||
const prevAgentNodeId = agentNodeIds[index - 1]
|
||||
const newEdge: Edge = {
|
||||
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
|
||||
source: prevAgentNodeId,
|
||||
target: agentNodeId,
|
||||
sourceHandle: 'right',
|
||||
targetHandle: 'left',
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
style: { stroke: '#67c23a', strokeWidth: 2 },
|
||||
markerEnd: {
|
||||
type: 'arrow' as any,
|
||||
color: '#43a8aa',
|
||||
width: 20,
|
||||
height: 20,
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
}
|
||||
})
|
||||
// 使用通用函数创建分支节点和边
|
||||
const result = createBranchNodesAndEdges(
|
||||
newAgentActions,
|
||||
branchStartX,
|
||||
branchStartY,
|
||||
parentNodeId,
|
||||
'#67c23a' // 根节点分支颜色
|
||||
)
|
||||
newBranchNodes.push(...result.nodes)
|
||||
newBranchEdges.push(...result.edges)
|
||||
|
||||
// 保存分支数据到 store
|
||||
if (newBranchNodes.length > 0) {
|
||||
// 将 IApiAgentAction 转换为 TaskProcess 格式用于存储
|
||||
// 与 fill-step-task-mock.ts 中的 TaskProcess 格式保持一致
|
||||
const branchTasks = newAgentActions.map(action => ({
|
||||
ID: action.id || uuidv4(),
|
||||
ActionType: action.type,
|
||||
@@ -809,291 +886,94 @@ const submitBranch = async () => {
|
||||
ImportantInput: action.inputs || []
|
||||
}))
|
||||
|
||||
// 使用任务过程分支存储
|
||||
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
isSyncing = true // 设置标志,避免 watch 触发重复同步
|
||||
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
|
||||
parentNodeId: parentNodeId,
|
||||
branchContent: branchContent,
|
||||
branchType: 'root',
|
||||
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
|
||||
edges: JSON.parse(JSON.stringify(newBranchEdges)),
|
||||
tasks: JSON.parse(JSON.stringify(branchTasks))
|
||||
})
|
||||
setTimeout(() => {
|
||||
isSyncing = false
|
||||
}, 100)
|
||||
saveBranchToStore('root', newBranchNodes, newBranchEdges, branchTasks)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Agent 节点分支
|
||||
const parentIsBranchTask = parentNode.data.isBranchTask || false
|
||||
const parentOriginalIndex = parentNode.data.originalIndex ?? 0
|
||||
let newAgentActions: IApiAgentAction[] = []
|
||||
|
||||
if (USE_MOCK_DATA) {
|
||||
// 使用 Mock API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
const currentTaskProcess = taskProcess.value || []
|
||||
// 调用真实 API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
const currentTaskProcess = taskProcess.value || []
|
||||
|
||||
// 根据父节点类型构建 existingSteps
|
||||
let existingSteps: any[] = []
|
||||
let baselineCompletion = 100
|
||||
// 根据父节点类型构建 existingSteps
|
||||
let existingSteps: any[] = []
|
||||
let baselineCompletion = 100
|
||||
|
||||
if (!parentIsBranchTask) {
|
||||
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
if (!parentIsBranchTask) {
|
||||
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
|
||||
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
|
||||
ID: p.ID,
|
||||
ActionType: p.ActionType,
|
||||
AgentName: p.AgentName,
|
||||
Description: p.Description,
|
||||
ImportantInput: p.ImportantInput || []
|
||||
}))
|
||||
} else {
|
||||
// 父节点是分支节点:从分支数据中获取步骤
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||
|
||||
// 找到父节点所属的分支
|
||||
const parentBranch = branches.find(branch =>
|
||||
branch.nodes.some(n => n.id === parentNode.id)
|
||||
)
|
||||
|
||||
if (parentBranch && parentBranch.tasks) {
|
||||
// 获取分支中从第一个节点到父节点的所有步骤
|
||||
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
|
||||
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
|
||||
ID: p.ID,
|
||||
ActionType: p.ActionType,
|
||||
AgentName: p.AgentName,
|
||||
Description: p.Description,
|
||||
ImportantInput: p.ImportantInput || []
|
||||
}))
|
||||
|
||||
// 分支节点的基线完成度:根据主流程进度计算
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
}
|
||||
}
|
||||
|
||||
// 调用 Mock API
|
||||
const response = await api.mockBranchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: existingSteps,
|
||||
Baseline_Completion: baselineCompletion,
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
|
||||
// 后端返回格式: [[action1, action2], [action3, action4]]
|
||||
// 取第一个分支
|
||||
if (response && response.length > 0) {
|
||||
const firstBranch = response[0]
|
||||
|
||||
// 直接遍历 action 数组
|
||||
firstBranch.forEach((action: any) => {
|
||||
// 直接使用接口返回的 ActionType
|
||||
newAgentActions.push({
|
||||
id: action.ID || uuidv4(),
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ElMessage.success('[Mock] 任务流程分支创建成功')
|
||||
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
|
||||
ID: p.ID,
|
||||
ActionType: p.ActionType,
|
||||
AgentName: p.AgentName,
|
||||
Description: p.Description,
|
||||
ImportantInput: p.ImportantInput || []
|
||||
}))
|
||||
} else {
|
||||
// 调用真实 API
|
||||
const generalGoal = agentsStore.agentRawPlan.data?.['General Goal'] || ''
|
||||
const currentTaskProcess = taskProcess.value || []
|
||||
// 父节点是分支节点:从分支数据中获取步骤
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||
|
||||
// 根据父节点类型构建 existingSteps
|
||||
let existingSteps: any[] = []
|
||||
let baselineCompletion = 100
|
||||
// 找到父节点所属的分支
|
||||
const parentBranch = branches.find(branch => branch.nodes.some(n => n.id === parentNode.id))
|
||||
|
||||
if (!parentIsBranchTask) {
|
||||
// 父节点是主流程节点:传递主流程从 0 到父节点的步骤
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
|
||||
existingSteps = currentTaskProcess.slice(0, parentOriginalIndex + 1).map((p: any) => ({
|
||||
if (parentBranch && parentBranch.tasks) {
|
||||
// 获取分支中从第一个节点到父节点的所有步骤
|
||||
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
|
||||
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
|
||||
ID: p.ID,
|
||||
ActionType: p.ActionType,
|
||||
AgentName: p.AgentName,
|
||||
Description: p.Description,
|
||||
ImportantInput: p.ImportantInput || []
|
||||
}))
|
||||
} else {
|
||||
// 父节点是分支节点:从分支数据中获取步骤
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
const branches = selectionStore.getTaskProcessBranches(taskStepId, currentAgents)
|
||||
|
||||
// 找到父节点所属的分支
|
||||
const parentBranch = branches.find(branch =>
|
||||
branch.nodes.some(n => n.id === parentNode.id)
|
||||
)
|
||||
|
||||
if (parentBranch && parentBranch.tasks) {
|
||||
// 获取分支中从第一个节点到父节点的所有步骤
|
||||
const parentIndexInBranch = parentBranch.nodes.findIndex(n => n.id === parentNode.id)
|
||||
existingSteps = parentBranch.tasks.slice(0, parentIndexInBranch + 1).map((p: any) => ({
|
||||
ID: p.ID,
|
||||
ActionType: p.ActionType,
|
||||
AgentName: p.AgentName,
|
||||
Description: p.Description,
|
||||
ImportantInput: p.ImportantInput || []
|
||||
}))
|
||||
|
||||
// 分支节点的基线完成度:根据主流程进度计算
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
}
|
||||
// 分支节点的基线完成度:根据主流程进度计算
|
||||
baselineCompletion =
|
||||
currentTaskProcess.length > 0
|
||||
? Math.round(((parentOriginalIndex + 1) / currentTaskProcess.length) * 100)
|
||||
: 100
|
||||
}
|
||||
|
||||
const response = await api.branchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: existingSteps,
|
||||
Baseline_Completion: baselineCompletion,
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
|
||||
// WebSocket 返回格式: { data: [[action1, action2], [action3, action4]], ... }
|
||||
// REST API 返回格式: [[action1, action2], [action3, action4]]
|
||||
const responseData = response.data || response
|
||||
// 后端返回格式: [[action1, action2], [action3, action4]]
|
||||
// 取第一个分支
|
||||
if (responseData && responseData.length > 0) {
|
||||
const firstBranch = responseData[0]
|
||||
|
||||
// 直接遍历 action 数组
|
||||
firstBranch.forEach((action: any) => {
|
||||
// 直接使用接口返回的 ActionType
|
||||
newAgentActions.push({
|
||||
id: action.ID || uuidv4(),
|
||||
type: action.ActionType,
|
||||
agent: action.AgentName,
|
||||
description: action.Description,
|
||||
inputs: action.ImportantInput || []
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
ElMessage.success('任务流程分支创建成功')
|
||||
}
|
||||
|
||||
const response = await api.branchTaskProcess({
|
||||
branch_Number: 1,
|
||||
Modification_Requirement: branchContent,
|
||||
Existing_Steps: existingSteps,
|
||||
Baseline_Completion: baselineCompletion,
|
||||
stepTaskExisting: currentTask.value,
|
||||
goal: generalGoal
|
||||
})
|
||||
|
||||
// 使用通用函数解析 API 响应
|
||||
const newAgentActions = parseBranchResponse(response)
|
||||
|
||||
ElMessage.success('任务流程分支创建成功')
|
||||
|
||||
// 创建新的 agent 节点
|
||||
if (newAgentActions.length > 0) {
|
||||
// 计算分支起始位置:在父 agent 节点右下方(固定位置)
|
||||
const branchStartX = parentNode.position.x + 150
|
||||
const branchStartY = parentNode.position.y + 200 // 固定位置:父节点下方200px
|
||||
const timestamp = Date.now()
|
||||
const agentNodeIds: string[] = []
|
||||
const branchStartY = parentNode.position.y + 200
|
||||
|
||||
// 为每个动作创建一个 agent 节点
|
||||
newAgentActions.forEach((action, index) => {
|
||||
const agentNodeId = `branch-agent-${parentNodeId}-${timestamp}-${index}`
|
||||
agentNodeIds.push(agentNodeId)
|
||||
|
||||
const agentInfo = getAgentMapIcon(action.agent)
|
||||
const actionTypeInfo = getActionTypeDisplay(action.type)
|
||||
|
||||
// 从 agentsStore 中获取 agent 的个人简介
|
||||
const agentProfile =
|
||||
agentsStore.agents.find((agent: any) => agent.Name === action.agent)?.Profile ||
|
||||
'暂无个人简介'
|
||||
|
||||
// 计算位置:横向排列
|
||||
const nodeX = branchStartX + index * 120
|
||||
const nodeY = branchStartY
|
||||
|
||||
// 创建 agent 节点
|
||||
const newAgentNode: Node = {
|
||||
id: agentNodeId,
|
||||
type: 'agent',
|
||||
position: { x: nodeX, y: nodeY },
|
||||
data: {
|
||||
agentName: action.agent,
|
||||
agentIcon: agentInfo.icon,
|
||||
agentColor: agentInfo.color,
|
||||
actionTypeColor: actionTypeInfo?.color || '#909399',
|
||||
agentDescription: action.description || '暂无描述',
|
||||
agentProfile: agentProfile,
|
||||
actionTypeName: actionTypeInfo?.name || '未知职责',
|
||||
actionTypeKey: actionTypeInfo?.key || 'unknown',
|
||||
isRoot: false,
|
||||
isBranchTask: true // 标记为分支任务
|
||||
}
|
||||
}
|
||||
|
||||
nodes.value.push(newAgentNode)
|
||||
newBranchNodes.push(newAgentNode)
|
||||
|
||||
// 创建连接边
|
||||
if (index === 0) {
|
||||
// 第一个 agent 连接到父节点
|
||||
const newEdge: Edge = {
|
||||
id: `edge-${parentNodeId}-${agentNodeId}`,
|
||||
source: parentNodeId,
|
||||
target: agentNodeId,
|
||||
sourceHandle: 'bottom',
|
||||
targetHandle: 'left',
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
style: { stroke: '#409eff', strokeWidth: 2, strokeDasharray: '5,5' },
|
||||
markerEnd: {
|
||||
type: 'arrow' as any,
|
||||
color: '#43a8aa',
|
||||
width: 20,
|
||||
height: 20,
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
} else {
|
||||
// 后续 agent 连接到前一个 agent
|
||||
const prevAgentNodeId = agentNodeIds[index - 1]
|
||||
const newEdge: Edge = {
|
||||
id: `edge-${prevAgentNodeId}-${agentNodeId}`,
|
||||
source: prevAgentNodeId,
|
||||
target: agentNodeId,
|
||||
sourceHandle: 'right',
|
||||
targetHandle: 'left',
|
||||
type: 'smoothstep',
|
||||
animated: true,
|
||||
style: { stroke: '#409eff', strokeWidth: 2 },
|
||||
markerEnd: {
|
||||
type: 'arrow' as any,
|
||||
color: '#43a8aa',
|
||||
width: 20,
|
||||
height: 20,
|
||||
strokeWidth: 2
|
||||
}
|
||||
}
|
||||
edges.value.push(newEdge)
|
||||
newBranchEdges.push(newEdge)
|
||||
}
|
||||
})
|
||||
// 使用通用函数创建分支节点和边
|
||||
const result = createBranchNodesAndEdges(
|
||||
newAgentActions,
|
||||
branchStartX,
|
||||
branchStartY,
|
||||
parentNodeId,
|
||||
'#409eff' // Agent 节点分支颜色
|
||||
)
|
||||
newBranchNodes.push(...result.nodes)
|
||||
newBranchEdges.push(...result.edges)
|
||||
|
||||
// 保存分支数据到 store
|
||||
if (newBranchNodes.length > 0) {
|
||||
@@ -1105,22 +985,7 @@ const submitBranch = async () => {
|
||||
ImportantInput: action.inputs || []
|
||||
}))
|
||||
|
||||
// 使用任务过程分支存储
|
||||
// 注意:需要对 nodes 和 edges 进行深拷贝,避免保存响应式引用
|
||||
const taskStepId = currentTask.value?.Id || ''
|
||||
const currentAgents = currentTask.value?.AgentSelection || []
|
||||
isSyncing = true // 设置标志,避免 watch 触发重复同步
|
||||
selectionStore.addTaskProcessBranch(taskStepId, currentAgents, {
|
||||
parentNodeId: parentNodeId,
|
||||
branchContent: branchContent,
|
||||
branchType: 'task',
|
||||
nodes: JSON.parse(JSON.stringify(newBranchNodes)),
|
||||
edges: JSON.parse(JSON.stringify(newBranchEdges)),
|
||||
tasks: JSON.parse(JSON.stringify(branchTasks))
|
||||
})
|
||||
setTimeout(() => {
|
||||
isSyncing = false
|
||||
}, 100)
|
||||
saveBranchToStore('task', newBranchNodes, newBranchEdges, branchTasks)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1154,6 +1019,20 @@ const handleBranchKeydown = (event: KeyboardEvent) => {
|
||||
}
|
||||
|
||||
onConnect(params => addEdges(params))
|
||||
|
||||
// 保存任务过程分支到数据库
|
||||
const saveTaskProcessBranchesToDB = async () => {
|
||||
const TaskID = (window as any).__CURRENT_TASK_ID__
|
||||
if (TaskID) {
|
||||
try {
|
||||
await selectionStore.saveTaskProcessBranchesToDB(TaskID)
|
||||
} catch (error) {
|
||||
console.error('保存任务过程分支数据失败:', error)
|
||||
}
|
||||
} else {
|
||||
console.warn('[saveTaskProcessBranchesToDB] 未找到 TaskID,跳过保存')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,183 +0,0 @@
|
||||
// 模拟后端原始返回格式的 Mock 数据 - fill_stepTask_TaskProcess 接口
|
||||
// 后端返回格式: IRawStepTask { StepName, TaskContent, InputObject_List, OutputObject, AgentSelection, TaskProcess, Collaboration_Brief_frontEnd }
|
||||
|
||||
import type { IRawStepTask } from '@/stores'
|
||||
|
||||
// TaskProcess 项格式
|
||||
interface RawTaskProcessItem {
|
||||
ID: string
|
||||
ActionType: string
|
||||
AgentName: string
|
||||
Description: string
|
||||
ImportantInput: string[]
|
||||
}
|
||||
|
||||
// Collaboration_Brief_frontEnd 数据项格式
|
||||
interface RawBriefDataItem {
|
||||
text: string
|
||||
color: number[] // [h, s, l]
|
||||
}
|
||||
|
||||
// 后端返回的完整数据格式
|
||||
export interface RawAgentTaskProcessResponse {
|
||||
StepName: string
|
||||
TaskContent: string
|
||||
InputObject_List: string[]
|
||||
OutputObject: string
|
||||
AgentSelection: string[]
|
||||
TaskProcess: RawTaskProcessItem[]
|
||||
Collaboration_Brief_frontEnd?: {
|
||||
template: string
|
||||
data: Record<string, RawBriefDataItem>
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟后端返回的原始数据结构(与后端缓存数据格式一致)
|
||||
// 使用与 AgentAssignmentBackendMock 相同的 agent 列表
|
||||
export const mockBackendAgentTaskProcessData: RawAgentTaskProcessResponse = {
|
||||
StepName: '腐蚀类型识别',
|
||||
TaskContent: '分析船舶制造中常见的材料腐蚀类型及其成因。',
|
||||
InputObject_List: [],
|
||||
OutputObject: '腐蚀类型及成因列表',
|
||||
AgentSelection: ['腐蚀机理研究员', '实验材料学家', '防护工程专家'],
|
||||
TaskProcess: [
|
||||
{
|
||||
ID: 'action_101',
|
||||
ActionType: 'Propose',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '分析海洋环境下的腐蚀机理,确定关键防护要素',
|
||||
ImportantInput: ['海洋环境参数', '防护性能指标'],
|
||||
},
|
||||
{
|
||||
ID: 'action_102',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '基于腐蚀机理分析结果,设计涂层材料的基础配方',
|
||||
ImportantInput: ['腐蚀机理分析结果', '涂层材料配方'],
|
||||
},
|
||||
{
|
||||
ID: 'action_103',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '筛选适用于防护涂层的二维材料,评估其性能潜力',
|
||||
ImportantInput: ['材料配方设计', '涂层材料配方'],
|
||||
},
|
||||
{
|
||||
ID: 'action_104',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '制定涂层材料性能测试实验方案,包括测试指标和方法',
|
||||
ImportantInput: ['二维材料筛选结果', '防护性能指标'],
|
||||
},
|
||||
{
|
||||
ID: 'action_105',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '模拟海洋流体环境对涂层材料的影响,优化涂层结构',
|
||||
ImportantInput: ['实验方案', '海洋环境参数'],
|
||||
},
|
||||
{
|
||||
ID: 'action_106',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '综合评估涂层材料的防护性能,提出改进建议',
|
||||
ImportantInput: ['流体力学模拟结果', '实验材料学测试结果', '二维材料性能数据'],
|
||||
},
|
||||
{
|
||||
ID: 'action_107',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '整理研发数据和测试结果,撰写完整的研发报告',
|
||||
ImportantInput: ['综合性能评估', '所有研发数据'],
|
||||
},
|
||||
],
|
||||
Collaboration_Brief_frontEnd: {
|
||||
template: '基于!<0>!、!<1>!和!<2>!,!<3>!、!<4>!、!<5>!和!<6>!执行!<7>!任务,以获得!<8>!。',
|
||||
data: {
|
||||
'0': {
|
||||
text: '涂层材料配方',
|
||||
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||
},
|
||||
'1': {
|
||||
text: '海洋环境参数',
|
||||
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||
},
|
||||
'2': {
|
||||
text: '防护性能指标',
|
||||
color: [120, 60, 70], // hsl(120, 60%, 70%)
|
||||
},
|
||||
'3': {
|
||||
text: '腐蚀机理研究员',
|
||||
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||
},
|
||||
'4': {
|
||||
text: '先进材料研发员',
|
||||
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||
},
|
||||
'5': {
|
||||
text: '二维材料科学家',
|
||||
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||
},
|
||||
'6': {
|
||||
text: '实验材料学家',
|
||||
color: [0, 0, 90], // hsl(0, 0%, 90%)
|
||||
},
|
||||
'7': {
|
||||
text: '研发适用于海洋环境的耐腐蚀防护涂层材料,并进行性能测试与评估',
|
||||
color: [0, 0, 87], // hsl(0, 0%, 87%)
|
||||
},
|
||||
'8': {
|
||||
text: '防护涂层材料研发报告',
|
||||
color: [30, 100, 80], // hsl(30, 100%, 80%)
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// 模拟后端API调用 - fill_stepTask_TaskProcess
|
||||
export const mockBackendFillAgentTaskProcess = async (
|
||||
goal: string,
|
||||
stepTask: any,
|
||||
agents: string[],
|
||||
): Promise<RawAgentTaskProcessResponse> => {
|
||||
// 模拟网络延迟 500ms
|
||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
||||
|
||||
// 在真实场景中,后端会根据传入的 goal、stepTask 和 agents 生成不同的 TaskProcess
|
||||
// 这里我们直接返回预设的 Mock 数据
|
||||
// 可以根据传入的 agents 动态修改 AgentSelection 和 TaskProcess
|
||||
|
||||
// 确保 agents 数组不为空
|
||||
const safeAgents = agents.length > 0 ? agents : ['腐蚀机理研究员']
|
||||
|
||||
const responseData: RawAgentTaskProcessResponse = {
|
||||
...mockBackendAgentTaskProcessData,
|
||||
AgentSelection: agents,
|
||||
TaskProcess: mockBackendAgentTaskProcessData.TaskProcess.map((action, index) => ({
|
||||
...action,
|
||||
AgentName: safeAgents[index % safeAgents.length],
|
||||
})),
|
||||
Collaboration_Brief_frontEnd: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd
|
||||
? {
|
||||
template: mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.template,
|
||||
data: { ...mockBackendAgentTaskProcessData.Collaboration_Brief_frontEnd.data },
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
|
||||
// 更新 Collaboration_Brief_frontEnd.data 中的 agent 引用
|
||||
if (responseData.Collaboration_Brief_frontEnd?.data) {
|
||||
const agentCount = Math.min(safeAgents.length, 4) // 最多4个agent
|
||||
for (let i = 0; i < agentCount; i++) {
|
||||
const key = String(i + 3) // agent从索引3开始
|
||||
if (responseData.Collaboration_Brief_frontEnd.data[key]) {
|
||||
responseData.Collaboration_Brief_frontEnd.data[key] = {
|
||||
...responseData.Collaboration_Brief_frontEnd.data[key],
|
||||
text: safeAgents[i]!,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return responseData
|
||||
}
|
||||
@@ -14,9 +14,15 @@ import websocket from '@/utils/websocket'
|
||||
import Notification from '@/components/Notification/Notification.vue'
|
||||
import { useNotification } from '@/composables/useNotification'
|
||||
|
||||
// 定义组件 props
|
||||
const props = defineProps<{
|
||||
TaskID?: string // 任务唯一标识,用于写入数据库
|
||||
}>()
|
||||
|
||||
// 定义组件事件
|
||||
const emit = defineEmits<{
|
||||
(e: 'refreshLine'): void
|
||||
(el: 'setCurrentTask', task: IRawStepTask): void
|
||||
(e: 'setCurrentTask', task: IRawStepTask): void
|
||||
}>()
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
@@ -411,7 +417,10 @@ async function executeBatchSteps(readySteps: IRawStepTask[]) {
|
||||
isStreaming.value = true
|
||||
currentExecutionId.value = executionId
|
||||
},
|
||||
currentExecutionId.value || undefined
|
||||
currentExecutionId.value || undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
props.TaskID || undefined
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -961,7 +970,8 @@ async function restartFromStep(stepIndex: number) {
|
||||
},
|
||||
newExecutionId, // 传入前端生成的 execution_id
|
||||
stepIndex,
|
||||
truncatedLog
|
||||
truncatedLog,
|
||||
props.TaskID || undefined // 传入 TaskID 用于更新数据库
|
||||
)
|
||||
|
||||
success('重新执行', `正在从步骤 ${stepIndex + 1} 重新执行...`)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,155 +0,0 @@
|
||||
// branch_TaskProcess 接口的 Mock 数据和 Mock API
|
||||
export interface BranchAction {
|
||||
ID: string
|
||||
ActionType: string
|
||||
AgentName: string
|
||||
Description: string
|
||||
ImportantInput: string[]
|
||||
}
|
||||
|
||||
export type BranchTaskProcessResponse = BranchAction[][]
|
||||
|
||||
// Mock 数据:模拟后端返回的原始任务流程数据(2D 数组)
|
||||
// 格式:[[action1, action2], [action3, action4]]
|
||||
const mockBranchTaskProcessDataRaw: BranchAction[][] = [
|
||||
// 第一个任务分支方案
|
||||
[
|
||||
{
|
||||
ID: 'agent3',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '详细分析用户需求文档',
|
||||
ImportantInput: ['agent2'],
|
||||
},
|
||||
{
|
||||
ID: 'agent4',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '设计系统整体架构',
|
||||
ImportantInput: ['agent3'],
|
||||
},
|
||||
{
|
||||
ID: 'agent5',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '实现系统核心功能',
|
||||
ImportantInput: ['agent4'],
|
||||
},
|
||||
{
|
||||
ID: 'agent6',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '进行系统集成测试',
|
||||
ImportantInput: ['agent5'],
|
||||
},
|
||||
],
|
||||
// 第二个任务分支方案
|
||||
[
|
||||
{
|
||||
ID: 'agent7',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '深入分析用户需求和技术约束',
|
||||
ImportantInput: ['agent2'],
|
||||
},
|
||||
{
|
||||
ID: 'agent8',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '设计系统技术架构和数据流',
|
||||
ImportantInput: ['agent8'],
|
||||
},
|
||||
{
|
||||
ID: 'agent9',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '评估系统安全性',
|
||||
ImportantInput: ['agent4'],
|
||||
},
|
||||
{
|
||||
ID: 'agent10',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '完成系统安全测试',
|
||||
ImportantInput: ['agent9'],
|
||||
},
|
||||
],
|
||||
|
||||
//第三个任务分支方案
|
||||
[
|
||||
{
|
||||
ID: 'agent12',
|
||||
ActionType: 'Critique',
|
||||
AgentName: '腐蚀机理研究员',
|
||||
Description: '设计系统整体架构',
|
||||
ImportantInput: ['agent11'],
|
||||
},
|
||||
{
|
||||
ID: 'agent13',
|
||||
ActionType: 'Improve',
|
||||
AgentName: '防护工程专家',
|
||||
Description: '实现系统核心功能',
|
||||
ImportantInput: ['agent12'],
|
||||
},
|
||||
{
|
||||
ID: 'agent14',
|
||||
ActionType: 'Finalize',
|
||||
AgentName: '实验材料学家',
|
||||
Description: '进行系统集成测试',
|
||||
ImportantInput: ['agent13'],
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
/**
|
||||
* Mock API:模拟后端 branch_TaskProcess 接口调用
|
||||
*
|
||||
* @param branch_Number - 分支数量
|
||||
* @param Modification_Requirement - 修改需求
|
||||
* @param Existing_Steps - 现有步骤列表(包含完整信息的对象数组)
|
||||
* @param Baseline_Completion - 基线完成度
|
||||
* @param stepTaskExisting - 现有任务
|
||||
* @param General_Goal - 总体目标
|
||||
* @returns Promise<BranchAction[][]> - 返回 2D 数组,与后端格式完全一致
|
||||
*/
|
||||
export const mockBranchTaskProcessAPI = async (params: {
|
||||
branch_Number: number
|
||||
Modification_Requirement: string
|
||||
Existing_Steps: BranchAction[]
|
||||
Baseline_Completion: number
|
||||
stepTaskExisting: any
|
||||
General_Goal: string
|
||||
}): Promise<BranchAction[][]> => {
|
||||
// 模拟网络延迟 800ms
|
||||
await new Promise((resolve) => setTimeout(resolve, 800))
|
||||
|
||||
console.log('[Mock API] branch_TaskProcess 调用参数:', params)
|
||||
|
||||
// 🆕 使用轮询方式选择分支方案(依次循环使用所有分支方案)
|
||||
const totalBranches = mockBranchTaskProcessDataRaw.length
|
||||
const sessionKey = `branch-task-process-index-${params.stepTaskExisting?.Id || 'default'}`
|
||||
|
||||
// 获取上一次的选择索引
|
||||
let lastIndex = parseInt(sessionStorage.getItem(sessionKey) || '0')
|
||||
|
||||
// 计算本次的选择索引(轮询到下一个分支)
|
||||
const selectedBranchIndex = (lastIndex + 1) % totalBranches
|
||||
|
||||
// 保存本次的选择索引
|
||||
sessionStorage.setItem(sessionKey, selectedBranchIndex.toString())
|
||||
|
||||
const rawBranchData = mockBranchTaskProcessDataRaw[selectedBranchIndex] || []
|
||||
|
||||
console.log(
|
||||
'[Mock API] branch_TaskProcess 选择分支方案:',
|
||||
selectedBranchIndex + 1,
|
||||
'/',
|
||||
totalBranches,
|
||||
)
|
||||
console.log('[Mock API] branch_TaskProcess 返回数据:', rawBranchData)
|
||||
|
||||
// 直接返回 2D 数组,与后端格式完全一致
|
||||
return [rawBranchData]
|
||||
}
|
||||
|
||||
export default mockBranchTaskProcessAPI
|
||||
@@ -83,8 +83,23 @@ const handleSubmit = async () => {
|
||||
}
|
||||
try {
|
||||
isAddingDimension.value = true
|
||||
|
||||
// 获取大任务ID和小任务信息
|
||||
const dbTaskId = (window as any).__CURRENT_TASK_ID__
|
||||
const stepTask = currentTask.value
|
||||
? {
|
||||
Id: currentTask.value.Id,
|
||||
StepName: currentTask.value.StepName,
|
||||
TaskContent: currentTask.value.TaskContent,
|
||||
InputObject_List: currentTask.value.InputObject_List,
|
||||
OutputObject: currentTask.value.OutputObject
|
||||
}
|
||||
: undefined
|
||||
|
||||
const response = await api.agentSelectModifyAddAspect({
|
||||
aspectList: [...scoreDimensions.value, newDimension]
|
||||
aspectList: [...scoreDimensions.value, newDimension],
|
||||
stepTask: stepTask,
|
||||
TaskID: dbTaskId
|
||||
})
|
||||
|
||||
scoreDimensions.value.push(response.aspectName)
|
||||
@@ -103,9 +118,10 @@ const handleSubmit = async () => {
|
||||
})
|
||||
//异步更新 store(等前端显示完成后再更新,避免触发重新初始化)
|
||||
await nextTick()
|
||||
// 使用 Id 作为 key
|
||||
const taskId = currentTask.value?.Id
|
||||
|
||||
//更新按任务ID的存储
|
||||
//更新按任务Id的存储
|
||||
if (taskId) {
|
||||
const existingTaskData = agentsStore.getTaskScoreData(taskId)
|
||||
if (existingTaskData) {
|
||||
@@ -172,7 +188,28 @@ const confirmAgentSelection = async () => {
|
||||
selectedAgents.value = new Set(existingGroup)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取大任务ID
|
||||
const dbTaskId = (window as any).__CURRENT_TASK_ID__
|
||||
|
||||
// 添加到内存 store
|
||||
agentsStore.addConfirmedAgentGroup(currentTask.value.Id, agentArray)
|
||||
|
||||
// 🆕 保存 confirmed_groups 到数据库
|
||||
if (dbTaskId && currentTask.value.Id) {
|
||||
try {
|
||||
const updatedGroups = agentsStore.getConfirmedAgentGroups(currentTask.value.Id)
|
||||
await api.updateAssignedAgents({
|
||||
task_id: dbTaskId,
|
||||
step_id: currentTask.value.Id,
|
||||
agents: agentArray,
|
||||
confirmed_groups: updatedGroups
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('❌ 保存 confirmed_groups 失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
isLoadingConfirm.value = true
|
||||
const stepTaskForApi = agentsStore.createStepTaskForApi(agentArray)
|
||||
@@ -186,7 +223,8 @@ const confirmAgentSelection = async () => {
|
||||
const filledTask = await api.fillStepTaskTaskProcess({
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: agentArray
|
||||
agents: agentArray,
|
||||
TaskID: dbTaskId
|
||||
})
|
||||
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentArray, filledTask)
|
||||
} catch (error) {
|
||||
@@ -212,6 +250,7 @@ const selectAgentGroup = async (agentNames: string[]) => {
|
||||
if (currentTask.value?.Id && agentNames.length > 0) {
|
||||
let taskProcessData = selectionStore.getAgentTaskProcess(currentTask.value.Id, agentNames)
|
||||
|
||||
// 如果 store 中没有数据,调用 API 生成
|
||||
if (!taskProcessData) {
|
||||
try {
|
||||
isLoadingSelectGroup.value = true
|
||||
@@ -225,9 +264,17 @@ const selectAgentGroup = async (agentNames: string[]) => {
|
||||
const filledTask = await api.fillStepTaskTaskProcess({
|
||||
goal,
|
||||
stepTask: stepTaskForApi,
|
||||
agents: agentNames
|
||||
agents: agentNames,
|
||||
TaskID: (window as any).__CURRENT_TASK_ID__
|
||||
})
|
||||
|
||||
// 🆕 先存储到 store,确保 getAgentCombinations 能获取到
|
||||
// 注意:需要转换为后端期望的格式 { process, brief }
|
||||
const groupKey = selectionStore.getAgentGroupKey(agentNames)
|
||||
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, {
|
||||
process: filledTask.process || [],
|
||||
brief: filledTask.brief || {}
|
||||
})
|
||||
selectionStore.setAgentTaskProcess(currentTask.value.Id, agentNames, filledTask)
|
||||
taskProcessData = filledTask
|
||||
} catch (error) {
|
||||
console.error('❌ 加载 TaskProcess 数据失败:', error)
|
||||
@@ -239,10 +286,16 @@ const selectAgentGroup = async (agentNames: string[]) => {
|
||||
|
||||
if (taskProcessData) {
|
||||
const convertedTaskProcess = convertToTaskProcess(taskProcessData.process || [])
|
||||
agentsStore.updateCurrentAgentSelection(
|
||||
|
||||
// 🆕 获取所有 agent combinations(包含新存储的数据)
|
||||
// getAgentCombinations 现在使用 stepId 作为第一层 key
|
||||
const agentCombinations = selectionStore.getAgentCombinations(currentTask.value.Id)
|
||||
|
||||
await agentsStore.updateCurrentAgentSelection(
|
||||
[...agentNames],
|
||||
convertedTaskProcess,
|
||||
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd
|
||||
taskProcessData.brief || currentTask.value.Collaboration_Brief_frontEnd,
|
||||
agentCombinations
|
||||
)
|
||||
}
|
||||
|
||||
@@ -345,9 +398,9 @@ const calculateAgentAverage = (agentData: AgentHeatmapData, selectedDimensions?:
|
||||
|
||||
// API调用 - 获取智能体评分数据
|
||||
const fetchAgentScores = async () => {
|
||||
// 使用 Id 作为 key
|
||||
const taskId = currentTask.value?.Id
|
||||
if (!taskId) {
|
||||
console.warn('⚠️ fetchAgentScores: 当前任务没有 Id')
|
||||
return null
|
||||
}
|
||||
//先检查 store 中是否有该任务的评分数据
|
||||
@@ -369,14 +422,15 @@ const fetchAgentScores = async () => {
|
||||
|
||||
// 检查是否已停止
|
||||
if (agentsStore.isStopping || agentsStore.hasStoppedFilling) {
|
||||
console.log('检测到停止信号,跳过获取智能体评分')
|
||||
return null
|
||||
}
|
||||
|
||||
// 调用 API 获取评分(如果没有缓存或预加载)
|
||||
const dbTaskId = (window as any).__CURRENT_TASK_ID__
|
||||
const agentScores = await api.agentSelectModifyInit({
|
||||
goal: agentsStore.agentRawPlan.data?.['General Goal'] || '',
|
||||
stepTask: currentTask.value
|
||||
stepTask: currentTask.value,
|
||||
TaskID: dbTaskId
|
||||
})
|
||||
|
||||
// 再次检查是否已停止(API 调用后)
|
||||
@@ -388,9 +442,7 @@ const fetchAgentScores = async () => {
|
||||
const firstAgent = Object.keys(agentScores)[0]
|
||||
const aspectList = firstAgent ? Object.keys(agentScores[firstAgent] || {}) : []
|
||||
|
||||
console.log('✅ 获取到的维度列表:', aspectList)
|
||||
|
||||
// 🆕 保存到 store(按任务ID存储)
|
||||
// 🆕 保存到 store(按 Id 存储)
|
||||
agentsStore.setTaskScoreData(taskId, {
|
||||
aspectList,
|
||||
agentScores
|
||||
@@ -438,7 +490,6 @@ const initHeatmapData = async () => {
|
||||
// 获取接口数据
|
||||
const scoreData = await fetchAgentScores()
|
||||
if (!scoreData) {
|
||||
console.warn('⚠️ initHeatmapData: 没有获取到评分数据')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
// Mock数据 - 用于agentSelectModifyAddAspect接口
|
||||
// 模拟用户输入新维度后,所有agent在该维度上的评分数据
|
||||
|
||||
import { vueAgentList } from './AgentAssignmentMock'
|
||||
|
||||
// 类型定义
|
||||
export interface NewDimensionScore {
|
||||
score: number
|
||||
reason: string
|
||||
}
|
||||
|
||||
export type NewDimensionScoreData = Record<string, NewDimensionScore>
|
||||
|
||||
// 模拟接口返回的数据结构
|
||||
export interface AgentAddAspectResponse {
|
||||
aspectName: string // 新添加的维度名称
|
||||
agentScores: NewDimensionScoreData // 所有agent在该维度上的评分
|
||||
}
|
||||
|
||||
// 生成指定维度名称的mock评分数据
|
||||
export const generateMockDimensionScores = (dimensionName: string): AgentAddAspectResponse => {
|
||||
const agentScores: NewDimensionScoreData = {}
|
||||
|
||||
vueAgentList.forEach((agent) => {
|
||||
// 随机生成1-5的评分
|
||||
const score = Math.floor(Math.random() * 5) + 1
|
||||
|
||||
// 根据评分生成不同的原因描述
|
||||
let reason = ''
|
||||
switch (score) {
|
||||
case 5:
|
||||
reason = `在"${dimensionName}"方面表现卓越,展现出杰出的能力和深刻的理解`
|
||||
break
|
||||
case 4:
|
||||
reason = `在"${dimensionName}"方面表现优秀,具有良好的专业能力和执行力`
|
||||
break
|
||||
case 3:
|
||||
reason = `在"${dimensionName}"方面表现合格,能够完成相关任务`
|
||||
break
|
||||
case 2:
|
||||
reason = `在"${dimensionName}"方面表现一般,仍有提升空间`
|
||||
break
|
||||
case 1:
|
||||
reason = `在"${dimensionName}"方面需要加强,建议进一步提升相关能力`
|
||||
break
|
||||
}
|
||||
|
||||
agentScores[agent] = { score, reason }
|
||||
})
|
||||
|
||||
return {
|
||||
aspectName: dimensionName,
|
||||
agentScores,
|
||||
}
|
||||
}
|
||||
|
||||
// 预设的一些常用维度及其评分数据
|
||||
export const presetDimensionScores: Record<string, AgentAddAspectResponse> = {
|
||||
创新性: generateMockDimensionScores('创新性'),
|
||||
技术能力: generateMockDimensionScores('技术能力'),
|
||||
沟通技巧: generateMockDimensionScores('沟通技巧'),
|
||||
问题解决: generateMockDimensionScores('问题解决'),
|
||||
团队协作: generateMockDimensionScores('团队协作'),
|
||||
学习能力: generateMockDimensionScores('学习能力'),
|
||||
执行力: generateMockDimensionScores('执行力'),
|
||||
责任心: generateMockDimensionScores('责任心'),
|
||||
适应性: generateMockDimensionScores('适应性'),
|
||||
领导力: generateMockDimensionScores('领导力'),
|
||||
}
|
||||
|
||||
// 模拟API调用函数(用于前端测试)
|
||||
export const mockAgentAddAspectApi = async (
|
||||
aspectList: string[],
|
||||
): Promise<AgentAddAspectResponse[]> => {
|
||||
// 获取新增的维度(最后一个)
|
||||
const newAspect = aspectList[aspectList.length - 1]
|
||||
|
||||
// 模拟网络延迟 500ms
|
||||
await new Promise((resolve) => setTimeout(resolve, 20000))
|
||||
|
||||
// 如果是预设维度,返回预设数据
|
||||
if (presetDimensionScores[newAspect]) {
|
||||
return [presetDimensionScores[newAspect]]
|
||||
}
|
||||
|
||||
// 否则动态生成新的评分数据
|
||||
return [generateMockDimensionScores(newAspect)]
|
||||
}
|
||||
|
||||
// Vue Composition API 兼容的hook
|
||||
export const useAgentAddAspectMock = () => {
|
||||
const addNewDimension = async (dimensionName: string) => {
|
||||
const response = await mockAgentAddAspectApi([dimensionName])
|
||||
return response[0]
|
||||
}
|
||||
|
||||
const getMultipleDimensions = async (dimensionNames: string[]) => {
|
||||
const responses: AgentAddAspectResponse[] = []
|
||||
|
||||
for (const dimension of dimensionNames) {
|
||||
if (presetDimensionScores[dimension]) {
|
||||
responses.push(presetDimensionScores[dimension])
|
||||
} else {
|
||||
responses.push(generateMockDimensionScores(dimension))
|
||||
}
|
||||
}
|
||||
|
||||
return responses
|
||||
}
|
||||
|
||||
return {
|
||||
addNewDimension,
|
||||
getMultipleDimensions,
|
||||
generateMockDimensionScores,
|
||||
}
|
||||
}
|
||||
@@ -1,192 +0,0 @@
|
||||
// 模拟后端原始返回格式的Mock数据 - 维度 -> agent -> { Reason, Score }
|
||||
import { vueAgentList, vueAspectList } from './AgentAssignmentMock'
|
||||
|
||||
// 后端返回的评分项格式
|
||||
export interface BackendScoreItem {
|
||||
Reason: string
|
||||
Score: number
|
||||
}
|
||||
|
||||
// 后端返回的完整数据格式
|
||||
export type BackendAgentScoreResponse = Record<string, Record<string, BackendScoreItem>>
|
||||
|
||||
// 模拟后端返回的原始数据结构(维度 -> agent -> { Reason, Score })
|
||||
export const mockBackendAgentScoreData: BackendAgentScoreResponse = {
|
||||
能力: {
|
||||
船舶设计师: { Reason: '展现出卓越的创造力和创新思维', Score: 4 },
|
||||
防护工程专家: { Reason: '展现出杰出的创造性问题解决能力', Score: 5 },
|
||||
病理生理学家: { Reason: '具有中等创造技能,有待提升', Score: 3 },
|
||||
药物化学家: { Reason: '在大多数情况下展现较强的创造性思维', Score: 4 },
|
||||
制剂工程师: { Reason: '展现出胜任的创造能力', Score: 3 },
|
||||
监管事务专家: { Reason: '具有较强的创造性表达能力', Score: 4 },
|
||||
物理学家: { Reason: '擅长创新性思维方法', Score: 5 },
|
||||
实验材料学家: { Reason: '展现出卓越的创造性思维和创新能力', Score: 5 },
|
||||
计算模拟专家: { Reason: '展现出良好的创造性问题解决能力', Score: 4 },
|
||||
腐蚀机理研究员: { Reason: '展现出卓越的创造性问题解决能力', Score: 5 },
|
||||
先进材料研发员: { Reason: '展现出平衡的创造能力', Score: 4 },
|
||||
肾脏病学家: { Reason: '展现出卓越的创造天赋', Score: 5 },
|
||||
临床研究协调员: { Reason: '展现出胜任的创造性思维', Score: 3 },
|
||||
中医药专家: { Reason: '展现出较强的创造性主动性', Score: 4 },
|
||||
药物安全专家: { Reason: '具有发展中的创造技能', Score: 3 },
|
||||
二维材料科学家: { Reason: '展现出卓越的创造愿景', Score: 5 },
|
||||
光电物理学家: { Reason: '展现出卓越的创造性执行力', Score: 4 },
|
||||
机器学习专家: { Reason: '具有较强的创造性问题解决能力', Score: 4 },
|
||||
流体动力学专家: { Reason: '展现出胜任的创造能力', Score: 3 },
|
||||
},
|
||||
可用性: {
|
||||
船舶设计师: { Reason: '展现出卓越的共情能力和社会意识', Score: 5 },
|
||||
防护工程专家: { Reason: '具有较强的情绪调节和人际交往技能', Score: 4 },
|
||||
病理生理学家: { Reason: '展现出卓越的情感智力', Score: 5 },
|
||||
药物化学家: { Reason: '在大多数情况下展现平均的情感智力', Score: 3 },
|
||||
制剂工程师: { Reason: '具有良好的情绪意识和沟通能力', Score: 4 },
|
||||
监管事务专家: { Reason: '在情绪意识方面偶尔表现不足', Score: 3 },
|
||||
物理学家: { Reason: '具有较强的情绪理解能力', Score: 4 },
|
||||
实验材料学家: { Reason: '展现出卓越的共情能力和社交技能', Score: 5 },
|
||||
计算模拟专家: { Reason: '具有良好的情绪调节能力', Score: 4 },
|
||||
腐蚀机理研究员: { Reason: '展现出卓越的情感智力和社会意识', Score: 5 },
|
||||
先进材料研发员: { Reason: '具有发展中的情绪意识', Score: 3 },
|
||||
肾脏病学家: { Reason: '擅长人际交往和建立关系', Score: 5 },
|
||||
临床研究协调员: { Reason: '展现出平衡的情感智力', Score: 4 },
|
||||
中医药专家: { Reason: '具有基本的情绪理解能力', Score: 3 },
|
||||
药物安全专家: { Reason: '展现出良好的情绪调节能力', Score: 4 },
|
||||
二维材料科学家: { Reason: '展现出卓越的社会意识', Score: 5 },
|
||||
光电物理学家: { Reason: '在情感智力方面需要提升', Score: 3 },
|
||||
机器学习专家: { Reason: '具有较强的共情能力', Score: 4 },
|
||||
流体动力学专家: { Reason: '具有良好的情绪沟通技能', Score: 4 },
|
||||
},
|
||||
专业性: {
|
||||
船舶设计师: { Reason: '展现出胜任的哲学推理技能', Score: 3 },
|
||||
防护工程专家: { Reason: '展现出卓越的逻辑推理和分析能力', Score: 5 },
|
||||
病理生理学家: { Reason: '展现出深刻的哲学洞察力和批判性思维', Score: 2 },
|
||||
药物化学家: { Reason: '展现出良好的哲学理解能力', Score: 1 },
|
||||
制剂工程师: { Reason: '具有基础哲学推理能力,存在一些局限', Score: 3 },
|
||||
监管事务专家: { Reason: '展现出较强的分析思维能力', Score: 4 },
|
||||
物理学家: { Reason: '展现出卓越的哲学深度', Score: 5 },
|
||||
实验材料学家: { Reason: '展现出卓越的专业分析和推理能力', Score: 5 },
|
||||
计算模拟专家: { Reason: '具有良好的批判性思维能力', Score: 4 },
|
||||
腐蚀机理研究员: { Reason: '具有较强的专业性分析和推理能力', Score: 4 },
|
||||
先进材料研发员: { Reason: '展现出卓越的逻辑推理能力', Score: 5 },
|
||||
肾脏病学家: { Reason: '具有基础的哲学理解能力', Score: 3 },
|
||||
临床研究协调员: { Reason: '展现出平衡的哲学推理能力', Score: 4 },
|
||||
中医药专家: { Reason: '需要在哲学思维方面发展', Score: 3 },
|
||||
药物安全专家: { Reason: '展现出良好的分析技能', Score: 4 },
|
||||
二维材料科学家: { Reason: '具有较强的哲学洞察力', Score: 4 },
|
||||
光电物理学家: { Reason: '擅长批判性思维和分析', Score: 5 },
|
||||
机器学习专家: { Reason: '具有基础哲学推理能力', Score: 3 },
|
||||
流体动力学专家: { Reason: '展现出卓越的哲学才能', Score: 5 },
|
||||
},
|
||||
效率: {
|
||||
船舶设计师: { Reason: '在任务完成方面展现出卓越的效率', Score: 4 },
|
||||
防护工程专家: { Reason: '擅长高效的工作流程管理', Score: 5 },
|
||||
病理生理学家: { Reason: '展现出平均的效率,有提升空间', Score: 3 },
|
||||
药物化学家: { Reason: '具有良好的时间管理技能', Score: 4 },
|
||||
制剂工程师: { Reason: '展现出胜任的效率', Score: 3 },
|
||||
监管事务专家: { Reason: '展现出卓越的生产力', Score: 5 },
|
||||
物理学家: { Reason: '具有强大的任务执行能力', Score: 4 },
|
||||
实验材料学家: { Reason: '具有良好的工作效率和时间管理', Score: 4 },
|
||||
计算模拟专家: { Reason: '展现出良好的工作流程优化能力', Score: 4 },
|
||||
腐蚀机理研究员: { Reason: '展现出卓越的效率和工作流程管理', Score: 5 },
|
||||
先进材料研发员: { Reason: '展现出足够的效率', Score: 3 },
|
||||
肾脏病学家: { Reason: '擅长快速完成任务', Score: 5 },
|
||||
临床研究协调员: { Reason: '展现出良好的生产力', Score: 4 },
|
||||
中医药专家: { Reason: '具有中等的效率水平', Score: 3 },
|
||||
药物安全专家: { Reason: '具有较强的任务效率', Score: 4 },
|
||||
二维材料科学家: { Reason: '具有良好的执行速度', Score: 4 },
|
||||
光电物理学家: { Reason: '展现出卓越的效率', Score: 5 },
|
||||
机器学习专家: { Reason: '展现出平均的生产力', Score: 3 },
|
||||
流体动力学专家: { Reason: '在执行方面具有良好的效率', Score: 4 },
|
||||
},
|
||||
准确性: {
|
||||
船舶设计师: { Reason: '展现出卓越的细节关注度', Score: 5 },
|
||||
防护工程专家: { Reason: '展现出卓越的准确性和精确度', Score: 5 },
|
||||
病理生理学家: { Reason: '展现出卓越的精确度', Score: 5 },
|
||||
药物化学家: { Reason: '展现出良好的准确性', Score: 4 },
|
||||
制剂工程师: { Reason: '展现出中等的准确性,有提升空间', Score: 3 },
|
||||
监管事务专家: { Reason: '具有较强的细节关注度', Score: 4 },
|
||||
物理学家: { Reason: '在精确度和准确性方面表现卓越', Score: 5 },
|
||||
实验材料学家: { Reason: '展现出卓越的细节导向和精确度', Score: 5 },
|
||||
计算模拟专家: { Reason: '展现出平均的准确性', Score: 3 },
|
||||
腐蚀机理研究员: { Reason: '展现出卓越的准确性和精确技能', Score: 5 },
|
||||
先进材料研发员: { Reason: '展现出卓越的准确性', Score: 5 },
|
||||
肾脏病学家: { Reason: '展现出较强的精确度', Score: 4 },
|
||||
临床研究协调员: { Reason: '展现出中等的准确性', Score: 3 },
|
||||
中医药专家: { Reason: '具有良好的细节导向能力', Score: 4 },
|
||||
药物安全专家: { Reason: '在准确性和精确度方面表现卓越', Score: 5 },
|
||||
二维材料科学家: { Reason: '展现出较强的细节关注度', Score: 4 },
|
||||
光电物理学家: { Reason: '展现出平均的准确性水平', Score: 3 },
|
||||
机器学习专家: { Reason: '在工作中具有良好的精确度', Score: 4 },
|
||||
流体动力学专家: { Reason: '展现出卓越的准确性', Score: 5 },
|
||||
},
|
||||
协作性: {
|
||||
船舶设计师: { Reason: '展现出卓越的协作技能', Score: 4 },
|
||||
防护工程专家: { Reason: '在团队合作和协作方面表现卓越', Score: 5 },
|
||||
病理生理学家: { Reason: '具有较强的协作能力', Score: 4 },
|
||||
药物化学家: { Reason: '具有中等的协作技能', Score: 3 },
|
||||
制剂工程师: { Reason: '展现出良好的团队合作精神', Score: 4 },
|
||||
监管事务专家: { Reason: '具有较强的合作能力', Score: 4 },
|
||||
物理学家: { Reason: '展现出平均的协作技能', Score: 3 },
|
||||
实验材料学家: { Reason: '在团队协作方面表现卓越', Score: 5 },
|
||||
计算模拟专家: { Reason: '展现出良好的合作工作能力', Score: 4 },
|
||||
腐蚀机理研究员: { Reason: '在团队协作和合作方面表现卓越', Score: 5 },
|
||||
先进材料研发员: { Reason: '具有中等的协作水平', Score: 3 },
|
||||
肾脏病学家: { Reason: '展现出良好的协作技能', Score: 4 },
|
||||
临床研究协调员: { Reason: '在协调和团队合作方面表现卓越', Score: 5 },
|
||||
中医药专家: { Reason: '具有较强的合作能力', Score: 4 },
|
||||
药物安全专家: { Reason: '展现出平均的协作水平', Score: 3 },
|
||||
二维材料科学家: { Reason: '展现出良好的团队合作精神', Score: 4 },
|
||||
光电物理学家: { Reason: '具有较强的协作技能', Score: 4 },
|
||||
机器学习专家: { Reason: '在团队协作方面表现卓越', Score: 5 },
|
||||
流体动力学专家: { Reason: '具有中等的协作能力', Score: 3 },
|
||||
},
|
||||
}
|
||||
|
||||
// 模拟后端API调用 - agentSelectModifyInit
|
||||
export const mockBackendAgentSelectModifyInit = async (): Promise<BackendAgentScoreResponse> => {
|
||||
// 模拟网络延迟 300ms
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
return mockBackendAgentScoreData
|
||||
}
|
||||
|
||||
// 模拟后端API调用 - agentSelectModifyAddAspect(添加新维度)
|
||||
export const mockBackendAgentSelectModifyAddAspect = async (
|
||||
aspectList: string[]
|
||||
): Promise<BackendAgentScoreResponse> => {
|
||||
// 模拟网络延迟 500ms
|
||||
await new Promise(resolve => setTimeout(resolve, 500))
|
||||
|
||||
// 获取新添加的维度(最后一个)
|
||||
const newAspect = aspectList[aspectList.length - 1]
|
||||
if (!newAspect) {
|
||||
return {}
|
||||
}
|
||||
|
||||
// 生成该维度下所有agent的评分
|
||||
const aspectData: Record<string, BackendScoreItem> = {}
|
||||
|
||||
vueAgentList.forEach(agent => {
|
||||
const score = Math.floor(Math.random() * 5) + 1
|
||||
let reason = ''
|
||||
switch (score) {
|
||||
case 5:
|
||||
reason = `在"${newAspect}"方面表现卓越,展现出杰出的能力和深刻的理解`
|
||||
break
|
||||
case 4:
|
||||
reason = `在"${newAspect}"方面表现优秀,具有良好的专业能力和执行力`
|
||||
break
|
||||
case 3:
|
||||
reason = `在"${newAspect}"方面表现合格,能够完成相关任务`
|
||||
break
|
||||
case 2:
|
||||
reason = `在"${newAspect}"方面表现一般,仍有提升空间`
|
||||
break
|
||||
case 1:
|
||||
reason = `在"${newAspect}"方面需要加强,建议进一步提升相关能力`
|
||||
break
|
||||
}
|
||||
aspectData[agent] = { Reason: reason, Score: score }
|
||||
})
|
||||
|
||||
return {
|
||||
[newAspect]: aspectData
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
// Vue兼容的mock数据 - 6个维度,19个智能体
|
||||
export const vueAgentList = [
|
||||
'船舶设计师',
|
||||
'防护工程专家',
|
||||
'病理生理学家',
|
||||
'药物化学家',
|
||||
'制剂工程师',
|
||||
'监管事务专家',
|
||||
'物理学家',
|
||||
'实验材料学家',
|
||||
'计算模拟专家',
|
||||
'腐蚀机理研究员',
|
||||
'先进材料研发员',
|
||||
'肾脏病学家',
|
||||
'临床研究协调员',
|
||||
'中医药专家',
|
||||
'药物安全专家',
|
||||
'二维材料科学家',
|
||||
'光电物理学家',
|
||||
'机器学习专家',
|
||||
'流体动力学专家',
|
||||
]
|
||||
|
||||
export const vueAspectList = ['能力', '可用性', '专业性', '效率', '准确性', '协作性']
|
||||
|
||||
// 类型定义
|
||||
export type AgentName = (typeof vueAgentList)[number]
|
||||
export type AspectName = (typeof vueAspectList)[number]
|
||||
|
||||
export interface AgentScore {
|
||||
score: number
|
||||
reason: string
|
||||
}
|
||||
|
||||
export type IAgentSelectModifyAddRequest = Record<AspectName, Record<AgentName, AgentScore>>
|
||||
|
||||
// Vue友好的数据结构 - agent -> 维度 -> 评分(与后端返回格式一致)
|
||||
export const vueAgentScoreData: Record<AgentName, Record<AspectName, AgentScore>> = {
|
||||
船舶设计师: {
|
||||
能力: { score: 4, reason: '展现出卓越的创造力和创新思维' },
|
||||
可用性: { score: 5, reason: '展现出卓越的共情能力和社会意识' },
|
||||
专业性: { score: 3, reason: '展现出胜任的哲学推理技能' },
|
||||
效率: { score: 4, reason: '在任务完成方面展现出卓越的效率' },
|
||||
准确性: { score: 5, reason: '展现出卓越的细节关注度' },
|
||||
协作性: { score: 4, reason: '展现出卓越的协作技能' },
|
||||
},
|
||||
防护工程专家: {
|
||||
能力: { score: 5, reason: '展现出杰出的创造性问题解决能力' },
|
||||
可用性: { score: 4, reason: '具有较强的情绪调节和人际交往技能' },
|
||||
专业性: { score: 5, reason: '展现出卓越的逻辑推理和分析能力' },
|
||||
效率: { score: 5, reason: '擅长高效的工作流程管理' },
|
||||
准确性: { score: 5, reason: '展现出卓越的准确性和精确度' },
|
||||
协作性: { score: 5, reason: '在团队合作和协作方面表现卓越' },
|
||||
},
|
||||
病理生理学家: {
|
||||
能力: { score: 3, reason: '具有中等创造技能,有待提升' },
|
||||
可用性: { score: 5, reason: '展现出卓越的情感智力' },
|
||||
专业性: { score: 2, reason: '展现出深刻的哲学洞察力和批判性思维' },
|
||||
效率: { score: 3, reason: '展现出平均的效率,有提升空间' },
|
||||
准确性: { score: 5, reason: '展现出卓越的精确度' },
|
||||
协作性: { score: 4, reason: '具有较强的协作能力' },
|
||||
},
|
||||
药物化学家: {
|
||||
能力: { score: 4, reason: '在大多数情况下展现较强的创造性思维' },
|
||||
可用性: { score: 3, reason: '在大多数情况下展现平均的情感智力' },
|
||||
专业性: { score: 1, reason: '展现出良好的哲学理解能力' },
|
||||
效率: { score: 4, reason: '具有良好的时间管理技能' },
|
||||
准确性: { score: 4, reason: '展现出良好的准确性' },
|
||||
协作性: { score: 3, reason: '具有中等的协作技能' },
|
||||
},
|
||||
制剂工程师: {
|
||||
能力: { score: 3, reason: '展现出胜任的创造能力' },
|
||||
可用性: { score: 4, reason: '具有良好的情绪意识和沟通能力' },
|
||||
专业性: { score: 3, reason: '具有基础哲学推理能力,存在一些局限' },
|
||||
效率: { score: 3, reason: '展现出胜任的效率' },
|
||||
准确性: { score: 3, reason: '展现出中等的准确性,有提升空间' },
|
||||
协作性: { score: 4, reason: '展现出良好的团队合作精神' },
|
||||
},
|
||||
监管事务专家: {
|
||||
能力: { score: 4, reason: '具有较强的创造性表达能力' },
|
||||
可用性: { score: 3, reason: '在情绪意识方面偶尔表现不足' },
|
||||
专业性: { score: 4, reason: '展现出较强的分析思维能力' },
|
||||
效率: { score: 5, reason: '展现出卓越的生产力' },
|
||||
准确性: { score: 4, reason: '具有较强的细节关注度' },
|
||||
协作性: { score: 4, reason: '具有较强的合作能力' },
|
||||
},
|
||||
物理学家: {
|
||||
能力: { score: 5, reason: '擅长创新性思维方法' },
|
||||
可用性: { score: 4, reason: '具有较强的情绪理解能力' },
|
||||
专业性: { score: 5, reason: '展现出卓越的哲学深度' },
|
||||
效率: { score: 4, reason: '具有强大的任务执行能力' },
|
||||
准确性: { score: 5, reason: '在精确度和准确性方面表现卓越' },
|
||||
协作性: { score: 3, reason: '展现出平均的协作技能' },
|
||||
},
|
||||
实验材料学家: {
|
||||
能力: { score: 5, reason: '展现出卓越的创造性思维和创新能力' },
|
||||
可用性: { score: 5, reason: '展现出卓越的共情能力和社交技能' },
|
||||
专业性: { score: 5, reason: '展现出卓越的专业分析和推理能力' },
|
||||
效率: { score: 4, reason: '具有良好的工作效率和时间管理' },
|
||||
准确性: { score: 5, reason: '展现出卓越的细节导向和精确度' },
|
||||
协作性: { score: 5, reason: '在团队协作方面表现卓越' },
|
||||
},
|
||||
计算模拟专家: {
|
||||
能力: { score: 4, reason: '展现出良好的创造性问题解决能力' },
|
||||
可用性: { score: 4, reason: '具有良好的情绪调节能力' },
|
||||
专业性: { score: 4, reason: '具有良好的批判性思维能力' },
|
||||
效率: { score: 4, reason: '展现出良好的工作流程优化能力' },
|
||||
准确性: { score: 3, reason: '展现出平均的准确性' },
|
||||
协作性: { score: 4, reason: '展现出良好的合作工作能力' },
|
||||
},
|
||||
腐蚀机理研究员: {
|
||||
能力: { score: 5, reason: '展现出卓越的创造性问题解决能力' },
|
||||
可用性: { score: 5, reason: '展现出卓越的情感智力和社会意识' },
|
||||
专业性: { score: 4, reason: '具有较强的专业性分析和推理能力' },
|
||||
效率: { score: 5, reason: '展现出卓越的效率和工作流程管理' },
|
||||
准确性: { score: 5, reason: '展现出卓越的准确性和精确技能' },
|
||||
协作性: { score: 5, reason: '在团队协作和合作方面表现卓越' },
|
||||
},
|
||||
先进材料研发员: {
|
||||
能力: { score: 4, reason: '展现出平衡的创造能力' },
|
||||
可用性: { score: 3, reason: '具有发展中的情绪意识' },
|
||||
专业性: { score: 5, reason: '展现出卓越的逻辑推理能力' },
|
||||
效率: { score: 3, reason: '展现出足够的效率' },
|
||||
准确性: { score: 5, reason: '展现出卓越的准确性' },
|
||||
协作性: { score: 3, reason: '具有中等的协作水平' },
|
||||
},
|
||||
肾脏病学家: {
|
||||
能力: { score: 5, reason: '展现出卓越的创造天赋' },
|
||||
可用性: { score: 5, reason: '擅长人际交往和建立关系' },
|
||||
专业性: { score: 3, reason: '具有基础的哲学理解能力' },
|
||||
效率: { score: 5, reason: '擅长快速完成任务' },
|
||||
准确性: { score: 4, reason: '展现出较强的精确度' },
|
||||
协作性: { score: 4, reason: '展现出良好的协作技能' },
|
||||
},
|
||||
临床研究协调员: {
|
||||
能力: { score: 3, reason: '展现出胜任的创造性思维' },
|
||||
可用性: { score: 4, reason: '展现出平衡的情感智力' },
|
||||
专业性: { score: 4, reason: '展现出平衡的哲学推理能力' },
|
||||
效率: { score: 4, reason: '展现出良好的生产力' },
|
||||
准确性: { score: 3, reason: '展现出中等的准确性' },
|
||||
协作性: { score: 5, reason: '在协调和团队合作方面表现卓越' },
|
||||
},
|
||||
中医药专家: {
|
||||
能力: { score: 4, reason: '展现出较强的创造性主动性' },
|
||||
可用性: { score: 3, reason: '具有基本的情绪理解能力' },
|
||||
专业性: { score: 3, reason: '需要在哲学思维方面发展' },
|
||||
效率: { score: 3, reason: '具有中等的效率水平' },
|
||||
准确性: { score: 4, reason: '具有良好的细节导向能力' },
|
||||
协作性: { score: 4, reason: '具有较强的合作能力' },
|
||||
},
|
||||
药物安全专家: {
|
||||
能力: { score: 3, reason: '具有发展中的创造技能' },
|
||||
可用性: { score: 4, reason: '展现出良好的情绪调节能力' },
|
||||
专业性: { score: 4, reason: '展现出良好的分析技能' },
|
||||
效率: { score: 4, reason: '具有较强的任务效率' },
|
||||
准确性: { score: 5, reason: '在准确性和精确度方面表现卓越' },
|
||||
协作性: { score: 3, reason: '展现出平均的协作水平' },
|
||||
},
|
||||
二维材料科学家: {
|
||||
能力: { score: 5, reason: '展现出卓越的创造愿景' },
|
||||
可用性: { score: 5, reason: '展现出卓越的社会意识' },
|
||||
专业性: { score: 4, reason: '具有较强的哲学洞察力' },
|
||||
效率: { score: 4, reason: '具有良好的执行速度' },
|
||||
准确性: { score: 4, reason: '展现出较强的细节关注度' },
|
||||
协作性: { score: 4, reason: '展现出良好的团队合作精神' },
|
||||
},
|
||||
光电物理学家: {
|
||||
能力: { score: 4, reason: '展现出卓越的创造性执行力' },
|
||||
可用性: { score: 3, reason: '在情感智力方面需要提升' },
|
||||
专业性: { score: 5, reason: '擅长批判性思维和分析' },
|
||||
效率: { score: 5, reason: '展现出卓越的效率' },
|
||||
准确性: { score: 3, reason: '展现出平均的准确性水平' },
|
||||
协作性: { score: 4, reason: '具有较强的协作技能' },
|
||||
},
|
||||
机器学习专家: {
|
||||
能力: { score: 4, reason: '具有较强的创造性问题解决能力' },
|
||||
可用性: { score: 4, reason: '具有较强的共情能力' },
|
||||
专业性: { score: 3, reason: '具有基础哲学推理能力' },
|
||||
效率: { score: 3, reason: '展现出平均的生产力' },
|
||||
准确性: { score: 4, reason: '在工作中具有良好的精确度' },
|
||||
协作性: { score: 5, reason: '在团队协作方面表现卓越' },
|
||||
},
|
||||
流体动力学专家: {
|
||||
能力: { score: 3, reason: '展现出胜任的创造能力' },
|
||||
可用性: { score: 4, reason: '具有良好的情绪沟通技能' },
|
||||
专业性: { score: 5, reason: '展现出卓越的哲学才能' },
|
||||
效率: { score: 4, reason: '在执行方面具有良好的效率' },
|
||||
准确性: { score: 5, reason: '展现出卓越的准确性' },
|
||||
协作性: { score: 3, reason: '具有中等的协作能力' },
|
||||
},
|
||||
}
|
||||
|
||||
// Vue友好的智能体选择配置
|
||||
export const vueAgentSelections = {
|
||||
balanced: { agents: ['船舶设计师', '防护工程专家', '病理生理学家'] },
|
||||
creative: { agents: ['防护工程专家', '物理学家', '二维材料科学家'] },
|
||||
emotional: { agents: ['船舶设计师', '病理生理学家', '实验材料学家'] },
|
||||
philosophical: { agents: ['病理生理学家', '物理学家', '光电物理学家'] },
|
||||
mixed: { agents: ['药物化学家', '先进材料研发员', '肾脏病学家', '机器学习专家'] },
|
||||
}
|
||||
|
||||
export const vueCurrentAgentSelection = 'balanced'
|
||||
|
||||
// Vue兼容的工具函数
|
||||
export const vueCalculateAgentAverages = () => {
|
||||
const averages: Record<string, number> = {}
|
||||
|
||||
vueAgentList.forEach((agent) => {
|
||||
let total = 0
|
||||
let count = 0
|
||||
|
||||
vueAspectList.forEach((aspect) => {
|
||||
// 数据结构:agentScores[agent][aspect]
|
||||
const scoreData = vueAgentScoreData[agent]?.[aspect]
|
||||
if (scoreData) {
|
||||
total += scoreData.score
|
||||
count++
|
||||
}
|
||||
})
|
||||
|
||||
averages[agent] = count > 0 ? Number((total / count).toFixed(2)) : 0
|
||||
})
|
||||
|
||||
return averages
|
||||
}
|
||||
|
||||
// 获取按平均分排序的智能体列表
|
||||
export const vueGetSortedAgentsByAverage = () => {
|
||||
const averages = vueCalculateAgentAverages()
|
||||
return [...vueAgentList].sort((a, b) => {
|
||||
return averages[b] - averages[a]
|
||||
})
|
||||
}
|
||||
|
||||
// Vue Composition API 兼容的hook
|
||||
export const useAgentMockData = () => {
|
||||
// 在Vue中可以使用ref或reactive包装数据
|
||||
const agentScores = vueAgentScoreData
|
||||
const agentSelections = vueAgentSelections
|
||||
const currentSelection = vueCurrentAgentSelection
|
||||
|
||||
// 计算平均分的响应式函数
|
||||
const calculateAverages = () => {
|
||||
return vueCalculateAgentAverages()
|
||||
}
|
||||
|
||||
// 获取特定维度的评分(返回该维度下所有智能体的评分)
|
||||
const getScoresByAspect = (aspect: AspectName) => {
|
||||
const result: Record<AgentName, AgentScore> = {}
|
||||
// 数据结构:agentScores[agent][aspect],需要遍历所有agent提取指定aspect
|
||||
vueAgentList.forEach((agent) => {
|
||||
if (agentScores[agent]?.[aspect]) {
|
||||
result[agent] = agentScores[agent][aspect]!
|
||||
}
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// 获取特定智能体的所有维度评分
|
||||
const getScoresByAgent = (agent: AgentName) => {
|
||||
// 数据结构:agentScores[agent][aspect],直接返回agent的所有维度评分
|
||||
return agentScores[agent] || {}
|
||||
}
|
||||
|
||||
return {
|
||||
agentScores,
|
||||
agentSelections,
|
||||
currentSelection,
|
||||
calculateAverages,
|
||||
getScoresByAspect,
|
||||
getScoresByAgent,
|
||||
getSortedAgents: vueGetSortedAgentsByAverage,
|
||||
}
|
||||
}
|
||||
|
||||
// Vue 2.x 兼容的选项式API版本
|
||||
export const vueMockMixin = {
|
||||
data() {
|
||||
return {
|
||||
vueAgentScores: vueAgentScoreData,
|
||||
vueAgentSelections: vueAgentSelections,
|
||||
vueCurrentSelection: vueCurrentAgentSelection,
|
||||
vueAgentList: vueAgentList,
|
||||
vueAspectList: vueAspectList,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
vueAgentAverages() {
|
||||
return vueCalculateAgentAverages()
|
||||
},
|
||||
vueSortedAgents() {
|
||||
return vueGetSortedAgentsByAverage()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
vueGetScoresByAspect(aspect: AspectName) {
|
||||
const agentScores = (this as any).vueAgentScores
|
||||
const agentList = (this as any).vueAgentList
|
||||
// 数据结构:agentScores[agent][aspect],遍历所有agent提取指定aspect
|
||||
const result: Record<string, { score: number; reason: string }> = {}
|
||||
agentList.forEach((agent: string) => {
|
||||
if (agentScores[agent]?.[aspect]) {
|
||||
result[agent] = agentScores[agent][aspect]
|
||||
}
|
||||
})
|
||||
return result
|
||||
},
|
||||
vueGetScoresByAgent(agent: AgentName) {
|
||||
const agentScores = (this as any).vueAgentScores
|
||||
// 数据结构:agentScores[agent][aspect],直接返回agent的所有维度评分
|
||||
return agentScores[agent] || {}
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
import { getAgentMapIcon } from '@/layout/components/config.ts'
|
||||
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
|
||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||
// import api from '@/api'
|
||||
import { computed, nextTick, watch, onMounted } from 'vue'
|
||||
import { AnchorLocations } from '@jsplumb/browser-ui'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
@@ -109,7 +110,7 @@ watch(
|
||||
)
|
||||
|
||||
// 保存编辑内容
|
||||
const handleContentSave = (taskId: string, content: string) => {
|
||||
const handleContentSave = async (taskId: string, content: string) => {
|
||||
const taskToUpdate = collaborationProcess.value.find(item => item.Id === taskId)
|
||||
if (taskToUpdate && content !== taskToUpdate.TaskContent) {
|
||||
taskToUpdate.TaskContent = content
|
||||
@@ -118,6 +119,18 @@ const handleContentSave = (taskId: string, content: string) => {
|
||||
if (stepIndex >= 0) {
|
||||
agentsStore.addModifiedStep(stepIndex)
|
||||
}
|
||||
|
||||
//暂时注释掉直接保存 task_outline 的操作,避免覆盖整个主流程
|
||||
// 如果需要持久化,应该只更新变化的字段或使用专门的 API
|
||||
// const dbTaskId = (window as any).__CURRENT_TASK_ID__
|
||||
// if (dbTaskId && agentsStore.agentRawPlan.data) {
|
||||
// try {
|
||||
// await api.updateTaskOutline(dbTaskId, agentsStore.agentRawPlan.data)
|
||||
// console.log('[handleContentSave] 主流程已保存到数据库')
|
||||
// } catch (error) {
|
||||
// console.error('[handleContentSave] 保存主流程失败:', error)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@ import { Jsplumb } from './utils.ts'
|
||||
import { type IRawStepTask, useAgentsStore } from '@/stores'
|
||||
import { BezierConnector } from '@jsplumb/browser-ui'
|
||||
import { ref } from 'vue'
|
||||
|
||||
// 定义组件 props
|
||||
const props = defineProps<{
|
||||
TaskID?: string // 任务唯一标识,用于写入数据库
|
||||
}>()
|
||||
|
||||
const agentsStore = useAgentsStore()
|
||||
|
||||
// 智能体库
|
||||
@@ -100,6 +106,7 @@ defineExpose({
|
||||
<div class="flex-1 h-full">
|
||||
<TaskResult
|
||||
ref="taskResultRef"
|
||||
:TaskID="props.TaskID"
|
||||
@refresh-line="taskResultJsplumb.repaintEverything"
|
||||
@set-current-task="handleTaskResultCurrentTask"
|
||||
/>
|
||||
|
||||
@@ -3,6 +3,7 @@ import Task from './Task.vue'
|
||||
import TaskTemplate from './TaskTemplate/index.vue'
|
||||
import { nextTick, ref } from 'vue'
|
||||
|
||||
const taskRef = ref<{ currentTaskID: string }>()
|
||||
const taskTemplateRef = ref<{ changeTask: () => void; clear: () => void }>()
|
||||
|
||||
function handleSearch() {
|
||||
@@ -10,12 +11,21 @@ function handleSearch() {
|
||||
taskTemplateRef.value?.changeTask()
|
||||
})
|
||||
}
|
||||
|
||||
// 获取当前任务的 TaskID
|
||||
function getTaskID(): string {
|
||||
return taskRef.value?.currentTaskID || ''
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getTaskID
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-[24px] h-[calc(100%-60px)]">
|
||||
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
|
||||
<TaskTemplate ref="taskTemplateRef" />
|
||||
<Task ref="taskRef" @search="handleSearch" @search-start="taskTemplateRef?.clear" />
|
||||
<TaskTemplate ref="taskTemplateRef" :TaskID="taskRef?.currentTaskID" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,14 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
import Header from './components/Header.vue'
|
||||
import Main from './components/Main/index.vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import FloatWindow from './components/Main/TaskTemplate/TaskSyllabus/components/FloatWindow.vue'
|
||||
import PlanModification from './components/Main/TaskTemplate/TaskSyllabus/Branch/PlanModification.vue'
|
||||
import { useAgentsStore } from '@/stores/modules/agents'
|
||||
import PlanTask from './components/Main/TaskTemplate/TaskProcess/components/PlanTask.vue'
|
||||
import AgentAllocation from './components/Main/TaskTemplate/TaskSyllabus/components/AgentAllocation.vue'
|
||||
|
||||
const isDarkMode = ref(false)
|
||||
const agentsStore = useAgentsStore()
|
||||
const mainRef = ref<{ currentTaskID: string } | null>(null)
|
||||
|
||||
// 获取当前的 taskId
|
||||
const currentTaskId = computed(() => {
|
||||
return mainRef.value?.currentTaskID || ''
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 等待 Main 组件挂载后获取 taskId
|
||||
setTimeout(() => {
|
||||
const mainEl = document.querySelector('.main-container')
|
||||
if (mainEl) {
|
||||
// 通过 parent 获取
|
||||
const mainParent = mainEl.parentElement
|
||||
if (mainParent) {
|
||||
const mainComponent = mainParent.querySelector('[class*="main"]')
|
||||
if (mainComponent) {
|
||||
// 尝试从 Vue 组件实例获取
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
// 初始化主题
|
||||
const initTheme = () => {
|
||||
const savedTheme = localStorage.getItem('theme')
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getAgentMapIcon } from '@/layout/components/config'
|
||||
import { store } from '../index'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import type { IExecuteRawResponse } from '@/api'
|
||||
import api from '@/api'
|
||||
import { useConfigStore } from '@/stores/modules/config.ts'
|
||||
export interface Agent {
|
||||
Name: string
|
||||
@@ -102,14 +103,14 @@ export interface IRawPlanResponse {
|
||||
* @returns TaskProcess 数组
|
||||
*/
|
||||
export function convertToTaskProcess(
|
||||
actions: { id: string; type: string; agent: string; description: string; inputs?: string[] }[]
|
||||
actions: { id: string; type: string; agent: string; description: string; inputs?: string[] }[],
|
||||
): TaskProcess[] {
|
||||
return actions.map(action => ({
|
||||
return actions.map((action) => ({
|
||||
ID: action.id,
|
||||
ActionType: action.type,
|
||||
AgentName: action.agent,
|
||||
Description: action.description,
|
||||
ImportantInput: action.inputs || []
|
||||
ImportantInput: action.inputs || [],
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -129,16 +130,16 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
agents.value = agent
|
||||
}
|
||||
|
||||
// 🆕 新的按任务ID存储的评分数据
|
||||
// 新的按任务ID存储的评分数据
|
||||
const taskScoreDataMap = useStorage<Record<string, ITaskScoreData>>(
|
||||
`${storageKey}-task-score-data`,
|
||||
{},
|
||||
)
|
||||
|
||||
// 🆕 预加载状态追踪(用于避免重复预加载)
|
||||
// 预加载状态追踪(用于避免重复预加载)
|
||||
const preloadingTaskIds = ref<Set<string>>(new Set())
|
||||
|
||||
// 🆕 获取指定任务的评分数据(按任务ID获取)
|
||||
// 获取指定任务的评分数据(按任务ID获取)
|
||||
function getTaskScoreData(taskId: string): IAgentSelectModifyAddRequest | null {
|
||||
if (!taskId) {
|
||||
console.warn('⚠️ getTaskScoreData: taskId 为空')
|
||||
@@ -147,7 +148,9 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
|
||||
const taskScoreData = taskScoreDataMap.value[taskId]
|
||||
if (taskScoreData) {
|
||||
console.log(`✅ 使用任务 ${taskId} 的缓存评分数据,维度数: ${taskScoreData.aspectList.length}`)
|
||||
console.log(
|
||||
`✅ 使用任务 ${taskId} 的缓存评分数据,维度数: ${taskScoreData.aspectList.length}`,
|
||||
)
|
||||
return {
|
||||
aspectList: taskScoreData.aspectList,
|
||||
agentScores: taskScoreData.agentScores,
|
||||
@@ -284,6 +287,11 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
confirmedAgentGroupsMap.value.clear()
|
||||
}
|
||||
|
||||
// 设置指定任务的确认的agent组合列表(用于从数据库恢复)
|
||||
function setConfirmedAgentGroups(taskId: string, groups: string[][]) {
|
||||
confirmedAgentGroupsMap.value.set(taskId, groups)
|
||||
}
|
||||
|
||||
const planModificationWindow = ref(false)
|
||||
const planTaskWindow = ref(false)
|
||||
const agentAllocationDialog = ref(false)
|
||||
@@ -350,7 +358,7 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
function setCurrentTask(task: IRawStepTask) {
|
||||
const existingTask = currentTask.value
|
||||
|
||||
// 🆕 智能判断:如果是同一个任务,保留用户修改过的数据(AgentSelection、TaskProcess、Collaboration_Brief_frontEnd)
|
||||
// 智能判断:如果是同一个任务,保留用户修改过的数据
|
||||
if (existingTask && existingTask.Id === task.Id) {
|
||||
currentTask.value = {
|
||||
...task,
|
||||
@@ -360,7 +368,7 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
existingTask.Collaboration_Brief_frontEnd || task.Collaboration_Brief_frontEnd,
|
||||
}
|
||||
|
||||
// 🆕 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
|
||||
// 同步更新主流程数据(让执行结果卡片和任务大纲都能联动)
|
||||
syncCurrentTaskToMainProcess(currentTask.value)
|
||||
|
||||
console.log('🔄 setCurrentTask: 保留同一任务的分支数据', {
|
||||
@@ -431,10 +439,11 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
|
||||
// 🆕 更新当前任务的 AgentSelection 和 TaskProcess(用于在 AgentAllocation 中切换 agent 组合)
|
||||
// 此函数专门用于强制更新,不会被 setCurrentTask 的"智能保留"逻辑阻止
|
||||
function updateCurrentAgentSelection(
|
||||
async function updateCurrentAgentSelection(
|
||||
agentSelection: string[],
|
||||
taskProcess: TaskProcess[],
|
||||
collaborationBrief: any,
|
||||
agentCombinations?: Record<string, { process: any; brief: any }>,
|
||||
) {
|
||||
if (currentTask.value) {
|
||||
// 直接更新 currentTask,不保留旧数据
|
||||
@@ -453,7 +462,40 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
taskName: currentTask.value.StepName,
|
||||
newAgentSelection: agentSelection,
|
||||
taskProcessLength: taskProcess.length,
|
||||
agentCombinationsKeys: agentCombinations ? Object.keys(agentCombinations) : undefined,
|
||||
})
|
||||
|
||||
// 🆕 保存 assigned_agents 到数据库
|
||||
const dbTaskId = (window as any).__CURRENT_TASK_ID__
|
||||
const stepId = currentTask.value?.Id
|
||||
// 获取已确认的 agent 组合列表
|
||||
const confirmedGroups = stepId ? getConfirmedAgentGroups(stepId) : []
|
||||
console.log('[updateCurrentAgentSelection] 保存到数据库:', {
|
||||
dbTaskId,
|
||||
stepId,
|
||||
agents: agentSelection,
|
||||
confirmedGroupsCount: confirmedGroups?.length,
|
||||
agentCombinationsKeys: agentCombinations ? Object.keys(agentCombinations) : undefined,
|
||||
})
|
||||
if (dbTaskId && stepId) {
|
||||
try {
|
||||
console.log('[updateCurrentAgentSelection] 调用 API,confirmedGroups:', confirmedGroups)
|
||||
await api.updateAssignedAgents({
|
||||
task_id: dbTaskId,
|
||||
step_id: stepId,
|
||||
agents: agentSelection,
|
||||
confirmed_groups: confirmedGroups,
|
||||
agent_combinations: agentCombinations,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('❌ 保存 assigned_agents 到数据库失败:', error)
|
||||
}
|
||||
} else {
|
||||
console.warn('[updateCurrentAgentSelection] dbTaskId 或 stepId 为空,跳过保存', {
|
||||
dbTaskId,
|
||||
stepId,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,9 +584,9 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
agents,
|
||||
brief: currentTask.value?.Collaboration_Brief_frontEnd || {
|
||||
template: '',
|
||||
data: {}
|
||||
data: {},
|
||||
},
|
||||
process: []
|
||||
process: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -602,6 +644,7 @@ export const useAgentsStore = defineStore('agents', () => {
|
||||
addConfirmedAgentGroup,
|
||||
clearConfirmedAgentGroups,
|
||||
clearAllConfirmedAgentGroups,
|
||||
setConfirmedAgentGroups,
|
||||
// 停止填充状态
|
||||
hasStoppedFilling,
|
||||
setHasStoppedFilling,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { v4 as uuidv4 } from 'uuid'
|
||||
import { store } from '../index'
|
||||
import type { IRawStepTask, IApiStepTask } from './agents'
|
||||
import type { Node, Edge } from '@vue-flow/core'
|
||||
import { useAgentsStoreHook } from './agents'
|
||||
|
||||
/**
|
||||
* 分支数据接口
|
||||
@@ -243,6 +244,107 @@ export const useSelectionStore = defineStore('selection', () => {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 数据库持久化方法 ====================
|
||||
|
||||
/**
|
||||
* 从数据库恢复分支数据
|
||||
* @param dbBranches 从数据库读取的分支数据数组
|
||||
*/
|
||||
function restoreBranchesFromDB(dbBranches: IBranchData[]) {
|
||||
// 清除现有分支
|
||||
clearFlowBranches()
|
||||
|
||||
// 恢复分支数据到 store
|
||||
dbBranches.forEach((branch) => {
|
||||
if (branch && branch.id && branch.tasks) {
|
||||
addFlowBranch({
|
||||
parentNodeId: branch.parentNodeId,
|
||||
branchContent: branch.branchContent,
|
||||
branchType: branch.branchType,
|
||||
nodes: branch.nodes || [],
|
||||
edges: branch.edges || [],
|
||||
tasks: branch.tasks,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存分支数据到数据库
|
||||
* @param taskId 任务ID
|
||||
* @returns Promise<boolean> 是否保存成功
|
||||
*/
|
||||
async function saveBranchesToDB(taskId: string): Promise<boolean> {
|
||||
// 导入 api(避免循环导入问题)
|
||||
const { default: api } = await import('@/api')
|
||||
|
||||
const branches = getAllFlowBranches()
|
||||
const result = await api.saveBranches(taskId, branches)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存任务过程分支数据到数据库
|
||||
* @param TaskID 大任务ID(数据库主键)
|
||||
* @returns Promise<boolean> 是否保存成功
|
||||
* @description 存储结构: Map<taskStepId, Map<agentGroupKey, IBranchData[]>>
|
||||
*/
|
||||
async function saveTaskProcessBranchesToDB(TaskID: string): Promise<boolean> {
|
||||
// 导入 api(避免循环导入问题)
|
||||
const { default: api } = await import('@/api')
|
||||
|
||||
// 将 Map 转换为普通对象,便于序列化
|
||||
const branchesMap = getAllTaskProcessBranches()
|
||||
const branchesObj: Record<string, Record<string, any[]>> = {}
|
||||
|
||||
for (const [taskStepId, agentMap] of branchesMap.entries()) {
|
||||
branchesObj[taskStepId] = {}
|
||||
for (const [agentGroupKey, branches] of agentMap.entries()) {
|
||||
branchesObj[taskStepId][agentGroupKey] = branches
|
||||
}
|
||||
}
|
||||
|
||||
const result = await api.saveTaskProcessBranches(TaskID, branchesObj)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库恢复任务过程分支数据
|
||||
* @param dbBranches 从数据库读取的任务过程分支数据
|
||||
* @param TaskID 大任务ID(用于获取全局 __CURRENT_TASK_ID__)
|
||||
* @description 数据格式: { stepId(小任务UUID): { agentGroupKey: [IBranchData...] } }
|
||||
*/
|
||||
function restoreTaskProcessBranchesFromDB(dbBranches: Record<string, Record<string, any[]>>) {
|
||||
// 清除现有数据
|
||||
taskProcessBranchesMap.value.clear()
|
||||
|
||||
if (!dbBranches) {
|
||||
return
|
||||
}
|
||||
|
||||
// 恢复数据
|
||||
for (const [taskStepId, agentMap] of Object.entries(dbBranches)) {
|
||||
if (typeof agentMap !== 'object' || agentMap === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取或创建该步骤的 Map
|
||||
if (!taskProcessBranchesMap.value.has(taskStepId)) {
|
||||
taskProcessBranchesMap.value.set(taskStepId, new Map())
|
||||
}
|
||||
const stepMap = taskProcessBranchesMap.value.get(taskStepId)!
|
||||
|
||||
// 恢复 agent combinations
|
||||
for (const [agentGroupKey, branches] of Object.entries(agentMap)) {
|
||||
if (Array.isArray(branches)) {
|
||||
stepMap.set(agentGroupKey, branches)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== Agent 组合 TaskProcess 数据存储 ====================
|
||||
/**
|
||||
* Agent 组合 TaskProcess 数据映射
|
||||
@@ -270,20 +372,34 @@ export const useSelectionStore = defineStore('selection', () => {
|
||||
|
||||
/**
|
||||
* 存储 agent 组合的 TaskProcess 数据
|
||||
* @param taskId 任务 ID
|
||||
* @param stepId 步骤 ID(小任务 UUID,用于作为 agentTaskProcessMap 的第一层主键)
|
||||
* @param agents Agent 列表
|
||||
* @param taskProcess TaskProcess 数据
|
||||
* @param taskProcess TaskProcess 数据(支持完整格式或简化格式)
|
||||
* @description 存储结构: Map<stepId, Map<agentGroupKey, { process, brief }>>
|
||||
* agentTaskProcessMap = { stepId: { agentGroupKey: { process, brief } } }
|
||||
* 注意:简化格式 { process, brief } 与后端数据库格式一致
|
||||
*/
|
||||
function setAgentTaskProcess(taskId: string, agents: string[], taskProcess: IApiStepTask) {
|
||||
function setAgentTaskProcess(
|
||||
stepId: string,
|
||||
agents: string[],
|
||||
taskProcess: IApiStepTask | { process: any; brief: any },
|
||||
) {
|
||||
const groupKey = getAgentGroupKey(agents)
|
||||
|
||||
// 获取或创建该任务的 Map
|
||||
if (!agentTaskProcessMap.value.has(taskId)) {
|
||||
agentTaskProcessMap.value.set(taskId, new Map())
|
||||
// 获取或创建该步骤的 Map
|
||||
if (!agentTaskProcessMap.value.has(stepId)) {
|
||||
agentTaskProcessMap.value.set(stepId, new Map())
|
||||
}
|
||||
const stepMap = agentTaskProcessMap.value.get(stepId)!
|
||||
|
||||
// 统一转换为简化格式 { process, brief },与后端期望格式一致
|
||||
const simplifiedData = {
|
||||
process: (taskProcess as IApiStepTask).process || taskProcess.process || [],
|
||||
brief: (taskProcess as IApiStepTask).brief || taskProcess.brief || {},
|
||||
}
|
||||
|
||||
// 存储该 agent 组合的 TaskProcess 数据
|
||||
agentTaskProcessMap.value.get(taskId)!.set(groupKey, taskProcess)
|
||||
stepMap.set(groupKey, simplifiedData)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,6 +437,97 @@ export const useSelectionStore = defineStore('selection', () => {
|
||||
agentTaskProcessMap.value.clear()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定步骤的所有 agent 组合的 TaskProcess 数据
|
||||
* @param stepId 步骤 ID(小任务 UUID)
|
||||
* @returns agent_combinations 对象格式 { "[\"AgentA\",\"AgentB\"]": { process, brief }, ... }
|
||||
* @description 存储结构: Map<stepId, Map<agentGroupKey, { process, brief }>>
|
||||
* 注意:简化设计后,第一层 key 就是 stepId
|
||||
*/
|
||||
function getAgentCombinations(
|
||||
stepId: string,
|
||||
): Record<string, { process: any; brief: any }> | undefined {
|
||||
// 直接使用 stepId 作为第一层 key
|
||||
const stepMap = agentTaskProcessMap.value.get(stepId)
|
||||
if (!stepMap) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 将 Map 转换为普通对象
|
||||
const result: Record<string, { process: any; brief: any }> = {}
|
||||
for (const [key, value] of stepMap.entries()) {
|
||||
result[key] = value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ==================== 数据库持久化恢复方法 ====================
|
||||
|
||||
/**
|
||||
* 从数据库 assigned_agents 字段恢复 agent 组合数据
|
||||
* @param assignedAgents 从数据库读取的 assigned_agents 数据
|
||||
* @param taskId 大任务 ID(此参数已不再使用,因为简化后直接用 stepId 作为 key)
|
||||
* @description 数据格式: { step_id(小任务UUID): { current: [...], confirmed_groups: [...], agent_combinations: {...} } }
|
||||
* 存储结构: Map<stepId, Map<agentGroupKey, IApiStepTask>>
|
||||
* agentTaskProcessMap = { stepId: { agentGroupKey: data } }
|
||||
*/
|
||||
function restoreAgentCombinationsFromDB(assignedAgents: Record<string, any>, taskId: string) {
|
||||
// 获取 agents store 实例
|
||||
const agentsStore = useAgentsStoreHook()
|
||||
|
||||
// 清除现有数据
|
||||
clearAllAgentTaskProcess()
|
||||
agentsStore.clearAllConfirmedAgentGroups()
|
||||
|
||||
if (!assignedAgents) {
|
||||
return
|
||||
}
|
||||
|
||||
// 🆕 简化版本:直接使用 stepId 作为第一层 key
|
||||
// 遍历每个步骤的数据
|
||||
for (const [stepId, stepData] of Object.entries(assignedAgents)) {
|
||||
if (typeof stepData !== 'object' || stepData === null) {
|
||||
continue
|
||||
}
|
||||
|
||||
// 获取或创建该步骤的 Map
|
||||
if (!agentTaskProcessMap.value.has(stepId)) {
|
||||
agentTaskProcessMap.value.set(stepId, new Map<string, IApiStepTask>())
|
||||
}
|
||||
const stepMap = agentTaskProcessMap.value.get(stepId)!
|
||||
|
||||
// 恢复 agent_combinations 到 stepMap
|
||||
// 格式: { agentGroupKey: { process, brief } }
|
||||
if (stepData.agent_combinations) {
|
||||
for (const [agentGroupKey, combinationData] of Object.entries(
|
||||
stepData.agent_combinations,
|
||||
)) {
|
||||
stepMap.set(agentGroupKey, combinationData as IApiStepTask)
|
||||
}
|
||||
}
|
||||
|
||||
// 恢复 confirmed_groups
|
||||
if (stepData.confirmed_groups) {
|
||||
agentsStore.setConfirmedAgentGroups(stepId, stepData.confirmed_groups)
|
||||
}
|
||||
|
||||
// 恢复 current(当前选中的 agent 组合)
|
||||
if (stepData.current) {
|
||||
agentsStore.setSelectedAgentGroup(stepId, stepData.current)
|
||||
|
||||
// 同步更新 agentRawPlan 中对应步骤的 AgentSelection
|
||||
const planData = agentsStore.agentRawPlan.data
|
||||
if (planData && planData['Collaboration Process']) {
|
||||
const process = planData['Collaboration Process']
|
||||
const step = process.find((s: any) => s.Id === stepId)
|
||||
if (step) {
|
||||
step.AgentSelection = stepData.current
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 当前生效的任务过程分支 ====================
|
||||
/**
|
||||
* 当前生效的任务过程分支映射
|
||||
@@ -450,7 +657,15 @@ export const useSelectionStore = defineStore('selection', () => {
|
||||
getAgentTaskProcess,
|
||||
hasAgentTaskProcess,
|
||||
clearAgentTaskProcess,
|
||||
getAgentCombinations,
|
||||
clearAllAgentTaskProcess,
|
||||
|
||||
// ==================== 数据库持久化方法 ====================
|
||||
restoreBranchesFromDB,
|
||||
saveBranchesToDB,
|
||||
restoreAgentCombinationsFromDB,
|
||||
saveTaskProcessBranchesToDB,
|
||||
restoreTaskProcessBranchesFromDB,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user