Files
AgentCoord/frontend/src/api/index.ts

745 lines
21 KiB
TypeScript
Raw Normal View History

import websocket from '@/utils/websocket'
2026-01-09 13:54:32 +08:00
import type { Agent, IApiStepTask, IRawPlanResponse, IRawStepTask } from '@/stores'
2026-01-30 15:27:00 +08:00
import { withRetry } from '@/utils/retry'
export interface ActionHistory {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
Action_Result: string
}
2026-01-30 15:27:00 +08:00
export interface BranchAction {
ID: string
ActionType: string
AgentName: string
Description: string
ImportantInput: string[]
}
export type IExecuteRawResponse = {
LogNodeType: string
NodeId: string
InputName_List?: string[] | null
OutputName?: string
content?: string
ActionHistory: ActionHistory[]
}
2026-01-21 15:18:15 +08:00
/**
* WebSocket
2026-01-21 15:18:15 +08:00
*/
export type StreamingEvent =
| {
type: 'step_start'
step_index: number
total_steps: number
step_name: string
task_description?: string
}
| {
type: 'action_complete'
step_index: number
step_name: string
action_index: number
total_actions: number
completed_actions: number
action_result: ActionHistory
batch_info?: {
batch_index: number
batch_size: number
is_parallel: boolean
}
}
| {
type: 'step_complete'
step_index: number
step_name: string
step_log_node: any
object_log_node: any
}
| {
type: 'execution_complete'
total_steps: number
}
| {
type: 'error'
message: string
}
2025-12-31 19:04:58 +08:00
export interface IFillAgentSelectionRequest {
goal: string
stepTask: IApiStepTask
agents: string[]
}
class Api {
// 提取响应数据的公共方法
private extractResponse<T>(raw: any): T {
return (raw.data || raw) as T
}
// 颜色向量转 HSL 字符串
private vec2Hsl = (color: number[]): string => {
const [h, s, l] = color
return `hsl(${h}, ${s}%, ${l}%)`
}
setAgents = (data: Pick<Agent, 'Name' | 'Profile' | 'apiUrl' | 'apiKey' | 'apiModel'>[]) =>
websocket.send('set_agents', data)
getAgents = (user_id: string = 'default_user') => websocket.send('get_agents', { user_id })
generateBasePlan = (data: {
goal: string
inputs: string[]
apiUrl?: string
apiKey?: string
apiModel?: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) =>
websocket.send(
'generate_base_plan',
{
'General Goal': data.goal,
'Initial Input Object': data.inputs,
apiUrl: data.apiUrl,
apiKey: data.apiKey,
apiModel: data.apiModel,
},
undefined,
data.onProgress,
)
2025-12-31 19:04:58 +08:00
2026-01-21 15:18:15 +08:00
/**
*
* + +
2026-01-21 15:18:15 +08:00
*/
executePlanOptimized = (
plan: IRawPlanResponse,
onMessage: (event: StreamingEvent) => void,
onError?: (error: Error) => void,
onComplete?: () => void,
_useWebSocket?: boolean,
existingKeyObjects?: Record<string, any>,
enableDynamic?: boolean,
onExecutionStarted?: (executionId: string) => void,
executionId?: string,
restartFromStepIndex?: number,
rehearsalLog?: any[],
TaskID?: string,
2026-01-21 15:18:15 +08:00
) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
void _useWebSocket // 保留参数位置以保持兼容性
2026-01-21 15:18:15 +08:00
const data = {
RehearsalLog: rehearsalLog || [], // 使用传递的 RehearsalLog
2026-01-21 15:18:15 +08:00
num_StepToRun: null,
existingKeyObjects: existingKeyObjects || {},
enable_dynamic: enableDynamic || false,
execution_id: executionId || null,
2026-01-30 15:27:00 +08:00
restart_from_step_index: restartFromStepIndex ?? null, // 新增:传递重新执行索引
task_id: TaskID || null, // 任务唯一标识,用于写入数据库
2026-01-21 15:18:15 +08:00
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,
})),
})),
},
}
websocket.subscribe(
'execute_plan_optimized',
data,
// onProgress
(progressData) => {
try {
let event: StreamingEvent
// 处理不同类型的progress数据
if (typeof progressData === 'string') {
event = JSON.parse(progressData)
} else {
event = progressData as StreamingEvent
2026-01-21 15:18:15 +08:00
}
// 处理特殊事件类型
if (event && typeof event === 'object') {
// 检查是否是execution_started事件
if ('status' in event && event.status === 'execution_started') {
if ('execution_id' in event && onExecutionStarted) {
onExecutionStarted(event.execution_id as string)
2026-01-21 15:18:15 +08:00
}
return
2026-01-21 15:18:15 +08:00
}
}
onMessage(event)
} catch (e) {
// Failed to parse WebSocket data
2026-01-21 15:18:15 +08:00
}
},
// onComplete
() => {
onComplete?.()
},
// onError
(error) => {
2026-01-21 15:18:15 +08:00
onError?.(error)
},
)
2026-01-21 15:18:15 +08:00
}
/**
*
*/
2025-12-31 19:04:58 +08:00
branchPlanOutline = (data: {
branch_Number: number
Modification_Requirement: string
Existing_Steps: IRawStepTask[]
2025-12-31 19:04:58 +08:00
Baseline_Completion: number
initialInputs: string[]
goal: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) =>
websocket.send(
'branch_plan_outline',
{
2025-12-31 19:04:58 +08:00
branch_Number: data.branch_Number,
Modification_Requirement: data.Modification_Requirement,
Existing_Steps: data.Existing_Steps,
Baseline_Completion: data.Baseline_Completion,
'Initial Input Object': data.initialInputs,
'General Goal': data.goal,
},
undefined,
data.onProgress,
)
2025-12-31 19:04:58 +08:00
2026-01-21 15:18:15 +08:00
/**
*
*/
2025-12-31 19:04:58 +08:00
branchTaskProcess = (data: {
branch_Number: number
Modification_Requirement: string
Existing_Steps: BranchAction[]
2025-12-31 19:04:58 +08:00
Baseline_Completion: number
stepTaskExisting: any
goal: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}) =>
websocket.send(
'branch_task_process',
{
2025-12-31 19:04:58 +08:00
branch_Number: data.branch_Number,
Modification_Requirement: data.Modification_Requirement,
Existing_Steps: data.Existing_Steps,
Baseline_Completion: data.Baseline_Completion,
stepTaskExisting: data.stepTaskExisting,
'General Goal': data.goal,
},
undefined,
data.onProgress,
)
2025-12-31 19:04:58 +08:00
fillStepTask = async (data: {
goal: string
stepTask: any
2026-02-02 17:09:20 +08:00
generation_id?: string
TaskID?: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
}): Promise<IRawStepTask> => {
const rawResponse = await withRetry(
() =>
websocket.send(
2026-01-30 15:27:00 +08:00
'fill_step_task',
{
'General Goal': data.goal,
stepTask: data.stepTask,
2026-02-02 17:09:20 +08:00
generation_id: data.generation_id || '',
task_id: data.TaskID || '',
2026-01-30 15:27:00 +08:00
},
undefined,
data.onProgress,
),
{
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(` [fillStepTask] 第${attempt}次重试,等待 ${delay}ms...`, error?.message)
},
2026-01-30 15:27:00 +08:00
},
)
2026-01-30 15:27:00 +08:00
let response = this.extractResponse<any>(rawResponse)
if (response?.filled_stepTask) {
response = response.filled_stepTask
2026-01-09 13:54:32 +08:00
}
const briefData: Record<string, { text: string; style?: Record<string, string> }> = {}
if (response.Collaboration_Brief_FrontEnd?.data) {
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
briefData[key] = {
2026-01-30 15:27:00 +08:00
text: (value as { text: string; color: number[] }).text,
2026-01-09 13:54:32 +08:00
style: {
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
2026-01-09 13:54:32 +08:00
},
}
}
}
return {
StepName: response.StepName || '',
TaskContent: response.TaskContent || '',
InputObject_List: response.InputObject_List || [],
OutputObject: response.OutputObject || '',
AgentSelection: response.AgentSelection || [],
Collaboration_Brief_frontEnd: {
template: response.Collaboration_Brief_FrontEnd?.template || '',
data: briefData,
},
TaskProcess: response.TaskProcess || [],
}
2025-12-31 19:04:58 +08:00
}
fillStepTaskTaskProcess = async (data: {
goal: string
stepTask: IApiStepTask
agents: string[]
TaskID?: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
2025-12-31 19:04:58 +08:00
}): Promise<IApiStepTask> => {
const rawResponse = await withRetry(
() =>
websocket.send(
2026-01-30 15:27:00 +08:00
'fill_step_task_process',
{
'General Goal': data.goal,
task_id: data.TaskID || undefined,
2026-01-30 15:27:00 +08:00
stepTask_lackTaskProcess: {
StepName: data.stepTask.name,
TaskContent: data.stepTask.content,
InputObject_List: data.stepTask.inputs,
OutputObject: data.stepTask.output,
AgentSelection: data.agents,
},
agents: data.agents,
2026-01-30 15:27:00 +08:00
},
undefined,
data.onProgress,
),
{
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(
`[fillStepTaskTaskProcess] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
2026-01-30 15:27:00 +08:00
},
)
2026-02-02 17:09:20 +08:00
let response = this.extractResponse<any>(rawResponse)
if (response?.filled_stepTask) {
response = response.filled_stepTask
2025-12-31 19:04:58 +08:00
}
const briefData: Record<string, { text: string; style: { background: string } }> = {}
if (response.Collaboration_Brief_FrontEnd?.data) {
for (const [key, value] of Object.entries(response.Collaboration_Brief_FrontEnd.data)) {
briefData[key] = {
2026-01-30 15:27:00 +08:00
text: (value as { text: string; color: number[] }).text,
2025-12-31 19:04:58 +08:00
style: {
background: this.vec2Hsl((value as { text: string; color: number[] }).color),
2025-12-31 19:04:58 +08:00
},
}
}
}
const process = (response.TaskProcess || []).map((action: any) => ({
2025-12-31 19:04:58 +08:00
id: action.ID,
type: action.ActionType,
agent: action.AgentName,
description: action.Description,
inputs: action.ImportantInput,
}))
return {
name: response.StepName || '',
content: response.TaskContent || '',
inputs: response.InputObject_List || [],
output: response.OutputObject || '',
agents: response.AgentSelection || [],
brief: {
template: response.Collaboration_Brief_FrontEnd?.template || '',
data: briefData,
},
process,
}
}
2026-01-21 15:18:15 +08:00
/**
2026-01-30 15:27:00 +08:00
*
2026-01-21 15:18:15 +08:00
*/
2025-12-31 19:04:58 +08:00
agentSelectModifyInit = async (data: {
goal: string
stepTask: any
TaskID?: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
2025-12-31 19:04:58 +08:00
}): Promise<Record<string, Record<string, { reason: string; score: number }>>> => {
2026-01-30 15:27:00 +08:00
const requestPayload = {
'General Goal': data.goal,
stepTask: {
Id: data.stepTask.Id || data.stepTask.id,
2026-01-30 15:27:00 +08:00
StepName: data.stepTask.StepName || data.stepTask.name,
TaskContent: data.stepTask.TaskContent || data.stepTask.content,
InputObject_List: data.stepTask.InputObject_List || data.stepTask.inputs,
OutputObject: data.stepTask.OutputObject || data.stepTask.output,
},
task_id: data.TaskID || '',
2026-01-30 15:27:00 +08:00
}
const rawResponse = await withRetry(
() =>
websocket.send(
2026-01-30 15:27:00 +08:00
'agent_select_modify_init',
requestPayload,
undefined,
data.onProgress,
),
{
maxRetries: 3,
initialDelayMs: 2000,
onRetry: (error, attempt, delay) => {
console.warn(
`[agentSelectModifyInit] 第${attempt}次重试,等待 ${delay}ms...`,
error?.message,
)
},
2026-01-30 15:27:00 +08:00
},
)
2026-01-30 15:27:00 +08:00
let response = this.extractResponse<any>(rawResponse)
if (response?.scoreTable) {
response = response.scoreTable
}
2026-02-02 17:09:20 +08:00
2025-12-31 19:04:58 +08:00
const transformedData: Record<string, Record<string, { reason: string; score: number }>> = {}
2026-02-02 17:09:20 +08:00
if (!response || typeof response !== 'object' || Array.isArray(response)) {
console.warn('[agentSelectModifyInit] 后端返回数据格式异常:', response)
return transformedData
}
2025-12-31 19:04:58 +08:00
for (const [aspect, agents] of Object.entries(response)) {
for (const [agentName, scoreInfo] of Object.entries(
(agents as Record<string, { Reason: string; Score: number }>) || {},
)) {
2025-12-31 19:04:58 +08:00
if (!transformedData[agentName]) {
transformedData[agentName] = {}
}
transformedData[agentName][aspect] = {
reason: scoreInfo.Reason,
score: scoreInfo.Score,
}
}
}
return transformedData
}
2026-01-21 15:18:15 +08:00
/**
*
*/
2025-12-31 19:04:58 +08:00
agentSelectModifyAddAspect = async (data: {
aspectList: string[]
stepTask?: {
Id?: string
StepName?: string
TaskContent?: string
InputObject_List?: string[]
OutputObject?: string
}
TaskID?: string
2026-01-30 15:27:00 +08:00
onProgress?: (progress: {
status: string
stage?: string
message?: string
[key: string]: any
}) => void
2025-12-31 19:04:58 +08:00
}): Promise<{
aspectName: string
agentScores: Record<string, { score: number; reason: string }>
}> => {
const rawResponse = await websocket.send(
'agent_select_modify_add_aspect',
{
aspectList: data.aspectList,
stepTask: data.stepTask,
task_id: data.TaskID || '',
},
undefined,
data.onProgress,
)
const response = this.extractResponse<any>(rawResponse)
2025-12-31 19:04:58 +08:00
const newAspect = data.aspectList[data.aspectList.length - 1]
if (!newAspect) {
throw new Error('aspectList is empty')
}
const scoreTable = response.scoreTable || response
const newAspectAgents = scoreTable[newAspect]
2025-12-31 19:04:58 +08:00
const agentScores: Record<string, { score: number; reason: string }> = {}
if (newAspectAgents) {
for (const [agentName, scoreInfo] of Object.entries(newAspectAgents as Record<string, { Score?: number; score?: number; Reason?: string; reason?: string }>)) {
2025-12-31 19:04:58 +08:00
agentScores[agentName] = {
score: scoreInfo.Score || scoreInfo.score || 0,
reason: scoreInfo.Reason || scoreInfo.reason || '',
2025-12-31 19:04:58 +08:00
}
}
}
return {
aspectName: newAspect,
agentScores,
}
}
2026-03-01 16:58:31 +08:00
/**
*
* @param taskId ID
* @param stepId ID
* @param aspectName
* @returns
*/
agentSelectModifyDeleteAspect = async (
taskId: string,
aspectName: string,
stepId?: string
): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send(
'agent_select_modify_delete_aspect',
{
task_id: taskId,
step_id: stepId || '',
aspect_name: aspectName,
},
undefined,
undefined
)
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('删除维度失败:', error)
return false
}
}
/**
*
* @param executionId ID
* @param newSteps
* @returns
*/
addStepsToExecution = async (executionId: string, newSteps: IRawStepTask[]): Promise<number> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
2026-02-02 17:09:20 +08:00
const rawResponse = await websocket.send('add_steps_to_execution', {
execution_id: executionId,
2026-01-30 15:27:00 +08:00
new_steps: newSteps.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,
2026-01-30 15:27:00 +08:00
TaskProcess: step.TaskProcess.map((action) => ({
ActionType: action.ActionType,
AgentName: action.AgentName,
Description: action.Description,
ID: action.ID,
ImportantInput: action.ImportantInput,
})),
})),
2026-02-02 17:09:20 +08:00
})
const response = this.extractResponse<{ added_count: number }>(rawResponse)
return response?.added_count || 0
}
/**
*
* @param taskId ID
* @param branches
* @returns
*/
saveBranches = async (taskId: string, branches: any[]): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_branches', {
task_id: taskId,
branches,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('保存分支数据失败:', error)
return false
}
}
/**
*
* @param TaskID ID
* @param branches Map结构转对象
* @returns
*/
saveTaskProcessBranches = async (
TaskID: string,
branches: Record<string, Record<string, any[]>>,
): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_task_process_branches', {
task_id: TaskID,
branches,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('保存任务过程分支数据失败:', error)
return false
}
}
/**
*
* @param taskId ID
* @param taskOutline
* @returns
*/
updateTaskOutline = async (taskId: string, taskOutline: any): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('save_task_outline', {
task_id: taskId,
task_outline: taskOutline,
})
const response = this.extractResponse<{ status: string }>(rawResponse)
return response?.status === 'success' || false
} catch (error) {
console.error('更新任务大纲失败:', error)
return false
}
}
/**
* assigned_agents
* @param params
* @returns
*/
updateAssignedAgents = async (params: {
task_id: string // 大任务ID数据库主键
step_id: string // 步骤级ID小任务UUID
agents: string[] // 选中的 agent 列表
confirmed_groups?: string[][] // 可选:确认的 agent 组合列表
agent_combinations?: Record<string, { process: any; brief: any }> // 可选agent 组合的 TaskProcess 数据
}): Promise<boolean> => {
if (!websocket.connected) {
throw new Error('WebSocket未连接')
}
try {
const rawResponse = await websocket.send('update_assigned_agents', {
task_id: params.task_id,
step_id: params.step_id,
agents: params.agents,
confirmed_groups: params.confirmed_groups,
agent_combinations: params.agent_combinations,
})
const response = this.extractResponse<{ status: string; error?: string }>(rawResponse)
if (response?.status === 'success') {
console.log('更新 assigned_agents 成功:', params)
return true
}
console.warn('更新 assigned_agents 失败:', response?.error)
return false
} catch (error) {
console.error('更新 assigned_agents 失败:', error)
return false
}
}
}
export default new Api()