完整实现 Swarm 多智能体协作系统
- 新增 CLIPluginAdapter 统一接口 (backend/app/core/agent_adapter.py) - 新增 LLM 服务层,支持 Anthropic/OpenAI/DeepSeek/Ollama (backend/app/services/llm_service.py) - 新增 Agent 执行引擎,支持文件锁自动管理 (backend/app/services/agent_executor.py) - 新增 NativeLLMAgent 原生 LLM 适配器 (backend/app/adapters/native_llm_agent.py) - 新增进程管理器 (backend/app/services/process_manager.py) - 新增 Agent 控制 API (backend/app/routers/agents_control.py) - 新增 WebSocket 实时通信 (backend/app/routers/websocket.py) - 更新前端 AgentsPage,支持启动/停止 Agent - 测试通过:Agent 启动、批量操作、栅栏同步 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
428
frontend/src/lib/api.ts
Normal file
428
frontend/src/lib/api.ts
Normal file
@@ -0,0 +1,428 @@
|
||||
// API 客户端 - 与后端交互
|
||||
|
||||
import type {
|
||||
Agent,
|
||||
AgentState,
|
||||
FileLock,
|
||||
Heartbeat,
|
||||
Meeting,
|
||||
MeetingQueue,
|
||||
Workflow,
|
||||
AgentResourceStatus,
|
||||
} from '../types';
|
||||
|
||||
// API 基础地址
|
||||
const API_BASE = 'http://localhost:8000/api';
|
||||
|
||||
// 通用请求函数
|
||||
async function request<T>(
|
||||
endpoint: string,
|
||||
options?: RequestInit
|
||||
): Promise<T> {
|
||||
const url = `${API_BASE}${endpoint}`;
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
...options,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({
|
||||
error: {
|
||||
code: 'UNKNOWN_ERROR',
|
||||
message: `HTTP ${response.status}: ${response.statusText}`,
|
||||
},
|
||||
}));
|
||||
throw new Error(error.error?.message || '请求失败');
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// ==================== Agent API ====================
|
||||
|
||||
export const agentApi = {
|
||||
// 列出所有 Agent
|
||||
list: () => request<{ agents: Agent[] }>('/agents'),
|
||||
|
||||
// 注册新 Agent
|
||||
register: (agent: Omit<Agent, 'status' | 'created_at'>) =>
|
||||
request<Agent>('/agents/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(agent),
|
||||
}),
|
||||
|
||||
// 获取 Agent 详情
|
||||
get: (agentId: string) => request<Agent>(`/agents/${agentId}`),
|
||||
|
||||
// 获取 Agent 状态
|
||||
getState: (agentId: string) =>
|
||||
request<AgentState>(`/agents/${agentId}/state`),
|
||||
|
||||
// 更新 Agent 状态
|
||||
updateState: (
|
||||
agentId: string,
|
||||
state: { task: string; progress: number; working_files?: string[] }
|
||||
) =>
|
||||
request<{ success: boolean }>(`/agents/${agentId}/state`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(state),
|
||||
}),
|
||||
};
|
||||
|
||||
// ==================== 文件锁 API ====================
|
||||
|
||||
export const lockApi = {
|
||||
// 获取所有锁
|
||||
list: () => request<{ locks: FileLock[] }>('/locks'),
|
||||
|
||||
// 获取文件锁
|
||||
acquire: (filePath: string, agentId: string, agentName: string) =>
|
||||
request<{ success: boolean }>('/locks/acquire', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_path: filePath, agent_id: agentId, agent_name: agentName }),
|
||||
}),
|
||||
|
||||
// 释放文件锁
|
||||
release: (filePath: string, agentId: string) =>
|
||||
request<{ success: boolean }>('/locks/release', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ file_path: filePath, agent_id: agentId }),
|
||||
}),
|
||||
|
||||
// 检查文件锁定状态
|
||||
check: (filePath: string) =>
|
||||
request<{ file_path: string; locked: boolean; locked_by?: string }>(
|
||||
`/locks/check?file_path=${encodeURIComponent(filePath)}`
|
||||
),
|
||||
};
|
||||
|
||||
// ==================== 心跳 API ====================
|
||||
|
||||
export const heartbeatApi = {
|
||||
// 获取所有心跳
|
||||
list: () => request<{ heartbeats: Record<string, Heartbeat> }>('/heartbeats'),
|
||||
|
||||
// 更新心跳
|
||||
update: (
|
||||
agentId: string,
|
||||
data: { status: Heartbeat['status']; current_task: string; progress: number }
|
||||
) =>
|
||||
request<{ success: boolean }>(`/heartbeats/${agentId}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
// 检查超时 Agent
|
||||
checkTimeouts: (timeoutSeconds: number = 60) =>
|
||||
request<{ timeout_seconds: number; timeout_agents: string[] }>(
|
||||
`/heartbeats/timeouts?timeout_seconds=${timeoutSeconds}`
|
||||
),
|
||||
};
|
||||
|
||||
// ==================== 会议 API ====================
|
||||
|
||||
export const meetingApi = {
|
||||
// 创建会议
|
||||
create: (data: {
|
||||
meeting_id: string;
|
||||
title: string;
|
||||
expected_attendees: string[];
|
||||
min_required?: number;
|
||||
}) =>
|
||||
request<{ success: boolean }>('/meetings/create', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
// 获取会议队列
|
||||
getQueue: (meetingId: string) =>
|
||||
request<MeetingQueue>(`/meetings/${meetingId}/queue`),
|
||||
|
||||
// 等待会议开始
|
||||
wait: (meetingId: string, agentId: string, timeout?: number) =>
|
||||
request<{ result: 'started' | 'timeout' | 'error'; meeting_id: string; agent_id: string }>(
|
||||
`/meetings/${meetingId}/wait`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ agent_id: agentId, timeout }),
|
||||
}
|
||||
),
|
||||
|
||||
// 结束会议
|
||||
end: (meetingId: string) =>
|
||||
request<{ success: boolean }>(`/meetings/${meetingId}/end`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
// 创建会议记录
|
||||
createRecord: (data: {
|
||||
meeting_id: string;
|
||||
title: string;
|
||||
attendees: string[];
|
||||
steps?: string[];
|
||||
}) =>
|
||||
request<Meeting>('/meetings/record/create', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
// 添加讨论
|
||||
addDiscussion: (
|
||||
meetingId: string,
|
||||
data: { agent_id: string; agent_name: string; content: string; step?: string }
|
||||
) =>
|
||||
request<{ success: boolean }>(`/meetings/${meetingId}/discuss`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
// 更新进度
|
||||
updateProgress: (meetingId: string, step: string) =>
|
||||
request<{ success: boolean }>(`/meetings/${meetingId}/progress`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ step }),
|
||||
}),
|
||||
|
||||
// 获取会议详情
|
||||
get: (meetingId: string, date?: string) => {
|
||||
const query = date ? `?date=${date}` : '';
|
||||
return request<Meeting>(`/meetings/${meetingId}${query}`);
|
||||
},
|
||||
|
||||
// 完成会议
|
||||
finish: (meetingId: string, consensus: string) =>
|
||||
request<{ success: boolean }>(`/meetings/${meetingId}/finish`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ consensus }),
|
||||
}),
|
||||
|
||||
// 列出今日会议
|
||||
listToday: () => {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
return request<{ meetings: Meeting[] }>(`/meetings?date=${today}`);
|
||||
},
|
||||
};
|
||||
|
||||
// ==================== 资源管理 API ====================
|
||||
|
||||
export const resourceApi = {
|
||||
// 执行任务
|
||||
execute: (agentId: string, task: string, timeout?: number) =>
|
||||
request<{
|
||||
success: boolean;
|
||||
message: string;
|
||||
files_locked: string[];
|
||||
duration_seconds: number;
|
||||
}>('/execute', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ agent_id: agentId, task, timeout }),
|
||||
}),
|
||||
|
||||
// 获取所有 Agent 状态
|
||||
getAllStatus: () => request<{ agents: AgentResourceStatus[] }>('/status'),
|
||||
|
||||
// 解析任务文件
|
||||
parseTask: (task: string) =>
|
||||
request<{ task: string; files: string[] }>('/parse-task', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task }),
|
||||
}),
|
||||
};
|
||||
|
||||
// ==================== 工作流 API ====================
|
||||
|
||||
export const workflowApi = {
|
||||
// 列出工作流文件
|
||||
listFiles: () =>
|
||||
request<{ files: Array<{ name: string; path: string; size: number; modified: number }> }>('/workflows/files'),
|
||||
|
||||
// 列出已加载的工作流
|
||||
list: () =>
|
||||
request<{ workflows: Array<{ workflow_id: string; name: string; status: string; progress: string }> }>('/workflows/list'),
|
||||
|
||||
// 启动工作流
|
||||
start: (path: string) =>
|
||||
request<Workflow>(`/workflows/start/${path}`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
// 获取工作流详情
|
||||
get: (workflowId: string) =>
|
||||
request<Workflow>(`/workflows/${workflowId}`),
|
||||
|
||||
// 获取工作流状态
|
||||
getStatus: (workflowId: string) =>
|
||||
request<Workflow>(`/workflows/${workflowId}/status`),
|
||||
|
||||
// 获取下一个节点
|
||||
getNext: (workflowId: string) =>
|
||||
request<{ meeting: { meeting_id: string; title: string; node_type: string; attendees: string[] } | null; message?: string }>(`/workflows/${workflowId}/next`),
|
||||
|
||||
// 标记节点完成
|
||||
complete: (workflowId: string, meetingId: string) =>
|
||||
request<{ success: boolean; message: string }>(`/workflows/${workflowId}/complete/${meetingId}`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
|
||||
// Agent 加入执行节点
|
||||
join: (workflowId: string, meetingId: string, agentId: string) =>
|
||||
request<{ status: 'ready' | 'waiting' | 'error'; progress: string; message: string; missing?: string[] }>(`/workflows/${workflowId}/join/${meetingId}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ agent_id: agentId }),
|
||||
}),
|
||||
|
||||
// 获取执行节点状态
|
||||
getExecutionStatus: (workflowId: string, meetingId: string) =>
|
||||
request<{ meeting_id: string; title: string; node_type: string; progress: string; is_ready: boolean; completed_attendees: string[]; missing: string[] }>(`/workflows/${workflowId}/execution/${meetingId}`),
|
||||
|
||||
// 强制跳转到指定节点
|
||||
jump: (workflowId: string, targetMeetingId: string) =>
|
||||
request<{ success: boolean; message: string; detail?: Workflow }>(`/workflows/${workflowId}/jump`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ target_meeting_id: targetMeetingId }),
|
||||
}),
|
||||
|
||||
// 处理节点失败
|
||||
handleFailure: (workflowId: string, meetingId: string) =>
|
||||
request<{ success: boolean; message: string; target?: string; detail?: Workflow }>(`/workflows/${workflowId}/fail/${meetingId}`, {
|
||||
method: 'POST',
|
||||
}),
|
||||
};
|
||||
|
||||
// ==================== 角色分配 API ====================
|
||||
|
||||
export const roleApi = {
|
||||
// 获取任务主要角色
|
||||
getPrimary: (task: string) =>
|
||||
request<{
|
||||
task: string;
|
||||
primary_role: string;
|
||||
role_scores: Record<string, number>;
|
||||
}>('/roles/primary', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task }),
|
||||
}),
|
||||
|
||||
// 分配角色
|
||||
allocate: (task: string, agents: string[]) =>
|
||||
request<{
|
||||
task: string;
|
||||
primary_role: string;
|
||||
allocation: Record<string, string>;
|
||||
}>('/roles/allocate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task, agents }),
|
||||
}),
|
||||
|
||||
// 解释角色分配
|
||||
explain: (task: string, agents: string[]) =>
|
||||
request<{ explanation: string }>('/roles/explain', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ task, agents }),
|
||||
}),
|
||||
};
|
||||
|
||||
// ==================== 系统 API ====================
|
||||
|
||||
export const systemApi = {
|
||||
// 健康检查
|
||||
health: () =>
|
||||
request<{
|
||||
status: string;
|
||||
version: string;
|
||||
services: Record<string, string>;
|
||||
}>('/health'),
|
||||
};
|
||||
|
||||
// ==================== 人类输入 API ====================
|
||||
|
||||
export const humanApi = {
|
||||
// 获取摘要
|
||||
summary: () =>
|
||||
request<{ participants: number; online_users: number; pending_tasks: number; urgent_tasks: number; pending_comments: number; last_updated: string }>('/humans/summary'),
|
||||
|
||||
// 注册参与者
|
||||
register: (userId: string, name: string, role?: string, avatar?: string) =>
|
||||
request<{ success: boolean; user_id: string }>('/humans/register', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ user_id: userId, name, role, avatar }),
|
||||
}),
|
||||
|
||||
// 获取参与者列表
|
||||
getParticipants: () =>
|
||||
request<{ participants: Array<{ id: string; name: string; role: string; status: string; avatar: string }> }>('/humans/participants'),
|
||||
|
||||
// 添加任务请求
|
||||
addTask: (content: string, options?: { from_user?: string; priority?: string; title?: string; target_files?: string[]; suggested_agent?: string; urgent?: boolean }) =>
|
||||
request<{ success: boolean; task_id: string }>('/humans/tasks', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ content, ...options }),
|
||||
}),
|
||||
|
||||
// 获取待处理任务
|
||||
getPendingTasks: (options?: { priority?: string; agent?: string }) => {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.priority) params.append('priority', options.priority);
|
||||
if (options?.agent) params.append('agent', options.agent);
|
||||
return request<{ tasks: Array<{ id: string; from_user: string; timestamp: string; priority: string; title: string; content: string; suggested_agent: string; urgent: boolean; is_urgent: boolean }>; count: number }>(`/humans/tasks?${params}`);
|
||||
},
|
||||
|
||||
// 获取紧急任务
|
||||
getUrgentTasks: () =>
|
||||
request<{ tasks: Array<{ id: string; from_user: string; content: string; title: string; suggested_agent: string }>; count: number }>('/humans/tasks/urgent'),
|
||||
|
||||
// 标记任务处理中
|
||||
markTaskProcessing: (taskId: string) =>
|
||||
request<{ success: boolean }>(`/humans/tasks/${taskId}/processing`, {
|
||||
method: 'PUT',
|
||||
}),
|
||||
|
||||
// 标记任务完成
|
||||
markTaskComplete: (taskId: string) =>
|
||||
request<{ success: boolean }>(`/humans/tasks/${taskId}/complete`, {
|
||||
method: 'PUT',
|
||||
}),
|
||||
|
||||
// 添加会议评论
|
||||
addComment: (meetingId: string, content: string, options?: { from_user?: string; comment_type?: string; priority?: string }) =>
|
||||
request<{ success: boolean; comment_id: string }>('/humans/comments', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ meeting_id: meetingId, content, ...options }),
|
||||
}),
|
||||
|
||||
// 获取待处理评论
|
||||
getPendingComments: (meetingId?: string) => {
|
||||
const params = meetingId ? `?meeting_id=${meetingId}` : '';
|
||||
return request<{ comments: Array<{ id: string; from_user: string; meeting_id: string; timestamp: string; type: string; priority: string; content: string }>; count: string }>(`/humans/comments${params}`);
|
||||
},
|
||||
|
||||
// 标记评论已处理
|
||||
markCommentAddressed: (commentId: string) =>
|
||||
request<{ success: boolean }>(`/humans/comments/${commentId}/addressed`, {
|
||||
method: 'PUT',
|
||||
}),
|
||||
|
||||
// 更新用户状态
|
||||
updateUserStatus: (userId: string, status: string, currentFocus?: string) =>
|
||||
request<{ success: boolean }>(`/humans/users/${userId}/status`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({ status, current_focus: currentFocus }),
|
||||
}),
|
||||
};
|
||||
|
||||
// 导出所有 API
|
||||
export const api = {
|
||||
agent: agentApi,
|
||||
lock: lockApi,
|
||||
heartbeat: heartbeatApi,
|
||||
meeting: meetingApi,
|
||||
resource: resourceApi,
|
||||
workflow: workflowApi,
|
||||
role: roleApi,
|
||||
system: systemApi,
|
||||
human: humanApi,
|
||||
};
|
||||
|
||||
export default api;
|
||||
Reference in New Issue
Block a user