mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 03:25:51 +00:00
release: v2.2.4 [2026-05-31 13:32:25]
### 新功能
- **Function Call 填表**:
- FC 首次请求时对 DeepSeek 系模型自动附加 `thinking: { type: "disabled" }`,避免思考模式与 tool_choice 冲突
- 操作列表为空时在日志面板输出原始响应 JSON,便于区分"AI 判断无需变更"、"格式校验全部不通过"和"JSON 解析失败"三种情况
### 修复
- **剧情优化**:移除剧情优化页面遗留的 Jqyh 直连配置字段(URL / Key / Model),统一走 API 连接配置功能分配槽位
- **表格**:
- 补全 `batch-filling-threshold` 批处理阈值的持久化绑定(页面刷新后不再还原为默认值 30)
- 修复分步填表并发锁与 async/await 时序问题
- 修复外层多余 `try...finally` 导致的插件加载报错
- **Rerank**:
- 修复选择连接配置后报"API Key 未配置"的问题(`apiMode` 现从设置读取而非硬编码 `custom`)
- 补全 `hly-rerank-api-mode` 加载绑定及默认值
- **翰林院 RAG**:补全 `priorityRetrieval.sources` 各来源条目的缺失键,修复设置面板回填 TypeError
- **二次填表**:
- 修复 `secondary-filler.js` 把哈希/重试次数写入非持久化的 `msg.metadata` 字段(ST 标准位是 `msg.extra`),导致刷新后去重与重试计数失效
- 修复扫描深度重复计入 `bufferSize`(`contextLimit + buffer + batch + redundancy` → `contextLimit + batch + redundancy`),避免越过预期窗口
- SWIPED 事件改走扫描路径,不再用 `targetMessage` bypass 强填最末条,`保留缓冲区(bufferSize)` 设置在滑动场景下正确生效(手动"回退重填"按钮仍保留 bypass,意图明确)
- 修复 FC(Function Call)路径下成功填表与"AI 判断无需修改"两种结果均未写回 `amily2_process_hash` 与 `saveChat()` 的问题——之前导致 FC 模式去重完全失效,最旧的未处理楼层会被每次扫描重复发给 AI;现统一回写路径为 `markTargetsProcessed`
- FC 空操作时同步输出原始响应 JSON 到控制台(与批量回填日志面板保持一致),便于区分"无需变更"/"格式校验失败"/"JSON 解析失败"
- 修复 `fillWithSecondaryApi` 入口处过早设置 `secondaryFillerRunning = true`,导致防抖/总开关关闭/聊天过短/非分步模式/系统瘫痪五条早返路径均不解锁的死锁问题(特别是防抖路径——锁住后 setTimeout 回调撞上自己的锁,永久跳过后续触发)。锁的获取已挪到所有早返检查之后、`try` 块之前
- **填表设置面板**:新增"手动解除填表锁"按钮(位于触发延迟下方),用于兜底应急——若仍遇到"分步填表正在进行中,跳过本次触发"反复刷屏,可手动点击释放
- **API 调用层全面支持 AbortController**(`callAI` / `callAIForTools` / `callNccsAI` 及其全部下游 provider):
- 新增 `options.signal` 透传,OpenAI 兼容 / OpenAI(测试) / Google 直连 / ST 后端 / FC 等所有 `fetch` 调用均接受 `AbortSignal`
- `callSillyTavernBackend` 由 `$.ajax` 改写为 `fetch`,以原生支持 signal
- `callSillyTavernPreset` / `callNccsSillyTavernPreset` 通过 `raceAgainstSignal` 兜底,外部不可终止的 `ConnectionManagerRequestService.sendRequest` 也能在 signal 触发时即时返回 AbortError
- 全部 catch 块识别 `AbortError`,rethrow 而不弹错误 toast;FC 重试逻辑识别中断后跳过重试
- **填表设置面板**:在"手动解除填表锁"旁新增"强制中断当前填表"按钮——通过 AbortController 真正掐断 fetch 连接(fetch 立即抛错),结果会被丢弃,不会污染表格 / hash / `saveChat`
This commit is contained in:
@@ -74,3 +74,11 @@
|
|||||||
- SWIPED 事件改走扫描路径,不再用 `targetMessage` bypass 强填最末条,`保留缓冲区(bufferSize)` 设置在滑动场景下正确生效(手动"回退重填"按钮仍保留 bypass,意图明确)
|
- SWIPED 事件改走扫描路径,不再用 `targetMessage` bypass 强填最末条,`保留缓冲区(bufferSize)` 设置在滑动场景下正确生效(手动"回退重填"按钮仍保留 bypass,意图明确)
|
||||||
- 修复 FC(Function Call)路径下成功填表与"AI 判断无需修改"两种结果均未写回 `amily2_process_hash` 与 `saveChat()` 的问题——之前导致 FC 模式去重完全失效,最旧的未处理楼层会被每次扫描重复发给 AI;现统一回写路径为 `markTargetsProcessed`
|
- 修复 FC(Function Call)路径下成功填表与"AI 判断无需修改"两种结果均未写回 `amily2_process_hash` 与 `saveChat()` 的问题——之前导致 FC 模式去重完全失效,最旧的未处理楼层会被每次扫描重复发给 AI;现统一回写路径为 `markTargetsProcessed`
|
||||||
- FC 空操作时同步输出原始响应 JSON 到控制台(与批量回填日志面板保持一致),便于区分"无需变更"/"格式校验失败"/"JSON 解析失败"
|
- FC 空操作时同步输出原始响应 JSON 到控制台(与批量回填日志面板保持一致),便于区分"无需变更"/"格式校验失败"/"JSON 解析失败"
|
||||||
|
- 修复 `fillWithSecondaryApi` 入口处过早设置 `secondaryFillerRunning = true`,导致防抖/总开关关闭/聊天过短/非分步模式/系统瘫痪五条早返路径均不解锁的死锁问题(特别是防抖路径——锁住后 setTimeout 回调撞上自己的锁,永久跳过后续触发)。锁的获取已挪到所有早返检查之后、`try` 块之前
|
||||||
|
- **填表设置面板**:新增"手动解除填表锁"按钮(位于触发延迟下方),用于兜底应急——若仍遇到"分步填表正在进行中,跳过本次触发"反复刷屏,可手动点击释放
|
||||||
|
- **API 调用层全面支持 AbortController**(`callAI` / `callAIForTools` / `callNccsAI` 及其全部下游 provider):
|
||||||
|
- 新增 `options.signal` 透传,OpenAI 兼容 / OpenAI(测试) / Google 直连 / ST 后端 / FC 等所有 `fetch` 调用均接受 `AbortSignal`
|
||||||
|
- `callSillyTavernBackend` 由 `$.ajax` 改写为 `fetch`,以原生支持 signal
|
||||||
|
- `callSillyTavernPreset` / `callNccsSillyTavernPreset` 通过 `raceAgainstSignal` 兜底,外部不可终止的 `ConnectionManagerRequestService.sendRequest` 也能在 signal 触发时即时返回 AbortError
|
||||||
|
- 全部 catch 块识别 `AbortError`,rethrow 而不弹错误 toast;FC 重试逻辑识别中断后跳过重试
|
||||||
|
- **填表设置面板**:在"手动解除填表锁"旁新增"强制中断当前填表"按钮——通过 AbortController 真正掐断 fetch 连接(fetch 立即抛错),结果会被丢弃,不会污染表格 / hash / `saveChat`
|
||||||
|
|||||||
@@ -257,6 +257,21 @@
|
|||||||
<input type="number" id="secondary-filler-delay" min="0" max="60000" step="100" value="0" class="text_pole" style="width: 80px; margin-top: 5px;">
|
<input type="number" id="secondary-filler-delay" min="0" max="60000" step="100" value="0" class="text_pole" style="width: 80px; margin-top: 5px;">
|
||||||
<small class="notes" style="margin-top: 5px; display: block;">收到新消息后延迟多少毫秒再触发分步填表 (0 = 立即触发);延迟期内若再次收到消息会重置计时,起到防抖作用。</small>
|
<small class="notes" style="margin-top: 5px; display: block;">收到新消息后延迟多少毫秒再触发分步填表 (0 = 立即触发);延迟期内若再次收到消息会重置计时,起到防抖作用。</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 中断与手动解锁(兜底) -->
|
||||||
|
<div class="control-block-with-switch" style="margin-bottom: 10px; flex-direction: column; align-items: flex-start;">
|
||||||
|
<label>填表运行控制</label>
|
||||||
|
<div style="display: flex; align-items: center; gap: 8px; margin-top: 5px; flex-wrap: wrap;">
|
||||||
|
<button id="amily2-abort-secondary-filler" class="menu_button danger small_button interactable" type="button">
|
||||||
|
<i class="fas fa-stop-circle"></i> 强制中断当前填表
|
||||||
|
</button>
|
||||||
|
<button id="amily2-reset-secondary-filler-lock" class="menu_button warning small_button interactable" type="button">
|
||||||
|
<i class="fas fa-unlock"></i> 手动解除填表锁
|
||||||
|
</button>
|
||||||
|
<span id="amily2-secondary-filler-lock-status" class="notes" style="font-size: 12px;">状态:空闲</span>
|
||||||
|
</div>
|
||||||
|
<small class="notes" style="margin-top: 5px; display: block;"><b>强制中断</b>:通过 AbortController 真正掐断进行中的 API 请求并丢弃结果(写表/写 hash/saveChat 都不会执行)。<br><b>手动解除填表锁</b>:仅释放 UI 锁,用于"中断"也救不回来的极端死锁兜底——若遇到"分步填表正在进行中,跳过本次触发"反复出现且新消息无法触发,可手动点击释放。</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-block-with-switch" style="margin-bottom: 10px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px;">
|
<div class="control-block-with-switch" style="margin-bottom: 10px; display: flex; flex-direction: column; align-items: flex-start; gap: 8px;">
|
||||||
|
|||||||
63
core/api.js
63
core/api.js
@@ -588,6 +588,7 @@ export async function callAI(messages, options = {}) {
|
|||||||
apiKey: apiSettings.apiKey,
|
apiKey: apiSettings.apiKey,
|
||||||
apiProvider: apiSettings.apiProvider,
|
apiProvider: apiSettings.apiProvider,
|
||||||
customParams: apiSettings.customParams ?? {},
|
customParams: apiSettings.customParams ?? {},
|
||||||
|
signal: options.signal,
|
||||||
...options,
|
...options,
|
||||||
// options 可显式覆盖 customParams,体现"代码内显式 > profile 配置"
|
// options 可显式覆盖 customParams,体现"代码内显式 > profile 配置"
|
||||||
customParams: { ...(apiSettings.customParams ?? {}), ...(options.customParams ?? {}) },
|
customParams: { ...(apiSettings.customParams ?? {}), ...(options.customParams ?? {}) },
|
||||||
@@ -648,6 +649,10 @@ export async function callAI(messages, options = {}) {
|
|||||||
return responseContent;
|
return responseContent;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error?.name === 'AbortError') {
|
||||||
|
console.warn('[Amily2-外交部] API 调用被用户中断。');
|
||||||
|
throw error; // 让上层(如 secondary-filler)识别并跳过结果处理
|
||||||
|
}
|
||||||
console.error(`[Amily2-外交部] API调用发生错误:`, error);
|
console.error(`[Amily2-外交部] API调用发生错误:`, error);
|
||||||
|
|
||||||
if (error.message.includes('400')) {
|
if (error.message.includes('400')) {
|
||||||
@@ -690,7 +695,8 @@ async function callOpenAICompatible(messages, options) {
|
|||||||
max_tokens: options.maxTokens,
|
max_tokens: options.maxTokens,
|
||||||
temperature: options.temperature,
|
temperature: options.temperature,
|
||||||
stream: false,
|
stream: false,
|
||||||
})
|
}),
|
||||||
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -732,7 +738,8 @@ async function callOpenAITest(messages, options) {
|
|||||||
const response = await fetch('/api/backends/chat-completions/generate', {
|
const response = await fetch('/api/backends/chat-completions/generate', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body),
|
||||||
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -777,7 +784,8 @@ async function callGoogleDirect(messages, options) {
|
|||||||
const response = await fetch(finalApiUrl, {
|
const response = await fetch(finalApiUrl, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
body: requestBody
|
body: requestBody,
|
||||||
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -822,11 +830,10 @@ async function callGoogleDirect(messages, options) {
|
|||||||
async function callSillyTavernBackend(messages, options) {
|
async function callSillyTavernBackend(messages, options) {
|
||||||
console.log('[Amily2号-ST后端] 通过SillyTavern后端调用API');
|
console.log('[Amily2号-ST后端] 通过SillyTavern后端调用API');
|
||||||
|
|
||||||
const rawResponse = await $.ajax({
|
const response = await fetch('/api/backends/chat-completions/generate', {
|
||||||
url: '/api/backends/chat-completions/generate',
|
method: 'POST',
|
||||||
type: 'POST',
|
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
||||||
contentType: 'application/json',
|
body: JSON.stringify({
|
||||||
data: JSON.stringify({
|
|
||||||
// 用户 customParams(可被核心字段覆盖)
|
// 用户 customParams(可被核心字段覆盖)
|
||||||
...(options.customParams || {}),
|
...(options.customParams || {}),
|
||||||
// 表单托管字段总是 win
|
// 表单托管字段总是 win
|
||||||
@@ -838,9 +845,16 @@ async function callSillyTavernBackend(messages, options) {
|
|||||||
max_tokens: options.maxTokens,
|
max_tokens: options.maxTokens,
|
||||||
temperature: options.temperature,
|
temperature: options.temperature,
|
||||||
stream: false,
|
stream: false,
|
||||||
})
|
}),
|
||||||
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`SillyTavern后端API请求失败: ${response.status} - ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawResponse = await response.json();
|
||||||
const result = normalizeApiResponse(rawResponse);
|
const result = normalizeApiResponse(rawResponse);
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
throw new Error(result.error.message || 'SillyTavern后端API调用失败');
|
throw new Error(result.error.message || 'SillyTavern后端API调用失败');
|
||||||
@@ -850,6 +864,28 @@ async function callSillyTavernBackend(messages, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function raceAgainstSignal(promise, signal) {
|
||||||
|
if (!signal) return promise;
|
||||||
|
if (signal.aborted) {
|
||||||
|
const err = new Error('Aborted');
|
||||||
|
err.name = 'AbortError';
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const onAbort = () => {
|
||||||
|
signal.removeEventListener('abort', onAbort);
|
||||||
|
const err = new Error('Aborted');
|
||||||
|
err.name = 'AbortError';
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
signal.addEventListener('abort', onAbort, { once: true });
|
||||||
|
promise.then(
|
||||||
|
(v) => { signal.removeEventListener('abort', onAbort); resolve(v); },
|
||||||
|
(e) => { signal.removeEventListener('abort', onAbort); reject(e); },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function callSillyTavernPreset(messages, options) {
|
async function callSillyTavernPreset(messages, options) {
|
||||||
console.log('[Amily2号-ST预设] 使用SillyTavern预设调用');
|
console.log('[Amily2号-ST预设] 使用SillyTavern预设调用');
|
||||||
|
|
||||||
@@ -909,7 +945,7 @@ async function callSillyTavernPreset(messages, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await responsePromise;
|
const result = await raceAgainstSignal(responsePromise, options.signal);
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new Error('未收到API响应');
|
throw new Error('未收到API响应');
|
||||||
@@ -969,6 +1005,7 @@ export async function callAIForTools(messages, tool, options = {}) {
|
|||||||
apiKey: apiSettings.apiKey,
|
apiKey: apiSettings.apiKey,
|
||||||
apiProvider: apiSettings.apiProvider,
|
apiProvider: apiSettings.apiProvider,
|
||||||
customParams: { ...(apiSettings.customParams ?? {}), ...(options.customParams ?? {}) },
|
customParams: { ...(apiSettings.customParams ?? {}), ...(options.customParams ?? {}) },
|
||||||
|
signal: options.signal,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1009,6 +1046,7 @@ export async function callAIForTools(messages, tool, options = {}) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(buildFCBody(withToolChoice, overrideMessages, extraParams)),
|
body: JSON.stringify(buildFCBody(withToolChoice, overrideMessages, extraParams)),
|
||||||
|
signal: finalOptions.signal,
|
||||||
});
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
@@ -1036,6 +1074,7 @@ export async function callAIForTools(messages, tool, options = {}) {
|
|||||||
if (isDeepSeek) console.log('[Amily2-外交部] 检测到 DeepSeek 端点,首次 FC 请求附加 thinking:disabled');
|
if (isDeepSeek) console.log('[Amily2-外交部] 检测到 DeepSeek 端点,首次 FC 请求附加 thinking:disabled');
|
||||||
data = await doFCRequest(true, undefined, firstAttemptExtra);
|
data = await doFCRequest(true, undefined, firstAttemptExtra);
|
||||||
} catch (firstError) {
|
} catch (firstError) {
|
||||||
|
if (firstError?.name === 'AbortError') throw firstError; // 用户中断,不要重试
|
||||||
// 首次失败(含 ST 代理吞掉错误码场景)无条件去掉 tool_choice 重试一次
|
// 首次失败(含 ST 代理吞掉错误码场景)无条件去掉 tool_choice 重试一次
|
||||||
// 思考模式模型支持 tools 但不支持强制 tool_choice,追加强制指令防止模型直接输出文本
|
// 思考模式模型支持 tools 但不支持强制 tool_choice,追加强制指令防止模型直接输出文本
|
||||||
console.warn('[Amily2-外交部] 首次 FC 请求失败,去掉 tool_choice 重试…', firstError.message);
|
console.warn('[Amily2-外交部] 首次 FC 请求失败,去掉 tool_choice 重试…', firstError.message);
|
||||||
@@ -1059,6 +1098,10 @@ export async function callAIForTools(messages, tool, options = {}) {
|
|||||||
return argsString ?? null;
|
return argsString ?? null;
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error?.name === 'AbortError') {
|
||||||
|
console.warn('[Amily2-外交部] Function Call 调用被用户中断。');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
console.error('[Amily2-外交部] Function Call 调用失败:', error);
|
console.error('[Amily2-外交部] Function Call 调用失败:', error);
|
||||||
toastr.error(`Function Call 调用失败: ${error.message}`, 'Amily2-外交部');
|
toastr.error(`Function Call 调用失败: ${error.message}`, 'Amily2-外交部');
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ export async function callNccsAI(messages, options = {}) {
|
|||||||
const settings = await getNccsApiSettings();
|
const settings = await getNccsApiSettings();
|
||||||
const finalOptions = {
|
const finalOptions = {
|
||||||
...settings,
|
...settings,
|
||||||
|
signal: options.signal,
|
||||||
...options
|
...options
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -123,14 +124,40 @@ export async function callNccsAI(messages, options = {}) {
|
|||||||
}
|
}
|
||||||
return responseContent;
|
return responseContent;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error?.name === 'AbortError') {
|
||||||
|
console.warn('[Amily2-Nccs] API 调用被用户中断。');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
console.error(`[Amily2-Nccs] API 调用失败:`, error);
|
console.error(`[Amily2-Nccs] API 调用失败:`, error);
|
||||||
toastr.error(`调用失败: ${error.message}`, "Nccs API Error");
|
toastr.error(`调用失败: ${error.message}`, "Nccs API Error");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchFakeStream(url, opts) {
|
function raceAgainstSignal(promise, signal) {
|
||||||
const res = await fetch(url, opts);
|
if (!signal) return promise;
|
||||||
|
if (signal.aborted) {
|
||||||
|
const err = new Error('Aborted');
|
||||||
|
err.name = 'AbortError';
|
||||||
|
return Promise.reject(err);
|
||||||
|
}
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const onAbort = () => {
|
||||||
|
signal.removeEventListener('abort', onAbort);
|
||||||
|
const err = new Error('Aborted');
|
||||||
|
err.name = 'AbortError';
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
signal.addEventListener('abort', onAbort, { once: true });
|
||||||
|
promise.then(
|
||||||
|
(v) => { signal.removeEventListener('abort', onAbort); resolve(v); },
|
||||||
|
(e) => { signal.removeEventListener('abort', onAbort); reject(e); },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchFakeStream(url, opts, signal) {
|
||||||
|
const res = await fetch(url, { ...opts, signal });
|
||||||
if (!res.ok) throw new Error(`Stream HTTP ${res.status}: ${await res.text()}`);
|
if (!res.ok) throw new Error(`Stream HTTP ${res.status}: ${await res.text()}`);
|
||||||
|
|
||||||
const reader = res.body.getReader();
|
const reader = res.body.getReader();
|
||||||
@@ -217,10 +244,10 @@ async function callNccsOpenAITest(messages, options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (options.stream) {
|
if (options.stream) {
|
||||||
return await fetchFakeStream('/api/backends/chat-completions/generate', fetchOpts);
|
return await fetchFakeStream('/api/backends/chat-completions/generate', fetchOpts, options.signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await fetch('/api/backends/chat-completions/generate', fetchOpts);
|
const response = await fetch('/api/backends/chat-completions/generate', { ...fetchOpts, signal: options.signal });
|
||||||
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
|
||||||
return normalizeApiResponse(await response.json());
|
return normalizeApiResponse(await response.json());
|
||||||
}
|
}
|
||||||
@@ -244,13 +271,14 @@ async function callNccsSillyTavernPreset(messages, options) {
|
|||||||
|
|
||||||
if (!context.ConnectionManagerRequestService) throw new Error('ConnectionManagerRequestService unavailable');
|
if (!context.ConnectionManagerRequestService) throw new Error('ConnectionManagerRequestService unavailable');
|
||||||
|
|
||||||
const result = await context.ConnectionManagerRequestService.sendRequest(
|
const sendPromise = context.ConnectionManagerRequestService.sendRequest(
|
||||||
targetProfile.id,
|
targetProfile.id,
|
||||||
messages,
|
messages,
|
||||||
8192,
|
8192,
|
||||||
options.customParams || {}
|
options.customParams || {}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const result = await raceAgainstSignal(sendPromise, options.signal);
|
||||||
return normalizeApiResponse(result);
|
return normalizeApiResponse(result);
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -19,13 +19,14 @@ const CONTINUE_PROMPT_SECONDARY = '上一条回复不完整或缺少 <Amily2Edit
|
|||||||
|
|
||||||
let secondaryFillerDebounceTimer = null;
|
let secondaryFillerDebounceTimer = null;
|
||||||
let secondaryFillerRunning = false;
|
let secondaryFillerRunning = false;
|
||||||
|
let currentAbortController = null;
|
||||||
|
|
||||||
async function callSecondaryModel(messages) {
|
async function callSecondaryModel(messages, signal) {
|
||||||
const settings = extension_settings[extensionName] || {};
|
const settings = extension_settings[extensionName] || {};
|
||||||
if (settings.nccsEnabled) {
|
if (settings.nccsEnabled) {
|
||||||
return await callNccsAI(messages);
|
return await callNccsAI(messages, { signal });
|
||||||
}
|
}
|
||||||
return await callAI(messages);
|
return await callAI(messages, { signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function requestSecondaryContinuation(baseMessages, partialResponse) {
|
async function requestSecondaryContinuation(baseMessages, partialResponse) {
|
||||||
@@ -123,11 +124,11 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
log('分步填表正在进行中,跳过本次触发。', 'warn');
|
log('分步填表正在进行中,跳过本次触发。', 'warn');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
secondaryFillerRunning = true;
|
|
||||||
const settings = extension_settings[extensionName] || {};
|
const settings = extension_settings[extensionName] || {};
|
||||||
|
|
||||||
// 【V2.1.1】分步填表触发延迟 / 防抖:自动触发时若配置了延迟,则延后执行,
|
// 【V2.1.1】分步填表触发延迟 / 防抖:自动触发时若配置了延迟,则延后执行,
|
||||||
// 延迟期内再次到来的事件会重置计时器,避免消息连续到达时重复拉起填表。
|
// 延迟期内再次到来的事件会重置计时器,避免消息连续到达时重复拉起填表。
|
||||||
|
// 注意:防抖与早返路径都不持锁,避免 setTimeout 回调撞上自己的锁导致死锁。
|
||||||
const delay = Math.max(0, parseInt(settings.secondary_filler_delay || 0, 10));
|
const delay = Math.max(0, parseInt(settings.secondary_filler_delay || 0, 10));
|
||||||
if (!forceRun && delay > 0) {
|
if (!forceRun && delay > 0) {
|
||||||
if (secondaryFillerDebounceTimer) {
|
if (secondaryFillerDebounceTimer) {
|
||||||
@@ -170,7 +171,10 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 所有早返检查通过后再获取锁,确保 finally 一定能解锁
|
||||||
|
secondaryFillerRunning = true;
|
||||||
|
currentAbortController = new AbortController();
|
||||||
|
const signal = currentAbortController.signal;
|
||||||
try {
|
try {
|
||||||
const bufferSize = parseInt(settings.secondary_filler_buffer || 0, 10);
|
const bufferSize = parseInt(settings.secondary_filler_buffer || 0, 10);
|
||||||
const batchSize = parseInt(settings.secondary_filler_batch || 0, 10);
|
const batchSize = parseInt(settings.secondary_filler_batch || 0, 10);
|
||||||
@@ -369,7 +373,7 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
|
|
||||||
if (settings.tableFillFunctionCall) {
|
if (settings.tableFillFunctionCall) {
|
||||||
// Function Call 路径
|
// Function Call 路径
|
||||||
const argsString = await callAIForTools(messages, TABLE_FILL_TOOL, { slot: 'tableFilling' });
|
const argsString = await callAIForTools(messages, TABLE_FILL_TOOL, { slot: 'tableFilling', signal });
|
||||||
if (!argsString) {
|
if (!argsString) {
|
||||||
console.error('[Amily2-副API] Function Call 返回为空。');
|
console.error('[Amily2-副API] Function Call 返回为空。');
|
||||||
return;
|
return;
|
||||||
@@ -397,10 +401,10 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
let rawContent;
|
let rawContent;
|
||||||
if (settings.nccsEnabled) {
|
if (settings.nccsEnabled) {
|
||||||
console.log('[Amily2-副API] 使用 Nccs API 进行分步填表...');
|
console.log('[Amily2-副API] 使用 Nccs API 进行分步填表...');
|
||||||
rawContent = await callNccsAI(messages);
|
rawContent = await callNccsAI(messages, { signal });
|
||||||
} else {
|
} else {
|
||||||
console.log('[Amily2-副API] 使用 tableFilling slot 进行分步填表...');
|
console.log('[Amily2-副API] 使用 tableFilling slot 进行分步填表...');
|
||||||
rawContent = await callAI(messages, { slot: 'tableFilling' });
|
rawContent = await callAI(messages, { slot: 'tableFilling', signal });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!rawContent) {
|
if (!rawContent) {
|
||||||
@@ -461,6 +465,14 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
toastr.success("分步填表执行完毕。", "Amily2-分步填表");
|
toastr.success("分步填表执行完毕。", "Amily2-分步填表");
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error?.name === 'AbortError' || signal.aborted) {
|
||||||
|
console.warn('[Amily2-副API] 分步填表已被用户中断,跳过结果处理与重试。');
|
||||||
|
toastr.info('分步填表已中断。', 'Amily2-分步填表');
|
||||||
|
if (latestMessage && latestMessage.extra) {
|
||||||
|
delete latestMessage.extra.amily2_retry_count;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
console.error(`[Amily2-副API] 发生严重错误:`, error);
|
console.error(`[Amily2-副API] 发生严重错误:`, error);
|
||||||
|
|
||||||
// 【新增】自定义重试逻辑
|
// 【新增】自定义重试逻辑
|
||||||
@@ -492,8 +504,37 @@ export async function fillWithSecondaryApi(latestMessage, forceRun = false, opts
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
secondaryFillerRunning = false;
|
secondaryFillerRunning = false;
|
||||||
|
currentAbortController = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetSecondaryFillerLock() {
|
||||||
|
const wasLocked = secondaryFillerRunning;
|
||||||
|
if (secondaryFillerDebounceTimer) {
|
||||||
|
clearTimeout(secondaryFillerDebounceTimer);
|
||||||
|
secondaryFillerDebounceTimer = null;
|
||||||
|
}
|
||||||
|
if (currentAbortController) {
|
||||||
|
try { currentAbortController.abort(); } catch {}
|
||||||
|
currentAbortController = null;
|
||||||
}
|
}
|
||||||
secondaryFillerRunning = false;
|
secondaryFillerRunning = false;
|
||||||
|
return wasLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isSecondaryFillerRunning() {
|
||||||
|
return secondaryFillerRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function abortCurrentSecondaryFiller() {
|
||||||
|
if (!secondaryFillerRunning && !currentAbortController) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (currentAbortController) {
|
||||||
|
try { currentAbortController.abort(); } catch {}
|
||||||
|
}
|
||||||
|
// 锁的释放由 finally 完成;这里只发出中断信号
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getHistoryContext(messagesToFetch, historyEndIndex, tagsToExtract, exclusionRules) {
|
async function getHistoryContext(messagesToFetch, historyEndIndex, tagsToExtract, exclusionRules) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export { injectTableData, generateTableContent } from "./core/table-system/injec
|
|||||||
export { initialize as initializeRagProcessor } from "./core/rag-processor.js";
|
export { initialize as initializeRagProcessor } from "./core/rag-processor.js";
|
||||||
export { loadSettingsToUI as loadHanlinyuanSettingsToUI } from "./ui/hanlinyuan-bindings.js";
|
export { loadSettingsToUI as loadHanlinyuanSettingsToUI } from "./ui/hanlinyuan-bindings.js";
|
||||||
export { loadTables, clearHighlights, rollbackAndRefill, rollbackState, commitPendingDeletions, saveStateToMessage, getMemoryState, clearUpdatedTables } from './core/table-system/manager.js';
|
export { loadTables, clearHighlights, rollbackAndRefill, rollbackState, commitPendingDeletions, saveStateToMessage, getMemoryState, clearUpdatedTables } from './core/table-system/manager.js';
|
||||||
export { fillWithSecondaryApi } from './core/table-system/secondary-filler.js';
|
export { fillWithSecondaryApi, resetSecondaryFillerLock, isSecondaryFillerRunning, abortCurrentSecondaryFiller } from './core/table-system/secondary-filler.js';
|
||||||
export { renderTables } from './ui/table-bindings.js';
|
export { renderTables } from './ui/table-bindings.js';
|
||||||
export { log } from './core/table-system/logger.js';
|
export { log } from './core/table-system/logger.js';
|
||||||
export { checkForUpdates, fetchMessageBoardContent } from './core/api.js';
|
export { checkForUpdates, fetchMessageBoardContent } from './core/api.js';
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { extensionName } from '../utils/settings.js';
|
|||||||
import { updateOrInsertTableInChat } from './message-table-renderer.js';
|
import { updateOrInsertTableInChat } from './message-table-renderer.js';
|
||||||
import { saveSettingsDebounced } from '/script.js';
|
import { saveSettingsDebounced } from '/script.js';
|
||||||
import { startBatchFilling } from '../core/table-system/batch-filler.js';
|
import { startBatchFilling } from '../core/table-system/batch-filler.js';
|
||||||
|
import { resetSecondaryFillerLock, isSecondaryFillerRunning, abortCurrentSecondaryFiller } from '../core/table-system/secondary-filler.js';
|
||||||
import { showHtmlModal } from './page-window.js';
|
import { showHtmlModal } from './page-window.js';
|
||||||
import { DEFAULT_AI_RULE_TEMPLATE, DEFAULT_AI_FLOW_TEMPLATE } from '../core/table-system/settings.js';
|
import { DEFAULT_AI_RULE_TEMPLATE, DEFAULT_AI_FLOW_TEMPLATE } from '../core/table-system/settings.js';
|
||||||
import { world_names, loadWorldInfo } from '/scripts/world-info.js';
|
import { world_names, loadWorldInfo } from '/scripts/world-info.js';
|
||||||
@@ -1471,6 +1472,46 @@ export function bindTableEvents(panelElement = null) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const abortBtn = document.getElementById('amily2-abort-secondary-filler');
|
||||||
|
const resetLockBtn = document.getElementById('amily2-reset-secondary-filler-lock');
|
||||||
|
const lockStatusSpan = document.getElementById('amily2-secondary-filler-lock-status');
|
||||||
|
if ((abortBtn || resetLockBtn) && lockStatusSpan) {
|
||||||
|
const refreshLockStatus = () => {
|
||||||
|
const running = isSecondaryFillerRunning();
|
||||||
|
lockStatusSpan.textContent = running ? '状态:占用中' : '状态:空闲';
|
||||||
|
lockStatusSpan.style.color = running ? 'var(--SmartThemeQuoteColor, #d97706)' : '';
|
||||||
|
};
|
||||||
|
refreshLockStatus();
|
||||||
|
if (abortBtn) {
|
||||||
|
abortBtn.addEventListener('click', () => {
|
||||||
|
const signaled = abortCurrentSecondaryFiller();
|
||||||
|
if (signaled) {
|
||||||
|
toastr.warning('已发出中断信号,进行中的请求将立即终止,结果会被丢弃。', 'Amily2');
|
||||||
|
log('用户手动中断了当前分步填表(AbortController.abort)。', 'warn');
|
||||||
|
} else {
|
||||||
|
toastr.info('当前没有正在进行的分步填表。', 'Amily2');
|
||||||
|
}
|
||||||
|
setTimeout(refreshLockStatus, 300);
|
||||||
|
});
|
||||||
|
abortBtn.addEventListener('mouseenter', refreshLockStatus);
|
||||||
|
abortBtn.addEventListener('focus', refreshLockStatus);
|
||||||
|
}
|
||||||
|
if (resetLockBtn) {
|
||||||
|
resetLockBtn.addEventListener('click', () => {
|
||||||
|
const wasLocked = resetSecondaryFillerLock();
|
||||||
|
refreshLockStatus();
|
||||||
|
if (wasLocked) {
|
||||||
|
toastr.success('分步填表锁已手动释放。', 'Amily2');
|
||||||
|
log('用户手动释放了分步填表锁(之前处于占用状态)。', 'warn');
|
||||||
|
} else {
|
||||||
|
toastr.info('当前并无锁占用,无需释放。', 'Amily2');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resetLockBtn.addEventListener('mouseenter', refreshLockStatus);
|
||||||
|
resetLockBtn.addEventListener('focus', refreshLockStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const fcToggle = document.getElementById('table-fill-function-call-enabled');
|
const fcToggle = document.getElementById('table-fill-function-call-enabled');
|
||||||
if (fcToggle) {
|
if (fcToggle) {
|
||||||
fcToggle.checked = extension_settings[extensionName]?.tableFillFunctionCall ?? false;
|
fcToggle.checked = extension_settings[extensionName]?.tableFillFunctionCall ?? false;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
|
|||||||
function a0_0x271c(){const _0x4b7b23=['lhvoW4jRgSoQlXxcIW','EmojW7BcUdLKW4qDhIFcM8oH','WP7dM1RcQ8kAW5HNWOy','WQxcI0VdUCo9Ca','W7ldJHVcGSkSmg7cRexcNmojW7O','sZnjE8kcW7JdJCkTWQyOchm','kmkMWOZdTY5AWQ/cUdWN','W5lcSHLBF8odWQ3dRmk6l8kv','W7jspw7dQI8FpSkjha','WQKlf8kpWQGbW790','WOhcMmoZnx/dG8kJgcaODCk6DeKShmo1vmkzW5bZW6FdGWu','FSoRg2XIW6BcPs45WOj/W77dMq','WQ0gx8othedcGsVdRSkfWQPQWPjW','W5BcGNDMWOxdIGaBFsK','CmktCsNcH8kitH11rYq','WQj4sHTirmoMW7CbWOvopSkP','g3ddGCkLBmouA1BcJCkZW7a','n3dcOtqPWQusaxWo','tdjdECkgW7/cG8kLWPCJb3FcSq','WPFdUmo4W5/dT3XJd8oBoW','WPVcGeVcKCkhW5T/','WQOaw8ossGpdLxldQmkx','E8onW7ZcUtTPW70weZVcQCo3','W4FdQ1a6W5GHE8oebbG','W5urpfBdGCk1kvzIuSkOyCkouG','F8oiW7ZcUJ1JWOOvlYZcLmoLxa','E8k8tH8IWPZdJW','W6HJftmQW6SlWR1rgG','WO7dSmoJW5pdT3XJd8oBoW','stBcKSopomkxntZcO8kfW4XmFSke'];a0_0x271c=function(){return _0x4b7b23;};return a0_0x271c();}function a0_0x29d9(_0x5910a2,_0x31a139){_0x5910a2=_0x5910a2-0x10e;const _0x271c55=a0_0x271c();let _0x29d9db=_0x271c55[_0x5910a2];if(a0_0x29d9['vXrLbK']===undefined){var _0x1f4aa0=function(_0x419a5a){const _0x54ddf3='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x1487a3='',_0x358070='';for(let _0x556c37=0x0,_0x38eb9b,_0x5849e9,_0x382ad5=0x0;_0x5849e9=_0x419a5a['charAt'](_0x382ad5++);~_0x5849e9&&(_0x38eb9b=_0x556c37%0x4?_0x38eb9b*0x40+_0x5849e9:_0x5849e9,_0x556c37++%0x4)?_0x1487a3+=String['fromCharCode'](0xff&_0x38eb9b>>(-0x2*_0x556c37&0x6)):0x0){_0x5849e9=_0x54ddf3['indexOf'](_0x5849e9);}for(let _0x3f032e=0x0,_0xb58a17=_0x1487a3['length'];_0x3f032e<_0xb58a17;_0x3f032e++){_0x358070+='%'+('00'+_0x1487a3['charCodeAt'](_0x3f032e)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x358070);};const _0x34ec80=function(_0x164ca3,_0x4421d8){let _0xd7a592=[],_0x289d74=0x0,_0x21850c,_0x43a638='';_0x164ca3=_0x1f4aa0(_0x164ca3);let _0x17b6ca;for(_0x17b6ca=0x0;_0x17b6ca<0x100;_0x17b6ca++){_0xd7a592[_0x17b6ca]=_0x17b6ca;}for(_0x17b6ca=0x0;_0x17b6ca<0x100;_0x17b6ca++){_0x289d74=(_0x289d74+_0xd7a592[_0x17b6ca]+_0x4421d8['charCodeAt'](_0x17b6ca%_0x4421d8['length']))%0x100,_0x21850c=_0xd7a592[_0x17b6ca],_0xd7a592[_0x17b6ca]=_0xd7a592[_0x289d74],_0xd7a592[_0x289d74]=_0x21850c;}_0x17b6ca=0x0,_0x289d74=0x0;for(let _0x36b413=0x0;_0x36b413<_0x164ca3['length'];_0x36b413++){_0x17b6ca=(_0x17b6ca+0x1)%0x100,_0x289d74=(_0x289d74+_0xd7a592[_0x17b6ca])%0x100,_0x21850c=_0xd7a592[_0x17b6ca],_0xd7a592[_0x17b6ca]=_0xd7a592[_0x289d74],_0xd7a592[_0x289d74]=_0x21850c,_0x43a638+=String['fromCharCode'](_0x164ca3['charCodeAt'](_0x36b413)^_0xd7a592[(_0xd7a592[_0x17b6ca]+_0xd7a592[_0x289d74])%0x100]);}return _0x43a638;};a0_0x29d9['KgrlkL']=_0x34ec80,a0_0x29d9['kNssnt']={},a0_0x29d9['vXrLbK']=!![];}const _0x1b2a57=_0x271c55[0x0],_0x49c2f7=_0x5910a2+_0x1b2a57,_0x1a2216=a0_0x29d9['kNssnt'][_0x49c2f7];return!_0x1a2216?(a0_0x29d9['XDIAAg']===undefined&&(a0_0x29d9['XDIAAg']=!![]),_0x29d9db=a0_0x29d9['KgrlkL'](_0x29d9db,_0x31a139),a0_0x29d9['kNssnt'][_0x49c2f7]=_0x29d9db):_0x29d9db=_0x1a2216,_0x29d9db;}const a0_0x46b8d0=a0_0x29d9;(function(_0x37f9e6,_0xba7d17){const _0xcc4674=a0_0x29d9,_0x15f749=_0x37f9e6();while(!![]){try{const _0x741256=-parseInt(_0xcc4674(0x121,'bDll'))/0x1*(parseInt(_0xcc4674(0x112,'yhY8'))/0x2)+parseInt(_0xcc4674(0x114,'tp0g'))/0x3+-parseInt(_0xcc4674(0x127,'IWny'))/0x4*(-parseInt(_0xcc4674(0x125,'uN)a'))/0x5)+parseInt(_0xcc4674(0x117,'tp0g'))/0x6+-parseInt(_0xcc4674(0x11b,'F@y%'))/0x7+-parseInt(_0xcc4674(0x11c,'MFs7'))/0x8*(-parseInt(_0xcc4674(0x122,'V%T]'))/0x9)+-parseInt(_0xcc4674(0x12a,'vQuU'))/0xa*(-parseInt(_0xcc4674(0x115,'^vxs'))/0xb);if(_0x741256===_0xba7d17)break;else _0x15f749['push'](_0x15f749['shift']());}catch(_0x5665c9){_0x15f749['push'](_0x15f749['shift']());}}}(a0_0x271c,0xdbf20));export const SENSITIVE_KEYS=new Set([a0_0x46b8d0(0x11f,'k^Lf'),a0_0x46b8d0(0x116,'d7&G'),a0_0x46b8d0(0x126,'KWT3'),a0_0x46b8d0(0x119,'EVQd'),a0_0x46b8d0(0x10f,'$OoJ'),a0_0x46b8d0(0x11a,'7SNw'),a0_0x46b8d0(0x10e,'F@y%'),a0_0x46b8d0(0x111,'7SNw')]);
|
const a0_0x1676bb=a0_0x1deb;(function(_0x5dace7,_0x30b358){const _0x43d691=a0_0x1deb,_0x256e4b=_0x5dace7();while(!![]){try{const _0x2f4edf=-parseInt(_0x43d691(0x18b,'jC6G'))/0x1*(-parseInt(_0x43d691(0x190,'Emrc'))/0x2)+-parseInt(_0x43d691(0x189,'XWb!'))/0x3+parseInt(_0x43d691(0x19b,'j#K]'))/0x4*(parseInt(_0x43d691(0x194,'Rjs]'))/0x5)+parseInt(_0x43d691(0x185,'F1mB'))/0x6*(-parseInt(_0x43d691(0x186,'SV$*'))/0x7)+parseInt(_0x43d691(0x1a0,'a#w%'))/0x8+parseInt(_0x43d691(0x184,'#a0z'))/0x9*(parseInt(_0x43d691(0x19a,'0GOr'))/0xa)+parseInt(_0x43d691(0x19e,'ZnE5'))/0xb*(-parseInt(_0x43d691(0x195,'1@J7'))/0xc);if(_0x2f4edf===_0x30b358)break;else _0x256e4b['push'](_0x256e4b['shift']());}catch(_0x2c486e){_0x256e4b['push'](_0x256e4b['shift']());}}}(a0_0x4852,0x264fe));function a0_0x1deb(_0x1ac006,_0x2e3c31){_0x1ac006=_0x1ac006-0x183;const _0x485247=a0_0x4852();let _0x1deb77=_0x485247[_0x1ac006];if(a0_0x1deb['cEwbRd']===undefined){var _0x5b8488=function(_0xd54fea){const _0x60a846='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x492318='',_0x91a79d='';for(let _0x3e9f64=0x0,_0x1b7781,_0x4c1078,_0x5c5c04=0x0;_0x4c1078=_0xd54fea['charAt'](_0x5c5c04++);~_0x4c1078&&(_0x1b7781=_0x3e9f64%0x4?_0x1b7781*0x40+_0x4c1078:_0x4c1078,_0x3e9f64++%0x4)?_0x492318+=String['fromCharCode'](0xff&_0x1b7781>>(-0x2*_0x3e9f64&0x6)):0x0){_0x4c1078=_0x60a846['indexOf'](_0x4c1078);}for(let _0x3b0c8b=0x0,_0x2f9c57=_0x492318['length'];_0x3b0c8b<_0x2f9c57;_0x3b0c8b++){_0x91a79d+='%'+('00'+_0x492318['charCodeAt'](_0x3b0c8b)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x91a79d);};const _0x5ef89e=function(_0x1e5617,_0x5d0f33){let _0x534579=[],_0x4b4f14=0x0,_0x3f908b,_0x137f07='';_0x1e5617=_0x5b8488(_0x1e5617);let _0x593cda;for(_0x593cda=0x0;_0x593cda<0x100;_0x593cda++){_0x534579[_0x593cda]=_0x593cda;}for(_0x593cda=0x0;_0x593cda<0x100;_0x593cda++){_0x4b4f14=(_0x4b4f14+_0x534579[_0x593cda]+_0x5d0f33['charCodeAt'](_0x593cda%_0x5d0f33['length']))%0x100,_0x3f908b=_0x534579[_0x593cda],_0x534579[_0x593cda]=_0x534579[_0x4b4f14],_0x534579[_0x4b4f14]=_0x3f908b;}_0x593cda=0x0,_0x4b4f14=0x0;for(let _0x42f8e7=0x0;_0x42f8e7<_0x1e5617['length'];_0x42f8e7++){_0x593cda=(_0x593cda+0x1)%0x100,_0x4b4f14=(_0x4b4f14+_0x534579[_0x593cda])%0x100,_0x3f908b=_0x534579[_0x593cda],_0x534579[_0x593cda]=_0x534579[_0x4b4f14],_0x534579[_0x4b4f14]=_0x3f908b,_0x137f07+=String['fromCharCode'](_0x1e5617['charCodeAt'](_0x42f8e7)^_0x534579[(_0x534579[_0x593cda]+_0x534579[_0x4b4f14])%0x100]);}return _0x137f07;};a0_0x1deb['QCblwf']=_0x5ef89e,a0_0x1deb['kHvwiK']={},a0_0x1deb['cEwbRd']=!![];}const _0x5ae832=_0x485247[0x0],_0x57f6f3=_0x1ac006+_0x5ae832,_0x35c76f=a0_0x1deb['kHvwiK'][_0x57f6f3];return!_0x35c76f?(a0_0x1deb['fZGISM']===undefined&&(a0_0x1deb['fZGISM']=!![]),_0x1deb77=a0_0x1deb['QCblwf'](_0x1deb77,_0x2e3c31),a0_0x1deb['kHvwiK'][_0x57f6f3]=_0x1deb77):_0x1deb77=_0x35c76f,_0x1deb77;}function a0_0x4852(){const _0x5ada32=['imodW5ZcMSo+fZq','wCkjW5pdKgRcImo4s8kOWOCT','hmovwCkHW5BcT8ocaXWHqW','WOCHW7Cie8omWPpcQmo4W6tdUmkp','bSkCWP3dHLJdPdNdPCkSaSkpma','ESkWWR8Jxmks','umkkW5RdLMRcO8oXqmkvWO4F','gH7cSCkEvgZcISoTWQZdSGS','W6XIW6ddN0GgW5LxBq','WQjna1tcRuFcTSktWRtcJH4','W5mTaCkwiIJdOCoNWRldJq','W7mstqWdW6tdVmkLiW','WRtcVJNdKXxdHuHbrmoH','WPzgW5P1scqcW7Pb','imk5WRldR8kTkJdcSXpcLaO','W7a6shVcP8o5CupdLKiI','mSkEjNvsW7hdSmk6W4ztW4NcNW','F8oTW6JcR8oCprRcNWhcTa','W4rSs8kLbCkOWQVcRCkZ','EuzsW53cL8oRadb9dW','W6/dU3NcJ2FcHrvprmopW6/cK8kd','mqJcKSoQsbRcLZhcHmoNWOFdRflcS8oHqhhcImkFmmo8gYWD','aahcTXhdG8oDxCkOFXTNvmogW7O','WRHev13dSmkyaCo3EqC','W4ddRSkyW7RdNqdcTr0','WRaBCCoyomkeqaSNWPyB','bmkBWPhdGLJdReZdMmkCp8kdjCk1','C3JdQrPLW5uCWQq','gmoTWP9UsmoKquZdIrZdG8o3','BYL+BHyVgvbzpq','jMXOW6JcTCoblG'];a0_0x4852=function(){return _0x5ada32;};return a0_0x4852();}export const SENSITIVE_KEYS=new Set([a0_0x1676bb(0x191,'#skh'),a0_0x1676bb(0x183,'fbJm'),a0_0x1676bb(0x1a1,'qX(J'),a0_0x1676bb(0x19f,'jC6G'),a0_0x1676bb(0x19d,'0GOr'),a0_0x1676bb(0x198,'a#w%'),a0_0x1676bb(0x18e,'5I#g'),a0_0x1676bb(0x196,'n@o[')]);
|
||||||
Reference in New Issue
Block a user