Compare commits

..

6 Commits
main ... pku

Author SHA1 Message Date
Nex Zhu
9a74d2813e Merge branch 'main' into pku 2025-11-20 09:42:26 +08:00
wuyifan18
f50dcd2fe7 update 2025-10-21 10:13:14 +08:00
wuyifan18
744945f68d update 2025-10-21 10:09:18 +08:00
wuyifan18
25853d8174 update 2025-10-19 18:54:11 +08:00
wuyifan18
ceb57acd23 update 2025-10-19 18:14:21 +08:00
wuyifan18
da2318a40c update 2025-10-18 14:31:34 +08:00
120 changed files with 16202 additions and 14561 deletions

View File

@ -1,30 +1,24 @@
import asyncio
from openai import OpenAI, AsyncOpenAI
import openai
import yaml
from termcolor import colored
import os
# Helper function to avoid circular import
def print_colored(text, text_color="green", background="on_white"):
print(colored(text, text_color, background))
# load config (apikey, apibase, model)
yaml_file = os.path.join(os.getcwd(), "config", "config.yaml")
try:
with open(yaml_file, "r", encoding="utf-8") as file:
yaml_data = yaml.safe_load(file)
except Exception:
yaml_data = {}
yaml_file = {}
OPENAI_API_BASE = os.getenv("OPENAI_API_BASE") or yaml_data.get(
"OPENAI_API_BASE", "https://api.openai.com"
)
openai.api_base = OPENAI_API_BASE
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") or yaml_data.get(
"OPENAI_API_KEY", ""
)
# Initialize OpenAI clients
client = OpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_API_BASE)
async_client = AsyncOpenAI(api_key=OPENAI_API_KEY, base_url=OPENAI_API_BASE)
openai.api_key = OPENAI_API_KEY
MODEL: str = os.getenv("OPENAI_API_MODEL") or yaml_data.get(
"OPENAI_API_MODEL", "gpt-4-turbo-preview"
)
@ -77,14 +71,14 @@ def LLM_Completion(
async def _achat_completion_stream_groq(messages: list[dict]) -> str:
from groq import AsyncGroq
groq_client = AsyncGroq(api_key=GROQ_API_KEY)
client = AsyncGroq(api_key=GROQ_API_KEY)
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(
stream = await client.chat.completions.create(
messages=messages,
# model='gemma-7b-it',
model="mixtral-8x7b-32768",
@ -98,9 +92,9 @@ async def _achat_completion_stream_groq(messages: list[dict]) -> str:
if attempt < max_attempts - 1: # i is zero indexed
continue
else:
raise Exception("failed")
raise "failed"
full_reply_content = response.choices[0].message.content
full_reply_content = stream.choices[0].message.content
print(colored(full_reply_content, "blue", "on_white"), end="")
print()
return full_reply_content
@ -109,14 +103,14 @@ async def _achat_completion_stream_groq(messages: list[dict]) -> str:
async def _achat_completion_stream_mixtral(messages: list[dict]) -> str:
from mistralai.client import MistralClient
from mistralai.models.chat_completion import ChatMessage
mistral_client = MistralClient(api_key=MISTRAL_API_KEY)
client = MistralClient(api_key=MISTRAL_API_KEY)
# client=AsyncGroq(api_key=GROQ_API_KEY)
max_attempts = 5
for attempt in range(max_attempts):
try:
messages[len(messages) - 1]["role"] = "user"
stream = mistral_client.chat(
stream = client.chat(
messages=[
ChatMessage(
role=message["role"], content=message["content"]
@ -125,13 +119,14 @@ async def _achat_completion_stream_mixtral(messages: list[dict]) -> str:
],
# model = "mistral-small-latest",
model="open-mixtral-8x7b",
# response_format={"type": "json_object"},
)
break # If the operation is successful, break the loop
except Exception:
if attempt < max_attempts - 1: # i is zero indexed
continue
else:
raise Exception("failed")
raise "failed"
full_reply_content = stream.choices[0].message.content
print(colored(full_reply_content, "blue", "on_white"), end="")
@ -140,11 +135,15 @@ async def _achat_completion_stream_mixtral(messages: list[dict]) -> str:
async def _achat_completion_stream_gpt35(messages: list[dict]) -> str:
response = await async_client.chat.completions.create(
openai.api_key = OPENAI_API_KEY
openai.api_base = OPENAI_API_BASE
response = await openai.ChatCompletion.acreate(
messages=messages,
max_tokens=4096,
n=1,
stop=None,
temperature=0.3,
timeout=30,
timeout=3,
model="gpt-3.5-turbo-16k",
stream=True,
)
@ -155,33 +154,40 @@ async def _achat_completion_stream_gpt35(messages: list[dict]) -> str:
# iterate through the stream of events
async for chunk in response:
collected_chunks.append(chunk) # save the event response
choices = chunk.choices
choices = chunk["choices"]
if len(choices) > 0:
chunk_message = chunk.choices[0].delta
chunk_message = chunk["choices"][0].get(
"delta", {}
) # extract the message
collected_messages.append(chunk_message) # save the message
if chunk_message.content:
if "content" in chunk_message:
print(
colored(chunk_message.content, "blue", "on_white"),
colored(chunk_message["content"], "blue", "on_white"),
end="",
)
print()
full_reply_content = "".join(
[m.content or "" for m in collected_messages if m is not None]
[m.get("content", "") for m in collected_messages]
)
return full_reply_content
async def _achat_completion_json(messages: list[dict]) -> str:
openai.api_key = OPENAI_API_KEY
openai.api_base = OPENAI_API_BASE
max_attempts = 5
for attempt in range(max_attempts):
try:
response = await async_client.chat.completions.create(
stream = await openai.ChatCompletion.acreate(
messages=messages,
max_tokens=4096,
n=1,
stop=None,
temperature=0.3,
timeout=30,
timeout=3,
model=MODEL,
response_format={"type": "json_object"},
)
@ -190,62 +196,62 @@ async def _achat_completion_json(messages: list[dict]) -> str:
if attempt < max_attempts - 1: # i is zero indexed
continue
else:
raise Exception("failed")
raise "failed"
full_reply_content = response.choices[0].message.content
full_reply_content = stream.choices[0].message.content
print(colored(full_reply_content, "blue", "on_white"), end="")
print()
return full_reply_content
async def _achat_completion_stream(messages: list[dict]) -> str:
try:
response = await async_client.chat.completions.create(
**_cons_kwargs(messages), stream=True
)
openai.api_key = OPENAI_API_KEY
openai.api_base = OPENAI_API_BASE
response = await openai.ChatCompletion.acreate(
**_cons_kwargs(messages), stream=True
)
# create variables to collect the stream of chunks
collected_chunks = []
collected_messages = []
# iterate through the stream of events
async for chunk in response:
collected_chunks.append(chunk) # save the event response
choices = chunk.choices
if len(choices) > 0:
chunk_message = chunk.choices[0].delta
collected_messages.append(chunk_message) # save the message
if chunk_message.content:
print(
colored(chunk_message.content, "blue", "on_white"),
end="",
)
print()
# create variables to collect the stream of chunks
collected_chunks = []
collected_messages = []
# iterate through the stream of events
async for chunk in response:
collected_chunks.append(chunk) # save the event response
choices = chunk["choices"]
if len(choices) > 0:
chunk_message = chunk["choices"][0].get(
"delta", {}
) # extract the message
collected_messages.append(chunk_message) # save the message
if "content" in chunk_message:
print(
colored(chunk_message["content"], "blue", "on_white"),
end="",
)
print()
full_reply_content = "".join(
[m.content or "" for m in collected_messages if m is not None]
)
return full_reply_content
except Exception as e:
print_colored(f"OpenAI API error in _achat_completion_stream: {str(e)}", "red")
raise
full_reply_content = "".join(
[m.get("content", "") for m in collected_messages]
)
return full_reply_content
def _chat_completion(messages: list[dict]) -> str:
try:
rsp = client.chat.completions.create(**_cons_kwargs(messages))
content = rsp.choices[0].message.content
return content
except Exception as e:
print_colored(f"OpenAI API error in _chat_completion: {str(e)}", "red")
raise
print(messages, flush=True)
rsp = openai.ChatCompletion.create(**_cons_kwargs(messages))
content = rsp["choices"][0]["message"]["content"]
print(content, flush=True)
return content
def _cons_kwargs(messages: list[dict]) -> dict:
kwargs = {
"messages": messages,
"max_tokens": 2000,
"temperature": 0.3,
"timeout": 15,
"max_tokens": 4096,
"n": 1,
"stop": None,
"temperature": 0.5,
"timeout": 3,
}
kwargs_mode = {"model": MODEL}
kwargs.update(kwargs_mode)

View File

@ -33,7 +33,7 @@ class JSON_ABILITY_REQUIREMENT_GENERATION(BaseModel):
PROMPT_AGENT_SELECTION_GENERATION = """
## Instruction
Based on "General Goal", "Current Task" and "Agent Board", output a formatted "Agent Selection Plan". Your selection should consider the ability needed for "Current Task" and the profile of each agent in "Agent Board".
Based on "General Goal", "Current Task" and "Agent Board", output a formatted "Agent Selection Plan". Your selection should consider the ability needed for "Current Task" and the profile of each agent in "Agent Board". Agent Selection Plan ranks from high to low according to ability.
## General Goal (Specify the general goal for the collaboration plan)
{General_Goal}
@ -80,10 +80,6 @@ def generate_AbilityRequirement(General_Goal, Current_Task):
def generate_AgentSelection(General_Goal, Current_Task, Agent_Board):
# Check if Agent_Board is None or empty
if Agent_Board is None or len(Agent_Board) == 0:
raise ValueError("Agent_Board cannot be None or empty. Please ensure agents are set via /setAgents endpoint before generating a plan.")
messages = [
{
"role": "system",

View File

@ -7,14 +7,14 @@ import AgentCoord.util as util
def generate_basePlan(
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List
General_Goal, Agent_Board, AgentProfile_Dict, InitialObject_List, context
):
basePlan = {
"Initial Input Object": InitialObject_List,
"Collaboration Process": [],
}
PlanOutline = generate_PlanOutline(
InitialObject_List=[], General_Goal=General_Goal
InitialObject_List=[], General_Goal=General_Goal + context
)
for stepItem in PlanOutline:
Current_Task = {
@ -44,7 +44,7 @@ def generate_basePlan(
),
}
TaskProcess = generate_TaskProcess(
General_Goal=General_Goal,
General_Goal=General_Goal + context,
Current_Task_Description=Current_Task_Description,
)
# add the generated AgentSelection and TaskProcess to the stepItem

View File

@ -5,7 +5,7 @@ import json
PROMPT_PLAN_OUTLINE_GENERATION = """
## Instruction
Based on "Output Format Example", "General Goal", and "Initial Key Object List", output a formatted "Plan_Outline".
Based on "Output Format Example", "General Goal", and "Initial Key Object List", output a formatted "Plan_Outline". The number of steps in the Plan Outline cannot exceed 5.
## Initial Key Object List (Specify the list of initial key objects available, each initial key object should be the input object of at least one Step)
{InitialObject_List}
@ -51,6 +51,7 @@ TaskContent: Describe the task of the current step.
InputObject_List: The list of the input obejects that will be used in current step.
OutputObject: The name of the final output object of current step.
请用中文回答
"""
@ -83,16 +84,4 @@ def generate_PlanOutline(InitialObject_List, General_Goal):
),
},
]
result = read_LLM_Completion(messages)
if isinstance(result, dict) and "Plan_Outline" in result:
return result["Plan_Outline"]
else:
# 如果格式不正确,返回默认的计划大纲
return [
{
"StepName": "Default Step",
"TaskContent": "Generated default plan step due to format error",
"InputObject_List": [],
"OutputObject": "Default Output"
}
]
return read_LLM_Completion(messages)["Plan_Outline"]

View File

@ -90,6 +90,7 @@ InputObject_List: List existing objects that should be utilized in current step.
AgentName: Specify the agent who will perform the action, You CAN ONLY USE THE NAME APPEARS IN "AgentInvolved".
ActionType: Specify the type of action, note that only the last action can be of type "Finalize", and the last action must be "Finalize".
请用中文回答
"""

View File

@ -80,14 +80,7 @@ class BaseAction():
Important_Mark = ""
action_Record += PROMPT_TEMPLATE_ACTION_RECORD.format(AgentName = actionInfo["AgentName"], Action_Description = actionInfo["AgentName"], Action_Result = actionInfo["Action_Result"], Important_Mark = Important_Mark)
# Handle missing agent profiles gracefully
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]
prompt = PROMPT_TEMPLATE_TAKE_ACTION_BASE.format(agentName = agentName, agentProfile = agentProfile, General_Goal = General_Goal, Current_Task_Description = TaskDescription, Input_Objects = inputObject_Record, History_Action = action_Record, Action_Description = self.info["Description"], Action_Custom_Note = self.Action_Custom_Note)
prompt = PROMPT_TEMPLATE_TAKE_ACTION_BASE.format(agentName = agentName, agentProfile = AgentProfile_Dict[agentName], General_Goal = General_Goal, Current_Task_Description = TaskDescription, Input_Objects = inputObject_Record, History_Action = action_Record, Action_Description = self.info["Description"], Action_Custom_Note = self.Action_Custom_Note)
print_colored(text = prompt, text_color="red")
messages = [{"role":"system", "content": prompt}]
ActionResult = LLM_Completion(messages,True,False)

View File

@ -5,7 +5,7 @@ Note: You can say something before you provide the improved version of the conte
The improved version you provide must be a completed version (e.g. if you provide a improved story, you should give completed story content, rather than just reporting where you have improved).
When you decide to give the improved version of the content, it should be start like this:
## Improved version of xxx
## xxx的改进版本
(the improved version of the content)
```

View File

@ -4,7 +4,7 @@ from termcolor import colored
# Accept inputs: num_StepToRun (the number of step to run, if None, run to the end), plan, RehearsalLog, AgentProfile_Dict
def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict, context):
# Prepare for execution
KeyObjects = {}
finishedStep_index = -1
@ -85,17 +85,9 @@ 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)
for ActionInfo in TaskProcess:
action_count += 1
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,
@ -109,7 +101,7 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
KeyObjects=KeyObjects,
)
ActionInfo_with_Result = currentAction.run(
General_Goal=plan["General Goal"],
General_Goal=plan["General Goal"] + "\n\n### Useful Information (some information can help accomplish the task)\n如果使用该信息,请在回答中显示指出是来自数联网的数据。例如,基于数联网数据搜索结果。" + context,
TaskDescription=TaskDescription,
agentName=agentName,
AgentProfile_Dict=AgentProfile_Dict,
@ -121,6 +113,8 @@ def executePlan(plan, num_StepToRun, RehearsalLog, AgentProfile_Dict):
ActionHistory.append(ActionInfo_with_Result)
# post processing for the group chat (finish)
objectLogNode["content"] = KeyObjects[OutputName]
if StepRun_count == len(plan["Collaboration Process"][(finishedStep_index + 1): run_to]):
objectLogNode["content"] += context
RehearsalLog.append(stepLogNode)
RehearsalLog.append(objectLogNode)
stepLogNode["ActionHistory"] = ActionHistory

View File

@ -20,8 +20,6 @@ def camel_case_to_normal(s):
def generate_template_sentence_for_CollaborationBrief(
input_object_list, output_object, agent_list, step_task
):
# Ensure step_task is not None
step_task = step_task if step_task is not None else "perform the task"
# Check if the names are in camel case (no spaces) and convert them to normal naming convention
input_object_list = (
[
@ -33,48 +31,29 @@ def generate_template_sentence_for_CollaborationBrief(
)
output_object = (
camel_case_to_normal(output_object)
if output_object is not None and is_camel_case(output_object)
else (output_object if output_object is not None else "unknown output")
if is_camel_case(output_object)
else output_object
)
# Format the agents into a string with proper grammar
if agent_list is None or len(agent_list) == 0:
agent_str = "Unknown agents"
elif all(agent is not None for agent in agent_list):
agent_str = (
" and ".join([", ".join(agent_list[:-1]), agent_list[-1]])
if len(agent_list) > 1
else agent_list[0]
)
else:
# Filter out None values
filtered_agents = [agent for agent in agent_list if agent is not None]
if filtered_agents:
agent_str = (
" and ".join([", ".join(filtered_agents[:-1]), filtered_agents[-1]])
if len(filtered_agents) > 1
else filtered_agents[0]
)
else:
agent_str = "Unknown agents"
agent_str = (
" and ".join([", ".join(agent_list[:-1]), agent_list[-1]])
if len(agent_list) > 1
else agent_list[0]
)
if input_object_list is None or len(input_object_list) == 0:
# Combine all the parts into the template sentence
template_sentence = f"{agent_str} perform the task of {step_task} to obtain {output_object}."
else:
# Format the input objects into a string with proper grammar
# Filter out None values from input_object_list
filtered_input_list = [obj for obj in input_object_list if obj is not None]
if filtered_input_list:
input_str = (
" and ".join(
[", ".join(filtered_input_list[:-1]), filtered_input_list[-1]]
)
if len(filtered_input_list) > 1
else filtered_input_list[0]
input_str = (
" and ".join(
[", ".join(input_object_list[:-1]), input_object_list[-1]]
)
else:
input_str = "unknown inputs"
if len(input_object_list) > 1
else input_object_list[0]
)
# Combine all the parts into the template sentence
template_sentence = f"Based on {input_str}, {agent_str} perform the task of {step_task} to obtain {output_object}."
@ -111,7 +90,7 @@ def read_LLM_Completion(messages, useGroq=True):
return json.loads(match.group(0).strip())
except Exception:
pass
return {} # 返回空对象而不是抛出异常
raise ("bad format!")
def read_json_content(text):
@ -132,7 +111,7 @@ def read_json_content(text):
if match:
return json.loads(match.group(0).strip())
return {} # 返回空对象而不是抛出异常
raise ("bad format!")
def read_outputObject_content(text, keyword):
@ -148,4 +127,4 @@ def read_outputObject_content(text, keyword):
if match:
return match.group(1).strip()
else:
return "" # 返回空字符串而不是抛出异常
raise ("bad format!")

View File

@ -0,0 +1,97 @@
[
{
"Icon": "Hailey_Johnson.png",
"Name": "船舶设计师",
"Profile": "提供船舶制造中的实际需求和约束。"
},
{
"Icon": "Jennifer_Moore.png",
"Name": "防护工程专家",
"Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。"
},
{
"Icon": "Jane_Moreno.png",
"Name": "病理生理学家",
"Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。"
},
{
"Icon": "Giorgio_Rossi.png",
"Name": "药物化学家",
"Profile": "负责将靶点概念转化为实际可合成的分子。"
},
{
"Icon": "Tamara_Taylor.png",
"Name": "制剂工程师",
"Profile": "负责将活性药物成分API变成稳定、可用、符合战场要求的剂型。"
},
{
"Icon": "Maria_Lopez.png",
"Name": "监管事务专家",
"Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。"
},
{
"Icon": "Sam_Moore.png",
"Name": "物理学家",
"Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。"
},
{
"Icon": "Yuriko_Yamamoto.png",
"Name": "实验材料学家",
"Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。"
},
{
"Icon": "Carlos_Gomez.png",
"Name": "计算模拟专家",
"Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。"
},
{
"Icon": "John_Lin.png",
"Name": "腐蚀机理研究员",
"Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。"
},
{
"Icon": "Arthur_Burton.png",
"Name": "先进材料研发员",
"Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。"
},
{
"Icon": "Eddy_Lin.png",
"Name": "肾脏病学家",
"Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。"
},
{
"Icon": "Isabella_Rodriguez.png",
"Name": "临床研究协调员",
"Profile": "负责受试者招募和临床试验流程优化。"
},
{
"Icon": "Latoya_Williams.png",
"Name": "中医药专家",
"Profile": "理解药物的中药成分和作用机制。"
},
{
"Icon": "Carmen_Ortiz.png",
"Name": "药物安全专家",
"Profile": "专注于药物不良反应数据收集、分析和报告。"
},
{
"Icon": "Rajiv_Patel.png",
"Name": "二维材料科学家",
"Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。"
},
{
"Icon": "Tom_Moreno.png",
"Name": "光电物理学家",
"Profile": "研究材料的光电转换机制和关键影响因素。"
},
{
"Icon": "Ayesha_Khan.png",
"Name": "机器学习专家",
"Profile": "专注于开发和应用AI模型用于材料模拟。"
},
{
"Icon": "Mei_Lin.png",
"Name": "流体动力学专家",
"Profile": "专注于流体行为理论和模拟。"
}
]

File diff suppressed because one or more lines are too long

View File

@ -9,4 +9,4 @@ GROQ_API_KEY: ""
MISTRAL_API_KEY: ""
## options under experimentation, leave them as Fasle unless you know what it is for
USE_CACHE: False
USE_CACHE: True

View File

@ -1,5 +1,5 @@
Flask==3.0.2
openai==2.8.1
openai==0.28.1
PyYAML==6.0.1
termcolor==2.4.0
groq==0.4.2

View File

@ -1,5 +1,6 @@
from flask import Flask, request, jsonify
import json
import time
from DataProcess import Add_Collaboration_Brief_FrontEnd
from AgentCoord.RehearsalEngine_V2.ExecutePlan import executePlan
from AgentCoord.PlanEngine.basePlan_Generator import generate_basePlan
@ -31,6 +32,56 @@ else:
USE_CACHE = USE_CACHE.lower() in ["true", "1", "yes"]
AgentBoard = None
AgentProfile_Dict = {}
c1 = '''
=====数联网搜索结果=====
1. 论文
- Surface Defect Detection and Evaluation for Marine Vessels using Multi-Stage Deep Learning提出一个面向船舶制造与检修的多阶段深度学习流水线用于船体表面缺陷检测与评估重点识别和量化与船舶制造材料腐蚀相关的现象包括腐蚀rust附着物fouling涂层剥离delamination研究特别关注船舶材料腐蚀在不同涂层颜色光照条件和相机角度下的鲁棒性有助于提高船舶制造过程中对材料腐蚀的检测与管理能力论文强调其方法在船舶材料表面腐蚀监测和维修决策中的应用价值链接https://arxiv.org/abs/2203.09580
论文复现链接http://8.130.138.52:21001/#/view/reproduction?doid=newID/5a4fef68-a138-4adb-ba88-43aa4474c08c
- Efficient Metal Corrosion Area Detection Model Combining Convolution and Transformer (MCD-Net)提出了一种高效的金属腐蚀区域检测模型该模型创新性地结合了卷积编码器与视觉Transformer序列编码器利用注意力融合模块增强边界识别并采用基于得分的多尺度增强机制来突出腐蚀区域该方法在阴影遮挡和复杂纹理条件下依然能保持高精度在公开数据集上取得了优异的F1性能特别适用于船舶制造与检修中对船体钢板和涂层腐蚀的自动化识别与量化分析链接https://www.mdpi.com/2076-3417/14/21/9900
论文复现链接http://8.130.138.52:21001/#/view/reproduction?doid=newID/b200a489-e2c8-4dbd-8767-682d15dd4c04
- Mechanical Failure and Metal Degradation of Ships and Marine Structures系统探讨了船舶与海洋结构中金属材料包括高强钢不锈钢铜合金钛合金等在海洋环境下的机械损伤与电化学腐蚀协同作用问题重点分析了"机械载荷+腐蚀"耦合导致的疲劳裂纹及腐蚀裂纹等失效机制并对相应的检测预警与保护措施进行了深入讨论链接https://doi.org/10.3390/met13020272
- Research Progress of Marine Anti-Fouling Coatings2024全面综述了海洋防污涂层的研究进展涵盖自我抛光涂料生物可降解涂料低表面能涂层仿生涂层和纳米涂层等新型涂层技术虽然重点在于防污但文中指出许多防污机制与防腐蚀机制相互关联通过阻碍生物附着可有效减少微生物引起的腐蚀和表面破坏链接https://doi.org/10.3390/coatings14091227
- Corrosion-Wear Behavior of Shipbuilding Steels (EH40, FH40, 921A) in Seawater研究了三种常用船用钢EH40FH40921A在海水环境中磨损与腐蚀共同作用下的复合失效行为通过机械-电化学方法系统测定了腐蚀磨损损失表面形貌变化和耐腐蚀性能差异发现在此类工况中921A钢表现最佳Journal of Chinese Society for Corrosion and Protection, Vol 45(4): 894-904, 2025
- Sulfur-Selenium Alloy Coatings for Universal Corrosion Resistance设计了一种硫-硒合金涂层能够在包括模拟海水和硫酸盐还原菌生物环境在内的各种条件下为钢材提供高效的防腐蚀保护约99.9%效率腐蚀速率比裸钢低6-7个数量级该涂层具有良好的机械性能非多孔结构能有效阻碍多种扩散性腐蚀因子的渗透arXiv:2009.02451
2. 代码
- CoaST - Anti-corrosive coatings research (DTU, Denmark)丹麦技术大学的CoaST项目专注于抗腐蚀涂层的性能表征与失效机制分析该项目采用电化学测试与表面/界面无损检测技术相结合的方法系统评估涂层的保护效果与失效途径为船舶腐蚀防护提供了重要的研究平台链接https://www.kt.dtu.dk/research/coast/research-areas/anti-corrosive-coatings
- Surface Defect Detection 仓库该仓库集成了多种表面缺陷检测算法为实现船舶腐蚀检测提供了可靠的代码基础和参考架构链接https://github.com/Charmve/Surface-Defect-Detection
- Hugging Face 预训练模型平台提供了轻量级的腐蚀检测模型可直接下载并微调用于快速验证和部署链接https://huggingface.co/mahdavian/corrosion-model
3. 数据集
用于训练和验证MCD-Net模型的相关数据集包括
- iamcloud/Corrosion_Dataset一个包含约2000多张标注图像的数据集ImageFolder格式Apache-2.0协议专门收录材料腐蚀图像适用于船舶腐蚀监测模型的训练链接https://huggingface.co/datasets/iamcloud/Corrosion_Dataset
- Francesco/corrosion-bi3q3此数据集提供了多种腐蚀实例可用于支持船舶涂层损坏与基体腐蚀的自动化检测任务链接https://huggingface.co/datasets/Francesco/corrosion-bi3q3
4. 研究团队
- 海洋腐蚀与防护全国重点实验室是我国在该领域最具权威性的国家级研究机构依托于中国船舶集团旗下725所实验室提供从材料设计到运维的全生命周期腐蚀控制解决方案其核心优势在于能够进行多技术阴极保护+涂层的综合集成研发与验证其研究成果广泛应用于船舶海洋平台等国家重大工程为保障我国海洋装备的安全与耐久性提供了关键支撑
- 水性船舶防腐涂层新材料项目团队是一支源自东北石油大学的创新创业团队专注于推动船舶涂料行业的环保化转型团队致力于攻克水性涂料在船舶高湿环境下干燥慢早期耐水性差的技术瓶颈旨在开发出兼具优异防腐性能和施工便利性的水性体系以替代传统溶剂型涂料其目标是实现进口产品替代并大幅减少船舶制造与维修过程中的VOCs排放
- 微纳一体化防腐耐磨技术研发团队是以广州航海学院青年技术骨干为核心的新锐力量团队针对铝合金在极端海洋环境中的腐蚀-磨损难题创新性地提出了微米级氧化陶瓷层+纳米自润滑固化层+改性硅烷钝化层的三层复合防护体系该技术使防护层的结合强度耐磨性提升2000倍和耐腐蚀性能自腐蚀电流密度降低6个数量级实现了质的飞跃
- 北京大学南京创新研究院BXCFD团队专注于自主可控的高性能计算流体力学软件研发团队在船舶与海洋工程水动力学领域取得关键突破其自主研发的软件在船舶兴波阻力预报精度上达到国际先进水平并成功实现了对复杂的船--舵6自由度运动的耦合高精度模拟为船舶水动力性能研究与优化提供了强大的国产化工具
- 清华大学多相流与生物流动力学实验室由罗先武教授带领长期专注于流体机械及工程领域团队重点研究高速船舶喷水推进装置的基础理论与空化流动机理其研究成果为攻克喷水推进系统中的空化振动与噪声问题提供了坚实的理论依据和技术支持对我国高性能船舶推进技术的发展具有重要意义
'''
context = {"材料腐蚀":c1}
Request_Cache: dict[str, str] = {}
app = Flask(__name__)
@ -48,6 +99,7 @@ def Handle_fill_stepTask_TaskProcess():
if USE_CACHE:
if requestIdentifier in Request_Cache:
time.sleep(1)
return jsonify(Request_Cache[requestIdentifier])
filled_stepTask = fill_stepTask_TaskProcess(
@ -118,6 +170,7 @@ def Handle_fill_stepTask():
if USE_CACHE:
if requestIdentifier in Request_Cache:
time.sleep(1)
return jsonify(Request_Cache[requestIdentifier])
filled_stepTask = fill_stepTask(
@ -211,19 +264,16 @@ def Handle_generate_basePlan():
if USE_CACHE:
if requestIdentifier in Request_Cache:
time.sleep(2)
return jsonify(Request_Cache[requestIdentifier])
try:
basePlan = generate_basePlan(
General_Goal=incoming_data["General Goal"],
Agent_Board=AgentBoard,
AgentProfile_Dict=AgentProfile_Dict,
InitialObject_List=incoming_data["Initial Input Object"],
)
except ValueError as e:
return jsonify({"error": str(e)}), 400
except Exception as e:
return jsonify({"error": f"An unexpected error occurred: {str(e)}"}), 500
basePlan = generate_basePlan(
General_Goal=incoming_data["General Goal"],
Agent_Board=AgentBoard,
AgentProfile_Dict=AgentProfile_Dict,
InitialObject_List=incoming_data["Initial Input Object"],
context="\n请注意,目标是给出解决方案,而不是工程实现,不需要实验验证,也不需要推广计划。"
)
basePlan_withRenderSpec = Add_Collaboration_Brief_FrontEnd(basePlan)
Request_Cache[requestIdentifier] = basePlan_withRenderSpec
response = jsonify(basePlan_withRenderSpec)
@ -233,6 +283,10 @@ def Handle_generate_basePlan():
@app.route("/executePlan", methods=["post"])
def Handle_executePlan():
incoming_data = request.get_json()
cur_context = ""
if "材料腐蚀" in incoming_data["plan"]["General Goal"]:
cur_context = context["材料腐蚀"]
requestIdentifier = str(
(
"/executePlan",
@ -244,6 +298,7 @@ def Handle_executePlan():
if USE_CACHE:
if requestIdentifier in Request_Cache:
time.sleep(3)
return jsonify(Request_Cache[requestIdentifier])
RehearsalLog = executePlan(
@ -251,20 +306,21 @@ def Handle_executePlan():
incoming_data["num_StepToRun"],
incoming_data["RehearsalLog"],
AgentProfile_Dict,
context=cur_context
)
Request_Cache[requestIdentifier] = RehearsalLog
response = jsonify(RehearsalLog)
return response
@app.route("/_saveRequestCashe", methods=["post"])
@app.route("/_saveRequestCashe", methods=["GET"])
def Handle_saveRequestCashe():
with open(
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "w"
) as json_file:
json.dump(Request_Cache, json_file, indent=4)
response = jsonify(
{"code": 200, "content": "request cashe sucessfully saved"}
{"code": 200, "content": "request cashe sucessfully saved: " + os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json")}
)
return response
@ -283,38 +339,10 @@ def set_agents():
def init():
global AgentBoard, AgentProfile_Dict, Request_Cache
# Load Request Cache
try:
with open(
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "r"
) as json_file:
Request_Cache = json.load(json_file)
print(f"✅ Loaded Request_Cache with {len(Request_Cache)} entries")
except Exception as e:
print(f"⚠️ Failed to load Request_Cache: {e}")
Request_Cache = {}
# Load Agent Board
try:
with open(
os.path.join(os.getcwd(), "AgentRepo", "agentBoard_v1.json"), "r", encoding="utf-8"
) as json_file:
AgentBoard = json.load(json_file)
print(f"✅ Loaded AgentBoard with {len(AgentBoard)} agents")
# Build AgentProfile_Dict
AgentProfile_Dict = {}
for item in AgentBoard:
name = item["Name"]
profile = item["Profile"]
AgentProfile_Dict[name] = profile
print(f"✅ Built AgentProfile_Dict with {len(AgentProfile_Dict)} profiles")
except Exception as e:
print(f"⚠️ Failed to load AgentBoard: {e}")
AgentBoard = []
AgentProfile_Dict = {}
with open(
os.path.join(os.getcwd(), "RequestCache", "Request_Cache.json"), "r"
) as json_file:
Request_Cache = json.load(json_file)
if __name__ == "__main__":
@ -324,8 +352,8 @@ if __name__ == "__main__":
parser.add_argument(
"--port",
type=int,
default=8000,
help="set the port number, 8000 by defaul.",
default=8017,
help="set the port number, 8017 by defaul.",
)
args = parser.parse_args()
init()

View File

@ -21,11 +21,12 @@ services:
ports:
- "8000:8000"
environment:
- OPENAI_API_BASE=htts://api.openai.com
- OPENAI_API_KEY=
- OPENAI_API_MODEL=gpt-4-turbo-preview
- FAST_DESIGN_MODE=True
- OPENAI_API_BASE=https://api.moleapi.com/v1
- OPENAI_API_KEY=sk-sps7FBCbEvu85DfPoS8SdnPwYLEoW7u5Dd8vCDTXqPLpHuyb
- OPENAI_API_MODEL=gpt-4.1-mini
- FAST_DESIGN_MODE=False
- GROQ_API_KEY=
- USE_CACHE=True
networks:
- agentcoord-network

View File

@ -15,8 +15,9 @@ FROM base AS runner
WORKDIR /app
EXPOSE 8080/tcp
COPY .npmrc ./
RUN npm install @modern-js/app-tools @modern-js/runtime --no-optional --no-shrinkwrap && mkdir src
COPY src ./src
COPY modern.config.ts package.json ./
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
ENV API_BASE=
CMD ["npm", "run", "serve"]

View File

@ -351,7 +351,7 @@ export default observer(({ style = {} }: IPlanModification) => {
overflowY: 'auto',
}}
>
<Box sx={{ marginBottom: '6px', fontWeight: '600' }}>Assignment</Box>
<Box sx={{ marginBottom: '6px', fontWeight: '600' }}></Box>
<Box>
{Object.keys(agentSelections).map(selectionId => (
<Box
@ -405,7 +405,7 @@ export default observer(({ style = {} }: IPlanModification) => {
overflowY: 'auto',
}}
>
<Box sx={{ marginBottom: '4px', fontWeight: '600' }}>Comparison</Box>
<Box sx={{ marginBottom: '4px', fontWeight: '600' }}></Box>
<Box
sx={{

View File

@ -65,7 +65,6 @@ export default observer(({ agent, style = {} }: IAgentCardProps) => (
userSelect: 'none',
}}
>
{agent.profile}
</Box>
</Box>
<Box
@ -105,7 +104,7 @@ export default observer(({ agent, style = {} }: IAgentCardProps) => (
marginLeft: '2px',
}}
>
Current Duty:
:
</Box>
<Box>
{agent.actions.map((action, index) => (

View File

@ -4,6 +4,7 @@ import Box from '@mui/material/Box';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import { IAgentAction, globalStorage } from '@/storage';
import { getActionTypeDisplayText } from '@/storage/plan/action'; // 导入映射函数
export interface IDutyItem {
action: IAgentAction;
@ -38,7 +39,7 @@ export default React.memo<IDutyItem>(({ action, style = {} }) => {
..._style,
}}
>
{action.type}
{getActionTypeDisplayText(action.type)}
<Box
onClick={() => setExpand(true)}
sx={{
@ -83,7 +84,7 @@ export default React.memo<IDutyItem>(({ action, style = {} }) => {
marginLeft: '2px',
}}
>
{action.type}
{getActionTypeDisplayText(action.type)}
</Box>
<Divider
sx={{

View File

@ -88,7 +88,7 @@ export default observer(({ style = {} }: IAgentBoardProps) => {
'& > input': { display: 'none' },
}}
>
<Title title="Agent Board" />
<Title title="智能体库" />
<Box
sx={{
flexGrow: 1,

View File

@ -1,4 +1,5 @@
import AbigailChen from '@/static/AgentIcons/Abigail_Chen.png';
import AbigailChenMilitary from '@/static/AgentIcons/Abigail_Chen_military.png';
import AdamSmith from '@/static/AgentIcons/Adam_Smith.png';
import ArthurBurton from '@/static/AgentIcons/Arthur_Burton.png';
import AyeshaKhan from '@/static/AgentIcons/Ayesha_Khan.png';
@ -8,9 +9,11 @@ import EddyLin from '@/static/AgentIcons/Eddy_Lin.png';
import FranciscoLopez from '@/static/AgentIcons/Francisco_Lopez.png';
import GiorgioRossi from '@/static/AgentIcons/Giorgio_Rossi.png';
import HaileyJohnson from '@/static/AgentIcons/Hailey_Johnson.png';
import HaileyJohnsonMilitary from '@/static/AgentIcons/Hailey_Johnson_military.png';
import IsabellaRodriguez from '@/static/AgentIcons/Isabella_Rodriguez.png';
import JaneMoreno from '@/static/AgentIcons/Jane_Moreno.png';
import JenniferMoore from '@/static/AgentIcons/Jennifer_Moore.png';
import JenniferMooreMilitary from '@/static/AgentIcons/Jennifer_Moore_military.png';
import JohnLin from '@/static/AgentIcons/John_Lin.png';
import KlausMueller from '@/static/AgentIcons/Klaus_Mueller.png';
import LatoyaWilliams from '@/static/AgentIcons/Latoya_Williams.png';
@ -27,6 +30,7 @@ import Unknown from '@/static/AgentIcons/Unknow.png';
export enum IconName {
AbigailChen = 'Abigail_Chen',
AbigailChenMilitary = 'Abigail_Chen_military',
AdamSmith = 'Adam_Smith',
ArthurBurton = 'Arthur_Burton',
AyeshaKhan = 'Ayesha_Khan',
@ -45,9 +49,11 @@ export enum IconName {
GabeSmith = 'Gabe_Smith',
GiorgioRossi = 'Giorgio_Rossi',
HaileyJohnson = 'Hailey_Johnson',
HaileyJohnsonMilitary = 'Hailey_Johnson_military',
IsabellaRodriguez = 'Isabella_Rodriguez',
JaneMoreno = 'Jane_Moreno',
JenniferMoore = 'Jennifer_Moore',
JenniferMooreMilitary = 'Jennifer_Moore_military',
JohnLin = 'John_Lin',
KlausMueller = 'Klaus_Mueller',
LatoyaWilliams = 'Latoya_Williams',
@ -83,6 +89,7 @@ export const IconMap = new Proxy<{ [key: string]: IconName }>(
export const IconUrl: { [key in IconName]: string } = {
[IconName.Unknown]: Unknown,
[IconName.AbigailChen]: AbigailChen,
[IconName.AbigailChenMilitary]: AbigailChenMilitary,
[IconName.AdamSmith]: AdamSmith,
[IconName.ArthurBurton]: ArthurBurton,
[IconName.AyeshaKhan]: AyeshaKhan,
@ -101,9 +108,11 @@ export const IconUrl: { [key in IconName]: string } = {
[IconName.GabeSmith]: AbigailChen,
[IconName.GiorgioRossi]: GiorgioRossi,
[IconName.HaileyJohnson]: HaileyJohnson,
[IconName.HaileyJohnsonMilitary]: HaileyJohnsonMilitary,
[IconName.IsabellaRodriguez]: IsabellaRodriguez,
[IconName.JaneMoreno]: JaneMoreno,
[IconName.JenniferMoore]: JenniferMoore,
[IconName.JenniferMooreMilitary]: JenniferMooreMilitary,
[IconName.JohnLin]: JohnLin,
[IconName.KlausMueller]: KlausMueller,
[IconName.LatoyaWilliams]: LatoyaWilliams,

View File

@ -151,7 +151,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
>
<LogoIcon />
<Box sx={{ marginLeft: '6px', fontWeight: 800, fontSize: '20px' }}>
AGENTCOORD
</Box>
</Box>
) : (

View File

@ -8,6 +8,7 @@ import RemoveIcon from '@mui/icons-material/Remove';
import AdjustIcon from '@mui/icons-material/Adjust';
import { ObjectProps, ProcessProps } from './interface';
import AgentIcon from '@/components/AgentIcon';
import DescriptionCard from '@/components/ProcessDiscription/DescriptionCard';
import { globalStorage } from '@/storage';
export interface IEditObjectProps {
@ -329,6 +330,13 @@ export const ProcessCard: React.FC<IProcessCardProps> = React.memo(
// handleEditStep(step.name, { ...step, task: text });
}}
/>
{/* 这里直接渲染对应的 DescriptionCard */}
{/* {(() => {
const step = globalStorage.planManager.currentPlan.find(
s => s.id === process.id
);
return step ? <DescriptionCard step={step} /> : null;
})()} */}
</Box>
)}
</Box>

View File

@ -118,10 +118,10 @@ const D3Graph: React.FC<D3GraphProps> = ({
stepName: stepCardName,
objectName: objectCardName,
type,
x1: objectRect.left + objectRect.width,
y1: objectRect.top + 0.5 * objectRect.height,
x2: stepRect.left,
y2: stepRect.top + 0.5 * stepRect.height,
x1: stepRect.left + stepRect.width,
y1: stepRect.top + 0.5 * stepRect.height,
x2: objectRect.left,
y2: objectRect.top + 0.5 * objectRect.height,
};
});
const objectMetrics = calculateLineMetrics(cardRect, 'object');
@ -152,17 +152,17 @@ const D3Graph: React.FC<D3GraphProps> = ({
userSelect: 'none',
}}
>
<marker
id="arrowhead"
markerWidth="4"
markerHeight="4"
refX="2"
refY="2"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L4,2 L0,4 z" fill="#E5E5E5" />
</marker>
<marker
id="arrowhead"
markerWidth="2"
markerHeight="2"
refX="1"
refY="1"
orient="auto"
markerUnits="strokeWidth"
>
<path d="M0,0 L2,1 L0,2 z" fill="#E5E5E5" />
</marker>
<marker
id="starter"
markerWidth="4"
@ -184,7 +184,7 @@ const D3Graph: React.FC<D3GraphProps> = ({
fill="#898989"
fontWeight="800"
>
Key Object
</text>
<line
x1={objectLine.x}
@ -204,7 +204,7 @@ const D3Graph: React.FC<D3GraphProps> = ({
fill="#898989"
fontWeight="800"
>
Process
</text>
<line
x1={processLine.x}

View File

@ -53,7 +53,7 @@ export default observer(() => {
const handleEditContent = (stepTaskId: string, newContent: string) => {
globalStorage.setStepTaskContent(stepTaskId, newContent);
};
const WidthRatio = ['30%', '15%', '52.5%'];
const WidthRatio = ['35%', '10%', '50%'];
const [cardRefMapReady, setCardRefMapReady] = React.useState(false);
@ -175,6 +175,34 @@ export default observer(() => {
sx={{ display: 'flex' }}
ref={ref}
>
<Box
sx={{
// display: 'flex',
alignItems: 'center',
// width: WidthRatio[2],
justifyContent: 'center',
flex: `0 0 ${WidthRatio[2]}`,
}}
>
{name && (
<ProcessCard
process={{
id,
name,
icons: agentIcons,
agents,
content,
cardRef: getCardRef(`process.${name}`),
}}
handleProcessClick={handleProcessClick}
isFocusing={focusingStepTaskId === id}
isAddActive={id === activeProcessIdAdd}
handleAddActive={handleProcessAdd}
handleEditContent={handleEditContent}
/>
)}
</Box>
<Box sx={{ flex: `0 0 ${WidthRatio[1]}` }} />
<Box
sx={{
display: 'flex',
@ -209,34 +237,6 @@ export default observer(() => {
/>
)}
</Box>
<Box sx={{ flex: `0 0 ${WidthRatio[1]}` }} />
<Box
sx={{
// display: 'flex',
alignItems: 'center',
// width: WidthRatio[2],
justifyContent: 'center',
flex: `0 0 ${WidthRatio[2]}`,
}}
>
{name && (
<ProcessCard
process={{
id,
name,
icons: agentIcons,
agents,
content,
cardRef: getCardRef(`process.${name}`),
}}
handleProcessClick={handleProcessClick}
isFocusing={focusingStepTaskId === id}
isAddActive={id === activeProcessIdAdd}
handleAddActive={handleProcessAdd}
handleEditContent={handleEditContent}
/>
)}
</Box>
</Box>
),
)}

View File

@ -25,7 +25,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
...style,
}}
>
<Title title="Plan Outline" />
<Title title="任务大纲" />
<Box
sx={{
position: 'relative',

View File

@ -19,7 +19,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
...style,
}}
>
<Title title="Task Process" />
<Title title="任务流程" />
<Stack
spacing={1}
sx={{

View File

@ -1,6 +1,6 @@
import React from 'react';
import React, { useState } from 'react';
import { observer } from 'mobx-react-lite';
import { Divider, SxProps } from '@mui/material';
import { Divider, SxProps, Tooltip, Typography} from '@mui/material';
import Box from '@mui/material/Box';
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
@ -9,6 +9,7 @@ import { globalStorage } from '@/storage';
import type { IExecuteStepHistoryItem } from '@/apis/execute-plan';
import AgentIcon from '@/components/AgentIcon';
import { getAgentActionStyle } from '@/storage/plan';
import { getActionTypeDisplayText } from '@/storage/plan/action';
export interface IStepHistoryItemProps {
item: IExecuteStepHistoryItem;
@ -46,6 +47,170 @@ export default observer(
}, 10);
}
}, [expand]);
interface DataSpaceCard {
name: string;
data_space: string;
doId: string;
fromRepo: string;
}
interface DataSpaceIndicatorProps {
cards?: DataSpaceCard[];
}
const DataSpaceIndicator = ({ cards = [] }: DataSpaceIndicatorProps) => {
const [currentCard, setCurrentCard] = useState(0);
if (!cards || cards.length === 0) {
return null;
}
const tooltipContent = (
<>
{/* 添加标题 */}
<Box
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '8px 12px',
backgroundColor: '#1565c0',
color: 'white',
borderRadius: '4px 4px 0 0',
fontWeight: 'bold',
fontSize: '14px',
}}
>
<span></span>
<Box sx={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
<span style={{ fontSize: '12px', color: 'rgba(255,255,255,0.8)' }}>
{currentCard + 1}/{cards.length}
</span>
<button
onClick={(e) => {
e.stopPropagation();
setCurrentCard((prev) => (prev + 1) % cards.length);
}}
style={{
fontSize: '12px',
padding: '2px 8px',
backgroundColor: 'white',
color: '#1565c0',
border: 'none',
borderRadius: '3px',
cursor: 'pointer',
fontWeight: 'bold'
}}
>
</button>
</Box>
</Box>
<Box
sx={{
padding: '12px',
borderRadius: '4px',
backgroundColor: '#e3f2fd',
border: '1px solid #1565c0',
minWidth: '450px', // 设置卡片最小宽度
}}
>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.name}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.data_space}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
DOID:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.doId}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'flex-start', marginBottom: '4px' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold', color: '#666', minWidth: '60px' }}>
:
</Typography>
<Typography variant="body2" sx={{ color: 'black' }}>
{cards[currentCard]?.fromRepo}
</Typography>
</Box>
</Box>
</>
);
return (
<Tooltip
title={tooltipContent}
arrow
slotProps={{
tooltip: {
sx: {
maxWidth: 500, // 只需要这一行来增加宽度
},
},
}}
>
<Box
sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: '20px',
height: '20px',
borderRadius: '50%',
backgroundColor: '#9e9e9e',
color: 'white',
fontSize: '12px',
fontWeight: '600',
cursor: 'pointer',
'&:hover': {
backgroundColor: '#1565c0',
}
}}
>
{cards.length}
</Box>
</Tooltip>
);
};
const cardData = [
{
name: "3D Semantic-Geometric Corrosion Mapping Implementations",
data_space: "江苏省产研院",
doId: "bdware.scenario/d8f3ff8c-3fb3-4573-88a6-5dd823627c37",
fromRepo: "https://arxiv.org/abs/2404.13691"
},
{
name: "RustSEG -- Automated segmentation of corrosion using deep learning",
data_space: "江苏省产研院",
doId: "bdware.scenario/67445299-110a-4a4e-9fda-42e4b5a493c2",
fromRepo: "https://arxiv.org/abs/2205.05426"
},
{
name: "Pixel-level Corrosion Detection on Metal Constructions by Fusion of Deep Learning Semantic and Contour Segmentation",
data_space: "江苏省产研院",
doId: "bdware.scenario/115d5135-85d3-4123-8b81-9eb9f07b6153",
fromRepo: "https://arxiv.org/abs/2008.05204"
},
];
const s = { ...getAgentActionStyle(item.type), ...style } as SxProps;
React.useEffect(() => {
console.log(item);
@ -77,7 +242,7 @@ export default observer(
{item.agent}
</Box>
<Box component="span" sx={{ fontWeight: 400 }}>
: {item.type}
: {getActionTypeDisplayText(item.type)}
</Box>
</Box>
{item.result ? (
@ -103,10 +268,14 @@ export default observer(
marginBottom: '4px',
color: '#0009',
fontWeight: 400,
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
}}
>
{item.description}
</Box>
<DataSpaceIndicator cards={cardData} />
<MarkdownBlock
text={item.result}
style={{

View File

@ -79,7 +79,7 @@ export default observer(({ style = {} }: { style?: SxProps }) => {
alignItems: 'center',
}}
>
<Title title="Execution Result" />
<Title title="执行结果" />
<Box
sx={{
flexGrow: 1,

View File

@ -4,6 +4,8 @@ import { SxProps } from '@mui/material';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import FormControl from '@mui/material/FormControl';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';
import FilledInput from '@mui/material/FilledInput';
import TelegramIcon from '@mui/icons-material/Telegram';
import CircularProgress from '@mui/material/CircularProgress';
@ -16,6 +18,18 @@ export interface UserGoalInputProps {
export default observer(({ style = {} }: UserGoalInputProps) => {
const inputRef = React.useRef<string>('');
const inputElementRef = React.useRef<HTMLInputElement>(null);
const goalOptions = [
'如何快速筛选慢性肾脏病药物潜在受试者?',
'如何补充“丹芍活血胶囊”不良反应数据?',
'如何快速研发用于战场失血性休克的药物?',
'二维材料的光电性质受哪些关键因素影响?',
'如何通过AI模拟的方法分析材料的微观结构?',
'如何分析获取液态金属热力学参数?',
'如何解决固态电池的成本和寿命难题?',
'如何解决船舶制造中的材料腐蚀难题?',
'如何解决船舶制造中流体模拟和建模优化难题?',
];
React.useEffect(() => {
if (inputElementRef.current) {
if (globalStorage.planManager) {
@ -32,42 +46,70 @@ export default observer(({ style = {} }: UserGoalInputProps) => {
...style,
}}
>
<FilledInput
<Autocomplete
freeSolo
disableClearable
disabled={
globalStorage.api.busy ||
!globalStorage.api.agentsReady ||
globalStorage.api.planReady
}
placeholder="Yout Goal"
fullWidth
inputRef={inputElementRef}
onChange={event => (inputRef.current = event.target.value)}
size="small"
sx={{
borderRadius: '10px',
background: '#E1E1E1',
borderBottom: 'none !important',
'&::before': {
borderBottom: 'none !important',
},
'& > input': {
padding: '10px',
},
options={goalOptions}
value={inputRef.current || (globalStorage.planManager ? globalStorage.planManager.goal : globalStorage.briefGoal)}
onChange={(event, newValue) => {
if (newValue) {
inputRef.current = newValue;
}
}}
startAdornment={
<Box
onInputChange={(event, newInputValue) => {
inputRef.current = newInputValue;
}}
renderInput={(params) => (
<TextField
{...params}
variant="filled"
placeholder="请输入你的任务"
fullWidth
size="small"
inputRef={inputElementRef}
sx={{
color: '#4A9C9E',
fontWeight: 800,
fontSize: '18px',
textWrap: 'nowrap',
userSelect: 'none',
borderRadius: '10px',
background: '#E1E1E1',
borderBottom: 'none !important',
'& .MuiFilledInput-root': {
height: '45px',
borderRadius: '10px',
background: '#E1E1E1',
paddingTop: '5px',
borderBottom: 'none !important',
'&::before': {
borderBottom: 'none !important',
},
'&::after': {
borderBottom: 'none !important',
},
},
}}
>
\General Goal:
</Box>
}
/>
InputProps={{
...params.InputProps,
startAdornment: (
<Box
sx={{
color: '#4A9C9E',
fontWeight: 800,
fontSize: '18px',
textWrap: 'nowrap',
userSelect: 'none',
}}
>
</Box>
),
}}
/>
)}
/>
{globalStorage.api.planGenerating ? (
<CircularProgress
sx={{

View File

@ -88,24 +88,24 @@ export default observer(() => {
setConnectors(<></>);
}
const outlinePosition =
ElementToLink[0]?.current?.getBoundingClientRect?.();
ElementToLink[1]?.current?.getBoundingClientRect?.();
if (!outlinePosition) {
return;
}
const agentRects = ElementToLink[1]
const agentRects = ElementToLink[0]
.map(ele => ele.current?.getBoundingClientRect?.() as DOMRect)
.filter(rect => rect);
if (agentRects.length === 0) {
return;
}
const agentsPosition = mergeRects(...agentRects);
const descriptionPosition =
ElementToLink[2]?.current?.getBoundingClientRect?.();
if (!descriptionPosition) {
return;
}
// const descriptionPosition =
// ElementToLink[2]?.current?.getBoundingClientRect?.();
// if (!descriptionPosition) {
// return;
// }
const LogRects = ElementToLink[3]
const LogRects = ElementToLink[2]
.map(ele => ele?.current?.getBoundingClientRect?.() as DOMRect)
.filter(rect => rect);
if (LogRects.length > 0) {
@ -115,15 +115,14 @@ export default observer(() => {
setConnectors(
drawConnectors(
outlinePosition,
agentsPosition,
descriptionPosition,
outlinePosition,
logPosition,
),
);
} else {
setConnectors(
drawConnectors(outlinePosition, agentsPosition, descriptionPosition),
drawConnectors(agentsPosition,outlinePosition),
);
}
} catch (e) {

View File

@ -1,33 +1,33 @@
import { Box } from '@mui/material';
import React from 'react';
// 定义你的图标属性类型,这里可以扩展成任何你需要的属性
interface CustomIconProps {
size?: number | string;
color?: string;
// ...其他你需要的props
}
// 创建你的自定义SVG图标组件
const CheckIcon: React.FC<CustomIconProps> = ({
size = '100%',
color = 'currentColor',
}) => {
return (
<Box
component="svg"
width={size}
height="auto"
viewBox="0 0 11 9"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.7204 0C7.37522 1.94391 4.95071 4.40066 3.85635 5.63171L1.18331 3.64484L0 4.54699L4.61463 9C5.4068 7.07085 7.92593 3.30116 11 0.620227L10.7204 0Z"
fill={color}
/>
</Box>
);
};
export default CheckIcon;
import { Box } from '@mui/material';
import React from 'react';
// 定义你的图标属性类型,这里可以扩展成任何你需要的属性
interface CustomIconProps {
size?: number | string;
color?: string;
// ...其他你需要的props
}
// 创建你的自定义SVG图标组件
const CheckIcon: React.FC<CustomIconProps> = ({
size = '100%',
color = 'currentColor',
}) => {
return (
<Box
component="svg"
width={size}
height="auto"
viewBox="0 0 11 9"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.7204 0C7.37522 1.94391 4.95071 4.40066 3.85635 5.63171L1.18331 3.64484L0 4.54699L4.61463 9C5.4068 7.07085 7.92593 3.30116 11 0.620227L10.7204 0Z"
fill={color}
/>
</Box>
);
};
export default CheckIcon;

View File

@ -44,7 +44,7 @@ export default observer(() => {
)}
{globalStorage.agentAssigmentWindow ? (
<FloatWindow
title="Assignment Exploration"
title="智能体选择"
onClose={() => (globalStorage.agentAssigmentWindow = false)}
>
<AgentAssignment

View File

@ -99,16 +99,7 @@ export default React.memo(() => {
}}
/>
<Box sx={{ height: 0, flexGrow: 1, display: 'flex' }}>
<ResizeableColumn columnWidth="25%">
<Outline
style={{
height: '100%',
width: '100%',
marginRight: '6px',
}}
/>
</ResizeableColumn>
<ResizeableColumn columnWidth="25%">
<ResizeableColumn columnWidth="19%">
<AgentBoard
style={{
height: '100%',
@ -118,8 +109,8 @@ export default React.memo(() => {
onAddAgent={() => setShowAgentHiring(true)}
/>
</ResizeableColumn>
<ResizeableColumn columnWidth="25%">
<ProcessDiscrption
<ResizeableColumn columnWidth="26%">
<Outline
style={{
height: '100%',
width: '100%',
@ -127,6 +118,15 @@ export default React.memo(() => {
}}
/>
</ResizeableColumn>
{/* <ResizeableColumn columnWidth="25%">
<ProcessDiscrption
style={{
height: '100%',
width: '100%',
marginRight: '6px',
}}
/>
</ResizeableColumn> */}
<ProcessRehearsal
style={{
height: '100%',

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -285,17 +285,15 @@ export class GlobalStorage {
}
public get ElementToLink(): [
React.RefObject<HTMLElement> | undefined,
React.RefObject<HTMLElement>[],
React.RefObject<HTMLElement> | undefined,
(React.RefObject<HTMLElement> | undefined)[],
] {
return [
this.refMap.OutlineCard.get(this.focusingStepTaskId ?? ''),
this.focusingStepTask?.agentSelection?.agents?.map(
agent => this.refMap.AgentCard.get(agent) ?? [],
) ?? ([] as any),
this.refMap.DiscriptionCard.get(this.focusingStepTaskId ?? ''),
this.refMap.OutlineCard.get(this.focusingStepTaskId ?? ''),
this.logManager.outdate
? []
: [

View File

@ -11,6 +11,17 @@ export enum ActionType {
Finalize = 'Finalize',
}
export const getActionTypeDisplayText = (actionType: ActionType | string): string => {
const displayMap: Record<string, string> = {
[ActionType.Propose]: '提议',
[ActionType.Critique]: '评审',
[ActionType.Improve]: '改进',
[ActionType.Finalize]: '总结',
};
return displayMap[actionType] || actionType;
};
const AgentActionStyles = new Map<ActionType | '', SxProps>([
[ActionType.Propose, { backgroundColor: '#B9EBF9', borderColor: '#94c2dc' }],
[ActionType.Critique, { backgroundColor: '#EFF9B9', borderColor: '#c0dc94' }],

View File

@ -20,7 +20,7 @@ const nameJoin = (names: string[]) => {
const last = tmp.pop()!;
let t = tmp.join(', ');
if (t.length > 0) {
t = `${t} and ${last}`;
t = `${t} ${last}`;
} else {
t = last;
}
@ -102,14 +102,14 @@ export class StepTaskNode implements INodeBase {
text: this.output,
style: { background: '#FFCA8C' },
};
outputSentence = `to obtain !<${indexOffset}>!`;
outputSentence = `得到 !<${indexOffset}>!`;
}
// Join them togeter
let content = inputSentence;
if (content) {
content = `Based on ${content}, ${nameSentence} perform the task of !<${actionIndex}>!`;
content = `基于${content}, ${nameSentence} 执行任务 !<${actionIndex}>!`;
} else {
content = `${nameSentence} perform the task of !<${actionIndex}>!`;
content = `${nameSentence} 执行任务 !<${actionIndex}>!`;
}
if (outputSentence) {
content = `${content}, ${outputSentence}.`;

View File

@ -1,6 +0,0 @@
node_modules
dist
.idea
.vscode
.git
.gitignore

View File

@ -1,8 +0,0 @@
[*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,vue,css,scss,sass,less,styl}]
charset = utf-8
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
max_line_length = 100

View File

@ -1,79 +0,0 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"DirectiveBinding": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"PropType": true,
"Ref": true,
"ShallowRef": true,
"Slot": true,
"Slots": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"getCurrentWatcher": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"isShallow": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onErrorCaptured": true,
"onMounted": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onUnmounted": true,
"onUpdated": true,
"onWatcherCleanup": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useId": true,
"useModel": true,
"useSlots": true,
"useTemplateRef": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true
}
}

View File

@ -1 +0,0 @@
* text=auto eol=lf

36
frontend/.gitignore vendored
View File

@ -1,36 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
dist-ssr
coverage
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
*.tsbuildinfo
.eslintcache
# Cypress
/cypress/videos/
/cypress/screenshots/
# Vitest
__screenshots__/

View File

@ -1,6 +0,0 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"singleQuote": true,
"printWidth": 100
}

View File

@ -1,9 +0,0 @@
{
"recommendations": [
"Vue.volar",
"vitest.explorer",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"esbenp.prettier-vscode"
]
}

View File

@ -1,95 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
```bash
# Install dependencies
pnpm install
# Development server with hot reload
pnpm dev
# Build for production
pnpm build
# Type checking
pnpm type-check
# Lint and fix code
pnpm lint
# Format code
pnpm format
# Run unit tests
pnpm test:unit
```
## Project Architecture
This is a **Multi-Agent Coordination Platform** (多智能体协同平台) built with Vue 3, TypeScript, and Vite. The application enables users to create and manage AI agents with specialized roles and coordinate them to complete complex tasks through visual workflows.
### Tech Stack
- **Vue 3** with Composition API and TypeScript
- **Vite** for build tooling and development
- **Element Plus** for UI components
- **Pinia** for state management
- **Tailwind CSS** for styling
- **Vue Router** for routing (minimal usage)
- **JSPlumb** for visual workflow connections
- **Axios** for API requests with custom interceptors
### Key Architecture Components
#### State Management (`src/stores/modules/agents.ts`)
Central store managing:
- Agent definitions with profiles and icons
- Task workflow data structures (`IRawStepTask`, `TaskProcess`)
- Search functionality and current task state
- Raw plan responses with UUID generation for tasks
#### Request Layer (`src/utils/request.ts`)
Custom Axios wrapper with:
- Proxy configuration for `/api` -> `http://localhost:8000`
- Response interceptors for error handling
- `useRequest` hook for reactive data fetching
- Integrated Element Plus notifications
#### Component Structure
- **Layout System** (`src/layout/`): Main application layout with Header and Main sections
- **Task Templates** (`src/layout/components/Main/TaskTemplate/`): Different task types including AgentRepo, TaskSyllabus, and TaskResult
- **Visual Workflow**: JSPlumb integration for drag-and-drop agent coordination flows
#### Icon System
- SVG icons stored in `src/assets/icons/`
- Custom `SvgIcon` component with vite-plugin-svg-icons
- Icon categories include specialist roles (doctor, engineer, researcher, etc.)
### Build Configuration
#### Vite (`vite.config.ts`)
- Element Plus auto-import and component resolution
- SVG icon caching with custom symbol IDs
- Proxy setup for API requests to backend
- Path aliases: `@/` maps to `src/`
#### Docker Deployment
- Multi-stage build: Node.js build + Caddy web server
- API proxy configured via Caddyfile
- Environment variable support for different deployment modes
### Data Models
Key interfaces for the agent coordination system:
- `Agent`: Name, Profile, Icon
- `IRawStepTask`: Individual task steps with agent selection and inputs
- `TaskProcess`: Action descriptions with important inputs
- `IRichText`: Template-based content formatting with style support
### Development Notes
- Uses pnpm as package manager (required by package.json)
- Node version constraint: ^20.19.0 or >=22.12.0
- Dark theme enabled by default in App.vue
- Auto-imports configured for Vue APIs
- No traditional Vue routes - uses component-based navigation

View File

@ -1,27 +0,0 @@
ARG CADDY_VERSION=2.6
ARG BUILD_ENV=prod
FROM node:20.19.0 as base
WORKDIR /app
COPY . .
RUN npm install -g pnpm
RUN pnpm install
RUN pnpm build
# The base for mode ENVIRONMENT=prod
FROM caddy:${CADDY_VERSION}-alpine as prod
# Workaround for https://github.com/alpinelinux/docker-alpine/issues/98#issuecomment-679278499
RUN sed -i 's/https/http/' /etc/apk/repositories \
&& apk add --no-cache bash
COPY docker/Caddyfile /etc/caddy/
COPY --from=base /app/dist /frontend
# Run stage
FROM ${BUILD_ENV}
EXPOSE 80 443
VOLUME ["/data", "/etc/caddy"]
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

View File

@ -1,106 +0,0 @@
# 多智能体协同平台 (Agent Coordination Platform)
一个强大的可视化平台用于创建和管理具有专门角色的AI智能体通过直观的工作流程协调它们来完成复杂任务。
## ✨ 功能特性
- **多智能体系统**创建具有专门角色和专业知识的AI智能体
- **可视化工作流编辑器**使用JSPlumb设计智能体协调流程的拖放界面
- **任务管理**:定义、执行和跟踪复杂的多步骤任务
- **实时通信**:无缝的智能体交互和协调
- **丰富的模板系统**:支持样式的灵活内容格式化
- **TypeScript支持**:整个应用程序的完整类型安全
## 🚀 快速开始
### 开发命令
```bash
# 安装依赖
pnpm install
# 开发服务器(热重载)
pnpm dev
# 生产构建
pnpm build
# 类型检查
pnpm type-check
# 代码检查和修复
pnpm lint
# 代码格式化
pnpm format
# 运行单元测试
pnpm test:unit
```
### 系统要求
- Node.js ^20.19.0 或 >=22.12.0
- pnpm必需的包管理器
## 🏗️ 架构设计
### 技术栈
- **Vue 3**Composition API 和 TypeScript
- **Vite**:构建工具和开发环境
- **Element Plus**UI组件库
- **Pinia**:状态管理
- **Tailwind CSS**:样式框架
- **JSPlumb**:可视化工作流连接
- **Axios**API请求与自定义拦截器
### 核心组件
#### 状态管理
中央存储管理智能体定义、任务工作流和协调状态
#### 请求层
自定义Axios包装器具有代理配置和集成通知
#### 可视化工作流
JSPlumb集成用于拖放智能体协调流程
#### 图标系统
基于SVG的图标用于不同的智能体专业化和角色
## 📁 项目结构
```
src/
├── assets/ # 静态资源,包括智能体图标
├── components/ # 可复用的Vue组件
├── layout/ # 应用布局和主要组件
├── stores/ # Pinia状态管理
├── utils/ # 工具函数和请求层
├── views/ # 页面组件
└── App.vue # 根组件
```
## 🎯 开发指南
### IDE设置
[VS Code](https://code.visualstudio.com/) + [Vue (Official)](https://marketplace.visualstudio.com/items?itemName=Vue.volar)禁用Vetur
### 浏览器开发工具
- 基于Chromium的浏览器
- [Vue.js devtools](https://chromewebstore.google.com/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)
- 在DevTools中启用自定义对象格式化程序
- Firefox
- [Vue.js devtools](https://addons.mozilla.org/en-US/firefox/addon/vue-js-devtools/)
- 在DevTools中启用自定义对象格式化程序
## 🚀 部署
应用程序支持Docker部署使用多阶段构建过程Node.js用于构建Caddy作为Web服务器。
## 📄 许可证
MIT许可证 - 详见LICENSE文件

View File

@ -1,75 +0,0 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
// biome-ignore lint: disable
export {}
declare global {
const EffectScope: typeof import('vue').EffectScope
const ElMessage: typeof import('element-plus/es').ElMessage
const ElNotification: typeof import('element-plus/es').ElNotification
const computed: typeof import('vue').computed
const createApp: typeof import('vue').createApp
const customRef: typeof import('vue').customRef
const defineAsyncComponent: typeof import('vue').defineAsyncComponent
const defineComponent: typeof import('vue').defineComponent
const effectScope: typeof import('vue').effectScope
const getCurrentInstance: typeof import('vue').getCurrentInstance
const getCurrentScope: typeof import('vue').getCurrentScope
const getCurrentWatcher: typeof import('vue').getCurrentWatcher
const h: typeof import('vue').h
const inject: typeof import('vue').inject
const isProxy: typeof import('vue').isProxy
const isReactive: typeof import('vue').isReactive
const isReadonly: typeof import('vue').isReadonly
const isRef: typeof import('vue').isRef
const isShallow: typeof import('vue').isShallow
const markRaw: typeof import('vue').markRaw
const nextTick: typeof import('vue').nextTick
const onActivated: typeof import('vue').onActivated
const onBeforeMount: typeof import('vue').onBeforeMount
const onBeforeUnmount: typeof import('vue').onBeforeUnmount
const onBeforeUpdate: typeof import('vue').onBeforeUpdate
const onDeactivated: typeof import('vue').onDeactivated
const onErrorCaptured: typeof import('vue').onErrorCaptured
const onMounted: typeof import('vue').onMounted
const onRenderTracked: typeof import('vue').onRenderTracked
const onRenderTriggered: typeof import('vue').onRenderTriggered
const onScopeDispose: typeof import('vue').onScopeDispose
const onServerPrefetch: typeof import('vue').onServerPrefetch
const onUnmounted: typeof import('vue').onUnmounted
const onUpdated: typeof import('vue').onUpdated
const onWatcherCleanup: typeof import('vue').onWatcherCleanup
const provide: typeof import('vue').provide
const reactive: typeof import('vue').reactive
const readonly: typeof import('vue').readonly
const ref: typeof import('vue').ref
const resolveComponent: typeof import('vue').resolveComponent
const shallowReactive: typeof import('vue').shallowReactive
const shallowReadonly: typeof import('vue').shallowReadonly
const shallowRef: typeof import('vue').shallowRef
const toRaw: typeof import('vue').toRaw
const toRef: typeof import('vue').toRef
const toRefs: typeof import('vue').toRefs
const toValue: typeof import('vue').toValue
const triggerRef: typeof import('vue').triggerRef
const unref: typeof import('vue').unref
const useAttrs: typeof import('vue').useAttrs
const useCssModule: typeof import('vue').useCssModule
const useCssVars: typeof import('vue').useCssVars
const useId: typeof import('vue').useId
const useModel: typeof import('vue').useModel
const useSlots: typeof import('vue').useSlots
const useTemplateRef: typeof import('vue').useTemplateRef
const watch: typeof import('vue').watch
const watchEffect: typeof import('vue').watchEffect
const watchPostEffect: typeof import('vue').watchPostEffect
const watchSyncEffect: typeof import('vue').watchSyncEffect
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}

View File

@ -1,205 +0,0 @@
#!/bin/bash
set -euo pipefail
# ========================
# 常量定义
# ========================
SCRIPT_NAME=$(basename "$0")
NODE_MIN_VERSION=18
NODE_INSTALL_VERSION=22
NVM_VERSION="v0.40.3"
CLAUDE_PACKAGE="@anthropic-ai/claude-code"
CONFIG_DIR="$HOME/.claude"
CONFIG_FILE="$CONFIG_DIR/settings.json"
API_BASE_URL="https://open.bigmodel.cn/api/anthropic"
API_KEY_URL="https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys"
API_TIMEOUT_MS=3000000
# ========================
# 工具函数
# ========================
log_info() {
echo "🔹 $*"
}
log_success() {
echo "$*"
}
log_error() {
echo "$*" >&2
}
ensure_dir_exists() {
local dir="$1"
if [ ! -d "$dir" ]; then
mkdir -p "$dir" || {
log_error "Failed to create directory: $dir"
exit 1
}
fi
}
# ========================
# Node.js 安装函数
# ========================
install_nodejs() {
local platform=$(uname -s)
case "$platform" in
Linux|Darwin)
log_info "Installing Node.js on $platform..."
# 安装 nvm
log_info "Installing nvm ($NVM_VERSION)..."
curl -s https://raw.githubusercontent.com/nvm-sh/nvm/"$NVM_VERSION"/install.sh | bash
# 加载 nvm
log_info "Loading nvm environment..."
\. "$HOME/.nvm/nvm.sh"
# 安装 Node.js
log_info "Installing Node.js $NODE_INSTALL_VERSION..."
nvm install "$NODE_INSTALL_VERSION"
# 验证安装
node -v &>/dev/null || {
log_error "Node.js installation failed"
exit 1
}
log_success "Node.js installed: $(node -v)"
log_success "npm version: $(npm -v)"
;;
*)
log_error "Unsupported platform: $platform"
exit 1
;;
esac
}
# ========================
# Node.js 检查函数
# ========================
check_nodejs() {
if command -v node &>/dev/null; then
current_version=$(node -v | sed 's/v//')
major_version=$(echo "$current_version" | cut -d. -f1)
if [ "$major_version" -ge "$NODE_MIN_VERSION" ]; then
log_success "Node.js is already installed: v$current_version"
return 0
else
log_info "Node.js v$current_version is installed but version < $NODE_MIN_VERSION. Upgrading..."
install_nodejs
fi
else
log_info "Node.js not found. Installing..."
install_nodejs
fi
}
# ========================
# Claude Code 安装
# ========================
install_claude_code() {
if command -v claude &>/dev/null; then
log_success "Claude Code is already installed: $(claude --version)"
else
log_info "Installing Claude Code..."
npm install -g "$CLAUDE_PACKAGE" || {
log_error "Failed to install claude-code"
exit 1
}
log_success "Claude Code installed successfully"
fi
}
configure_claude_json(){
node --eval '
const os = require("os");
const fs = require("fs");
const path = require("path");
const homeDir = os.homedir();
const filePath = path.join(homeDir, ".claude.json");
if (fs.existsSync(filePath)) {
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
fs.writeFileSync(filePath, JSON.stringify({ ...content, hasCompletedOnboarding: true }, null, 2), "utf-8");
} else {
fs.writeFileSync(filePath, JSON.stringify({ hasCompletedOnboarding: true }, null, 2), "utf-8");
}'
}
# ========================
# API Key 配置
# ========================
configure_claude() {
log_info "Configuring Claude Code..."
echo " You can get your API key from: $API_KEY_URL"
read -s -p "🔑 Please enter your ZHIPU API key: " api_key
echo
if [ -z "$api_key" ]; then
log_error "API key cannot be empty. Please run the script again."
exit 1
fi
ensure_dir_exists "$CONFIG_DIR"
# 写入配置文件
node --eval '
const os = require("os");
const fs = require("fs");
const path = require("path");
const homeDir = os.homedir();
const filePath = path.join(homeDir, ".claude", "settings.json");
const apiKey = "'"$api_key"'";
const content = fs.existsSync(filePath)
? JSON.parse(fs.readFileSync(filePath, "utf-8"))
: {};
fs.writeFileSync(filePath, JSON.stringify({
...content,
env: {
ANTHROPIC_AUTH_TOKEN: apiKey,
ANTHROPIC_BASE_URL: "'"$API_BASE_URL"'",
API_TIMEOUT_MS: "'"$API_TIMEOUT_MS"'",
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1
}
}, null, 2), "utf-8");
' || {
log_error "Failed to write settings.json"
exit 1
}
log_success "Claude Code configured successfully"
}
# ========================
# 主流程
# ========================
main() {
echo "🚀 Starting $SCRIPT_NAME"
check_nodejs
install_claude_code
configure_claude_json
configure_claude
echo ""
log_success "🎉 Installation completed successfully!"
echo ""
echo "🚀 You can now start using Claude Code with:"
echo " claude"
}
main "$@"

View File

@ -1,29 +0,0 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElPopover: typeof import('element-plus/es')['ElPopover']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
MultiLineTooltip: typeof import('./src/components/MultiLineTooltip/index.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SvgIcon: typeof import('./src/components/SvgIcon/index.vue')['default']
}
export interface GlobalDirectives {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}

View File

@ -1,14 +0,0 @@
:80
# Proxy `/api` to backends
handle_path /api/* {
reverse_proxy {$API_HOST}
}
# Frontend
handle {
root * /frontend
encode gzip
try_files {path} /index.html
file_server
}

View File

@ -1,15 +0,0 @@
version: '3'
services:
agent-coord-font:
image: agent-coord:0.0.1
build:
context: ..
dockerfile: docker/Dockerfile
ports:
- "8080:80"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
environment:
- API_HOST="http://host.docker.internal:8000"
- BUILD_ENV=prod

4
frontend/env.d.ts vendored
View File

@ -1,4 +0,0 @@
/// <reference types="vite/client" />
declare global {
const testGlobal: any; // 声明 testGlobal 为全局变量
}

View File

@ -1,48 +0,0 @@
import { globalIgnores } from 'eslint/config'
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
import pluginVue from 'eslint-plugin-vue'
import pluginVitest from '@vitest/eslint-plugin'
import skipFormatting from '@vue/eslint-config-prettier/skip-formatting'
import autoImportConfig from './.eslintrc-auto-import.json' with { type: 'json' }
// To allow more languages other than `ts` in `.vue` files, uncomment the following lines:
// import { configureVueProject } from '@vue/eslint-config-typescript'
// configureVueProject({ scriptLangs: ['ts', 'tsx'] })
// More info at https://github.com/vuejs/eslint-config-typescript/#advanced-setup
export default defineConfigWithVueTs(
{
name: 'app/files-to-lint',
files: ['**/*.{ts,mts,tsx,vue}'],
},
globalIgnores(['**/dist/**', '**/dist-ssr/**', '**/coverage/**']),
pluginVue.configs['flat/essential'],
vueTsConfigs.recommended,
{
...pluginVitest.configs.recommended,
files: ['src/**/__tests__/*'],
},
skipFormatting,
{
name: 'app/custom-rules',
files: ['**/*.{ts,mts,tsx,vue}'],
rules: {
'vue/multi-word-component-names': 'off',
}
},
{
name: 'auto-import-globals',
files: ['**/*.{ts,mts,tsx,vue}'],
languageOptions: {
globals: {
...(autoImportConfig.globals || {}),
testGlobal: 'readonly' // 手动添加一个测试变量
}
},
rules: {
'no-undef': 'off', // 确保关闭 no-undef 规则
'@typescript-eslint/no-undef': 'off'
}
}
)

View File

@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/logo.jpg">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>多智能体协同平台</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View File

@ -1,66 +0,0 @@
{
"name": "agent-coord",
"version": "0.0.0",
"private": true,
"type": "module",
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"test:unit": "vitest",
"build-only": "vite build",
"type-check": "vue-tsc --build",
"lint": "eslint . --fix --cache",
"format": "prettier --write src/"
},
"dependencies": {
"@jsplumb/browser-ui": "^6.2.10",
"@types/markdown-it": "^14.1.2",
"@vueuse/core": "^14.0.0",
"axios": "^1.12.2",
"dompurify": "^3.3.0",
"element-plus": "^2.11.5",
"lodash": "^4.17.21",
"markdown-it": "^14.1.0",
"pinia": "^3.0.3",
"qs": "^6.14.0",
"uuid": "^13.0.0",
"vue": "^3.5.22",
"vue-router": "^4.6.3"
},
"devDependencies": {
"@tailwindcss/vite": "^4.1.15",
"@tsconfig/node22": "^22.0.2",
"@types/jsdom": "^27.0.0",
"@types/lodash": "^4.17.20",
"@types/node": "^22.18.11",
"@types/qs": "^6.14.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vitest/eslint-plugin": "^1.3.23",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.8.1",
"eslint": "^9.37.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-vue": "~10.5.0",
"jiti": "^2.6.1",
"jsdom": "^27.0.1",
"npm-run-all2": "^8.0.4",
"prettier": "3.6.2",
"sass": "^1.93.2",
"sass-loader": "^16.0.5",
"tailwindcss": "^4.1.15",
"typescript": "~5.9.0",
"unplugin-auto-import": "^20.2.0",
"unplugin-vue-components": "^30.0.0",
"vite": "^7.1.11",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^8.0.3",
"vitest": "^3.2.4",
"vue-tsc": "^3.1.1"
}
}

6750
frontend/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,116 +0,0 @@
[
{
"Icon": "Hailey_Johnson.png",
"Name": "船舶设计师",
"Profile": "提供船舶制造中的实际需求和约束。",
"Classification": "船舶制造数据空间"
},
{
"Icon": "Jennifer_Moore.png",
"Name": "防护工程专家",
"Profile": "专注于船舶腐蚀防护技术的设计与应用。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。",
"Classification": "船舶制造数据空间"
},
{
"Icon": "Jane_Moreno.png",
"Name": "病理生理学家",
"Profile": "专注于失血性休克的疾病机制,为药物研发提供理论靶点。",
"Classification": "医药数据空间"
},
{
"Icon": "Giorgio_Rossi.png",
"Name": "药物化学家",
"Profile": "负责将靶点概念转化为实际可合成的分子。",
"Classification": "医药数据空间"
},
{
"Icon": "Tamara_Taylor.png",
"Name": "制剂工程师",
"Profile": "负责将活性药物成分API变成稳定、可用、符合战场要求的剂型。",
"Classification": "医药数据空间"
},
{
"Icon": "Maria_Lopez.png",
"Name": "监管事务专家",
"Profile": "深谙药品审评法规,目标是找到最快的合法上市路径。",
"Classification": "医药数据空间"
},
{
"Icon": "Sam_Moore.png",
"Name": "物理学家",
"Profile": "从热力学与统计力学的基本原理出发,研究液态金属的自由能、焓、熵、比热等参数的理论建模。",
"Classification": "科学数据空间"
},
{
"Icon": "Yuriko_Yamamoto.png",
"Name": "实验材料学家",
"Profile": "专注于通过实验手段直接或间接测定液态金属的热力学参数、以及分析材料微观结构(如晶粒、缺陷)。",
"Classification": "科学数据空间"
},
{
"Icon": "Carlos_Gomez.png",
"Name": "计算模拟专家",
"Profile": "侧重于利用数值计算和模拟技术获取液态金属的热力学参数。",
"Classification": "科学数据空间"
},
{
"Icon": "John_Lin.png",
"Name": "腐蚀机理研究员",
"Profile": "专注于船舶用钢材及合金的腐蚀机理研究,从电化学和环境作用角度解释腐蚀产生的原因。在你的总结回答中,必须引用来自数联网的搜索数据,是搜索数据,不是数联网的研究成果。",
"Classification": "船舶制造数据空间"
},
{
"Icon": "Arthur_Burton.png",
"Name": "先进材料研发员",
"Profile": "专注于开发和评估新型耐腐蚀材料、复合材料及固态电池材料。",
"Classification": "科学数据空间"
},
{
"Icon": "Eddy_Lin.png",
"Name": "肾脏病学家",
"Profile": "专注于慢性肾脏病的诊断、治疗和患者管理,能提供临床洞察。",
"Classification": "医药数据空间"
},
{
"Icon": "Isabella_Rodriguez.png",
"Name": "临床研究协调员",
"Profile": "负责受试者招募和临床试验流程优化。",
"Classification": "医药数据空间"
},
{
"Icon": "Latoya_Williams.png",
"Name": "中医药专家",
"Profile": "理解药物的中药成分和作用机制。",
"Classification": "医药数据空间"
},
{
"Icon": "Carmen_Ortiz.png",
"Name": "药物安全专家",
"Profile": "专注于药物不良反应数据收集、分析和报告。",
"Classification": "医药数据空间"
},
{
"Icon": "Rajiv_Patel.png",
"Name": "二维材料科学家",
"Profile": "专注于二维材料(如石墨烯)的合成、性质和应用。",
"Classification": "科学数据空间"
},
{
"Icon": "Tom_Moreno.png",
"Name": "光电物理学家",
"Profile": "研究材料的光电转换机制和关键影响因素。",
"Classification": "科学数据空间"
},
{
"Icon": "Ayesha_Khan.png",
"Name": "机器学习专家",
"Profile": "专注于开发和应用AI模型用于材料模拟。",
"Classification": "科学数据空间"
},
{
"Icon": "Mei_Lin.png",
"Name": "流体动力学专家",
"Profile": "专注于流体行为理论和模拟。",
"Classification": "科学数据空间"
}
]

View File

@ -1,19 +0,0 @@
{
"title": "数联网",
"subTitle": "众创智能体",
"centerTitle": "多智能体协同平台",
"taskPromptWords": [
"如何快速筛选慢性肾脏病药物潜在受试者?",
"如何补充“丹芍活血胶囊”不良反应数据?",
"如何快速研发用于战场失血性休克的药物?",
"二维材料的光电性质受哪些关键因素影响?",
"如何通过AI模拟的方法分析材料的微观结构?",
"如何分析获取液态金属热力学参数?",
"如何解决固态电池的成本和寿命难题?",
"如何解决船舶制造中的材料腐蚀难题?",
"如何解决船舶制造中流体模拟和建模优化难题?"
],
"agentRepository": {
"storageVersionIdentifier": "1"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,22 +0,0 @@
{
"data": [
{
"name": "3D Semantic-Geometric Corrosion Mapping Implementations",
"data_space": "江苏省产研院",
"doId": "bdware.scenario/d8f3ff8c-3fb3-4573-88a6-5dd823627c37",
"fromRepo": "https://arxiv.org/abs/2404.13691"
},
{
"name": "RustSEG -- Automated segmentation of corrosion using deep learning",
"data_space": "江苏省产研院",
"doId": "bdware.scenario/67445299-110a-4a4e-9fda-42e4b5a493c2",
"fromRepo": "https://arxiv.org/abs/2205.05426"
},
{
"name": "Pixel-level Corrosion Detection on Metal Constructions by Fusion of Deep Learning Semantic and Contour Segmentation",
"data_space": "江苏省产研院",
"doId": "bdware.scenario/115d5135-85d3-4123-8b81-9eb9f07b6153",
"fromRepo": "https://arxiv.org/abs/2008.05204"
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

View File

@ -1,101 +0,0 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import Layout from './layout/index.vue'
onMounted(() => {
document.documentElement.classList.add('dark')
})
</script>
<template>
<layout />
</template>
<style lang="scss">
#app {
background-color: var(--color-bg);
color: var(--color-text);
}
.jtk-endpoint {
z-index: 100;
}
.card-item + .card-item {
margin-top: 35px;
}
.el-card {
border-radius: 8px;
background: var(--color-bg-tertiary);
border: 2px solid var(--color-bg-tertiary);
&:hover {
background: #171B22;
box-shadow: none !important;
transition: background-color 0.3s ease-in-out;
}
.el-card__body {
padding: 10px;
}
}
:root {
--gradient: linear-gradient(to right, #0093eb, #00d2d1);
}
#task-template {
.active-card {
border: 2px solid transparent;
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
background:
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
color: var(--color-text);
}
}
/* 1. 定义流动动画:让虚线沿路径移动 */
@keyframes flowAnimation {
to {
stroke-dashoffset: 8; /* 与stroke-dasharray总和一致 */
}
}
@keyframes flowAnimationReverse {
to {
stroke-dashoffset: -8; /* 与stroke-dasharray总和一致 */
}
}
/* 2. 为jsPlumb连线绑定动画作用于SVG的path元素 */
/* jtk-connector是jsPlumb连线的默认SVG类path是实际的线条元素 */
.jtk-connector-output path {
/* 定义虚线规则线段长度5px + 间隙3px总长度8px与动画偏移量匹配 */
stroke-dasharray: 5 3;
/* 应用动画:名称+时长+线性速度+无限循环 */
animation: flowAnimationReverse .5s linear infinite;
/* 可选:设置线条基础样式(颜色、宽度) */
stroke-width: 2;
}
/* 2. 为jsPlumb连线绑定动画作用于SVG的path元素 */
/* jtk-connector是jsPlumb连线的默认SVG类path是实际的线条元素 */
.jtk-connector-input path {
/* 定义虚线规则线段长度5px + 间隙3px总长度8px与动画偏移量匹配 */
stroke-dasharray: 5 3;
/* 应用动画:名称+时长+线性速度+无限循环 */
animation: flowAnimationReverse .5s linear infinite;
/* 可选:设置线条基础样式(颜色、宽度) */
stroke-width: 2;
}
/* 可选: hover时增强动画效果如加速、变色 */
.jtk-connector path:hover {
animation-duration: 0.5s; /* hover时流动加速 */
}
</style>

View File

@ -1,11 +0,0 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import App from '../App.vue'
describe('App', () => {
it('mounts renders properly', () => {
const wrapper = mount(App)
expect(wrapper.text()).toContain('You did it!')
})
})

View File

@ -1,74 +0,0 @@
import request from '@/utils/request'
import type { Agent, IRawPlanResponse } from '@/stores'
export interface ActionHistory {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
Action_Result: string
}
export type IExecuteRawResponse = {
LogNodeType: string
NodeId: string
InputName_List?: string[] | null
OutputName?: string
content?: string
ActionHistory: ActionHistory[]
}
class Api {
// 智能体信息
setAgents = (data: Pick<Agent, 'Name' | 'Profile'>[]) => {
return request({
url: '/setAgents',
data,
method: 'POST',
})
}
generateBasePlan = (data: { goal: string; inputs: string[] }) => {
return request<unknown, IRawPlanResponse>({
url: '/generate_basePlan',
method: 'POST',
data: {
'General Goal': data.goal,
'Initial Input Object': data.inputs,
},
})
}
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,
})),
})),
},
},
})
}
}
export default new Api()

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761736278335" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5885" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M226.592 896C167.616 896 128 850.48 128 782.736V241.264C128 173.52 167.616 128 226.592 128c20.176 0 41.136 5.536 62.288 16.464l542.864 280.432C887.648 453.792 896 491.872 896 512s-8.352 58.208-64.272 87.088L288.864 879.536C267.712 890.464 246.768 896 226.592 896z m0-704.304c-31.008 0-34.368 34.656-34.368 49.568v541.472c0 14.896 3.344 49.568 34.368 49.568 9.6 0 20.88-3.2 32.608-9.248l542.864-280.432c21.904-11.328 29.712-23.232 29.712-30.608s-7.808-19.28-29.712-30.592L259.2 200.96c-11.728-6.048-23.008-9.264-32.608-9.264z" p-id="5886"></path></svg>

