import { apiGet, apiPost, apiDelete, apiPut, apiRequest } from './base' import { useUserStore } from '@/stores/user' /** * 智能体API模块 * 包含智能体管理、聊天、配置等功能 * 权限要求: 任何已登录用户(普通用户、管理员、超级管理员) */ // ============================================================================= // === 智能体聊天分组 === // ============================================================================= export const agentApi = { /** * 简单聊天调用(非流式) * @param {string} query - 查询内容 * @returns {Promise} - 聊天响应 */ simpleCall: (query) => apiPost('/api/chat/call', { query }), /** * 生成对话标题 * @param {string} query - 查询内容 * @param {Object} modelSpec - 模型配置 * @returns {Promise} - 生成的标题 */ generateTitle: async (query, modelSpec) => { const response = await apiPost('/api/chat/call', { query: `根据以下对话内容生成一个简短的标题(最多30个字符,中英文均可),不要包含 markdown 标记:\n\n${query.slice(0, 2000)}`, meta: { model_spec: modelSpec } }) return response.response }, /** * 获取智能体列表 * @returns {Promise} - 智能体列表 */ getAgents: ({ includeSubagents = false } = {}) => { const params = new URLSearchParams() if (includeSubagents) params.set('include_subagents', 'true') const query = params.toString() return apiGet(query ? `/api/agent?${query}` : '/api/agent') }, getAgentBackends: () => apiGet('/api/agent/backends'), /** * 获取单个智能体详情 * @param {string} agentId - 智能体ID * @returns {Promise} - 智能体详情 */ getAgentDetail: (agentId) => apiGet(`/api/agent/${agentId}`), /** * 获取智能体历史消息 * @param {string} agentId - 智能体ID * @param {string} threadId - 会话ID * @returns {Promise} - 历史消息 */ getAgentHistory: (threadId) => apiGet(`/api/chat/thread/${threadId}/history`), /** * 获取指定会话的 AgentState * @param {string} agentId - 智能体ID * @param {string} threadId - 会话ID * @returns {Promise} - AgentState */ getAgentState: (threadId, { includeMessages = false } = {}) => apiGet(`/api/chat/thread/${threadId}/state${includeMessages ? '?include_messages=true' : ''}`), /** * Submit feedback for a message * @param {number} messageId - Message ID * @param {string} rating - 'like' or 'dislike' * @param {string|null} reason - Optional reason for dislike * @returns {Promise} - Feedback response */ submitMessageFeedback: (messageId, rating, reason = null) => apiPost(`/api/chat/message/${messageId}/feedback`, { rating, reason }), /** * Get feedback status for a message * @param {number} messageId - Message ID * @returns {Promise} - Feedback status */ getMessageFeedback: (messageId) => apiGet(`/api/chat/message/${messageId}/feedback`), createAgent: (payload) => apiPost('/api/agent', payload), updateAgent: (agentId, payload) => apiPut(`/api/agent/${agentId}`, payload), deleteAgent: (agentId) => apiDelete(`/api/agent/${agentId}`), /** * 创建异步运行任务(Run) * @param {Object} data - run 请求体 * @returns {Promise} */ createAgentRun: (data) => apiPost('/api/agent/runs', { query: data.query, agent_slug: data.agent_slug, thread_id: data.thread_id, meta: data.meta || {}, image_content: data.image_content || null, model_spec: data.model_spec || null, resume: data.resume ?? null, created_by_run_id: data.created_by_run_id || null }), /** * 获取 Run 状态 * @param {string} runId - run ID * @returns {Promise} */ getAgentRun: (runId) => apiGet(`/api/agent/runs/${runId}`), /** * 取消 Run * @param {string} runId - run ID * @returns {Promise} */ cancelAgentRun: (runId) => apiPost(`/api/agent/runs/${runId}/cancel`, {}), /** * 获取线程活跃 Run * @param {string} threadId - 线程ID * @returns {Promise} */ getThreadActiveRun: (threadId) => apiGet(`/api/agent/thread/${threadId}/active_run`), /** * 打开 Run 事件 SSE 连接(调用方负责关闭) * @param {string} runId - run ID * @param {string} afterSeq - 起始 seq/cursor * @param {Object} options - { signal, verbose } * @returns {Promise} */ streamAgentRunEvents: (runId, afterSeq = '0-0', options = {}) => { const { signal, verbose = false } = options const headers = { ...useUserStore().getAuthHeaders() } const cursor = String(afterSeq || '0-0') if (cursor && cursor !== '0-0') { headers['Last-Event-ID'] = cursor } const params = new URLSearchParams({ verbose: String(verbose) }) return fetch(`/api/agent/runs/${runId}/events?${params.toString()}`, { method: 'GET', headers, signal }) } } // ============================================================================= // === 多模态图片支持分组 === // ============================================================================= export const multimodalApi = { /** * 上传图片并获取base64编码 * @param {File} file - 图片文件 * @returns {Promise} - 上传结果 */ uploadImage: (file) => { const formData = new FormData() formData.append('file', file) return apiRequest( '/api/chat/image/upload', { method: 'POST', body: formData }, true ) } } // ============================================================================= // === 对话线程分组 === // ============================================================================= export const threadApi = { /** * 获取对话线程列表 * @param {string | null | undefined} agentId - 智能体ID,可选;不传时返回全部智能体对话 * @param {number} limit - 返回数量限制,默认100 * @param {number} offset - 偏移量,默认0 * @returns {Promise} - 对话线程列表 */ getThreads: (agentId = null, limit = 100, offset = 0) => { const params = new URLSearchParams({ limit: String(limit), offset: String(offset) }) if (agentId) { params.set('agent_id', agentId) } const url = `/api/chat/threads?${params.toString()}` return apiGet(url) }, /** * 搜索历史对话 * @param {string} query - 搜索关键词 * @param {Object} options - 搜索选项 * @param {string | null | undefined} options.agentId - 智能体ID,可选 * @param {number} options.limit - 返回数量限制 * @param {number} options.offset - 偏移量 * @returns {Promise} - 搜索结果 */ searchThreads: (query, { agentId = null, limit = 20, offset = 0 } = {}) => { const params = new URLSearchParams({ q: query, limit: String(limit), offset: String(offset) }) if (agentId) { params.set('agent_id', agentId) } return apiGet(`/api/chat/threads/search?${params.toString()}`) }, /** * 创建新对话线程 * @param {string} agentId - 智能体ID * @param {string} title - 对话标题 * @param {Object} metadata - 元数据 * @returns {Promise} - 创建结果 */ createThread: (agentId, title, metadata) => apiPost('/api/chat/thread', { agent_id: agentId, title: title || '新的对话', metadata: metadata || {} }), /** * 更新对话线程 * @param {string} threadId - 对话线程ID * @param {string} title - 对话标题 * @param {boolean} is_pinned - 是否置顶 * @returns {Promise} - 更新结果 */ updateThread: (threadId, title, is_pinned) => apiPut(`/api/chat/thread/${threadId}`, { title, is_pinned }), /** * 删除对话线程 * @param {string} threadId - 对话线程ID * @returns {Promise} - 删除结果 */ deleteThread: (threadId) => apiDelete(`/api/chat/thread/${threadId}`), /** * 获取线程附件列表 * @param {string} threadId - 对话线程ID * @returns {Promise} */ getThreadAttachments: (threadId) => apiGet(`/api/chat/thread/${threadId}/attachments`), /** * 列出线程文件(目录) * @param {string} threadId * @param {string} path * @param {boolean} recursive * @returns {Promise} */ listThreadFiles: (threadId, path = '/home/gem/user-data', recursive = false) => apiGet( `/api/chat/thread/${threadId}/files?path=${encodeURIComponent(path)}&recursive=${recursive}` ), /** * 读取线程文本文件内容(分页) * @param {string} threadId * @param {string} path * @param {number} offset * @param {number} limit * @returns {Promise} */ readThreadFile: (threadId, path, offset = 0, limit = 2000) => apiGet( `/api/chat/thread/${threadId}/files/content?path=${encodeURIComponent(path)}&offset=${offset}&limit=${limit}` ), /** * 获取线程文件下载/预览 URL * @param {string} threadId * @param {string} path * @param {boolean} download * @returns {string} */ getThreadArtifactUrl: (threadId, path, download = false) => { const encodedPath = path .split('/') .filter(Boolean) .map((segment) => encodeURIComponent(segment)) .join('/') const query = download ? '?download=true' : '' return `/api/chat/thread/${threadId}/artifacts/${encodedPath}${query}` }, /** * 下载线程文件(带鉴权) * @param {string} threadId * @param {string} path * @returns {Promise} */ downloadThreadArtifact: (threadId, path) => apiGet(threadApi.getThreadArtifactUrl(threadId, path, true), {}, true, 'blob'), /** * 保存交付物到 workspace/saved_artifacts * @param {string} threadId * @param {string} path * @returns {Promise} */ saveThreadArtifactToWorkspace: (threadId, path) => apiPost(`/api/chat/thread/${threadId}/artifacts/save`, { path }), /** * 上传临时附件 * @param {File} file * @returns {Promise} */ uploadTmpAttachment: (file) => { const formData = new FormData() formData.append('file', file) return apiRequest('/api/chat/attachments/tmp', { method: 'POST', body: formData }) }, /** * 解析临时附件 * @param {Object} payload * @returns {Promise} */ parseTmpAttachment: (payload) => apiPost('/api/chat/attachments/tmp/parse', payload), /** * 确认添加临时附件到线程 * @param {string} threadId * @param {Array} attachments * @returns {Promise} */ confirmTmpThreadAttachments: (threadId, attachments) => apiPost(`/api/chat/thread/${threadId}/attachments/confirm`, { attachments }), /** * 上传附件 * @param {string} threadId * @param {File} file * @returns {Promise} */ uploadThreadAttachment: (threadId, file) => { const formData = new FormData() formData.append('file', file) return apiRequest(`/api/chat/thread/${threadId}/attachments`, { method: 'POST', body: formData }) }, /** * 删除附件 * @param {string} threadId * @param {string} fileId * @returns {Promise} */ deleteThreadAttachment: (threadId, fileId) => apiDelete(`/api/chat/thread/${threadId}/attachments/${fileId}`) }