mirror of
https://github.com/Wx-2025/ST-Amily2-Chat-Optimisation.git
synced 2026-06-06 11:35:50 +00:00
312 lines
13 KiB
JavaScript
312 lines
13 KiB
JavaScript
import { amilyHelper } from "../tavern-helper/main.js";
|
||
import * as charApi from "./char-api.js";
|
||
|
||
export const tools = {
|
||
|
||
read_world_info: async ({ book_name, return_full = false }) => {
|
||
const entries = await amilyHelper.getLorebookEntries(book_name);
|
||
|
||
if (return_full) {
|
||
return JSON.stringify(entries, null, 2);
|
||
}
|
||
|
||
const summary = entries.map(e => {
|
||
let keys = e.key;
|
||
if (Array.isArray(keys)) keys = keys.join(', ');
|
||
|
||
return {
|
||
uid: e.uid,
|
||
keys: keys,
|
||
comment: e.comment || keys || "Unnamed Entry",
|
||
};
|
||
});
|
||
|
||
return JSON.stringify({
|
||
info: "Index of world book entries. Use 'read_world_entry' with 'uid' to read specific content.",
|
||
total_entries: entries.length,
|
||
entries: summary
|
||
}, null, 2);
|
||
},
|
||
|
||
read_world_entry: async ({ book_name, uid }) => {
|
||
const entries = await amilyHelper.getLorebookEntries(book_name);
|
||
const entry = entries.find(e => String(e.uid) === String(uid));
|
||
|
||
if (!entry) {
|
||
return `Entry with UID ${uid} not found in world book "${book_name}".`;
|
||
}
|
||
|
||
return JSON.stringify(entry, null, 2);
|
||
},
|
||
|
||
write_world_info_entry: async ({ book_name, entries }) => {
|
||
if (typeof entries === 'string') {
|
||
try {
|
||
const cleanEntries = entries.replace(/```json/g, '').replace(/```/g, '').trim();
|
||
entries = JSON.parse(cleanEntries);
|
||
} catch (e) {
|
||
return `错误: 'entries' 参数必须是有效的 JSON 数组。解析错误: ${e.message}`;
|
||
}
|
||
}
|
||
if (!Array.isArray(entries)) {
|
||
if (typeof entries === 'object' && entries !== null) {
|
||
entries = [entries];
|
||
} else {
|
||
return "错误: 'entries' 参数必须是数组或对象。";
|
||
}
|
||
}
|
||
|
||
const updates = [];
|
||
const creates = [];
|
||
|
||
for (const entry of entries) {
|
||
if (entry.uid !== undefined) {
|
||
updates.push(entry);
|
||
} else {
|
||
creates.push(entry);
|
||
}
|
||
}
|
||
|
||
let resultMsg = "";
|
||
if (updates.length > 0) {
|
||
const success = await amilyHelper.setLorebookEntries(book_name, updates);
|
||
resultMsg += success ? `成功更新了 ${updates.length} 个条目。 ` : `更新条目失败。 `;
|
||
}
|
||
if (creates.length > 0) {
|
||
const success = await amilyHelper.createLorebookEntries(book_name, creates);
|
||
resultMsg += success ? `成功创建了 ${creates.length} 个条目。 ` : `创建条目失败。 `;
|
||
}
|
||
return resultMsg || "未执行任何操作。";
|
||
},
|
||
|
||
create_world_book: async ({ book_name }) => {
|
||
const success = await amilyHelper.createLorebook(book_name);
|
||
return success ? `世界书 "${book_name}" 创建成功。` : `创建世界书 "${book_name}" 失败。`;
|
||
},
|
||
|
||
read_character_card: async ({ chid }) => {
|
||
const char = charApi.getCharacter(chid);
|
||
if (!char) return "未找到角色。";
|
||
|
||
const safeChar = {
|
||
name: char.name,
|
||
description: char.description,
|
||
personality: char.personality,
|
||
scenario: char.scenario,
|
||
first_mes: char.first_mes,
|
||
mes_example: char.mes_example,
|
||
alternate_greetings: char.data?.alternate_greetings || []
|
||
};
|
||
return JSON.stringify(safeChar, null, 2);
|
||
},
|
||
|
||
update_character_card: async (args) => {
|
||
const { chid, ...updates } = args;
|
||
const finalUpdates = args.updates || updates;
|
||
|
||
const success = charApi.updateCharacter(chid, finalUpdates);
|
||
if (success) {
|
||
const updatedFields = Object.keys(finalUpdates).join(', ');
|
||
return `角色卡更新成功 [ID: ${chid}]。已更新字段: ${updatedFields}。`;
|
||
} else {
|
||
return "更新角色卡失败。";
|
||
}
|
||
},
|
||
|
||
edit_character_text: async ({ chid, field, diff }) => {
|
||
const char = charApi.getCharacter(chid);
|
||
if (!char) return "未找到角色。";
|
||
|
||
const allowedFields = ['description', 'personality', 'scenario', 'first_mes', 'mes_example'];
|
||
if (!allowedFields.includes(field)) {
|
||
return `无效的字段。允许的字段: ${allowedFields.join(', ')}`;
|
||
}
|
||
|
||
let content = char[field] || '';
|
||
const changes = diff.split('------- SEARCH');
|
||
|
||
// Remove the first empty split if any
|
||
if (changes[0].trim() === '') changes.shift();
|
||
|
||
for (const change of changes) {
|
||
const parts = change.split('=======');
|
||
if (parts.length !== 2) continue;
|
||
|
||
const searchBlock = parts[0].trim();
|
||
const replaceBlock = parts[1].split('+++++++ REPLACE')[0].trim();
|
||
|
||
if (!content.includes(searchBlock)) {
|
||
return `错误: 在字段 '${field}' 中未找到以下搜索块:\n${searchBlock}`;
|
||
}
|
||
|
||
content = content.replace(searchBlock, replaceBlock);
|
||
}
|
||
|
||
const success = charApi.updateCharacter(chid, { [field]: content });
|
||
return success ? `字段 '${field}' 更新成功。` : `更新字段 '${field}' 失败。`;
|
||
},
|
||
|
||
manage_first_message: async ({ action, chid, index, message }) => {
|
||
let success = false;
|
||
switch (action) {
|
||
case 'add':
|
||
success = charApi.addFirstMessage(chid, message);
|
||
break;
|
||
case 'update':
|
||
success = charApi.updateFirstMessage(chid, index, message);
|
||
break;
|
||
case 'remove':
|
||
success = charApi.removeFirstMessage(chid, index);
|
||
break;
|
||
default:
|
||
return "无效的操作。";
|
||
}
|
||
return success ? `开场白 ${action} 成功。` : `开场白 ${action} 失败。`;
|
||
},
|
||
|
||
create_character: async ({ name }) => {
|
||
const result = await charApi.createNewCharacter(name);
|
||
if (result === -1) return "创建角色失败。";
|
||
if (result === -2) return "角色创建请求已发送。请手动刷新角色列表以查看新角色。";
|
||
return `角色创建成功,ID: ${result}`;
|
||
}
|
||
};
|
||
|
||
export function getToolDefinitions() {
|
||
return [
|
||
{
|
||
name: "read_world_info",
|
||
description: "Read the index (list of entries with keys and comments) of a world book. Does NOT return full content.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
book_name: { type: "string", description: "The name of the world book." }
|
||
},
|
||
required: ["book_name"]
|
||
}
|
||
},
|
||
{
|
||
name: "read_world_entry",
|
||
description: "Read the full content of a specific world book entry.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
book_name: { type: "string", description: "The name of the world book." },
|
||
uid: { type: "number", description: "The UID of the entry to read." }
|
||
},
|
||
required: ["book_name", "uid"]
|
||
}
|
||
},
|
||
{
|
||
name: "write_world_info_entry",
|
||
description: "Create or update entries in a world book.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
book_name: { type: "string", description: "The name of the world book." },
|
||
entries: {
|
||
type: "array",
|
||
items: {
|
||
type: "object",
|
||
properties: {
|
||
uid: { type: "number", description: "Entry ID (optional, for update)." },
|
||
comment: { type: "string", description: "Entry title/comment." },
|
||
content: { type: "string", description: "Entry content." },
|
||
key: { type: "array", items: { type: "string" }, description: "Keywords." },
|
||
enabled: { type: "boolean", description: "Is enabled." },
|
||
constant: { type: "boolean", description: "Constant (Blue light)." },
|
||
position: { type: "string", enum: ["before_character_definition", "after_character_definition", "before_author_note", "after_author_note", "at_depth_as_system"], description: "Insertion position." },
|
||
depth: { type: "number", description: "Insertion depth." },
|
||
scanDepth: { type: "number", description: "Scan depth." },
|
||
exclude_recursion: { type: "boolean", description: "Exclude from recursion." },
|
||
prevent_recursion: { type: "boolean", description: "Prevent recursion." }
|
||
}
|
||
}
|
||
}
|
||
},
|
||
required: ["book_name", "entries"]
|
||
}
|
||
},
|
||
{
|
||
name: "create_world_book",
|
||
description: "Create a new empty world book.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
book_name: { type: "string", description: "The name of the new world book." }
|
||
},
|
||
required: ["book_name"]
|
||
}
|
||
},
|
||
{
|
||
name: "read_character_card",
|
||
description: "Read character card data.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
chid: { type: "number", description: "Character ID." }
|
||
},
|
||
required: ["chid"]
|
||
}
|
||
},
|
||
{
|
||
name: "update_character_card",
|
||
description: "Update character card fields (overwrite).",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
chid: { type: "number", description: "Character ID." },
|
||
name: { type: "string" },
|
||
description: { type: "string" },
|
||
personality: { type: "string" },
|
||
scenario: { type: "string" },
|
||
first_mes: { type: "string" },
|
||
mes_example: { type: "string" }
|
||
},
|
||
required: ["chid"]
|
||
}
|
||
},
|
||
{
|
||
name: "edit_character_text",
|
||
description: "Edit a specific text field of a character using SEARCH/REPLACE blocks.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
chid: { type: "number", description: "Character ID." },
|
||
field: { type: "string", enum: ["description", "personality", "scenario", "first_mes", "mes_example"], description: "The field to edit." },
|
||
diff: {
|
||
type: "string",
|
||
description: "One or more SEARCH/REPLACE blocks following this exact format:\n------- SEARCH\n[exact content to find]\n=======\n[new content to replace with]\n+++++++ REPLACE"
|
||
}
|
||
},
|
||
required: ["chid", "field", "diff"]
|
||
}
|
||
},
|
||
{
|
||
name: "manage_first_message",
|
||
description: "Add, update, or remove alternate greetings.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
action: { type: "string", enum: ["add", "update", "remove"] },
|
||
chid: { type: "number", description: "Character ID." },
|
||
index: { type: "number", description: "Index of the greeting (required for update/remove)." },
|
||
message: { type: "string", description: "Content of the greeting (required for add/update)." }
|
||
},
|
||
required: ["action", "chid"]
|
||
}
|
||
},
|
||
{
|
||
name: "create_character",
|
||
description: "Create a new character card.",
|
||
parameters: {
|
||
type: "object",
|
||
properties: {
|
||
name: { type: "string", description: "Name of the new character." }
|
||
},
|
||
required: ["name"]
|
||
}
|
||
}
|
||
];
|
||
}
|