Files
writing-agent/tests/truncation.test.ts
chenmoranzhi 9dc927282f 预设兼容:按真实抓包对齐装配与包装层,补上声明式工具与预设正则。
- 装配只认 prompt_order.enabled;squash_system_messages 只合并相邻 system(新增 system 档,连接符按酒馆用单换行)
- 包装层:new_chat_prompt、示例对话(new_example_chat_prompt + <START> 切段 + name)、角色卡三块与 personality_format/scenario_format、{{user}}/{{char}} 取值、任务契约并进预设条目
- 工具协议:按 ToolBindings / 伪工具协议数据声明真 tools(presetToolPolicy=preset),工具描述用到的条目不再重复作为消息发出
- 正则:promptOnly 作用于对话历史(含 depth 过滤)、写入时脚本改写回复;{{//}} 注释按配平扫描
- tools/st-request-diff:真假上游抓包对比工具、三份预设的机制结论与实测报告
2026-10-23 00:13:08 +08:00

61 lines
1.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
isCompleteJson,
salvageTruncatedPayload,
} from "../src/llm/truncation.js";
describe("isCompleteJson", () => {
it("accepts complete objects and empty", () => {
expect(isCompleteJson('{"content":"x"}')).toBe(true);
expect(isCompleteJson("")).toBe(true);
});
it("rejects truncated objects", () => {
expect(isCompleteJson('{"content":"第一章')).toBe(false);
});
});
describe("salvageTruncatedPayload", () => {
it("returns null when the field never started", () => {
expect(salvageTruncatedPayload('{"content', ["content"])).toBeNull();
expect(salvageTruncatedPayload('{"other":"x"}', ["content"])).toBeNull();
});
it("keeps the generated text before the cut", () => {
const raw = '{"content":"第一章\\n她推开门';
expect(salvageTruncatedPayload(raw, ["content"])).toEqual({
field: "content",
text: "第一章\n她推开门",
});
});
it("stops at the closing quote when the value is complete", () => {
const raw = '{"content":"正文","outputTags":["a"';
expect(salvageTruncatedPayload(raw, ["content"])?.text).toBe("正文");
});
it("tries fields in order", () => {
const raw = '{"display_stream":"走了","content":"没到"';
expect(salvageTruncatedPayload(raw, ["content", "display_stream"])?.field).toBe(
"content",
);
expect(
salvageTruncatedPayload('{"display_stream":"走了', [
"content",
"display_stream",
])?.field,
).toBe("display_stream");
});
it("decodes unicode escapes and skips dangling escapes", () => {
expect(
salvageTruncatedPayload('{"content":"\\u7b2c\\u4e00\\u7ae0', [
"content",
])?.text,
).toBe("第一章");
expect(
salvageTruncatedPayload('{"content":"收尾\\', ["content"])?.text,
).toBe("收尾");
});
});