// static/js/api.js /* * 通用的API请求函数。 * @param { string } endpoint API端点,不包含 / api前缀。 * @param { string } method HTTP方法,如'GET', 'POST', 'DELETE'。 * @param { Object } [data = null] 请求体数据,GET请求时转换为查询参数。 * @param { string } [baseUrl = ''] 可选的基础URL,如果提供则覆盖默认的API_BASE_URL。 * @returns { Promise < Object >} API响应数据。 */ export async function apiRequest(endpoint, method = 'GET', data = null, baseUrl = '') { let url; if (baseUrl && (baseUrl.startsWith('http://') || baseUrl.startsWith('https://'))) { // 如果baseUrl是完整的URL,则直接拼接endpoint url = `${baseUrl}${endpoint}`; } else { // 否则,使用默认的 /api 前缀或提供的相对baseUrl url = `${baseUrl || '/api'}${endpoint}`; } const options = { method: method, headers: { 'Content-Type': 'application/json', }, }; if (method === 'GET' && data) { const query = new URLSearchParams(data).toString(); url = `${url}?${query}`; } else if (data) { options.body = JSON.stringify(data); } try { const response = await fetch(url, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: response.statusText })); throw new Error(errorData.error || errorData.message || `API请求失败: ${response.status}`); } // 根据Content-Type判断返回JSON还是文本 const contentType = response.headers.get('Content-Type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } else { // 假设是纯文本,例如日志文件 return await response.text(); } } catch (error) { console.error(`API请求 (${method} ${url}) 失败:`, error); throw error; // 重新抛出错误以便调用方处理 } } // --- 配置管理 API --- export async function fetchConfig() { return apiRequest('/config', 'GET'); } export async function saveConfig(configData) { return apiRequest('/config', 'POST', configData); } // --- 角色管理 API --- export async function fetchRoles() { return apiRequest('/roles', 'GET'); } export async function createRole(roleId, roleName) { return apiRequest('/roles', 'POST', { id: roleId, name: roleName }); } export async function deleteRole(roleId) { return apiRequest(`/roles/${roleId}`, 'DELETE'); } // --- 记忆管理 API --- export async function fetchMemories() { return apiRequest('/memories', 'GET'); } export async function createMemory(memoryId, memoryName) { return apiRequest('/memories', 'POST', { id: memoryId, name: memoryName }); } export async function deleteMemory(memoryId) { return apiRequest(`/memories/${memoryId}`, 'DELETE'); } // --- 会话管理 API --- export async function fetchActiveSession() { return apiRequest('/active_session', 'GET'); } export async function setActiveSession(roleId, memoryId) { return apiRequest('/active_session', 'POST', { role_id: roleId, memory_id: memoryId }); } // --- 特征内容 API --- export async function fetchFeaturesContent() { return apiRequest('/features_content', 'GET'); } export async function saveFeaturesContent(content) { return apiRequest('/features_content', 'POST', content); } // --- 记忆内容 API --- export async function fetchMemoryContent() { return apiRequest('/memory_content', 'GET'); } export async function saveMemoryContent(content) { return apiRequest('/memory_content', 'POST', content); } export async function triggerMemoryUpdate() { return apiRequest('/memory/trigger_update', 'POST'); } // --- 聊天与日志 API --- export async function sendMessage(message, useStream = false) { if (!useStream) { // 使用标准响应方式 return apiRequest('/chat', 'POST', { message: message }); } else { // 使用流式响应方式 const url = '/api/chat?stream=true'; const options = { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ message: message }), }; try { const response = await fetch(url, options); if (!response.ok) { const errorData = await response.json().catch(() => ({ message: response.statusText })); throw new Error(errorData.error || errorData.message || `API请求失败: ${response.status}`); } // 检查是否返回了流 if (response.body) { const reader = response.body.getReader(); const decoder = new TextDecoder('utf-8'); // 创建一个更强健的处理SSE的异步迭代器 return { [Symbol.asyncIterator]() { let buffer = ''; return { async next() { try { // 读取新数据块 const { done, value } = await reader.read(); if (done) { console.log('流已结束'); // 处理buffer中剩余的数据 if (buffer.trim().length > 0) { console.log('处理buffer中剩余数据:', buffer); const finalValue = buffer; buffer = ''; return { done: false, value: finalValue }; } return { done: true, value: undefined }; } // 解码二进制数据并添加到缓冲区 buffer += decoder.decode(value, { stream: true }); // 检查是否有完整的SSE消息 (以"data: "开头的行) // 注意:SSE消息格式为 "data: {...}\n\n" const lines = buffer.split('\n\n'); // 如果没有完整的消息,继续读取 if (lines.length < 2) { return this.next(); } // 提取完整的消息并更新buffer const completeMessage = lines[0]; buffer = lines.slice(1).join('\n\n'); // 移除 "data: " 前缀并解析 JSON if (completeMessage.startsWith('data: ')) { try { const jsonString = completeMessage.substring(6); // 移除 "data: " const parsedData = JSON.parse(jsonString); if (parsedData.end) { // 如果收到结束标记,则流结束 console.log('收到流结束标记。'); return { done: true, value: undefined }; } else if (parsedData.chunk !== undefined) { // 返回 chunk 内容 console.log('解析并返回 chunk:', parsedData.chunk.substring(0, 50) + '...'); return { done: false, value: parsedData.chunk }; } } catch (parseError) { console.error('解析SSE数据失败:', parseError, '原始数据:', completeMessage); // 如果解析失败,可以返回原始数据抛出错误,这里选择返回原始数据 return { done: false, value: completeMessage }; } } // 如果不是有效的SSE数据行,继续读取 return this.next(); } catch (error) { console.error('读取流时出错:', error); return { done: true, value: undefined }; } } }; } }; } else { throw new Error('服务器未返回流响应'); } } catch (error) { console.error(`流式API请求 (POST ${url}) 失败:`, error); throw error; } } } export async function fetchChatLog(limit = null) { return apiRequest('/chat_log', 'GET', limit ? { limit: limit } : null); } export async function clearChatLog() { return apiRequest('/chat_log', 'DELETE'); } // --- 模型列表 API --- export async function fetchModels() { try { // 始终通过后端代理获取模型列表 const data = await apiRequest('/proxy_models', 'GET'); if (data && Array.isArray(data.models)) { return data.models.map(model => { if (typeof model === 'string') { return model; } else if (model && typeof model.name === 'string') { return model.name; } return null; }).filter(model => model !== null); } return []; } catch (error) { console.error("获取模型列表失败:", error); throw error; } } // --- 日志 API --- export async function fetchLogs() { return apiRequest('/logs', 'GET'); } console.log("api.js loaded. Type of fetchConfig:", typeof fetchConfig);