From 9d5d8b749245e2b4c501e579676e4e2aabcd8f19 Mon Sep 17 00:00:00 2001 From: MagicalAstrogy <103271693+MagicalAstrogy@users.noreply.github.com> Date: Sat, 20 Jun 2026 20:32:48 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20v4=20=E6=89=80?= =?UTF-8?q?=E9=9C=80=E7=9A=84=E6=A0=BC=E5=BC=8F=E5=8C=96=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/function/update/invoke_extra_model.ts | 173 +++++++++++++++++++-- src/panel/update/Prompt.vue | 18 ++- src/panel/update/prompt_toolcall.md | 4 +- src/store.ts | 7 +- tests/extra_model_max_chat_history.test.ts | 167 +++++++++++++++++++- tests/setup.ts | 7 + tests/store_response_format.test.ts | 14 ++ 7 files changed, 375 insertions(+), 15 deletions(-) diff --git a/src/function/update/invoke_extra_model.ts b/src/function/update/invoke_extra_model.ts index 771cf9f..d951bb3 100644 --- a/src/function/update/invoke_extra_model.ts +++ b/src/function/update/invoke_extra_model.ts @@ -21,15 +21,134 @@ import { useDataStore } from '@/store'; import { normalizeBaseURL } from '@/util'; import { literalYamlify, uuidv4 } from '@util/common'; import { compare } from 'compare-versions'; +import YAML from 'yaml'; //测试用,为了使首次请求必失败 let debug_extra_request_counter = 0; +const V4_COMPATIBLE_FORMATTED_OUTPUT = '格式化输出(v4兼容)'; +const JSON_OBJECT_CUSTOM_INCLUDE_BODY = Object.freeze({ + response_format: { + type: 'json_object', + }, +}); + function generateRandomHeader(): string { return _.times(4, () => uuidv4().slice(0, 8)).join('\n'); } -function setExtraAnalysisStates() { +function isV4CompatibleFormattedOutput(): boolean { + return useDataStore().settings.额外模型解析配置.应答格式 === V4_COMPATIBLE_FORMATTED_OUTPUT; +} + +function assertV4CompatibleFormattedOutputUsable() { + const store = useDataStore(); + if ( + store.settings.额外模型解析配置.应答格式 === V4_COMPATIBLE_FORMATTED_OUTPUT && + store.settings.额外模型解析配置.模型来源 === '与插头相同' + ) { + throw new Error( + '[MVU额外模型解析]格式化输出(v4兼容)需要额外模型来源为自定义,不能与插头相同。' + ); + } +} + +function isPlainObject(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function parseCustomIncludeBody(body: unknown): Record { + if (typeof body !== 'string' || body.trim() === '') { + return {}; + } + + const parsed = YAML.parse(body); + if (isPlainObject(parsed)) { + return parsed; + } + if (Array.isArray(parsed)) { + return Object.assign({}, ...parsed.filter(isPlainObject)); + } + throw new Error('[MVU额外模型解析]custom_include_body 不是 YAML object,无法合并配置。'); +} + +function buildJsonObjectCustomIncludeBody(original_body: unknown): string { + return YAML.stringify({ + ...parseCustomIncludeBody(original_body), + ...JSON_OBJECT_CUSTOM_INCLUDE_BODY, + }).trimEnd(); +} + +async function saveSillyTavernSettings() { + const save_settings = + typeof builtin === 'undefined' ? undefined : builtin.saveSettings.bind(builtin); + if (typeof save_settings !== 'function') { + throw new Error('[MVU额外模型解析]无法获取 SillyTavern saveSettings,不能临时更新配置。'); + } + await save_settings(); +} + +let temporary_json_object_response_format_state: { + had_original_body: boolean; + original_body: unknown; +} | null = null; + +async function setTemporaryJsonObjectResponseFormat() { + if (!isV4CompatibleFormattedOutput()) { + temporary_json_object_response_format_state = null; + return; + } + + assertV4CompatibleFormattedOutputUsable(); + const oai_settings = SillyTavern.chatCompletionSettings; + if (!isPlainObject(oai_settings)) { + throw new Error('[MVU额外模型解析]无法获取 SillyTavern OpenAI 设置。'); + } + + const had_original_body = Object.prototype.hasOwnProperty.call( + oai_settings, + 'custom_include_body' + ); + const original_body = oai_settings.custom_include_body; + oai_settings.custom_include_body = buildJsonObjectCustomIncludeBody(original_body); + try { + await saveSillyTavernSettings(); + temporary_json_object_response_format_state = { + had_original_body, + original_body, + }; + } catch (error) { + if (had_original_body) { + oai_settings.custom_include_body = original_body; + } else { + delete oai_settings.custom_include_body; + } + temporary_json_object_response_format_state = null; + throw error; + } +} + +async function restoreTemporaryJsonObjectResponseFormat() { + if (!temporary_json_object_response_format_state) { + return; + } + + const oai_settings = SillyTavern.chatCompletionSettings; + if (!isPlainObject(oai_settings)) { + throw new Error('[MVU额外模型解析]无法获取 SillyTavern OpenAI 设置,不能恢复配置。'); + } + + const { had_original_body, original_body } = temporary_json_object_response_format_state; + temporary_json_object_response_format_state = null; + if (had_original_body) { + oai_settings.custom_include_body = original_body; + } else { + delete oai_settings.custom_include_body; + } + await saveSillyTavernSettings(); +} + +async function setExtraAnalysisStates() { const store = useDataStore(); if (store.runtimes.is_during_extra_analysis === true) { @@ -41,15 +160,25 @@ function setExtraAnalysisStates() { //因为这个操作是幂等的,所以无所谓。 store.runtimes.is_during_extra_analysis = true; + try { + await setTemporaryJsonObjectResponseFormat(); + } catch (error) { + store.runtimes.is_during_extra_analysis = false; + throw error; + } } -function unsetExtraAnalysisStates() { +async function unsetExtraAnalysisStates() { const store = useDataStore(); SillyTavern.unregisterMacro('lastUserMessage'); clearExtraModelRequestOverrides(); - store.runtimes.is_during_extra_analysis = false; store.runtimes.is_function_call_enabled = false; + try { + await restoreTemporaryJsonObjectResponseFormat(); + } finally { + store.runtimes.is_during_extra_analysis = false; + } } let is_analysis_in_progress = false; @@ -78,28 +207,36 @@ export async function invokeExtraModelWithStrategy(): Promise { is_manual_canceled: boolean; }> => { let is_manual_canceled = false; + let did_set_extra_analysis_states = false; try { - setExtraAnalysisStates(); + await setExtraAnalysisStates(); + did_set_extra_analysis_states = true; return { result: await recordedInvoke(), is_manual_canceled: false }; } catch (e) { /** 已经记录, 忽略 */ if (e === 'Clicked stop button') is_manual_canceled = true; } finally { - unsetExtraAnalysisStates(); + if (did_set_extra_analysis_states) { + await unsetExtraAnalysisStates(); + } } return { result: null, is_manual_canceled: is_manual_canceled }; }; const concurrentInvoke = async (times: number) => { const uuids = _.times(times, uuidv4); + let did_set_extra_analysis_states = false; try { - setExtraAnalysisStates(); + await setExtraAnalysisStates(); + did_set_extra_analysis_states = true; //在函数调用的模式下,允许接受 **任意** 有效的函数结果,因此被允许被覆盖。 return await Promise.any(uuids.map(recordedInvoke)); } catch (e) { /** 已经记录, 忽略 */ } finally { uuids.forEach(stopGenerationById); - unsetExtraAnalysisStates(); + if (did_set_extra_analysis_states) { + await unsetExtraAnalysisStates(); + } } return null; }; @@ -152,6 +289,8 @@ export async function invokeExtraModelWithStrategy(): Promise { ); } return concurrentInvoke(store.settings.额外模型解析配置.请求次数 - 1); + default: + return null; } } finally { is_analysis_in_progress = false; @@ -162,11 +301,15 @@ export async function invokeExtraModelWithStrategy(): Promise { * @brief 调用额外模型解析,可能会抛出异常。 */ export async function generateExtraModel(): Promise { + let did_set_extra_analysis_states = false; try { - setExtraAnalysisStates(); + await setExtraAnalysisStates(); + did_set_extra_analysis_states = true; return await invokeExtraModel(); } finally { - unsetExtraAnalysisStates(); + if (did_set_extra_analysis_states) { + await unsetExtraAnalysisStates(); + } } } @@ -245,7 +388,7 @@ function normalizeGenerateResultByResponseFormat( result: string | GenerateToolCallResult, response_format: string ): string { - if (response_format === '格式化输出') { + if (response_format === '格式化输出' || response_format === V4_COMPATIBLE_FORMATTED_OUTPUT) { const formatted = extractFromFormattedOutput(result); if (formatted) { return formatted; @@ -257,6 +400,9 @@ function normalizeGenerateResultByResponseFormat( async function requestReply(generation_id?: string, batch_id?: string): Promise { const store = useDataStore(); const response_format = store.settings.额外模型解析配置.应答格式; + const is_v4_compatible_formatted_output = response_format === V4_COMPATIBLE_FORMATTED_OUTPUT; + + assertV4CompatibleFormattedOutputUsable(); const config: GenerateRawConfig = { user_input: '遵循指令', @@ -280,6 +426,9 @@ async function requestReply(generation_id?: string, batch_id?: string): Promise< top_p: unset_if_equal(store.settings.额外模型解析配置.top_p, 1), top_k: unset_if_equal(store.settings.额外模型解析配置.top_k, 0), }; + if (is_v4_compatible_formatted_output) { + config.custom_api.source = 'custom'; + } } let task = decoded_extra_model_task; @@ -292,6 +441,10 @@ async function requestReply(generation_id?: string, batch_id?: string): Promise< task += '\n You are in formatted-output mode. Do not output tags, markdown, or prose. Return only a JSON object matching the provided json_schema: {"analysis":"...","json_patch":[...]}. Put MVU JsonPatch dialect operations in `json_patch`.'; config.json_schema = MVU_JSON_PATCH_RESPONSE_SCHEMA; + } else if (is_v4_compatible_formatted_output) { + task += + '\n You are in formatted-output mode. Do not output tags, markdown, or prose. Return only a JSON object: {"analysis":"...","json_patch":[...]}. Put MVU JsonPatch dialect operations in `json_patch`. Return exactly one JSON object that conforms to this schema:' + + JSON.stringify(MVU_JSON_PATCH_RESPONSE_SCHEMA.value); } //因为部分预设会用到 {{lastUserMessage}},因此进行修正。 diff --git a/src/panel/update/Prompt.vue b/src/panel/update/Prompt.vue index 12bded0..2e9cee6 100644 --- a/src/panel/update/Prompt.vue +++ b/src/panel/update/Prompt.vue @@ -82,8 +82,12 @@ watch( ); watch( - () => store.settings.额外模型解析配置.应答格式, - value => { + () => + [ + store.settings.额外模型解析配置.应答格式, + store.settings.额外模型解析配置.模型来源, + ] as const, + ([value, model_source]) => { if (value === '工具调用') { const version_message = getFunctionCallingApiVersionUnsupportedMessage(); if (version_message) { @@ -105,6 +109,16 @@ watch( return; } } + if (value === '格式化输出(v4兼容)' && model_source === '与插头相同') { + toastr.error( + '格式化输出(v4兼容)需要额外模型来源为自定义,不能与插头相同', + "[MVU]无法使用'格式化输出(v4兼容)'", + { + timeOut: 5000, + } + ); + store.settings.额外模型解析配置.应答格式 = '聊天消息'; + } } ); diff --git a/src/panel/update/prompt_toolcall.md b/src/panel/update/prompt_toolcall.md index 7ea1684..5acf91b 100644 --- a/src/panel/update/prompt_toolcall.md +++ b/src/panel/update/prompt_toolcall.md @@ -7,6 +7,8 @@ calling。通常能减少正文干扰, 但不支持工具调用的模型或反代会报错或退化。 - **格式化输出**: 要求提供商支持 OpenAI 兼容的 `response_format.json_schema`。通常最适合 JsonPatch 变量更新, 因为返回会被约束为结构化 JSON。 +- **格式化输出(v4兼容)**: 用于只支持 `response_format.type = json_object` 的渠道,如 dsv4f 等。 + 这个模式仅在额外模型来源为 **自定义** 时可用。 如果你的渠道明确支持 `response_format.json_schema`, 优先尝试 **格式化输出**。如果不支持, 改用 -**聊天消息**;如果渠道支持 tools/function calling, 也可以尝试 **工具调用**。 +**格式化输出(v4兼容)** 或 **聊天消息**;如果渠道支持 tools/function calling, 也可以尝试 **工具调用**。 diff --git a/src/store.ts b/src/store.ts index 11c129a..501a237 100644 --- a/src/store.ts +++ b/src/store.ts @@ -4,7 +4,12 @@ import { defineStore } from 'pinia'; import { ref, toRaw, watch } from 'vue'; import * as z from 'zod'; -export const EXTRA_MODEL_RESPONSE_FORMATS = ['聊天消息', '工具调用', '格式化输出'] as const; +export const EXTRA_MODEL_RESPONSE_FORMATS = [ + '聊天消息', + '工具调用', + '格式化输出', + '格式化输出(v4兼容)', +] as const; const ExtraModelResponseFormat = z.enum(EXTRA_MODEL_RESPONSE_FORMATS); diff --git a/tests/extra_model_max_chat_history.test.ts b/tests/extra_model_max_chat_history.test.ts index fe432f0..93a05fb 100644 --- a/tests/extra_model_max_chat_history.test.ts +++ b/tests/extra_model_max_chat_history.test.ts @@ -1,4 +1,7 @@ -import { generateExtraModel } from '@/function/update/invoke_extra_model'; +import { + generateExtraModel, + invokeExtraModelWithStrategy, +} from '@/function/update/invoke_extra_model'; import { useDataStore } from '@/store'; describe('extra model max chat history', () => { @@ -29,4 +32,166 @@ describe('extra model max chat history', () => { }) ); }); + + test('uses temporary saved custom json_object response format for v4 compatible formatted output', async () => { + const store = useDataStore(); + store.versions.tavernhelper = '4.3.9'; + store.settings.额外模型解析配置.应答格式 = '格式化输出(v4兼容)'; + store.settings.额外模型解析配置.模型来源 = '自定义'; + store.settings.额外模型解析配置.api地址 = 'https://example.com/v1'; + store.settings.额外模型解析配置.模型名称 = 'deepseek-chat'; + (globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body = + 'existing_flag: true'; + + (globalThis as any).generateRaw = jest.fn().mockImplementation(async config => { + expect((globalThis as any).builtin.saveSettings).toHaveBeenCalledTimes(1); + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body) + .toContain(`response_format:\n type: json_object`); + expect(config.custom_api).toEqual( + expect.objectContaining({ + source: 'custom', + apiurl: 'https://example.com/v1', + model: 'deepseek-chat', + }) + ); + expect(config.json_schema).toBeUndefined(); + return JSON.stringify({ + analysis: 'ok', + json_patch: [{ op: 'replace', path: '/x', value: 1 }], + }); + }); + + const result = await generateExtraModel(); + + expect(result).toContain(''); + expect(result).toContain('"op": "replace"'); + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body).toBe( + 'existing_flag: true' + ); + expect((globalThis as any).builtin.saveSettings).toHaveBeenCalledTimes(2); + }); + + test('restores saved custom json_object response format after v4 compatible request failure', async () => { + const store = useDataStore(); + store.versions.tavernhelper = '4.3.9'; + store.settings.额外模型解析配置.应答格式 = '格式化输出(v4兼容)'; + store.settings.额外模型解析配置.模型来源 = '自定义'; + (globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body = + 'existing_flag: true'; + (globalThis as any).generateRaw = jest.fn().mockRejectedValue(new Error('request failed')); + + await expect(generateExtraModel()).rejects.toThrow('request failed'); + + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body).toBe( + 'existing_flag: true' + ); + expect((globalThis as any).builtin.saveSettings).toHaveBeenCalledTimes(2); + }); + + test('keeps temporary custom json_object response format for concurrent strategy requests', async () => { + const store = useDataStore(); + store.versions.tavernhelper = '4.3.9'; + store.settings.额外模型解析配置.应答格式 = '格式化输出(v4兼容)'; + store.settings.额外模型解析配置.模型来源 = '自定义'; + store.settings.额外模型解析配置.请求方式 = '同时请求多次'; + store.settings.额外模型解析配置.请求次数 = 2; + store.settings.通知.额外模型解析中 = false; + (globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body = + 'existing_flag: true'; + + (globalThis as any).generateRaw = jest.fn().mockImplementation(async config => { + expect((globalThis as any).builtin.saveSettings).toHaveBeenCalledTimes(1); + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body) + .toContain(`response_format:\n type: json_object`); + expect(config.custom_api).toEqual( + expect.objectContaining({ + source: 'custom', + }) + ); + return JSON.stringify({ + analysis: 'ok', + json_patch: [{ op: 'replace', path: '/x', value: 1 }], + }); + }); + + const result = await invokeExtraModelWithStrategy(); + + expect(result).toContain(''); + expect((globalThis as any).generateRaw).toHaveBeenCalledTimes(2); + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body).toBe( + 'existing_flag: true' + ); + expect((globalThis as any).builtin.saveSettings).toHaveBeenCalledTimes(2); + }); + + test('blocks reentry while temporary settings save is pending', async () => { + const store = useDataStore(); + store.versions.tavernhelper = '4.3.9'; + store.settings.额外模型解析配置.应答格式 = '格式化输出(v4兼容)'; + store.settings.额外模型解析配置.模型来源 = '自定义'; + + let resolve_save_settings!: () => void; + let save_settings_calls = 0; + (globalThis as any).builtin.saveSettings = jest.fn(() => { + save_settings_calls++; + if (save_settings_calls === 1) { + return new Promise(resolve => { + resolve_save_settings = resolve; + }); + } + return Promise.resolve(); + }); + + const first_request = generateExtraModel(); + await Promise.resolve(); + + await expect(generateExtraModel()).rejects.toThrow( + 'setExtraAnalysisStates() should not be called recursively.' + ); + + resolve_save_settings(); + (globalThis as any).generateRaw = jest.fn().mockResolvedValue( + JSON.stringify({ + analysis: 'ok', + json_patch: [{ op: 'replace', path: '/x', value: 1 }], + }) + ); + await first_request; + expect((globalThis as any).SillyTavern.chatCompletionSettings.custom_include_body).toBe( + undefined + ); + }); + + test('rejects v4 compatible formatted output when model source matches the extension', async () => { + const store = useDataStore(); + store.settings.额外模型解析配置.应答格式 = '格式化输出(v4兼容)'; + store.settings.额外模型解析配置.模型来源 = '与插头相同'; + + await expect(generateExtraModel()).rejects.toThrow('不能与插头相同'); + + expect((globalThis as any).generateRaw).not.toHaveBeenCalled(); + expect((globalThis as any).builtin.saveSettings).not.toHaveBeenCalled(); + }); + + test('keeps json_schema for regular formatted output without temporary settings save', async () => { + const store = useDataStore(); + store.settings.额外模型解析配置.应答格式 = '格式化输出'; + (globalThis as any).generateRaw = jest.fn().mockResolvedValue( + JSON.stringify({ + analysis: 'ok', + json_patch: [{ op: 'replace', path: '/x', value: 1 }], + }) + ); + + await generateExtraModel(); + + expect((globalThis as any).generateRaw).toHaveBeenCalledWith( + expect.objectContaining({ + json_schema: expect.objectContaining({ + name: 'mvu_json_patch', + }), + }) + ); + expect((globalThis as any).builtin.saveSettings).not.toHaveBeenCalled(); + }); }); diff --git a/tests/setup.ts b/tests/setup.ts index 22a4e7e..10148a1 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -26,6 +26,9 @@ import { watch } from 'vue'; chat: [], extension_settings: {}, }; +(globalThis as any).builtin = { + saveSettings: jest.fn().mockResolvedValue(undefined), +}; (globalThis as any).appendInexistentScriptButtons = jest.fn(); (globalThis as any).getButtonEvent = jest.fn((button_name: string) => button_name); @@ -61,6 +64,9 @@ const __eventHandlers = new Map unknown>>( beforeEach(() => { setActivePinia(createPinia()); __eventHandlers.clear(); + (globalThis as any).SillyTavern.chatCompletionSettings = { function_calling: true }; + (globalThis as any).builtin.saveSettings = jest.fn().mockResolvedValue(undefined); + (globalThis as any).stopGenerationById = jest.fn(); }); // Mock functions that are not available in test environment @@ -92,6 +98,7 @@ beforeEach(() => { (globalThis as any).getChatMessages = jest.fn(); (globalThis as any).getVariables = jest.fn(); (globalThis as any).getLastMessageId = jest.fn(); +(globalThis as any).stopGenerationById = jest.fn(); (globalThis as any).replaceVariables = jest.fn(); (globalThis as any).setChatMessage = jest.fn(); (globalThis as any).setChatMessages = jest.fn(); diff --git a/tests/store_response_format.test.ts b/tests/store_response_format.test.ts index 78f3318..11e8c89 100644 --- a/tests/store_response_format.test.ts +++ b/tests/store_response_format.test.ts @@ -44,6 +44,20 @@ describe('extra model response format settings', () => { expect(store.settings.额外模型解析配置.应答格式).toBe('格式化输出'); }); + test('accepts v4 compatible formatted output response format', () => { + (globalThis as any).SillyTavern.extensionSettings = { + mvu_settings: { + 额外模型解析配置: { + 应答格式: '格式化输出(v4兼容)', + }, + }, + }; + + const store = useDataStore(); + + expect(store.settings.额外模型解析配置.应答格式).toBe('格式化输出(v4兼容)'); + }); + test('defaults max chat history to the previous hardcoded value', () => { const store = useDataStore(); From 2f48c92071693d9335b88a7e921a14a4fea10f2a Mon Sep 17 00:00:00 2001 From: MagicalAstrogy <103271693+MagicalAstrogy@users.noreply.github.com> Date: Sat, 20 Jun 2026 21:02:49 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=BC=80?= =?UTF-8?q?=E5=85=B3=20thinking?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 5 +++++ src/function/update/invoke_extra_model.ts | 9 +++++++++ src/panel/update/Prompt.vue | 12 ++++++++++++ src/store.ts | 1 + tests/extra_model_max_chat_history.test.ts | 3 +++ tests/store_response_format.test.ts | 6 ++++++ 6 files changed, 36 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b05a5af..e73a0e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2026-06-20 +## 体验改进 + - 现在支持调整在额外模型解析过程中,提供的消息楼层数。默认为 2。 + - 增加了应答格式 `格式化输出(v4兼容)`, 用于 官deepseek-v4 等渠道。(注:如果同时使用其他包含额外解析的插件,可能会有兼容性问题) + - # 2026-04-25 ## 体验改进 - 现在额外模型解析支持指定当前环境下的已有预设,代表使用那个预设的提示词结构,api/预设脚本/正则 等依然使用当前预设/手动指定的。 diff --git a/src/function/update/invoke_extra_model.ts b/src/function/update/invoke_extra_model.ts index d951bb3..9b805a3 100644 --- a/src/function/update/invoke_extra_model.ts +++ b/src/function/update/invoke_extra_model.ts @@ -32,6 +32,11 @@ const JSON_OBJECT_CUSTOM_INCLUDE_BODY = Object.freeze({ type: 'json_object', }, }); +const DISABLED_THINKING_CUSTOM_INCLUDE_BODY = Object.freeze({ + thinking: { + type: 'disabled', + }, +}); function generateRandomHeader(): string { return _.times(4, () => uuidv4().slice(0, 8)).join('\n'); @@ -73,9 +78,13 @@ function parseCustomIncludeBody(body: unknown): Record { } function buildJsonObjectCustomIncludeBody(original_body: unknown): string { + const store = useDataStore(); return YAML.stringify({ ...parseCustomIncludeBody(original_body), ...JSON_OBJECT_CUSTOM_INCLUDE_BODY, + ...(store.settings.额外模型解析配置.关闭thinking + ? DISABLED_THINKING_CUSTOM_INCLUDE_BODY + : {}), }).trimEnd(); } diff --git a/src/panel/update/Prompt.vue b/src/panel/update/Prompt.vue index 2e9cee6..6b52f01 100644 --- a/src/panel/update/Prompt.vue +++ b/src/panel/update/Prompt.vue @@ -29,6 +29,18 @@ /> + + + + 关闭 + + +