打通 Boss 直聘落档挂载与真值→投影映射,并修正带 tools 时的推理强度。
落档自动把产物挂进 context_order;创作期用 declare_variable/declare_map 钉目录与分档;真值变更持续重投影。顺带强制 tools 请求 reasoning_effort=none,并支持消息 restart。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { Blackboard } from "../src/blackboard/blackboard.js";
|
||||
import {
|
||||
lookupValueMapContent,
|
||||
parseValueMapDoc,
|
||||
reprojectValueMaps,
|
||||
serializeValueMapDoc,
|
||||
upsertValueMapEntry,
|
||||
VALUE_MAP_TAG,
|
||||
} from "../src/skills/value-map.js";
|
||||
import {
|
||||
parseVariableCatalog,
|
||||
seedVariablesFromCatalog,
|
||||
serializeVariableCatalog,
|
||||
upsertVariableField,
|
||||
VARIABLE_CATALOG_TAG,
|
||||
} from "../src/skills/variable-catalog.js";
|
||||
import { stringifyTableDoc, createTableFromValues, parseTableDoc } from "../src/blackboard/table-cells.js";
|
||||
|
||||
describe("variable-catalog", () => {
|
||||
it("upserts fields and seeds empty 变量.当前", () => {
|
||||
let doc = null as ReturnType<typeof parseVariableCatalog>;
|
||||
const a = upsertVariableField(doc, {
|
||||
key: "好感",
|
||||
type: "number",
|
||||
initial: 10,
|
||||
user_visible: true,
|
||||
});
|
||||
expect(a.error).toBeUndefined();
|
||||
const b = upsertVariableField(a.doc, {
|
||||
key: "地点",
|
||||
type: "string",
|
||||
initial: "旧港",
|
||||
user_visible: false,
|
||||
});
|
||||
const raw = serializeVariableCatalog(b.doc);
|
||||
const parsed = parseVariableCatalog(raw);
|
||||
expect(parsed?.fields).toHaveLength(2);
|
||||
expect(parsed?.fields.find((f) => f.key === "地点")?.user_visible).toBe(false);
|
||||
|
||||
const seeded = seedVariablesFromCatalog({
|
||||
catalogRaw: raw,
|
||||
currentRaw: null,
|
||||
initialRaw: null,
|
||||
});
|
||||
expect(seeded.seededKeys).toEqual(expect.arrayContaining(["好感", "地点"]));
|
||||
expect(seeded.current).toContain("好感");
|
||||
expect(seeded.initial).toContain("旧港");
|
||||
});
|
||||
});
|
||||
|
||||
describe("value-map", () => {
|
||||
it("looks up range bands (min inclusive, max exclusive)", () => {
|
||||
const { doc } = upsertValueMapEntry(null, {
|
||||
id: "aff",
|
||||
field: "好感",
|
||||
target_tag: "上下文.角色态度",
|
||||
bands: [
|
||||
{ min: 0, max: 30, content: "冷淡" },
|
||||
{ min: 30, max: 60, content: "熟悉" },
|
||||
{ min: 60, content: "恋爱" },
|
||||
],
|
||||
});
|
||||
const entry = doc.maps[0]!;
|
||||
expect(lookupValueMapContent(entry, 0)).toBe("冷淡");
|
||||
expect(lookupValueMapContent(entry, 29)).toBe("冷淡");
|
||||
expect(lookupValueMapContent(entry, 30)).toBe("熟悉");
|
||||
expect(lookupValueMapContent(entry, 60)).toBe("恋爱");
|
||||
});
|
||||
|
||||
it("reprojects target tag when 变量.当前 changes", () => {
|
||||
const board = new Blackboard();
|
||||
const { doc } = upsertValueMapEntry(null, {
|
||||
id: "aff",
|
||||
field: "好感",
|
||||
target_tag: "上下文.角色态度",
|
||||
bands: [
|
||||
{ min: 0, max: 50, content: "档A" },
|
||||
{ min: 50, content: "档B" },
|
||||
],
|
||||
});
|
||||
board.write({
|
||||
tag: VALUE_MAP_TAG,
|
||||
content: serializeValueMapDoc(doc),
|
||||
source: "test",
|
||||
});
|
||||
const tableRaw = stringifyTableDoc(
|
||||
createTableFromValues({ ["好感"]: 20 }, "test"),
|
||||
);
|
||||
board.write({
|
||||
tag: "变量.当前",
|
||||
content: tableRaw,
|
||||
source: "test",
|
||||
});
|
||||
expect(parseTableDoc(board.getContentByTag("变量.当前"))?.rows.map((r) => r.key)).toEqual([
|
||||
"好感",
|
||||
]);
|
||||
const r1 = reprojectValueMaps({ blackboard: board, currentRaw: tableRaw });
|
||||
expect(r1.skipped, JSON.stringify(r1)).toEqual([]);
|
||||
expect(r1.written[0]?.tag).toBe("上下文.角色态度");
|
||||
expect(board.getContentByTag("上下文.角色态度")).toBe("档A");
|
||||
|
||||
const tableRaw2 = stringifyTableDoc(
|
||||
createTableFromValues({ ["好感"]: 80 }, "test"),
|
||||
);
|
||||
board.write({
|
||||
tag: "变量.当前",
|
||||
content: tableRaw2,
|
||||
source: "test",
|
||||
});
|
||||
reprojectValueMaps({ blackboard: board, currentRaw: tableRaw2 });
|
||||
expect(board.getContentByTag("上下文.角色态度")).toBe("档B");
|
||||
});
|
||||
|
||||
it("rejects bad target_tag", () => {
|
||||
const { error } = upsertValueMapEntry(null, {
|
||||
id: "x",
|
||||
field: "好感",
|
||||
target_tag: "设计.美学纲领",
|
||||
bands: [{ content: "no" }],
|
||||
});
|
||||
expect(error).toMatch(/上下文/);
|
||||
});
|
||||
|
||||
it("round-trips parse", () => {
|
||||
const raw = serializeValueMapDoc({
|
||||
schema: "value-map.v1",
|
||||
maps: [
|
||||
{
|
||||
id: "ch",
|
||||
field: "章节",
|
||||
target_tag: "大纲.当前章",
|
||||
bands: [
|
||||
{ when: "eq", value: 1, content: "第一章" },
|
||||
{ when: "default", content: "未定" },
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
const parsed = parseValueMapDoc(raw);
|
||||
expect(parsed?.maps[0]?.bands).toHaveLength(2);
|
||||
expect(lookupValueMapContent(parsed!.maps[0]!, 1)).toBe("第一章");
|
||||
expect(lookupValueMapContent(parsed!.maps[0]!, 9)).toBe("未定");
|
||||
});
|
||||
});
|
||||
|
||||
describe("catalog tag constants", () => {
|
||||
it("uses stable design tags", () => {
|
||||
expect(VARIABLE_CATALOG_TAG).toBe("设计.变量目录");
|
||||
expect(VALUE_MAP_TAG).toBe("设计.变量映射");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user