refactor: 重构项目结构和代码组织

This commit is contained in:
2026-04-23 23:40:28 +08:00
parent 481555c257
commit ab860c61d9
165 changed files with 51 additions and 17248 deletions

View File

@@ -1,299 +0,0 @@
# 共享类型系统文档
## 概述
本目录包含项目的完整类型系统,采用**双格式兼容**设计:
1. **SillyTavern 原生格式** - 用于导入导出兼容
2. **扩展内部格式** - 用于增强功能和自定义特性
## 目录结构
```
shared/types/
├── sillytavern/ # SillyTavern 原生格式(从官方文档读取)
│ ├── character.types.ts # 角色卡 V2 规范
│ ├── chat.types.ts # 聊天记录JSONL
│ ├── world.types.ts # 世界书 / Lorebook
│ └── preset.types.ts # 生成预设
├── extended/ # 您的自定义扩展
│ ├── chat-ext.types.ts # 扩展聊天(动态表头)
│ ├── preset-ext.types.ts # 扩展预设(位置锚点)
│ └── world-ext.types.ts # 统一触发器系统
├── adapters/ # 导入导出适配器
│ └── import-export.types.ts # 适配器接口
├── common.types.ts # 通用工具类型
└── index.ts # 主导出文件
```
## 核心设计原则
### 1. **双格式系统**
每个数据实体都有**两种表示形式**
- **ST 格式**:精确的 SillyTavern 规范(用于兼容性)
- **扩展格式**:增强了您的自定义功能
示例:
```typescript
// SillyTavern 格式(导入/导出)
interface STChatMessage {
name: string;
is_user: boolean;
mes: string;
// ... ST 字段
}
// 扩展格式(内部使用)
interface ExtendedChatMessage {
senderName: string;
senderRole: 'user' | 'assistant';
mes: string;
reasoning?: string; // 您的自定义字段
image?: string; // 您的自定义字段
audio?: string; // 您的自定义字段
dynamicData?: Record<string, any>; // 动态表头
}
```
### 2. **适配器模式**
适配器处理格式之间的转换:
```typescript
interface ChatAdapter {
importFromST(jsonlContent: string): InternalChatSession;
exportToST(chat: InternalChatSession): string;
}
```
### 3. **保留原始数据**
内部模型保留原始 ST 数据:
```typescript
interface InternalCharacter {
// ... 您的字段
stData?: STCharacterCardV2; // 保留原始数据
}
```
## Data Models
### Chat History (JSONL)
**Metadata (First line):**
- `characterName`: Character name
- `dynamicHeaders`: User-defined dynamic table headers
- `characterDescription`: Character description
- `systemPrompt`: System prompt
- `firstMessages`: Alternate first messages
**Messages (Subsequent lines):**
- `senderName`: Who sent the message
- `senderRole`: user/assistant/system/narrator
- `mes`: Message content
- `swipes`: Alternative responses
- `reasoning`: Chain of thought
- `image/audio`: Media attachments
- `dynamicData`: Custom dynamic header values
### 预设系统
**固定占位符位置(顺序必须保持):**
1. `system` - 系统提示词(来自角色卡)
2. `world_info_before` - 世界书(前置)
3. `persona_description` - 角色设定描述
4. `char_description` - 角色详细描述
5. `world_info_after` - 世界书(后置)
6. `history` - 聊天记录(对话历史)
7. `user_input` - 用户当前输入
**核心参数:**
- `temperature`: 温度(创造性)
- `maxReplyLength`: 最大回复长度
- `topP`, `topK`: 采样参数
- `repetitionPenalty`: 重复惩罚系数
- `entryOrder`: 预设条目名称的有序列表
- `maxContextTokens`: 最大上下文 token 数
- `streaming`: 是否流式传输
**API 配置:**
```typescript
interface ApiConfig {
apiUrl: string; // API 地址
apiKey: string; // API 密钥
apiType: 'primary' | 'secondary' | 'image_gen' | 'rag'; // API 类型
model: string; // 模型名称
}
```
### 世界书(统一触发器系统)
将原有的 `key``constant` 整合为统一的四元素字典:
```typescript
interface TriggerConfig {
permanent: PermanentTrigger; // 永久激活(无需触发器)
keyword: KeywordTrigger; // 关键词触发
rag: RagTrigger; // RAG/数据库触发
variable: VariableTrigger; // 变量比较触发
}
```
**触发器类型详解:**
1. **永久触发器** (`permanent`)
- `enabled`: 是否永久激活
- 无其他条件,始终生效
2. **关键词触发器** (`keyword`)
- `enabled`: 是否启用
- `keywords`: 关键词数组
- `useRegex`: 是否使用正则表达式
- `matchWholeWords`: 是否匹配完整单词
- `caseSensitive`: 是否区分大小写
3. **RAG 触发器** (`rag`)
- `enabled`: 是否启用
- `databaseName`: 关联的数据库/集合名称
- `threshold`: 相似度阈值0-1
- `topK`: 检索的相似条目数量
4. **变量触发器** (`variable`)
- `enabled`: 是否启用
- `variableA`: 变量 A
- `operator`: 运算符(`>`, `<`, `=`, `contains`, `<=`, `>=`, `!=`
- `variableB`: 变量 B可选
- `constantA`: 常量值(可选,替代 variableB
**其他字段:**
- `content`: 条目内容
- `position`: 插入位置(`before_char` / `after_char`
- `scanDepth`: 扫描深度(扫描多少条消息)
- `priority`: 优先级(同位置的先后顺序)
## 使用示例
### 导入角色卡
```typescript
import { CharacterAdapter, STCharacterCardV2 } from '@shared/types';
const adapter: CharacterAdapter = new CharacterAdapterImpl();
// 从 ST 格式导入
const stCard: STCharacterCardV2 = parsePNG('character.png');
const internalChar = adapter.importFromST(stCard);
// 导出为 ST 格式
const stExport = adapter.exportToST(internalChar);
```
### 聊天会话管理
```typescript
import { ChatAdapter } from '@shared/types';
const adapter: ChatAdapter = new ChatAdapterImpl();
// 导入 JSONL
const jsonlContent = await readFile('chat.jsonl');
const chat = adapter.importFromST(jsonlContent);
// 添加带自定义字段的消息
chat.messages.push({
senderName: '用户',
senderRole: 'user',
mes: '你好!',
reasoning: undefined,
dynamicData: { mood: 'happy' }
});
// 导出回 JSONL
const exported = adapter.exportToST(chat);
```
### 世界书统一触发器
```typescript
import { ExtendedWorldInfoEntry, TriggerConfig } from '@shared/types';
const entry: ExtendedWorldInfoEntry = {
id: '1',
content: '魔法剑发出蓝色光芒。',
position: 'after_char',
scanDepth: 5,
priority: 1,
triggers: {
permanent: { enabled: false },
keyword: {
enabled: true,
keywords: ['剑', '武器'],
useRegex: false,
matchWholeWords: true,
caseSensitive: false
},
rag: { enabled: false },
variable: { enabled: false }
},
enabled: true
};
```
## 兼容性说明
### SillyTavern V2 规范
- 完全兼容 [Character Card V2](https://github.com/malfoyslastname/character-card-spec-v2)
- 支持 PNG 嵌入tEXt 数据块)
- 支持 JSON 格式
### 聊天记录
- JSONL 格式(每行一个 JSON 对象)
- 第一行:元数据
- 后续行:消息
- 支持完整性哈希校验
### 世界书
- 兼容 ST Lorebook 格式
- 扩展触发器系统可转换为 `keys` + `constant`
### 预设
- 支持所有标准 ST 参数
- 固定占位符位置用于提示词构建
- 保留条目顺序
## 迁移指南
### 从 SillyTavern 到内部格式
1. 解析 ST 格式PNG/JSON/JSONL
2. 使用适配器转换为内部格式
3. 原始 ST 数据保存在 `stData` 字段中
4. 根据需要添加自定义字段
### 从内部格式到 SillyTavern 格式
1. 使用适配器将内部格式转换为 ST 格式
2. 不在 ST 规范中的自定义字段会被排除
3. 如果可用,使用原始 ST 数据
4. 以所需格式导出PNG/JSON/JSONL
## 类型安全
所有类型都使用 TypeScript 完整定义:
- 核心模型中不使用 `any` 类型
- 严格的空值检查
- 基于枚举的常量
- 全面的接口定义
## 下一步计划
1. 实现适配器类
2. 创建验证函数
3. 为转换逻辑添加单元测试
4. 构建导入导出 UI 组件

View File

@@ -1,170 +0,0 @@
/**
* Import/Export Adapter Interfaces
* Ensures data compatibility between SillyTavern format and internal format
*/
import type { STCharacterCardV2 } from '../sillytavern/character.types';
import type { STChatSession } from '../sillytavern/chat.types';
import type { STWorldInfo } from '../sillytavern/world.types';
import type { STPreset } from '../sillytavern/preset.types';
import type { ExtendedChatMetadata, ExtendedChatMessage } from '../extended/chat-ext.types';
import type { ExtendedPreset } from '../extended/preset-ext.types';
import type { ExtendedWorldInfo } from '../extended/world-ext.types';
/**
* Character Card Adapter
*/
export interface CharacterAdapter {
/**
* Import from SillyTavern format
*/
importFromST(stCard: STCharacterCardV2): InternalCharacter;
/**
* Export to SillyTavern format
*/
exportToST(internalChar: InternalCharacter): STCharacterCardV2;
/**
* Validate SillyTavern format
*/
validateSTFormat(data: any): boolean;
}
/**
* Chat Session Adapter
*/
export interface ChatAdapter {
/**
* Import from SillyTavern JSONL format
*/
importFromST(jsonlContent: string): InternalChatSession;
/**
* Export to SillyTavern JSONL format
*/
exportToST(chat: InternalChatSession): string;
/**
* Parse JSONL to structured data
*/
parseJSONL(jsonlContent: string): STChatSession;
/**
* Convert structured data to JSONL
*/
toJSONL(session: STChatSession): string;
}
/**
* World Info Adapter
*/
export interface WorldInfoAdapter {
/**
* Import from SillyTavern format
*/
importFromST(stWorldInfo: STWorldInfo): ExtendedWorldInfo;
/**
* Export to SillyTavern format
*/
exportToST(extendedWorldInfo: ExtendedWorldInfo): STWorldInfo;
/**
* Convert unified trigger config to ST format
*/
triggersToST(triggers: any): { keys: string[]; constant: boolean };
/**
* Convert ST format to unified trigger config
*/
triggersFromST(keys: string[], constant: boolean): any;
}
/**
* Preset Adapter
*/
export interface PresetAdapter {
/**
* Import from SillyTavern format
*/
importFromST(stPreset: STPreset): ExtendedPreset;
/**
* Export to SillyTavern format
*/
exportToST(extendedPreset: ExtendedPreset): STPreset;
}
/**
* Internal unified data models
*/
export interface InternalCharacter {
id: string;
name: string;
description: string;
personality: string;
scenario: string;
firstMessage: string;
exampleMessages: string;
avatarUrl?: string;
tags: string[];
createdAt: Date;
updatedAt: Date;
// Original ST data preserved
stData?: STCharacterCardV2;
}
export interface InternalChatSession {
id: string;
characterId: string;
metadata: ExtendedChatMetadata;
messages: ExtendedChatMessage[];
createdAt: Date;
updatedAt: Date;
}
/**
* Import result with validation
*/
export interface ImportResult<T = any> {
success: boolean;
data?: T;
errors: ImportError[];
warnings: ImportWarning[];
sourceFormat: string;
importedAt: Date;
}
export interface ImportError {
code: string;
message: string;
field?: string;
severity: 'error' | 'critical';
}
export interface ImportWarning {
code: string;
message: string;
suggestion?: string;
}
/**
* Export options
*/
export interface ExportOptions {
format: 'json' | 'png' | 'jsonl';
includeMetadata: boolean;
includeExtensions: boolean;
compress: boolean;
}
export interface ExportResult {
success: boolean;
content?: string | Blob;
filename: string;
mimeType: string;
error?: string;
}

View File

@@ -1,36 +0,0 @@
/**
* Common shared types
*/
export interface BaseEntity {
id: string;
createdAt: Date;
updatedAt: Date;
}
export interface SoftDeleteEntity {
deletedAt?: Date;
isDeleted: boolean;
}
export type Entity<T = {}> = T & BaseEntity;
export interface PaginatedResponse<T> {
items: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}
export interface SortOption {
field: string;
order: 'asc' | 'desc';
}
export interface FilterOption {
field: string;
operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'contains';
value: any;
}

View File

@@ -1,73 +0,0 @@
/**
* Extended Chat Metadata (Your custom additions)
* This is the "AI character card" part of chat JSONL
*/
export interface ExtendedChatMetadata {
// Core identity
characterName: string;
// Dynamic table headers (user-defined, with defaults)
dynamicHeaders: DynamicHeader[];
// Character info
characterDescription: string;
characterAvatar?: string; // URL or base64
aiCharacterName: string;
creatorNotes?: string;
// Messages
firstMessages: string[]; // List of alternate first messages
systemPrompt: string;
// Extensions (preserve original ST extensions)
extensions?: Record<string, any>;
// Your custom fields
createdAt: Date;
updatedAt: Date;
tags?: string[];
category?: string;
}
export interface DynamicHeader {
id: string;
name: string;
type: 'text' | 'number' | 'boolean' | 'select' | 'multiselect';
defaultValue?: any;
options?: string[]; // For select/multiselect types
required?: boolean;
order: number;
}
/**
* Extended Chat Message (Your custom additions)
*/
export interface ExtendedChatMessage {
// Core fields
senderName: string;
senderRole: 'user' | 'assistant' | 'system' | 'narrator';
mes: string; // Message content
// Your custom fields
swipes?: string[]; // Alternative responses
reasoning?: string; // Chain of thought / reasoning process
image?: string; // Base64 or URL
audio?: string; // Base64 or URL
// Metadata
timestamp: Date;
tokens?: number;
model?: string;
// Dynamic data (matches dynamicHeaders)
dynamicData?: Record<string, any>;
}
/**
* Complete Extended Chat Session
*/
export interface ExtendedChatSession {
metadata: ExtendedChatMetadata;
messages: ExtendedChatMessage[];
}

View File

@@ -1,70 +0,0 @@
/**
* Extended Preset Format (Your custom additions)
*/
export interface ExtendedPreset {
// Core sampling parameters
temperature: number;
maxReplyLength: number;
topP: number;
topK: number;
repetitionPenalty: number;
// Ordered list of preset entry names
entryOrder: string[];
// Context settings
maxContextTokens: number;
streaming: boolean;
// Your custom fields
id: string;
name: string;
description?: string;
createdAt: Date;
updatedAt: Date;
// Preset entries with fixed placeholders
entries: PresetEntry[];
}
/**
* Preset Entry with position anchoring
*/
export interface PresetEntry {
name: string;
context: string; // Content/prompt
position: PresetPosition;
order: number;
}
/**
* Fixed placeholder positions for preset entries
* These anchor specific content types in the prompt
*/
export enum PresetPosition {
SYSTEM = 'system', // System prompt (from character card)
WORLD_INFO_BEFORE = 'world_info_before', // World Info (before character)
PERSONA_DESCRIPTION = 'persona_description', // Character persona description
CHAR_DESCRIPTION = 'char_description', // Character detailed description
WORLD_INFO_AFTER = 'world_info_after', // World Info (after character)
HISTORY = 'history', // Chat history
USER_INPUT = 'user_input' // Current user input
}
/**
* API Configuration for LLM
*/
export interface ApiConfig {
apiUrl: string;
apiKey: string;
apiType: ApiType;
model: string;
}
export enum ApiType {
PRIMARY = 'primary', // Main chat/completion API
SECONDARY = 'secondary', // Fallback API
IMAGE_GEN = 'image_gen', // Image generation API
RAG = 'rag' // RAG/Embedding API
}

View File

@@ -1,102 +0,0 @@
/**
* Extended World Info Format (Your custom additions)
* Integrates "key" and "constant" into a unified trigger system
*/
export interface ExtendedWorldInfo {
id: string;
name: string;
description?: string;
entries: ExtendedWorldInfoEntry[];
metadata: WorldInfoMetadata;
createdAt: Date;
updatedAt: Date;
}
export interface ExtendedWorldInfoEntry {
id: string;
content: string;
position: 'before_char' | 'after_char';
scanDepth: number; // How many messages to scan
priority: number; // Order at same position
// Unified trigger system (replaces key + constant)
triggers: TriggerConfig;
enabled: boolean;
// Your custom fields
category?: string;
tags?: string[];
usageCount?: number;
lastUsed?: Date;
}
/**
* Unified Trigger Configuration
* Replaces separate "key" and "constant" fields
*/
export interface TriggerConfig {
permanent: PermanentTrigger; // Always active (no trigger needed)
keyword: KeywordTrigger; // Keyword-based activation
rag: RagTrigger; // RAG/database-based activation
variable: VariableTrigger; // Variable/expression-based activation
}
/**
* Permanent trigger - always active, no conditions
*/
export interface PermanentTrigger {
enabled: boolean;
}
/**
* Keyword trigger - activates when keywords are found
*/
export interface KeywordTrigger {
enabled: boolean;
keywords: string[];
useRegex: boolean;
matchWholeWords: boolean;
caseSensitive: boolean;
}
/**
* RAG trigger - activates based on vector similarity
*/
export interface RagTrigger {
enabled: boolean;
databaseName: string; // Associated database/collection name
threshold?: number; // Similarity threshold (0-1)
topK?: number; // Number of similar entries to retrieve
}
/**
* Variable trigger - activates based on variable comparison
*/
export interface VariableTrigger {
enabled: boolean;
variableA: string;
operator: ComparisonOperator;
variableB?: string; // Can be variable or constant
constantA?: any; // Alternative to variableB
}
export enum ComparisonOperator {
GREATER_THAN = '>',
LESS_THAN = '<',
EQUALS = '=',
CONTAINS = 'contains',
NOT_GREATER_THAN = '<=',
NOT_LESS_THAN = '>=',
NOT_EQUALS = '!='
}
export interface WorldInfoMetadata {
entryCount: number;
categories: string[];
tags: string[];
}

View File

@@ -1,29 +0,0 @@
/**
* Shared Types - Main Export File
* Import from this file in your application
*/
// ============================================
// SillyTavern Original Formats (Compatibility Layer)
// ============================================
export * from './sillytavern/character.types';
export * from './sillytavern/chat.types';
export * from './sillytavern/world.types';
export * from './sillytavern/preset.types';
// ============================================
// Extended Data (Your Custom Additions)
// ============================================
export * from './extended/chat-ext.types';
export * from './extended/preset-ext.types';
export * from './extended/world-ext.types';
// ============================================
// Import/Export Adapters
// ============================================
export * from './adapters/import-export.types';
// ============================================
// Common Types
// ============================================
export * from './common.types';

View File

@@ -1,97 +0,0 @@
/**
* SillyTavern Character Card V2 Specification
* Reference: https://github.com/malfoyslastname/character-card-spec-v2
*/
export interface STCharacterCardV2 {
spec: 'chara_card_v2';
spec_version: '2.0';
data: STCharacterData;
}
export interface STCharacterData {
// Core fields
name: string;
description: string;
personality: string;
scenario: string;
first_mes: string;
mes_example: string;
// Optional fields
creator_notes?: string;
system_prompt?: string;
post_history_instructions?: string;
alternate_greetings?: string[];
tags?: string[];
creator?: string;
character_version?: string;
// Character Book (World Info)
character_book?: STCharacterBook;
// Extensions
extensions?: Record<string, any>;
}
export interface STCharacterBook {
entries: STWorldInfoEntry[];
extensions?: Record<string, any>;
}
export interface STWorldInfoEntry {
id?: number;
keys: string[];
keysecondary?: string[];
content: string;
comment?: string;
constant: boolean;
constantRule?: 'none' | 'always' | 'position';
selective: boolean;
order: number;
insertion_order?: number;
position: 'before_char' | 'after_char';
use_regex: boolean;
match_whole_words?: boolean;
case_sensitive?: boolean;
useProbability?: boolean;
probability?: number;
depth?: number;
scan_depth?: number;
group?: string;
group_override?: boolean;
group_weight?: number;
prevent_recursion?: boolean;
delay_until_recursion?: boolean;
display_index?: number;
enabled: boolean;
exclude_from_banbook?: boolean;
linked_entries?: number[];
search_range?: 'both' | 'user' | 'assistant';
role?: number;
vectorized?: boolean;
sticky?: boolean;
cooldown?: number;
delay?: number;
automation_id?: string;
title?: string;
display_title?: string;
}
/**
* V1 Character Card (Legacy)
*/
export interface STCharacterCardV1 {
name: string;
context: string;
greeting: string;
examples: string;
author: string;
first_mes: string;
avatar: string;
chat: string;
mes_example: string;
description: string;
personality: string;
scenario: string;
}

View File

@@ -1,73 +0,0 @@
/**
* SillyTavern Chat History Format (JSONL)
* Each line is a JSON object representing a message or metadata
*/
/**
* Chat Metadata (First line of JSONL file)
*/
export interface STChatMetadata {
user_name: string;
character_name: string;
create_date: string; // ISO 8601 format
chat_metadata?: {
integrity?: string; // SHA256 hash for integrity check
[key: string]: any;
};
}
/**
* Chat Message (Subsequent lines in JSONL file)
*/
export interface STChatMessage {
name: string;
is_user: boolean;
send_date: number; // Unix timestamp
mes: string; // Message content
// Optional fields
force_avatar?: string;
extra?: STMessageExtra;
is_system?: boolean;
}
export interface STMessageExtra {
gen_id?: number;
send_date?: number;
swipes?: string[]; // Alternative responses
swipe_id?: number;
continueHistory?: boolean;
continueSwipeId?: number;
dryReason?: string;
abuse?: boolean;
rollbackChecked?: boolean;
prompt?: any;
memory?: any;
notes?: string;
bias?: any[];
display_text?: string;
model?: string;
model_params?: any;
persona_description?: string;
attachments?: STAttachment[];
reasoning?: string; // Chain of thought / reasoning
image?: string; // Base64 or path
audio?: string; // Base64 or path
}
export interface STAttachment {
type: 'image' | 'file' | 'audio';
path: string;
preview?: string;
name?: string;
size?: number;
mime?: string;
}
/**
* Complete Chat Session
*/
export interface STChatSession {
metadata: STChatMetadata;
messages: STChatMessage[];
}

View File

@@ -1,100 +0,0 @@
/**
* SillyTavern Preset Format
*/
export interface STPreset {
name: string;
// Core sampling parameters
temp?: number; // Temperature
rep_pen?: number; // Repetition penalty
rep_pen_range?: number;
top_p?: number;
top_k?: number;
top_a?: number;
min_p?: number;
typical?: number;
tfs?: number;
epsilon_cutoff?: number;
eta_cutoff?: number;
// Advanced parameters
rep_pen_slope?: number;
guidance_scale?: number;
negative_prompt?: string;
penalty_alpha?: number;
num_beams?: number;
length_penalty?: number;
no_repeat_ngram_size?: number;
encoder_rep_pen?: number;
freq_pen?: number; // Frequency penalty
presence_pen?: number; // Presence penalty
// Sampling options
do_sample?: boolean;
early_stopping?: boolean;
// Dynamic temperature
dynatemp?: boolean;
dynatemp_range?: number;
dynatemp_exponent?: number;
// Smoothing
smoothing_factor?: number;
smoothing_curve?: number;
// DRY (Don't Repeat Yourself)
dry_allowed_length?: number;
dry_multiplier?: number;
dry_base?: number;
dry_sequence_breakers?: string[];
dry_penalty_last_n?: number;
// Token settings
add_bos_token?: boolean;
ban_eos_token?: boolean;
skip_special_tokens?: boolean;
// Mirostat
mirostat_mode?: number;
mirostat_tau?: number;
mirostat_eta?: number;
// Grammar
grammar_string?: string;
json_schema?: any;
// Model specific
model?: string;
sampler_priority?: string[];
sampler_order?: number[];
use_default_badwordsids?: boolean;
seed?: number;
// Context
maxContextTokens?: number;
streaming?: boolean;
// Metadata
description?: string;
created_at?: number;
updated_at?: number;
}
/**
* Preset Entry (for ordered list in preset configuration)
*/
export interface STPresetEntry {
name: string;
context: string; // Content/prompt
position: 'before_char' | 'after_char' | 'system' | 'history' | 'user_input';
order: number;
}
/**
* Quick Preset (Simplified format)
*/
export interface STQuickPreset {
name: string;
settings: Partial<STPreset>;
}

View File

@@ -1,55 +0,0 @@
/**
* SillyTavern World Info (Lorebook) Format
*/
export interface STWorldInfo {
name: string;
description?: string;
entries: STWorldInfoEntry[];
metadata?: STWorldInfoMetadata;
}
export interface STWorldInfoEntry {
uid?: number;
key: string[]; // Trigger keywords
keysecondary?: string[]; // Secondary triggers
comment?: string;
content: string; // Lore content
constant: boolean; // Always active
constantRule?: 'none' | 'always' | 'position';
selective: boolean; // Use secondary keys
order: number; // Insertion order
position: 'before_char' | 'after_char'; // Insertion position
use_regex: boolean;
match_whole_words?: boolean;
case_sensitive?: boolean;
useProbability?: boolean;
probability?: number;
depth?: number; // Scan depth (how many messages to scan)
scan_depth?: number;
group?: string;
group_override?: boolean;
group_weight?: number;
prevent_recursion?: boolean;
delay_until_recursion?: boolean;
display_index?: number;
enabled: boolean;
exclude_from_banbook?: boolean;
linked_entries?: number[];
search_range?: 'both' | 'user' | 'assistant';
role?: number;
vectorized?: boolean; // RAG support
sticky?: boolean;
cooldown?: number;
delay?: number;
automation_id?: string;
title?: string;
display_title?: string;
}
export interface STWorldInfoMetadata {
created_at?: number;
updated_at?: number;
version?: string;
source?: string;
}