预设兼容:按真实抓包对齐装配与包装层,补上声明式工具与预设正则。
- 装配只认 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:真假上游抓包对比工具、三份预设的机制结论与实测报告
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { importSillyTavernPreset } from "../src/preset/importer.js";
|
||||
import {
|
||||
declarePresetTools,
|
||||
extractDeclaredPayload,
|
||||
} from "../src/preset/tool-declaration.js";
|
||||
|
||||
function importRaw(raw: unknown, name: string) {
|
||||
return importSillyTavernPreset(raw, { name }).preset;
|
||||
}
|
||||
|
||||
describe("声明式工具(预设数据 → 真 tools)", () => {
|
||||
it("从 ToolBindings 的 form 生成 tools", () => {
|
||||
const preset = importRaw(
|
||||
{
|
||||
prompts: [{ identifier: "main", name: "main", role: "system", content: "MAIN" }],
|
||||
prompt_order: [
|
||||
{ character_id: 100001, order: [{ identifier: "main", enabled: true }] },
|
||||
],
|
||||
extensions: {
|
||||
SPreset: {
|
||||
ToolBindings: {
|
||||
aaa: {
|
||||
enabled: true,
|
||||
resolvedName: "game_content",
|
||||
form: {
|
||||
name: "game_content",
|
||||
description: "# 你的全部输出应当在此工具内。",
|
||||
parameters: [{ name: "content", type: "string", required: true }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"bindings",
|
||||
);
|
||||
const declared = declarePresetTools(preset);
|
||||
expect(declared?.source).toBe("bindings");
|
||||
expect(declared?.tools).toHaveLength(1);
|
||||
const fn = declared!.tools[0]!.function;
|
||||
expect(fn.name).toBe("game_content");
|
||||
expect(fn.description).toContain("全部输出");
|
||||
expect(fn.parameters).toEqual({
|
||||
type: "object",
|
||||
properties: { content: { type: "string" } },
|
||||
required: ["content"],
|
||||
});
|
||||
expect(declared?.payloadFields).toEqual(["content"]);
|
||||
});
|
||||
|
||||
it("只有伪工具协议条目时从正文取工具名与字段", () => {
|
||||
const preset = importRaw(
|
||||
{
|
||||
prompts: [
|
||||
{
|
||||
identifier: "guide-sys",
|
||||
name: "【ToolCall引导】系统声明",
|
||||
role: "system",
|
||||
content:
|
||||
"调用 %TOOL_NAME% 才能输出正文;执行宿 dispatch_stream_payload_34d98628087f3798。",
|
||||
},
|
||||
{
|
||||
identifier: "guide-field",
|
||||
name: "【ToolCall引导】字段说明",
|
||||
role: "system",
|
||||
content: "stream_target = \"display_stream\"\n正文写在这里。",
|
||||
},
|
||||
],
|
||||
prompt_order: [
|
||||
{
|
||||
character_id: 100001,
|
||||
order: [
|
||||
{ identifier: "guide-sys", enabled: true },
|
||||
{ identifier: "guide-field", enabled: true },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
"protocol",
|
||||
);
|
||||
const declared = declarePresetTools(preset);
|
||||
expect(declared?.source).toBe("protocol");
|
||||
const fn = declared!.tools[0]!.function;
|
||||
expect(fn.name).toBe("dispatch_stream_payload_34d98628087f3798");
|
||||
expect(declared?.payloadFields).toContain("display_stream");
|
||||
const params = fn.parameters as { properties: Record<string, unknown>; required: string[] };
|
||||
expect(Object.keys(params.properties)).toContain("display_stream");
|
||||
expect(params.required).toContain("display_stream");
|
||||
});
|
||||
|
||||
it("没有工具协议就返回 null", () => {
|
||||
const preset = importRaw(
|
||||
{
|
||||
prompts: [{ identifier: "main", name: "main", role: "system", content: "MAIN" }],
|
||||
prompt_order: [
|
||||
{ character_id: 100001, order: [{ identifier: "main", enabled: true }] },
|
||||
],
|
||||
},
|
||||
"plain",
|
||||
);
|
||||
expect(declarePresetTools(preset)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractDeclaredPayload", () => {
|
||||
it("按字段优先级取正文", () => {
|
||||
expect(
|
||||
extractDeclaredPayload(JSON.stringify({ display_stream: "正文" }), ["display_stream"]),
|
||||
).toBe("正文");
|
||||
});
|
||||
|
||||
it("字段不在时退回任意字符串字段", () => {
|
||||
expect(extractDeclaredPayload(JSON.stringify({ other: "兜底" }), ["content"])).toBe("兜底");
|
||||
});
|
||||
|
||||
it("坏 JSON 返回 null", () => {
|
||||
expect(extractDeclaredPayload("{oops", ["content"])).toBeNull();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user