diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..dd3cfc2 --- /dev/null +++ b/.env.example @@ -0,0 +1,15 @@ +# Application +NODE_ENV=development +PORT=3000 +FRONTEND_URL=http://localhost:5173 + +# Data Directory +DATA_DIR=./data + +# LLM API Keys (配置你使用的 LLM 提供商) +OPENAI_API_KEY=your-openai-api-key +ANTHROPIC_API_KEY=your-anthropic-api-key + +# Optional: Custom LLM Provider Settings +LLM_PROVIDER=openai # openai | anthropic | custom +LLM_MODEL=gpt-4 # 根据你的 provider 选择合适的模型 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d30b6a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Dependencies +node_modules/ +.pnp +.pnp.js + +# Testing +coverage/ +*.lcov + +# Production +dist/ +build/ + +# Environment variables +.env +.env.local +.env.*.local + +# Logs +logs/ +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Docker +docker-compose.override.yml + +# Data (optional - uncomment if you don't want to commit data) +# data/ + +# OS +Thumbs.db diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..18ebf6e --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,429 @@ +# 项目架构设计文档 + +## 📐 整体架构 + +本项目采用**前后端分离 + 共享层**的单体仓库(monorepo)架构,严格遵循 MVC 设计模式。 + +``` +┌─────────────────────────────────────────────┐ +│ SillyTavern Repalice │ +├─────────────────────────────────────────────┤ +│ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ Client │◄──►│ Shared │ │ +│ │ (Vue3) │ │ (Types) │ │ +│ └──────────┘ └──────────┘ │ +│ ▲ ▲ │ +│ │ HTTP/API │ Type Safety │ +│ ▼ ▼ │ +│ ┌──────────┐ ┌──────────┐ │ +│ │ Server │◄──►│ Shared │ │ +│ │(NestJS) │ │(Schemas) │ │ +│ └──────────┘ └──────────┘ │ +│ │ │ +│ ▼ │ +│ ┌──────────┐ │ +│ │ Data │ │ +│ │ (Local) │ │ +│ └──────────┘ │ +└─────────────────────────────────────────────┘ +``` + +## 🎯 核心设计决策 + +### 1. 双数据格式策略 + +**问题**: 需要兼容 SillyTavern 的导入导出,同时支持自定义扩展功能 + +**解决方案**: +- **外部格式** (`ST*` 前缀): 严格遵循 SillyTavern 规范 +- **内部格式** (无前缀): 继承并扩展,添加自定义字段 +- **转换器**: 双向转换,保证数据完整性 + +**示例**: +```typescript +// SillyTavern 格式 +interface STWorldInfoEntry { + constant?: boolean; // 永久激活标志 + selective?: boolean; // RAG 标志 +} + +// 内部格式 +interface WorldInfoEntry extends STWorldInfoEntry { + activationType: ActivationType; // 4种枚举 + logicExpression?: LogicExpression; + ragConfig?: RAGConfig; +} +``` + +### 2. MVC 分层架构 + +#### Model (模型层) +``` +server/src/ +├── services/ # 业务逻辑 +├── persistence/ # 数据访问 +│ ├── interfaces/ # 仓储接口 +│ └── implementations/ # 具体实现 +└── dto/ # 数据传输对象 +``` + +**职责**: +- Service: 业务规则、数据验证、事务管理 +- Repository: CRUD 操作、数据持久化 +- DTO: API 输入输出验证 + +#### View (视图层) +``` +client/src/ +├── components/ # UI 组件 +│ ├── TopBar/ +│ ├── LeftPanel/ +│ ├── CenterPanel/ +│ ├── RightPanel/ +│ └── common/ +├── layouts/ # 页面布局 +└── stores/ # 状态管理 +``` + +**职责**: +- Components: UI 渲染、用户交互 +- Layouts: 页面结构、路由视图 +- Stores: 客户端状态管理 + +#### Controller (控制层) +``` +server/src/controllers/ # API 端点 +client/src/router/ # 前端路由 +``` + +**职责**: +- 接收请求、参数验证 +- 调用 Service 处理业务 +- 返回响应 + +### 3. 依赖注入 (DI) + +**后端 DI** (NestJS): +```typescript +@Injectable() +export class CharacterService { + constructor( + @Inject(CHARACTER_REPOSITORY) + private repo: CharacterRepository, + private llmService: LLMService, + ) {} +} +``` + +**优势**: +- 松耦合:模块间通过接口通信 +- 可测试:轻松 mock 依赖 +- 可替换:不同实现无缝切换 + +### 4. 工具层 (Tools Layer) + +独立于 MVC 的工具层,提供通用能力: + +``` +server/src/tools/ +├── context-chunker.ts # 上下文分块 +├── prompt-assembler.ts # 提示词组装 +└── token-counter.ts # Token 计数 +``` + +**特点**: +- 无状态纯函数 +- 不依赖 DI 容器 +- 可在任何层调用 + +## 🗂️ 目录结构设计原则 + +### 后端 (NestJS) + +``` +server/src/ +├── modules/ # 按业务领域划分 +│ ├── chat/ # 聊天管理 +│ ├── character/ # 角色卡管理 +│ ├── llm/ # LLM 集成 +│ ├── import-export/# 导入导出 +│ └── workflow/ # 工作流引擎 +│ +├── persistence/ # 持久化抽象 +│ ├── interfaces/ # 仓储接口 +│ ├── implementations/ # 实现 +│ └── file-system/ # 文件系统实现 +│ +├── core/ # 基础设施 +│ ├── config/ # 配置管理 +│ ├── filters/ # 异常过滤 +│ ├── interceptors/ # 拦截器 +│ └── guards/ # 守卫 +│ +├── controllers/ # 路由层 (横向切割) +├── services/ # 业务层 (横向切割) +└── tools/ # 工具层 (横向切割) +``` + +**设计原则**: +1. **垂直切片**: 每个 module 自包含 controller + service +2. **水平分层**: persistence/core 跨模块复用 +3. **依赖方向**: controllers → services → persistence + +### 前端 (Vue3) + +``` +client/src/ +├── components/ # 按布局区域划分 +│ ├── TopBar/ # 顶部栏 +│ ├── LeftPanel/ # 左侧面板 +│ ├── CenterPanel/ # 中央面板 +│ ├── RightPanel/ # 右侧面板 +│ └── common/ # 通用组件 +│ +├── layouts/ # 布局模板 +├── stores/ # 全局状态 +├── router/ # 路由配置 +├── composables/ # 组合式函数 +└── api/ # API 客户端 +``` + +**设计原则**: +1. **布局驱动**: 组件按 UI 区域组织 +2. **关注点分离**: 状态、路由、API 独立管理 +3. **组合优于继承**: 使用 composables 复用逻辑 + +### 共享层 (Shared) + +``` +shared/ +├── types/ # TypeScript 类型 +│ ├── sillytavern.types.ts # ST 兼容格式 +│ ├── internal.types.ts # 内部扩展格式 +│ └── converters.ts # 转换器 +│ +└── schemas/ # Zod 运行时验证 + ├── sillytavern.schemas.ts + └── internal.schemas.ts +``` + +**设计原则**: +1. **单一真相源**: 前后端共用类型定义 +2. **编译时 + 运行时**: TypeScript + Zod 双重验证 +3. **向后兼容**: 转换器保证旧数据可用 + +## 🔄 数据流 + +### 典型请求流程 + +``` +User Action (Frontend) + ↓ +Vue Component + ↓ +Pinia Store + ↓ +API Client (axios) + ↓ +HTTP Request + ↓ +NestJS Controller + ↓ +Service (Business Logic) + ↓ +Repository (Data Access) + ↓ +File System + ↓ +Response (JSON) + ↓ +Update UI +``` + +### 数据转换流程 + +``` +SillyTavern Import File + ↓ +Zod Schema Validation (ST Schema) + ↓ +Parse to ST Types + ↓ +Converter (convertSTToInternal) + ↓ +Internal Types + ↓ +Save to File System +``` + +## 🧪 测试策略 + +### 单元测试 +- **目标**: Services, Tools, Utils +- **工具**: Jest +- **Mock**: 外部依赖 (Repository, LLM API) + +### 集成测试 +- **目标**: Controller + Service 协作 +- **工具**: Supertest +- **范围**: 单个模块的完整流程 + +### E2E 测试 +- **目标**: 关键用户流程 +- **工具**: Playwright / Cypress +- **场景**: 创建角色、发送消息、导入导出 + +## 🚀 部署架构 + +### 开发环境 +``` +Client (Vite Dev Server) :5173 + ↓ Proxy /api +Server (NestJS) :3000 + ↓ +Local File System ./data +``` + +### 生产环境 (Docker) +``` +Client (Nginx) :80 + ↓ Proxy /api +Server (Node.js) :3000 + ↓ +Volume Mount /app/data +``` + +## 📊 关键技术选型理由 + +| 技术 | 选型理由 | +|------|---------| +| **NestJS** | 内置 DI、模块化、TypeScript 优先、企业级 | +| **Vue 3** | 组合式 API、响应式系统、学习曲线平缓 | +| **Vite** | 极速开发体验、原生 ESM、热更新快 | +| **Zod** | TypeScript 优先、运行时验证、 schema 推导 | +| **Vercel AI SDK** | 统一 LLM 接口、流式支持、工具调用 | +| **Pinia** | Vue 3 官方推荐、TypeScript 友好、轻量 | +| **文件系统** | 零依赖、易备份、用户可控、无需数据库 | + +## 🎨 设计模式应用 + +### 1. Repository Pattern +```typescript +interface CharacterRepository { + findById(id: string): Promise; + save(character: CharacterCard): Promise; + delete(id: string): Promise; +} + +class FileSystemCharacterRepository implements CharacterRepository { + // 实现细节隐藏 +} +``` + +### 2. Factory Pattern +```typescript +class LLMProviderFactory { + static create(provider: ProviderType): LLMProvider { + switch (provider) { + case 'openai': return new OpenAIProvider(); + case 'anthropic': return new AnthropicProvider(); + } + } +} +``` + +### 3. Strategy Pattern +```typescript +interface ActivationStrategy { + shouldActivate(entry: WorldInfoEntry, context: string): boolean; +} + +class KeywordActivationStrategy implements ActivationStrategy { + // 关键词匹配逻辑 +} + +class RAGActivationStrategy implements ActivationStrategy { + // 向量检索逻辑 +} +``` + +### 4. Adapter Pattern +```typescript +// 适配器:SillyTavern 格式 → 内部格式 +class SillyTavernAdapter { + static toInternal(stCard: STCharacterCard): CharacterCard { + return convertSTCharacterCardToInternal(stCard); + } + + static toExternal(card: CharacterCard): STCharacterCard { + return convertCharacterCardToST(card); + } +} +``` + +## 🔐 安全性考虑 + +1. **输入验证**: Zod schemas 在边界验证所有输入 +2. **文件上传**: 限制类型、大小,扫描恶意内容 +3. **API 密钥**: 环境变量管理,不提交到版本控制 +4. **CORS**: 严格配置允许的来源 +5. **路径遍历**: 验证文件路径,防止 `../` 攻击 + +## 📈 性能优化 + +1. **懒加载**: 前端路由和组件按需加载 +2. **缓存**: LLM 响应缓存、角色卡元数据缓存 +3. **分页**: 聊天记录、角色列表分页加载 +4. **流式响应**: SSE 传输 LLM 生成内容 +5. **索引**: 为搜索字段建立内存索引 + +## 🔄 扩展性设计 + +### 新增 LLM Provider +1. 实现 `LLMProvider` 接口 +2. 注册到 `LLMProviderFactory` +3. 更新配置 schema + +### 新增持久化方式 +1. 实现 Repository 接口 +2. 创建新模块 (如 `persistence/sqlite/`) +3. 通过 DI 切换实现 + +### 新增激活方式 +1. 扩展 `ActivationType` 枚举 +2. 实现新的 `ActivationStrategy` +3. 更新世界书 UI + +## 📝 编码规范 + +### TypeScript +- 严格模式: `strict: true` +- 禁止 `any`,使用 `unknown` 代替 +- 接口命名不加 `I` 前缀 +- 枚举使用 PascalCase + +### Vue 3 +- 优先使用 `