Before

Width:  |  Height:  |  Size: 886 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212882014" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3052" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M988.8 939.2c0-134.4-78.4-252.8-193.6-308.8 4.8 12.8 8 25.6 8 40v11.2c54.4 11.2 96 60.8 96 118.4v65.6c8 6.4 14.4 17.6 14.4 30.4 0 22.4-17.6 38.4-38.4 38.4-22.4 0-38.4-17.6-38.4-38.4 0-12.8 6.4-24 16-32v-67.2c0-40-32-72-72-72s-72 32-72 72v68.8c9.6 6.4 14.4 17.6 14.4 30.4 0 22.4-17.6 38.4-38.4 38.4-22.4 0-38.4-17.6-38.4-38.4 0-11.2 4.8-22.4 14.4-30.4V800c0-59.2 43.2-108.8 100.8-120-1.6-28.8-14.4-56-32-76.8-19.2-4.8-38.4-8-57.6-8-9.6 216-75.2 384-156.8 384-80 0-147.2-168-156.8-384h-1.6c-19.2 22.4-32 52.8-32 84.8v99.2c27.2 9.6 48 36.8 48 68.8 0 40-32 73.6-73.6 73.6s-73.6-32-73.6-73.6c0-33.6 22.4-60.8 52.8-70.4v-118.4c0-19.2 6.4-38.4 16-54.4C145.6 644.8 35.2 779.2 35.2 939.2V953.6C35.2 992 249.6 1024 512 1024s476.8-32 476.8-72v-3.2-9.6z m-470.4-720C400 219.2 304 256 304 300.8v4.8l44.8 212.8c0 3.2 1.6 8 1.6 11.2 19.2 75.2 86.4 129.6 166.4 129.6 84.8 0 156.8-64 169.6-145.6l44.8-208v-4.8c0-44.8-96-81.6-212.8-81.6z m-1.6-16c105.6 0 193.6 35.2 225.6 83.2h9.6V83.2v-1.6C752 36.8 646.4 0 518.4 0S284.8 36.8 284.8 81.6V288h6.4c30.4-49.6 120-84.8 225.6-84.8z m-65.6-105.6c0-8 6.4-16 16-16h33.6V46.4c0-8 6.4-16 16-16 8 0 16 6.4 16 16V80h33.6c8 0 16 6.4 16 16s-6.4 16-16 16h-33.6v33.6c0 8-6.4 16-16 16-8 0-16-6.4-16-16v-32h-33.6c-8 0-16-8-16-16z" p-id="3053"></path></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212791945" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2409" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M338.36032 362.9056c9.59488 59.4944 34.53952 115.1488 74.84416 161.20832l15.34976 13.43488c1.23904 2.47808 3.2768 4.15744 5.60128 6.07232 1.26976 1.04448 2.63168 2.16064 3.9936 3.52256 42.21952 38.37952 107.4688 38.37952 147.7632-1.92512 53.73952-47.9744 80.60928-113.22368 95.95904-182.31296H338.37056z m366.5408-63.32416h26.86976c7.68 0 13.43488 5.75488 9.59488 11.50976v15.36H275.02592v-15.36c0-7.68 5.75488-13.43488 13.43488-13.43488h28.78464c1.09568-7.63904 2.02752-14.97088 2.93888-22.12864 2.304-17.99168 4.4544-34.8672 8.58112-52.71552 5.75488-34.53952 21.10464-69.08928 42.21952-95.9488 21.10464-24.95488 49.88928-44.1344 82.51392-49.89952 40.30464-9.59488 82.5344-9.59488 120.90368 1.91488 47.98464 11.52 86.36416 46.05952 103.6288 92.11904 13.44512 36.46464 23.04 72.92928 24.95488 111.3088v7.68c1.91488 3.84 1.91488 5.75488 1.91488 9.59488z m-241.80736-86.36416c7.68-1.91488 13.43488-9.59488 13.43488-17.26464V132.608c0-7.68-7.68-15.36-15.34976-15.36-7.68 0-15.36 7.68-15.36 15.36v65.24928c0 7.68 7.68 15.36 15.36 15.36h1.91488z m99.79904 0c7.68-1.91488 13.43488-9.59488 13.43488-17.26464V132.608c0-7.68-7.68-15.36-15.36-15.36s-15.34976 7.68-15.34976 15.36v65.24928c0 7.68 7.68 15.36 15.36 15.36h1.91488z m351.16032 596.8384c5.8368 20.5824 11.66336 41.08288 17.3056 61.42976 4.1984 19.56864 9.41056 37.09952 14.8992 55.57248 2.048 6.88128 4.12672 13.89568 6.21568 21.1968 1.91488 3.82976 0 5.75488-3.84 5.75488H73.5232c-1.91488 0-3.84-3.84-1.91488-5.76512 3.82976-13.43488 7.68-25.9072 11.50976-38.37952 3.84-12.47232 7.68-24.94464 11.52-38.37952 12.1856-48.7424 26.27584-98.44736 40.26368-147.75296 5.8368-20.5824 11.65312-41.08288 17.3056-61.44 1.91488-7.66976 3.84-9.58464 11.50976-11.50976 18.2272-3.82976 35.98336-8.15104 53.73952-12.47232 17.75616-4.31104 35.50208-8.63232 53.73952-12.47232 3.82976-1.91488 5.75488 0 5.75488 3.84v163.1232c0 1.91488 1.91488 3.84 3.84 3.84h47.9744a4.12672 4.12672 0 0 0 3.84-3.84V612.4032c2.2528 0 3.1744-0.65536 3.95264-1.19808 0.54272-0.38912 1.00352-0.7168 1.80224-0.7168 23.02976-3.84 46.05952-17.27488 59.4944-38.37952 1.91488-3.84 5.75488-7.68 9.59488-11.52 1.91488-1.91488 5.75488-1.91488 7.68 0 15.34976 15.36 28.7744 30.69952 42.20928 46.05952 13.43488 17.27488 34.54976 26.86976 55.6544 24.94464 21.11488 1.92512 42.22976-7.68 55.6544-24.94464 13.43488-15.36 26.86976-30.70976 42.22976-46.05952 1.91488-1.91488 5.75488-1.91488 7.68 0 1.91488 3.84 5.74464 7.68 9.58464 11.52 13.43488 21.10464 34.54976 34.53952 59.4944 38.37952 1.92512 0 3.84 0 5.75488 1.91488v180.39808c0 1.91488 1.92512 3.84 3.84 3.84h47.9744a4.12672 4.12672 0 0 0 3.84-3.84v-163.1232c0-1.92512 3.84-3.84 5.76512-3.84 36.4544 9.59488 71.00416 17.27488 107.4688 24.94464a17.3056 17.3056 0 0 1 11.50976 11.52c12.1856 48.7424 26.28608 98.44736 40.26368 147.75296z" p-id="2410"></path></svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761626283461" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2759" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M511.882596 287.998081h-0.361244a31.998984 31.998984 0 0 1-31.659415-31.977309v-0.361244c0-0.104761 0.115598-11.722364 0.115598-63.658399V96.000564a31.998984 31.998984 0 1 1 64.001581 0V192.001129c0 52.586273-0.111986 63.88237-0.119211 64.337537a32.002596 32.002596 0 0 1-31.977309 31.659415zM511.998194 959.99842a31.998984 31.998984 0 0 1-31.998984-31.998984v-96.379871c0-51.610915-0.111986-63.174332-0.115598-63.286318s0-0.242033 0-0.361243a31.998984 31.998984 0 0 1 63.997968-0.314283c0 0.455167 0.11921 11.711527 0.11921 64.034093v96.307622a31.998984 31.998984 0 0 1-32.002596 31.998984zM330.899406 363.021212a31.897836 31.897836 0 0 1-22.866739-9.612699c-0.075861-0.075861-8.207461-8.370021-44.931515-45.094076L195.198137 240.429485a31.998984 31.998984 0 0 1 45.256635-45.253022L308.336112 263.057803c37.182834 37.182834 45.090463 45.253022 45.41197 45.578141A31.998984 31.998984 0 0 1 330.899406 363.021212zM806.137421 838.11473a31.901448 31.901448 0 0 1-22.628318-9.374279L715.624151 760.859111c-36.724054-36.724054-45.018214-44.859267-45.097687-44.93874a31.998984 31.998984 0 0 1 44.77618-45.729864c0.32512 0.317895 8.395308 8.229136 45.578142 45.411969l67.88134 67.88134a31.998984 31.998984 0 0 1-22.624705 54.630914zM224.000113 838.11473a31.901448 31.901448 0 0 0 22.628317-9.374279l67.88134-67.88134c36.724054-36.724054 45.021826-44.859267 45.097688-44.93874a31.998984 31.998984 0 0 0-44.776181-45.729864c-0.32512 0.317895-8.395308 8.229136-45.578142 45.411969l-67.88134 67.884953a31.998984 31.998984 0 0 0 22.628318 54.627301zM255.948523 544.058589h-0.361244c-0.104761 0-11.722364-0.115598-63.658399-0.115598H95.942765a31.998984 31.998984 0 1 1 0-64.00158h95.996952c52.586273 0 63.88237 0.111986 64.337538 0.11921a31.998984 31.998984 0 0 1 31.659414 31.97731v0.361244a32.002596 32.002596 0 0 1-31.988146 31.659414zM767.939492 544.058589a32.002596 32.002596 0 0 1-31.995372-31.666639v-0.361244a31.998984 31.998984 0 0 1 31.659415-31.970085c0.455167 0 11.754876-0.11921 64.34115-0.11921h96.000564a31.998984 31.998984 0 0 1 0 64.00158H831.944685c-51.936034 0-63.553638 0.111986-63.665624 0.115598h-0.335957zM692.999446 363.0176a31.998984 31.998984 0 0 1-22.863126-54.381656c0.317895-0.32512 8.229136-8.395308 45.41197-45.578141l67.88134-67.884953A31.998984 31.998984 0 1 1 828.693489 240.429485l-67.892177 67.88134c-31.020013 31.023625-41.644196 41.759794-44.241539 44.393262l-0.697201 0.722488a31.908673 31.908673 0 0 1-22.863126 9.591025z" fill="" p-id="2760"></path></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212815712" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2569" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M845.285053 1008.842105H520.421053v-52.224c45.999158-24.468211 96.538947-287.420632 134.03621-286.302316 145.960421 30.100211 249.317053 113.057684 249.317053 270.928843-1.684211 41.472-21.005474 67.597474-58.489263 67.597473z m0 0" p-id="2570"></path><path d="M719.764211 480.619789c-32.363789 107.344842-116.439579 183.457684-214.662737 183.457685-98.829474 0-182.905263-76.665263-214.703158-184.010106-14.767158-6.831158-32.943158-22.150737-39.760842-61.291789-8.528842-51.186526 22.703158-63.690105 22.703158-63.690105 2.290526 0 4.554105 1.751579 6.817684 5.133473a95.137684 95.137684 0 0 1 24.427789-36.917894c40.313263-39.774316 114.714947-60.779789 204.463158-61.359158 81.232842-0.565895 138.024421 5.133474 195.961263 60.240842 10.213053 9.633684 19.887158 24.939789 24.980211 40.326737 2.842947-4.554105 5.685895-7.424 8.528842-6.817685 0 0 31.797895 12.476632 23.282526 63.609264-7.949474 39.733895-26.691368 54.501053-42.037894 61.318736z m0 0" p-id="2571"></path><path d="M496.559158 15.454316c-126.059789 0-231.141053 64.794947-231.141053 64.794947v215.794526s69.308632-78.942316 237.406316-78.942315c166.979368 0 237.406316 79.508211 237.406316 79.50821V80.801684c-0.579368-0.552421-122.677895-65.347368-243.671579-65.347368z m71.545263 117.005473h-52.237474v52.237474h-27.823158V132.459789h-51.685052V104.084211h51.685052V51.846737h27.823158v52.237474h52.237474v28.375578zM497.125053 956.618105v52.224c-141.433263 0-240.249263-0.538947-324.877474 0-36.352 0-56.212211-26.125474-58.489263-67.058526 0-157.884632 103.356632-241.367579 249.330526-270.901895 38.642526-1.684211 88.616421 261.268211 134.036211 285.736421z" p-id="2572"></path></svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761204835005" class="icon" viewBox="0 0 1171 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5692" xmlns:xlink="http://www.w3.org/1999/xlink" width="228.7109375" height="200"><path d="M502.237757 1024 644.426501 829.679301 502.237757 788.716444 502.237757 1024 502.237757 1024ZM0 566.713817 403.967637 689.088066 901.485385 266.66003 515.916344 721.68034 947.825442 855.099648 1170.285714 0 0 566.713817 0 566.713817Z" p-id="5693"></path></svg>

