From c16413e71ab0b13eb21d4c9148f69915e5d3012b Mon Sep 17 00:00:00 2001 From: Wx-2025 <351320169@qq.com> Date: Thu, 4 Sep 2025 16:47:14 +0800 Subject: [PATCH] Update cwb_apiService.js --- CharacterWorldBook/src/cwb_apiService.js | 352 +++++++++++++++-------- 1 file changed, 237 insertions(+), 115 deletions(-) diff --git a/CharacterWorldBook/src/cwb_apiService.js b/CharacterWorldBook/src/cwb_apiService.js index bf4192f..6c995fa 100644 --- a/CharacterWorldBook/src/cwb_apiService.js +++ b/CharacterWorldBook/src/cwb_apiService.js @@ -123,37 +123,134 @@ async function callCwbSillyTavernPreset(messages, options) { } async function callCwbOpenAITest(messages, options) { - const response = await fetch('/api/backends/chat-completions/generate', { - method: 'POST', - headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' }, - body: JSON.stringify({ - chat_completion_source: 'openai', - custom_prompt_post_processing: 'strict', - enable_web_search: false, - frequency_penalty: 0, - group_names: [], - include_reasoning: false, - max_tokens: options.maxTokens || 65000, - messages: messages, - model: options.model, - presence_penalty: 0.12, - proxy_password: options.apiKey, - reasoning_effort: 'medium', - request_images: false, - reverse_proxy: options.apiUrl, - stream: false, - temperature: options.temperature || 1, - top_p: options.top_p || 1 - }) - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`CWB全兼容API请求失败: ${response.status} - ${errorText}`); + // 参数验证 + if (!Array.isArray(messages) || messages.length === 0) { + throw new Error('消息数组不能为空'); + } + + if (!options?.apiUrl?.trim()) { + throw new Error('API URL 不能为空'); } - const responseData = await response.json(); - return responseData?.choices?.[0]?.message?.content; + if (!options?.model?.trim()) { + throw new Error('模型名称不能为空'); + } + + // 确保所有必需的参数都存在且有效 + const validatedOptions = { + maxTokens: Math.max(1, parseInt(options.maxTokens) || 65000), + temperature: Math.max(0, Math.min(2, parseFloat(options.temperature) || 1)), + top_p: Math.max(0, Math.min(1, parseFloat(options.top_p) || 1)), + apiUrl: options.apiUrl.trim(), + apiKey: (options.apiKey || '').trim(), + model: options.model.trim() + }; + + // 验证消息格式 + const validatedMessages = messages.map((msg, index) => { + if (!msg || typeof msg !== 'object') { + throw new Error(`消息 ${index} 格式无效`); + } + if (!msg.role || !['system', 'user', 'assistant'].includes(msg.role)) { + throw new Error(`消息 ${index} 的角色无效`); + } + if (!msg.content || typeof msg.content !== 'string') { + throw new Error(`消息 ${index} 的内容无效`); + } + return { + role: msg.role, + content: msg.content.trim() + }; + }); + + const requestBody = { + chat_completion_source: 'openai', + custom_prompt_post_processing: 'strict', + enable_web_search: false, + frequency_penalty: 0, + group_names: [], + include_reasoning: false, + max_tokens: validatedOptions.maxTokens, + messages: validatedMessages, + model: validatedOptions.model, + presence_penalty: 0.12, + proxy_password: validatedOptions.apiKey, + reasoning_effort: 'medium', + request_images: false, + reverse_proxy: validatedOptions.apiUrl, + stream: false, + temperature: validatedOptions.temperature, + top_p: validatedOptions.top_p + }; + + try { + const response = await fetch('/api/backends/chat-completions/generate', { + method: 'POST', + headers: { + ...getRequestHeaders(), + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }, + body: JSON.stringify(requestBody) + }); + + if (!response.ok) { + let errorText; + try { + errorText = await response.text(); + } catch (e) { + errorText = '无法读取错误响应'; + } + + // 根据HTTP状态码提供更具体的错误信息 + let errorMessage = `CWB OpenAI Test API请求失败 (${response.status})`; + if (response.status === 400) { + errorMessage += ': 请求格式错误,请检查参数配置'; + } else if (response.status === 401) { + errorMessage += ': 认证失败,请检查API密钥'; + } else if (response.status === 403) { + errorMessage += ': 访问被拒绝,请检查权限设置'; + } else if (response.status === 429) { + errorMessage += ': 请求频率超限,请稍后重试'; + } else if (response.status >= 500) { + errorMessage += ': 服务器错误,请稍后重试'; + } + errorMessage += errorText ? ` - ${errorText}` : ''; + + throw new Error(errorMessage); + } + + let responseData; + try { + responseData = await response.json(); + } catch (e) { + throw new Error('API返回的响应不是有效的JSON格式'); + } + + // 使用标准化响应处理 + const normalizedResponse = normalizeApiResponse(responseData); + + if (normalizedResponse.error) { + throw new Error(normalizedResponse.error.message || 'API返回错误响应'); + } + + if (normalizedResponse.content) { + return normalizedResponse.content; + } + + // 兼容直接响应格式 + if (responseData?.choices?.[0]?.message?.content) { + return responseData.choices[0].message.content.trim(); + } + + throw new Error('API响应格式不正确或未包含有效内容'); + + } catch (error) { + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error('网络连接失败,请检查网络状态'); + } + throw error; + } } export async function callCwbAPI(systemPrompt, userPromptContent, options = {}) { @@ -281,21 +378,24 @@ export async function loadModels($panel) { } const rawData = await response.json(); - const result = normalizeApiResponse(rawData); - const modelList = result.data || []; + const modelList = Array.isArray(rawData) ? rawData : (rawData.data || rawData.models || []); - if (result.error || !Array.isArray(modelList)) { - const errorMessage = result.error?.message || 'API未返回有效的模型列表数组'; + if (!Array.isArray(modelList)) { + const errorMessage = 'API未返回有效的模型列表数组'; throw new Error(errorMessage); } models = modelList - .map(m => ({ - id: m.id || m.model || m, - name: m.id || m.model || m - })) + .map(m => { + const modelIdRaw = m.name || m.id || m.model || m; + const modelName = String(modelIdRaw).replace(/^models\//, ''); + return { + id: modelName, + name: modelName + }; + }) .filter(m => m.id) - .sort((a, b) => a.name.localeCompare(b.name)); + .sort((a, b) => String(a.name).localeCompare(String(b.name))); } $modelSelect.empty(); @@ -316,6 +416,86 @@ export async function loadModels($panel) { } } +async function fetchCwbModels() { + console.log('[CWB] 开始获取模型列表'); + + const apiSettings = getCwbApiSettings(); + + try { + if (apiSettings.apiMode === 'sillytavern_preset') { + const context = getContext(); + if (!context?.extensionSettings?.connectionManager?.profiles) { + throw new Error('无法获取SillyTavern配置文件列表'); + } + + const targetProfile = context.extensionSettings.connectionManager.profiles.find(p => p.id === apiSettings.tavernProfile); + if (!targetProfile) { + throw new Error(`未找到配置文件ID: ${apiSettings.tavernProfile}`); + } + + const models = []; + if (targetProfile.openai_model) { + models.push({ id: targetProfile.openai_model, name: targetProfile.openai_model }); + } + + if (models.length === 0) { + throw new Error('当前预设未配置模型'); + } + + console.log('[CWB] SillyTavern预设模式获取到模型:', models); + return models; + + } else { + if (!apiSettings.apiUrl || !apiSettings.apiKey) { + throw new Error('API URL或Key未配置'); + } + + const response = await fetch('/api/backends/chat-completions/status', { + method: 'POST', + headers: { + ...getRequestHeaders(), + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + reverse_proxy: apiSettings.apiUrl, + proxy_password: apiSettings.apiKey, + chat_completion_source: 'openai' + }) + }); + + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + const rawData = await response.json(); + const models = Array.isArray(rawData) ? rawData : (rawData.data || rawData.models || []); + + if (!Array.isArray(models)) { + const errorMessage = 'API未返回有效的模型列表数组'; + throw new Error(errorMessage); + } + + const formattedModels = models + .map(m => { + const modelIdRaw = m.name || m.id || m.model || m; + const modelName = String(modelIdRaw).replace(/^models\//, ''); + return { + id: modelName, + name: modelName + }; + }) + .filter(m => m.id) + .sort((a, b) => String(a.name).localeCompare(String(b.name))); + + console.log('[CWB] 全兼容模式获取到模型:', formattedModels); + return formattedModels; + } + } catch (error) { + console.error('[CWB] 获取模型列表失败:', error); + throw error; + } +} + export async function fetchModelsAndConnect($panel) { const apiSettings = getCwbApiSettings(); const $modelSelect = $panel.find('#cwb-api-model'); @@ -327,94 +507,36 @@ export async function fetchModelsAndConnect($panel) { $apiStatus.text('状态: 请先选择SillyTavern预设').css('color', 'orange'); return; } - - $apiStatus.text('状态: 正在从预设获取模型...').css('color', '#61afef'); - showToastr('info', '正在从预设获取模型...'); - - try { - const context = getContext(); - if (!context?.extensionSettings?.connectionManager?.profiles) { - throw new Error('无法获取SillyTavern配置文件列表'); - } - - const targetProfile = context.extensionSettings.connectionManager.profiles.find(p => p.id === apiSettings.tavernProfile); - if (!targetProfile) { - throw new Error(`未找到配置文件ID: ${apiSettings.tavernProfile}`); - } - - $modelSelect.empty(); - if (targetProfile.openai_model) { - $modelSelect.append(jQuery('