直聊的种子历史改成逐条真多轮,不再靠块头文本猜轮次。
一条一个角色下拉加一整句正文,换行原样保留;旧盘里的纯文本在读盘时迁移,切得出轮次的转成条目,切不出的挪进史前设定免得悄悄丢掉。历史编辑器做成共用控件,预设工作台的用例与假历史也用同一套。
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import { importSillyTavernPreset } from "../src/preset/importer.js";
|
||||
import {
|
||||
EMPTY_SHELL_SLOTS,
|
||||
assembleDirectChatMessages,
|
||||
createShell,
|
||||
getDirectChatsDir,
|
||||
getShell,
|
||||
worldInfoPackFromDirectChat,
|
||||
} from "../src/direct-chat/index.js";
|
||||
|
||||
function samplePreset() {
|
||||
return importSillyTavernPreset({
|
||||
prompts: [
|
||||
{ identifier: "main", name: "Main", role: "system", content: "MAIN" },
|
||||
{ identifier: "chatHistory", name: "History", role: "system", marker: true },
|
||||
],
|
||||
prompt_order: [
|
||||
{
|
||||
character_id: 1,
|
||||
order: [
|
||||
{ identifier: "main", enabled: true },
|
||||
{ identifier: "chatHistory", enabled: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
}).preset;
|
||||
}
|
||||
|
||||
const slots = (extra: Record<string, unknown> = {}) => ({
|
||||
...EMPTY_SHELL_SLOTS,
|
||||
...extra,
|
||||
});
|
||||
|
||||
describe("直聊种子历史:结构化真多轮", () => {
|
||||
beforeEach(() => {
|
||||
process.env.WRITING_AGENT_DATA_DIR = mkdtempSync(
|
||||
path.join(tmpdir(), "wa-seed-history-"),
|
||||
);
|
||||
});
|
||||
|
||||
it("seedMessages 每条一个 message,排在已有轮次之前", () => {
|
||||
const messages = assembleDirectChatMessages(samplePreset(), {
|
||||
message: "继续",
|
||||
slots: slots({
|
||||
seedMessages: [
|
||||
{ role: "user", content: "前情-问" },
|
||||
{ role: "assistant", content: "前情-答" },
|
||||
],
|
||||
}),
|
||||
priorMessages: [{ role: "user", content: "真-问" }],
|
||||
});
|
||||
const contents = messages.map((m) => String(m.content));
|
||||
const at = (text: string) => contents.findIndex((c) => c.includes(text));
|
||||
expect(at("前情-问")).toBeGreaterThanOrEqual(0);
|
||||
expect(at("前情-问")).toBeLessThan(at("前情-答"));
|
||||
expect(at("前情-答")).toBeLessThan(at("真-问"));
|
||||
expect(at("真-问")).toBeLessThan(at("继续"));
|
||||
// 真轮次,不是塞进 system 的 few-shot
|
||||
expect(messages.some((m) => m.name)).toBe(false);
|
||||
});
|
||||
|
||||
it("seedMessages 优先于旧版 seedHistory 文本", () => {
|
||||
const pack = worldInfoPackFromDirectChat({
|
||||
message: "继续",
|
||||
slots: slots({
|
||||
seedHistory: "## 用户\n\n旧文本",
|
||||
seedMessages: [{ role: "assistant", content: "新结构" }],
|
||||
}),
|
||||
priorMessages: [],
|
||||
});
|
||||
expect(pack.history).toEqual([{ role: "assistant", content: "新结构" }]);
|
||||
});
|
||||
|
||||
it("脏条目丢掉,全空时回落旧文本", () => {
|
||||
const pack = worldInfoPackFromDirectChat({
|
||||
message: "继续",
|
||||
slots: slots({
|
||||
seedHistory: "## 用户\n\n旧文本",
|
||||
seedMessages: [
|
||||
{ role: "system", content: "不是历史角色" },
|
||||
{ role: "user", content: " " },
|
||||
],
|
||||
}),
|
||||
priorMessages: [],
|
||||
});
|
||||
expect(pack.history).toEqual([{ role: "user", content: "旧文本" }]);
|
||||
});
|
||||
|
||||
it("读旧盘时把种子历史文本迁成结构化条目并落盘", () => {
|
||||
const shell = createShell({ name: "旧壳", slots: slots() });
|
||||
const file = path.join(getDirectChatsDir(), "shells", `${shell.id}.json`);
|
||||
writeFileSync(
|
||||
file,
|
||||
JSON.stringify({
|
||||
...shell,
|
||||
seedHistory: "【用户】\n我推开门\n\n【助手】\n屋里没有人。\n\n【系统】\n旁白一句",
|
||||
}),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const loaded = getShell(shell.id);
|
||||
expect(loaded?.seedHistory).toBe("");
|
||||
expect(loaded?.seedMessages).toEqual([
|
||||
{ role: "user", content: "我推开门" },
|
||||
{ role: "assistant", content: "屋里没有人。" },
|
||||
{ role: "assistant", content: "旁白一句" },
|
||||
]);
|
||||
// 迁移要写回盘,下次读到的就是新形状
|
||||
const onDisk = JSON.parse(readFileSync(file, "utf8")) as { seedMessages?: unknown };
|
||||
expect(Array.isArray(onDisk.seedMessages)).toBe(true);
|
||||
});
|
||||
|
||||
it("切不出轮次的旧文本挪进前情,避免变成看不见的发送内容", () => {
|
||||
const shell = createShell({ name: "读料壳", slots: slots({ loreBefore: "设定" }) });
|
||||
const file = path.join(getDirectChatsDir(), "shells", `${shell.id}.json`);
|
||||
writeFileSync(file, JSON.stringify({ ...shell, seedHistory: "一段没有块头的读料" }), "utf8");
|
||||
|
||||
const loaded = getShell(shell.id);
|
||||
expect(loaded?.seedHistory).toBe("");
|
||||
expect(loaded?.seedMessages).toEqual([]);
|
||||
expect(loaded?.loreBefore).toBe("设定\n\n一段没有块头的读料");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user