上传文件至 static
This commit is contained in:
265
static/api.js
Normal file
265
static/api.js
Normal file
@ -0,0 +1,265 @@
|
||||
// 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);
|
Reference in New Issue
Block a user