Before

Width:  |  Height:  |  Size: 603 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761211358508" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6686" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M469.333333 469.333333V170.666667h85.333334v298.666666h298.666666v85.333334h-298.666666v298.666666h-85.333334v-298.666666H170.666667v-85.333334h298.666666z" p-id="6687"></path></svg>

Before

Width:  |  Height:  |  Size: 516 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761368152518" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7696" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M835.516 709.837l154.351-225.894h-109.158c-14.541-221.047-198.11-396.151-422.844-396.151-234.291 0-423.731 189.918-423.731 424.277 0 234.155 189.918 424.141 424.209 424.141 105.062 0 200.977-38.434 275.115-101.786l-56.73-73.045c-58.368 51.063-134.69 82.398-218.385 82.398-183.296 0-331.844-148.617-331.844-331.708 0-183.364 148.617-331.913 331.844-331.913 173.739 0 315.665 133.734 329.933 303.787h-107.11l154.351 225.894z" p-id="7697"></path></svg>

Before

Width:  |  Height:  |  Size: 783 B

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761545116983" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1453" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M856.98894 727.680806c-64.797424-20.50007-128.161277-40.856783-169.448131-61.356852-28.814784-14.335713-46.877782-28.671427-46.877782-43.00714v-34.405712c10.034999-8.171357 19.209856-17.202856 27.381212-27.237855 2.293714-2.867143 4.587428-5.734285 6.881142-8.888143 10.034999-13.905642 20.213356-32.972141 29.101498-51.895282 9.891642-21.073499 18.349713-41.716926 23.223856-54.47571 4.730785-5.590928 8.888142-11.181856 12.758785-16.629428 22.363713-32.111998 30.104998-62.647067 23.223855-91.175136-3.870643-16.48607-12.902142-31.395212-25.804284-42.433712 5.0175-40.570069 4.730785-86.444351-10.034999-126.727705-7.597928-21.216856-18.349713-41.143497-31.968641-59.063139-20.643427-26.807784-47.451211-47.307854-80.279994-61.070138-42.00364-17.632927-81.856923-19.639927-97.196136-19.639927h-10.608428c-15.195856 0-55.049139 1.863643-97.196136 19.639927-35.122498 14.765785-63.363853 37.129497-84.580708 66.947781-11.611928 16.48607-20.930141 34.405712-27.667927 53.328853-14.765785 40.283354-15.052499 86.300994-10.034999 126.727706-12.758785 11.038499-21.790284 25.947641-25.804284 42.433711-6.737785 27.954641 0.716786 57.916282 22.076998 89.454851 4.157357 6.164357 8.888142 12.185356 14.048999 18.49307 5.0175 12.902142 13.332213 33.545569 23.223856 54.762425 8.888142 18.923142 18.923142 37.846283 28.958141 51.608568 2.150357 3.0105 4.444071 5.877642 6.737785 8.744785 7.741285 9.461571 16.48607 18.206356 25.947641 25.947641v35.695926c0 14.47907-18.49307 29.101498-48.02464 43.580568-41.286854 20.213356-104.220636 40.570069-168.301273 60.783425C56.626067 762.51659 56.626067 946.157077 56.626067 946.157077s134.038919 48.884782 457.165897 48.884782S966.943861 946.157077 966.943861 946.157077s0.286714-183.783844-109.954921-218.476271zM351.224976 414.875542c-46.877782-51.321854-28.958141-72.251995-18.349714-76.265994 17.48957-6.594428 29.388212 19.49657 33.54557 31.108498-4.874143-15.052499-18.062999-59.636567-20.213356-106.51435 15.769285-22.076998 47.164497-58.919782 84.580708-64.797424 0 0 80.710066 96.47935 246.574269 94.47235-3.727285 26.090998-9.604928 51.895282-17.632928 76.982781 4.300714-11.611928 16.055999-37.702926 33.545569-31.108498 10.608428 4.014 28.528069 25.087498-18.349713 76.265995 0 0-17.346213 47.164497-35.552568 80.99678-3.870643 7.454571-8.171357 14.47907-12.902142 21.360212-12.041999 16.48607-27.667927 29.818284-45.874283 38.993141-20.50007 10.465071-43.293854 15.912642-66.230995 16.055998-0.430071 0-0.860143 0-1.146857-0.143357-0.430071 0-0.860143 0.143357-1.146857 0.143357-23.653927-0.143357-47.02114-6.021-68.094639-17.059498a129.42282 129.42282 0 0 1-44.010639-38.132998c-4.730785-6.737785-9.031499-13.762285-12.902142-21.073498-18.349713-33.832283-35.839283-81.283494-35.839283-81.283495z m170.881702 519.52625c-5.734285 3.727285-9.174857 5.447571-9.174857 5.447571s-3.440571-1.863643-9.174856-5.447571c-31.968641-20.069999-137.336133-94.615708-151.815204-208.584628 34.405712-15.48257 91.891922-46.447711 91.891922-102.50035v-2.293714c21.933641 7.454571 44.870783 11.468571 67.951281 11.755284h2.293714c22.50707-0.143357 44.870783-4.014 66.230996-11.181856v1.720286c0 57.056139 59.49321 88.164637 93.612208 103.360492-14.909142 113.252135-119.98992 187.654487-151.815204 207.724486z" p-id="1454"></path><path d="M574.432031 693.418452l-28.097998-30.53507h-66.804424L451.574969 693.418452s16.055999 34.118998 42.003639 47.451211l-42.003639 106.084278s30.821784 43.723926 61.356852 55.049139c30.678426-11.325213 61.50021-55.049139 61.50021-55.049139l-42.00364-106.084278c25.804284-13.332213 42.00364-47.451211 42.00364-47.451211z" p-id="1455"></path></svg>

