diff --git a/.gitignore b/.gitignore
index e69de29..98d3247 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,2 @@
+WorkDiary.md
+Structure.md
diff --git a/core/api.js b/core/api.js
index e409dee..c64b0b1 100644
--- a/core/api.js
+++ b/core/api.js
@@ -1,5 +1,6 @@
import { extension_settings, getContext } from "/scripts/extensions.js";
import { characters } from "/script.js";
+import { getSlotProfile } from './api/api-resolver.js';
import { world_names } from "/scripts/world-info.js";
import { extensionName } from "../utils/settings.js";
import { extractContentByTag, replaceContentByTag, extractFullTagBlock } from '../utils/tagProcessor.js';
@@ -433,28 +434,43 @@ async function fetchSillyTavernPresetModels() {
}
-export function getApiSettings() {
+export async function getApiSettings() {
+ // 优先读取 'main' 槽位分配的 Profile
+ const profile = await getSlotProfile('main');
+ if (profile) {
+ return {
+ apiProvider: profile.provider,
+ apiUrl: profile.apiUrl,
+ apiKey: profile.apiKey ?? '',
+ model: profile.model,
+ maxTokens: profile.maxTokens ?? 65500,
+ temperature: profile.temperature ?? 1.0,
+ tavernProfile: '',
+ };
+ }
+
+ // 降级:读旧 DOM 面板配置
const settings = extension_settings[extensionName] || {};
const apiProvider = document.getElementById('amily2_api_provider')?.value || 'openai';
-
+
let model;
if (apiProvider === 'sillytavern_preset') {
const context = getContext();
const profileId = document.getElementById('amily2_preset_selector')?.value;
- const profile = context.extensionSettings?.connectionManager?.profiles?.find(p => p.id === profileId);
- model = profile?.openai_model || 'Preset Model';
+ const stProfile = context.extensionSettings?.connectionManager?.profiles?.find(p => p.id === profileId);
+ model = stProfile?.openai_model || 'Preset Model';
} else {
model = document.getElementById('amily2_model')?.value;
}
return {
- apiProvider: apiProvider,
- apiUrl: document.getElementById('amily2_api_url')?.value.trim() || '',
- apiKey: document.getElementById('amily2_api_key')?.value.trim() || '',
- model: model,
- maxTokens: settings.maxTokens || 4000,
- temperature: settings.temperature || 0.7,
- tavernProfile: document.getElementById('amily2_preset_selector')?.value || ''
+ apiProvider,
+ apiUrl: document.getElementById('amily2_api_url')?.value.trim() || '',
+ apiKey: document.getElementById('amily2_api_key')?.value.trim() || '',
+ model,
+ maxTokens: settings.maxTokens || 4000,
+ temperature: settings.temperature || 0.7,
+ tavernProfile: document.getElementById('amily2_preset_selector')?.value || '',
};
}
@@ -468,8 +484,8 @@ export async function testApiConnection() {
$button.prop("disabled", true).html(' 测试中');
try {
- const apiSettings = getApiSettings();
-
+ const apiSettings = await getApiSettings();
+
if (apiSettings.apiProvider === 'sillytavern_preset') {
if (!apiSettings.tavernProfile) {
throw new Error("请先在下方选择一个SillyTavern预设");
@@ -518,7 +534,7 @@ export async function callAI(messages, options = {}) {
return null;
}
- const apiSettings = getApiSettings();
+ const apiSettings = await getApiSettings();
const finalOptions = {
maxTokens: apiSettings.maxTokens,
diff --git a/core/api/JqyhApi.js b/core/api/JqyhApi.js
index 9a4fb34..fbda8d6 100644
--- a/core/api/JqyhApi.js
+++ b/core/api/JqyhApi.js
@@ -2,6 +2,7 @@ import { extension_settings, getContext } from "/scripts/extensions.js";
import { characters, this_chid, getRequestHeaders, saveSettingsDebounced, eventSource, event_types } from "/script.js";
import { extensionName } from "../../utils/settings.js";
import { amilyHelper } from '../../core/tavern-helper/main.js';
+import { getSlotProfile, providerToApiMode } from './api-resolver.js';
let ChatCompletionService = undefined;
try {
@@ -42,15 +43,30 @@ function normalizeApiResponse(responseData) {
return data;
}
-export function getJqyhApiSettings() {
+export async function getJqyhApiSettings() {
+ // 优先读取 'jqyh' 槽位分配的 Profile
+ const profile = await getSlotProfile('jqyh');
+ if (profile) {
+ return {
+ apiMode: providerToApiMode(profile.provider),
+ apiUrl: profile.apiUrl,
+ apiKey: profile.apiKey ?? '',
+ model: profile.model,
+ maxTokens: profile.maxTokens ?? 65500,
+ temperature: profile.temperature ?? 1.0,
+ tavernProfile: '',
+ };
+ }
+
+ // 降级:读旧 extension_settings 字段
return {
- apiMode: extension_settings[extensionName]?.jqyhApiMode || 'openai_test',
- apiUrl: extension_settings[extensionName]?.jqyhApiUrl?.trim() || '',
- apiKey: extension_settings[extensionName]?.jqyhApiKey?.trim() || '',
- model: extension_settings[extensionName]?.jqyhModel || '',
- maxTokens: extension_settings[extensionName]?.jqyhMaxTokens || 4000,
- temperature: extension_settings[extensionName]?.jqyhTemperature || 0.7,
- tavernProfile: extension_settings[extensionName]?.jqyhTavernProfile || ''
+ apiMode: extension_settings[extensionName]?.jqyhApiMode || 'openai_test',
+ apiUrl: extension_settings[extensionName]?.jqyhApiUrl?.trim() || '',
+ apiKey: extension_settings[extensionName]?.jqyhApiKey?.trim() || '',
+ model: extension_settings[extensionName]?.jqyhModel || '',
+ maxTokens: extension_settings[extensionName]?.jqyhMaxTokens || 4000,
+ temperature: extension_settings[extensionName]?.jqyhTemperature || 0.7,
+ tavernProfile: extension_settings[extensionName]?.jqyhTavernProfile || '',
};
}
@@ -60,7 +76,7 @@ export async function callJqyhAI(messages, options = {}) {
return null;
}
- const apiSettings = getJqyhApiSettings();
+ const apiSettings = await getJqyhApiSettings();
const finalOptions = {
maxTokens: apiSettings.maxTokens,
@@ -258,7 +274,7 @@ async function callJqyhSillyTavernPreset(messages, options) {
export async function fetchJqyhModels() {
console.log('[Amily2号-Jqyh外交部] 开始获取模型列表');
- const apiSettings = getJqyhApiSettings();
+ const apiSettings = await getJqyhApiSettings();
try {
if (apiSettings.apiMode === 'sillytavern_preset') {
@@ -339,7 +355,7 @@ export async function fetchJqyhModels() {
export async function testJqyhApiConnection() {
console.log('[Amily2号-Jqyh外交部] 开始API连接测试');
- const apiSettings = getJqyhApiSettings();
+ const apiSettings = await getJqyhApiSettings();
if (apiSettings.apiMode === 'sillytavern_preset') {
if (!apiSettings.tavernProfile) {
diff --git a/core/api/NccsApi.js b/core/api/NccsApi.js
index b044607..408ac1a 100644
--- a/core/api/NccsApi.js
+++ b/core/api/NccsApi.js
@@ -2,6 +2,7 @@ import { extension_settings, getContext } from "/scripts/extensions.js";
import { characters, this_chid, getRequestHeaders, saveSettingsDebounced, eventSource, event_types } from "/script.js";
import { extensionName } from "../../utils/settings.js";
import { amilyHelper } from '../../core/tavern-helper/main.js';
+import { getSlotProfile, providerToApiMode } from './api-resolver.js';
let ChatCompletionService = undefined;
try {
@@ -36,17 +37,34 @@ if (window.Amily2Bus) {
toastr.error("核心组件 Amily2Bus 丢失,请检查安装。", "Nccs-System");
}
-export function getNccsApiSettings() {
+export async function getNccsApiSettings() {
+ // 优先读取 'nccs' 槽位分配的 Profile
+ const profile = await getSlotProfile('nccs');
+ if (profile) {
+ return {
+ nccsEnabled: true,
+ apiMode: providerToApiMode(profile.provider),
+ apiUrl: profile.apiUrl,
+ apiKey: profile.apiKey ?? '',
+ model: profile.model,
+ maxTokens: profile.maxTokens ?? 65500,
+ temperature: profile.temperature ?? 1.0,
+ tavernProfile: '',
+ useFakeStream: false,
+ };
+ }
+
+ // 降级:读旧 extension_settings 字段
return {
- nccsEnabled: extension_settings[extensionName]?.nccsEnabled || false,
- apiMode: extension_settings[extensionName]?.nccsApiMode || 'openai_test',
- apiUrl: extension_settings[extensionName]?.nccsApiUrl?.trim() || '',
- apiKey: extension_settings[extensionName]?.nccsApiKey?.trim() || '',
- model: extension_settings[extensionName]?.nccsModel || '',
- maxTokens: extension_settings[extensionName]?.nccsMaxTokens || 4000,
- temperature: extension_settings[extensionName]?.nccsTemperature || 0.7,
+ nccsEnabled: extension_settings[extensionName]?.nccsEnabled || false,
+ apiMode: extension_settings[extensionName]?.nccsApiMode || 'openai_test',
+ apiUrl: extension_settings[extensionName]?.nccsApiUrl?.trim() || '',
+ apiKey: extension_settings[extensionName]?.nccsApiKey?.trim() || '',
+ model: extension_settings[extensionName]?.nccsModel || '',
+ maxTokens: extension_settings[extensionName]?.nccsMaxTokens || 4000,
+ temperature: extension_settings[extensionName]?.nccsTemperature || 0.7,
tavernProfile: extension_settings[extensionName]?.nccsTavernProfile || '',
- useFakeStream: extension_settings[extensionName]?.nccsFakeStreamEnabled || false
+ useFakeStream: extension_settings[extensionName]?.nccsFakeStreamEnabled || false,
};
}
@@ -60,7 +78,7 @@ export async function callNccsAI(messages, options = {}) {
return null;
}
- const settings = getNccsApiSettings();
+ const settings = await getNccsApiSettings();
const finalOptions = {
...settings,
...options
@@ -238,7 +256,7 @@ async function callNccsSillyTavernPreset(messages, options) {
export async function fetchNccsModels() {
console.log('[Amily2号-Nccs外交部] 开始获取模型列表');
- const apiSettings = getNccsApiSettings();
+ const apiSettings = await getNccsApiSettings();
try {
if (apiSettings.apiMode === 'sillytavern_preset') {
@@ -320,7 +338,7 @@ export async function fetchNccsModels() {
export async function testNccsApiConnection() {
console.log('[Amily2号-Nccs外交部] 开始API连接测试');
- const apiSettings = getNccsApiSettings();
+ const apiSettings = await getNccsApiSettings();
if (apiSettings.apiMode === 'sillytavern_preset') {
if (!apiSettings.tavernProfile) {
diff --git a/core/api/Ngms_api.js b/core/api/Ngms_api.js
index b6d3d99..b121738 100644
--- a/core/api/Ngms_api.js
+++ b/core/api/Ngms_api.js
@@ -2,6 +2,7 @@ import { extension_settings, getContext } from "/scripts/extensions.js";
import { characters, this_chid, getRequestHeaders, saveSettingsDebounced, eventSource, event_types } from "/script.js";
import { extensionName } from "../../utils/settings.js";
import { amilyHelper } from '../../core/tavern-helper/main.js';
+import { getSlotProfile, providerToApiMode } from './api-resolver.js';
let ChatCompletionService = undefined;
try {
@@ -42,16 +43,32 @@ function normalizeApiResponse(responseData) {
return data;
}
-export function getNgmsApiSettings() {
+export async function getNgmsApiSettings() {
+ // 优先读取 'ngms' 槽位分配的 Profile
+ const profile = await getSlotProfile('ngms');
+ if (profile) {
+ return {
+ apiMode: providerToApiMode(profile.provider),
+ apiUrl: profile.apiUrl,
+ apiKey: profile.apiKey ?? '',
+ model: profile.model,
+ maxTokens: profile.maxTokens ?? 65500,
+ temperature: profile.temperature ?? 1.0,
+ tavernProfile: '',
+ useFakeStream: false,
+ };
+ }
+
+ // 降级:读旧 extension_settings 字段
return {
- apiMode: extension_settings[extensionName]?.ngmsApiMode || 'openai_test',
- apiUrl: extension_settings[extensionName]?.ngmsApiUrl?.trim() || '',
- apiKey: extension_settings[extensionName]?.ngmsApiKey?.trim() || '',
- model: extension_settings[extensionName]?.ngmsModel || '',
- maxTokens: extension_settings[extensionName]?.ngmsMaxTokens || 4000,
- temperature: extension_settings[extensionName]?.ngmsTemperature || 0.7,
+ apiMode: extension_settings[extensionName]?.ngmsApiMode || 'openai_test',
+ apiUrl: extension_settings[extensionName]?.ngmsApiUrl?.trim() || '',
+ apiKey: extension_settings[extensionName]?.ngmsApiKey?.trim() || '',
+ model: extension_settings[extensionName]?.ngmsModel || '',
+ maxTokens: extension_settings[extensionName]?.ngmsMaxTokens || 4000,
+ temperature: extension_settings[extensionName]?.ngmsTemperature || 0.7,
tavernProfile: extension_settings[extensionName]?.ngmsTavernProfile || '',
- useFakeStream: extension_settings[extensionName]?.ngmsFakeStreamEnabled || false
+ useFakeStream: extension_settings[extensionName]?.ngmsFakeStreamEnabled || false,
};
}
@@ -61,7 +78,7 @@ export async function callNgmsAI(messages, options = {}) {
return null;
}
- const apiSettings = getNgmsApiSettings();
+ const apiSettings = await getNgmsApiSettings();
const finalOptions = {
maxTokens: apiSettings.maxTokens,
@@ -324,7 +341,7 @@ async function callNgmsSillyTavernPreset(messages, options) {
export async function fetchNgmsModels() {
console.log('[Amily2号-Ngms外交部] 开始获取模型列表');
- const apiSettings = getNgmsApiSettings();
+ const apiSettings = await getNgmsApiSettings();
try {
if (apiSettings.apiMode === 'sillytavern_preset') {
@@ -407,7 +424,7 @@ export async function fetchNgmsModels() {
export async function testNgmsApiConnection() {
console.log('[Amily2号-Ngms外交部] 开始API连接测试');
- const apiSettings = getNgmsApiSettings();
+ const apiSettings = await getNgmsApiSettings();
if (apiSettings.apiMode === 'sillytavern_preset') {
if (!apiSettings.tavernProfile) {
diff --git a/core/api/api-resolver.js b/core/api/api-resolver.js
new file mode 100644
index 0000000..d4c60b8
--- /dev/null
+++ b/core/api/api-resolver.js
@@ -0,0 +1,45 @@
+/**
+ * api-resolver.js — API 配置槽位解析器
+ *
+ * 职责:
+ * 优先从 ApiProfileManager 读取功能槽分配的 Profile(含解密 Key),
+ * 无分配时返回 null,由调用方执行旧配置兜底。
+ *
+ * 使用方式:
+ * const profile = await getSlotProfile('main');
+ * if (profile) { // 用 profile.provider / apiUrl / apiKey / model ... }
+ * else { // 回退到旧 DOM / extension_settings 读取 }
+ *
+ * provider → apiMode 映射(供 Nccs / Ngms / Jqyh 内部 switch 使用):
+ * 'openai' → 'openai_test' (经 ST 后端代理发送,规避 CORS)
+ * 'google' → 'openai_test' (Google OpenAI-compat 同样走代理)
+ * 'sillytavern_backend'→ 'openai_test'
+ * 'sillytavern_preset' → 'sillytavern_preset'
+ */
+
+import { apiProfileManager } from '../../utils/config/ApiProfileManager.js';
+
+/**
+ * 将 Profile.provider 映射到子模块使用的 apiMode 字段。
+ * @param {string} provider
+ * @returns {'openai_test'|'sillytavern_preset'}
+ */
+export function providerToApiMode(provider) {
+ return provider === 'sillytavern_preset' ? 'sillytavern_preset' : 'openai_test';
+}
+
+/**
+ * 获取功能槽对应的完整 Profile(含解密 Key)。
+ * 未分配或读取失败时返回 null。
+ *
+ * @param {string} slot 功能槽名(见 ApiProfileManager.SLOTS)
+ * @returns {Promise