UNPKG

@lenml/char-card-reader

Version:

SillyTavern character card info reader

109 lines (103 loc) 3.35 kB
import { SpecV3 } from "./spec_types/spec_v3"; import { deepClone, uniq } from "./utils"; // lorebook export class CharacterBook implements SpecV3.Lorebook { static from_json(data: any) { if (typeof data !== "object" || data === null) { throw new Error("data must be an object"); } let entries: any[] = []; if (Array.isArray(data)) { entries = data; } else { entries = // from lorabook.json Array.isArray(data?.entries) ? data.entries : // from character card Array.isArray(data?.data?.character_book?.entries) ? data.data.character_book.entries : []; } const book = new CharacterBook(entries); const character_book = data?.character_book ?? data?.data?.character_book ?? data; book.name = character_book?.name; book.description = character_book?.description; book.recursive_scanning = character_book?.recursive_scanning ?? true; book.scan_depth = character_book?.scan_depth ?? 10; book.extensions = character_book?.extensions ?? {}; return book; } name: string = "unknown"; description: string = ""; // TODO token_budget?: number; recursive_scanning: boolean = true; extensions: SpecV3.Lorebook["extensions"] = {}; entries: SpecV3.Lorebook["entries"] = []; scan_depth?: number | undefined = 10; constructor(entries: SpecV3.Lorebook["entries"] = []) { this.entries = deepClone(entries); this._keys_fix(); } public _keys_fix() { const pattern = /[,|;,;]/g; // 修复 keys ,有部分情况 keys 错误可能导致搜索出错 for (const entry of this.entries) { const { keys } = entry; const fixed_keys: string[] = []; for (const k of keys) { if (pattern.test(k)) { fixed_keys.push( ...k .split(pattern) .map((x) => x.trim()) .filter(Boolean) ); } else { fixed_keys.push(k); } } entry.keys = fixed_keys; } } private _scan( context: string, matched: SpecV3.Lorebook["entries"] = [], current_depth = 1 ): SpecV3.Lorebook["entries"] { if (current_depth >= (this.scan_depth ?? 10)) { return uniq(matched); } const current_context = [ context, ...uniq(matched).map((x) => x.content), ].join("\n"); const pending_entries = this.entries .filter((x) => x.content?.trim()) .filter((x) => x.enabled && !matched.includes(x)); if (pending_entries.length === 0) { return uniq(matched); } for (const entry of pending_entries) { const is_matched = entry.keys.some((k) => current_context.includes(k)); if (is_matched) { matched.push(entry); } } if (this.recursive_scanning) { return this._scan(context, matched, current_depth + 1); } return uniq(matched); } public scan(context: string): SpecV3.Lorebook["entries"] { const matched = this._scan(context); const constant = this.entries.filter( (x) => x.constant && x.enabled && x.content?.trim() ); return uniq([...matched, ...constant]).sort( (a, b) => a.insertion_order - b.insertion_order ); } }