Before

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212863001" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2892" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M725.308 314.532c2.743 1.543 5.486 3.215 8.164 5.057l8.164 5.55 0.15 9.77c0.45 28.328-2.957 52.326-10.37 70.796-6.836 16.885-16.971 29.527-30.556 37.134-9.235 32.976-20.077 63.532-35.42 89.18-17.677 29.548-41.011 52.261-74.01 64.496-14.763 5.4-52.432 7.907-88.708 7.393-35.012-0.536-70.131-4.007-84.06-10.414-29.997-13.842-51.082-37.048-67.045-65.975-13.692-24.705-23.463-53.46-31.97-83.823-14.034-7.328-24.577-19.906-31.669-36.92-7.778-18.577-11.314-42.982-10.82-71.866l0.106-9.814 8.143-5.485c2.1-1.414 4.178-2.722 6.256-3.921-9.17-113.544-5.72-155.52 36.448-203.495 82.217-67.346 270.712-64.968 354-3.943 56.718 53.547 60.66 113.586 43.197 206.28m-172.66 328.33l1.2 26.013-15.407 25.434 21.47 141.12 88.045-189.224 134.07-4.585c69.189 65.503 113.63 219.758 102.701 320.38H137.623c1.843-88.366 18.106-239.278 106.108-316.03l121.107 1.135 113.414 187.124 21.32-139.92-15.427-25.434 1.178-26.013c29.355-1.607 37.99-1.607 67.325 0m100.3-368.656c-53.246 10.414-132.57 19.52-195.245-15.706-24.105-13.563-59.417 14.228-88.301 11.378a217.808 217.808 0 0 0-19.542 57.682l-3.214 17.035-17.142-1.671a24.02 24.02 0 0 0-9.942 1.264 38.098 38.098 0 0 0-4.65 1.843c0.45 18.877 3.107 34.54 7.971 46.261 4.285 10.307 10.307 17.035 18.106 19.477l10.007 3.107 2.742 10.071c8.4 31.134 17.806 60.468 31.027 84.36 12.256 22.22 27.984 39.833 49.711 49.84 9.107 4.156 38.226 6.556 68.653 7.006 32.334 0.471 64.603-1.243 75.253-5.164 23.934-8.871 41.204-25.97 54.618-48.34 14.399-24.127 24.62-54.661 33.62-88.173l2.549-9.578 9.535-3.321c7.542-2.7 13.392-9.536 17.549-19.97 4.714-11.614 7.264-27.02 7.692-45.534a35.355 35.355 0 0 0-4.178-1.67 25.413 25.413 0 0 0-9.706-1.48l-16.67 1.072-3.108-16.435a213.844 213.844 0 0 0-17.334-53.354m0 0" p-id="2893"></path></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761545158610" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1618" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 625.777778l85.333333 85.333333-56.888889 85.333333 56.888889 142.222223 79.644445-290.133334c142.222222 34.133333 290.133333 102.4 290.133333 204.8v113.777778H56.888889v-113.777778c0-108.088889 147.911111-176.355556 290.133333-204.8l79.644445 290.133334 56.888889-142.222223-56.888889-85.333333L512 625.777778z m196.266667-261.688889c0 110.933333-85.333333 204.8-196.266667 204.8-107.235556 0-190.577778-87.722667-195.982222-193.763556L315.733333 364.088889h392.533334zM521.159111 56.888889c12.970667 0.170667 28.444444 0.967111 41.415111 4.949333l9.159111 3.584v136.533334h34.133334v-119.466667c65.024 32.483556 114.574222 103.708444 119.125333 184.149333l0.341333 12.117334h42.666667v42.666666h-512V278.755556h42.666667c0-81.066667 38.513778-154.453333 100.864-190.805334L409.6 82.488889v119.466667h42.666667v-136.533334c14.222222-7.111111 34.360889-8.305778 50.574222-8.533333h18.318222z" p-id="1619"></path></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212898988" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3212" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M105.9 959.3c5.5-79.7 16.6-187.3 41.1-286.1 5.1-25.5 23.2-46.9 23.8-47.7 2.3-15.1 187.9-15.2 187.9-15.2l30.7-2.9c-4.4 27.2-7.7 68.5 4 104 15.2 46.6 39.4 92.8 72.3 120.3l7-123.7H536l10.9 116c23-32.9 49.8-84.7 52.6-150.7 2.2-51.6 15.1-75.2 26.2-86.1 76.9 3.1 173.3 17.2 212 38.4 36.9 20.2 48.5 146.4 81.9 333.8H105.9v-0.1z" p-id="3213"></path><path d="M528.1 690.2h-39.7l-31.8-26.6 47.7-62 47.6 53.2-23.8 35.4z m210.7-526.8c-19-38-129.4-95-249.8-98.9-120.4-4-247.9 65.9-246.9 117 1 51 64.2 73 64.2 73-3.3 9.9-1.6 48.3-1.6 48.3s2.9 5.3 8.4 12.7C294.5 560.8 509 565 509 565c185.9-19.3 171.1-221.2 169.3-250.4-0.7-10.7-2-19.2-3.6-26.8 2.3-16.5-4.1-33.5-4.1-33.5 27.1-10.9 87.3-52.9 68.2-90.9z m-280.2-19.1c-0.2-1.6-0.5-3.2-0.5-4.9 0-20.7 18.2-37.5 40.7-37.5 22.5 0 40.7 16.8 40.7 37.5 0 2.6-0.3 5.2-0.9 7.7 12.1-1.1 25.2-2.1 37.1-2.3 0 0-33.9 15.8-53.2 25.1-2.9 1.9-6 3.4-9.4 4.6-0.2 0.1-0.5 0.3-0.8 0.4 0 0-1.1 0.3-2.9 0.6-3.4 0.9-7 1.5-10.7 1.5-0.7 0-1.4-0.2-2.1-0.2-9 0-20.4-1.8-29.6-9.5 0 0-13.9-9.5-45.3-22.6 0.1 0 16.8-2.1 36.9-0.4z m181.3 107.8H332v-36.4h307.9v36.4z" p-id="3214"></path></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212837698" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2731" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M855.7 781.7c-11.8-25.2-29.1-47.3-48.4-67.2-26.3-27-56.5-50-87.9-70.7-32.1-20.9-65.6-39.7-100.4-55.6 28.1-18.7 49.6-45.9 64.9-75.7 20.9-40.7 31.9-85.6 38.1-130.7 6.3-47.9 7.5-96.5 3.7-144.7 32.3-12.9 64.5-25.8 96.8-38.7 12-5 24.3-9.4 36.2-14.7-114.9-39.9-229.9-79.6-344.8-119.4-2-1-4 0.5-5.9 1-108.1 39.5-216.3 78.9-324.5 118.4 39.4 18.4 79 36.5 118.5 54.9-8.8 55.8-7.2 113.4 7.1 168.2 9 34.8 23 68.2 41.4 99.1 18.3 30.4 40.2 58.9 67 82.3-48 15.6-94.7 36.5-136.1 65.7-28.4 20-54.1 43.9-74.7 71.9-23 31.1-39.5 67.3-46.7 105.4-3.7 19.5-5.3 39.5-3.2 59.2 112.7 48 235.5 71.4 357.9 69.7-37.9-32.3-75.9-64.6-113.9-96.9 30.3-73.3 60.5-146.6 90.8-219.9 0.4-1.4 1.8-2.9 0.8-4.2-8.6-14.2-17.4-28.2-25.8-42.5 14.6 7.7 30.8 12.6 47.4 12.7 14.9 0.3 29.7-4.3 42-12.7-9.1 13.7-18.3 27.4-27.5 41.1 17.8 40.6 35.7 81.2 53.5 121.9 14.7 33.9 29.9 67.6 44.4 101.5-37.3 32.9-74.3 66.1-111.4 99.2 90.1-1.2 180-15.1 266.2-41.2 26.5-8.2 52.7-17.2 78-28.6 6.5-15.2 10.3-31.6 10.5-48.1 0.2-21-5.2-41.8-14-60.7z" p-id="2732"></path><path d="M216.4 380.8h11V392h21.5v-11.2h11v-26.3h-11V223.3h-21.5c-0.1 43.7 0 87.5 0 131.2h-11v26.3z" p-id="2733"></path></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1761212761688" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2247" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M636.478451 965.736274l10.285126-0.276102c-2.068945-0.25056-5.512321-0.662889-10.064973-1.188336a398.001259 398.001259 0 0 1-0.220153 1.464438zM319.897045 966.147387l15.81934-0.411113-0.214071-1.463222c-7.760064 0.893989-12.318797 1.463222-12.318797 1.463222l-3.286472 0.411113zM701.706773 232.246056c0-130.470023-177.671401-195.699562-177.671401-195.699562-25.16304-11.331153-50.139985-8.750141-75.875908 0 0 0-177.679915 57.274865-177.679915 195.699562 0 20.694314-37.558465 16.252347-49.98673 81.7142h531.217713c-22.614869-65.914321-50.003758-61.019886-50.003759-81.7142zM261.149228 691.016641C179.668559 732.292152 110.143011 786.116343 110.143011 839.449144v27.720942c0 57.176344 150.840799 106.349362 150.840799 106.349362l58.913235-7.372061-5.835859 0.152039s-48.965029-277.278751-52.911958-275.282785z" p-id="2248"></path><path d="M649.282557 868.865624c-18.205742 19.018238-43.489197 24.747062-67.840958 15.467826-25.402654-9.681835-41.173341-37.079238-36.441892-62.991526 2.217335-12.137567 7.629918-22.585677 16.273024-31.609489 36.338505-37.95255 72.662414-75.917262 108.994838-113.872244-20.740534-9.049353-41.343624-19.515709-62.035506-26.357458-5.480697-1.774598-39.908378-17.257019-18.379674-82.532777h-0.291915c50.639889-51.988778 86.252256-102.79895 86.252256-215.156888h-378.248369c0 113.53411 34.725677 163.877219 85.702483 215.795451 22.010362 57.542453-17.334863 78.908171-25.565639 81.90881-22.185511 8.003326-37.025721 17.671781-59.579774 27.987314-4.070993 1.864605 34.183202 264.833458 37.380883 286.769626 28.691558-3.307149 101.308969-11.070862 150.593888-11.070862 49.355464 0 121.929088 7.763713 150.601185 11.070862 1.212662-8.076305 7.314894-48.820288 14.275842-97.177161l-1.690672 1.768516zM780.85699 731.325185a466047.419516 466047.419516 0 0 1-98.887294 103.3694c-11.765376 66.245158-22.605138 130.427453-22.605138 130.427452l-12.599765 0.336918c1.455924 0.177581 2.246526 0.276103 2.246526 0.276103l62.66069 7.96562s150.381033-49.146259 150.381033-106.531808v-27.719726c-0.002433-37.857677-33.167582-75.24221-81.196052-108.123959z" p-id="2249"></path><path d="M843.295095 519.324624l-52.478951 54.703584 62.663122 57.647056 52.688157-55.292278c0.620319 1.188336 0.975481 1.74419 1.211445 2.343831 20.077644 51.086275-8.213748 109.894908-61.513708 127.951044-20.261307 6.862426-40.735469 7.679787-61.405457 2.17598-2.048268-0.54734-2.96415 0.256642-4.130592 1.474169A361833.588559 361833.588559 0 0 1 643.464941 853.40023c-13.557002 14.162724-32.385495 18.42711-50.519474 11.518465-18.916067-7.210291-30.659549-27.611474-27.138329-46.907031 1.652967-9.039623 5.682605-16.819147 12.118106-23.538049 45.458404-47.47748 90.870589-94.993883 136.371564-142.431225 1.828115-1.904743 2.042186-3.3631 1.162793-5.751934-19.576524-53.126028 12.26163-112.586604 68.353025-127.899958 18.585231-5.074449 37.137621-5.129183 55.717987-0.199475 1.046027 0.278535 2.072594 0.622751 3.764482 1.133601zM604.103904 841.024267c5.432045-0.211638 9.560204-4.471159 9.419112-9.706161-0.147174-5.362715-4.626847-9.473846-10.113626-9.281669-5.391906 0.189745-9.684268 4.732666-9.44587 10.004157 0.233532 5.146212 4.800779 9.190445 10.140384 8.983673z" p-id="2250"></path></svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,93 +0,0 @@
<template>
<el-tooltip :disabled="!isOverflow" effect="light" placement="top" :content="text">
<div
ref="containerRef"
class="multi-line-ellipsis"
:style="containerStyle"
@mouseenter="handleMouseEnter"
@mouseleave="handleMouseLeave"
>
<slot>
{{ text }}
</slot>
</div>
</el-tooltip>
</template>
<script lang="ts" setup>
import { computed, type HTMLAttributes, nextTick, onMounted, ref } from 'vue'
import { ElTooltip } from 'element-plus'
// props
interface Props {
text?: string
lines?: number
maxWidth?: string | number
}
const props = withDefaults(defineProps<Props>(), {
text: '',
lines: 3,
maxWidth: '100%',
})
const isOverflow = ref(false)
const containerRef = ref<HTMLElement | null>(null)
//
const containerStyle = computed(
() =>
({
maxWidth: props.maxWidth,
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
WebkitLineClamp: props.lines,
overflow: 'hidden',
textOverflow: 'ellipsis',
lineHeight: '1.5',
wordBreak: 'break-all',
}) as HTMLAttributes['style'],
)
//
const checkOverflow = (element: HTMLElement): boolean => {
// 使
if (props.lines === 1) {
return element.scrollWidth > element.clientWidth
}
// 使
else {
return element.scrollHeight > element.clientHeight
}
}
//
const handleMouseEnter = (event: MouseEvent) => {
const element = event.target as HTMLElement
console.log(checkOverflow(element))
isOverflow.value = checkOverflow(element)
}
//
const handleMouseLeave = () => {
isOverflow.value = false
}
//
onMounted(() => {
nextTick(() => {
if (containerRef.value) {
isOverflow.value = checkOverflow(containerRef.value)
}
})
})
</script>
<style scoped>
.multi-line-ellipsis {
cursor: default;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
}
</style>

