- 装配只认 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:真假上游抓包对比工具、三份预设的机制结论与实测报告
131 lines
3.6 KiB
TypeScript
131 lines
3.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
import { importSillyTavernPreset } from "../src/preset/importer.js";
|
||
import {
|
||
assemblePresetProbe,
|
||
DEFAULT_PROBE_CONTEXT,
|
||
PROBE_SYSTEM_PROMPT,
|
||
runPresetProbe,
|
||
} from "../src/preset/probe.js";
|
||
|
||
function samplePreset() {
|
||
return importSillyTavernPreset({
|
||
prompts: [
|
||
{ identifier: "main", name: "Main", role: "system", content: "MAIN" },
|
||
{
|
||
identifier: "charDescription",
|
||
name: "Desc",
|
||
role: "system",
|
||
marker: true,
|
||
},
|
||
{
|
||
identifier: "chatHistory",
|
||
name: "History",
|
||
role: "system",
|
||
marker: true,
|
||
},
|
||
{ identifier: "jailbreak", name: "PHI", role: "system", content: "PHI" },
|
||
],
|
||
prompt_order: [
|
||
{
|
||
character_id: 1,
|
||
order: [
|
||
{ identifier: "main", enabled: true },
|
||
{ identifier: "charDescription", enabled: true },
|
||
{ identifier: "chatHistory", enabled: true },
|
||
{ identifier: "jailbreak", enabled: true },
|
||
],
|
||
},
|
||
],
|
||
}).preset;
|
||
}
|
||
|
||
describe("assemblePresetProbe", () => {
|
||
it("fills the sandwich with sample lore, history, and the current turn", () => {
|
||
const messages = assemblePresetProbe(
|
||
samplePreset(),
|
||
{ message: "我推开门。" },
|
||
{ squash: "none" },
|
||
);
|
||
|
||
expect(messages.map((m) => [m.role, m.content])).toEqual([
|
||
// 任务契约并进预设自有的 main 条目,不再单独一条
|
||
["system", `MAIN\n\n${PROBE_SYSTEM_PROMPT}`],
|
||
["system", `### 试跑设定\n\n${DEFAULT_PROBE_CONTEXT.loreBefore}`],
|
||
["system", DEFAULT_PROBE_CONTEXT.history],
|
||
["user", "我推开门。"],
|
||
["system", "PHI"],
|
||
]);
|
||
});
|
||
|
||
it("omits empty optional bags", () => {
|
||
const messages = assemblePresetProbe(
|
||
samplePreset(),
|
||
{
|
||
message: "你好",
|
||
loreBefore: "",
|
||
history: "",
|
||
loreAfter: "",
|
||
postTurn: "",
|
||
},
|
||
{ squash: "none" },
|
||
);
|
||
|
||
expect(messages.map((m) => m.content)).toEqual([
|
||
`MAIN\n\n${PROBE_SYSTEM_PROMPT}`,
|
||
"你好",
|
||
"PHI",
|
||
]);
|
||
});
|
||
});
|
||
|
||
describe("runPresetProbe", () => {
|
||
it("returns assembled messages without calling a model", async () => {
|
||
const result = await runPresetProbe({
|
||
preset: samplePreset(),
|
||
input: { message: "我推开门。" },
|
||
});
|
||
|
||
expect(result.completed).toBe(false);
|
||
expect(result.reply).toBeNull();
|
||
// 缺省 strict:system 段会折进 user,所以只断言正文没丢
|
||
expect(
|
||
result.messages.map((m) => m.content).join("\n"),
|
||
).toContain("我推开门。");
|
||
});
|
||
|
||
it("returns the model reply when complete is provided", async () => {
|
||
const result = await runPresetProbe({
|
||
preset: samplePreset(),
|
||
input: { message: "我推开门。" },
|
||
complete: async () => ({ content: "请进。", reasoning: "短回复" }),
|
||
});
|
||
|
||
expect(result.completed).toBe(true);
|
||
expect(result.reply).toBe("请进。");
|
||
expect(result.reasoning).toBe("短回复");
|
||
});
|
||
|
||
it("keeps messages when generate fails", async () => {
|
||
const result = await runPresetProbe({
|
||
preset: samplePreset(),
|
||
input: { message: "我推开门。" },
|
||
complete: async () => {
|
||
throw new Error("upstream down");
|
||
},
|
||
});
|
||
|
||
expect(result.completed).toBe(false);
|
||
expect(result.error).toBe("upstream down");
|
||
expect(result.messages.length).toBeGreaterThan(0);
|
||
});
|
||
|
||
it("rejects an empty turn", async () => {
|
||
await expect(
|
||
runPresetProbe({
|
||
preset: samplePreset(),
|
||
input: { message: " " },
|
||
}),
|
||
).rejects.toThrow("缺少本轮输入");
|
||
});
|
||
});
|