添加假流功能的设置选项和事件绑定

This commit is contained in:
2026-01-20 11:56:58 +08:00
parent 859588461d
commit ab161e475d
3 changed files with 86 additions and 4 deletions

View File

@@ -139,6 +139,14 @@
</label> </label>
</div> </div>
<div class="amily2_settings_block">
<label for="amily2_ngms_fakestream_enabled">启用 Fake Stream (防超时)</label>
<label class="toggle-switch">
<input id="amily2_ngms_fakestream_enabled" type="checkbox" />
<span class="slider"></span>
</label>
</div>
<div class="amily2_settings_block" id="amily2_ngms_content" style="display: none;"> <div class="amily2_settings_block" id="amily2_ngms_content" style="display: none;">
<div class="control-group"> <div class="control-group">
<label for="amily2_ngms_api_mode">API调用模式</label> <label for="amily2_ngms_api_mode">API调用模式</label>

View File

@@ -50,7 +50,8 @@ export function getNgmsApiSettings() {
model: extension_settings[extensionName]?.ngmsModel || '', model: extension_settings[extensionName]?.ngmsModel || '',
maxTokens: extension_settings[extensionName]?.ngmsMaxTokens || 4000, maxTokens: extension_settings[extensionName]?.ngmsMaxTokens || 4000,
temperature: extension_settings[extensionName]?.ngmsTemperature || 0.7, temperature: extension_settings[extensionName]?.ngmsTemperature || 0.7,
tavernProfile: extension_settings[extensionName]?.ngmsTavernProfile || '' tavernProfile: extension_settings[extensionName]?.ngmsTavernProfile || '',
useFakeStream: extension_settings[extensionName]?.ngmsFakeStreamEnabled || false
}; };
} }
@@ -73,12 +74,22 @@ export async function callNgmsAI(messages, options = {}) {
...options ...options
}; };
// 确保 stream 标志位存在
finalOptions.stream = finalOptions.useFakeStream ?? apiSettings.useFakeStream ?? false;
if (finalOptions.apiMode !== 'sillytavern_preset') { if (finalOptions.apiMode !== 'sillytavern_preset') {
if (!finalOptions.apiUrl || !finalOptions.model || !finalOptions.apiKey) { if (!finalOptions.apiUrl || !finalOptions.model || !finalOptions.apiKey) {
console.warn("[Amily2-Ngms外交部] API配置不完整无法调用AI"); console.warn("[Amily2-Ngms外交部] API配置不完整无法调用AI");
toastr.error("API配置不完整请检查URL、Key和模型配置。", "Ngms-外交部"); toastr.error("API配置不完整请检查URL、Key和模型配置。", "Ngms-外交部");
return null; return null;
} }
} else {
// [限制] 预设模式暂不支持流式
if (finalOptions.stream) {
console.warn("[Amily2-Ngms] 预设模式目前尚不支持流式处理方案,已自动切换为标准模式。");
toastr.warning("SillyTavern预设模式目前暂不支持流式处理假流式已为您切换为标准请求模式。该功能将在后续版本中支持。", "Ngms-外交部");
finalOptions.stream = false;
}
} }
console.groupCollapsed(`[Amily2号-Ngms统一API调用] ${new Date().toLocaleTimeString()}`); console.groupCollapsed(`[Amily2号-Ngms统一API调用] ${new Date().toLocaleTimeString()}`);
@@ -87,6 +98,7 @@ export async function callNgmsAI(messages, options = {}) {
model: finalOptions.model, model: finalOptions.model,
maxTokens: finalOptions.maxTokens, maxTokens: finalOptions.maxTokens,
temperature: finalOptions.temperature, temperature: finalOptions.temperature,
stream: finalOptions.stream,
messagesCount: messages.length messagesCount: messages.length
}); });
console.log("【消息内容】:", messages); console.log("【消息内容】:", messages);
@@ -139,6 +151,54 @@ export async function callNgmsAI(messages, options = {}) {
} }
} }
async function fetchFakeStream(url, opts) {
const res = await fetch(url, opts);
if (!res.ok) {
const errorText = await res.text();
throw new Error(`Stream HTTP ${res.status}: ${errorText}`);
}
const reader = res.body.getReader();
const decoder = new TextDecoder();
let fullContent = "";
let buffer = "";
try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed === 'data: [DONE]') continue;
if (trimmed.startsWith('data: ')) {
try {
const json = JSON.parse(trimmed.substring(6));
const delta = json.choices?.[0]?.delta?.content;
if (delta) fullContent += delta;
} catch (e) {
console.warn('[NgmsApi] SSE Parse Error:', e);
}
}
}
}
} finally {
reader.releaseLock();
}
if (!fullContent && buffer) {
try {
const data = JSON.parse(buffer);
return data.choices?.[0]?.message?.content || data.content || buffer;
} catch { return buffer; }
}
return fullContent;
}
async function callNgmsOpenAITest(messages, options) { async function callNgmsOpenAITest(messages, options) {
const isGoogleApi = options.apiUrl.includes('googleapis.com'); const isGoogleApi = options.apiUrl.includes('googleapis.com');
@@ -148,7 +208,7 @@ async function callNgmsOpenAITest(messages, options) {
model: options.model, model: options.model,
reverse_proxy: options.apiUrl, reverse_proxy: options.apiUrl,
proxy_password: options.apiKey, proxy_password: options.apiKey,
stream: false, stream: !!options.stream,
max_tokens: options.maxTokens || 30000, max_tokens: options.maxTokens || 30000,
temperature: options.temperature || 1, temperature: options.temperature || 1,
top_p: options.top_p || 1, top_p: options.top_p || 1,
@@ -167,11 +227,17 @@ async function callNgmsOpenAITest(messages, options) {
}); });
} }
const response = await fetch('/api/backends/chat-completions/generate', { const fetchOpts = {
method: 'POST', method: 'POST',
headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' }, headers: { ...getRequestHeaders(), 'Content-Type': 'application/json' },
body: JSON.stringify(body) body: JSON.stringify(body)
}); };
if (options.stream) {
return await fetchFakeStream('/api/backends/chat-completions/generate', fetchOpts);
}
const response = await fetch('/api/backends/chat-completions/generate', fetchOpts);
if (!response.ok) { if (!response.ok) {
const errorText = await response.text(); const errorText = await response.text();

View File

@@ -408,6 +408,7 @@ function bindNgmsApiEvents() {
// Ngms API 开关控制 // Ngms API 开关控制
const ngmsToggle = document.getElementById('amily2_ngms_enabled'); const ngmsToggle = document.getElementById('amily2_ngms_enabled');
const ngmsFakeStreamToggle = document.getElementById('amily2_ngms_fakestream_enabled');
const ngmsContent = document.getElementById('amily2_ngms_content'); const ngmsContent = document.getElementById('amily2_ngms_content');
if (ngmsToggle && ngmsContent) { if (ngmsToggle && ngmsContent) {
@@ -421,6 +422,13 @@ function bindNgmsApiEvents() {
}); });
} }
if (ngmsFakeStreamToggle) {
ngmsFakeStreamToggle.checked = extension_settings[extensionName].ngmsFakeStreamEnabled ?? false;
ngmsFakeStreamToggle.addEventListener('change', function() {
updateAndSaveSetting('ngmsFakeStreamEnabled', this.checked);
});
}
// API模式切换 // API模式切换
const apiModeSelect = document.getElementById('amily2_ngms_api_mode'); const apiModeSelect = document.getElementById('amily2_ngms_api_mode');
const compatibleConfig = document.getElementById('amily2_ngms_compatible_config'); const compatibleConfig = document.getElementById('amily2_ngms_compatible_config');