View File

@ -1,35 +0,0 @@
<script setup lang="ts">
const props = withDefaults(
defineProps<{
iconClass: string
prefix?: string
color?: string
size?: string
}>(),
{
prefix: 'icon',
color: '',
size: '1em',
},
)
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`)
</script>
<template>
<svg aria-hidden="true" class="svg-icon" :style="`color:${props.color}`">
<use :xlink:href="symbolId" />
</svg>
</template>
<style scoped>
.svg-icon {
display: inline-block;
width: v-bind('props.size');
height: v-bind('props.size');
overflow: hidden;
vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致而span等标签的下边缘会和字体的基线对齐故需设置一个往下的偏移比例来纠正视觉上的未对齐效果 */
outline: none;
fill: currentcolor; /* 定义元素的颜色currentColor是一个变量这个变量的值就表示当前元素的color值如果当前元素未设置color值则从父元素继承 */
}
</style>

View File

@ -1,21 +0,0 @@
<script setup lang="ts">
import { useConfigStore } from '@/stores'
defineOptions({
name: 'AppHeader'
})
const configStore = useConfigStore()
</script>
<template>
<div class="bg-[var(--color-bg-secondary)] h-[60px] relative pl-[27px] font-[900] text-[24px]">
<div class="absolute left-0 h-full flex items-center">
<img class="w-[36.8px] h-[36.8px] rounded-full mr-[12px]" src="/logo.jpg" alt="logo" />
<span class="text-[#9E0000]">{{ configStore.config.title }}</span>{{ configStore.config.subTitle }}
</div>
<div class="text-center h-full w-full tracking-[8.5px] flex items-center justify-center">
{{ configStore.config.centerTitle }}
</div>
</div>
</template>

View File

@ -1,224 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { useAgentsStore, useConfigStore } from '@/stores'
import api from '@/api'
import { changeBriefs } from '@/utils/collaboration_Brief_FrontEnd.ts'
import { ElMessage } from 'element-plus'
const emit = defineEmits<{
(e: 'search-start'): void
(e: 'search', value: string): void
}>()
const agentsStore = useAgentsStore()
const configStore = useConfigStore()
const searchValue = ref('')
const triggerOnFocus = ref(true)
const isFocus = ref(false)
async function handleSearch() {
try {
triggerOnFocus.value = false
if (!searchValue.value) {
ElMessage.warning('请输入搜索内容')
return
}
emit('search-start')
agentsStore.resetAgent()
agentsStore.setAgentRawPlan({ loading: true })
const data = await api.generateBasePlan({
goal: searchValue.value,
inputs: [],
})
data['Collaboration Process'] = changeBriefs(data['Collaboration Process'])
agentsStore.setAgentRawPlan({ data })
emit('search', searchValue.value)
} finally {
triggerOnFocus.value = true
agentsStore.setAgentRawPlan({ loading: false })
}
}
const querySearch = (queryString: string, cb: (v: { value: string }[]) => void) => {
const results = queryString
? configStore.config.taskPromptWords.filter(createFilter(queryString))
: configStore.config.taskPromptWords
// call callback function to return suggestions
cb(results.map((item) => ({ value: item })))
}
const createFilter = (queryString: string) => {
return (restaurant: string) => {
return restaurant.toLowerCase().includes(queryString.toLowerCase())
}
}
const taskContainerRef = ref<HTMLDivElement | null>(null)
</script>
<template>
<el-tooltip
content="请先点击智能体库右侧的按钮上传智能体信息"
placement="top"
effect="light"
:disabled="agentsStore.agents.length > 0"
>
<div class="task-root-container">
<div class="task-container" ref="taskContainerRef" id="task-container">
<span class="text-[var(--color-text)] font-bold task-title">任务</span>
<el-autocomplete
v-model.trim="searchValue"
class="task-input"
size="large"
:rows="isFocus ? 3 : 1"
placeholder="请输入您的任务"
type="textarea"
:append-to="taskContainerRef"
:fetch-suggestions="querySearch"
@change="agentsStore.setSearchValue"
:disabled="!(agentsStore.agents.length > 0)"
:debounce="0"
:trigger-on-focus="triggerOnFocus"
@focus="isFocus = true"
@blur="isFocus = false"
@select="isFocus = false"
>
</el-autocomplete>
<el-button
class="task-button"
color="linear-gradient(to right, #00C7D2, #315AB4)"
size="large"
title="点击搜索任务"
circle
:loading="agentsStore.agentRawPlan.loading"
:disabled="!searchValue"
@click.stop="handleSearch"
>
<SvgIcon
v-if="!agentsStore.agentRawPlan.loading"
icon-class="paper-plane"
size="18px"
color="var(--color-text)"
/>
</el-button>
</div>
</div>
</el-tooltip>
</template>
<style scoped lang="scss">
.task-root-container {
height: 60px;
margin-bottom: 24px;
position: relative;
}
.task-container {
width: 40%;
margin: 0 auto;
border: 2px solid transparent;
$bg: var(--el-input-bg-color, var(--el-fill-color-blank));
background:
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
border-radius: 40px;
position: absolute;
left: 50%;
transform: translateX(-50%);
z-index: 998;
min-height: 100%;
overflow: hidden;
padding: 0 55px 0 47px;
:deep(.el-popper) {
position: static !important;
width: 100%;
min-width: 100%;
background: var(--color-bg-tertiary);
box-shadow: none;
border: none;
transition: height 0s ease-in-out;
border-top: 1px solid #494B51;
border-radius: 0;
li {
height: 45px;
box-sizing: border-box;
line-height: 45px;
font-size: 14px;
&:hover {
background: #1C1E25;
color: #00F3FF;
}
}
.el-popper__arrow {
display: none;
}
}
:deep(.el-autocomplete) {
min-height: 56px;
width: 100%;
.task-input {
height: 100%;
}
.el-textarea__inner {
border-radius: 0;
box-shadow: none;
font-size: 14px;
height: 100%;
line-height: 1.5;
padding: 18px 0;
resize: none;
&::placeholder {
line-height: 1.2;
font-size: 18px;
vertical-align: middle;
}
.el-icon.is-loading {
& + span {
display: none;
}
}
}
}
.task-title {
position: absolute;
top: 28px;
left: 10px;
z-index: 999;
transform: translateY(-50%);
}
.task-button {
background: linear-gradient(to right, #00c7d2, #315ab4);
border: none; //
position: absolute;
top: 28px;
right: 10px;
transform: translateY(-50%);
z-index: 999;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
}
.task-button.is-loading {
:deep(span) {
display: none !important;
}
}
}
</style>

View File

@ -1,141 +0,0 @@
<script setup lang="ts">
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { type Agent, useAgentsStore } from '@/stores'
import { v4 as uuidv4 } from 'uuid'
const porps = defineProps<{
agentList: Agent[]
}>()
const taskProcess = computed(() => {
const list = agentsStore.currentTask?.TaskProcess ?? []
return list.map((item) => ({
...item,
key: uuidv4(),
}))
})
const agentsStore = useAgentsStore()
</script>
<template>
<div
v-for="item in porps.agentList"
:key="item.Name"
class="user-item"
:class="agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'active-card' : ''"
>
<div class="flex items-center justify-between relative h-[41px]">
<div
class="w-[44px] h-[44px] rounded-full flex items-center justify-center flex-shrink-0 relative right-[2px] icon-container"
:style="{ background: getAgentMapIcon(item.Name).color }"
>
<svg-icon
:icon-class="getAgentMapIcon(item.Name).icon"
color="var(--color-text)"
size="24px"
/>
</div>
<div class="flex-1 text-[14px] flex flex-col items-end justify-end truncate ml-1">
<span
class="w-full truncate text-right"
:style="
agentsStore.currentTask?.AgentSelection?.includes(item.Name) ? 'color:#00F3FF' : ''
"
>{{ item.Name }}</span
>
<div
v-if="agentsStore.currentTask?.AgentSelection?.includes(item.Name)"
class="flex items-center gap-[7px] h-[8px] mr-1"
>
<!-- 小圆点 -->
<div
v-for="item1 in taskProcess.filter((i) => i.AgentName === item.Name)"
:key="item1.key"
class="w-[6px] h-[6px] rounded-full"
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
></div>
</div>
</div>
</div>
<!-- 职责信息只有当执行流程中有当前智能体并且鼠标移入时才显示 -->
<div class="duty-info">
<div class="w-full flex justify-center">
<div
class="rounded-[9px] bg-[var(--color-bg-quaternary)] text-[12px] py-0.5 px-5 text-center my-2"
>
当前职责
</div>
</div>
<div class="p-[8px] pt-0">
<div
v-for="(item1, index1) in taskProcess.filter((i) => i.AgentName === item.Name)"
:key="item1.key"
class="text-[12px]"
>
<div>
<div class="mx-1 inline-block h-[14px]">
<div
:style="{ background: getActionTypeDisplay(item1.ActionType)?.color }"
class="w-[6px] h-[6px] rounded-full mt-[7px]"
></div>
</div>
<span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }"
>{{ getActionTypeDisplay(item1.ActionType)?.name }}</span
>
<span>{{ item1.Description }}</span>
</div>
<!-- 分割线 -->
<div
v-if="index1 !== taskProcess.filter((i) => i.AgentName === item.Name).length - 1"
class="h-[1px] w-full bg-[#494B51] my-[8px]"
></div>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.user-item {
background: #1d222b;
border-radius: 40px;
padding-right: 12px;
cursor: pointer;
transition: all 0.25s ease;
color: #969696;
border: 2px solid transparent;
.duty-info {
transition: height 0.25s ease;
height: 0;
overflow: hidden;
}
& + .user-item {
margin-top: 8px;
}
&:hover {
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.2);
color: #b8b8b8;
}
}
.active-card {
background:
linear-gradient(#171B22, #171B22) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
&:hover {
border-radius: 20px;
.duty-info {
height: auto;
}
.icon-container {
bottom: 2px;
}
}
}
</style>

View File

@ -1,190 +0,0 @@
<script setup lang="ts">
import { ElNotification } from 'element-plus'
import { pick } from 'lodash'
import api from '@/api/index.ts'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { agentMapDuty } from '@/layout/components/config.ts'
import { type Agent, useAgentsStore } from '@/stores'
import { onMounted } from 'vue'
import { readConfig } from '@/utils/readJson.ts'
import AgentRepoList from './AgentRepoList.vue'
const agentsStore = useAgentsStore()
// agentsStore.agentsjson
onMounted(async () => {
if (!agentsStore.agents.length) {
const res = await readConfig<Agent[]>('agent.json')
agentsStore.setAgents(res)
}
await api.setAgents(agentsStore.agents.map((item) => pick(item, ['Name', 'Profile'])))
})
// agent
const fileInput = ref<HTMLInputElement>()
const triggerFileSelect = () => {
fileInput.value?.click()
}
const handleFileSelect = (event: Event) => {
const input = event.target as HTMLInputElement
if (input.files && input.files[0]) {
const file = input.files[0]
readFileContent(file)
}
}
const readFileContent = async (file: File) => {
const reader = new FileReader()
reader.onload = async (e) => {
if (!e.target?.result) {
return
}
try {
const json = JSON.parse(e.target.result?.toString?.() ?? '{}')
// JSON
if (Array.isArray(json)) {
const isValid = json.every(
(item) =>
typeof item.Name === 'string' &&
typeof item.Icon === 'string' &&
typeof item.Profile === 'string',
)
if (isValid) {
// JSON
agentsStore.setAgents(
json.map((item) => ({
Name: item.Name,
Icon: item.Icon.replace(/\.png$/, ''),
Profile: item.Profile,
Classification: item.Classification,
})),
)
await api.setAgents(json.map((item) => pick(item, ['Name', 'Profile'])))
} else {
ElNotification.error({
title: '错误',
message: 'JSON 格式错误',
})
}
} else {
console.error('JSON is not an array')
ElNotification.error({
title: '错误',
message: 'JSON 格式错误',
})
}
} catch (e) {
console.error(e)
}
}
reader.readAsText(file)
}
// currentTaskagent
const agentList = computed(() => {
const selected: Agent[] = []
const unselected: {
title: string
data: Agent[]
}[] = []
const obj: Record<string, Agent[]> = {}
if (!agentsStore.agents.length) {
return {
selected,
unselected,
}
}
for (const agent of agentsStore.agents) {
// if (agentsStore.currentTask?.AgentSelection?.includes(agent.Name)) {
// selected.push(agent)
// continue
// }
if (obj[agent.Classification]) {
obj[agent.Classification]!.push(agent)
} else {
const arr = [agent]
obj[agent.Classification] = arr
unselected.push({
title: agent.Classification,
data: arr,
})
}
}
return {
selected,
unselected: unselected,
}
})
</script>
<template>
<div class="agent-repo h-full flex flex-col" id="agent-repo">
<!-- 头部 -->
<div class="flex items-center justify-between">
<span class="text-[18px] font-bold">智能体库</span>
<!-- 上传文件 -->
<input type="file" accept=".json" @change="handleFileSelect" class="hidden" ref="fileInput" />
<div class="plus-button" @click="triggerFileSelect">
<svg-icon icon-class="plus" color="var(--color-text)" size="18px" />
</div>
</div>
<!-- 智能体列表 -->
<div class="pt-[18px] flex-1 overflow-y-auto relative">
<!-- 已选中的智能体 -->
<AgentRepoList :agent-list="agentList.selected" />
<!-- 为选择的智能体 -->
<div v-for="agent in agentList.unselected" :key="agent.title">
<p class="text-[12px] font-bold py-[8px]">{{ agent.title }}</p>
<AgentRepoList :agent-list="agent.data" />
</div>
</div>
<!-- 底部提示栏 -->
<div class="w-full grid grid-cols-3 gap-x-[10px] bg-[#1d222b] rounded-[20px] p-[8px] mt-[10px]">
<div
v-for="item in Object.values(agentMapDuty)"
:key="item.key"
class="flex items-center justify-center gap-x-1"
>
<span class="text-[12px]">{{ item.name }}</span>
<div class="w-[8px] h-[8px] rounded-full" :style="{ background: item.color }"></div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
.agent-repo {
padding: 0 8px;
.plus-button {
background: #1d2128;
width: 24px;
height: 24px;
padding: 0;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
&:hover {
background: #374151;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
}
}
}
#agent-repo {
:deep(.agent-repo-item-popover) {
padding: 0;
border-radius: 20px;
background: var(--color-bg-secondary);
}
}
</style>

View File

@ -1,105 +0,0 @@
<script setup lang="ts">
import type { IExecuteRawResponse } from '@/api'
import { computed } from 'vue'
import MarkdownIt from 'markdown-it'
import DOMPurify from 'dompurify'
import Iod from './Iod.vue'
const props = defineProps<{
executePlans: IExecuteRawResponse[]
nodeId?: string
actionId?: string
}>()
const md = new MarkdownIt({
html: true,
linkify: true,
typographer: true,
breaks: true,
})
function sanitize(str?: string) {
if (!str) {
return ''
}
const cleanStr = str
.replace(/\\n/g, '\n')
.replace(/\n\s*\d+\./g, '\n$&')
const html = md.render(cleanStr)
return html
// return DOMPurify.sanitize(html)
}
interface Data {
Description: string
Content: string
LogNodeType: string
}
const data = computed<Data | null>(() => {
for (const result of props.executePlans) {
if (result.NodeId === props.nodeId) {
// LogNodeType objectContent
if (result.LogNodeType === 'object') {
return {
Description: props.nodeId,
Content: sanitize(result.content),
LogNodeType: result.LogNodeType,
}
}
if (!result.ActionHistory) {
return null
}
for (const action of result.ActionHistory) {
if (action.ID === props.actionId) {
return {
Description: action.Description,
Content: sanitize(action.Action_Result),
LogNodeType: result.LogNodeType,
}
}
}
}
}
return null
})
</script>
<template>
<div v-if="data" class="card-item w-full pl-[56px] pr-[41px]">
<!-- 分割线 -->
<div v-if="data.LogNodeType !== 'object'" class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div
v-if="data.Description"
class="text-[16px] flex items-center gap-1 text-[var(--color-text-secondary)] mb-1"
>
{{ data.Description }}
<Iod v-if="data.LogNodeType !== 'object'"/>
</div>
<div class="rounded-[8px] p-[15px] text-[14px] bg-[var(--color-bg-quaternary)]">
<div
class="markdown-content max-h-[240px] overflow-y-auto max-w-full"
v-html="data.Content"
></div>
</div>
</div>
</template>
<style scoped lang="scss">
.card-item + .card-item {
margin-top: 10px;
}
.markdown-content {
:deep(code) {
display: block;
width: 100px;
max-width: 100%;
}
:deep(pre) {
overflow-x: auto;
max-width: 100%;
}
}
</style>

View File

@ -1,76 +0,0 @@
<script setup lang="ts">
import { readConfig } from '@/utils/readJson.ts'
import { onMounted } from 'vue'
interface Iod {
name: string
data_space: string
doId: string
fromRepo: string
}
const data = ref<Iod[]>([])
const displayIndex = ref(0)
const displayIod = computed(() => {
return data.value[displayIndex.value]!
})
onMounted(async () => {
const res = await readConfig<{ data: Iod[] }>('iodConfig.json')
data.value = res.data
})
function handleNext() {
if (displayIndex.value === data.value.length - 1) {
displayIndex.value = 0
} else {
displayIndex.value++
}
}
</script>
<template>
<el-popover trigger="hover" width="440">
<template #reference>
<div
class="rounded-full w-[20px] h-[20px] bg-[var(--color-bg-quaternary)] flex justify-center items-center cursor-pointer"
>
{{ data.length }}
</div>
</template>
<template #default v-if="data.length">
<div>
<div class="flex justify-between items-center p-2 pb-0 rounded-[8px] text-[16px] font-bold">
<span>数联网搜索结果</span>
<div class="flex items-center gap-3">
<div>{{ `${displayIndex + 1}/${data.length}` }}</div>
<el-button type="primary" size="small" @click="handleNext">下一个</el-button>
</div>
</div>
<!-- 分割线 -->
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div class="p-2 pt-0">
<div class="flex items-center w-full gap-3">
<div class="font-bold w-[75px] text-right flex-shrink-0">名称:</div>
<div class="text-[var(--color-text-secondary)] flex-1 break-words">{{ displayIod.name }}</div>
</div>
<div class="flex items-center w-full gap-3">
<div class="font-bold w-[75px] text-right flex-shrink-0">数据空间:</div>
<div class="text-[var(--color-text-secondary)] lex-1 break-words">{{ displayIod.data_space }}</div>
</div>
<div class="flex items-center w-full gap-3">
<div class="font-bold w-[75px] text-right flex-shrink-0">DOID:</div>
<div class="text-[var(--color-text-secondary)] lex-1 break-words">{{ displayIod.doId }}</div>
</div>
<div class="flex items-center w-full gap-3">
<div class="font-bold w-[75px] text-right flex-shrink-0">来源仓库:</div>
<div class="text-[var(--color-text-secondary)] flex-1 break-words break-al">{{ displayIod.fromRepo }}</div>
</div>
</div>
</div>
</template>
</el-popover>
</template>
<style scoped lang="scss"></style>

View File

@ -1,400 +0,0 @@
<script setup lang="ts">
import { computed, onUnmounted, ref } from 'vue'
import { throttle } from 'lodash'
import { AnchorLocations, BezierConnector } from '@jsplumb/browser-ui'
import SvgIcon from '@/components/SvgIcon/index.vue'
import { getActionTypeDisplay, getAgentMapIcon } from '@/layout/components/config.ts'
import { type ConnectArg, Jsplumb } from '@/layout/components/Main/TaskTemplate/utils.ts'
import variables from '@/styles/variables.module.scss'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import api from '@/api'
import ExecutePlan from './ExecutePlan.vue'
const emit = defineEmits<{
(e: 'refreshLine'): void
(el: 'setCurrentTask', task: IRawStepTask): void
}>()
const agentsStore = useAgentsStore()
const collaborationProcess = computed(() => {
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})
const jsplumb = new Jsplumb('task-results-main', {
connector: {
type: BezierConnector.type,
options: { curviness: 30, stub: 10 },
},
})
// 线
let timer: number
function handleCollapse() {
if (timer) {
clearInterval(timer)
}
timer = setInterval(() => {
jsplumb.repaintEverything()
emit('refreshLine')
}, 1)
//
const timer1 = setTimeout(() => {
clearInterval(timer)
}, 3000)
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
if (timer1) {
clearInterval(timer1)
}
})
}
// 线
function createInternalLine(id?: string) {
const arr: ConnectArg[] = []
jsplumb.reset()
collaborationProcess.value.forEach((item) => {
// 线
arr.push({
sourceId: `task-results-${item.Id}-0`,
targetId: `task-results-${item.Id}-1`,
anchor: [AnchorLocations.Left, AnchorLocations.Left],
})
collaborationProcess.value.forEach((jitem) => {
// 线
if (item.InputObject_List!.includes(jitem.OutputObject ?? '')) {
arr.push({
sourceId: `task-results-${jitem.Id}-1`,
targetId: `task-results-${item.Id}-0`,
anchor: [AnchorLocations.Left, AnchorLocations.Left],
config: {
type: 'output',
},
})
}
// InputObject线
jitem.TaskProcess.forEach((i) => {
if (i.ImportantInput?.includes(`InputObject:${item.OutputObject}`)) {
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
const sourceId = `task-results-${item.Id}-1`
const targetId = `task-results-${jitem.Id}-0-${i.ID}`
arr.push({
sourceId,
targetId,
anchor: [AnchorLocations.Right, AnchorLocations.Right],
config: {
stops: [
[0, color],
[1, color],
],
transparent: targetId !== id,
},
})
}
})
})
// TaskProcess线
item.TaskProcess?.forEach((i) => {
if (!i.ImportantInput?.length) {
return
}
item.TaskProcess?.forEach((i2) => {
if (i.ImportantInput.includes(`ActionResult:${i2.ID}`)) {
const color = getActionTypeDisplay(i.ActionType)?.color ?? ''
const sourceId = `task-results-${item.Id}-0-${i2.ID}`
const targetId = `task-results-${item.Id}-0-${i.ID}`
arr.push({
sourceId,
targetId,
anchor: [AnchorLocations.Right, AnchorLocations.Right],
config: {
stops: [
[0, color],
[1, color],
],
transparent: targetId !== id,
},
})
}
})
})
})
jsplumb.connects(arr)
jsplumb.repaintEverything()
}
const loading = ref(false)
async function handleRun() {
try {
loading.value = true
const d = await api.executePlan(agentsStore.agentRawPlan.data!)
agentsStore.setExecutePlan(d)
} finally {
loading.value = false
}
}
//
const isScrolling = ref(false)
let scrollTimer: number
//
function handleScroll() {
isScrolling.value = true
emit('refreshLine')
//
if (scrollTimer) {
clearTimeout(scrollTimer)
}
jsplumb.repaintEverything()
//
scrollTimer = setTimeout(() => {
isScrolling.value = false
}, 300)
}
//
const handleMouseEnter = throttle((id) => {
if (!isScrolling.value) {
createInternalLine(id)
}
}, 100)
const handleMouseLeave = throttle(() => {
if (!isScrolling.value) {
createInternalLine()
}
}, 100)
function clear() {
jsplumb.reset()
}
defineExpose({
createInternalLine,
clear,
})
</script>
<template>
<div class="h-full flex flex-col relative" id="task-results">
<!-- 标题与执行按钮 -->
<div class="text-[18px] font-bold mb-[18px] flex justify-between items-center px-[20px]">
<span>执行结果</span>
<div class="flex items-center gap-[14px]">
<el-button circle :color="variables.tertiary" disabled title="点击刷新">
<svg-icon icon-class="refresh" />
</el-button>
<el-popover :disabled="Boolean(agentsStore.agentRawPlan.data)" title="请先输入要执行的任务">
<template #reference>
<el-button
circle
:color="variables.tertiary"
title="点击运行"
:disabled="!agentsStore.agentRawPlan.data"
@click="handleRun"
>
<svg-icon icon-class="action" />
</el-button>
</template>
</el-popover>
</div>
</div>
<!-- 内容 -->
<div
v-loading="agentsStore.agentRawPlan.loading"
class="flex-1 overflow-auto relative"
@scroll="handleScroll"
>
<div id="task-results-main" class="px-[40px] relative">
<div v-for="item in collaborationProcess" :key="item.Id" class="card-item">
<el-card
class="card-item w-full relative"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
shadow="hover"
:id="`task-results-${item.Id}-0`"
@click="emit('setCurrentTask', item)"
>
<div class="text-[18px] mb-[15px]">{{ item.StepName }}</div>
<!-- 折叠面板 -->
<el-collapse @change="handleCollapse">
<el-collapse-item
v-for="item1 in item.TaskProcess"
:key="`task-results-${item.Id}-${item1.ID}`"
:name="`task-results-${item.Id}-${item1.ID}`"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
@mouseenter="() => handleMouseEnter(`task-results-${item.Id}-0-${item1.ID}`)"
@mouseleave="handleMouseLeave"
>
<template v-if="loading" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<template #title>
<div class="flex items-center gap-[15px]">
<!-- 右侧链接点 -->
<div
class="absolute right-0 top-1/2 transform -translate-y-1/2"
:id="`task-results-${item.Id}-0-${item1.ID}`"
></div>
<div
class="w-[41px] h-[41px] rounded-full flex items-center justify-center"
:style="{ background: getAgentMapIcon(item1.AgentName).color }"
>
<svg-icon
:icon-class="getAgentMapIcon(item1.AgentName).icon"
color="var(--color-text)"
size="24px"
/>
</div>
<div class="text-[16px]">
<span>{{ item1.AgentName }}:&nbsp; &nbsp;</span>
<span :style="{ color: getActionTypeDisplay(item1.ActionType)?.color }">
{{ getActionTypeDisplay(item1.ActionType)?.name }}
</span>
</div>
</div>
</template>
<ExecutePlan
:action-id="item1.ID"
:node-id="item.StepName"
:execute-plans="agentsStore.executePlan"
/>
</el-collapse-item>
</el-collapse>
</el-card>
<el-card
class="card-item w-full relative output-object-card"
shadow="hover"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
:id="`task-results-${item.Id}-1`"
@click="emit('setCurrentTask', item)"
>
<!-- <div class="text-[18px]">{{ item.OutputObject }}</div>-->
<el-collapse @change="handleCollapse">
<el-collapse-item
class="output-object"
:disabled="Boolean(!agentsStore.executePlan.length || loading)"
>
<template v-if="loading" #icon>
<SvgIcon icon-class="loading" size="20px" class="animate-spin" />
</template>
<template v-else-if="!agentsStore.executePlan.length" #icon>
<span></span>
</template>
<template #title>
<div class="text-[18px]">{{ item.OutputObject }}</div>
</template>
<ExecutePlan :node-id="item.OutputObject" :execute-plans="agentsStore.executePlan" />
</el-collapse-item>
</el-collapse>
</el-card>
</div>
</div>
</div>
</div>
</template>
<style scoped lang="scss">
#task-results {
:deep(.el-collapse) {
border: none;
border-radius: 20px;
.el-collapse-item + .el-collapse-item {
margin-top: 10px;
}
.el-collapse-item__header {
border: none;
background: var(--color-bg-secondary);
min-height: 41px;
line-height: 41px;
border-radius: 20px;
transition: border-radius 1ms;
position: relative;
.el-collapse-item__title {
background: var(--color-bg-secondary);
border-radius: 20px;
}
.el-icon {
font-size: 20px;
font-weight: bold;
}
&.is-active {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.output-object {
.el-collapse-item__header {
background: none;
.el-collapse-item__title {
background: none;
}
}
.el-collapse-item__wrap {
background: none;
.card-item {
background: var(--color-bg-secondary);
padding: 25px;
padding-top: 10px;
border-radius: 7px;
}
}
}
.el-collapse-item__wrap {
border: none;
background: var(--color-bg-secondary);
border-bottom-left-radius: 20px;
border-bottom-right-radius: 20px;
}
}
:deep(.el-card) {
.el-card__body {
padding-right: 40px;
}
}
.output-object-card {
:deep(.el-card__body) {
padding-top: 0;
padding-bottom: 0;
padding-right: 0;
}
}
.active-card {
background:
linear-gradient(var(--color-bg-tertiary), var(--color-bg-tertiary)) padding-box,
linear-gradient(to right, #00c8d2, #315ab4) border-box;
}
.card-item + .card-item {
margin-top: 10px;
}
}
</style>

View File

@ -1,31 +0,0 @@
<template>
<div class="absolute inset-0 flex items-start gap-[14%]">
<!-- 左侧元素 -->
<div class="flex-1 relative h-full flex justify-center">
<!-- 背景那一根线 -->
<div
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
>
<!-- 线底部的小圆球 -->
<div
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
></div>
</div>
</div>
<!-- 右侧元素 -->
<div class="flex-1 relative h-full flex justify-center">
<!-- 背景那一根线 -->
<div
class="h-full bg-[var(--color-bg-tertiary)] w-[5px]"
>
<!-- 线底部的小圆球 -->
<div
class="absolute bottom-0 left-1/2 transform -translate-x-1/2 bg-[var(--color-bg-tertiary)] w-[15px] h-[15px] rounded-full"
></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
</script>

View File

@ -1,204 +0,0 @@
<script setup lang="ts">
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 { computed } from 'vue'
import { AnchorLocations } from '@jsplumb/browser-ui'
import MultiLineTooltip from '@/components/MultiLineTooltip/index.vue'
import Bg from './Bg.vue'
const emit = defineEmits<{
(el: 'resetAgentRepoLine'): void
(el: 'setCurrentTask', task: IRawStepTask): void
}>()
const jsplumb = new Jsplumb('task-syllabus')
const handleScroll = () => {
emit('resetAgentRepoLine')
}
const agentsStore = useAgentsStore()
const collaborationProcess = computed(() => {
return agentsStore.agentRawPlan.data?.['Collaboration Process'] ?? []
})
function handleCurrentTask(task: IRawStepTask, transparent: boolean): ConnectArg[] {
// 线
const arr: ConnectArg[] = [
{
sourceId: `task-syllabus-flow-${task.Id}`,
targetId: `task-syllabus-output-object-${task.Id}`,
anchor: [AnchorLocations.Right, AnchorLocations.Left],
config: {
transparent,
},
},
]
// 线
task.InputObject_List?.forEach((item) => {
const id = collaborationProcess.value.find((i) => i.OutputObject === item)?.Id
if (id) {
arr.push({
sourceId: `task-syllabus-output-object-${id}`,
targetId: `task-syllabus-flow-${task.Id}`,
anchor: [AnchorLocations.Left, AnchorLocations.Right],
config: {
type: 'output',
transparent,
},
})
}
})
return arr
}
function changeTask(task?: IRawStepTask, isEmit?: boolean) {
jsplumb.reset()
const arr: ConnectArg[] = []
agentsStore.agentRawPlan.data?.['Collaboration Process']?.forEach((item) => {
arr.push(...handleCurrentTask(item, item.Id !== task?.Id))
})
jsplumb.connects(arr)
if (isEmit && task) {
emit('setCurrentTask', task)
}
}
function clear() {
jsplumb.reset()
}
defineExpose({
changeTask,
clear,
})
</script>
<template>
<div class="h-full flex flex-col">
<div class="text-[18px] font-bold mb-[18px]">任务大纲</div>
<div
v-loading="agentsStore.agentRawPlan.loading"
class="flex-1 w-full overflow-y-auto relative"
@scroll="handleScroll"
>
<div v-show="collaborationProcess.length > 0" class="w-full relative min-h-full" id="task-syllabus">
<Bg />
<div class="w-full flex items-center gap-[14%] mb-[35px]">
<div class="flex-1 flex justify-center">
<div
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
>
流程
</div>
</div>
<div class="flex-1 flex justify-center">
<div
class="card-item w-[45%] h-[41px] flex justify-center relative z-99 items-center rounded-[20px] bg-[var(--color-bg-tertiary)]"
>
产物
</div>
</div>
</div>
<div
v-for="item in collaborationProcess"
:key="item.Id"
class="card-item w-full flex items-center gap-[14%]"
>
<!-- 流程卡片 -->
<el-card
class="w-[43%] overflow-y-auto relative z-99 task-syllabus-flow-card"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
shadow="hover"
:id="`task-syllabus-flow-${item.Id}`"
@click="changeTask(item, true)"
>
<MultiLineTooltip placement="right" :text="item.StepName" :lines="2">
<div class="text-[18px] font-bold text-center">
{{ item.StepName }}
</div>
</MultiLineTooltip>
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<MultiLineTooltip placement="right" :text="item.StepName" :lines="3">
<div
class="text-[14px] text-[var(--color-text-secondary)]"
:title="item.TaskContent"
>
{{ item.TaskContent }}
</div>
</MultiLineTooltip>
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div
class="flex items-center gap-2 overflow-y-auto flex-wrap relative w-full max-h-[72px]"
>
<!-- 连接到智能体库的连接点 -->
<div
class="absolute left-[-10px] top-1/2 transform -translate-y-1/2"
:id="`task-syllabus-flow-agents-${item.Id}`"
></div>
<el-tooltip
v-for="agentSelection in item.AgentSelection"
:key="agentSelection"
effect="light"
placement="right"
>
<template #content>
<div class="w-[150px]">
<div class="text-[18px] font-bold">{{ agentSelection }}</div>
<div class="h-[1px] w-full bg-[#494B51] my-[8px]"></div>
<div>
{{
item.TaskProcess.find((i) => i.AgentName === agentSelection)?.Description
}}
</div>
</div>
</template>
<div
class="w-[31px] h-[31px] rounded-full flex items-center justify-center"
:style="{ background: getAgentMapIcon(agentSelection).color }"
>
<svg-icon
:icon-class="getAgentMapIcon(agentSelection).icon"
color="var(--color-text)"
size="24px"
/>
</div>
</el-tooltip>
</div>
</el-card>
<!-- 产物卡片 -->
<el-card
class="w-[43%] relative"
shadow="hover"
:class="agentsStore.currentTask?.StepName === item.StepName ? 'active-card' : ''"
:id="`task-syllabus-output-object-${item.Id}`"
>
<div class="text-[18px] font-bold text-center">{{ item.OutputObject }}</div>
</el-card>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.task-syllabus-flow-card {
:deep(.el-card__body) {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
overflow: auto;
}
}
</style>

View File

@ -1,122 +0,0 @@
<script setup lang="ts">
import AgentRepo from './AgentRepo/index.vue'
import TaskSyllabus from './TaskSyllabus/index.vue'
import TaskResult from './TaskResult/index.vue'
import { Jsplumb } from './utils.ts'
import { type IRawStepTask, useAgentsStore } from '@/stores'
import { BezierConnector } from '@jsplumb/browser-ui'
const agentsStore = useAgentsStore()
//
const agentRepoJsplumb = new Jsplumb('task-template', {
connector: {
type: BezierConnector.type,
options: {
curviness: 30, // 线
stub: 20, //
alwaysRespectStubs: true,
},
},
})
//
const taskSyllabusRef = ref<{
changeTask: (task?: IRawStepTask, isEmit?: boolean) => void
clear: () => void
}>()
//
const taskResultRef = ref<{
createInternalLine: () => void
clear: () => void
}>()
const taskResultJsplumb = new Jsplumb('task-template')
function scrollToElementTop(elementId: string) {
const element = document.getElementById(elementId);
if (element) {
element.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
}
function handleTaskSyllabusCurrentTask(task: IRawStepTask) {
scrollToElementTop(`task-results-${task.Id}-0`)
agentsStore.setCurrentTask(task)
}
function handleTaskResultCurrentTask(task: IRawStepTask) {
scrollToElementTop(`task-syllabus-flow-${task.Id}`)
agentsStore.setCurrentTask(task)
// 线
taskSyllabusRef.value?.changeTask(task, false)
}
function changeTask() {
taskResultRef.value?.createInternalLine()
taskSyllabusRef.value?.changeTask()
}
function resetAgentRepoLine() {
agentRepoJsplumb.repaintEverything()
taskResultJsplumb.repaintEverything()
}
function clear() {
taskSyllabusRef.value?.clear()
taskResultRef.value?.clear()
agentRepoJsplumb.repaintEverything()
taskResultJsplumb.repaintEverything()
}
defineExpose({
changeTask,
resetAgentRepoLine,
clear,
})
</script>
<template>
<div
class="task-template flex gap-6 items-center h-[calc(100%-84px)] relative overflow-hidden"
id="task-template"
>
<!-- 智能体库 -->
<div class="w-[9.5%] min-w-[179px] h-full relative flex-shrink-0">
<AgentRepo @resetAgentRepoLine="agentRepoJsplumb.repaintEverything" />
</div>
<!-- 任务大纲 -->
<div class="w-[35.5%] min-w-[600px] h-full px-[20px] flex-shrink-0">
<TaskSyllabus
ref="taskSyllabusRef"
@resetAgentRepoLine="resetAgentRepoLine"
@set-current-task="handleTaskSyllabusCurrentTask"
/>
</div>
<!-- 执行结果 -->
<div class="flex-1 h-full">
<TaskResult
ref="taskResultRef"
@refresh-line="taskResultJsplumb.repaintEverything"
@set-current-task="handleTaskResultCurrentTask"
/>
</div>
</div>
</template>
<style scoped lang="scss">
.task-template {
& > div {
box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.8);
border-radius: 24px;
border: 1px solid #414752;
background: var(--color-bg-quinary);
padding-top: 20px;
padding-bottom: 20px;
}
}
</style>

View File

@ -1,169 +0,0 @@
import type {
AnchorSpec,
ConnectorSpec,
ConnectParams,
EndpointOptions,
JsPlumbInstance,
} from '@jsplumb/browser-ui'
import { BezierConnector, DotEndpoint, newInstance } from '@jsplumb/browser-ui'
export interface JsplumbConfig {
connector?: ConnectorSpec
type?: 'input' | 'output'
stops?: [[number, string], [number, string]]
// 连接线条是否变透明一些
transparent?: boolean
}
export interface ConnectArg {
sourceId: string
targetId: string
anchor: AnchorSpec
config?: JsplumbConfig
}
const defaultConfig: JsplumbConfig = {
connector: {
type: BezierConnector.type,
options: {
curviness: 70,
stub: 10,
},
},
type: 'input',
}
export class Jsplumb {
instance!: JsPlumbInstance
containerId: string
config: JsplumbConfig
constructor(eleId: string, config = {} as JsplumbConfig) {
this.containerId = eleId
this.config = { ...defaultConfig, ...config }
onMounted(() => {
this.init()
})
}
init = () => {
if (this.instance) {
return
}
this.instance = newInstance({
container: document.querySelector(`#${this.containerId}`)!, // 或指定共同的父容器
})
}
getStops = (type?: 'input' | 'output'): [[number, string], [number, string]] => {
if (type === 'input') {
return [
[0, '#FF6161'],
[1, '#D76976'],
]
}
return [
[0, '#0093EB'],
[1, '#00D2D1'],
]
}
_connect = (
sourceId: string,
targetId: string,
anchor: AnchorSpec,
_config = {} as JsplumbConfig,
) => {
const config = {
...defaultConfig,
...this.config,
..._config,
}
this.init()
// 连接两个元素
const sourceElement = document.querySelector(`#${sourceId}`)
const targetElement = document.querySelector(`#${targetId}`)
const stops = _config.stops ?? this.getStops(config.type)
// 如果config.transparent为true则将stops都加一些透明度
if (config.transparent) {
stops[0][1] = stops[0][1] + '30'
stops[1][1] = stops[1][1] + '30'
}
if (targetElement && sourceElement) {
this.instance.connect({
source: sourceElement,
target: targetElement,
connector: config.connector,
anchor: anchor,
paintStyle: {
stroke: stops[0][1],
strokeWidth: 2.5,
dashstyle: '0',
zIndex: 100,
opacity: 0.9,
gradient: {
stops: stops,
type: 'linear',
},
},
sourceEndpointStyle: { fill: stops[0][1] },
endpoint: {
type: DotEndpoint.type,
options: { radius: 5 },
},
cssClass: `jtk-connector-${config.type}`
} as unknown as ConnectParams<unknown>)
// 为源元素添加端点
this.instance.addEndpoint(sourceElement, {
anchor: (anchor as [AnchorSpec, AnchorSpec])[0],
paintStyle: { fill: stops[0][1], zIndex: 100 }, // source端点颜色
} as unknown as EndpointOptions)
// 为目标元素添加端点
this.instance.addEndpoint(targetElement, {
anchor: (anchor as [AnchorSpec, AnchorSpec])[1],
paintStyle: { fill: stops[1][1], zIndex: 100 }, // target端点颜色
} as unknown as EndpointOptions)
}
}
connect = (
sourceId: string,
targetId: string,
anchor: AnchorSpec,
config = {} as JsplumbConfig,
) => {
this._connect(sourceId, targetId, anchor, config)
}
connects = (args: ConnectArg[]) => {
this.instance.batch(() => {
args.forEach(({ sourceId, targetId, anchor, config }) => {
this._connect(sourceId, targetId, anchor, config)
})
})
}
repaintEverything = () => {
// 重新验证元素位置
const container = document.querySelector(`#${this.containerId}`)
if (container) {
const elements = container.querySelectorAll('[id^="task-results-"]')
elements.forEach((element) => {
this.instance.revalidate(element)
})
}
this.instance.repaintEverything()
}
reset = () => {
this.instance.deleteEveryConnection()
const allEndpoints = this.instance.selectEndpoints()
allEndpoints.each((endpoint) => {
this.instance.deleteEndpoint(endpoint)
})
}
}

