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($('