237 lines
6.7 KiB
TypeScript
237 lines
6.7 KiB
TypeScript
// File: frontend/lib/api.ts (Update)
|
|
// Description: 添加运行工作流的 API 函数
|
|
|
|
import axios from "axios";
|
|
import type { Node, Edge } from "reactflow"; // Import React Flow types
|
|
import type {
|
|
Assistant,
|
|
Session,
|
|
Message,
|
|
AssistantCreateData,
|
|
AssistantUpdateData,
|
|
ChatApiResponse,
|
|
} from "./types"; // Assuming types are defined
|
|
|
|
// --- Types ---
|
|
// Workflow Run types (match backend pydantic models)
|
|
interface WorkflowNodeData {
|
|
label?: string | null;
|
|
text?: string | null;
|
|
displayText?: string | null;
|
|
model?: string | null;
|
|
temperature?: number | null;
|
|
systemPrompt?: string | null;
|
|
// Add other node data fields as needed
|
|
[key: string]: any; // Allow extra fields
|
|
}
|
|
interface WorkflowNode {
|
|
id: string;
|
|
type: string;
|
|
position: { x: number; y: number };
|
|
data: WorkflowNodeData;
|
|
}
|
|
interface WorkflowEdge {
|
|
id: string;
|
|
source: string;
|
|
target: string;
|
|
sourceHandle?: string | null;
|
|
targetHandle?: string | null;
|
|
}
|
|
interface WorkflowRunPayload {
|
|
nodes: WorkflowNode[];
|
|
edges: WorkflowEdge[];
|
|
}
|
|
export interface WorkflowRunResult {
|
|
success: boolean;
|
|
message?: string | null;
|
|
output?: string | null;
|
|
output_node_id?: string | null;
|
|
}
|
|
|
|
// --- API Client Setup ---
|
|
const API_BASE_URL =
|
|
process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000/api/v1";
|
|
|
|
const apiClient = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
});
|
|
|
|
// --- Helper for Error Handling ---
|
|
const handleApiError = (error: unknown, context: string): string => {
|
|
console.error(`API Error (${context}):`, error);
|
|
if (axios.isAxiosError(error) && error.response) {
|
|
// 尝试提取后端返回的详细错误信息
|
|
return (
|
|
error.response.data?.detail || `服务器错误 (${error.response.status})`
|
|
);
|
|
} else if (error instanceof Error) {
|
|
return error.message;
|
|
}
|
|
return "发生未知网络错误";
|
|
};
|
|
|
|
// --- Chat API ---
|
|
/**
|
|
* 发送聊天消息到后端 (更新)
|
|
* @param message 用户消息
|
|
* @param sessionId 当前会话 ID (可以是 'temp-new-chat')
|
|
* @param assistantId 当前助手 ID
|
|
* @returns 包含 AI 回复和可能的新会话信息的对象
|
|
*/
|
|
export const sendChatMessage = async (
|
|
message: string,
|
|
sessionId: string,
|
|
assistantId: string
|
|
): Promise<ChatApiResponse> => {
|
|
try {
|
|
const response = await apiClient.post<ChatApiResponse>("/chat/", {
|
|
message,
|
|
session_id: sessionId,
|
|
assistant_id: assistantId,
|
|
});
|
|
return response.data; // 返回整个响应体
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "sendChatMessage"));
|
|
}
|
|
};
|
|
|
|
// --- Assistant API ---
|
|
/** 获取所有助手列表 */
|
|
export const getAssistants = async (): Promise<Assistant[]> => {
|
|
try {
|
|
const response = await apiClient.get<Assistant[]>("/assistants/");
|
|
return response.data;
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "getAssistants"));
|
|
}
|
|
};
|
|
|
|
/** 创建新助手 */
|
|
export const createAssistant = async (
|
|
data: AssistantCreateData
|
|
): Promise<Assistant> => {
|
|
try {
|
|
const response = await apiClient.post<Assistant>("/assistants/", data);
|
|
return response.data;
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "createAssistant"));
|
|
}
|
|
};
|
|
|
|
/** 更新助手 */
|
|
export const updateAssistant = async (
|
|
id: string,
|
|
data: AssistantUpdateData
|
|
): Promise<Assistant> => {
|
|
try {
|
|
const response = await apiClient.put<Assistant>(`/assistants/${id}`, data);
|
|
return response.data;
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "updateAssistant"));
|
|
}
|
|
};
|
|
|
|
/** 删除助手 */
|
|
export const deleteAssistant = async (id: string): Promise<void> => {
|
|
try {
|
|
await apiClient.delete(`/assistants/${id}`);
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "deleteAssistant"));
|
|
}
|
|
};
|
|
|
|
// --- Session API ---
|
|
/** 获取指定助手的所有会话 */
|
|
export const getSessionsByAssistant = async (
|
|
assistantId: string
|
|
): Promise<Session[]> => {
|
|
try {
|
|
const response = await apiClient.get<Session[]>(
|
|
`/sessions/assistant/${assistantId}`
|
|
);
|
|
return response.data;
|
|
} catch (error) {
|
|
// 如果助手没有会话,后端可能返回 404 或空列表,这里统一处理为返回空列表
|
|
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
return [];
|
|
}
|
|
throw new Error(handleApiError(error, "getSessionsByAssistant"));
|
|
}
|
|
};
|
|
|
|
/** 删除会话 */
|
|
export const deleteSession = async (sessionId: string): Promise<void> => {
|
|
try {
|
|
await apiClient.delete(`/sessions/${sessionId}`);
|
|
} catch (error) {
|
|
throw new Error(handleApiError(error, "deleteSession"));
|
|
}
|
|
};
|
|
|
|
// 注意:创建会话的 API (POST /sessions/) 在后端被整合到了 POST /chat/ 逻辑中,
|
|
//当前端发送 sessionId 为 'temp-new-chat' 的消息时,后端会自动创建。
|
|
//如果需要单独创建会话(例如,不发送消息就创建),则需要单独实现前端调用 POST /sessions/。
|
|
|
|
// --- Message API (New) ---
|
|
/** 获取指定会话的消息列表 */
|
|
export const getMessagesBySession = async (
|
|
sessionId: string,
|
|
limit: number = 100,
|
|
skip: number = 0
|
|
): Promise<Message[]> => {
|
|
try {
|
|
const response = await apiClient.get<Message[]>(
|
|
`/messages/session/${sessionId}`,
|
|
{
|
|
params: { limit, skip },
|
|
}
|
|
);
|
|
return response.data;
|
|
} catch (error) {
|
|
// Handle 404 specifically if needed (session exists but no messages)
|
|
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
return []; // Return empty list if session not found or no messages
|
|
}
|
|
throw new Error(handleApiError(error, "getMessagesBySession"));
|
|
}
|
|
};
|
|
|
|
// --- Workflow API (New) ---
|
|
/**
|
|
* 发送工作流定义到后端执行
|
|
* @param nodes - React Flow 节点数组
|
|
* @param edges - React Flow 边数组
|
|
* @returns 工作流执行结果
|
|
*/
|
|
export const runWorkflow = async (nodes: Node[], edges: Edge[]): Promise<WorkflowRunResult> => {
|
|
// Map React Flow nodes/edges to the structure expected by the backend API
|
|
const payload: WorkflowRunPayload = {
|
|
nodes: nodes.map(n => ({
|
|
id: n.id,
|
|
type: n.type || 'default', // Ensure type is present
|
|
position: n.position,
|
|
data: n.data as WorkflowNodeData, // Assume data matches for now
|
|
})),
|
|
edges: edges.map(e => ({
|
|
id: e.id,
|
|
source: e.source,
|
|
target: e.target,
|
|
sourceHandle: e.sourceHandle,
|
|
targetHandle: e.targetHandle,
|
|
})),
|
|
};
|
|
|
|
try {
|
|
const response = await apiClient.post<WorkflowRunResult>('/workflow/run', payload);
|
|
return response.data;
|
|
} catch (error) {
|
|
// Return a failed result structure on API error
|
|
return {
|
|
success: false,
|
|
message: handleApiError(error, 'runWorkflow'),
|
|
};
|
|
}
|
|
}; |