约束声明与写入时机,避免抢跑下游模块。 Co-authored-by: Cursor <cursoragent@cursor.com>
150 lines
5.8 KiB
TypeScript
150 lines
5.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
buildDictateMessages,
|
|
buildDictateSystemPrompt,
|
|
defaultDictateOrder,
|
|
extractDictateDialogue,
|
|
isAllowedProductTag,
|
|
isDictateModeValue,
|
|
sortDictateProducts,
|
|
} from "../src/dictate/index.js";
|
|
import {
|
|
formatModuleCatalogForDictate,
|
|
loadModuleCatalog,
|
|
} from "../src/skills/creation-flow.js";
|
|
|
|
describe("dictate context", () => {
|
|
it("assembles full dialogue + products with almost no filtering", () => {
|
|
const messages = buildDictateMessages({
|
|
systemPrompt: "SYS",
|
|
products: [{ tag: "用户.需求", content: "雨夜压抑" }],
|
|
dialogue: [
|
|
{ role: "user", text: "我想要压抑雨夜" },
|
|
{ role: "assistant", text: "已写入用户.需求" },
|
|
],
|
|
});
|
|
expect(messages[0]).toEqual({ role: "system", content: "SYS" });
|
|
expect(messages[1]?.role).toBe("user");
|
|
const body = messages[1]?.content ?? "";
|
|
expect(body).toContain("用户.需求");
|
|
expect(body).toContain("雨夜压抑");
|
|
expect(body).toContain("我想要压抑雨夜");
|
|
expect(body).toContain("已写入用户.需求");
|
|
});
|
|
|
|
it("after clear (empty dialogue) still carries products", () => {
|
|
const messages = buildDictateMessages({
|
|
systemPrompt: "SYS",
|
|
products: [{ tag: "设计.美学纲领", content: "冷色短句" }],
|
|
dialogue: [],
|
|
});
|
|
const body = messages[1]?.content ?? "";
|
|
expect(body).toContain("冷色短句");
|
|
expect(body).toContain("尚无对话");
|
|
});
|
|
|
|
it("extracts only user + dictate_reply turns", () => {
|
|
const turns = extractDictateDialogue([
|
|
{ kind: "orchestrator_prompt", text: "欢迎" },
|
|
{ kind: "user_input", text: "你好" },
|
|
{ kind: "agent_tool", text: "insert" },
|
|
{ kind: "dictate_reply", text: "已记下" },
|
|
]);
|
|
expect(turns).toEqual([
|
|
{ role: "user", text: "你好" },
|
|
{ role: "assistant", text: "已记下" },
|
|
]);
|
|
});
|
|
|
|
it("validates product tags and mode value", () => {
|
|
expect(isAllowedProductTag("用户.需求")).toBe(true);
|
|
expect(isAllowedProductTag("设计.美学纲领")).toBe(true);
|
|
expect(isAllowedProductTag("设计.正文组成")).toBe(true);
|
|
expect(isAllowedProductTag("设计.开场白")).toBe(true);
|
|
expect(isAllowedProductTag("变量.当前")).toBe(false);
|
|
expect(isDictateModeValue("dictate")).toBe(true);
|
|
expect(isDictateModeValue("recipe")).toBe(false);
|
|
});
|
|
|
|
it("system prompt requires insert toolcall and relative order", () => {
|
|
const prompt = buildDictateSystemPrompt({
|
|
recipeName: "数据化跑团体验",
|
|
recipeBrief:
|
|
"设定 + 真值 + 分档映射\n怎么做:\n- declare_variable\n- declare_map",
|
|
moduleGuide:
|
|
"【能力 · 何时落盘】\n- 美学纲领与交互范式:…\n 落盘:insert 「设计.美学纲领与交互范式」\n 何时用:还不能回答站位",
|
|
});
|
|
expect(prompt).toContain("设计.正文组成");
|
|
expect(prompt).toContain("设计.开场白");
|
|
expect(prompt).toContain("输出.开场白");
|
|
expect(prompt).toContain("insert");
|
|
expect(prompt).toContain("declare_variable");
|
|
expect(prompt).toContain("declare_map");
|
|
expect(prompt).toContain("数据化跑团体验");
|
|
expect(prompt).toMatch(/order/);
|
|
expect(prompt).toContain("仅允许 toolcall");
|
|
expect(prompt).toContain("设计.模仿范例");
|
|
expect(prompt).toContain("设计.模仿要点");
|
|
expect(prompt).toContain("设计.叙事指南与故事推进");
|
|
expect(prompt).toContain("大纲扩写");
|
|
expect(prompt).toContain("禁止扮演停笔");
|
|
expect(prompt).toContain("只挂载这些产物");
|
|
expect(prompt).toContain("【能力 · 何时落盘】");
|
|
expect(prompt).toContain("设计.美学纲领与交互范式");
|
|
expect(prompt).toContain("每轮通常只推进一刀");
|
|
expect(prompt).toContain("勿因配方名叫数据化就抢跑 D");
|
|
});
|
|
|
|
it("formats module catalog for dictate with land tags and when", async () => {
|
|
const catalog = await loadModuleCatalog("dialogue/world-simulator");
|
|
expect(catalog).toBeTruthy();
|
|
const block = formatModuleCatalogForDictate(catalog!);
|
|
expect(block).toContain("【能力 · 何时落盘】");
|
|
expect(block).toContain("美学纲领与交互范式");
|
|
expect(block).toContain("落盘:insert 「设计.美学纲领与交互范式」");
|
|
expect(block).toContain("何时用");
|
|
expect(block).toContain("何时不用");
|
|
expect(block).toContain("declare_variable");
|
|
expect(block).toContain("设计.开场白");
|
|
expect(block).toContain("〔落档程序〕");
|
|
expect(block).not.toContain("role=prototype");
|
|
});
|
|
|
|
it("sorts products by relative order: neg then 0 then pos", () => {
|
|
const sorted = sortDictateProducts([
|
|
{ tag: "设计.开场白", content: "开", order: 20 },
|
|
{ tag: "用户.需求", content: "需", order: -40 },
|
|
{ tag: "设计.正文组成", content: "格", order: 0 },
|
|
]);
|
|
expect(sorted.map((p) => p.tag)).toEqual([
|
|
"用户.需求",
|
|
"设计.正文组成",
|
|
"设计.开场白",
|
|
]);
|
|
});
|
|
|
|
it("default order puts stable/important before volatile", () => {
|
|
expect(defaultDictateOrder("用户.需求")).toBeLessThan(
|
|
defaultDictateOrder("设计.正文组成"),
|
|
);
|
|
expect(defaultDictateOrder("设计.正文组成")).toBeLessThan(
|
|
defaultDictateOrder("设计.开场白"),
|
|
);
|
|
});
|
|
|
|
it("context lists products in order", () => {
|
|
const body =
|
|
buildDictateMessages({
|
|
systemPrompt: "SYS",
|
|
products: [
|
|
{ tag: "设计.开场白", content: "尾", order: 10 },
|
|
{ tag: "用户.需求", content: "头", order: -10 },
|
|
],
|
|
dialogue: [],
|
|
})[1]?.content ?? "";
|
|
expect(body.indexOf("用户.需求")).toBeLessThan(body.indexOf("设计.开场白"));
|
|
expect(body).toContain("order=-10");
|
|
expect(body).toContain("order=10");
|
|
});
|
|
});
|