From 7a6ee83096f5b7509d64f0c78ef0a5def774cd40 Mon Sep 17 00:00:00 2001 From: Wx-2025 <351320169@qq.com> Date: Thu, 23 Oct 2025 22:31:22 +0800 Subject: [PATCH] Update api.js --- core/api.js | 1117 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 700 insertions(+), 417 deletions(-) diff --git a/core/api.js b/core/api.js index 2f41563..bec5333 100644 --- a/core/api.js +++ b/core/api.js @@ -1,474 +1,757 @@ import { extension_settings, getContext } from "/scripts/extensions.js"; -import { characters, eventSource, event_types } from "/script.js"; -import { loadWorldInfo, createNewWorldInfo, createWorldInfoEntry, saveWorldInfo, world_names } from "/scripts/world-info.js"; -import { compatibleWriteToLorebook, safeLorebooks, safeCharLorebooks, safeLorebookEntries } from "./tavernhelper-compatibility.js"; +import { characters } from "/script.js"; +import { world_names } from "/scripts/world-info.js"; import { extensionName } from "../utils/settings.js"; +import { extractContentByTag, replaceContentByTag, extractFullTagBlock } from '../utils/tagProcessor.js'; +import { + getCombinedWorldbookContent, + findLatestSummaryLore, + DEDICATED_LOREBOOK_NAME, + getChatIdentifier, +} from "./lore.js"; +import { compatibleTriggerSlash } from "./tavernhelper-compatibility.js"; + +import { + isGoogleEndpoint, + convertToGoogleRequest, + parseGoogleResponse, + buildGoogleApiUrl +} from '../core/utils/googleAdapter.js'; + +import { + intelligentPoll, + createGooglePollingTask, + progressTracker +} from '../core/utils/pollingManager.js'; + +import { + buildGoogleEmbeddingRequest, + parseGoogleEmbeddingResponse, + buildGoogleEmbeddingApiUrl +} from './utils/googleAdapter.js'; + +import { getRequestHeaders } from '/script.js'; -export const LOREBOOK_PREFIX = "Amily2档案-"; -export const DEDICATED_LOREBOOK_NAME = "Amily2号-国史馆"; -export const INTRODUCTORY_TEXT = - "【Amily2号自动档案】\n此卷宗由Amily2号优化助手自动生成并维护,记录核心事件脉络。\n---\n"; +let ChatCompletionService = undefined; +try { + const module = await import('/scripts/custom-request.js'); + ChatCompletionService = module.ChatCompletionService; + console.log('[Amily2号-外交部] 已成功召唤“皇家信使”(ChatCompletionService)。'); +} catch (e) { + console.warn("[Amily2号-外交部] 未能召唤“皇家信使”,部分高级功能(如Claw代理)将受限。请考虑更新SillyTavern版本。", e); +} + +const UPDATE_CHECK_URL = + "https://raw.githubusercontent.com/Wx-2025/ST-Amily2-Chat-Optimisation/refs/heads/main/amily2_update_info.json"; -export async function getChatIdentifier() { - let attempts = 0; - const maxAttempts = 50; - const interval = 100; - - while (attempts < maxAttempts) { +const MESSAGE_BOARD_URL = + "https://raw.githubusercontent.com/Wx-2025/ST-Amily2-Chat-Optimisation/refs/heads/main/amily2_message_board.json"; + +export async function fetchMessageBoardContent() { + if (!MESSAGE_BOARD_URL) { + console.log('[Amily2号-内务府] 任务取消:陛下尚未配置留言板URL。'); + return null; + } try { - const context = getContext(); - if (context && context.characterId) { - const character = characters[context.characterId]; - if (character && character.avatar) { - return `char-${character.avatar.replace(/\.(png|webp|jpg|jpeg|gif)$/, "")}`; + const response = await fetch(MESSAGE_BOARD_URL, { cache: 'no-store' }); + if (!response.ok) { + throw new Error(`服务器响应异常: ${response.status}`); } - return `char-${context.characterId}`; - } - if (context && context.chat_filename) { - const fileName = context.chat_filename.split(/[\\/]/).pop(); - return fileName.replace(/\.jsonl?$/, ""); - } + const data = await response.json(); + return data; } catch (error) { - console.warn( - `[Amily2-户籍管理处] 等待上下文时发生轻微错误 (尝试次数 ${attempts + 1}):`, - error.message, - ); + console.error('[Amily2号-内务府] 获取留言板内容失败:', error); + return null; } - await new Promise((resolve) => setTimeout(resolve, interval)); - attempts++; - } - - console.error("[Amily2-国史馆] 户籍管理处在长时间等待后,仍无法确定户籍。"); - toastr.warning( - "Amily2号无法确定当前聊天身份,世界书功能将受影响。", - "上下文错误", - ); - return "unknown_chat_timeout"; } - -export async function findLatestSummaryLore(lorebookName, chatIdentifier) { - try { - const bookData = await loadWorldInfo(lorebookName); - if (!bookData || !bookData.entries) { - return null; + +export async function checkForUpdates() { + if (!UPDATE_CHECK_URL || UPDATE_CHECK_URL.includes('YourUsername')) { + console.log('[Amily2号-外交部] 任务取消:陛下尚未配置情报来源URL。'); + return null; + } + + + try { + console.log('[Amily2号-外交部] 已派遣使者前往云端获取最新情报...'); + const response = await fetch(UPDATE_CHECK_URL, { + method: 'GET', + cache: 'no-store', + mode: 'cors' + }); + + + + if (!response.ok) { + throw new Error(`远方服务器响应异常,状态: ${response.status}`); + } + + const data = await response.json(); + console.log('[Amily2号-外交部] 情报已成功获取并解析。'); + return data; + + } catch (error) { + console.error('[Amily2号-外交部] 紧急军情:外交任务失败!', error); + return null; } - const entriesArray = Object.values(bookData.entries); - const uniqueLoreName = `${LOREBOOK_PREFIX}${chatIdentifier}`; - return ( - entriesArray.find( - (entry) => entry.comment === uniqueLoreName && !entry.disable, - ) || null - ); - } catch (error) { - console.error( - `[Amily2-国史馆] 钦差大臣在 '${lorebookName}' 检索时发生错误:`, - error, - ); - return null; - } } - -export async function getCombinedWorldbookContent(lorebookName) { - if (!lorebookName) return ""; - try { - const bookData = await loadWorldInfo(lorebookName); - if (!bookData || !bookData.entries) { - return ""; - } - const activeContents = Object.values(bookData.entries) - .filter((entry) => !entry.disable) - .map((entry) => `[条目: ${entry.comment || "无标题"}]\n${entry.content}`); - return activeContents.join("\n\n---\n\n"); - } catch (error) { - console.error( - `[Amily2-国史馆] 钦差大臣在整合 '${lorebookName}' 时发生错误:`, - error, - ); - toastr.error(`读取世界书 '${lorebookName}' 失败!`, "档案整合错误"); - return ""; - } -} - -async function refreshWorldbookListOnly(newBookName = null) { - console.log("[Amily2号-工部-v1.3] 执行“圣谕广播”式UI更新..."); - try { - if (newBookName) { - if (Array.isArray(world_names) && !world_names.includes(newBookName)) { - world_names.push(newBookName); - world_names.sort(); - console.log(`[Amily2号-工部] 已将《${newBookName}》注入前端数据模型。`); - } else { - console.log(`[Amily2号-工部] 《${newBookName}》已存在于数据模型中,跳过注入。`); - } - } - - if ( - eventSource && - typeof eventSource.emit === "function" && - event_types.CHARACTER_PAGE_LOADED - ) { - console.log(`[Amily2号-工部] 正在广播事件: ${event_types.CHARACTER_PAGE_LOADED}`); - eventSource.emit(event_types.CHARACTER_PAGE_LOADED); - console.log("[Amily2号-工部] “character_page_loaded”事件已广播,UI应已响应刷新。"); - } else { - console.error("[Amily2号] 致命错误: eventSource 或 event_types.CHARACTER_PAGE_LOADED 未找到。无法广播刷新事件。"); - toastr.error("Amily2号无法触发UI刷新。", "核心事件系统缺失"); - } - } catch (error) { - console.error("[Amily2号-工部] “圣谕广播”式刷新失败:", error); - } -} - -export async function writeSummaryToLorebook(pendingData) { - if (!pendingData || !pendingData.summary || !pendingData.sourceAiMessageTimestamp || !pendingData.settings) { - console.warn("[Amily助手-国史馆] 接到一份残缺的待办文书,写入任务已中止。", pendingData); - return; - } - - const context = getContext(); - const chat = context.chat; - let isSourceMessageValid = false; - let sourceMessageCandidate = null; - // 寻找最新的 AI 消息以进行时间戳验证 - for (let i = chat.length - 1; i >= 0; i--) { - if (!chat[i].is_user) { - sourceMessageCandidate = chat[i]; - break; + +function normalizeApiResponse(responseData) { + let data = responseData; + if (typeof data === 'string') { + try { + data = JSON.parse(data); + } catch (e) { + console.error(`[${extensionName}] API响应JSON解析失败:`, e); + return { error: { message: 'Invalid JSON response' } }; } } + if (data && typeof data.data === 'object' && data.data !== null && !Array.isArray(data.data)) { + if (Object.hasOwn(data.data, 'data')) { + data = data.data; + } + } + if (data && data.choices && data.choices[0]) { + return { content: data.choices[0].message?.content?.trim() }; + } + if (data && data.content) { + return { content: data.content.trim() }; + } + if (data && data.data) { + return { data: data.data }; + } + if (data && data.error) { + return { error: data.error }; + } + return data; +} - if (sourceMessageCandidate && sourceMessageCandidate.send_date === pendingData.sourceAiMessageTimestamp) { - isSourceMessageValid = true; + +export async function fetchModels() { + if (window.AMILY2_LOCK_MODEL_FETCHING) { + console.warn("[Amily2号-使节团] 上次任务尚未完成,本次任务取消。"); + toastr.info("上次任务尚未完成,请稍后再试。", "任务排队中"); + return []; } - if (!isSourceMessageValid) { - console.log("[Amily助手-逆时寻踪] 裁决: 源消息已被修改或删除,遵旨废黜过时总结。"); - return; - } - - const { summary: summaryToCommit, settings } = pendingData; - - console.groupCollapsed(`[Amily助手-存档任务] ${new Date().toLocaleTimeString()}`); - console.time("总结写入总耗时"); + window.AMILY2_LOCK_MODEL_FETCHING = true; try { - const chatIdentifier = await getChatIdentifier(); - const character = characters[context.characterId]; - let targetLorebookName = null; + const apiProvider = $("#amily2_api_provider").val() || 'openai'; + const apiUrl = $("#amily2_api_url").val().trim(); + const apiKey = $("#amily2_api_key").val().trim(); + const $button = $("#amily2_refresh_models"); + const $selector = $("#amily2_model"); - switch (settings.target) { - case "character_main": - targetLorebookName = character?.data?.extensions?.world; - if (!targetLorebookName) { - toastr.warning("角色未绑定主世界书,总结写入任务已中止。", "Amily助手"); - console.groupEnd(); - return; - } + console.log(`[Amily2号-使节团] 使用 API 提供商: ${apiProvider}`); + + $button.prop("disabled", true).html(' 加载中'); + $selector.empty().append($('