From b4c83e706b7ea5f23513e60f0f866222bad82a9e Mon Sep 17 00:00:00 2001 From: moranzhi Date: Mon, 26 Oct 2026 01:09:20 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A3=85=E9=85=8D=E5=8F=AA=E8=A1=A5=E7=BC=BA?= =?UTF-8?q?=E4=B8=8D=E9=87=8D=E6=8E=92=EF=BC=8C=E6=B6=88=E6=81=AF=E6=95=B4?= =?UTF-8?q?=E5=BD=A2=E4=B9=9F=E4=B8=8D=E5=86=8D=E6=8A=8A=E5=B0=BE=E9=83=A8?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=8F=90=E7=A4=BA=E6=90=AC=E5=88=B0=E5=89=8D?= =?UTF-8?q?=E9=9D=A2=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 工作台左轨的顺序被当成真实顺序用:补洞时只插缺的、不动已有的条目;整形把落单的 system 接在最后一条后面,之前是插在它前面,排出来的正文顺序跟左轨对不上。 --- src/llm/message-squash.ts | 7 +++- src/preset/markers.ts | 77 ++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 46 deletions(-) diff --git a/src/llm/message-squash.ts b/src/llm/message-squash.ts index 7e6392b..a01b06d 100644 --- a/src/llm/message-squash.ts +++ b/src/llm/message-squash.ts @@ -109,6 +109,11 @@ function prefixInto(message: ChatMessage, prefix: string): ChatMessage { return { ...message, content: joinText(prefix, contentOf(message)) } as ChatMessage; } +/** 排在末尾、后面没有可吞的消息时,往最后一条**后面**接(前缀会把顺序倒过来)。 */ +function suffixInto(message: ChatMessage, suffix: string): ChatMessage { + return { ...message, content: joinText(contentOf(message), suffix) } as ChatMessage; +} + /** * 把所有 system 内容并进**紧随其后**的非 system 消息(无则并回上一条), * 使序列只剩 user / assistant。 @@ -140,7 +145,7 @@ function foldSystemMessages(messages: ChatMessage[]): ChatMessage[] { if (pending.length > 0) { for (let i = out.length - 1; i >= 0; i--) { if (!isToolGroupMessage(out[i]!)) { - out[i] = prefixInto(out[i]!, pending.splice(0).join("\n\n")); + out[i] = suffixInto(out[i]!, pending.splice(0).join("\n\n")); break; } } diff --git a/src/preset/markers.ts b/src/preset/markers.ts index 38307b5..c1660a7 100644 --- a/src/preset/markers.ts +++ b/src/preset/markers.ts @@ -28,7 +28,7 @@ export const WORLD_BOOK_AFTER = "worldBookAfter"; export const CURRENT_TURN = "currentTurn"; export const POST_TURN = "postTurn"; -/** 导入时插入的自有洞(不含共用的 chatHistory)。 */ +/** 运行时填洞的四个自有洞(不含共用的 chatHistory);缺失时自动补进顺序。 */ export const APP_INSERTED_MARKERS = [ WORLD_BOOK_BEFORE, WORLD_BOOK_AFTER, @@ -36,8 +36,6 @@ export const APP_INSERTED_MARKERS = [ POST_TURN, ] as const; -const APP_INSERTED_SET = new Set(APP_INSERTED_MARKERS); - type MarkerDef = { id: string; name: string; @@ -78,8 +76,10 @@ function reindex( } /** - * 以 chatHistory 为锚点插入四个自有 marker。 - * 可重复调用:先摘掉已插入的自有洞,再按规则放回。 + * 以 chatHistory 为锚点补齐四个自有 marker:**只补缺的,不动已有的**。 + * 已经排进 `prompt_order` 的洞保留作者给的顺序——工作台左轨就是真实发送顺序; + * 缺的洞按酒馆的规范位置插进来:历史前 → 对话历史 → 历史后 → 本轮对话 → … → 本轮之后。 + * 可重复调用:补齐之后再调一次是无操作(之前会把这四个洞重新搅回锚点,覆盖作者排的顺序)。 */ export function applyExtendedMarkers(preset: PresetPackage): PresetPackage { let prompts = [...preset.prompts]; @@ -87,59 +87,46 @@ export function applyExtendedMarkers(preset: PresetPackage): PresetPackage { prompts = upsertMarkerEntry(prompts, def); } - const rawOrder = [...preset.promptOrder] - .sort((a, b) => a.orderIndex - b.orderIndex) - .filter((item) => !APP_INSERTED_SET.has(item.promptId)); - - const histIndex = rawOrder.findIndex( - (item) => item.promptId === CHAT_HISTORY_MARKER, + const enabledOf = new Map( + preset.promptOrder.map((item) => [item.promptId, item.enabled]), ); + const next = [...preset.promptOrder].sort((a, b) => a.orderIndex - b.orderIndex); + const has = (id: string) => next.some((item) => item.promptId === id); + const indexOf = (id: string) => next.findIndex((item) => item.promptId === id); + const insertAt = (index: number, id: string) => { + const item: PresetPromptOrderItem = { + promptId: id, + enabled: enabledOf.get(id) ?? true, + orderIndex: 0, + }; + next.splice(Math.max(0, Math.min(index, next.length)), 0, item); + }; + // 预设自带酒馆的 worldInfoBefore / worldInfoAfter(并启用)时,不插自有的世界书洞: // 内容直接填进预设那两条,位置上才对得上酒馆。 - const hasStBefore = rawOrder.some( + const hasStBefore = next.some( (item) => item.promptId === WORLD_INFO_BEFORE_MARKER && item.enabled, ); - const hasStAfter = rawOrder.some( + const hasStAfter = next.some( (item) => item.promptId === WORLD_INFO_AFTER_MARKER && item.enabled, ); - const inserted = (id: string): PresetPromptOrderItem => ({ - promptId: id, - enabled: true, - orderIndex: 0, - }); - const beforeMarkers = hasStBefore ? [] : [inserted(WORLD_BOOK_BEFORE)]; - const afterMarkers = hasStAfter ? [] : [inserted(WORLD_BOOK_AFTER)]; - - let next: PresetPromptOrderItem[]; - if (histIndex < 0) { - next = reindex([ - ...rawOrder, - ...beforeMarkers, - inserted(CHAT_HISTORY_MARKER), - ...afterMarkers, - inserted(CURRENT_TURN), - inserted(POST_TURN), - ]); - } else { - const before = rawOrder.slice(0, histIndex); - const history = rawOrder[histIndex]!; - const after = rawOrder.slice(histIndex + 1); - next = reindex([ - ...before, - ...beforeMarkers, - history, - ...afterMarkers, - inserted(CURRENT_TURN), - ...after, - inserted(POST_TURN), - ]); + if (!has(CHAT_HISTORY_MARKER)) insertAt(next.length, CHAT_HISTORY_MARKER); + if (!has(WORLD_BOOK_BEFORE) && !hasStBefore) insertAt(indexOf(CHAT_HISTORY_MARKER), WORLD_BOOK_BEFORE); + if (!has(WORLD_BOOK_AFTER) && !hasStAfter) insertAt(indexOf(CHAT_HISTORY_MARKER) + 1, WORLD_BOOK_AFTER); + if (!has(CURRENT_TURN)) { + const afterWorldBook = indexOf(WORLD_BOOK_AFTER); + insertAt( + afterWorldBook >= 0 ? afterWorldBook + 1 : indexOf(CHAT_HISTORY_MARKER) + 1, + CURRENT_TURN, + ); } + if (!has(POST_TURN)) insertAt(next.length, POST_TURN); return { ...preset, prompts, - promptOrder: next, + promptOrder: reindex(next), }; }