View File

@ -1,22 +0,0 @@
<script setup lang="ts">
import Task from './Task.vue'
import TaskTemplate from './TaskTemplate/index.vue'
import { nextTick } from 'vue'
const taskTemplateRef = ref<{ changeTask: () => void, clear: () => void }>()
function handleSearch() {
nextTick(() => {
taskTemplateRef.value?.changeTask()
})
}
</script>
<template>
<div class="p-[24px] h-[calc(100%-60px)]">
<Task @search="handleSearch" @search-start="taskTemplateRef?.clear" />
<TaskTemplate ref="taskTemplateRef" />
</div>
</template>
<style scoped lang="scss"></style>

View File

@ -1,145 +0,0 @@
export interface AgentMapIcon {
name: string
icon: string
color: string
}
// "肾脏病学家": {
// name: "肾脏病学家",
// icon: "doctor",
// color: "#00A2D2",
// },
export const agentMapIcon = new Map<string, AgentMapIcon>()
agentMapIcon.set("船舶设计师", {
name: '船舶设计师',
icon: 'shejishi',
color: '#65AE00',
})
agentMapIcon.set("防护工程专家", {
name: '防护工程专家',
icon: 'engineer',
color: '#B06CFE',
})
agentMapIcon.set("病理生理学家", {
name: '病理生理学家',
icon: 'doctor',
color: '#00A2D2',
})
agentMapIcon.set("药物化学家", {
name: '药物化学家',
icon: 'specialist',
color: '#FF7914',
})
agentMapIcon.set("制剂工程师", {
name: '制剂工程师',
icon: 'medical',
color: '#65AE00',
})
agentMapIcon.set("监管事务专家", {
name: '监管事务专家',
icon: 'researcher',
color: '#0064C4',
})
agentMapIcon.set("物理学家", {
name: '物理学家',
icon: 'specialist',
color: '#B06CFE',
})
agentMapIcon.set("实验材料学家", {
name: '实验材料学家',
icon: 'researcher',
color: '#C01E6A',
})
agentMapIcon.set("计算模拟专家", {
name: '计算模拟专家',
icon: 'researcher',
color: '#FF7914',
})
agentMapIcon.set("腐蚀机理研究员", {
name: '腐蚀机理研究员',
icon: 'specialist',
color: '#00C8D2',
})
agentMapIcon.set("先进材料研发员", {
name: '先进材料研发员',
icon: 'engineer',
color: '#00C8D2',
})
agentMapIcon.set("肾脏病学家", {
name: '肾脏病学家',
icon: 'doctor',
color: '#00A2D2',
})
agentMapIcon.set("临床研究协调员", {
name: '临床研究协调员',
icon: 'renyuan',
color: '#FF7914',
})
agentMapIcon.set("中医药专家", {
name: '中医药专家',
icon: 'medical',
color: '#00C8D2',
})
agentMapIcon.set("药物安全专家", {
name: '药物安全专家',
icon: 'medical',
color: '#65AE00',
})
agentMapIcon.set("二维材料科学家", {
name: '二维材料科学家',
icon: 'shejishi',
color: '#EB6363',
})
agentMapIcon.set("光电物理学家", {
name: '光电物理学家',
icon: 'specialist',
color: '#079EFF',
})
agentMapIcon.set("机器学习专家", {
name: '机器学习专家',
icon: 'researcher',
color: '#8700AE',
})
agentMapIcon.set("流体动力学专家", {
name: '流体动力学专家',
icon: 'specialist',
color: '#EB6363',
})
export function getAgentMapIcon(agentName: string):AgentMapIcon {
return agentMapIcon.get(agentName) ?? agentMapIcon.get('监管事务专家')!
}
export interface AgentMapDuty {
name: string
key: string
color: string
}
// 职责映射
// 提议 评审 改进 总结
export const agentMapDuty: Record<string, AgentMapDuty> = {
Propose: {
name: '提议',
key: 'propose',
color: '#06A3FF',
},
Critique: {
name: '评审',
key: 'review',
color: '#FFFC08',
},
Improve: {
name: '改进',
key: 'improve',
color: '#BF65FF',
},
Finalize: {
name: '总结',
key: 'summary',
color: '#FFA236',
},
}
export function getActionTypeDisplay(type: string) {
return agentMapDuty[type]
}

View File

@ -1,11 +0,0 @@
<script setup lang="ts">
import Header from './components/Header.vue'
import Main from './components/Main/index.vue'
</script>
<template>
<div class="h-full">
<Header />
<Main />
</div>
</template>

View File

@ -1,23 +0,0 @@
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './styles/index.scss'
import './styles/tailwindcss.css'
import 'element-plus/theme-chalk/dark/css-vars.css'
import 'virtual:svg-icons-register'
import { initService } from '@/utils/request.ts'
import { setupStore, useConfigStore } from '@/stores'
async function init() {
const app = createApp(App)
setupStore(app)
initService()
const configStore = useConfigStore()
await configStore.initConfig()
document.title = configStore.config.centerTitle
app.use(router)
app.mount('#app')
}
void init()

View File

@ -1,8 +0,0 @@
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [],
})
export default router

Some files were not shown because too many files have changed in this diff Show More