- 装配只认 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:真假上游抓包对比工具、三份预设的机制结论与实测报告
61 lines
1.8 KiB
TypeScript
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("收尾");
|
|
});
|
|
});
|