2026-05-28 12:53:29 +08:00
|
|
|
|
import { apiGet, apiPost, apiDelete, apiPut, apiRequest } from './base'
|
2025-05-04 21:04:01 +08:00
|
|
|
|
import { useUserStore } from '@/stores/user'
|
2025-05-02 23:56:59 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2025-08-24 22:32:37 +08:00
|
|
|
|
* 智能体API模块
|
|
|
|
|
|
* 包含智能体管理、聊天、配置等功能
|
2025-05-02 23:56:59 +08:00
|
|
|
|
* 权限要求: 任何已登录用户(普通用户、管理员、超级管理员)
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2025-08-24 22:32:37 +08:00
|
|
|
|
// =============================================================================
|
|
|
|
|
|
// === 智能体聊天分组 ===
|
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
export const agentApi = {
|
2025-05-02 23:56:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 简单聊天调用(非流式)
|
|
|
|
|
|
* @param {string} query - 查询内容
|
|
|
|
|
|
* @returns {Promise} - 聊天响应
|
|
|
|
|
|
*/
|
2025-08-24 22:32:37 +08:00
|
|
|
|
simpleCall: (query) => apiPost('/api/chat/call', { query }),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
|
2026-03-23 09:43:04 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 生成对话标题
|
|
|
|
|
|
* @param {string} query - 查询内容
|
|
|
|
|
|
* @param {Object} modelSpec - 模型配置
|
|
|
|
|
|
* @returns {Promise<string>} - 生成的标题
|
|
|
|
|
|
*/
|
|
|
|
|
|
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
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2025-05-02 23:56:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取智能体列表
|
|
|
|
|
|
* @returns {Promise} - 智能体列表
|
|
|
|
|
|
*/
|
2026-06-01 22:29:18 +08:00
|
|
|
|
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')
|
|
|
|
|
|
},
|
2026-05-24 00:47:08 +08:00
|
|
|
|
|
|
|
|
|
|
getAgentBackends: () => apiGet('/api/agent/backends'),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取单个智能体详情
|
|
|
|
|
|
* @param {string} agentId - 智能体ID
|
|
|
|
|
|
* @returns {Promise} - 智能体详情
|
|
|
|
|
|
*/
|
2026-05-24 00:47:08 +08:00
|
|
|
|
getAgentDetail: (agentId) => apiGet(`/api/agent/${agentId}`),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
|
2025-05-15 22:34:32 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取智能体历史消息
|
2025-07-23 04:02:36 +08:00
|
|
|
|
* @param {string} agentId - 智能体ID
|
2025-05-15 22:34:32 +08:00
|
|
|
|
* @param {string} threadId - 会话ID
|
|
|
|
|
|
* @returns {Promise} - 历史消息
|
|
|
|
|
|
*/
|
2026-03-27 01:42:19 +08:00
|
|
|
|
getAgentHistory: (threadId) => apiGet(`/api/chat/thread/${threadId}/history`),
|
2025-05-02 23:56:59 +08:00
|
|
|
|
|
2025-11-17 12:01:59 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 获取指定会话的 AgentState
|
|
|
|
|
|
* @param {string} agentId - 智能体ID
|
|
|
|
|
|
* @param {string} threadId - 会话ID
|
|
|
|
|
|
* @returns {Promise} - AgentState
|
|
|
|
|
|
*/
|
2026-03-27 01:42:19 +08:00
|
|
|
|
getAgentState: (threadId) => apiGet(`/api/chat/thread/${threadId}/state`),
|
2025-11-17 12:01:59 +08:00
|
|
|
|
|
2025-10-05 14:50:34 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 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`),
|
|
|
|
|
|
|
2026-05-24 00:47:08 +08:00
|
|
|
|
createAgent: (payload) => apiPost('/api/agent', payload),
|
2026-01-22 05:57:13 +08:00
|
|
|
|
|
2026-05-24 00:47:08 +08:00
|
|
|
|
updateAgent: (agentId, payload) => apiPut(`/api/agent/${agentId}`, payload),
|
2026-01-22 05:57:13 +08:00
|
|
|
|
|
2026-05-24 00:47:08 +08:00
|
|
|
|
deleteAgent: (agentId) => apiDelete(`/api/agent/${agentId}`),
|
2025-08-24 22:32:37 +08:00
|
|
|
|
|
2026-02-25 16:26:09 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建异步运行任务(Run)
|
|
|
|
|
|
* @param {Object} data - run 请求体
|
|
|
|
|
|
* @returns {Promise<Object>}
|
|
|
|
|
|
*/
|
2026-03-26 23:13:44 +08:00
|
|
|
|
createAgentRun: (data) =>
|
2026-05-24 00:47:08 +08:00
|
|
|
|
apiPost('/api/agent/runs', {
|
2026-02-25 16:26:09 +08:00
|
|
|
|
query: data.query,
|
2026-05-24 00:47:08 +08:00
|
|
|
|
agent_id: data.agent_id,
|
2026-03-26 23:13:44 +08:00
|
|
|
|
thread_id: data.thread_id,
|
|
|
|
|
|
meta: data.meta || {},
|
2026-05-28 12:53:29 +08:00
|
|
|
|
image_content: data.image_content || null,
|
|
|
|
|
|
resume: data.resume ?? null,
|
|
|
|
|
|
parent_run_id: data.parent_run_id || null,
|
|
|
|
|
|
resume_request_id: data.resume_request_id || null
|
2026-02-25 16:26:09 +08:00
|
|
|
|
}),
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取 Run 状态
|
|
|
|
|
|
* @param {string} runId - run ID
|
|
|
|
|
|
* @returns {Promise<Object>}
|
|
|
|
|
|
*/
|
2026-05-24 00:47:08 +08:00
|
|
|
|
getAgentRun: (runId) => apiGet(`/api/agent/runs/${runId}`),
|
2026-02-25 16:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消 Run
|
|
|
|
|
|
* @param {string} runId - run ID
|
|
|
|
|
|
* @returns {Promise<Object>}
|
|
|
|
|
|
*/
|
2026-05-24 00:47:08 +08:00
|
|
|
|
cancelAgentRun: (runId) => apiPost(`/api/agent/runs/${runId}/cancel`, {}),
|
2026-02-25 16:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取线程活跃 Run
|
|
|
|
|
|
* @param {string} threadId - 线程ID
|
|
|
|
|
|
* @returns {Promise<Object>}
|
|
|
|
|
|
*/
|
2026-05-24 00:47:08 +08:00
|
|
|
|
getThreadActiveRun: (threadId) => apiGet(`/api/agent/thread/${threadId}/active_run`),
|
2026-02-25 16:26:09 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 打开 Run 事件 SSE 连接(调用方负责关闭)
|
|
|
|
|
|
* @param {string} runId - run ID
|
2026-05-19 18:37:29 +08:00
|
|
|
|
* @param {string} afterSeq - 起始 seq/cursor
|
2026-02-25 16:26:09 +08:00
|
|
|
|
* @param {Object} options - { signal }
|
|
|
|
|
|
* @returns {Promise<Response>}
|
|
|
|
|
|
*/
|
2026-05-19 18:37:29 +08:00
|
|
|
|
streamAgentRunEvents: (runId, afterSeq = '0-0', options = {}) => {
|
2026-02-25 16:26:09 +08:00
|
|
|
|
const { signal } = options
|
2026-05-28 12:53:29 +08:00
|
|
|
|
const headers = {
|
|
|
|
|
|
...useUserStore().getAuthHeaders()
|
|
|
|
|
|
}
|
|
|
|
|
|
const cursor = String(afterSeq || '0-0')
|
|
|
|
|
|
if (cursor && cursor !== '0-0') {
|
|
|
|
|
|
headers['Last-Event-ID'] = cursor
|
|
|
|
|
|
}
|
|
|
|
|
|
return fetch(`/api/agent/runs/${runId}/events`, {
|
|
|
|
|
|
method: 'GET',
|
|
|
|
|
|
headers,
|
|
|
|
|
|
signal
|
|
|
|
|
|
})
|
2025-11-01 21:34:16 +08:00
|
|
|
|
}
|
2025-05-02 23:56:59 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-12 14:04:34 +08:00
|
|
|
|
// =============================================================================
|
|
|
|
|
|
// === 多模态图片支持分组 ===
|
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
export const multimodalApi = {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 上传图片并获取base64编码
|
|
|
|
|
|
* @param {File} file - 图片文件
|
|
|
|
|
|
* @returns {Promise} - 上传结果
|
|
|
|
|
|
*/
|
|
|
|
|
|
uploadImage: (file) => {
|
2026-01-15 06:01:34 +08:00
|
|
|
|
const formData = new FormData()
|
|
|
|
|
|
formData.append('file', file)
|
2025-11-12 14:04:34 +08:00
|
|
|
|
|
2026-01-15 06:01:34 +08:00
|
|
|
|
return apiRequest(
|
|
|
|
|
|
'/api/chat/image/upload',
|
|
|
|
|
|
{
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
body: formData
|
|
|
|
|
|
},
|
|
|
|
|
|
true
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-12 14:04:34 +08:00
|
|
|
|
|
2025-08-24 22:32:37 +08:00
|
|
|
|
// =============================================================================
|
|
|
|
|
|
// === 对话线程分组 ===
|
|
|
|
|
|
// =============================================================================
|
|
|
|
|
|
|
2025-05-15 22:34:32 +08:00
|
|
|
|
export const threadApi = {
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取对话线程列表
|
2026-03-16 21:39:49 +08:00
|
|
|
|
* @param {string | null | undefined} agentId - 智能体ID,可选;不传时返回全部智能体对话
|
2026-03-06 10:14:43 +08:00
|
|
|
|
* @param {number} limit - 返回数量限制,默认100
|
|
|
|
|
|
* @param {number} offset - 偏移量,默认0
|
2025-05-15 22:34:32 +08:00
|
|
|
|
* @returns {Promise} - 对话线程列表
|
|
|
|
|
|
*/
|
2026-03-16 21:39:49 +08:00
|
|
|
|
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()}`
|
2026-01-15 06:01:34 +08:00
|
|
|
|
return apiGet(url)
|
2025-05-15 22:34:32 +08:00
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 创建新对话线程
|
|
|
|
|
|
* @param {string} agentId - 智能体ID
|
|
|
|
|
|
* @param {string} title - 对话标题
|
|
|
|
|
|
* @param {Object} metadata - 元数据
|
|
|
|
|
|
* @returns {Promise} - 创建结果
|
|
|
|
|
|
*/
|
2026-01-15 06:01:34 +08:00
|
|
|
|
createThread: (agentId, title, metadata) =>
|
|
|
|
|
|
apiPost('/api/chat/thread', {
|
|
|
|
|
|
agent_id: agentId,
|
|
|
|
|
|
title: title || '新的对话',
|
|
|
|
|
|
metadata: metadata || {}
|
|
|
|
|
|
}),
|
2025-05-15 22:34:32 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新对话线程
|
|
|
|
|
|
* @param {string} threadId - 对话线程ID
|
|
|
|
|
|
* @param {string} title - 对话标题
|
2026-03-06 20:29:58 +08:00
|
|
|
|
* @param {boolean} is_pinned - 是否置顶
|
2025-05-15 22:34:32 +08:00
|
|
|
|
* @returns {Promise} - 更新结果
|
|
|
|
|
|
*/
|
2026-03-06 20:29:58 +08:00
|
|
|
|
updateThread: (threadId, title, is_pinned) =>
|
2026-01-15 06:01:34 +08:00
|
|
|
|
apiPut(`/api/chat/thread/${threadId}`, {
|
|
|
|
|
|
title,
|
2026-03-06 20:29:58 +08:00
|
|
|
|
is_pinned
|
2026-01-15 06:01:34 +08:00
|
|
|
|
}),
|
2025-05-15 22:34:32 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 删除对话线程
|
|
|
|
|
|
* @param {string} threadId - 对话线程ID
|
|
|
|
|
|
* @returns {Promise} - 删除结果
|
|
|
|
|
|
*/
|
2025-11-08 10:51:30 +08:00
|
|
|
|
deleteThread: (threadId) => apiDelete(`/api/chat/thread/${threadId}`),
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取线程附件列表
|
|
|
|
|
|
* @param {string} threadId - 对话线程ID
|
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
|
*/
|
|
|
|
|
|
getThreadAttachments: (threadId) => apiGet(`/api/chat/thread/${threadId}/attachments`),
|
|
|
|
|
|
|
2026-03-04 08:53:50 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 列出线程文件(目录)
|
|
|
|
|
|
* @param {string} threadId
|
|
|
|
|
|
* @param {string} path
|
2026-03-14 23:54:52 +08:00
|
|
|
|
* @param {boolean} recursive
|
2026-03-04 08:53:50 +08:00
|
|
|
|
* @returns {Promise}
|
|
|
|
|
|
*/
|
2026-03-26 03:29:34 +08:00
|
|
|
|
listThreadFiles: (threadId, path = '/home/gem/user-data', recursive = false) =>
|
2026-03-20 21:18:13 +08:00
|
|
|
|
apiGet(
|
|
|
|
|
|
`/api/chat/thread/${threadId}/files?path=${encodeURIComponent(path)}&recursive=${recursive}`
|
|
|
|
|
|
),
|
2026-03-04 08:53:50 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 读取线程文本文件内容(分页)
|
|
|
|
|
|
* @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}`
|
|
|
|
|
|
},
|
|
|
|
|
|
|
2026-03-05 22:50:38 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 下载线程文件(带鉴权)
|
|
|
|
|
|
* @param {string} threadId
|
|
|
|
|
|
* @param {string} path
|
|
|
|
|
|
* @returns {Promise<Response>}
|
|
|
|
|
|
*/
|
|
|
|
|
|
downloadThreadArtifact: (threadId, path) =>
|
|
|
|
|
|
apiGet(threadApi.getThreadArtifactUrl(threadId, path, true), {}, true, 'blob'),
|
|
|
|
|
|
|
2026-03-31 20:18:22 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 保存交付物到 workspace/saved_artifacts
|
|
|
|
|
|
* @param {string} threadId
|
|
|
|
|
|
* @param {string} path
|
|
|
|
|
|
* @returns {Promise}
|
|
|
|
|
|
*/
|
|
|
|
|
|
saveThreadArtifactToWorkspace: (threadId, path) =>
|
|
|
|
|
|
apiPost(`/api/chat/thread/${threadId}/artifacts/save`, { path }),
|
|
|
|
|
|
|
2026-05-27 18:22:29 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 上传临时附件
|
|
|
|
|
|
* @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 }),
|
|
|
|
|
|
|
2025-11-08 10:51:30 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 上传附件
|
|
|
|
|
|
* @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}
|
|
|
|
|
|
*/
|
2026-01-15 06:01:34 +08:00
|
|
|
|
deleteThreadAttachment: (threadId, fileId) =>
|
|
|
|
|
|
apiDelete(`/api/chat/thread/${threadId}/attachments/${fileId}`)
|
|
|
|
|
|
}
|