Files
writing-agent/tests/dialogue-history.test.ts
moranzhi 94f67fa744 引入上下文片段、固定槽与投影排序,并完善机遇裁定与游玩期 UI。
把创作产物收敛为可挂载片段与 play_slots/context_order,同步修订世界模拟器模块与运行时拼装。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 01:36:32 +08:00

141 lines
4.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
appendDialogueHistoryTurn,
projectDialogueHistory,
buildHistoryFallback,
isDialogueHistoryRef,
} from "../src/skills/dialogue-history.js";
import { assembleWorkerContext } from "../src/skills/context-segments.js";
import { Blackboard } from "../src/blackboard/blackboard.js";
import {
contextOrderToSegments,
parseContextOrder,
applyContextOrderEdit,
synthesizeContextOrderFromWorkers,
} from "../src/skills/context-order.js";
describe("dialogue history projection", () => {
it("recognizes history refs and projects by level", () => {
expect(isDialogueHistoryRef("对话.历史")).toBe(true);
expect(isDialogueHistoryRef("history")).toBe(true);
const long = Array.from({ length: 30 }, (_, i) => `## 用户\n\n第${i}`).join(
"\n\n",
);
const summary = projectDialogueHistory(long, "summary");
expect(summary).toContain("仅最近");
expect(projectDialogueHistory(long, "fixed")).toContain("不注入");
});
it("assembles history tag in flat segment order", () => {
const bb = new Blackboard();
bb.write({
tag: "对话.历史",
content: appendDialogueHistoryTurn("", { role: "用户", text: "推门" }),
source: "test",
});
bb.write({ tag: "用户.最新输入", content: "再看一眼", source: "test" });
const text = assembleWorkerContext({
inputs: {},
segments: [
{
id: "p",
tier: "static",
tags: [],
label: "## persona",
inline: "GM",
projection: "fixed",
},
{
id: "h",
tier: "static",
tags: ["对话.历史"],
label: "## 对话.历史",
projection: "full",
},
{
id: "u",
tier: "dynamic",
tags: ["用户.最新输入"],
label: "## 用户.最新输入",
projection: "full",
},
],
blackboard: bb,
workerId: "world-simulator",
workerName: "主世界层",
outputTags: [],
});
const iPersona = text.indexOf("GM");
const iHist = text.indexOf("推门");
const iUser = text.indexOf("再看一眼");
expect(iPersona).toBeGreaterThanOrEqual(0);
expect(iHist).toBeGreaterThan(iPersona);
expect(iUser).toBeGreaterThan(iHist);
});
it("falls back to event stream when history empty", () => {
expect(
buildHistoryFallback({
eventStream: "门开了",
latestUser: "走进去",
}),
).toContain("门开了");
});
});
describe("flat context order", () => {
it("synthesizes history tag and moves across it", () => {
const doc = synthesizeContextOrderFromWorkers(
[
{
ref: "narrator",
name: "叙事转述",
context: {
static: ["设计.叙事指南"],
dynamic: ["运行.本轮.裁决", "用户.最新输入"],
},
},
],
{ gm: true, narrator: true, perspective: false },
)!;
expect(doc.slots[0]!.inserts.some((i) => i.ref === "对话.历史")).toBe(true);
const histIdx = doc.slots[0]!.inserts.findIndex((i) => i.ref === "对话.历史");
const moved = applyContextOrderEdit(doc, {
action: "set_anchor",
slotRef: "narrator",
index: doc.slots[0]!.inserts.length - 1,
anchor: "pre_history",
});
const newHist = moved.slots[0]!.inserts.findIndex((i) => i.ref === "对话.历史");
const lastUser = moved.slots[0]!.inserts.findIndex(
(i) => i.ref === "用户.最新输入",
);
expect(lastUser).toBeLessThan(newHist);
expect(histIdx).toBeGreaterThanOrEqual(0);
});
it("maps to segments in list order including history", () => {
const doc = parseContextOrder({
schema: "context-order.v1",
slots: [
{
ref: "world-simulator",
inserts: [
{ order: 0, ref: "worker.persona", projection: "fixed" },
{ order: 1, ref: "对话.历史", projection: "summary" },
{ order: 2, ref: "用户.最新输入", projection: "full" },
],
},
],
})!;
const segs = contextOrderToSegments({
slot: doc.slots[0]!,
personaText: "裁决",
});
expect(segs[0]!.inline).toContain("裁决");
expect(segs[1]!.tags).toEqual(["对话.历史"]);
expect(segs[2]!.tags).toEqual(["用户.最新输入"]);
});
});