mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-17 13:06:21 +00:00
Compare commits
322 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 562ef7fd8e | |||
| af59bfe013 | |||
| c0974ea955 | |||
| c83d7afbe6 | |||
| 210a41bb8b | |||
| e8c08335c3 | |||
| cf82bc0628 | |||
| e702064e39 | |||
| d14debc7d7 | |||
| 8231aea1c7 | |||
| af274190a8 | |||
| eb50c8b781 | |||
| c25492e377 | |||
| 082b641270 | |||
| 33874c20d1 | |||
| 0d48ebddd9 | |||
| 95393ec093 | |||
| 565e0d9a39 | |||
| 5fe2518dae | |||
| 1cb36da520 | |||
| cdded292f4 | |||
| 9b62685b08 | |||
| 249b46e5cf | |||
| dbe7d7ef7c | |||
| 1e4011e489 | |||
| 3a8229a2c6 | |||
| ed8174fc3d | |||
| 304e6c13d8 | |||
| a63485d915 | |||
| 01d66a9368 | |||
| 794748373b | |||
| a060901c65 | |||
| 7a34c8babe | |||
| 63e3b70681 | |||
| 476b897fe0 | |||
| ae3ed6ec47 | |||
| 5a69857e09 | |||
| e01c72cf03 | |||
| 95d4a3a4be | |||
| 6430f57665 | |||
| 99ded4ac33 | |||
| 582f6d1fbf | |||
| 8b12fdfb82 | |||
| ba3f67f7d4 | |||
| 0a6d3ad3f9 | |||
| e10aa05a11 | |||
| d835c33699 | |||
| 55c45a5197 | |||
| b51839fc54 | |||
| 1e9b05d7ce | |||
| 92da716028 | |||
| 635d0d649b | |||
| 3ae352b79e | |||
| a194d48545 | |||
| 0ee4f18d89 | |||
| 49ea508cc4 | |||
| 88592d3f08 | |||
| 7e0b715e22 | |||
| 8db47eb816 | |||
| 6325602480 | |||
| b745f11873 | |||
| 7f31eba0d9 | |||
| dfeb42ce1c | |||
| 138071d0e3 | |||
| 1aaa5f5152 | |||
| e1acbf21fd | |||
| 79bc08a076 | |||
| fa6ef94067 | |||
| a30a65cd4c | |||
| 2e6018a496 | |||
| 1776a24943 | |||
| 27d133a417 | |||
| de3478b17a | |||
| 694cdbea8f | |||
| ffbb804b3f | |||
| 5947148c01 | |||
| b0cb96e5c2 | |||
| f1d732d166 | |||
| c6de50e385 | |||
| b04a5d7906 | |||
| 088cc2c56c | |||
| acb49c1393 | |||
| e82d4b7274 | |||
| 2dc03b47d6 | |||
| 3e64ee659e | |||
| f653ce1737 | |||
| eeabb69088 | |||
| 356cf029dd | |||
| 6e7b420347 | |||
| ee464838ac | |||
| ec5af1b4c7 | |||
| 273e0277d1 | |||
| 4fb18ac6a8 | |||
| 74c8ef2686 | |||
| e35e3787c0 | |||
| c0e5a1d6e3 | |||
| abba6a9eff | |||
| 4ea45b18d0 | |||
| a21e71833b | |||
| dd9bbdc362 | |||
| 1a443e8d8a | |||
| 4a00672f9b | |||
| 3db2a39585 | |||
| 9a4939cca9 | |||
| 5c8b48efee | |||
| 6d3e5d1a80 | |||
| 67401c07aa | |||
| cf81abc909 | |||
| 13e936fbe0 | |||
| 8636fe45e3 | |||
| 1a77d9f9c6 | |||
| 8616977253 | |||
| 1e17da19d9 | |||
| 4349ad955d | |||
| 7d619cc0c1 | |||
| af003c5efa | |||
| d54113036a | |||
| 652e0ff415 | |||
| 800528d459 | |||
| b050bd7b9f | |||
| 0719f18af1 | |||
| c2dfed6f7b | |||
| 02566b1a95 | |||
| 8f9464acf8 | |||
| a21472724b | |||
| 20ef12443d | |||
| 4a24df9155 | |||
| dbec6b6776 | |||
| d9a4361994 | |||
| 1251548120 | |||
| 6b0f8eddb5 | |||
| 18e4c04fb9 | |||
| 785d5d7647 | |||
| c665646534 | |||
| ebd924c371 | |||
| b52183cdbf | |||
| 78a2f9e874 | |||
| 8f274aeba9 | |||
| 9146362c9b | |||
| 1c61b21133 | |||
| c14d165124 | |||
| 69817182a0 | |||
| 4099dfd955 | |||
| 48ad272f90 | |||
| 9352a9c80e | |||
| f02d43b8d3 | |||
| 6808099ce9 | |||
| 08347bbe01 | |||
| 47ec2d81dc | |||
| 541d0f46b2 | |||
| af3daa4022 | |||
| 58d34ff53f | |||
| b5389e44a2 | |||
| e827f286e6 | |||
| 7f60544950 | |||
| 116af84640 | |||
| 764fd4fa78 | |||
| f37ec1d4c2 | |||
| a94e8810a3 | |||
| 45b7a43170 | |||
| 7dc000e10c | |||
| f25fda6de2 | |||
| d2ff75cc05 | |||
| 8de927a419 | |||
| fe9d4afa05 | |||
| 54704b1247 | |||
| 65d2c0c2ea | |||
| 9f99b0bc2a | |||
| a78fc1fd24 | |||
| 4e0e99e503 | |||
| a0074fc280 | |||
| 6c01f21cec | |||
| 8f4a98fed0 | |||
| 70f52a3c1f | |||
| 8711ea244c | |||
| 255a89c9f3 | |||
| b8c4c4e927 | |||
| 54c0ac4426 | |||
| b7e7186229 | |||
| 0d1086fe55 | |||
| 9f044edd07 | |||
| 1762dc9148 | |||
| 2e928cd587 | |||
| 0ea738222d | |||
| 25a5be639a | |||
| e137b33c8d | |||
| 47c6e7fc17 | |||
| 046138b32c | |||
| c018f3d05b | |||
| 96ff5aa461 | |||
| 9f29869fd2 | |||
| e3afca3678 | |||
| b47bb5b3aa | |||
| cee555a0f0 | |||
| 28f84d5cb4 | |||
| d0063f809a | |||
| 5a4b0fd344 | |||
| 47874ac6c7 | |||
| dfdcdce298 | |||
| 72443783a1 | |||
| c21c14e715 | |||
| 46f2a28ee7 | |||
| 11ceb8b346 | |||
| 01103e7d1f | |||
| 37f7028c4c | |||
| d1f68c5b9f | |||
| 9568eafd76 | |||
| 66562170ac | |||
| ed42753fe5 | |||
| cc67b5443d | |||
| 20e026c9f1 | |||
| 240c7b7f4f | |||
| b73d0972b2 | |||
| d942a635b3 | |||
| a47ec04f20 | |||
| 20eb99534c | |||
| 96b9c9705a | |||
| 3f42ee3dfe | |||
| 0f9755bbd7 | |||
| 87ae85f7c7 | |||
| 04764ad1cb | |||
| e8044e3eca | |||
| e9412f146c | |||
| 6c72681b26 | |||
| 31d2cb8fd7 | |||
| 1cf371f418 | |||
| ebc205a8ae | |||
| 78eaead0d2 | |||
| 45fa4e01ae | |||
| 504b14dd3f | |||
| d753169b69 | |||
| cfb2246492 | |||
| 3a0b0d2173 | |||
| 7f1b631c35 | |||
| eca41d33bf | |||
| c8dea6322d | |||
| 2c677e597a | |||
| a1f7bff302 | |||
| e00ee6c6d1 | |||
| f89b73059d | |||
| 642dc3b6c9 | |||
| e33c84bcae | |||
| fd84da415b | |||
| dacfffdb63 | |||
| c38079d36f | |||
| cf0272cc1b | |||
| 35da7651b7 | |||
| b2c1209eb7 | |||
| 5e814e017d | |||
| 9a804b5df5 | |||
| 0d4264e9ad | |||
| 419557a2df | |||
| 8ba662c9de | |||
| 43a455549c | |||
| 58f1a52eb9 | |||
| 4b134968bf | |||
| 937dd85c6b | |||
| bed591c559 | |||
| 9089e2683e | |||
| 7fe17e4028 | |||
| 6ace931e52 | |||
| 76de2a20c1 | |||
| 4ee1d292ff | |||
| b6aad3b1bc | |||
| b980c3d702 | |||
| 987fbf2adf | |||
| 790af5ff40 | |||
| 3eaeb6c531 | |||
| aa841a3879 | |||
| a49422b322 | |||
| e3728750af | |||
| 26a743f3bd | |||
| bfbf38d106 | |||
| f8eb891d3b | |||
| 52ec64dfd4 | |||
| e6fc02eb9d | |||
| 575e334d7f | |||
| a4ed5a053f | |||
| dbb49337b4 | |||
| 8be822b0b7 | |||
| 5a999c0fbc | |||
| 9abeb7b545 | |||
| 8751141c7c | |||
| 3519cb20d9 | |||
| e135e2bb87 | |||
| a494af8e7f | |||
| ea819a3421 | |||
| b00e6d7817 | |||
| 58378fd10f | |||
| c0736ead54 | |||
| 9e0621f873 | |||
| 082898825c | |||
| 7832287abd | |||
| 5fe229c600 | |||
| 6b9337b8ae | |||
| b3856026d2 | |||
| 8dbcbd8c4b | |||
| a5b6aadc1f | |||
| 3690724751 | |||
| 6f87034303 | |||
| ae28f1794c | |||
| 68783fd2f7 | |||
| 9e43c1d6e0 | |||
| 4d5246acd8 | |||
| f813d0c429 | |||
| 1a75f4a6b5 | |||
| f4c3002b55 | |||
| e3aacfcdce | |||
| 67ad3c42cc | |||
| df2d001336 | |||
| a66856dc83 | |||
| e7036af61e | |||
| 86ff95ff15 | |||
| 926577302e | |||
| 38d9d98b97 | |||
| e2448eb091 | |||
| 5747239625 | |||
| d3544f90d3 | |||
| 3e537cd01d | |||
| 72e77b497c | |||
| 285e2f35f7 | |||
| 9b8435b024 |
@@ -1,176 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs: src/services/**/*,src/database/**/*,src/server/**/*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# LobeChat 后端技术架构指南
|
||||
|
||||
本指南旨在阐述 LobeChat 项目的后端分层架构,重点介绍各核心目录的职责以及它们之间的协作方式。
|
||||
|
||||
## 目录结构映射
|
||||
|
||||
```
|
||||
src/
|
||||
├── server/
|
||||
│ ├── routers/ # tRPC API 路由定义
|
||||
│ └── services/ # 业务逻辑服务层
|
||||
│ └── */impls/ # 平台特定实现
|
||||
├── database/
|
||||
│ ├── models/ # 数据模型 (单表 CRUD)
|
||||
│ ├── repositories/ # 仓库层 (复杂查询/聚合)
|
||||
│ └── schemas/ # Drizzle ORM 表定义
|
||||
└── services/ # 客户端服务 (调用 tRPC 或直接访问 Model)
|
||||
```
|
||||
|
||||
## 核心架构分层
|
||||
|
||||
LobeChat 的后端设计注重模块化、可测试性和灵活性,以适应不同的运行环境(如浏览器端 PGLite、服务端远程 PostgreSQL 以及 Electron 桌面应用)。
|
||||
|
||||
其主要分层如下:
|
||||
|
||||
1. 客户端服务层 (`src/services`):
|
||||
- 位于 src/services/。
|
||||
- 这是客户端业务逻辑的核心层,负责封装各种业务操作和数据处理逻辑。
|
||||
- 环境适配: 根据不同的运行环境,服务层会选择合适的数据访问方式:
|
||||
- 本地数据库模式: 直接调用 `Model` 层进行数据操作,适用于浏览器 PGLite 和本地 Electron 应用。
|
||||
- 远程数据库模式: 通过 `tRPC` 客户端调用服务端 API,适用于需要云同步的场景。
|
||||
- 类型转换: 对于简单的数据类型转换,直接在此层进行类型断言,如 `this.pluginModel.query() as Promise<LobeTool[]>`
|
||||
- 每个服务模块通常包含 `client.ts`(本地模式)、`server.ts`(远程模式)和 `type.ts`(接口定义)文件,在实现时应该确保本地模式和远程模式业务逻辑实现一致,只是数据库不同。
|
||||
|
||||
2. API 接口层 (`TRPC`):
|
||||
- 位于 src/server/routers/
|
||||
- 使用 `tRPC` 构建类型安全的 API。Router 根据运行时环境(如 Edge Functions, Node.js Lambda)进行组织。
|
||||
- 负责接收客户端请求,并将其路由到相应的 `Service` 层进行处理。
|
||||
- 新建 lambda 端点时可以参考 src/server/routers/lambda/\_template.ts
|
||||
|
||||
3. 仓库层 (`Repositories`):
|
||||
- 位于 src/database/repositories/。
|
||||
- 主要处理复杂的跨表查询和数据聚合逻辑,特别是当需要从多个 `Model` 获取数据并进行组合时。
|
||||
- 与 `Model` 层不同,`Repository` 层专注于复杂的业务查询场景,而不涉及简单的领域模型转换。
|
||||
- 当业务逻辑涉及多表关联、复杂的数据统计或需要事务处理时,会使用 `Repository` 层。
|
||||
- 如果数据操作简单(仅涉及单个 `Model`),则通常直接在 `src/services` 层调用 `Model` 并进行简单的类型断言。
|
||||
|
||||
4. 模型层 (`Models`):
|
||||
- 位于 src/database/models/ (例如 src/database/models/plugin.ts 和 src/database/models/document.ts)。
|
||||
- 提供对数据库中各个表(由 src/database/schemas/ 中的 Drizzle ORM schema 定义)的基本 CRUD (创建、读取、更新、删除) 操作和简单的查询能力。
|
||||
- `Model` 类专注于单个数据表的直接操作,不涉及复杂的领域模型转换,这些转换通常在上层的 `src/services` 中通过类型断言完成。
|
||||
- model(例如 Topic) 层接口经常需要从对应的 schema 层导入 NewTopic 和 TopicItem
|
||||
- 创建新的 model 时可以参考 src/database/models/\_template.ts
|
||||
|
||||
5. 数据库 (`Database`):
|
||||
- 客户端模式 (浏览器/PWA): 使用 PGLite (基于 WASM 的 PostgreSQL),数据存储在用户浏览器本地。
|
||||
- 服务端模式 (云部署): 使用远程 PostgreSQL 数据库。
|
||||
- Electron 桌面应用:
|
||||
- Electron 客户端会启动一个本地 Node.js 服务。
|
||||
- 本地服务通过 `tRPC` 与 Electron 的渲染进程通信。
|
||||
- 数据库选择依赖于是否开启云同步功能:
|
||||
- 云同步开启: 连接到远程 PostgreSQL 数据库。
|
||||
- 云同步关闭: 使用 PGLite (通过 Node.js 的 WASM 实现) 在本地存储数据。
|
||||
|
||||
## 数据流向说明
|
||||
|
||||
### 浏览器/PWA 模式
|
||||
|
||||
```
|
||||
UI (React) → Zustand action -> Client Service → Model Layer → PGLite (本地数据库)
|
||||
```
|
||||
|
||||
### 服务端模式
|
||||
|
||||
```
|
||||
UI (React) → Zustand action → Client Service -> TRPC Client → TRPC Routers → Repositories/Models → Remote PostgreSQL
|
||||
```
|
||||
|
||||
### Electron 桌面应用模式
|
||||
|
||||
```
|
||||
UI (Electron Renderer) → Zustand action → Client Service -> TRPC Client → 本地 Node.js 服务 → TRPC Routers → Repositories/Models → PGLite/Remote PostgreSQL (取决于云同步设置)
|
||||
```
|
||||
|
||||
## 服务层 (Server Services)
|
||||
|
||||
- 位于 src/server/services/。
|
||||
- 核心职责是封装独立的、可复用的业务逻辑单元。这些服务应易于测试。
|
||||
- 平台差异抽象: 一个关键特性是通过其内部的 `impls` 子目录(例如 src/server/services/file/impls 包含 s3.ts 和 local.ts)来抹平不同运行环境带来的差异(例如云端使用 S3 存储,桌面版使用本地文件系统)。这使得上层(如 `tRPC` routers)无需关心底层具体实现。
|
||||
- 目标是使 `tRPC` router 层的逻辑尽可能纯粹,专注于请求处理和业务流程编排。
|
||||
- 服务可能会调用 `Repository` 层或直接调用 `Model` 层进行数据持久化和检索,也可能调用其他服务。
|
||||
|
||||
## 最佳实践 (Best Practices)
|
||||
|
||||
### 数据库操作封装原则
|
||||
|
||||
**连续的数据库操作应该封装到 Model 层**
|
||||
|
||||
当业务逻辑涉及多个相关的数据库操作时,建议将这些操作封装到 Model 层中,而不是在上层(Service 或 Router 层)中进行多次数据库调用。
|
||||
|
||||
**优势:**
|
||||
|
||||
- **代码复用**: Client DB 环境的 service 实现和 Server DB 的 lambda 层实现可以复用相同的 Model 方法
|
||||
- **事务一致性**: 相关的数据库操作可以在同一个方法中管理,便于维护数据一致性
|
||||
- **性能优化**: 减少数据库连接次数,提高查询效率
|
||||
- **职责清晰**: Model 层专注数据访问,上层专注业务协调
|
||||
|
||||
**示例:**
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐:在 Model 层封装连续的数据库操作
|
||||
class GenerationBatchModel {
|
||||
async delete(id: string): Promise<{ deletedBatch: BatchItem; thumbnailUrls: string[] }> {
|
||||
// 1. 查询相关数据
|
||||
const batchWithGenerations = await this.db.query.generationBatches.findFirst({...});
|
||||
|
||||
// 2. 收集需要处理的数据
|
||||
const thumbnailUrls = [...];
|
||||
|
||||
// 3. 执行删除操作
|
||||
const [deletedBatch] = await this.db.delete(generationBatches)...;
|
||||
|
||||
return { deletedBatch, thumbnailUrls };
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 上层使用简洁
|
||||
const { thumbnailUrls } = await model.delete(id);
|
||||
await fileService.deleteFiles(thumbnailUrls);
|
||||
```
|
||||
|
||||
### 文件操作与数据库操作的执行顺序
|
||||
|
||||
**删除操作原则:数据库删除在前,文件删除在后**
|
||||
|
||||
当业务逻辑同时涉及数据库记录和文件系统操作时,应该遵循"数据库优先"的原则。
|
||||
|
||||
**原因:**
|
||||
|
||||
- **用户体验优先**: 如果先删除文件再删除数据库记录,可能出现文件已删除但数据库记录仍存在的情况,用户访问时会遇到文件不存在的错误
|
||||
- **影响程度较小**: 如果先删除数据库记录再删除文件,即使文件删除失败,用户也看不到这个记录,只是造成一些存储空间浪费,对用户体验影响更小
|
||||
- **数据一致性**: 数据库记录是业务逻辑的核心,应该优先保证其一致性
|
||||
|
||||
**示例:**
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐:先删除数据库记录,再删除文件
|
||||
async deleteGeneration(id: string) {
|
||||
// 1. 先删除数据库记录
|
||||
const deletedGeneration = await generationModel.delete(id);
|
||||
|
||||
// 2. 再删除相关文件
|
||||
if (deletedGeneration.asset?.thumbnailUrl) {
|
||||
await fileService.deleteFile(deletedGeneration.asset.thumbnailUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 不推荐:先删除文件
|
||||
async deleteGeneration(id: string) {
|
||||
const generation = await generationModel.findById(id);
|
||||
|
||||
// 如果这里删除成功,但后面数据库删除失败,用户会遇到访问错误
|
||||
await fileService.deleteFile(generation.asset.thumbnailUrl);
|
||||
await generationModel.delete(id); // 可能失败
|
||||
}
|
||||
```
|
||||
|
||||
**创建操作原则:数据库创建在前,文件操作在后**
|
||||
|
||||
创建操作同样应该优先处理数据库记录,确保数据的一致性和完整性。
|
||||
@@ -1,58 +0,0 @@
|
||||
---
|
||||
description: How to code review
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Role Description
|
||||
|
||||
- You are a senior full-stack engineer skilled in performance optimization, security, and design systems.
|
||||
- You excel at reviewing code and providing constructive feedback.
|
||||
- Your task is to review submitted Git diffs **in Chinese** and return a structured review report.
|
||||
- Review style: concise, direct, focused on what matters most, with actionable suggestions.
|
||||
|
||||
## Before the Review
|
||||
|
||||
Gather the modified code and context. Please strictly follow the process below:
|
||||
|
||||
1. Use `read_file` to read [package.json](mdc:package.json)
|
||||
2. Use terminal to run command `git diff HEAD | cat` to obtain the diff and list the changed files. If you recieived empty result, run the same command once more.
|
||||
3. Use `read_file` to open each changed file.
|
||||
4. Use `read_file` to read [rules-attach.mdc](mdc:.cursor/rules/rules-attach.mdc). Even if you think it's unnecessary, you must read it.
|
||||
5. combine changed files, step3 and `agent_requestable_workspace_rules`, list the rules which need to read
|
||||
6. Use `read_file` to read the rules list in step 5
|
||||
|
||||
## Review
|
||||
|
||||
### Code Style
|
||||
|
||||
read [typescript.mdc](mdc:.cursor/rules/typescript.mdc) for the consolidated project code style and optimization rules.
|
||||
|
||||
### Code Optimization
|
||||
|
||||
The optimization checklist has been consolidated into [typescript.mdc](mdc:.cursor/rules/typescript.mdc): loops, debouncing/throttling, design system components, theming tokens, concurrency with `Promise.*`, minimal DB column selection, and package reuse.
|
||||
|
||||
### Obvious Bugs
|
||||
|
||||
- Do not silently swallow errors in `catch` blocks; at minimum, log them.
|
||||
- Revert temporary code used only for testing (e.g., debug logs, temporary configs).
|
||||
- Remove empty handlers (e.g., an empty `onClick`).
|
||||
- Confirm the UI degrades gracefully for unauthenticated users.
|
||||
- Don't leave any debug logs in the code (except when using the `debug` module properly).
|
||||
- When using the `debug` module, avoid `import { log } from 'debug'` as it logs directly to console. Use proper debug namespaces instead.
|
||||
- Check logs for sensitive information like api key, etc
|
||||
|
||||
## After the Review: output
|
||||
|
||||
1. Summary
|
||||
- Start with a brief explanation of what the change set does.
|
||||
- Summarize the changes for each modified file (or logical group).
|
||||
2. Comments Issues
|
||||
- List the most critical issues first.
|
||||
- Use an ordered list, which will be convenient for me to reference later.
|
||||
- For each issue:
|
||||
- Mark severity tag (`❌ Must fix`, `⚠️ Should fix`, `💅 Nitpick`)
|
||||
- Provode file path to the relevant file.
|
||||
- Provide recommended fix
|
||||
- End with a **git commit** command, instruct the author to run it.
|
||||
- We use gitmoji to label commit messages, format: [emoji] <type>(<scope>): <subject>
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# Guide to Optimize Output(Response) Rendering
|
||||
|
||||
## File Path and Code Symbol Rendering
|
||||
|
||||
- When rendering file paths, use backtick wrapping instead of markdown links so they can be parsed as clickable links in Cursor IDE.
|
||||
- Good: `src/components/Button.tsx`
|
||||
- Bad: [src/components/Button.tsx](src/components/Button.tsx)
|
||||
|
||||
- Don't use line and column number in file path, this will make file path not clickable in Cursor IDE.
|
||||
- Good: `src/components/Button.tsx` `10:20` (add a space between the file path and the line and column number)
|
||||
- Bad: `src/components/Button.tsx:10:20`
|
||||
|
||||
- When rendering functions, variables, or other code symbols, use backtick wrapping so they can be parsed as navigable links in Cursor IDE
|
||||
- Good: The `useState` hook in `MyComponent`
|
||||
- Bad: The useState hook in MyComponent
|
||||
|
||||
## Markdown Render
|
||||
|
||||
- don't use br tag to wrap in table cell
|
||||
|
||||
## Terminal Command Output
|
||||
|
||||
- If terminal commands don't produce output, it's likely due to paging issues. Try piping the command to `cat` to ensure full output is displayed.
|
||||
- Good: `git show commit_hash -- file.txt | cat`
|
||||
- Good: `git log --oneline | cat`
|
||||
- Reason: Some git commands use pagers by default, which may prevent output from being captured properly
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
description: 包含添加 debug 日志请求时
|
||||
globs:
|
||||
description: 包含添加 console.log 日志请求时
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Debug 包使用指南
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs: src/database/models/**/*
|
||||
alwaysApply: false
|
||||
---
|
||||
1. first read [lobe-chat-backend-architecture.mdc](mdc:.cursor/rules/lobe-chat-backend-architecture.mdc)
|
||||
2. refer to the [_template.ts](mdc:src/database/models/_template.ts) to create new model
|
||||
3. if an operation involves multiple models or complex queries, consider defining it in the `repositories` layer under `src/database/repositories/`
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
description: Explain how group chat works in LobeHub (Multi-agent orchestratoin)
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
This rule explains how group chat (multi-agent orchestration) works. Not confused with session group, which is a organization method to manage session.
|
||||
|
||||
## Key points
|
||||
|
||||
- A supervisor will devide who and how will speak next
|
||||
- Each agent will speak just like in single chat (if was asked to speak)
|
||||
- Not coufused with session group
|
||||
|
||||
## Related Files
|
||||
|
||||
- src/store/chat/slices/message/supervisor.ts
|
||||
- src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts
|
||||
- src/prompts/groupChat/index.ts (All prompts here)
|
||||
|
||||
## Snippets
|
||||
|
||||
```tsx
|
||||
// Detect whether in group chat
|
||||
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
||||
|
||||
// Member actions
|
||||
const addAgentsToGroup = useChatGroupStore((s) => s.addAgentsToGroup);
|
||||
const removeAgentFromGroup = useChatGroupStore((s) => s.removeAgentFromGroup);
|
||||
const persistReorder = useChatGroupStore((s) => s.reorderGroupMembers);
|
||||
|
||||
// Get group info
|
||||
const groupConfig = useChatGroupStore(chatGroupSelectors.currentGroupConfig);
|
||||
const currentGroupMemebers = useSessionStore(sessionSelectors.currentGroupAgents);
|
||||
```
|
||||
+95
-93
@@ -2,117 +2,115 @@
|
||||
globs: *.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
# LobeChat 国际化指南
|
||||
# LobeChat Internationalization Guide
|
||||
|
||||
## 架构概览
|
||||
## Key Points
|
||||
|
||||
LobeChat 使用 react-i18next 进行国际化,采用良好的命名空间架构:
|
||||
- Default language: Chinese (zh-CN) as the source language
|
||||
- Supported languages: 18 languages including English, Japanese, Korean, Arabic, etc.
|
||||
- Framework: react-i18next with Next.js app router
|
||||
- Translation automation: @lobehub/i18n-cli for automatic translation, config file: .i18nrc.js
|
||||
- Never manually modify any json file. You can only modify files in `default` folder
|
||||
|
||||
- 默认语言:中文(zh-CN),作为源语言
|
||||
- 支持语言:18 种语言,包括英语、日语、韩语、阿拉伯语等
|
||||
- 框架:react-i18next 配合 Next.js app router
|
||||
- 翻译自动化:@lobehub/i18n-cli 用于自动翻译,配置文件:.i18nrc.js
|
||||
|
||||
## 目录结构
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
src/locales/
|
||||
├── default/ # 源语言文件(zh-CN)
|
||||
│ ├── index.ts # 命名空间导出
|
||||
│ ├── common.ts # 通用翻译
|
||||
│ ├── chat.ts # 聊天相关翻译
|
||||
│ ├── setting.ts # 设置翻译
|
||||
│ └── ... # 其他命名空间文件
|
||||
└── resources.ts # 类型定义和语言配置
|
||||
├── default/ # Source language files (zh-CN)
|
||||
│ ├── index.ts # Namespace exports
|
||||
│ ├── common.ts # Common translations
|
||||
│ ├── chat.ts # Chat-related translations
|
||||
│ ├── setting.ts # Settings translations
|
||||
│ └── ... # Other namespace files
|
||||
└── resources.ts # Type definitions and language configuration
|
||||
|
||||
locales/ # 翻译文件
|
||||
├── en-US/ # 英语翻译
|
||||
│ ├── common.json # 通用翻译
|
||||
│ ├── chat.json # 聊天翻译
|
||||
│ ├── setting.json # 设置翻译
|
||||
│ └── ... # 其他命名空间 JSON 文件
|
||||
├── ja-JP/ # 日语翻译
|
||||
locales/ # Translation files
|
||||
├── en-US/ # English translations
|
||||
│ ├── common.json # Common translations
|
||||
│ ├── chat.json # Chat translations
|
||||
│ ├── setting.json # Settings translations
|
||||
│ └── ... # Other namespace JSON files
|
||||
├── ja-JP/ # Japanese translations
|
||||
│ ├── common.json
|
||||
│ ├── chat.json
|
||||
│ └── ...
|
||||
└── ... # 其他语言文件夹
|
||||
└── ... # Other language folders
|
||||
```
|
||||
|
||||
## 添加新翻译的工作流程
|
||||
## Workflow for Adding New Translations
|
||||
|
||||
### 1. 添加新的翻译键
|
||||
### 1. Adding New Translation Keys
|
||||
|
||||
第一步:在 src/locales/default 目录下的相应命名空间文件中添加翻译键
|
||||
Step 1: Add translation keys in the corresponding namespace files under src/locales/default directory
|
||||
|
||||
```typescript
|
||||
// 示例:src/locales/default/common.ts
|
||||
// Example: src/locales/default/common.ts
|
||||
export default {
|
||||
// ... 现有键
|
||||
newFeature: {
|
||||
title: "新功能标题",
|
||||
description: "功能描述文案",
|
||||
button: "操作按钮",
|
||||
},
|
||||
// ... existing keys
|
||||
newFeature: {
|
||||
title: '新功能标题',
|
||||
description: '功能描述文案',
|
||||
button: '操作按钮',
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
第二步:如果创建新命名空间,需要在 src/locales/default/index.ts 中导出
|
||||
Step 2: If creating a new namespace, export it in src/locales/default/index.ts
|
||||
|
||||
```typescript
|
||||
import newNamespace from "./newNamespace";
|
||||
import newNamespace from './newNamespace';
|
||||
|
||||
const resources = {
|
||||
// ... 现有命名空间
|
||||
newNamespace,
|
||||
// ... existing namespaces
|
||||
newNamespace,
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 2. 翻译过程
|
||||
### 2. Translation Process
|
||||
|
||||
开发模式:
|
||||
Development mode:
|
||||
|
||||
一般情况下不需要你帮我跑自动翻译工具,跑一次很久,需要的时候我会自己跑。
|
||||
但是为了立马能看到效果,还是需要先翻译 `locales/zh-CN/namespace.json`,不需要翻译其它语言。
|
||||
Generally, you don't need to help me run the automatic translation tool as it takes a long time. I'll run it myself when needed. However, to see immediate results, you still need to translate `locales/zh-CN/namespace.json` first, no need to translate other languages.
|
||||
|
||||
生产模式:
|
||||
Production mode:
|
||||
|
||||
```bash
|
||||
# 为所有语言生成翻译
|
||||
# Generate translations for all languages
|
||||
npm run i18n
|
||||
```
|
||||
|
||||
## 在组件中使用
|
||||
## Usage in Components
|
||||
|
||||
### 基本用法
|
||||
### Basic Usage
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const MyComponent = () => {
|
||||
const { t } = useTranslation("common");
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t("newFeature.title")}</h1>
|
||||
<p>{t("newFeature.description")}</p>
|
||||
<button>{t("newFeature.button")}</button>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<h1>{t('newFeature.title')}</h1>
|
||||
<p>{t('newFeature.description')}</p>
|
||||
<button>{t('newFeature.button')}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 带参数的用法
|
||||
### Usage with Parameters
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation("common");
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
<p>{t("welcome.message", { name: "John" })}</p>;
|
||||
<p>{t('welcome.message', { name: 'John' })}</p>;
|
||||
|
||||
// 对应的语言文件:
|
||||
// welcome: { message: '欢迎 {{name}} 使用!' }
|
||||
// Corresponding language file:
|
||||
// welcome: { message: 'Welcome {{name}}!' }
|
||||
```
|
||||
|
||||
### 多个命名空间
|
||||
### Multiple Namespaces
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation(['common', 'chat']);
|
||||
@@ -121,59 +119,63 @@ const { t } = useTranslation(['common', 'chat']);
|
||||
<span>{t('chat:typing')}</span>
|
||||
```
|
||||
|
||||
## 类型安全
|
||||
## Type Safety
|
||||
|
||||
项目使用 TypeScript 实现类型安全的翻译,类型从 src/locales/resources.ts 自动生成:
|
||||
The project uses TypeScript to implement type-safe translations, with types automatically generated from src/locales/resources.ts:
|
||||
|
||||
```typescript
|
||||
import type { DefaultResources, NS, Locales } from "@/locales/resources";
|
||||
import type { DefaultResources, Locales, NS } from '@/locales/resources';
|
||||
|
||||
// 可用类型:
|
||||
// - NS: 可用命名空间键 ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: 支持的语言代码 ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
// Available types:
|
||||
// - NS: Available namespace keys ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: Supported language codes ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
|
||||
const namespace: NS = "common";
|
||||
const locale: Locales = "en-US";
|
||||
const namespace: NS = 'common';
|
||||
const locale: Locales = 'en-US';
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
## Best Practices
|
||||
|
||||
### 1. 命名空间组织
|
||||
### 1. Namespace Organization
|
||||
|
||||
- common: 共享 UI 元素(按钮、标签、操作)
|
||||
- chat: 聊天特定功能
|
||||
- setting: 配置和设置
|
||||
- error: 错误消息和处理
|
||||
- [feature]: 功能特定或页面特定的命名空间
|
||||
- components: 可复用组件文案
|
||||
- common: Shared UI elements (buttons, labels, actions)
|
||||
- chat: Chat-specific functionality
|
||||
- setting: Configuration and settings
|
||||
- error: Error messages and handling
|
||||
- [feature]: Feature-specific or page-specific namespaces
|
||||
- components: Reusable component text
|
||||
|
||||
### 2. 键命名约定
|
||||
### 2. Key Naming Conventions
|
||||
|
||||
```typescript
|
||||
// ✅ 好:层次结构
|
||||
// ✅ Good: Hierarchical structure
|
||||
export default {
|
||||
modal: {
|
||||
confirm: {
|
||||
title: "确认操作",
|
||||
message: "确定要执行此操作吗?",
|
||||
actions: {
|
||||
confirm: "确认",
|
||||
cancel: "取消",
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
confirm: {
|
||||
title: '确认操作',
|
||||
message: '确定要执行此操作吗?',
|
||||
actions: {
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ❌ 避免:扁平结构
|
||||
// ❌ Avoid: Flat structure
|
||||
export default {
|
||||
modalConfirmTitle: "确认操作",
|
||||
modalConfirmMessage: "确定要执行此操作吗?",
|
||||
modalConfirmTitle: '确认操作',
|
||||
modalConfirmMessage: '确定要执行此操作吗?',
|
||||
};
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
## Troubleshooting
|
||||
|
||||
### 缺少翻译键
|
||||
### Missing Translation Keys
|
||||
|
||||
- Check if the key exists in src/locales/default/namespace.ts
|
||||
- Ensure the namespace is correctly imported in the component
|
||||
- Ensure new namespaces are exported in src/locales/default/index.ts
|
||||
|
||||
- 检查键是否存在于 src/locales/default/namespace.ts 中
|
||||
- 确保在组件中正确导入命名空间
|
||||
|
||||
@@ -4,41 +4,35 @@ alwaysApply: true
|
||||
|
||||
## Project Description
|
||||
|
||||
You are developing an open-source, modern-design AI chat framework: lobe chat.
|
||||
You are developing an open-source, modern-design AI chat framework: lobehub(previous lobe-chat).
|
||||
|
||||
Emoji logo: 🤯
|
||||
support platforms:
|
||||
|
||||
- web desktop/mobile
|
||||
- desktop(electron)
|
||||
- mobile app(react native). coming soon
|
||||
|
||||
logo emoji: 🤯
|
||||
|
||||
## Project Technologies Stack
|
||||
|
||||
read [package.json](mdc:package.json) to know all npm packages you can use.
|
||||
|
||||
The project uses the following technologies:
|
||||
|
||||
- pnpm as package manager
|
||||
- Next.js 15 for frontend and backend, using app router instead of pages router
|
||||
- react 19, using hooks, functional components, react server components
|
||||
- TypeScript programming language
|
||||
- antd, @lobehub/ui for component framework
|
||||
- Next.js 15
|
||||
- react 19
|
||||
- TypeScript
|
||||
- `@lobehub/ui`, antd for component framework
|
||||
- antd-style for css-in-js framework
|
||||
- react-layout-kit for flex layout
|
||||
- lucide-react, `@ant-design/icons` for icons
|
||||
- react-layout-kit for flex layout component
|
||||
- react-i18next for i18n
|
||||
- lucide-react, @ant-design/icons for icons
|
||||
- @lobehub/icons for AI provider/model logo icon
|
||||
- @formkit/auto-animate for react list animation
|
||||
- zustand for global state management
|
||||
- nuqs for type-safe search params state manager
|
||||
- SWR for react data fetch
|
||||
- zustand for state management
|
||||
- nuqs for search params management
|
||||
- SWR for data fetch
|
||||
- aHooks for react hooks library
|
||||
- dayjs for date and time library
|
||||
- dayjs for time library
|
||||
- lodash-es for utility library
|
||||
- fast-deep-equal for deep comparison of JavaScript objects
|
||||
- zod for data validation
|
||||
- TRPC for type safe backend
|
||||
- PGLite for client DB and PostgreSQL for backend DB
|
||||
- Drizzle ORM
|
||||
- Vitest for testing, testing-library for react component test
|
||||
- Prettier for code formatting
|
||||
- ESLint for code linting
|
||||
- Cursor AI for code editing and AI coding assistance
|
||||
|
||||
Note: All tools and libraries used are the latest versions. The application only needs to be compatible with the latest browsers;
|
||||
- Vitest for testing
|
||||
|
||||
+102
-221
@@ -5,235 +5,116 @@ alwaysApply: false
|
||||
|
||||
# LobeChat Project Structure
|
||||
|
||||
## Directory Structure
|
||||
note: some not very important files are not shown for simplicity.
|
||||
|
||||
note: some files are not shown for simplicity.
|
||||
## Complete Project Structure
|
||||
|
||||
this project use common monorepo structure. The workspace packages name use `@lobechat/` namespace.
|
||||
|
||||
```plaintext
|
||||
lobe-chat/
|
||||
├── apps/ # Applications directory
|
||||
│ └── desktop/ # Electron desktop application
|
||||
│ ├── src/ # Desktop app source code
|
||||
│ └── resources/ # Desktop app resources
|
||||
├── docs/ # Project documentation
|
||||
│ ├── development/ # Development docs
|
||||
│ ├── self-hosting/ # Self-hosting docs
|
||||
│ └── usage/ # Usage guides
|
||||
├── locales/ # Internationalization files (multiple locales)
|
||||
│ ├── en-US/ # English (example)
|
||||
│ └── zh-CN/ # Simplified Chinese (example)
|
||||
├── packages/ # Monorepo packages directory
|
||||
│ ├── const/ # Constants definition package
|
||||
│ ├── database/ # Database related package
|
||||
│ ├── electron-client-ipc/ # Electron renderer ↔ main IPC client
|
||||
│ ├── electron-server-ipc/ # Electron main process IPC server
|
||||
│ ├── model-bank/ # Built-in model presets/catalog exports
|
||||
│ ├── model-runtime/ # AI model runtime package
|
||||
│ ├── types/ # TypeScript type definitions
|
||||
│ ├── utils/ # Utility functions package
|
||||
│ ├── file-loaders/ # File processing packages
|
||||
│ ├── prompts/ # AI prompt management
|
||||
│ └── web-crawler/ # Web crawling functionality
|
||||
├── public/ # Static assets
|
||||
│ ├── icons/ # Application icons
|
||||
│ ├── images/ # Image resources
|
||||
│ └── screenshots/ # Application screenshots
|
||||
├── scripts/ # Build and tool scripts
|
||||
├── src/ # Main application source code (see below)
|
||||
├── .cursor/ # Cursor AI configuration
|
||||
├── docker-compose/ # Docker configuration
|
||||
├── package.json # Project dependencies
|
||||
├── pnpm-workspace.yaml # pnpm monorepo configuration
|
||||
├── next.config.ts # Next.js configuration
|
||||
├── drizzle.config.ts # Drizzle ORM configuration
|
||||
└── tsconfig.json # TypeScript configuration
|
||||
```
|
||||
|
||||
## Core Source Directory (`src/`)
|
||||
|
||||
```plaintext
|
||||
src/
|
||||
├── app/ # Next.js App Router routes
|
||||
│ ├── (backend)/ # Backend API routes
|
||||
│ │ ├── api/ # REST API endpoints
|
||||
│ │ │ ├── auth/ # Authentication endpoints
|
||||
│ │ │ └── webhooks/ # Webhook handlers for various auth providers
|
||||
│ │ ├── middleware/ # Request middleware
|
||||
│ │ ├── oidc/ # OpenID Connect endpoints
|
||||
│ │ ├── trpc/ # tRPC API routes
|
||||
│ │ │ ├── async/ # Async tRPC endpoints
|
||||
│ │ │ ├── desktop/ # Desktop runtime endpoints
|
||||
│ │ │ ├── edge/ # Edge runtime endpoints
|
||||
│ │ │ ├── lambda/ # Lambda runtime endpoints
|
||||
│ │ │ └── tools/ # Tools-specific endpoints
|
||||
│ │ └── webapi/ # Web API endpoints
|
||||
│ │ ├── chat/ # Chat-related APIs for various providers
|
||||
│ │ ├── models/ # Model management APIs
|
||||
│ │ ├── plugin/ # Plugin system APIs
|
||||
│ │ ├── stt/ # Speech-to-text APIs
|
||||
│ │ ├── text-to-image/ # Image generation APIs
|
||||
│ │ └── tts/ # Text-to-speech APIs
|
||||
│ ├── [variants]/ # Page route variants
|
||||
│ │ ├── (main)/ # Main application routes
|
||||
│ │ │ ├── chat/ # Chat interface and workspace
|
||||
│ │ │ ├── discover/ # Discover page (assistants, models, providers)
|
||||
│ │ │ ├── files/ # File management interface
|
||||
│ │ │ ├── image/ # Image generation interface
|
||||
│ │ │ ├── profile/ # User profile and stats
|
||||
│ │ │ ├── repos/ # Knowledge base repositories
|
||||
│ │ │ └── settings/ # Application settings
|
||||
│ │ └── @modal/ # Modal routes
|
||||
│ └── manifest.ts # PWA configuration
|
||||
├── components/ # Global shared components
|
||||
│ ├── Analytics/ # Analytics tracking components
|
||||
│ ├── Error/ # Error handling components
|
||||
│ └── Loading/ # Loading state components
|
||||
├── config/ # Application configuration
|
||||
│ ├── featureFlags/ # Feature flags & experiments
|
||||
│ └── modelProviders/ # Model provider configurations
|
||||
├── features/ # Feature components (UI Layer)
|
||||
│ ├── AgentSetting/ # Agent configuration and management
|
||||
│ ├── ChatInput/ # Chat input with file upload and tools
|
||||
│ ├── Conversation/ # Message display and interaction
|
||||
│ ├── FileManager/ # File upload and knowledge base
|
||||
│ └── PluginStore/ # Plugin marketplace and management
|
||||
├── hooks/ # Custom React hooks
|
||||
├── layout/ # Global layout components
|
||||
│ ├── AuthProvider/ # Authentication provider
|
||||
│ └── GlobalProvider/ # Global state provider
|
||||
├── libs/ # External library integrations
|
||||
│ ├── analytics/ # Analytics services integration
|
||||
│ ├── next-auth/ # NextAuth.js configuration
|
||||
│ └── oidc-provider/ # OIDC provider implementation
|
||||
├── locales/ # Internationalization resources
|
||||
│ └── default/ # Default language definitions
|
||||
├── migrations/ # Client-side data migrations
|
||||
├── server/ # Server-side code
|
||||
│ ├── modules/ # Server modules
|
||||
│ ├── routers/ # tRPC routers
|
||||
│ └── services/ # Server services
|
||||
├── services/ # Service layer (per-domain, client/server split)
|
||||
│ ├── user/ # User services
|
||||
│ │ ├── client.ts # Client DB (PGLite) implementation
|
||||
│ │ └── server.ts # Server DB implementation (via tRPC)
|
||||
│ ├── aiModel/ # AI model services
|
||||
│ ├── session/ # Session services
|
||||
│ └── message/ # Message services
|
||||
├── store/ # Zustand state management
|
||||
│ ├── agent/ # Agent state
|
||||
│ ├── chat/ # Chat state
|
||||
│ └── user/ # User state
|
||||
├── styles/ # Global styles
|
||||
├── tools/ # Built-in tool system
|
||||
│ ├── artifacts/ # Code artifacts and preview
|
||||
│ └── web-browsing/ # Web search and browsing
|
||||
├── types/ # TypeScript type definitions
|
||||
└── utils/ # Utility functions
|
||||
├── client/ # Client-side utilities
|
||||
└── server/ # Server-side utilities
|
||||
```
|
||||
|
||||
## Key Monorepo Packages
|
||||
|
||||
```plaintext
|
||||
packages/
|
||||
├── const/ # Global constants and configurations
|
||||
├── database/ # Database schemas and models
|
||||
│ ├── src/models/ # Data models (CRUD operations)
|
||||
│ ├── src/schemas/ # Drizzle database schemas
|
||||
│ ├── src/repositories/ # Complex query layer
|
||||
│ └── migrations/ # Database migration files
|
||||
├── model-runtime/ # AI model runtime
|
||||
│ └── src/
|
||||
│ ├── openai/ # OpenAI provider integration
|
||||
│ ├── anthropic/ # Anthropic provider integration
|
||||
│ ├── google/ # Google AI provider integration
|
||||
│ ├── ollama/ # Ollama local model integration
|
||||
│ ├── types/ # Runtime type definitions
|
||||
│ └── utils/ # Runtime utilities
|
||||
├── types/ # Shared TypeScript type definitions
|
||||
│ └── src/
|
||||
│ ├── agent/ # Agent-related types
|
||||
│ ├── message/ # Message and chat types
|
||||
│ ├── user/ # User and session types
|
||||
│ └── tool/ # Tool and plugin types
|
||||
├── utils/ # Shared utility functions
|
||||
│ └── src/
|
||||
│ ├── client/ # Client-side utilities
|
||||
│ ├── server/ # Server-side utilities
|
||||
│ ├── fetch/ # HTTP request utilities
|
||||
│ └── tokenizer/ # Token counting utilities
|
||||
├── file-loaders/ # File loaders (PDF, DOCX, etc.)
|
||||
├── prompts/ # AI prompt management
|
||||
└── web-crawler/ # Web crawling functionality
|
||||
├── apps/
|
||||
│ └── desktop/
|
||||
├── docs/
|
||||
├── locales/
|
||||
│ ├── en-US/
|
||||
│ └── zh-CN/
|
||||
├── packages/
|
||||
│ ├── const/
|
||||
│ ├── context-engine/
|
||||
│ ├── database/
|
||||
│ │ ├── src/
|
||||
│ │ │ ├── models/
|
||||
│ │ │ ├── schemas/
|
||||
│ │ │ └── repositories/
|
||||
│ ├── model-bank/
|
||||
│ ├── model-runtime/
|
||||
│ │ └── src/
|
||||
│ │ ├── openai/
|
||||
│ │ └── anthropic/
|
||||
│ ├── types/
|
||||
│ │ └── src/
|
||||
│ │ ├── message/
|
||||
│ │ └── user/
|
||||
│ └── utils/
|
||||
├── public/
|
||||
├── scripts/
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── (backend)/
|
||||
│ │ │ ├── api/
|
||||
│ │ │ │ ├── auth/
|
||||
│ │ │ │ └── webhooks/
|
||||
│ │ │ ├── middleware/
|
||||
│ │ │ ├── oidc/
|
||||
│ │ │ ├── trpc/
|
||||
│ │ │ └── webapi/
|
||||
│ │ │ ├── chat/
|
||||
│ │ │ └── tts/
|
||||
│ │ ├── [variants]/
|
||||
│ │ │ ├── (main)/
|
||||
│ │ │ │ ├── chat/
|
||||
│ │ │ │ └── settings/
|
||||
│ │ │ └── @modal/
|
||||
│ │ └── manifest.ts
|
||||
│ ├── components/
|
||||
│ ├── config/
|
||||
│ ├── features/
|
||||
│ │ └── ChatInput/
|
||||
│ ├── hooks/
|
||||
│ ├── layout/
|
||||
│ │ ├── AuthProvider/
|
||||
│ │ └── GlobalProvider/
|
||||
│ ├── libs/
|
||||
│ │ └── oidc-provider/
|
||||
│ ├── locales/
|
||||
│ │ └── default/
|
||||
│ ├── server/
|
||||
│ │ ├── modules/
|
||||
│ │ ├── routers/
|
||||
│ │ │ ├── async/
|
||||
│ │ │ ├── desktop/
|
||||
│ │ │ ├── edge/
|
||||
│ │ │ └── lambda/
|
||||
│ │ └── services/
|
||||
│ ├── services/
|
||||
│ │ ├── user/
|
||||
│ │ │ ├── client.ts
|
||||
│ │ │ └── server.ts
|
||||
│ │ └── message/
|
||||
│ ├── store/
|
||||
│ │ ├── agent/
|
||||
│ │ ├── chat/
|
||||
│ │ └── user/
|
||||
│ ├── styles/
|
||||
│ └── utils/
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Architecture Map
|
||||
|
||||
- Presentation: `src/features`, `src/components`, `src/layout` — UI composition, global providers
|
||||
- State: `src/store` — Zustand slices, selectors, middleware
|
||||
- Client Services: `src/services/<domain>/{client|server}.ts` — client: PGLite; server: tRPC bridge
|
||||
- API Routers: `src/app/(backend)/webapi` (REST), `src/app/(backend)/trpc/{edge|lambda|async|desktop|tools}`; Lambda router triggers Async router for long-running tasks (e.g., image)
|
||||
- Server Services: `src/server/services` (business logic), `src/server/modules` (infra adapters)
|
||||
- Data Access: `packages/database/src/{schemas,models,repositories}` — Schema (Drizzle), Model (CRUD), Repository (complex queries)
|
||||
- Integrations: `src/libs` — analytics, auth, trpc, logging, runtime helpers
|
||||
- UI Components: `src/components`, `src/features`
|
||||
- Global providers: `src/layout`
|
||||
- Zustand stores: `src/store`
|
||||
- Client Services: `src/services/`
|
||||
- clientDB: `src/services/<domain>/client.ts`
|
||||
- serverDB: `src/services/<domain>/server.ts`
|
||||
- API Routers:
|
||||
- `src/app/(backend)/webapi` (REST)
|
||||
- `src/server/routers/{edge|lambda|async|desktop|tools}` (tRPC)
|
||||
- Server:
|
||||
- Services(can access serverDB): `src/server/services`
|
||||
- Modules(can't access db): `src/server/modules` (Server only Third-party Service Module)
|
||||
- Database:
|
||||
- Schema (Drizzle): `packages/database/src/schemas`
|
||||
- Model (CRUD): `packages/database/src/models`
|
||||
- Repository (bff-queries): `packages/database/src/repositories`
|
||||
- Third-party Integrations: `src/libs` — analytics, oidc etc.
|
||||
|
||||
## Data Flow Architecture
|
||||
|
||||
### Unified Flow Pattern
|
||||
|
||||
```
|
||||
UI Layer → State Management → Client Service → [Environment Branch] → Database
|
||||
↓ ↓ ↓ ↓ ↓
|
||||
React Zustand Environment Local/Remote PGLite/
|
||||
Components Store Adaptation Routing PostgreSQL
|
||||
```
|
||||
|
||||
### Environment-Specific Routing
|
||||
|
||||
| Mode | UI | Service Route | Database |
|
||||
| --------------- | -------- | ---------------------- | ------------------- |
|
||||
| **Browser/PWA** | React | Direct Model Access | PGLite (Local) |
|
||||
| **Server** | React | tRPC → Server Services | PostgreSQL (Remote) |
|
||||
| **Desktop** | Electron | tRPC → Local Node.js | PGLite/PostgreSQL\* |
|
||||
|
||||
_\*Depends on cloud sync configuration_
|
||||
|
||||
### Key Characteristics
|
||||
|
||||
- **Type Safety**: End-to-end type safety via tRPC and Drizzle ORM
|
||||
- **Local/Remote Dual Mode**: PGLite enables user data ownership and local control
|
||||
|
||||
## Quick Map
|
||||
|
||||
- App Routes: `src/app` — UI routes (App Router) and backend routes under `(backend)`
|
||||
- Web API: `src/app/(backend)/webapi` — REST-like endpoints
|
||||
- tRPC Routers: `src/server/routers` — typed RPC endpoints by runtime
|
||||
- Client Services: `src/services` — environment-adaptive client-side business logic
|
||||
- Server Services: `src/server/services` — platform-agnostic business logic
|
||||
- Database: `packages/database` — schemas/models/repositories/migrations
|
||||
- State: `src/store` — Zustand stores and slices
|
||||
- Integrations: `src/libs` — analytics/auth/trpc/logging/runtime helpers
|
||||
- Tools: `src/tools` — built-in tool system
|
||||
|
||||
## Common Tasks
|
||||
|
||||
- Add Web API route: `src/app/(backend)/webapi/<module>/route.ts`
|
||||
- Add tRPC endpoint: `src/server/routers/{edge|lambda|desktop}/...`
|
||||
- Add client/server service: `src/services/<domain>/{client|server}.ts` (client: PGLite; server: tRPC)
|
||||
- Add server service: `src/server/services/<domain>`
|
||||
- Add a new model/provider: `src/config/modelProviders/<provider>.ts` + `packages/model-bank/src/aiModels/<provider>.ts` + `packages/model-runtime/src/<provider>/index.ts`
|
||||
- Add DB schema/model/repository: `packages/database/src/{schemas|models|repositories}`
|
||||
- Add Zustand slice: `src/store/<domain>/slices`
|
||||
|
||||
## Env Modes
|
||||
|
||||
- `NEXT_PUBLIC_CLIENT_DB`: selects client DB mode (e.g., `pglite`) vs server-backed
|
||||
- `NEXT_PUBLIC_IS_DESKTOP_APP`: enables desktop-specific routes and behavior
|
||||
- `NEXT_PUBLIC_SERVICE_MODE`: controls service routing preference (client/server)
|
||||
|
||||
## Boundaries
|
||||
|
||||
- Keep client logic in `src/services`; server-only logic stays in `src/server/services`
|
||||
- Don’t mix Web API (`webapi/`) with tRPC (`src/server/routers/`)
|
||||
- Place business UI under `src/features`, global reusable UI under `src/components`
|
||||
- **Browser/PWA**: React UI → Client Service → Direct Model Access → PGLite (Web WASM)
|
||||
- **Server**: React UI → Client Service → tRPC Lambda → Server Services → PostgreSQL (Remote)
|
||||
- **Desktop**:
|
||||
- Cloud sync disabled: Electron UI → Client Service → tRPC Lambda → Local Server Services → PGLite (Node WASM)
|
||||
- Cloud sync enabled: Electron UI → Client Service → tRPC Lambda → Cloud Server Services → PostgreSQL (Remote)
|
||||
|
||||
@@ -9,6 +9,7 @@ alwaysApply: false
|
||||
- 如果要写复杂样式的话用 antd-style ,简单的话可以用 style 属性直接写内联样式
|
||||
- 如果需要 flex 布局或者居中布局应该使用 react-layout-kit 的 Flexbox 和 Center 组件
|
||||
- 选择组件时优先顺序应该是 src/components > 安装的组件 package > lobe-ui > antd
|
||||
- 使用 selector 访问 zustand store 的数据,而不是直接从 store 获取
|
||||
|
||||
## antd-style token system
|
||||
|
||||
@@ -85,9 +86,9 @@ const Card: FC<CardProps> = ({ title, content }) => {
|
||||
|
||||
## Lobe UI 包含的组件
|
||||
|
||||
- 不知道 @lobehub/ui 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜,大部分组件都是在 antd 的基础上扩展了属性
|
||||
- 不知道 `@lobehub/ui` 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜,大部分组件都是在 antd 的基础上扩展了属性
|
||||
- 具体用法不懂可以联网搜索,例如 ActionIcon 就爬取 https://ui.lobehub.com/components/action-icon
|
||||
- 可以阅读 node_modules/@lobehub/ui/es/index.js 了解有哪些组件,每个组件的属性是什么
|
||||
- 可以阅读 `node_modules/@lobehub/ui/es/index.js` 了解有哪些组件,每个组件的属性是什么
|
||||
|
||||
- General
|
||||
- ActionIcon
|
||||
|
||||
@@ -4,20 +4,12 @@ globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
# 📋 Available Rules Index
|
||||
# Available project rules index
|
||||
|
||||
The following rules are available via `read_file` from the `.cursor/rules/` directory:
|
||||
|
||||
## General
|
||||
|
||||
- `project-introduce.mdc` – Project description and tech stack
|
||||
- `cursor-rules.mdc` – Cursor rules authoring and optimization guide
|
||||
- `code-review.mdc` – How to code review
|
||||
All following rules are saved under `.cursor/rules/` directory:
|
||||
|
||||
## Backend
|
||||
|
||||
- `backend-architecture.mdc` – Backend layer architecture and design guidelines
|
||||
- `define-database-model.mdc` – Database model definition guidelines
|
||||
- `drizzle-schema-style-guide.mdc` – Style guide for defining Drizzle ORM schemas
|
||||
|
||||
## Frontend
|
||||
@@ -42,7 +34,6 @@ The following rules are available via `read_file` from the `.cursor/rules/` dire
|
||||
|
||||
## Debugging
|
||||
|
||||
- `debug.mdc` – General debugging guide
|
||||
- `debug-usage.mdc` – Using the debug package and namespace conventions
|
||||
|
||||
## Testing
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
|
||||
## System Role
|
||||
|
||||
You are an expert in full-stack Web development, proficient in JavaScript, TypeScript, CSS, React, Node.js, Next.js, Postgresql, Redis, S3, all kinds of network protocols.
|
||||
|
||||
You are an LLM expert, you are familiar with all kinds of LLM models, ai agents, ai workflow, prompt engineering and context engineering.
|
||||
|
||||
You are an expert in Ai art. In Ai image generation, you are proficient in Stable Diffusion and ComfyUI's architectural principles, workflows, model structures, parameter configurations, training methods, and inference optimization.
|
||||
|
||||
You are an expert in UI/UX design, proficient in web interaction patterns, responsive design, accessibility, and user behavior optimization. You excel at improving user retention and paid conversion rates through various interaction details.
|
||||
|
||||
## Problem Solving
|
||||
|
||||
- When modifying existing code, clearly describe the differences and reasons for the changes
|
||||
- Provide alternative solutions that may be better overall or superior in specific aspects
|
||||
- Provide optimization suggestions for deprecated API usage
|
||||
- Cite sources whenever possible at the end, not inline
|
||||
- When you provide multiple solutions, provide the recommended solution first, and note it as `Recommended`
|
||||
- Express uncertainty when there might not be a correct answer, instead of take action by guessing and assuming
|
||||
|
||||
## Code Implementation
|
||||
|
||||
- Focus on maintainable over being performant
|
||||
- Be sure to reference file path
|
||||
- If doc links or required files are missing, ask for them before proceeding with the task rather than making assumptions
|
||||
- If you're unable to get valid result when using tools, please clearly state in the output
|
||||
@@ -8,11 +8,13 @@ alwaysApply: false
|
||||
|
||||
## Types and Type Safety
|
||||
|
||||
- Avoid explicit type annotations when TypeScript can infer types.
|
||||
- Avoid implicitly `any` variables; explicitly type when necessary (e.g., `let a: number` instead of `let a`).
|
||||
- Use the most accurate type possible (e.g., prefer `Record<PropertyKey, unknown>` over `object`).
|
||||
- Prefer `interface` over `type` for object shapes (e.g., React component props). Keep `type` for unions, intersections, and utility types.
|
||||
- Prefer `as const satisfies XyzInterface` over plain `as const` when suitable.
|
||||
- avoid explicit type annotations when TypeScript can infer types.
|
||||
- avoid implicitly `any` variables; explicitly type when necessary (e.g., `let a: number` instead of `let a`).
|
||||
- use the most accurate type possible (e.g., prefer `Record<PropertyKey, unknown>` over `object` and `any`).
|
||||
- prefer `interface` over `type` for object shapes (e.g., React component props). Keep `type` for unions, intersections, and utility types.
|
||||
- prefer `as const satisfies XyzInterface` over plain `as const` when suitable.
|
||||
- prefer `@ts-expect-error` over `@ts-ignore` over `as any`
|
||||
- Avoid meaningless null/undefined parameters; design strict function contracts.
|
||||
|
||||
## Imports and Modules
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node",
|
||||
"features": {
|
||||
"ghcr.io/devcontainer-community/devcontainer-features/bun.sh:1": {}
|
||||
}
|
||||
"ghcr.io/devcontainer-community/devcontainer-features/bun.sh:1": {},
|
||||
"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {}
|
||||
},
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node"
|
||||
}
|
||||
|
||||
@@ -173,6 +173,16 @@ OPENAI_API_KEY=sk-xxxxxxxxx
|
||||
|
||||
# NEBIUS_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
### NewAPI Service ###
|
||||
|
||||
# NEWAPI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# NEWAPI_PROXY_URL=https://your-newapi-server.com
|
||||
|
||||
### Vercel AI Gateway ###
|
||||
|
||||
# VERCELAIGATEWAY_API_KEY=your_vercel_ai_gateway_api_key
|
||||
|
||||
|
||||
########################################
|
||||
############ Market Service ############
|
||||
########################################
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
name: '🐛 反馈缺陷'
|
||||
description: '反馈一个问题缺陷'
|
||||
labels: ['unconfirm']
|
||||
type: Bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
在创建新的 Issue 之前,请先[搜索已有问题](https://github.com/lobehub/lobe-chat/issues),如果发现已有类似的问题,请给它 **👍 点赞**,这样可以帮助我们更快地解决问题。
|
||||
如果你在使用过程中遇到问题,可以尝试以下方式获取帮助:
|
||||
- 在 [GitHub Discussions](https://github.com/lobehub/lobe-chat/discussions) 的版块发起讨论。
|
||||
- 在 [LobeChat 社区](https://discord.gg/AYFPHvv2jT) 提问,与其他用户交流。
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: '📦 部署环境'
|
||||
multiple: true
|
||||
options:
|
||||
- 'Official Preview'
|
||||
- 'Official Cloud'
|
||||
- 'Vercel'
|
||||
- 'Zeabur'
|
||||
- 'Sealos'
|
||||
- 'Netlify'
|
||||
- 'Docker'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: '📦 部署模式'
|
||||
multiple: true
|
||||
options:
|
||||
- '客户端模式(lobe-chat 镜像)'
|
||||
- '客户端 Pglite 模式(lobe-chat-pglite 镜像)'
|
||||
- '服务端模式(lobe-chat-database 镜像)'
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: '📌 软件版本'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: '💻 系统环境'
|
||||
multiple: true
|
||||
options:
|
||||
- 'Windows'
|
||||
- 'macOS'
|
||||
- 'Ubuntu'
|
||||
- 'Other Linux'
|
||||
- 'iOS'
|
||||
- 'Android'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: '🌐 浏览器'
|
||||
multiple: true
|
||||
options:
|
||||
- 'Chrome'
|
||||
- 'Edge'
|
||||
- 'Safari'
|
||||
- 'Firefox'
|
||||
- 'Other'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🐛 问题描述'
|
||||
description: 请提供一个清晰且简洁的问题描述,若上述选项为`Other`,也请详细说明。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '📷 复现步骤'
|
||||
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🚦 期望结果'
|
||||
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '📝 补充信息'
|
||||
description: 如果您的问题需要进一步说明,或者您遇到的问题无法在一个简单的示例中复现,请在这里添加更多信息。
|
||||
@@ -1,21 +0,0 @@
|
||||
name: '🌠 功能需求'
|
||||
description: '提出需求或建议'
|
||||
title: '[Request] '
|
||||
type: Feature
|
||||
body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🥰 需求描述'
|
||||
description: 请添加一个清晰且简洁的问题描述,阐述您希望通过这个功能需求解决的问题。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '🧐 解决方案'
|
||||
description: 请清晰且简洁地描述您想要的解决方案。
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: '📝 补充信息'
|
||||
description: 在这里添加关于问题的任何其他背景信息。
|
||||
@@ -1,7 +1,7 @@
|
||||
contact_links:
|
||||
- name: Ask a question for self-hosting | 咨询自部署问题
|
||||
- name: Ask a question for self-hosting
|
||||
url: https://github.com/lobehub/lobe-chat/discussions/new?category=self-hosting-%E7%A7%81%E6%9C%89%E5%8C%96%E9%83%A8%E7%BD%B2
|
||||
about: Please post questions, and ideas in discussions. | 请在讨论区发布问题和想法。
|
||||
- name: Questions and ideas | 其他问题和想法
|
||||
about: Please post questions, and ideas in discussions.
|
||||
- name: Questions and ideas
|
||||
url: https://github.com/lobehub/lobe-chat/discussions/new/choose
|
||||
about: Please post questions, and ideas in discussions. | 请在讨论区发布问题和想法。
|
||||
about: Please post questions, and ideas in discussions.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#### 💻 变更类型 | Change Type
|
||||
#### 💻 Change Type
|
||||
|
||||
<!-- For change type, change [ ] to [x]. -->
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
- [ ] 📝 docs
|
||||
- [ ] 🔨 chore
|
||||
|
||||
#### 🔀 变更说明 | Description of Change
|
||||
#### 🔀 Description of Change
|
||||
|
||||
<!-- Thank you for your Pull Request. Please provide a description above. -->
|
||||
|
||||
#### 📝 补充信息 | Additional Information
|
||||
#### 📝 Additional Information
|
||||
|
||||
<!-- Add any other context about the Pull Request here. -->
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// @ts-check
|
||||
/**
|
||||
* Lock closed issues after 7 days of inactivity
|
||||
* @param {object} github - GitHub API client
|
||||
* @param {object} context - GitHub Actions context
|
||||
*/
|
||||
module.exports = async ({ github, context }) => {
|
||||
const sevenDaysAgo = new Date();
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
|
||||
|
||||
const lockComment = `This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.`;
|
||||
|
||||
let page = 1;
|
||||
let hasMore = true;
|
||||
let totalLocked = 0;
|
||||
|
||||
while (hasMore) {
|
||||
// Get closed issues (pagination)
|
||||
const { data: issues } = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'closed',
|
||||
sort: 'updated',
|
||||
direction: 'asc',
|
||||
per_page: 100,
|
||||
page: page,
|
||||
});
|
||||
|
||||
if (issues.length === 0) {
|
||||
hasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
for (const issue of issues) {
|
||||
// Skip if already locked
|
||||
if (issue.locked) continue;
|
||||
|
||||
// Skip pull requests
|
||||
if (issue.pull_request) continue;
|
||||
|
||||
// Check if updated more than 7 days ago
|
||||
const updatedAt = new Date(issue.updated_at);
|
||||
if (updatedAt > sevenDaysAgo) {
|
||||
// Since issues are sorted by updated_at ascending,
|
||||
// once we hit a recent issue, all remaining will be recent too
|
||||
hasMore = false;
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
// Add comment before locking
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: lockComment,
|
||||
});
|
||||
|
||||
// Lock the issue
|
||||
await github.rest.issues.lock({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
lock_reason: 'resolved',
|
||||
});
|
||||
|
||||
totalLocked++;
|
||||
console.log(`Locked issue #${issue.number}: ${issue.title}`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to lock issue #${issue.number}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
page++;
|
||||
}
|
||||
|
||||
console.log(`Total issues locked: ${totalLocked}`);
|
||||
};
|
||||
@@ -36,10 +36,19 @@ module.exports = async ({ github, context, releaseUrl, version, tag }) => {
|
||||
// Generate combined download table
|
||||
let assetTable = '| Platform | File | Size |\n| --- | --- | --- |\n';
|
||||
|
||||
// Add macOS assets
|
||||
// Add macOS assets with architecture detection
|
||||
macAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| macOS | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
|
||||
// Detect architecture from filename
|
||||
let architecture = '';
|
||||
if (asset.name.includes('arm64')) {
|
||||
architecture = ' (Apple Silicon)';
|
||||
} else if (asset.name.includes('x64') || asset.name.includes('-mac.')) {
|
||||
architecture = ' (Intel)';
|
||||
}
|
||||
|
||||
assetTable += `| macOS${architecture} | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
|
||||
// Add Windows assets
|
||||
|
||||
@@ -26,7 +26,7 @@ jobs:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
name: Claude Translator
|
||||
concurrency:
|
||||
group: translator-${{ github.event.comment.id || github.event.issue.number || github.event.review.id }}
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
pull_request_review:
|
||||
types: [submitted, edited]
|
||||
pull_request_review_comment:
|
||||
types: [created, edited]
|
||||
|
||||
jobs:
|
||||
translate:
|
||||
if: |
|
||||
(github.event_name == 'issues') ||
|
||||
(github.event_name == 'issue_comment' && github.event.sender.type != 'Bot') ||
|
||||
(github.event_name == 'pull_request_review' && github.event.sender.type != 'Bot') ||
|
||||
(github.event_name == 'pull_request_review_comment' && github.event.sender.type != 'Bot')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
# update issues/comments
|
||||
issues: write
|
||||
pull-requests: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude for translation
|
||||
uses: anthropics/claude-code-action@main
|
||||
id: claude
|
||||
with:
|
||||
# Warning: Permissions should have been controlled by workflow permission.
|
||||
# Now `contents: read` is safe for files, but we could make a fine-grained token to control it.
|
||||
# See: https://github.com/anthropics/claude-code-action/blob/main/docs/security.md
|
||||
github_token: ${{ secrets.GH_TOKEN }}
|
||||
allowed_non_write_users: "*"
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
claude_args: "--allowed-tools Bash(gh issue:*),Bash(gh api:repos/*/issues:*),Bash(gh api:repos/*/pulls/*/reviews/*),Bash(gh api:repos/*/pulls/comments/*)"
|
||||
prompt: |
|
||||
You are a multilingual translation assistant. You need to respond to the following four types of GitHub Webhook events:
|
||||
|
||||
- issues
|
||||
- issue_comment
|
||||
- pull_request_review
|
||||
- pull_request_review_comment
|
||||
|
||||
Please complete the following tasks:
|
||||
|
||||
1. Retrieve complete information for the current event.
|
||||
|
||||
- If the current event is 'issues', get the issue information.
|
||||
- If the current event is 'issue_comment', get the comment information.
|
||||
- If the current event is 'pull_request_review', get the review information.
|
||||
- If the current event is 'pull_request_review_comment', get the comment information.
|
||||
|
||||
2. Intelligently detect content.
|
||||
|
||||
- If the retrieved information is already translated content following the format requirements, check if the translation matches the original content. If not, retranslate to match and follow the format requirements.
|
||||
- If the retrieved information is untranslated content, check its language. If not in English, translate to English.
|
||||
- If the retrieved information is partially translated to English, translate it completely to English.
|
||||
- If the retrieved information contains references to already translated content, clean the referenced content to contain only English. Referenced content should not include "This xxx was translated by Claude" and "Original Content" etc.
|
||||
- If the retrieved information contains other types of references (i.e., references to non-Claude translated content), keep them as-is without translation.
|
||||
- If the retrieved information is email reply content, place email content references at the end during translation. Include only the reply content itself in both original and translated content, without email content references.
|
||||
- If the retrieved information doesn't need any processing, skip the task.
|
||||
|
||||
3. Format requirements:
|
||||
|
||||
- Title: English translation (if non-English)
|
||||
- Content format:
|
||||
[Translated content]
|
||||
|
||||
---
|
||||
> This issue/comment/review was translated by Claude.
|
||||
|
||||
<details>
|
||||
<summary>Original Content</summary>
|
||||
[Original content]
|
||||
</details>
|
||||
|
||||
4. Update using gh tool:
|
||||
|
||||
- Choose the correct command based on the Event type in environment information:
|
||||
- If Event is 'issues': gh issue edit [ISSUE_NUMBER] --title "[English title]" --body "[Translated content + Original content]"
|
||||
- If Event is 'issue_comment': gh api -X PATCH /repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }} -f body="[Translated content + Original content]"
|
||||
- If Event is 'pull_request_review': gh api -X PUT /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews/${{ github.event.review.id }} -f body="[Translated content]"
|
||||
- If Event is 'pull_request_review_comment': gh api -X PATCH /repos/${{ github.repository }}/pulls/comments/${{ github.event.comment.id }} -f body="[Translated content + Original content]"
|
||||
|
||||
<environment_context>
|
||||
- Event: ${{ github.event_name }}
|
||||
- Issue Number: ${{ github.event.issue.number }}
|
||||
- Repository: ${{ github.repository }}
|
||||
- (Review) Comment ID: ${{ github.event.comment.id || 'N/A' }}
|
||||
- Pull Request Number: ${{ github.event.pull_request.number || 'N/A' }}
|
||||
- Review ID: ${{ github.event.review.id || 'N/A' }}
|
||||
</environment_context>
|
||||
|
||||
|
||||
Use the following command to get complete information:
|
||||
gh issue view ${{ github.event.issue.number }} --json title,body,comments
|
||||
@@ -28,22 +28,23 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
version: 10
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
- name: Install deps
|
||||
run: pnpm install
|
||||
run: bun i
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
run: bun run lint
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=6144
|
||||
|
||||
@@ -61,9 +62,10 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
# 主要逻辑:确定构建版本号
|
||||
- name: Set version
|
||||
@@ -93,24 +95,25 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-2025, ubuntu-latest]
|
||||
os: [macos-latest, macos-13, windows-2025, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 10
|
||||
package-manager-cache: false
|
||||
|
||||
# node-linker=hoisted 模式将可以确保 asar 压缩可用
|
||||
- name: Install deps
|
||||
- name: Install dependencies
|
||||
run: pnpm install --node-linker=hoisted
|
||||
|
||||
- name: Install deps on Desktop
|
||||
@@ -172,6 +175,28 @@ jobs:
|
||||
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }}
|
||||
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_BASE_URL }}
|
||||
|
||||
# 处理 macOS latest-mac.yml 重命名 (避免多架构覆盖)
|
||||
- name: Rename macOS latest-mac.yml for multi-architecture support
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
cd apps/desktop/release
|
||||
if [ -f "latest-mac.yml" ]; then
|
||||
# 使用系统架构检测,与 electron-builder 输出保持一致
|
||||
SYSTEM_ARCH=$(uname -m)
|
||||
if [[ "$SYSTEM_ARCH" == "arm64" ]]; then
|
||||
ARCH_SUFFIX="arm64"
|
||||
else
|
||||
ARCH_SUFFIX="x64"
|
||||
fi
|
||||
|
||||
mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml"
|
||||
echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)"
|
||||
ls -la latest-mac-*.yml
|
||||
else
|
||||
echo "⚠️ latest-mac.yml not found, skipping rename"
|
||||
ls -la latest*.yml || echo "No latest*.yml files found"
|
||||
fi
|
||||
|
||||
# 上传构建产物
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -189,8 +214,64 @@ jobs:
|
||||
apps/desktop/release/*.tar.gz*
|
||||
retention-days: 5
|
||||
|
||||
publish-pr:
|
||||
# 合并 macOS 多架构 latest-mac.yml 文件
|
||||
merge-mac-files:
|
||||
needs: [build, version]
|
||||
name: Merge macOS Release Files for PR
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
# 下载所有平台的构建产物
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: release
|
||||
pattern: release-*
|
||||
merge-multiple: true
|
||||
|
||||
# 列出下载的构建产物
|
||||
- name: List downloaded artifacts
|
||||
run: ls -R release
|
||||
|
||||
# 仅为该步骤在脚本目录安装 yaml 单包,避免安装整个 monorepo 依赖
|
||||
- name: Install yaml only for merge step
|
||||
run: |
|
||||
cd scripts/electronWorkflow
|
||||
# 在脚本目录创建最小 package.json,防止 bun 向上寻找根 package.json
|
||||
if [ ! -f package.json ]; then
|
||||
echo '{"name":"merge-mac-release","private":true}' > package.json
|
||||
fi
|
||||
bun add --no-save yaml@2.8.1
|
||||
|
||||
# 合并 macOS YAML 文件 (使用 bun 运行 JavaScript)
|
||||
- name: Merge latest-mac.yml files
|
||||
run: bun run scripts/electronWorkflow/mergeMacReleaseFiles.js
|
||||
|
||||
# 上传合并后的构建产物
|
||||
- name: Upload artifacts with merged macOS files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: merged-release-pr
|
||||
path: release/
|
||||
retention-days: 1
|
||||
|
||||
publish-pr:
|
||||
needs: [merge-mac-files, version]
|
||||
name: Publish PR Build
|
||||
runs-on: ubuntu-latest
|
||||
# Grant write permissions for creating release and commenting on PR
|
||||
@@ -204,22 +285,21 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# 下载所有平台的构建产物
|
||||
- name: Download artifacts
|
||||
# 下载合并后的构建产物
|
||||
- name: Download merged artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: merged-release-pr
|
||||
path: release
|
||||
pattern: release-*
|
||||
merge-multiple: true
|
||||
|
||||
# 列出所有构建产物
|
||||
- name: List artifacts
|
||||
- name: List final artifacts
|
||||
run: ls -R release
|
||||
|
||||
# 生成PR发布描述
|
||||
- name: Generate PR Release Body
|
||||
id: pr_release_body
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
@@ -258,7 +338,7 @@ jobs:
|
||||
|
||||
# 在 PR 上添加评论,包含构建信息和下载链接
|
||||
- name: Comment on PR
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
name: Publish Database Docker Image
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -160,10 +162,9 @@ jobs:
|
||||
run: |
|
||||
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
|
||||
|
||||
|
||||
- name: Comment on PR with Docker build info
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
@@ -178,5 +179,3 @@ jobs:
|
||||
platforms: "linux/amd64, linux/arm64",
|
||||
});
|
||||
core.info(`Status: ${result.updated ? 'Updated' : 'Created'}, ID: ${result.id}`);
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
name: Publish Docker Pglite Image
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
name: Publish Docker Image
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -28,8 +28,7 @@ jobs:
|
||||
👀 @{{ author }}
|
||||
|
||||
Thank you for raising an issue. We will investigate into the matter and get back to you as soon as possible.
|
||||
Please make sure you have given us as much context as possible.\
|
||||
非常感谢您提交 issue。我们会尽快调查此事,并尽快回复您。 请确保您已经提供了尽可能多的背景信息。
|
||||
Please make sure you have given us as much context as possible.
|
||||
- name: Auto Comment on Issues Closed
|
||||
uses: wow-actions/auto-comment@v1
|
||||
with:
|
||||
@@ -37,8 +36,7 @@ jobs:
|
||||
issuesClosed: |
|
||||
✅ @{{ author }}
|
||||
|
||||
This issue is closed, If you have any questions, you can comment and reply.\
|
||||
此问题已经关闭。如果您有任何问题,可以留言并回复。
|
||||
This issue is closed, If you have any questions, you can comment and reply.
|
||||
- name: Auto Comment on Pull Request Opened
|
||||
uses: wow-actions/auto-comment@v1
|
||||
with:
|
||||
@@ -48,9 +46,7 @@ jobs:
|
||||
|
||||
Thank you for raising your pull request and contributing to our Community
|
||||
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.
|
||||
If you encounter any problems, please feel free to connect with us.\
|
||||
非常感谢您提出拉取请求并为我们的社区做出贡献,请确保您已经遵循了我们的贡献指南,我们会尽快审查它。
|
||||
如果您遇到任何问题,请随时与我们联系。
|
||||
If you encounter any problems, please feel free to connect with us.
|
||||
- name: Auto Comment on Pull Request Merged
|
||||
uses: actions-cool/pr-welcome@main
|
||||
if: github.event.pull_request.merged == true
|
||||
@@ -59,8 +55,7 @@ jobs:
|
||||
comment: |
|
||||
❤️ Great PR @${{ github.event.pull_request.user.login }} ❤️
|
||||
|
||||
The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our [discord](https://discord.com/invite/AYFPHvv2jT) and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.\
|
||||
项目的成长离不开用户反馈和贡献,感谢您的贡献! 如果您对 LobeHub 开发者社区感兴趣,请加入我们的 [discord](https://discord.com/invite/AYFPHvv2jT),然后私信 @arvinxx 或 @canisminor1990。他们会邀请您加入我们的私密开发者频道。我们将会讨论关于 Lobe Chat 的开发,分享和讨论全球范围内的 AI 消息。
|
||||
The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our [discord](https://discord.com/invite/AYFPHvv2jT) and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.
|
||||
emoji: 'hooray'
|
||||
pr-emoji: '+1, heart'
|
||||
- name: Remove inactive
|
||||
|
||||
@@ -38,8 +38,7 @@ jobs:
|
||||
body: |
|
||||
👋 @{{ author }}
|
||||
<br/>
|
||||
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
|
||||
由于该 issue 被标记为已修复,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
|
||||
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
|
||||
- name: need reproduce
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
@@ -50,8 +49,7 @@ jobs:
|
||||
body: |
|
||||
👋 @{{ author }}
|
||||
<br/>
|
||||
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
|
||||
由于该 issue 被标记为需要更多信息,却 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
|
||||
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
|
||||
- name: need reproduce
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
@@ -62,5 +60,4 @@ jobs:
|
||||
body: |
|
||||
👋 @{{ github.event.issue.user.login }}
|
||||
<br/>
|
||||
Since the issue was labeled with `🙅🏻♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
|
||||
由于该 issue 被标记为暂不处理,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
|
||||
Since the issue was labeled with `🙅🏻♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
name: Issue Translate
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
BOT_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
@@ -0,0 +1,26 @@
|
||||
name: "Lock Stale Issues"
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 1 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: lock-threads
|
||||
|
||||
jobs:
|
||||
lock-closed-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Lock closed issues after 7 days of inactivity
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const lockScript = require('./.github/scripts/lock-closed-issues.js');
|
||||
await lockScript({ github, context });
|
||||
@@ -24,20 +24,21 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
version: 10
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
- name: Install deps
|
||||
run: pnpm install
|
||||
run: bun i
|
||||
|
||||
- name: Lint
|
||||
run: pnpm run lint
|
||||
run: bun run lint
|
||||
|
||||
version:
|
||||
name: Determine version
|
||||
@@ -52,9 +53,10 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
# 主要逻辑:确定构建版本号
|
||||
- name: Set version
|
||||
@@ -80,24 +82,25 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-2025, ubuntu-latest]
|
||||
os: [macos-latest, macos-13, windows-2025, ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
run_install: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 10
|
||||
package-manager-cache: false
|
||||
|
||||
# node-linker=hoisted 模式将可以确保 asar 压缩可用
|
||||
- name: Install deps
|
||||
- name: Install dependencies
|
||||
run: pnpm install --node-linker=hoisted
|
||||
|
||||
- name: Install deps on Desktop
|
||||
@@ -154,7 +157,29 @@ jobs:
|
||||
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_BETA_DESKTOP_PROJECT_ID }}
|
||||
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_BETA_DESKTOP_BASE_URL }}
|
||||
|
||||
# 上传构建产物,移除了 zip 相关部分
|
||||
# 处理 macOS latest-mac.yml 重命名 (避免多架构覆盖)
|
||||
- name: Rename macOS latest-mac.yml for multi-architecture support
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
cd apps/desktop/release
|
||||
if [ -f "latest-mac.yml" ]; then
|
||||
# 使用系统架构检测,与 electron-builder 输出保持一致
|
||||
SYSTEM_ARCH=$(uname -m)
|
||||
if [[ "$SYSTEM_ARCH" == "arm64" ]]; then
|
||||
ARCH_SUFFIX="arm64"
|
||||
else
|
||||
ARCH_SUFFIX="x64"
|
||||
fi
|
||||
|
||||
mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml"
|
||||
echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)"
|
||||
ls -la latest-mac-*.yml
|
||||
else
|
||||
echo "⚠️ latest-mac.yml not found, skipping rename"
|
||||
ls -la latest*.yml || echo "No latest*.yml files found"
|
||||
fi
|
||||
|
||||
# 上传构建产物 (工作流处理重命名,不依赖 electron-builder 钩子)
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -171,17 +196,28 @@ jobs:
|
||||
apps/desktop/release/*.tar.gz*
|
||||
retention-days: 5
|
||||
|
||||
# 正式版发布 job
|
||||
publish-release:
|
||||
# 合并 macOS 多架构 latest-mac.yml 文件
|
||||
merge-mac-files:
|
||||
needs: [build, version]
|
||||
name: Publish Beta Release
|
||||
name: Merge macOS Release Files
|
||||
runs-on: ubuntu-latest
|
||||
# Grant write permission to contents for uploading release assets
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
artifact_path: ${{ steps.set_path.outputs.path }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
# 下载所有平台的构建产物
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -190,11 +226,52 @@ jobs:
|
||||
pattern: release-*
|
||||
merge-multiple: true
|
||||
|
||||
# 列出所有构建产物
|
||||
- name: List artifacts
|
||||
# 列出下载的构建产物
|
||||
- name: List downloaded artifacts
|
||||
run: ls -R release
|
||||
|
||||
# 将构建产物上传到现有 release
|
||||
# 仅为该步骤在脚本目录安装 yaml 单包,避免安装整个 monorepo 依赖
|
||||
- name: Install yaml only for merge step
|
||||
run: |
|
||||
cd scripts/electronWorkflow
|
||||
# 在脚本目录创建最小 package.json,防止 bun 向上寻找根 package.json
|
||||
if [ ! -f package.json ]; then
|
||||
echo '{"name":"merge-mac-release","private":true}' > package.json
|
||||
fi
|
||||
bun add --no-save yaml@2.8.1
|
||||
|
||||
# 合并 macOS YAML 文件 (使用 bun 运行 JavaScript)
|
||||
- name: Merge latest-mac.yml files
|
||||
run: bun run scripts/electronWorkflow/mergeMacReleaseFiles.js
|
||||
|
||||
# 上传合并后的构建产物
|
||||
- name: Upload artifacts with merged macOS files
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: merged-release
|
||||
path: release/
|
||||
retention-days: 1
|
||||
|
||||
# 发布所有平台构建产物
|
||||
publish-release:
|
||||
needs: [merge-mac-files]
|
||||
name: Publish Beta Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
# 下载合并后的构建产物
|
||||
- name: Download merged artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: merged-release
|
||||
path: release
|
||||
|
||||
# 列出所有构建产物
|
||||
- name: List final artifacts
|
||||
run: ls -R release
|
||||
|
||||
# 将构建产物上传到现有 release (现在包含合并后的 latest-mac.yml)
|
||||
- name: Upload to Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
name: Release CI
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -27,12 +33,13 @@ jobs:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
name: Database Schema Visualization CI
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,11 +15,13 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Install dbdocs
|
||||
run: sudo npm install -g dbdocs
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
- name: Check dbdocs
|
||||
run: dbdocs
|
||||
- name: Install deps
|
||||
run: bun i
|
||||
|
||||
- name: sync database schema to dbdocs
|
||||
env:
|
||||
|
||||
+26
-14
@@ -11,7 +11,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
package: [file-loaders, prompts, model-runtime, web-crawler, electron-server-ipc, utils]
|
||||
package:
|
||||
- file-loaders
|
||||
- prompts
|
||||
- model-runtime
|
||||
- web-crawler
|
||||
- electron-server-ipc
|
||||
- utils
|
||||
- python-interpreter
|
||||
- context-engine
|
||||
- agent-runtime
|
||||
|
||||
name: Test package ${{ matrix.package }}
|
||||
|
||||
@@ -19,12 +28,13 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
@@ -35,7 +45,7 @@ jobs:
|
||||
run: bun run --filter @lobechat/${{ matrix.package }} test:coverage
|
||||
|
||||
- name: Upload ${{ matrix.package }} coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/${{ matrix.package }}/coverage/lcov.info
|
||||
@@ -53,12 +63,13 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
@@ -69,13 +80,12 @@ jobs:
|
||||
run: bun run --filter ${{ matrix.package }} test:coverage
|
||||
|
||||
- name: Upload ${{ matrix.package }} coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/${{ matrix.package }}/coverage/lcov.info
|
||||
flags: packages/${{ matrix.package }}
|
||||
|
||||
|
||||
# App tests
|
||||
test-website:
|
||||
name: Test Website
|
||||
@@ -86,12 +96,13 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
@@ -102,7 +113,7 @@ jobs:
|
||||
run: bun run test-app:coverage
|
||||
|
||||
- name: Upload App Coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./coverage/app/lcov.info
|
||||
@@ -129,12 +140,13 @@ jobs:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v5
|
||||
with:
|
||||
node-version: 22
|
||||
package-manager-cache: false
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
@@ -162,7 +174,7 @@ jobs:
|
||||
APP_URL: https://home.com
|
||||
|
||||
- name: Upload Database coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ./packages/database/coverage/lcov.info
|
||||
|
||||
@@ -23,6 +23,7 @@ Desktop.ini
|
||||
.history/
|
||||
.windsurfrules
|
||||
*.code-workspace
|
||||
.vscode/sessions.json
|
||||
|
||||
# Temporary files
|
||||
.temp/
|
||||
@@ -38,6 +39,7 @@ tmp/
|
||||
.env
|
||||
.env.local
|
||||
.env*.local
|
||||
.env.development
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
@@ -112,3 +114,6 @@ CLAUDE.local.md
|
||||
*.ppt*
|
||||
*.doc*
|
||||
*.xls*
|
||||
|
||||
prd
|
||||
GEMINI.md
|
||||
|
||||
Vendored
+5
-2
@@ -11,6 +11,7 @@
|
||||
{ "rule": "prettier/prettier", "severity": "off" },
|
||||
{ "rule": "react/jsx-sort-props", "severity": "off" },
|
||||
{ "rule": "sort-keys-fix/sort-keys-fix", "severity": "off" },
|
||||
{ "rule": "simple-import-sort/exports", "severity": "off" },
|
||||
{ "rule": "typescript-sort-keys/interface", "severity": "off" }
|
||||
],
|
||||
"eslint.validate": [
|
||||
@@ -81,13 +82,15 @@
|
||||
|
||||
"**/src/config/modelProviders/*.ts": "${filename} • provider",
|
||||
"**/packages/model-bank/src/aiModels/*.ts": "${filename} • model",
|
||||
"**/packages/model-runtime/src/*/index.ts": "${dirname} • runtime",
|
||||
"**/packages/model-runtime/src/providers/*/index.ts": "${dirname} • runtime",
|
||||
|
||||
"**/src/server/services/*/index.ts": "${dirname} • server/service",
|
||||
"**/src/server/routers/lambda/*.ts": "${filename} • lambda",
|
||||
"**/src/server/routers/async/*.ts": "${filename} • async",
|
||||
"**/src/server/routers/edge/*.ts": "${filename} • edge",
|
||||
|
||||
"**/src/locales/default/*.ts": "${filename} • locale"
|
||||
"**/src/locales/default/*.ts": "${filename} • locale",
|
||||
|
||||
"**/index.*": "${dirname}/${filename}.${extname}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ Built with modern technologies:
|
||||
- **Database**: PostgreSQL, PGLite, Drizzle ORM
|
||||
- **Testing**: Vitest, Testing Library
|
||||
- **Package Manager**: pnpm (monorepo structure)
|
||||
- **Build Tools**: Next.js (Turbopack in dev, Webpack in prod), Vitest
|
||||
- **Build Tools**: Next.js (Turbopack in dev, Webpack in prod)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
@@ -28,7 +28,7 @@ The project follows a well-organized monorepo structure:
|
||||
|
||||
### Git Workflow
|
||||
|
||||
- Use rebase for git pull: `git pull --rebase`
|
||||
- Use rebase for git pull
|
||||
- Git commit messages should prefix with gitmoji
|
||||
- Git branch name format: `username/feat/feature-name`
|
||||
- Use `.github/PULL_REQUEST_TEMPLATE.md` for PR descriptions
|
||||
@@ -44,24 +44,7 @@ The project follows a well-organized monorepo structure:
|
||||
|
||||
#### TypeScript
|
||||
|
||||
- Follow strict TypeScript practices for type safety and code quality
|
||||
- Use proper type annotations
|
||||
- Prefer interfaces over types for object shapes
|
||||
- Use generics for reusable components
|
||||
|
||||
#### React Components
|
||||
|
||||
- Use functional components with hooks
|
||||
- Follow the component structure guidelines
|
||||
- Use antd-style & @lobehub/ui for styling
|
||||
- Implement proper error boundaries
|
||||
|
||||
#### Database Schema
|
||||
|
||||
- Follow Drizzle ORM naming conventions
|
||||
- Use plural snake_case for table names
|
||||
- Implement proper foreign key relationships
|
||||
- Follow the schema style guide
|
||||
|
||||
### Testing Strategy
|
||||
|
||||
@@ -70,64 +53,57 @@ The project follows a well-organized monorepo structure:
|
||||
**Commands**:
|
||||
|
||||
- Web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'`
|
||||
- Packages: `cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path-pattern]'`
|
||||
- Packages: `cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path-pattern]'` (each subpackage contains its own vitest.config.mts)
|
||||
|
||||
**Important Notes**:
|
||||
|
||||
- Wrap file paths in single quotes to avoid shell expansion
|
||||
- Never run `bun run test` - this runs all tests and takes ~10 minutes
|
||||
- If a test fails twice, stop and ask for help
|
||||
- Always add tests for new code
|
||||
- Never run `bun run test` - this runs all tests and takes \~10 minutes
|
||||
|
||||
### Type Checking
|
||||
|
||||
- Use `bun run type-check` to check for type errors
|
||||
- Ensure all TypeScript errors are resolved before committing
|
||||
|
||||
### Internationalization
|
||||
### i18n
|
||||
|
||||
- Add new keys to `src/locales/default/namespace.ts`
|
||||
- Translate at least `zh-CN` files for development preview
|
||||
- Use hierarchical nested objects, not flat keys
|
||||
- Don't run `pnpm i18n` manually (handled by CI)
|
||||
- **Keys**: Add to `src/locales/default/namespace.ts`
|
||||
- **Dev**: Translate `locales/zh-CN/namespace.json` locale file only for preview
|
||||
- DON'T run `pnpm i18n`, let CI auto handle it
|
||||
|
||||
## Available Development Rules
|
||||
## Project Rules Index
|
||||
|
||||
The project provides comprehensive rules in `.cursor/rules/` directory:
|
||||
All following rules are saved under `.cursor/rules/` directory:
|
||||
|
||||
### Core Development
|
||||
### Backend
|
||||
|
||||
- `backend-architecture.mdc` - Three-layer architecture and data flow
|
||||
- `react-component.mdc` - Component patterns and UI library usage
|
||||
- `drizzle-schema-style-guide.mdc` - Database schema conventions
|
||||
- `define-database-model.mdc` - Model templates and CRUD patterns
|
||||
- `i18n.mdc` - Internationalization workflow
|
||||
- `drizzle-schema-style-guide.mdc` – Style guide for defining Drizzle ORM schemas
|
||||
|
||||
### State Management & UI
|
||||
### Frontend
|
||||
|
||||
- `zustand-slice-organization.mdc` - Store organization patterns
|
||||
- `zustand-action-patterns.mdc` - Action implementation patterns
|
||||
- `packages/react-layout-kit.mdc` - Flex layout component usage
|
||||
- `react-component.mdc` – React component style guide and conventions
|
||||
- `i18n.mdc` – Internationalization guide using react-i18next
|
||||
- `typescript.mdc` – TypeScript code style guide
|
||||
- `packages/react-layout-kit.mdc` – Usage guide for react-layout-kit
|
||||
|
||||
### Testing & Quality
|
||||
### State Management
|
||||
|
||||
- `testing-guide/testing-guide.mdc` - Comprehensive testing strategy
|
||||
- `code-review.mdc` - Code review process and standards
|
||||
- `zustand-action-patterns.mdc` – Recommended patterns for organizing Zustand actions
|
||||
- `zustand-slice-organization.mdc` – Best practices for structuring Zustand slices
|
||||
|
||||
### Desktop (Electron)
|
||||
|
||||
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
|
||||
- `desktop-local-tools-implement.mdc` - Tool integration workflow
|
||||
- `desktop-menu-configuration.mdc` - Menu system configuration
|
||||
- `desktop-window-management.mdc` - Window management patterns
|
||||
- `desktop-controller-tests.mdc` - Controller testing guide
|
||||
- `desktop-feature-implementation.mdc` – Implementing new Electron desktop features
|
||||
- `desktop-controller-tests.mdc` – Desktop controller unit testing guide
|
||||
- `desktop-local-tools-implement.mdc` – Workflow to add new desktop local tools
|
||||
- `desktop-menu-configuration.mdc` – Desktop menu configuration guide
|
||||
- `desktop-window-management.mdc` – Desktop window management guide
|
||||
|
||||
## Best Practices
|
||||
### Debugging
|
||||
|
||||
- **Conservative for existing code, modern approaches for new features**
|
||||
- **Code Language**: Use Chinese for files with existing Chinese comments, American English for new files
|
||||
- Always add tests for new functionality
|
||||
- Follow the established patterns in the codebase
|
||||
- Use proper error handling and logging
|
||||
- Implement proper accessibility features
|
||||
- Consider internationalization from the start
|
||||
- `debug-usage.mdc` – Using the debug package and namespace conventions
|
||||
|
||||
### Testing
|
||||
|
||||
- `testing-guide/testing-guide.mdc` – Comprehensive testing guide for Vitest
|
||||
- `testing-guide/electron-ipc-test.mdc` – Electron IPC interface testing strategy
|
||||
- `testing-guide/db-model-test.mdc` – Database Model testing guide
|
||||
|
||||
+1924
File diff suppressed because it is too large
Load Diff
@@ -72,29 +72,4 @@ Some useful rules of this project. Read them when needed.
|
||||
|
||||
### 📋 Complete Rule Files
|
||||
|
||||
**Core Development**
|
||||
|
||||
- `backend-architecture.mdc` - Three-layer architecture, data flow
|
||||
- `react-component.mdc` - antd-style, Lobe UI usage
|
||||
- `drizzle-schema-style-guide.mdc` - Schema naming, patterns
|
||||
- `define-database-model.mdc` - Model templates, CRUD patterns
|
||||
- `i18n.mdc` - Internationalization workflow
|
||||
|
||||
**State & UI**
|
||||
|
||||
- `zustand-slice-organization.mdc` - Store organization
|
||||
- `zustand-action-patterns.mdc` - Action patterns
|
||||
- `packages/react-layout-kit.mdc` - flex layout components usage
|
||||
|
||||
**Testing & Quality**
|
||||
|
||||
- `testing-guide/testing-guide.mdc` - Test strategy, mock patterns
|
||||
- `code-review.mdc` - Review process and standards
|
||||
|
||||
**Desktop (Electron)**
|
||||
|
||||
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
|
||||
- `desktop-local-tools-implement.mdc` - Tool integration workflow
|
||||
- `desktop-menu-configuration.mdc` - App menu, context menu, tray menu
|
||||
- `desktop-window-management.mdc` - Window creation, state management, multi-window
|
||||
- `desktop-controller-tests.mdc` - Controller unit testing guide
|
||||
Some useful project rules are listed in @.cursor/rules/rules-index.mdc
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
## Set global build ENV
|
||||
ARG NODEJS_VERSION="22"
|
||||
ARG NODEJS_VERSION="24"
|
||||
|
||||
## Base image for all building stages
|
||||
FROM node:${NODEJS_VERSION}-slim AS base
|
||||
@@ -126,7 +126,7 @@ ENV NODE_ENV="production" \
|
||||
NODE_OPTIONS="--dns-result-order=ipv4first --use-openssl-ca" \
|
||||
NODE_EXTRA_CA_CERTS="" \
|
||||
NODE_TLS_REJECT_UNAUTHORIZED="" \
|
||||
SSL_CERT_DIR="/etc/ssl/certs/ca-certificates.crt"
|
||||
SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
# Make the middleware rewrite through local as default
|
||||
# refs: https://github.com/lobehub/lobe-chat/issues/5876
|
||||
@@ -196,6 +196,8 @@ ENV \
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Nebius
|
||||
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
|
||||
# NewAPI
|
||||
NEWAPI_API_KEY="" NEWAPI_PROXY_URL="" \
|
||||
# Novita
|
||||
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
|
||||
# Nvidia NIM
|
||||
@@ -255,7 +257,9 @@ ENV \
|
||||
# FAL
|
||||
FAL_API_KEY="" FAL_MODEL_LIST="" \
|
||||
# BFL
|
||||
BFL_API_KEY="" BFL_MODEL_LIST=""
|
||||
BFL_API_KEY="" BFL_MODEL_LIST="" \
|
||||
# Vercel AI Gateway
|
||||
VERCELAIGATEWAY_API_KEY="" VERCELAIGATEWAY_MODEL_LIST=""
|
||||
|
||||
USER nextjs
|
||||
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
## Set global build ENV
|
||||
ARG NODEJS_VERSION="22"
|
||||
ARG NODEJS_VERSION="24"
|
||||
|
||||
## Base image for all building stages
|
||||
FROM node:${NODEJS_VERSION}-slim AS base
|
||||
@@ -149,7 +149,7 @@ ENV NODE_ENV="production" \
|
||||
NODE_OPTIONS="--dns-result-order=ipv4first --use-openssl-ca" \
|
||||
NODE_EXTRA_CA_CERTS="" \
|
||||
NODE_TLS_REJECT_UNAUTHORIZED="" \
|
||||
SSL_CERT_DIR="/etc/ssl/certs/ca-certificates.crt"
|
||||
SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
# Make the middleware rewrite through local as default
|
||||
# refs: https://github.com/lobehub/lobe-chat/issues/5876
|
||||
@@ -238,6 +238,8 @@ ENV \
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Nebius
|
||||
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
|
||||
# NewAPI
|
||||
NEWAPI_API_KEY="" NEWAPI_PROXY_URL="" \
|
||||
# Novita
|
||||
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
|
||||
# Nvidia NIM
|
||||
@@ -297,7 +299,9 @@ ENV \
|
||||
# FAL
|
||||
FAL_API_KEY="" FAL_MODEL_LIST="" \
|
||||
# BFL
|
||||
BFL_API_KEY="" BFL_MODEL_LIST=""
|
||||
BFL_API_KEY="" BFL_MODEL_LIST="" \
|
||||
# Vercel AI Gateway
|
||||
VERCELAIGATEWAY_API_KEY="" VERCELAIGATEWAY_MODEL_LIST=""
|
||||
|
||||
USER nextjs
|
||||
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
## Set global build ENV
|
||||
ARG NODEJS_VERSION="22"
|
||||
ARG NODEJS_VERSION="24"
|
||||
|
||||
## Base image for all building stages
|
||||
FROM node:${NODEJS_VERSION}-slim AS base
|
||||
@@ -128,7 +128,7 @@ ENV NODE_ENV="production" \
|
||||
NODE_OPTIONS="--dns-result-order=ipv4first --use-openssl-ca" \
|
||||
NODE_EXTRA_CA_CERTS="" \
|
||||
NODE_TLS_REJECT_UNAUTHORIZED="" \
|
||||
SSL_CERT_DIR="/etc/ssl/certs/ca-certificates.crt"
|
||||
SSL_CERT_FILE="/etc/ssl/certs/ca-certificates.crt"
|
||||
|
||||
# Make the middleware rewrite through local as default
|
||||
# refs: https://github.com/lobehub/lobe-chat/issues/5876
|
||||
@@ -198,6 +198,8 @@ ENV \
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Nebius
|
||||
NEBIUS_API_KEY="" NEBIUS_MODEL_LIST="" NEBIUS_PROXY_URL="" \
|
||||
# NewAPI
|
||||
NEWAPI_API_KEY="" NEWAPI_PROXY_URL="" \
|
||||
# Novita
|
||||
NOVITA_API_KEY="" NOVITA_MODEL_LIST="" \
|
||||
# Nvidia NIM
|
||||
@@ -253,7 +255,9 @@ ENV \
|
||||
# FAL
|
||||
FAL_API_KEY="" FAL_MODEL_LIST="" \
|
||||
# BFL
|
||||
BFL_API_KEY="" BFL_MODEL_LIST=""
|
||||
BFL_API_KEY="" BFL_MODEL_LIST="" \
|
||||
# Vercel AI Gateway
|
||||
VERCELAIGATEWAY_API_KEY="" VERCELAIGATEWAY_MODEL_LIST=""
|
||||
|
||||
USER nextjs
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
Apache License Version 2.0
|
||||
LobeHub Community License
|
||||
|
||||
Copyright (c) 2024/06/17 - current LobeHub LLC. All rights reserved.
|
||||
|
||||
----------
|
||||
|
||||
From 1.0, LobeChat is licensed under the Apache License 2.0, with the following additional conditions:
|
||||
From 1.0, LobeChat is licensed under the LobeHub Community License, based on Apache License 2.0 with the following additional conditions:
|
||||
|
||||
1. The commercial usage of LobeChat:
|
||||
|
||||
@@ -22,17 +22,3 @@ Please contact hello@lobehub.com by email to inquire about licensing matters.
|
||||
b. Your contributed code may be used for commercial purposes, including but not limited to its cloud edition.
|
||||
|
||||
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
|
||||
|
||||
----------
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -384,7 +384,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
||||
|
||||
| Recent Submits | Description |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-07-21**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-09-27**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
|
||||
| [Web](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | Smart web search that reads and analyzes pages to deliver comprehensive answers from Google results.<br/>`web` `search` |
|
||||
| [Bing_websearch](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | Search for information from the internet base BingApi<br/>`bingsearch` |
|
||||
| [Google CSE](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | Searches Google through their official CSE API.<br/>`web` `search` |
|
||||
@@ -819,7 +819,7 @@ Every bit counts and your one-time donation sparkles in our galaxy of support! Y
|
||||
</details>
|
||||
|
||||
Copyright © 2025 [LobeHub][profile-link]. <br />
|
||||
This project is [Apache 2.0](./LICENSE) licensed.
|
||||
This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
|
||||
<!-- LINK GROUP -->
|
||||
|
||||
|
||||
+2
-2
@@ -377,7 +377,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
| 最近新增 | 描述 |
|
||||
| -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-07-21**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-09-27**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
|
||||
| [网页](https://lobechat.com/discover/plugin/web)<br/><sup>By **Proghit** on **2025-01-24**</sup> | 智能网页搜索,读取和分析页面,以提供来自 Google 结果的全面答案。<br/>`网页` `搜索` |
|
||||
| [必应网页搜索](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | 通过 BingApi 搜索互联网上的信息<br/>`bingsearch` |
|
||||
| [谷歌自定义搜索引擎](https://lobechat.com/discover/plugin/google-cse)<br/><sup>By **vsnthdev** on **2024-12-02**</sup> | 通过他们的官方自定义搜索引擎 API 搜索谷歌。<br/>`网络` `搜索` |
|
||||
@@ -840,7 +840,7 @@ $ pnpm run dev
|
||||
</details>
|
||||
|
||||
Copyright © 2025 [LobeHub][profile-link]. <br />
|
||||
This project is [Apache 2.0](./LICENSE) licensed.
|
||||
This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
|
||||
<!-- LINK GROUP -->
|
||||
|
||||
|
||||
@@ -1,16 +1,27 @@
|
||||
const dotenv = require('dotenv');
|
||||
const os = require('node:os');
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const packageJSON = require('./package.json');
|
||||
|
||||
const channel = process.env.UPDATE_CHANNEL;
|
||||
const arch = os.arch();
|
||||
const hasAppleCertificate = Boolean(process.env.CSC_LINK);
|
||||
|
||||
console.log(`🚄 Build Version ${packageJSON.version}, Channel: ${channel}`);
|
||||
console.log(`🏗️ Building for architecture: ${arch}`);
|
||||
|
||||
const isNightly = channel === 'nightly';
|
||||
const isBeta = packageJSON.name.includes('beta');
|
||||
|
||||
// https://www.electron.build/code-signing-mac#how-to-disable-code-signing-during-the-build-process-on-macos
|
||||
if (!hasAppleCertificate) {
|
||||
// Disable auto discovery to keep electron-builder from searching unavailable signing identities
|
||||
process.env.CSC_IDENTITY_AUTO_DISCOVERY = 'false';
|
||||
console.log('⚠️ Apple certificate link not found, macOS artifacts will be unsigned.');
|
||||
}
|
||||
|
||||
// 根据版本类型确定协议 scheme
|
||||
const getProtocolScheme = () => {
|
||||
if (isNightly) return 'lobehub-nightly';
|
||||
@@ -84,15 +95,17 @@ const config = {
|
||||
NSMicrophoneUsageDescription: "Application requests access to the device's microphone.",
|
||||
},
|
||||
gatekeeperAssess: false,
|
||||
hardenedRuntime: true,
|
||||
notarize: true,
|
||||
hardenedRuntime: hasAppleCertificate,
|
||||
notarize: hasAppleCertificate,
|
||||
...(hasAppleCertificate ? {} : { identity: null }),
|
||||
target:
|
||||
// 降低构建时间,nightly 只打 arm64
|
||||
// 降低构建时间,nightly 只打 dmg
|
||||
// 根据当前机器架构只构建对应架构的包
|
||||
isNightly
|
||||
? [{ arch: ['arm64'], target: 'dmg' }]
|
||||
? [{ arch: [arch === 'arm64' ? 'arm64' : 'x64'], target: 'dmg' }]
|
||||
: [
|
||||
{ arch: ['x64', 'arm64'], target: 'dmg' },
|
||||
{ arch: ['x64', 'arm64'], target: 'zip' },
|
||||
{ arch: [arch === 'arm64' ? 'arm64' : 'x64'], target: 'dmg' },
|
||||
{ arch: [arch === 'arm64' ? 'arm64' : 'x64'], target: 'zip' },
|
||||
],
|
||||
},
|
||||
npmRebuild: true,
|
||||
|
||||
@@ -52,14 +52,14 @@
|
||||
"@typescript/native-preview": "7.0.0-dev.20250711.1",
|
||||
"consola": "^3.1.0",
|
||||
"cookie": "^1.0.2",
|
||||
"electron": "^37.4.0",
|
||||
"electron": "^38.0.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-is": "^3.0.0",
|
||||
"electron-log": "^5.3.3",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-vite": "^3.0.0",
|
||||
"execa": "^9.5.2",
|
||||
"fix-path": "^4.0.0",
|
||||
"fix-path": "^5.0.0",
|
||||
"http-proxy-agent": "^7.0.2",
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"just-diff": "^6.0.2",
|
||||
|
||||
@@ -46,4 +46,55 @@ export const appBrowsers = {
|
||||
},
|
||||
} satisfies Record<string, BrowserWindowOpts>;
|
||||
|
||||
// Window templates for multi-instance windows
|
||||
export interface WindowTemplate {
|
||||
allowMultipleInstances: boolean;
|
||||
// Include common BrowserWindow options
|
||||
autoHideMenuBar?: boolean;
|
||||
baseIdentifier: string;
|
||||
basePath: string;
|
||||
devTools?: boolean;
|
||||
height?: number;
|
||||
keepAlive?: boolean;
|
||||
minWidth?: number;
|
||||
parentIdentifier?: string;
|
||||
showOnInit?: boolean;
|
||||
title?: string;
|
||||
titleBarStyle?: 'hidden' | 'default' | 'hiddenInset' | 'customButtonsOnHover';
|
||||
vibrancy?:
|
||||
| 'appearance-based'
|
||||
| 'content'
|
||||
| 'fullscreen-ui'
|
||||
| 'header'
|
||||
| 'hud'
|
||||
| 'menu'
|
||||
| 'popover'
|
||||
| 'selection'
|
||||
| 'sheet'
|
||||
| 'sidebar'
|
||||
| 'titlebar'
|
||||
| 'tooltip'
|
||||
| 'under-page'
|
||||
| 'under-window'
|
||||
| 'window';
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export const windowTemplates = {
|
||||
chatSingle: {
|
||||
allowMultipleInstances: true,
|
||||
autoHideMenuBar: true,
|
||||
baseIdentifier: 'chatSingle',
|
||||
basePath: '/chat',
|
||||
height: 600,
|
||||
keepAlive: false, // Multi-instance windows don't need to stay alive
|
||||
minWidth: 400,
|
||||
parentIdentifier: 'chat',
|
||||
titleBarStyle: 'hidden',
|
||||
vibrancy: 'under-window',
|
||||
width: 900,
|
||||
},
|
||||
} satisfies Record<string, WindowTemplate>;
|
||||
|
||||
export type AppBrowsersIdentifiers = keyof typeof appBrowsers;
|
||||
export type WindowTemplateIdentifiers = keyof typeof windowTemplates;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InterceptRouteParams } from '@lobechat/electron-client-ipc';
|
||||
import { extractSubPath, findMatchingRoute } from '~common/routes';
|
||||
|
||||
import { AppBrowsersIdentifiers, BrowsersIdentifiers } from '@/appBrowsers';
|
||||
import { AppBrowsersIdentifiers, BrowsersIdentifiers, WindowTemplateIdentifiers } from '@/appBrowsers';
|
||||
import { IpcClientEventSender } from '@/types/ipcClientEvent';
|
||||
|
||||
import { ControllerModule, ipcClientEvent, shortcut } from './index';
|
||||
@@ -100,6 +100,77 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new multi-instance window
|
||||
*/
|
||||
@ipcClientEvent('createMultiInstanceWindow')
|
||||
async createMultiInstanceWindow(params: {
|
||||
templateId: WindowTemplateIdentifiers;
|
||||
path: string;
|
||||
uniqueId?: string;
|
||||
}) {
|
||||
try {
|
||||
console.log('[BrowserWindowsCtr] Creating multi-instance window:', params);
|
||||
|
||||
const result = this.app.browserManager.createMultiInstanceWindow(
|
||||
params.templateId,
|
||||
params.path,
|
||||
params.uniqueId,
|
||||
);
|
||||
|
||||
// Show the window
|
||||
result.browser.show();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
windowId: result.identifier,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[BrowserWindowsCtr] Failed to create multi-instance window:', error);
|
||||
return {
|
||||
error: error.message,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all windows by template
|
||||
*/
|
||||
@ipcClientEvent('getWindowsByTemplate')
|
||||
async getWindowsByTemplate(templateId: string) {
|
||||
try {
|
||||
const windowIds = this.app.browserManager.getWindowsByTemplate(templateId);
|
||||
return {
|
||||
success: true,
|
||||
windowIds,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('[BrowserWindowsCtr] Failed to get windows by template:', error);
|
||||
return {
|
||||
error: error.message,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all windows by template
|
||||
*/
|
||||
@ipcClientEvent('closeWindowsByTemplate')
|
||||
async closeWindowsByTemplate(templateId: string) {
|
||||
try {
|
||||
this.app.browserManager.closeWindowsByTemplate(templateId);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('[BrowserWindowsCtr] Failed to close windows by template:', error);
|
||||
return {
|
||||
error: error.message,
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open target window and navigate to specified sub-path
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { beforeEach, describe, expect, it, vi, Mock } from 'vitest';
|
||||
import { InterceptRouteParams } from '@lobechat/electron-client-ipc';
|
||||
import { Mock, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { AppBrowsersIdentifiers, BrowsersIdentifiers } from '@/appBrowsers';
|
||||
import type { App } from '@/core/App';
|
||||
import type { IpcClientEventSender } from '@/types/ipcClientEvent';
|
||||
import { BrowsersIdentifiers, AppBrowsersIdentifiers } from '@/appBrowsers';
|
||||
|
||||
import BrowserWindowsCtr from '../BrowserWindowsCtr';
|
||||
|
||||
@@ -33,12 +33,14 @@ const mockApp = {
|
||||
closeWindow: mockCloseWindow,
|
||||
minimizeWindow: mockMinimizeWindow,
|
||||
maximizeWindow: mockMaximizeWindow,
|
||||
retrieveByIdentifier: mockRetrieveByIdentifier.mockImplementation((identifier: AppBrowsersIdentifiers | string) => {
|
||||
if (identifier === BrowsersIdentifiers.settings || identifier === 'some-other-window') {
|
||||
return { show: mockShow };
|
||||
}
|
||||
return { show: mockShow }; // Default mock for other identifiers
|
||||
}),
|
||||
retrieveByIdentifier: mockRetrieveByIdentifier.mockImplementation(
|
||||
(identifier: AppBrowsersIdentifiers | string) => {
|
||||
if (identifier === BrowsersIdentifiers.settings || identifier === 'some-other-window') {
|
||||
return { show: mockShow };
|
||||
}
|
||||
return { show: mockShow }; // Default mock for other identifiers
|
||||
},
|
||||
),
|
||||
},
|
||||
} as unknown as App;
|
||||
|
||||
@@ -104,7 +106,11 @@ describe('BrowserWindowsCtr', () => {
|
||||
const baseParams = { source: 'link-click' as const };
|
||||
|
||||
it('should not intercept if no matching route is found', async () => {
|
||||
const params: InterceptRouteParams = { ...baseParams, path: '/unknown/route', url: 'app://host/unknown/route' };
|
||||
const params: InterceptRouteParams = {
|
||||
...baseParams,
|
||||
path: '/unknown/route',
|
||||
url: 'app://host/unknown/route',
|
||||
};
|
||||
(findMatchingRoute as Mock).mockReturnValue(undefined);
|
||||
const result = await browserWindowsCtr.interceptRoute(params);
|
||||
expect(findMatchingRoute).toHaveBeenCalledWith(params.path);
|
||||
@@ -112,7 +118,11 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
|
||||
it('should show settings window if matched route target is settings', async () => {
|
||||
const params: InterceptRouteParams = { ...baseParams, path: '/settings/common', url: 'app://host/settings/common' };
|
||||
const params: InterceptRouteParams = {
|
||||
...baseParams,
|
||||
path: '/settings?active=common',
|
||||
url: 'app://host/settings?active=common',
|
||||
};
|
||||
const matchedRoute = { targetWindow: BrowsersIdentifiers.settings, pathPrefix: '/settings' };
|
||||
const subPath = 'common';
|
||||
(findMatchingRoute as Mock).mockReturnValue(matchedRoute);
|
||||
@@ -134,7 +144,11 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
|
||||
it('should open target window if matched route target is not settings', async () => {
|
||||
const params: InterceptRouteParams = { ...baseParams, path: '/other/page', url: 'app://host/other/page' };
|
||||
const params: InterceptRouteParams = {
|
||||
...baseParams,
|
||||
path: '/other/page',
|
||||
url: 'app://host/other/page',
|
||||
};
|
||||
const targetWindowIdentifier = 'some-other-window' as AppBrowsersIdentifiers;
|
||||
const matchedRoute = { targetWindow: targetWindowIdentifier, pathPrefix: '/other' };
|
||||
(findMatchingRoute as Mock).mockReturnValue(matchedRoute);
|
||||
@@ -154,7 +168,11 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
|
||||
it('should return error if processing route interception fails for settings', async () => {
|
||||
const params: InterceptRouteParams = { ...baseParams, path: '/settings/general', url: 'app://host/settings/general' };
|
||||
const params: InterceptRouteParams = {
|
||||
...baseParams,
|
||||
path: '/settings?active=general',
|
||||
url: 'app://host/settings?active=general',
|
||||
};
|
||||
const matchedRoute = { targetWindow: BrowsersIdentifiers.settings, pathPrefix: '/settings' };
|
||||
const subPath = 'general';
|
||||
const errorMessage = 'Processing error for settings';
|
||||
@@ -173,7 +191,11 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
|
||||
it('should return error if processing route interception fails for other window', async () => {
|
||||
const params: InterceptRouteParams = { ...baseParams, path: '/another/custom', url: 'app://host/another/custom' };
|
||||
const params: InterceptRouteParams = {
|
||||
...baseParams,
|
||||
path: '/another/custom',
|
||||
url: 'app://host/another/custom',
|
||||
};
|
||||
const targetWindowIdentifier = 'another-custom-window' as AppBrowsersIdentifiers;
|
||||
const matchedRoute = { targetWindow: targetWindowIdentifier, pathPrefix: '/another' };
|
||||
const errorMessage = 'Processing error for other window';
|
||||
@@ -192,4 +214,4 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -336,7 +336,6 @@ export default class Browser {
|
||||
vibrancy: 'sidebar',
|
||||
visualEffectState: 'active',
|
||||
webPreferences: {
|
||||
backgroundThrottling: false,
|
||||
contextIsolation: true,
|
||||
preload: join(preloadDir, 'index.js'),
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WebContents } from 'electron';
|
||||
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import { AppBrowsersIdentifiers, appBrowsers } from '../../appBrowsers';
|
||||
import { AppBrowsersIdentifiers, appBrowsers, WindowTemplate, WindowTemplateIdentifiers, windowTemplates } from '../../appBrowsers';
|
||||
import type { App } from '../App';
|
||||
import type { BrowserWindowOpts } from './Browser';
|
||||
import Browser from './Browser';
|
||||
@@ -14,9 +14,9 @@ const logger = createLogger('core:BrowserManager');
|
||||
export class BrowserManager {
|
||||
app: App;
|
||||
|
||||
browsers: Map<AppBrowsersIdentifiers, Browser> = new Map();
|
||||
browsers: Map<string, Browser> = new Map();
|
||||
|
||||
private webContentsMap = new Map<WebContents, AppBrowsersIdentifiers>();
|
||||
private webContentsMap = new Map<WebContents, string>();
|
||||
|
||||
constructor(app: App) {
|
||||
logger.debug('Initializing BrowserManager');
|
||||
@@ -51,12 +51,12 @@ export class BrowserManager {
|
||||
};
|
||||
|
||||
broadcastToWindow = <T extends MainBroadcastEventKey>(
|
||||
identifier: AppBrowsersIdentifiers,
|
||||
identifier: string,
|
||||
event: T,
|
||||
data: MainBroadcastParams<T>,
|
||||
) => {
|
||||
logger.debug(`Broadcasting event ${event} to window: ${identifier}`);
|
||||
this.browsers.get(identifier).broadcast(event, data);
|
||||
this.browsers.get(identifier)?.broadcast(event, data);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -87,13 +87,21 @@ export class BrowserManager {
|
||||
* @param identifier Window identifier
|
||||
* @param subPath Sub-path, such as 'agent', 'about', etc.
|
||||
*/
|
||||
async redirectToPage(identifier: AppBrowsersIdentifiers, subPath?: string) {
|
||||
async redirectToPage(identifier: string, subPath?: string) {
|
||||
try {
|
||||
// Ensure window is retrieved or created
|
||||
const browser = this.retrieveByIdentifier(identifier);
|
||||
browser.hide();
|
||||
|
||||
const baseRoute = appBrowsers[identifier].path;
|
||||
// Handle both static and dynamic windows
|
||||
let baseRoute: string;
|
||||
if (identifier in appBrowsers) {
|
||||
baseRoute = appBrowsers[identifier as AppBrowsersIdentifiers].path;
|
||||
} else {
|
||||
// For dynamic windows, extract base route from the browser options
|
||||
const browserOptions = browser.options;
|
||||
baseRoute = browserOptions.path;
|
||||
}
|
||||
|
||||
// Build complete URL path
|
||||
const fullPath = subPath ? `${baseRoute}/${subPath}` : baseRoute;
|
||||
@@ -114,13 +122,75 @@ export class BrowserManager {
|
||||
/**
|
||||
* get Browser by identifier
|
||||
*/
|
||||
retrieveByIdentifier(identifier: AppBrowsersIdentifiers) {
|
||||
retrieveByIdentifier(identifier: string) {
|
||||
const browser = this.browsers.get(identifier);
|
||||
|
||||
if (browser) return browser;
|
||||
|
||||
logger.debug(`Browser ${identifier} not found, initializing new instance`);
|
||||
return this.retrieveOrInitialize(appBrowsers[identifier]);
|
||||
// Check if it's a static browser
|
||||
if (identifier in appBrowsers) {
|
||||
logger.debug(`Browser ${identifier} not found, initializing new instance`);
|
||||
return this.retrieveOrInitialize(appBrowsers[identifier as AppBrowsersIdentifiers]);
|
||||
}
|
||||
|
||||
throw new Error(`Browser ${identifier} not found and is not a static browser`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a multi-instance window from template
|
||||
* @param templateId Template identifier
|
||||
* @param path Full path with query parameters
|
||||
* @param uniqueId Optional unique identifier, will be generated if not provided
|
||||
* @returns The window identifier and Browser instance
|
||||
*/
|
||||
createMultiInstanceWindow(templateId: WindowTemplateIdentifiers, path: string, uniqueId?: string) {
|
||||
const template = windowTemplates[templateId];
|
||||
if (!template) {
|
||||
throw new Error(`Window template ${templateId} not found`);
|
||||
}
|
||||
|
||||
// Generate unique identifier
|
||||
const windowId = uniqueId || `${template.baseIdentifier}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
// Create browser options from template
|
||||
const browserOpts: BrowserWindowOpts = {
|
||||
...template,
|
||||
identifier: windowId,
|
||||
path: path,
|
||||
};
|
||||
|
||||
logger.debug(`Creating multi-instance window: ${windowId} with path: ${path}`);
|
||||
|
||||
const browser = this.retrieveOrInitialize(browserOpts);
|
||||
|
||||
return {
|
||||
identifier: windowId,
|
||||
browser: browser,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all windows based on template
|
||||
* @param templateId Template identifier
|
||||
* @returns Array of window identifiers matching the template
|
||||
*/
|
||||
getWindowsByTemplate(templateId: string): string[] {
|
||||
const prefix = `${templateId}_`;
|
||||
return Array.from(this.browsers.keys()).filter(id => id.startsWith(prefix));
|
||||
}
|
||||
|
||||
/**
|
||||
* Close all windows based on template
|
||||
* @param templateId Template identifier
|
||||
*/
|
||||
closeWindowsByTemplate(templateId: string): void {
|
||||
const windowIds = this.getWindowsByTemplate(templateId);
|
||||
windowIds.forEach(id => {
|
||||
const browser = this.browsers.get(id);
|
||||
if (browser) {
|
||||
browser.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +214,7 @@ export class BrowserManager {
|
||||
* @param options Browser window options
|
||||
*/
|
||||
private retrieveOrInitialize(options: BrowserWindowOpts) {
|
||||
let browser = this.browsers.get(options.identifier as AppBrowsersIdentifiers);
|
||||
let browser = this.browsers.get(options.identifier);
|
||||
if (browser) {
|
||||
logger.debug(`Retrieved existing browser: ${options.identifier}`);
|
||||
return browser;
|
||||
@@ -153,7 +223,7 @@ export class BrowserManager {
|
||||
logger.debug(`Creating new browser: ${options.identifier}`);
|
||||
browser = new Browser(options, this.app);
|
||||
|
||||
const identifier = options.identifier as AppBrowsersIdentifiers;
|
||||
const identifier = options.identifier;
|
||||
this.browsers.set(identifier, browser);
|
||||
|
||||
// 记录 WebContents 和 identifier 的映射
|
||||
@@ -166,32 +236,32 @@ export class BrowserManager {
|
||||
|
||||
browser.browserWindow.on('show', () => {
|
||||
if (browser.webContents)
|
||||
this.webContentsMap.set(browser.webContents, browser.identifier as AppBrowsersIdentifiers);
|
||||
this.webContentsMap.set(browser.webContents, browser.identifier);
|
||||
});
|
||||
|
||||
return browser;
|
||||
}
|
||||
|
||||
closeWindow(identifier: string) {
|
||||
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
|
||||
const browser = this.browsers.get(identifier);
|
||||
browser?.close();
|
||||
}
|
||||
|
||||
minimizeWindow(identifier: string) {
|
||||
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
|
||||
const browser = this.browsers.get(identifier);
|
||||
browser?.browserWindow.minimize();
|
||||
}
|
||||
|
||||
maximizeWindow(identifier: string) {
|
||||
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
|
||||
if (browser.browserWindow.isMaximized()) {
|
||||
const browser = this.browsers.get(identifier);
|
||||
if (browser?.browserWindow.isMaximized()) {
|
||||
browser?.browserWindow.unmaximize();
|
||||
} else {
|
||||
browser?.browserWindow.maximize();
|
||||
}
|
||||
}
|
||||
|
||||
getIdentifierByWebContents(webContents: WebContents): AppBrowsersIdentifiers | null {
|
||||
getIdentifierByWebContents(webContents: WebContents): string | null {
|
||||
return this.webContentsMap.get(webContents) || null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,519 @@
|
||||
[
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.6"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add promptfoo to improve prompts quality."]
|
||||
},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Type not preserved when model is sorted."]
|
||||
},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Allow switching model type."]
|
||||
},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-10-05",
|
||||
"version": "1.134.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support double-click to open multi agent window on the desktop."]
|
||||
},
|
||||
"date": "2025-10-04",
|
||||
"version": "1.134.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["type not preserved when model is disabled or sorted."],
|
||||
"improvements": ["Nano banana support aspect_ratio."]
|
||||
},
|
||||
"date": "2025-10-04",
|
||||
"version": "1.133.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Custom provider fails when client requests are enabled."],
|
||||
"improvements": ["Optimized extendParams UI, update i18n."]
|
||||
},
|
||||
"date": "2025-10-04",
|
||||
"version": "1.133.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["OllamaCloud error."],
|
||||
"improvements": ["Fix chat minimap overflow."]
|
||||
},
|
||||
"date": "2025-10-01",
|
||||
"version": "1.133.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor a ssrf-safe-fetch module."],
|
||||
"fixes": ["Fix frontend random API key config not work."]
|
||||
},
|
||||
"date": "2025-10-01",
|
||||
"version": "1.133.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add minimap to chat list for quick navigation."]
|
||||
},
|
||||
"date": "2025-09-30",
|
||||
"version": "1.133.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-30",
|
||||
"version": "1.133.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add builtin Python plugin, add Claude Sonnet 4.5 model to AI chat models."]
|
||||
},
|
||||
"date": "2025-09-29",
|
||||
"version": "1.133.0"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-29",
|
||||
"version": "1.132.19"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Refactor tools-engine and fix search token count."],
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-28",
|
||||
"version": "1.132.18"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix input empty group name."]
|
||||
},
|
||||
"date": "2025-09-27",
|
||||
"version": "1.132.17"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Resolve qwen-image-edit imageUrls conversion issue."]
|
||||
},
|
||||
"date": "2025-09-26",
|
||||
"version": "1.132.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Add proxyUrl configuration for NEW API provider."]
|
||||
},
|
||||
"date": "2025-09-25",
|
||||
"version": "1.132.15"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-25",
|
||||
"version": "1.132.14"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-25",
|
||||
"version": "1.132.13"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Slove setting proxy page with style error."]
|
||||
},
|
||||
"date": "2025-09-25",
|
||||
"version": "1.132.12"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": [
|
||||
"Enhanced Nvidia NIM chat experience, OpenAI models in AiHubMix use Responses API."
|
||||
]
|
||||
},
|
||||
"date": "2025-09-24",
|
||||
"version": "1.132.11"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Macos desktop sign."]
|
||||
},
|
||||
"date": "2025-09-24",
|
||||
"version": "1.132.10"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-23",
|
||||
"version": "1.132.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor all @/types in model runtime to @lobechat/types."]
|
||||
},
|
||||
"date": "2025-09-23",
|
||||
"version": "1.132.8"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-23",
|
||||
"version": "1.132.7"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-23",
|
||||
"version": "1.132.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Move the ModelProvider to model-bank."]
|
||||
},
|
||||
"date": "2025-09-22",
|
||||
"version": "1.132.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Enable thinkingBudget control for Vertex Gemini 2.5 models, update i18n."]
|
||||
},
|
||||
"date": "2025-09-22",
|
||||
"version": "1.132.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Added AUTH_MICROSOFT_ENTRA_ID_BASE_URL routing."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.132.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix non stream mode in OpenAI Response API."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.132.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix missing provider in server message."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.132.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support google video understanding."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.132.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Enhanced AkashChat experience."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.131.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Update Responses search tool to web_search."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.131.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Use ID as name if provider name is empty."]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.131.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": [
|
||||
"Extend custom provider runtime options, Optimized modelFetch for Vercel AI Gateway, update i18n."
|
||||
]
|
||||
},
|
||||
"date": "2025-09-21",
|
||||
"version": "1.131.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Qwen provider add qwen-image-edit model support."]
|
||||
},
|
||||
"date": "2025-09-19",
|
||||
"version": "1.131.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix oidc open direct issue."]
|
||||
},
|
||||
"date": "2025-09-18",
|
||||
"version": "1.130.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add scroll support for pinned assistants using ScrollShadow."]
|
||||
},
|
||||
"date": "2025-09-18",
|
||||
"version": "1.130.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix svg xss issue."]
|
||||
},
|
||||
"date": "2025-09-18",
|
||||
"version": "1.129.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Add qwen provider support for image-edit model."]
|
||||
},
|
||||
"date": "2025-09-17",
|
||||
"version": "1.129.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Improve db migrations sql."],
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-17",
|
||||
"version": "1.129.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update SiliconCloud reasoning models."]
|
||||
},
|
||||
"date": "2025-09-16",
|
||||
"version": "1.129.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support Vercel AI Gateway provider."]
|
||||
},
|
||||
"date": "2025-09-16",
|
||||
"version": "1.129.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix azure ai runtime error."]
|
||||
},
|
||||
"date": "2025-09-16",
|
||||
"version": "1.128.10"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve error handle with agent config, support .doc file parse."]
|
||||
},
|
||||
"date": "2025-09-15",
|
||||
"version": "1.128.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": [
|
||||
"Enable toggling search on/off via search button click & historyCount button."
|
||||
]
|
||||
},
|
||||
"date": "2025-09-15",
|
||||
"version": "1.128.8"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-14",
|
||||
"version": "1.128.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-14",
|
||||
"version": "1.128.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Google stream error unable to abort request."]
|
||||
},
|
||||
"date": "2025-09-13",
|
||||
"version": "1.128.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Fix discover plugin link."]
|
||||
},
|
||||
"date": "2025-09-13",
|
||||
"version": "1.128.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix open chat page with float link modal."]
|
||||
},
|
||||
"date": "2025-09-13",
|
||||
"version": "1.128.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n, Update model configs."]
|
||||
},
|
||||
"date": "2025-09-13",
|
||||
"version": "1.128.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor message proccesser to the context engine."]
|
||||
},
|
||||
"date": "2025-09-12",
|
||||
"version": "1.128.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["ChatInput support resize."]
|
||||
},
|
||||
"date": "2025-09-12",
|
||||
"version": "1.128.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Improve OpenAIStream processing to emit usage data for chunks lacking choices."]
|
||||
},
|
||||
"date": "2025-09-11",
|
||||
"version": "1.127.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor model runtime folder structure and add more tests."]
|
||||
},
|
||||
"date": "2025-09-11",
|
||||
"version": "1.127.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Delete files should delete chunks、embedings、fileChunk."]
|
||||
},
|
||||
"date": "2025-09-11",
|
||||
"version": "1.127.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix not remove message with server mode."],
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-09-11",
|
||||
"version": "1.127.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Seedream 4.0."],
|
||||
"improvements": ["Add hotkey tooltip to typobar actions."]
|
||||
},
|
||||
"date": "2025-09-10",
|
||||
"version": "1.127.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add CometAPI model provider and chat models, update i18n."]
|
||||
},
|
||||
"date": "2025-09-10",
|
||||
"version": "1.126.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix editor key handling."]
|
||||
},
|
||||
"date": "2025-09-09",
|
||||
"version": "1.126.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix Assistant List error message."]
|
||||
},
|
||||
"date": "2025-09-09",
|
||||
"version": "1.126.1"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-09-08",
|
||||
"version": "1.126.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add Math and TaskList to Editor."]
|
||||
},
|
||||
"date": "2025-09-08",
|
||||
"version": "1.125.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Revert V1 Mobile."]
|
||||
},
|
||||
"date": "2025-09-06",
|
||||
"version": "1.124.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor to remove edge runtime and add more tests."]
|
||||
},
|
||||
"date": "2025-09-06",
|
||||
"version": "1.124.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix ChatInput send command switch."]
|
||||
},
|
||||
"date": "2025-09-06",
|
||||
"version": "1.124.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Enhance NewAPI with environment variables and fix routers compatibility."],
|
||||
"improvements": ["Update doubao-seed-1.6-vision models."]
|
||||
},
|
||||
"date": "2025-09-06",
|
||||
"version": "1.124.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["ChatInput support rich text and support parallel send."]
|
||||
},
|
||||
"date": "2025-09-06",
|
||||
"version": "1.124.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Remove edge runtime."]
|
||||
},
|
||||
"date": "2025-09-05",
|
||||
"version": "1.123.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix mobile header title to loog not ellipsis."]
|
||||
},
|
||||
"date": "2025-09-05",
|
||||
"version": "1.123.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Not use branch topic when this topic is not save."]
|
||||
|
||||
@@ -8,6 +8,9 @@ services:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
@@ -29,11 +32,52 @@ services:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: searxng
|
||||
|
||||
grafana:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: grafana
|
||||
|
||||
tempo:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: tempo
|
||||
|
||||
prometheus:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: prometheus
|
||||
|
||||
otel-collector:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: otel-collector
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: otel-tracing-test
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
|
||||
@@ -9,6 +9,9 @@ services:
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
@@ -58,7 +61,7 @@ services:
|
||||
wait \$MINIO_PID
|
||||
"
|
||||
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
casdoor:
|
||||
image: casbin/casdoor:v2.13.0
|
||||
container_name: lobe-casdoor
|
||||
@@ -162,11 +165,90 @@ services:
|
||||
wait \$LOBE_PID
|
||||
"
|
||||
|
||||
grafana:
|
||||
profiles:
|
||||
- otel
|
||||
image: grafana/grafana:12.2.0-17419259409
|
||||
container_name: lobe-grafana
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||
- GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
tempo:
|
||||
profiles:
|
||||
- otel
|
||||
image: grafana/tempo:latest
|
||||
container_name: lobe-tempo
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./tempo/tempo.yaml:/etc/tempo.yaml
|
||||
- tempo_data:/var/tempo
|
||||
command: ['-config.file=/etc/tempo.yaml']
|
||||
|
||||
prometheus:
|
||||
profiles:
|
||||
- otel
|
||||
image: prom/prometheus
|
||||
container_name: lobe-prometheus
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--web.enable-otlp-receiver'
|
||||
- '--web.enable-remote-write-receiver'
|
||||
- '--enable-feature=exemplar-storage'
|
||||
|
||||
otel-collector:
|
||||
profiles:
|
||||
- otel
|
||||
image: otel/opentelemetry-collector
|
||||
container_name: lobe-otel-collector
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./otel-collector/collector-config.yaml:/etc/otelcol/config.yaml
|
||||
command: ['--config', '/etc/otelcol/config.yaml']
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
|
||||
container_name: lobe-otel-tracing-test
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- ENDPOINT=127.0.0.1:4317
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# Proxy, if you need it
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# Other environment variables, as needed. You can refer to the environment variables configuration for the client version, making sure not to have ACCESS_CODE.
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===========================
|
||||
# ====== Preset config ======
|
||||
# ===========================
|
||||
# if no special requirements, no need to change
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 configuration
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# Configure the bucket information of MinIO
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# Configure for casdoor
|
||||
origin=http://localhost:8000
|
||||
@@ -0,0 +1,42 @@
|
||||
# Proxy,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===================
|
||||
# ===== 预设配置 =====
|
||||
# ===================
|
||||
# 如没有特殊需要不用更改
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 配置
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# 在下方配置 minio 中添加的桶
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# 为 casdoor 配置
|
||||
origin=http://localhost:8000
|
||||
@@ -0,0 +1,251 @@
|
||||
name: lobe-chat-database
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg17
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=*'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
minio server /etc/minio/data --address ':${MINIO_PORT}' --console-address ':9001' &
|
||||
MINIO_PID=\$!
|
||||
while ! curl -s http://localhost:${MINIO_PORT}/minio/health/live; do
|
||||
echo 'Waiting for MinIO to start...'
|
||||
sleep 1
|
||||
done
|
||||
sleep 5
|
||||
mc alias set myminio http://localhost:${MINIO_PORT} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}
|
||||
echo 'Creating bucket ${MINIO_LOBE_BUCKET}'
|
||||
mc mb myminio/${MINIO_LOBE_BUCKET}
|
||||
wait \$MINIO_PID
|
||||
"
|
||||
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
casdoor:
|
||||
image: casbin/casdoor:v2.13.0
|
||||
container_name: lobe-casdoor
|
||||
entrypoint: /bin/sh -c './server --createDatabase=true'
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
httpport: ${CASDOOR_PORT}
|
||||
RUNNING_IN_DOCKER: 'true'
|
||||
driverName: 'postgres'
|
||||
dataSourceName: 'user=postgres password=${POSTGRES_PASSWORD} host=postgresql port=5432 sslmode=disable dbname=casdoor'
|
||||
runmode: 'dev'
|
||||
volumes:
|
||||
- ./init_data.json:/init_data.json
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
searxng:
|
||||
image: searxng/searxng
|
||||
container_name: lobe-searxng
|
||||
volumes:
|
||||
- './searxng-settings.yml:/etc/searxng/settings.yml'
|
||||
environment:
|
||||
- 'SEARXNG_SETTINGS_FILE=/etc/searxng/settings.yml'
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:12.2.0-17419259409
|
||||
container_name: lobe-grafana
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||
- GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:latest
|
||||
container_name: lobe-tempo
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./tempo/tempo.yaml:/etc/tempo.yaml
|
||||
- tempo_data:/var/tempo
|
||||
command: ['-config.file=/etc/tempo.yaml']
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
container_name: lobe-prometheus
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--web.enable-otlp-receiver'
|
||||
- '--web.enable-remote-write-receiver'
|
||||
- '--enable-feature=exemplar-storage'
|
||||
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector
|
||||
container_name: lobe-otel-collector
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./otel-collector/collector-config.yaml:/etc/otelcol/config.yaml
|
||||
command: ['--config', '/etc/otelcol/config.yaml']
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
|
||||
container_name: lobe-otel-tracing-test
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- ENDPOINT=127.0.0.1:4317
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobe-chat-database
|
||||
container_name: lobe-chat
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
minio:
|
||||
condition: service_started
|
||||
casdoor:
|
||||
condition: service_started
|
||||
|
||||
environment:
|
||||
- 'NEXT_AUTH_SSO_PROVIDERS=casdoor'
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'NEXT_AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
- 'S3_ACCESS_KEY=${MINIO_ROOT_USER}'
|
||||
- 'S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}'
|
||||
- 'S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}'
|
||||
- 'LLM_VISION_IMAGE_USE_BASE64=1'
|
||||
- 'S3_SET_ACL=0'
|
||||
- 'SEARXNG_URL=http://searxng:8080'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
/bin/node /app/startServer.js &
|
||||
LOBE_PID=\$!
|
||||
sleep 3
|
||||
if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch OIDC configuration from Casdoor'
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
else
|
||||
if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
|
||||
printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
printf '❌错误:Auth 的 issuer 冲突,OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
fi
|
||||
if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/minio/health/live 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch MinIO health status'
|
||||
echo 'Request URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法获取 MinIO 健康状态'
|
||||
echo '请求 URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
wait \$LOBE_PID
|
||||
"
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: 1
|
||||
|
||||
prune: true
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
uid: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:9090
|
||||
basicAuth: false
|
||||
isDefault: false
|
||||
version: 1
|
||||
editable: false
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: 1
|
||||
|
||||
prune: true
|
||||
|
||||
datasources:
|
||||
- name: Tempo
|
||||
type: tempo
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:3200
|
||||
basicAuth: false
|
||||
isDefault: true
|
||||
version: 1
|
||||
editable: false
|
||||
apiVersion: 1
|
||||
uid: tempo
|
||||
@@ -0,0 +1,45 @@
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 127.0.0.1:13133
|
||||
|
||||
receivers:
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-metrics
|
||||
scrape_interval: 10s
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:8888"]
|
||||
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
|
||||
exporters:
|
||||
prometheusremotewrite:
|
||||
endpoint: http://127.0.0.1:9090/api/v1/write
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
otlp:
|
||||
endpoint: 127.0.0.1:14317
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [prometheus, otlp]
|
||||
exporters: [prometheusremotewrite]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
@@ -0,0 +1,11 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9090"]
|
||||
- job_name: "tempo"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:3200"]
|
||||
@@ -0,0 +1,58 @@
|
||||
stream_over_http_enabled: true
|
||||
server:
|
||||
http_listen_port: 3200
|
||||
log_level: info
|
||||
|
||||
query_frontend:
|
||||
search:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
metadata_slo:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
trace_by_id:
|
||||
duration_slo: 5s
|
||||
|
||||
distributor:
|
||||
max_attribute_bytes: 10485760
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 127.0.0.1:14317
|
||||
http:
|
||||
endpoint: 127.0.0.1:14318
|
||||
|
||||
ingester:
|
||||
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
|
||||
|
||||
compactor:
|
||||
compaction:
|
||||
block_retention: 1h # overall Tempo trace retention. set for demo purposes
|
||||
|
||||
metrics_generator:
|
||||
registry:
|
||||
external_labels:
|
||||
source: tempo
|
||||
cluster: docker-compose
|
||||
storage:
|
||||
path: /var/tempo/generator/wal
|
||||
remote_write:
|
||||
- url: http://127.0.0.1:9090/api/v1/write
|
||||
send_exemplars: true
|
||||
traces_storage:
|
||||
path: /var/tempo/generator/traces
|
||||
|
||||
storage:
|
||||
trace:
|
||||
backend: local # backend configuration to use
|
||||
wal:
|
||||
path: /var/tempo/wal # where to store the wal locally
|
||||
local:
|
||||
path: /var/tempo/blocks
|
||||
|
||||
overrides:
|
||||
defaults:
|
||||
metrics_generator:
|
||||
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
|
||||
generate_native_histograms: both
|
||||
@@ -0,0 +1,44 @@
|
||||
# Proxy, if you need it
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# Other environment variables, as needed. You can refer to the environment variables configuration for the client version, making sure not to have ACCESS_CODE.
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===========================
|
||||
# ====== Preset config ======
|
||||
# ===========================
|
||||
# if no special requirements, no need to change
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 configuration
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# Configure the bucket information of MinIO
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
GF_SECURITY_ADMIN_PASSWORD=YOUR_GRAFANA_PASSWORD
|
||||
|
||||
# Configure for casdoor
|
||||
origin=http://localhost:8000
|
||||
@@ -0,0 +1,42 @@
|
||||
# Proxy,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===================
|
||||
# ===== 预设配置 =====
|
||||
# ===================
|
||||
# 如没有特殊需要不用更改
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 配置
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# 在下方配置 minio 中添加的桶
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# 为 casdoor 配置
|
||||
origin=http://localhost:8000
|
||||
@@ -0,0 +1,249 @@
|
||||
name: lobe-chat-database
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg17
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=*'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
minio server /etc/minio/data --address ':${MINIO_PORT}' --console-address ':9001' &
|
||||
MINIO_PID=\$!
|
||||
while ! curl -s http://localhost:${MINIO_PORT}/minio/health/live; do
|
||||
echo 'Waiting for MinIO to start...'
|
||||
sleep 1
|
||||
done
|
||||
sleep 5
|
||||
mc alias set myminio http://localhost:${MINIO_PORT} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}
|
||||
echo 'Creating bucket ${MINIO_LOBE_BUCKET}'
|
||||
mc mb myminio/${MINIO_LOBE_BUCKET}
|
||||
wait \$MINIO_PID
|
||||
"
|
||||
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
casdoor:
|
||||
image: casbin/casdoor:v2.13.0
|
||||
container_name: lobe-casdoor
|
||||
entrypoint: /bin/sh -c './server --createDatabase=true'
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
httpport: ${CASDOOR_PORT}
|
||||
RUNNING_IN_DOCKER: 'true'
|
||||
driverName: 'postgres'
|
||||
dataSourceName: 'user=postgres password=${POSTGRES_PASSWORD} host=postgresql port=5432 sslmode=disable dbname=casdoor'
|
||||
runmode: 'dev'
|
||||
volumes:
|
||||
- ./init_data.json:/init_data.json
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
searxng:
|
||||
image: searxng/searxng
|
||||
container_name: lobe-searxng
|
||||
volumes:
|
||||
- './searxng-settings.yml:/etc/searxng/settings.yml'
|
||||
environment:
|
||||
- 'SEARXNG_SETTINGS_FILE=/etc/searxng/settings.yml'
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:12.2.0-17419259409
|
||||
container_name: lobe-grafana
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD}
|
||||
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:latest
|
||||
container_name: lobe-tempo
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./tempo/tempo.yaml:/etc/tempo.yaml
|
||||
- tempo_data:/var/tempo
|
||||
command: ['-config.file=/etc/tempo.yaml']
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
container_name: lobe-prometheus
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--web.enable-otlp-receiver'
|
||||
- '--web.enable-remote-write-receiver'
|
||||
- '--enable-feature=exemplar-storage'
|
||||
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector
|
||||
container_name: lobe-otel-collector
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./otel-collector/collector-config.yaml:/etc/otelcol/config.yaml
|
||||
command: ['--config', '/etc/otelcol/config.yaml']
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
|
||||
container_name: lobe-otel-tracing-test
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- ENDPOINT=127.0.0.1:4317
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobe-chat-database
|
||||
container_name: lobe-chat
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
minio:
|
||||
condition: service_started
|
||||
casdoor:
|
||||
condition: service_started
|
||||
|
||||
environment:
|
||||
- 'NEXT_AUTH_SSO_PROVIDERS=casdoor'
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'NEXT_AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
- 'S3_ACCESS_KEY=${MINIO_ROOT_USER}'
|
||||
- 'S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}'
|
||||
- 'S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}'
|
||||
- 'LLM_VISION_IMAGE_USE_BASE64=1'
|
||||
- 'S3_SET_ACL=0'
|
||||
- 'SEARXNG_URL=http://searxng:8080'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
/bin/node /app/startServer.js &
|
||||
LOBE_PID=\$!
|
||||
sleep 3
|
||||
if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch OIDC configuration from Casdoor'
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
else
|
||||
if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
|
||||
printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
printf '❌错误:Auth 的 issuer 冲突,OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
fi
|
||||
if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/minio/health/live 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch MinIO health status'
|
||||
echo 'Request URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法获取 MinIO 健康状态'
|
||||
echo '请求 URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
wait \$LOBE_PID
|
||||
"
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
uid: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:9090
|
||||
basicAuth: false
|
||||
isDefault: false
|
||||
version: 1
|
||||
editable: false
|
||||
jsonData:
|
||||
httpMethod: GET
|
||||
@@ -0,0 +1,20 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Tempo
|
||||
type: tempo
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:3200
|
||||
basicAuth: false
|
||||
isDefault: true
|
||||
version: 1
|
||||
editable: false
|
||||
apiVersion: 1
|
||||
uid: tempo
|
||||
jsonData:
|
||||
httpMethod: GET
|
||||
serviceMap:
|
||||
datasourceUid: prometheus
|
||||
streamingEnabled:
|
||||
search: true
|
||||
@@ -0,0 +1,45 @@
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 127.0.0.1:13133
|
||||
|
||||
receivers:
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-metrics
|
||||
scrape_interval: 10s
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:8888"]
|
||||
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
|
||||
exporters:
|
||||
prometheusremotewrite:
|
||||
endpoint: http://127.0.0.1:9090/api/v1/write
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
otlp:
|
||||
endpoint: 127.0.0.1:14317
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [prometheus]
|
||||
exporters: [prometheusremotewrite]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
@@ -0,0 +1,11 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9090"]
|
||||
- job_name: "tempo"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:3200"]
|
||||
@@ -0,0 +1,58 @@
|
||||
stream_over_http_enabled: true
|
||||
server:
|
||||
http_listen_port: 3200
|
||||
log_level: info
|
||||
|
||||
query_frontend:
|
||||
search:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
metadata_slo:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
trace_by_id:
|
||||
duration_slo: 5s
|
||||
|
||||
distributor:
|
||||
max_attribute_bytes: 10485760
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 127.0.0.1:14317
|
||||
http:
|
||||
endpoint: 127.0.0.1:14318
|
||||
|
||||
ingester:
|
||||
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
|
||||
|
||||
compactor:
|
||||
compaction:
|
||||
block_retention: 1h # overall Tempo trace retention. set for demo purposes
|
||||
|
||||
metrics_generator:
|
||||
registry:
|
||||
external_labels:
|
||||
source: tempo
|
||||
cluster: docker-compose
|
||||
storage:
|
||||
path: /var/tempo/generator/wal
|
||||
remote_write:
|
||||
- url: http://127.0.0.1:9090/api/v1/write
|
||||
send_exemplars: true
|
||||
traces_storage:
|
||||
path: /var/tempo/generator/traces
|
||||
|
||||
storage:
|
||||
trace:
|
||||
backend: local # backend configuration to use
|
||||
wal:
|
||||
path: /var/tempo/wal # where to store the wal locally
|
||||
local:
|
||||
path: /var/tempo/blocks
|
||||
|
||||
overrides:
|
||||
defaults:
|
||||
metrics_generator:
|
||||
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
|
||||
generate_native_histograms: both
|
||||
@@ -42,7 +42,7 @@ export type ChatMessage = {
|
||||
|
||||
```ts
|
||||
// src/store/chatStore.ts
|
||||
import create from 'zustand';
|
||||
import { create } from 'zustand';
|
||||
|
||||
type ChatState = {
|
||||
messages: ChatMessage[];
|
||||
|
||||
@@ -4,37 +4,88 @@ The directory structure of LobeChat is as follows:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # Main logic and state management related code for the application
|
||||
├── app # Next.js App Router implementation with route groups and API routes
|
||||
├── components # Reusable UI components
|
||||
├── config # Application configuration files, including client-side and server-side environment variables
|
||||
├── const # Used to define constants, such as action types, route names, etc.
|
||||
├── features # Function modules related to business functions, such as agent settings, plugin development pop-ups, etc.
|
||||
├── hooks # Custom utility hooks reused throughout the application
|
||||
├── layout # Application layout components, such as navigation bars, sidebars, etc.
|
||||
├── libs # Third-party integrations (analytics, OIDC, etc.)
|
||||
├── locales # Internationalization language files
|
||||
├── server # Server-side modules and services
|
||||
├── services # Encapsulated backend service interfaces, such as HTTP requests
|
||||
├── store # Zustand store for state management
|
||||
├── styles # Global styles and CSS-in-JS configurations
|
||||
├── types # TypeScript type definition files
|
||||
└── utils # Common utility functions
|
||||
```
|
||||
|
||||
## app
|
||||
|
||||
In the `app` folder, we organize each route page according to the app router's [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to separately handle the implementation of desktop and mobile code. Taking the file structure of the `welcome` page as an example:
|
||||
The `app` directory follows Next.js 13+ App Router conventions with a sophisticated architecture using [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to organize backend services, platform variants, and application routes:
|
||||
|
||||
```bash
|
||||
welcome
|
||||
├── (desktop) # Desktop implementation
|
||||
│ ├── features # Desktop-specific features
|
||||
│ ├── index.tsx # Main entry file for desktop
|
||||
│ └── layout.desktop.tsx # Desktop layout component
|
||||
├── (mobile) # Mobile implementation
|
||||
│ ├── features # Mobile-specific features
|
||||
│ ├── index.tsx # Main entry file for mobile
|
||||
│ └── layout.mobile.tsx # Mobile layout component
|
||||
├── features # This folder contains features code shared by both desktop and mobile, such as the Banner component
|
||||
│ └── Banner
|
||||
└── page.tsx # This is the main entry file for the page, used to load desktop or mobile code based on the device type
|
||||
app
|
||||
├── (backend)/ # Backend API routes and services
|
||||
│ ├── api/ # REST API endpoints
|
||||
│ │ ├── auth/ # Authentication routes
|
||||
│ │ └── webhooks/ # Webhook handlers
|
||||
│ ├── middleware/ # Request middleware
|
||||
│ ├── oidc/ # OpenID Connect routes
|
||||
│ ├── trpc/ # tRPC API endpoints
|
||||
│ │ ├── async/ # Async tRPC routes
|
||||
│ │ ├── desktop/ # Desktop-specific tRPC routes
|
||||
│ │ ├── edge/ # Edge runtime tRPC routes
|
||||
│ │ ├── lambda/ # Lambda tRPC routes
|
||||
│ │ └── tools/ # Tools tRPC routes
|
||||
│ └── webapi/ # Web API endpoints
|
||||
│ ├── chat/ # Chat-related APIs
|
||||
│ ├── models/ # Model management APIs
|
||||
│ ├── tts/ # Text-to-speech APIs
|
||||
│ └── ...
|
||||
├── [variants]/ # Platform and device variants
|
||||
│ ├── (auth)/ # Authentication pages
|
||||
│ │ ├── login/
|
||||
│ │ ├── signup/
|
||||
│ │ └── next-auth/
|
||||
│ ├── (main)/ # Main application routes
|
||||
│ │ ├── (mobile)/ # Mobile-specific routes
|
||||
│ │ │ └── me/ # Mobile profile pages
|
||||
│ │ ├── _layout/ # Layout components
|
||||
│ │ ├── chat/ # Chat interface
|
||||
│ │ ├── discover/ # Discovery pages
|
||||
│ │ ├── files/ # File management
|
||||
│ │ ├── image/ # Image generation
|
||||
│ │ ├── profile/ # User profile
|
||||
│ │ ├── repos/ # Repository management
|
||||
│ │ └── settings/ # Application settings
|
||||
│ └── @modal/ # Parallel modal routes
|
||||
│ ├── (.)changelog/
|
||||
│ └── _layout/
|
||||
├── desktop/ # Desktop-specific routes
|
||||
│ └── devtools/
|
||||
├── manifest.ts # PWA manifest
|
||||
├── robots.tsx # Robots.txt generation
|
||||
├── sitemap.tsx # Sitemap generation
|
||||
└── sw.ts # Service worker
|
||||
```
|
||||
|
||||
In this way, we can clearly distinguish and manage desktop and mobile code, while also easily reusing code required on both devices, thereby improving development efficiency and maintaining code cleanliness and maintainability.
|
||||
### Architecture Explanation
|
||||
|
||||
**Route Groups:**
|
||||
- `(backend)` - Contains all server-side API routes, middleware, and backend services
|
||||
- `[variants]` - Dynamic route group handling different platform variants and main application pages
|
||||
- `@modal` - Parallel routes for modal dialogs using Next.js parallel routing
|
||||
|
||||
**Platform Organization:**
|
||||
- The architecture supports multiple platforms (web, desktop, mobile) through route organization
|
||||
- Desktop-specific routes are in the `desktop/` directory
|
||||
- Mobile-specific routes are organized under `(main)/(mobile)/`
|
||||
- Shared layouts and components are in `_layout/` directories
|
||||
|
||||
**API Architecture:**
|
||||
- REST APIs in `(backend)/api/` and `(backend)/webapi/`
|
||||
- tRPC endpoints organized by runtime environment (edge, lambda, async, desktop)
|
||||
- Authentication and OIDC handling in dedicated route groups
|
||||
|
||||
This architecture provides clear separation of concerns while maintaining flexibility for different deployment targets and runtime environments.
|
||||
|
||||
@@ -4,37 +4,88 @@ LobeChat 的文件夹目录架构如下:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # 应用主要逻辑和状态管理相关的代码
|
||||
├── app # Next.js App Router 实现,包含路由组和 API 路由
|
||||
├── components # 可复用的 UI 组件
|
||||
├── config # 应用的配置文件,包含客户端环境变量与服务端环境变量
|
||||
├── const # 用于定义常量,如 action 类型、路由名等
|
||||
├── features # 与业务功能相关的功能模块,如 Agent 设置、插件开发弹窗等
|
||||
├── hooks # 全应用复用自定义的工具 Hooks
|
||||
├── layout # 应用的布局组件,如导航栏、侧边栏等
|
||||
├── libs # 第三方集成(分析、OIDC 等)
|
||||
├── locales # 国际化的语言文件
|
||||
├── server # 服务端模块和服务
|
||||
├── services # 封装的后端服务接口,如 HTTP 请求
|
||||
├── store # 用于状态管理的 zustand store
|
||||
├── styles # 全局样式和 CSS-in-JS 配置
|
||||
├── types # TypeScript 的类型定义文件
|
||||
└── utils # 通用的工具函数
|
||||
```
|
||||
|
||||
## app
|
||||
|
||||
在 `app` 文件夹中,我们将每个路由页面按照 app router 的 [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 进行组织,以此来分别处理桌面端和移动端的代码实现。以 `welcome` 页面的文件结构为例:
|
||||
`app` 目录遵循 Next.js 13+ App Router 约定,采用复杂的架构,使用 [路由组](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 来组织后端服务、平台变体和应用路由:
|
||||
|
||||
```bash
|
||||
welcome
|
||||
├── (desktop) # 桌面端实现
|
||||
│ ├── features # 桌面端特有的功能
|
||||
│ ├── index.tsx # 桌面端的主入口文件
|
||||
│ └── layout.desktop.tsx # 桌面端的布局组件
|
||||
├── (mobile) # 移动端实现
|
||||
│ ├── features # 移动端特有的功能
|
||||
│ ├── index.tsx # 移动端的主入口文件
|
||||
│ └── layout.mobile.tsx # 移动端的布局组件
|
||||
├── features # 此文件夹包含双端共享的特性代码,如 Banner 组件
|
||||
│ └── Banner
|
||||
└── page.tsx # 此为页面的主入口文件,用于根据设备类型选择加载桌面端或移动端的代码
|
||||
app
|
||||
├── (backend)/ # 后端 API 路由和服务
|
||||
│ ├── api/ # REST API 端点
|
||||
│ │ ├── auth/ # 身份验证路由
|
||||
│ │ └── webhooks/ # Webhook 处理器
|
||||
│ ├── middleware/ # 请求中间件
|
||||
│ ├── oidc/ # OpenID Connect 路由
|
||||
│ ├── trpc/ # tRPC API 端点
|
||||
│ │ ├── async/ # 异步 tRPC 路由
|
||||
│ │ ├── desktop/ # 桌面端专用 tRPC 路由
|
||||
│ │ ├── edge/ # Edge 运行时 tRPC 路由
|
||||
│ │ ├── lambda/ # Lambda tRPC 路由
|
||||
│ │ └── tools/ # 工具 tRPC 路由
|
||||
│ └── webapi/ # Web API 端点
|
||||
│ ├── chat/ # 聊天相关 API
|
||||
│ ├── models/ # 模型管理 API
|
||||
│ ├── tts/ # 文本转语音 API
|
||||
│ └── ...
|
||||
├── [variants]/ # 平台和设备变体
|
||||
│ ├── (auth)/ # 身份验证页面
|
||||
│ │ ├── login/
|
||||
│ │ ├── signup/
|
||||
│ │ └── next-auth/
|
||||
│ ├── (main)/ # 主应用路由
|
||||
│ │ ├── (mobile)/ # 移动端专用路由
|
||||
│ │ │ └── me/ # 移动端个人资料页面
|
||||
│ │ ├── _layout/ # 布局组件
|
||||
│ │ ├── chat/ # 聊天界面
|
||||
│ │ ├── discover/ # 发现页面
|
||||
│ │ ├── files/ # 文件管理
|
||||
│ │ ├── image/ # 图像生成
|
||||
│ │ ├── profile/ # 用户资料
|
||||
│ │ ├── repos/ # 仓库管理
|
||||
│ │ └── settings/ # 应用设置
|
||||
│ └── @modal/ # 并行模态框路由
|
||||
│ ├── (.)changelog/
|
||||
│ └── _layout/
|
||||
├── desktop/ # 桌面端专用路由
|
||||
│ └── devtools/
|
||||
├── manifest.ts # PWA 清单
|
||||
├── robots.tsx # Robots.txt 生成
|
||||
├── sitemap.tsx # 站点地图生成
|
||||
└── sw.ts # Service Worker
|
||||
```
|
||||
|
||||
通过这种方式,我们可以清晰地区分和管理桌面端和移动端的代码,同时也能方便地复用在两种设备上都需要的代码,从而提高开发效率并保持代码的整洁和可维护性。
|
||||
### 架构说明
|
||||
|
||||
**路由组:**
|
||||
- `(backend)` - 包含所有服务端 API 路由、中间件和后端服务
|
||||
- `[variants]` - 处理不同平台变体和主应用页面的动态路由组
|
||||
- `@modal` - 使用 Next.js 并行路由的模态框对话框并行路由
|
||||
|
||||
**平台组织:**
|
||||
- 架构通过路由组织支持多个平台(Web、桌面端、移动端)
|
||||
- 桌面端专用路由位于 `desktop/` 目录中
|
||||
- 移动端专用路由组织在 `(main)/(mobile)/` 下
|
||||
- 共享布局和组件位于 `_layout/` 目录中
|
||||
|
||||
**API 架构:**
|
||||
- `(backend)/api/` 和 `(backend)/webapi/` 中的 REST API
|
||||
- 按运行时环境组织的 tRPC 端点(edge、lambda、async、desktop)
|
||||
- 专用路由组中的身份验证和 OIDC 处理
|
||||
|
||||
这种架构在保持不同部署目标和运行时环境灵活性的同时,提供了清晰的关注点分离。
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
---
|
||||
title: Image Generation Development Setup
|
||||
description: Configure local environment for developing text-to-image and image-to-image features
|
||||
---
|
||||
|
||||
# Image Generation Development Setup
|
||||
|
||||
This guide helps developers set up the local environment for developing image generation features (text-to-image, image-to-image) with file storage capabilities.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker installed and running
|
||||
- Node.js and pnpm installed
|
||||
- PostgreSQL client tools (optional, for debugging)
|
||||
|
||||
<Callout type="warning">
|
||||
**Security Notice**: This setup is designed for local development only. It uses default credentials and open permissions that are NOT suitable for production environments.
|
||||
</Callout>
|
||||
|
||||
## Quick Setup
|
||||
|
||||
Run the provided script to automatically set up all required services:
|
||||
|
||||
```bash
|
||||
# Set up PostgreSQL and MinIO for image storage
|
||||
./scripts/setup-image-generation-dev.sh
|
||||
|
||||
# Start the development server
|
||||
pnpm dev:desktop
|
||||
```
|
||||
|
||||
This script will:
|
||||
|
||||
1. Start PostgreSQL (no authentication for local development)
|
||||
2. Run database migrations to initialize schema
|
||||
3. Start MinIO (S3-compatible storage)
|
||||
4. Create and configure the storage bucket
|
||||
5. Add necessary S3 environment variables to `.env.desktop`
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The image generation feature requires:
|
||||
|
||||
- **PostgreSQL**: Stores metadata about generated images
|
||||
- **MinIO/S3**: Stores the actual image files
|
||||
- **Server Mode**: Required for file handling (`NEXT_PUBLIC_SERVICE_MODE=server`)
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
The following environment variables are automatically configured by the setup script:
|
||||
|
||||
```bash
|
||||
# S3 Storage Configuration (MinIO for local development)
|
||||
S3_ACCESS_KEY_ID=minioadmin
|
||||
S3_SECRET_ACCESS_KEY=minioadmin
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=lobe-chat
|
||||
S3_REGION=us-east-1
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000/lobe-chat
|
||||
S3_ENABLE_PATH_STYLE=1 # Required for MinIO
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### 1. Image Generation API
|
||||
|
||||
When developing image generation features, generated images will be:
|
||||
|
||||
1. Created by the AI model
|
||||
2. Uploaded to S3/MinIO via presigned URLs
|
||||
3. Metadata stored in PostgreSQL
|
||||
4. Served via the public S3 URL
|
||||
|
||||
### 2. File Storage Structure
|
||||
|
||||
```
|
||||
lobe-chat/ # S3 Bucket
|
||||
├── generated/ # Generated images
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # User uploads for image-to-image
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### 3. Testing Your Implementation
|
||||
|
||||
After setting up the environment, you can test:
|
||||
|
||||
```typescript
|
||||
// Example: Upload generated image
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// Upload to S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
## Manual Setup
|
||||
|
||||
If you prefer to set up services manually:
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```bash
|
||||
docker run -d --name lobe-postgres \
|
||||
-p 5432:5432 \
|
||||
-e POSTGRES_HOST_AUTH_METHOD=trust \
|
||||
-e POSTGRES_DB=postgres \
|
||||
postgres:15
|
||||
```
|
||||
|
||||
### MinIO
|
||||
|
||||
```bash
|
||||
# Start MinIO
|
||||
docker run -d --name lobe-minio \
|
||||
-p 9000:9000 -p 9001:9001 \
|
||||
-e MINIO_ROOT_USER=minioadmin \
|
||||
-e MINIO_ROOT_PASSWORD=minioadmin \
|
||||
quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z \
|
||||
server /data --console-address ":9001"
|
||||
|
||||
# Create bucket
|
||||
docker run --rm \
|
||||
--link lobe-minio:minio \
|
||||
--entrypoint bash \
|
||||
quay.io/minio/mc:RELEASE.2025-04-18T16-45-00Z \
|
||||
-c "
|
||||
mc config host add minio http://minio:9000 minioadmin minioadmin &&
|
||||
mc mb minio/lobe-chat &&
|
||||
mc anonymous set public minio/lobe-chat
|
||||
"
|
||||
```
|
||||
|
||||
## Service URLs
|
||||
|
||||
- **PostgreSQL**: `postgres://postgres@localhost:5432/postgres`
|
||||
- **MinIO API**: `http://localhost:9000`
|
||||
- **MinIO Console**: `http://localhost:9001` (minioadmin/minioadmin)
|
||||
- **Application**: `http://localhost:3015`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Port Conflicts
|
||||
|
||||
If ports are already in use:
|
||||
|
||||
```bash
|
||||
# Check what's using the ports
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO Console
|
||||
```
|
||||
|
||||
### Reset Environment
|
||||
|
||||
To completely reset your development environment:
|
||||
|
||||
```bash
|
||||
# Stop and remove containers
|
||||
docker stop lobe-postgres lobe-minio
|
||||
docker rm lobe-postgres lobe-minio
|
||||
|
||||
# Re-run setup
|
||||
./scripts/setup-image-generation-dev.sh
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
|
||||
The setup script runs migrations automatically. If you need to run them manually:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
Note: In development mode with `pnpm dev:desktop`, migrations also run automatically on startup.
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- [Server Database Setup](/docs/self-hosting/server-database)
|
||||
- [S3 Storage Configuration](/docs/self-hosting/advanced/s3)
|
||||
- [Environment Variables](/docs/self-hosting/environment-variables)
|
||||
@@ -1,190 +0,0 @@
|
||||
---
|
||||
title: 图像生成开发环境配置
|
||||
description: 配置本地环境以开发文本生图和图像处理功能
|
||||
---
|
||||
|
||||
# 图像生成开发环境配置
|
||||
|
||||
本指南帮助开发者配置本地环境,用于开发图像生成功能(文生图、图生图)等和文件存储能力。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 已安装并运行 Docker
|
||||
- 已安装 Node.js 和 pnpm
|
||||
- PostgreSQL 客户端工具(可选,用于调试)
|
||||
|
||||
<Callout type="warning">
|
||||
**安全提醒**:此配置仅适用于本地开发。使用的默认凭据和开放权限不适合生产环境。
|
||||
</Callout>
|
||||
|
||||
## 快速配置
|
||||
|
||||
运行提供的脚本来自动配置所有必需的服务:
|
||||
|
||||
```bash
|
||||
# 配置 PostgreSQL 和 MinIO 用于图像存储
|
||||
./scripts/setup-image-generation-dev.sh
|
||||
|
||||
# 启动开发服务器
|
||||
pnpm dev:desktop
|
||||
```
|
||||
|
||||
此脚本将执行:
|
||||
|
||||
1. 启动 PostgreSQL(本地开发无需身份验证)
|
||||
2. 运行数据库迁移以初始化模式
|
||||
3. 启动 MinIO(S3 兼容存储)
|
||||
4. 创建并配置存储桶
|
||||
5. 在 `.env.desktop` 中添加必要的 S3 环境变量
|
||||
|
||||
## 架构概览
|
||||
|
||||
图像生成功能需要:
|
||||
|
||||
- **PostgreSQL**:存储生成图像的元数据
|
||||
- **MinIO/S3**:存储实际的图像文件
|
||||
- **服务器模式**:文件处理所需(`NEXT_PUBLIC_SERVICE_MODE=server`)
|
||||
|
||||
## 环境配置
|
||||
|
||||
以下环境变量会被配置脚本自动设置:
|
||||
|
||||
```bash
|
||||
# S3 存储配置(本地开发使用 MinIO)
|
||||
S3_ACCESS_KEY_ID=minioadmin
|
||||
S3_SECRET_ACCESS_KEY=minioadmin
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=lobe-chat
|
||||
S3_REGION=us-east-1
|
||||
S3_PUBLIC_DOMAIN=http://localhost:9000/lobe-chat
|
||||
S3_ENABLE_PATH_STYLE=1 # MinIO 必需
|
||||
```
|
||||
|
||||
## 开发工作流
|
||||
|
||||
### 1. 图像生成 API
|
||||
|
||||
在开发图像生成功能时,生成的图像将:
|
||||
|
||||
1. 由 AI 模型创建
|
||||
2. 通过预签名 URL 上传到 S3/MinIO
|
||||
3. 元数据存储在 PostgreSQL 中
|
||||
4. 通过公共 S3 URL 提供服务
|
||||
|
||||
### 2. 文件存储结构
|
||||
|
||||
```
|
||||
lobe-chat/ # S3 存储桶
|
||||
├── generated/ # 生成的图像
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # 用户上传的图像处理文件
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### 3. 测试您的实现
|
||||
|
||||
配置环境后,您可以测试:
|
||||
|
||||
```typescript
|
||||
// 示例:上传生成的图像
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// 上传到 S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
## 手动配置
|
||||
|
||||
如果您希望手动配置服务:
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
```bash
|
||||
docker run -d --name lobe-postgres \
|
||||
-p 5432:5432 \
|
||||
-e POSTGRES_HOST_AUTH_METHOD=trust \
|
||||
-e POSTGRES_DB=postgres \
|
||||
postgres:15
|
||||
```
|
||||
|
||||
### MinIO
|
||||
|
||||
```bash
|
||||
# 启动 MinIO
|
||||
docker run -d --name lobe-minio \
|
||||
-p 9000:9000 -p 9001:9001 \
|
||||
-e MINIO_ROOT_USER=minioadmin \
|
||||
-e MINIO_ROOT_PASSWORD=minioadmin \
|
||||
quay.io/minio/minio:RELEASE.2025-04-22T22-12-26Z \
|
||||
server /data --console-address ":9001"
|
||||
|
||||
# 创建存储桶
|
||||
docker run --rm \
|
||||
--link lobe-minio:minio \
|
||||
--entrypoint bash \
|
||||
quay.io/minio/mc:RELEASE.2025-04-18T16-45-00Z \
|
||||
-c "
|
||||
mc config host add minio http://minio:9000 minioadmin minioadmin &&
|
||||
mc mb minio/lobe-chat &&
|
||||
mc anonymous set public minio/lobe-chat
|
||||
"
|
||||
```
|
||||
|
||||
## 服务地址
|
||||
|
||||
- **PostgreSQL**:`postgres://postgres@localhost:5432/postgres`
|
||||
- **MinIO API**:`http://localhost:9000`
|
||||
- **MinIO 控制台**:`http://localhost:9001` (minioadmin/minioadmin)
|
||||
- **应用程序**:`http://localhost:3015`
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 端口冲突
|
||||
|
||||
如果端口已被占用:
|
||||
|
||||
```bash
|
||||
# 检查端口使用情况
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO 控制台
|
||||
```
|
||||
|
||||
### 重置环境
|
||||
|
||||
要完全重置开发环境:
|
||||
|
||||
```bash
|
||||
# 停止并删除容器
|
||||
docker stop lobe-postgres lobe-minio
|
||||
docker rm lobe-postgres lobe-minio
|
||||
|
||||
# 重新运行配置
|
||||
./scripts/setup-image-generation-dev.sh
|
||||
```
|
||||
|
||||
### 数据库迁移
|
||||
|
||||
配置脚本会自动运行迁移。如需手动运行:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
注意:在使用 `pnpm dev:desktop` 的开发模式下,迁移也会在启动时自动运行。
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [服务器数据库配置](/docs/self-hosting/server-database)
|
||||
- [S3 存储配置](/docs/self-hosting/advanced/s3)
|
||||
- [环境变量](/docs/self-hosting/environment-variables)
|
||||
@@ -53,6 +53,18 @@ Now, you can open `http://localhost:3010` in your browser, and you should see th
|
||||
|
||||

|
||||
|
||||
## Working with Server-Side Features
|
||||
|
||||
The basic setup above uses LobeChat's client-side database mode. If you need to work with server-side features such as:
|
||||
|
||||
- Database persistence
|
||||
- File uploads and storage
|
||||
- Image generation
|
||||
- Multi-user authentication
|
||||
- Advanced server-side integrations
|
||||
|
||||
Please refer to the [Work with Server-Side Database](/docs/development/basic/work-with-server-side-database) guide for complete setup instructions.
|
||||
|
||||
During the development process, if you encounter any issues with environment setup or have any questions about LobeChat development, feel free to ask us at any time. We look forward to seeing your contributions!
|
||||
|
||||
[codespaces-link]: https://codespaces.new/lobehub/lobe-chat
|
||||
|
||||
@@ -53,6 +53,18 @@ bun run dev
|
||||
|
||||

|
||||
|
||||
## 使用服务端功能
|
||||
|
||||
上述基础设置使用 LobeChat 的客户端数据库模式。如果你需要开发服务端功能,如:
|
||||
|
||||
- 数据库持久化
|
||||
- 文件上传和存储
|
||||
- 图像生成
|
||||
- 多用户身份验证
|
||||
- 高级服务端集成
|
||||
|
||||
请参考[使用服务端数据库](/docs/development/basic/work-with-server-side-database)指南获得完整的设置说明。
|
||||
|
||||
在开发过程中,如果你在环境设置上遇到任何问题,或者有任何关于 LobeChat 开发的问题,欢迎随时向我们提问。我们期待看到你的贡献!
|
||||
|
||||
[codespaces-link]: https://codespaces.new/lobehub/lobe-chat
|
||||
|
||||
@@ -11,7 +11,13 @@ But here is the easier approach that can reduce your pain.
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
The project already includes a `.env.development` file with all necessary environment variables for server-side database mode. This file configures:
|
||||
First, copy the example environment file to create your development configuration:
|
||||
|
||||
```bash
|
||||
cp .env.example.development .env.development
|
||||
```
|
||||
|
||||
This file contains all necessary environment variables for server-side database mode and configures:
|
||||
|
||||
- **Service Mode**: `NEXT_PUBLIC_SERVICE_MODE=server`
|
||||
- **Database**: PostgreSQL with connection string
|
||||
@@ -60,6 +66,88 @@ And you can check all Docker services are running by running:
|
||||
docker-compose -f docker-compose.development.yml ps
|
||||
```
|
||||
|
||||
## Image Generation Development
|
||||
|
||||
When working with image generation features (text-to-image, image-to-image), the Docker Compose setup already includes all necessary storage services for handling generated images and user uploads.
|
||||
|
||||
### Image Generation Configuration
|
||||
|
||||
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `.env.example.development`. No additional setup is required.
|
||||
|
||||
### Image Generation Architecture
|
||||
|
||||
The image generation feature requires:
|
||||
|
||||
- **PostgreSQL**: Stores metadata about generated images
|
||||
- **MinIO/S3**: Stores the actual image files
|
||||
- **Server Mode**: Required for file handling (`NEXT_PUBLIC_SERVICE_MODE=server`)
|
||||
|
||||
### Storage Configuration
|
||||
|
||||
The `.env.example.development` file includes all necessary S3 environment variables:
|
||||
|
||||
```bash
|
||||
# S3 Storage Configuration (MinIO for local development)
|
||||
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
|
||||
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
||||
S3_ENDPOINT=http://localhost:${MINIO_PORT}
|
||||
S3_BUCKET=${MINIO_LOBE_BUCKET}
|
||||
S3_PUBLIC_DOMAIN=http://localhost:${MINIO_PORT}
|
||||
S3_ENABLE_PATH_STYLE=1 # Required for MinIO
|
||||
S3_SET_ACL=0 # MinIO compatibility
|
||||
```
|
||||
|
||||
### File Storage Structure
|
||||
|
||||
Generated images and user uploads are organized in the MinIO bucket:
|
||||
|
||||
```
|
||||
lobe/ # S3 Bucket (MINIO_LOBE_BUCKET)
|
||||
├── generated/ # Generated images
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # User uploads for image-to-image
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### Development Workflow for Images
|
||||
|
||||
When developing image generation features, generated images will be:
|
||||
|
||||
1. Created by the AI model
|
||||
2. Uploaded to S3/MinIO via presigned URLs
|
||||
3. Metadata stored in PostgreSQL
|
||||
4. Served via the public S3 URL
|
||||
|
||||
Example code for testing image upload:
|
||||
|
||||
```typescript
|
||||
// Example: Upload generated image
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// Upload to S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Service URLs
|
||||
|
||||
When running with Docker Compose development setup:
|
||||
|
||||
- **PostgreSQL**: `postgres://postgres@localhost:5432/lobechat`
|
||||
- **MinIO API**: `http://localhost:9000`
|
||||
- **MinIO Console**: `http://localhost:9001` (admin/CHANGE_THIS_PASSWORD_IN_PRODUCTION)
|
||||
- **Application**: `http://localhost:3010`
|
||||
|
||||
### Reset Services
|
||||
|
||||
If you encounter issues, you can reset the entire stack:
|
||||
@@ -75,3 +163,27 @@ docker-compose -f docker-compose.development.yml down -v
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### Port Conflicts
|
||||
|
||||
If ports are already in use:
|
||||
|
||||
```bash
|
||||
# Check what's using the ports
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO Console
|
||||
```
|
||||
|
||||
#### Database Migrations
|
||||
|
||||
The setup script runs migrations automatically. If you need to run them manually:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
Note: In development mode with `pnpm dev:desktop`, migrations also run automatically on startup.
|
||||
|
||||
@@ -11,7 +11,13 @@ LobeChat 提供了内置的客户端数据库体验。
|
||||
|
||||
### 环境配置
|
||||
|
||||
项目已经包含了一个 `.env.development` 文件,其中包含服务端数据库模式所需的所有环境变量。此文件配置了:
|
||||
首先,复制示例环境文件来创建你的开发配置:
|
||||
|
||||
```bash
|
||||
cp .env.example.development .env.development
|
||||
```
|
||||
|
||||
此文件包含服务端数据库模式所需的所有环境变量,配置了:
|
||||
|
||||
- **服务模式**: `NEXT_PUBLIC_SERVICE_MODE=server`
|
||||
- **数据库**: 带连接字符串的 PostgreSQL
|
||||
@@ -60,6 +66,88 @@ pnpm dev
|
||||
docker-compose -f docker-compose.development.yml ps
|
||||
```
|
||||
|
||||
## 图像生成开发
|
||||
|
||||
在开发图像生成功能(文生图、图生图)时,Docker Compose 配置已经包含了处理生成图像和用户上传所需的所有存储服务。
|
||||
|
||||
### 图像生成配置
|
||||
|
||||
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `.env.example.development` 中的所有必要环境变量。无需额外配置。
|
||||
|
||||
### 图像生成架构
|
||||
|
||||
图像生成功能需要:
|
||||
|
||||
- **PostgreSQL**:存储生成图像的元数据
|
||||
- **MinIO/S3**:存储实际的图像文件
|
||||
- **服务器模式**:文件处理所需(`NEXT_PUBLIC_SERVICE_MODE=server`)
|
||||
|
||||
### 存储配置
|
||||
|
||||
`.env.example.development` 文件包含所有必要的 S3 环境变量:
|
||||
|
||||
```bash
|
||||
# S3 存储配置(本地开发使用 MinIO)
|
||||
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
|
||||
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
||||
S3_ENDPOINT=http://localhost:${MINIO_PORT}
|
||||
S3_BUCKET=${MINIO_LOBE_BUCKET}
|
||||
S3_PUBLIC_DOMAIN=http://localhost:${MINIO_PORT}
|
||||
S3_ENABLE_PATH_STYLE=1 # MinIO 必需
|
||||
S3_SET_ACL=0 # MinIO 兼容性
|
||||
```
|
||||
|
||||
### 文件存储结构
|
||||
|
||||
生成的图像和用户上传在 MinIO 存储桶中按以下方式组织:
|
||||
|
||||
```
|
||||
lobe/ # S3 存储桶 (MINIO_LOBE_BUCKET)
|
||||
├── generated/ # 生成的图像
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # 用户上传的图像处理文件
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### 图像开发工作流
|
||||
|
||||
在开发图像生成功能时,生成的图像将:
|
||||
|
||||
1. 由 AI 模型创建
|
||||
2. 通过预签名 URL 上传到 S3/MinIO
|
||||
3. 元数据存储在 PostgreSQL 中
|
||||
4. 通过公共 S3 URL 提供服务
|
||||
|
||||
测试图像上传的示例代码:
|
||||
|
||||
```typescript
|
||||
// 示例:上传生成的图像
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// 上传到 S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### 服务地址
|
||||
|
||||
运行 Docker Compose 开发环境时:
|
||||
|
||||
- **PostgreSQL**:`postgres://postgres@localhost:5432/lobechat`
|
||||
- **MinIO API**:`http://localhost:9000`
|
||||
- **MinIO 控制台**:`http://localhost:9001` (admin/CHANGE_THIS_PASSWORD_IN_PRODUCTION)
|
||||
- **应用程序**:`http://localhost:3010`
|
||||
|
||||
### 重置服务
|
||||
|
||||
如遇到问题,可以重置整个服务堆栈:
|
||||
@@ -75,3 +163,27 @@ docker-compose -f docker-compose.development.yml down -v
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
|
||||
### 故障排除
|
||||
|
||||
#### 端口冲突
|
||||
|
||||
如果端口已被占用:
|
||||
|
||||
```bash
|
||||
# 检查端口使用情况
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO 控制台
|
||||
```
|
||||
|
||||
#### 数据库迁移
|
||||
|
||||
配置脚本会自动运行迁移。如需手动运行:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
注意:在使用 `pnpm dev:desktop` 的开发模式下,迁移也会在启动时自动运行。
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
table agents {
|
||||
id text [pk, not null]
|
||||
slug varchar(100) [unique]
|
||||
title text
|
||||
description text
|
||||
title varchar(255)
|
||||
description varchar(1000)
|
||||
tags jsonb [default: `[]`]
|
||||
avatar text
|
||||
background_color text
|
||||
@@ -16,6 +16,7 @@ table agents {
|
||||
provider text
|
||||
system_role text
|
||||
tts jsonb
|
||||
virtual boolean [default: false]
|
||||
opening_message text
|
||||
opening_questions text[] [default: `[]`]
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
@@ -24,6 +25,8 @@ table agents {
|
||||
|
||||
indexes {
|
||||
(client_id, user_id) [name: 'client_id_user_id_unique', unique]
|
||||
title [name: 'agents_title_idx']
|
||||
description [name: 'agents_description_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +139,7 @@ table chat_groups {
|
||||
config jsonb
|
||||
client_id text
|
||||
user_id text [not null]
|
||||
group_id text
|
||||
pinned boolean [default: false]
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
@@ -314,6 +318,24 @@ table message_chunks {
|
||||
}
|
||||
}
|
||||
|
||||
table message_groups {
|
||||
id varchar(255) [pk, not null]
|
||||
topic_id text
|
||||
user_id text [not null]
|
||||
parent_group_id varchar(255)
|
||||
parent_message_id text
|
||||
title varchar(255)
|
||||
description text
|
||||
client_id varchar(255)
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
(client_id, user_id) [name: 'message_groups_client_id_user_id_unique', unique]
|
||||
}
|
||||
}
|
||||
|
||||
table message_plugins {
|
||||
id text [pk, not null]
|
||||
tool_call_id text
|
||||
@@ -385,7 +407,7 @@ table message_translates {
|
||||
|
||||
table messages {
|
||||
id text [pk, not null]
|
||||
role text [not null]
|
||||
role varchar(255) [not null]
|
||||
content text
|
||||
reasoning jsonb
|
||||
search jsonb
|
||||
@@ -407,6 +429,7 @@ table messages {
|
||||
agent_id text
|
||||
group_id text
|
||||
target_id text
|
||||
message_group_id varchar(255)
|
||||
accessed_at "timestamp with time zone" [not null, default: `now()`]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
@@ -417,6 +440,9 @@ table messages {
|
||||
topic_id [name: 'messages_topic_id_idx']
|
||||
parent_id [name: 'messages_parent_id_idx']
|
||||
quota_id [name: 'messages_quota_id_idx']
|
||||
user_id [name: 'messages_user_id_idx']
|
||||
session_id [name: 'messages_session_id_idx']
|
||||
thread_id [name: 'messages_thread_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,6 +648,7 @@ table chunks {
|
||||
|
||||
indexes {
|
||||
(client_id, user_id) [name: 'chunks_client_id_user_id_unique', unique]
|
||||
user_id [name: 'chunks_user_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,6 +662,7 @@ table embeddings {
|
||||
|
||||
indexes {
|
||||
(client_id, user_id) [name: 'embeddings_client_id_user_id_unique', unique]
|
||||
chunk_id [name: 'embeddings_chunk_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -834,6 +862,8 @@ table sessions {
|
||||
indexes {
|
||||
(slug, user_id) [name: 'slug_user_id_unique', unique]
|
||||
(client_id, user_id) [name: 'sessions_client_id_user_id_unique', unique]
|
||||
user_id [name: 'sessions_user_id_idx']
|
||||
(id, user_id) [name: 'sessions_id_user_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -884,6 +914,8 @@ table topics {
|
||||
|
||||
indexes {
|
||||
(client_id, user_id) [name: 'topics_client_id_user_id_unique', unique]
|
||||
user_id [name: 'topics_user_id_idx']
|
||||
(id, user_id) [name: 'topics_id_user_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -983,6 +1015,12 @@ ref: generations.generation_batch_id > generation_batches.id
|
||||
|
||||
ref: generations.async_task_id - async_tasks.id
|
||||
|
||||
ref: message_groups.user_id - users.id
|
||||
|
||||
ref: message_groups.topic_id - topics.id
|
||||
|
||||
ref: message_groups.parent_group_id > message_groups.id
|
||||
|
||||
ref: messages_files.file_id > files.id
|
||||
|
||||
ref: messages_files.message_id > messages.id
|
||||
@@ -995,6 +1033,8 @@ ref: messages.topic_id - topics.id
|
||||
|
||||
ref: threads.source_message_id - messages.id
|
||||
|
||||
ref: messages.message_group_id > message_groups.id
|
||||
|
||||
ref: sessions.group_id - session_groups.id
|
||||
|
||||
ref: topic_documents.document_id > documents.id
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
title: Observability with Grafana, Prometheus, and Tempo
|
||||
description: >-
|
||||
Monitor and analyze your LobeChat instance using Grafana dashboards, Prometheus metrics, and Tempo traces. This guide covers setup, configuration for self-hosted deployments.
|
||||
tags:
|
||||
- Observability
|
||||
- Grafana
|
||||
- Prometheus
|
||||
- Tempo
|
||||
---
|
||||
|
||||
# Observability with [Grafana](https://grafana.com/), [Prometheus](https://prometheus.io/), and [Tempo](https://grafana.com/docs/tempo/latest/)
|
||||
|
||||
LobeChat supports advanced observability for self-hosted deployments using open-source tools:
|
||||
|
||||
- **Grafana** for dashboards and visualization
|
||||
- **Prometheus** for metrics collection
|
||||
- **Tempo** for distributed tracing
|
||||
- **otel-collector** ingesting other OpenTelemetry supported data
|
||||
|
||||
We provided Docker Compose (`docker-compose`) file presets to bootstrap the observability stack with advanced self-hosting features.
|
||||
This guide will help you set up and use these tools to monitor your LobeChat instance.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- `docker` CLI
|
||||
- OrbStack (macOS), or Docker Desktop Windows
|
||||
- `docker-compose` plugin enabled (check through `docker compose version`)
|
||||
|
||||
## 1. Deploy
|
||||
|
||||
```bash
|
||||
curl -O https://raw.githubusercontent.com/lobehub/lobe-chat/HEAD/docker-compose/production/grafana/docker-compose.yml
|
||||
curl -O https://raw.githubusercontent.com/lobehub/lobe-chat/HEAD/docker-compose/production/grafana/.env.example
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
1. Update the password & secrets in the `.env` file as needed.
|
||||
2. Start the stack with the correct profile:
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
This will launch Grafana, Prometheus, Tempo, and the otel-collector alongside LobeChat with Casdoor, Minio, and other advanced services.
|
||||
|
||||
## 2. Access Grafana Dashboards
|
||||
|
||||
- Open Grafana in your browser at [http://localhost:3000](http://localhost:3000) (or your server's IP).
|
||||
- Default login (if required):
|
||||
- **Username:** admin
|
||||
- **Password:** (see your environment or Docker Compose file)
|
||||
|
||||
## 3. Explore traces & metrics
|
||||
|
||||
Click on "Explore" in the left sidebar to access the query editor to run ad-hoc queries against your Prometheus and Tempo data sources.
|
||||
|
||||
## 4. Troubleshooting
|
||||
|
||||
- Ensure all containers are running: `docker compose ps`
|
||||
- Check logs for any service: `docker compose logs <service-name>`
|
||||
- Verify Prometheus and Tempo are scraping and receiving data from LobeChat and otel-collector.
|
||||
|
||||
## See Also
|
||||
|
||||
- [Langfuse Observability](https://lobehub.com/docs/self-hosting/advanced/observability/langfuse)
|
||||
- [Self-Hosting Overview](https://lobehub.com/docs/self-hosting/start)
|
||||
|
||||
---
|
||||
|
||||
For questions or feedback, open an issue on GitHub or join our community discussions.
|
||||
@@ -0,0 +1,70 @@
|
||||
---
|
||||
title: 使用 Grafana、Prometheus 和 Tempo 进行可观测性监控
|
||||
description: >-
|
||||
使用 Grafana、Prometheus 指标和 Tempo 链路追踪,监控和分析你的 LobeChat 实例。本指南涵盖自托管部署的搭建、配置和示例仪表盘。
|
||||
tags:
|
||||
- 可观测性
|
||||
- Grafana
|
||||
- Prometheus
|
||||
- Tempo
|
||||
---
|
||||
|
||||
# 使用 [Grafana](https://grafana.com/)、[Prometheus](https://prometheus.io/) 和 [Tempo](https://grafana.com/docs/tempo/latest/) 进行可观测性监控
|
||||
|
||||
LobeChat 支持通过开源工具实现自托管部署的高级可观测性:
|
||||
|
||||
- **Grafana**:仪表盘与可视化
|
||||
- **Prometheus**:指标采集
|
||||
- **Tempo**:分布式链路追踪
|
||||
- **otel-collector**:采集 OpenTelemetry 支持的数据
|
||||
|
||||
我们提供了 Docker Compose (`docker-compose`) 预设文件,帮助你一键启动包含高级可观测性功能的自托管栈。
|
||||
|
||||
## 前置条件
|
||||
|
||||
- 已安装 `docker` 命令行工具
|
||||
- OrbStack(macOS)或 Docker Desktop(Windows)
|
||||
- 启用 `docker-compose` 插件(可通过 `docker compose version` 检查)
|
||||
|
||||
## 1. 部署
|
||||
|
||||
```bash
|
||||
curl -O https://raw.githubusercontent.com/lobehub/lobe-chat/HEAD/docker-compose/production/grafana/docker-compose.yml
|
||||
curl -O https://raw.githubusercontent.com/lobehub/lobe-chat/HEAD/docker-compose/production/grafana/.env.example
|
||||
mv .env.example .env
|
||||
```
|
||||
|
||||
1. 根据需要修改 `.env` 文件中的密码和密钥。
|
||||
2. 使用如下命令启动服务:
|
||||
|
||||
```sh
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
这将会启动 Grafana、Prometheus、Tempo、otel-collector 以及 LobeChat、Casdoor、Minio 等高级服务。
|
||||
|
||||
## 2. 访问 Grafana 仪表盘
|
||||
|
||||
- 在浏览器中打开 [http://localhost:3000](http://localhost:3000)(或你的服务器 IP)。
|
||||
- 默认登录信息(如需):
|
||||
- **用户名:** admin
|
||||
- **密码:** 见你的环境变量或 Docker Compose 文件
|
||||
|
||||
## 3. 探索链路追踪与指标
|
||||
|
||||
点击左侧边栏的 “Explore” 进入查询编辑器,可对 Prometheus 和 Tempo 数据源进行即席查询。
|
||||
|
||||
## 4. 故障排查
|
||||
|
||||
- 确认所有容器已运行:`docker compose ps`
|
||||
- 查看服务日志:`docker compose logs <服务名>`
|
||||
- 检查 Prometheus 和 Tempo 是否正常采集 LobeChat 及 otel-collector 的数据。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [Langfuse 可观测性](https://lobehub.com/zh/docs/self-hosting/advanced/observability/langfuse)
|
||||
- [自托管总览](https://lobehub.com/zh/docs/self-hosting/start)
|
||||
|
||||
---
|
||||
|
||||
如有问题或建议,欢迎在 GitHub 提 Issue 或加入社区讨论。
|
||||
@@ -34,15 +34,15 @@ CRAWLER_IMPLS="native,search1api"
|
||||
|
||||
Supported crawler types are listed below:
|
||||
|
||||
| Value | Description | Environment Variable |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------- | -------------------------- |
|
||||
| `browserless` | Headless browser crawler based on [Browserless](https://www.browserless.io/), suitable for rendering complex pages. | `BROWSERLESS_TOKEN` |
|
||||
| `exa` | Crawler capabilities provided by [Exa](https://exa.ai/), API required. | `EXA_API_KEY` |
|
||||
| `firecrawl` | [Firecrawl](https://firecrawl.dev/) headless browser API, ideal for modern websites. | `FIRECRAWL_API_KEY` |
|
||||
| `jina` | Crawler service from [Jina AI](https://jina.ai/), supports fast content summarization. | `JINA_READER_API_KEY` |
|
||||
| `native` | Built-in general-purpose crawler for standard web structures. | |
|
||||
| `search1api` | Page crawling capabilities from [Search1API](https://www.search1api.com), great for structured content extraction. | `SEARCH1API_CRAWL_API_KEY` |
|
||||
| `tavily` | Web scraping and summarization API from [Tavily](https://www.tavily.com/). | `TAVILY_API_KEY` |
|
||||
| Value | Description | Environment Variable |
|
||||
| ------------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| `browserless` | Headless browser crawler based on [Browserless](https://www.browserless.io/), suitable for rendering complex pages. | `BROWSERLESS_TOKEN` |
|
||||
| `exa` | Crawler capabilities provided by [Exa](https://exa.ai/), API required. | `EXA_API_KEY` |
|
||||
| `firecrawl` | [Firecrawl](https://firecrawl.dev/) headless browser API, ideal for modern websites. | `FIRECRAWL_API_KEY` |
|
||||
| `jina` | Crawler service from [Jina AI](https://jina.ai/), supports fast content summarization. | `JINA_READER_API_KEY` |
|
||||
| `native` | Built-in general-purpose crawler for standard web structures. | |
|
||||
| `search1api` | Page crawling capabilities from [Search1API](https://www.search1api.com), great for structured content extraction. | `SEARCH1API_API_KEY` `SEARCH1API_CRAWL_API_KEY` `SEARCH1API_SEARCH_API_KEY` |
|
||||
| `tavily` | Web scraping and summarization API from [Tavily](https://www.tavily.com/). | `TAVILY_API_KEY` |
|
||||
|
||||
> 💡 Setting multiple crawlers increases success rate; the system will try different ones based on priority.
|
||||
|
||||
@@ -58,19 +58,19 @@ SEARCH_PROVIDERS="searxng"
|
||||
|
||||
Supported search engines include:
|
||||
|
||||
| Value | Description | Environment Variable |
|
||||
| ------------ | --------------------------------------------------------------------------------------- | ------------------------------------------- |
|
||||
| `anspire` | Search service provided by [Anspire](https://anspire.ai/). | `ANSPIRE_API_KEY` |
|
||||
| `bocha` | Search service from [Bocha](https://open.bochaai.com/). | `BOCHA_API_KEY` |
|
||||
| `brave` | [Brave](https://search.brave.com/help/api), a privacy-friendly search source. | `BRAVE_API_KEY` |
|
||||
| `exa` | [Exa](https://exa.ai/), a search API designed for AI. | `EXA_API_KEY` |
|
||||
| `firecrawl` | Search capabilities via [Firecrawl](https://firecrawl.dev/). | `FIRECRAWL_API_KEY` |
|
||||
| `google` | Uses [Google Programmable Search Engine](https://programmablesearchengine.google.com/). | `GOOGLE_PSE_API_KEY` `GOOGLE_PSE_ENGINE_ID` |
|
||||
| `jina` | Semantic search provided by [Jina AI](https://jina.ai/). | `JINA_READER_API_KEY` |
|
||||
| `kagi` | Premium search API by [Kagi](https://kagi.com/), requires a subscription key. | `KAGI_API_KEY` |
|
||||
| `search1api` | Aggregated search capabilities from [Search1API](https://www.search1api.com). | `SEARCH1API_CRAWL_API_KEY` |
|
||||
| `searxng` | Use a self-hosted or public [SearXNG](https://searx.space/) instance. | `SEARXNG_URL` |
|
||||
| `tavily` | [Tavily](https://www.tavily.com/), offers fast web summaries and answers. | `TAVILY_API_KEY` |
|
||||
| Value | Description | Environment Variable |
|
||||
| ------------ | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
||||
| `anspire` | Search service provided by [Anspire](https://anspire.ai/). | `ANSPIRE_API_KEY` |
|
||||
| `bocha` | Search service from [Bocha](https://open.bochaai.com/). | `BOCHA_API_KEY` |
|
||||
| `brave` | [Brave](https://search.brave.com/help/api), a privacy-friendly search source. | `BRAVE_API_KEY` |
|
||||
| `exa` | [Exa](https://exa.ai/), a search API designed for AI. | `EXA_API_KEY` |
|
||||
| `firecrawl` | Search capabilities via [Firecrawl](https://firecrawl.dev/). | `FIRECRAWL_API_KEY` |
|
||||
| `google` | Uses [Google Programmable Search Engine](https://programmablesearchengine.google.com/). | `GOOGLE_PSE_API_KEY` `GOOGLE_PSE_ENGINE_ID` |
|
||||
| `jina` | Semantic search provided by [Jina AI](https://jina.ai/). | `JINA_READER_API_KEY` |
|
||||
| `kagi` | Premium search API by [Kagi](https://kagi.com/), requires a subscription key. | `KAGI_API_KEY` |
|
||||
| `search1api` | Aggregated search capabilities from [Search1API](https://www.search1api.com). | `SEARCH1API_API_KEY` `SEARCH1API_CRAWL_API_KEY` `SEARCH1API_SEARCH_API_KEY` |
|
||||
| `searxng` | Use a self-hosted or public [SearXNG](https://searx.space/) instance. | `SEARXNG_URL` |
|
||||
| `tavily` | [Tavily](https://www.tavily.com/), offers fast web summaries and answers. | `TAVILY_API_KEY` |
|
||||
|
||||
> ⚠️ Some search providers require you to apply for an API Key and configure it in your `.env` file.
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ CRAWLER_IMPLS="native,search1api"
|
||||
| `firecrawl` | [Firecrawl](https://firecrawl.dev/) 无头浏览器 API,适合现代网站抓取。 | `FIRECRAWL_API_KEY` |
|
||||
| `jina` | 使用 [Jina AI](https://jina.ai/) 的爬虫服务,支持快速提取摘要信息。 | `JINA_READER_API_KEY` |
|
||||
| `native` | 内置通用爬虫,适用于标准网页结构。 | |
|
||||
| `search1api` | 利用 [Search1API](https://www.search1api.com) 提供的页面抓取能力,适合结构化内容提取。 | `SEARCH1API_CRAWL_API_KEY` |
|
||||
| `search1api` | 利用 [Search1API](https://www.search1api.com) 提供的页面抓取能力,适合结构化内容提取。 | `SEARCH1API_API_KEY` `SEARCH1API_CRAWL_API_KEY` `SEARCH1API_SEARCH_API_KEY` |
|
||||
| `tavily` | 使用 [Tavily](https://www.tavily.com/) 的网页抓取与摘要 API。 | `TAVILY_API_KEY` |
|
||||
|
||||
> 💡 设置多个爬虫可提升成功率,系统将根据优先级尝试不同爬虫。
|
||||
@@ -64,7 +64,7 @@ SEARCH_PROVIDERS="searxng"
|
||||
| `google` | 使用 [Google Programmable Search Engine](https://programmablesearchengine.google.com/)。 | `GOOGLE_PSE_API_KEY` `GOOGLE_PSE_ENGINE_ID` |
|
||||
| `jina` | 使用 [Jina AI](https://jina.ai/) 提供的语义搜索服务。 | `JINA_READER_API_KEY` |
|
||||
| `kagi` | [Kagi](https://kagi.com/) 提供的高级搜索 API,需订阅 Key。 | `KAGI_API_KEY` |
|
||||
| `search1api` | 使用 [Search1API](https://www.search1api.com) 聚合搜索能力。 | `SEARCH1API_CRAWL_API_KEY` |
|
||||
| `search1api` | 使用 [Search1API](https://www.search1api.com) 聚合搜索能力。 | `SEARCH1API_API_KEY` `SEARCH1API_CRAWL_API_KEY` `SEARCH1API_SEARCH_API_KEY` |
|
||||
| `searxng` | 使用自托管或公共 [SearXNG](https://searx.space/) 实例。 | `SEARXNG_URL` |
|
||||
| `tavily` | [Tavily](https://www.tavily.com/),快速网页摘要与答案返回。 | `TAVILY_API_KEY` |
|
||||
|
||||
|
||||
@@ -205,6 +205,13 @@ LobeChat provides a complete authentication service capability when deployed. Th
|
||||
|
||||
### Microsoft Entra ID
|
||||
|
||||
#### `AUTH_MICROSOFT_ENTRA_ID_BASE_URL`
|
||||
|
||||
- Type: Required
|
||||
- Description: - Description: Base URL for Azure login. Use when authenticating against other Microsoft sovereignty clouds like Azure US Government.
|
||||
- Default: `https://login.microsoftonline.com`
|
||||
- Example: `https://login.microsoftonline.us`
|
||||
|
||||
#### `AUTH_AZURE_AD_ID`
|
||||
|
||||
- Type: Required
|
||||
|
||||
@@ -145,6 +145,13 @@ For specific content, please refer to the [Feature Flags](/docs/self-hosting/adv
|
||||
- Default: `0`
|
||||
- Example: `1` or `0`
|
||||
|
||||
### `NEXT_PUBLIC_ASSET_PREFIX`
|
||||
|
||||
- Type: Optional
|
||||
- Description: The path access prefix for static resources can be set to the URL for CDN access. For more details, please refer to: [assetPrefix](https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix)
|
||||
- Default: -
|
||||
- Example: `https://cdn.example.com`
|
||||
|
||||
## Plugin Service
|
||||
|
||||
### `PLUGINS_INDEX_URL`
|
||||
|
||||
@@ -141,6 +141,13 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
|
||||
- 默认值:`0`
|
||||
- 示例:`1` 或 `0`
|
||||
|
||||
### `NEXT_PUBLIC_ASSET_PREFIX`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:静态资源的路径访问前缀,你可以设置为 CDN 访问的 URL,具体可参考: [assetPrefix](https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix)
|
||||
- 默认值:-
|
||||
- 示例:`https://cdn.example.com`
|
||||
|
||||
## 插件服务
|
||||
|
||||
### `PLUGINS_INDEX_URL`
|
||||
|
||||
@@ -3,6 +3,7 @@ title: LobeChat Model Service Providers - Environment Variables and Configuratio
|
||||
description: >-
|
||||
Learn about the environment variables and configuration settings for various model service providers like OpenAI, Google AI, AWS Bedrock, Ollama, Perplexity AI, Anthropic AI, Mistral AI, Groq AI, OpenRouter AI, and 01.AI.
|
||||
|
||||
|
||||
tags:
|
||||
- Model Service Providers
|
||||
- Environment Variables
|
||||
@@ -646,9 +647,9 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
||||
- Type: Optional
|
||||
- Description: Used to control the FAL model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model. Separate multiple entries with commas. The definition syntax follows the same rules as other providers' model lists.
|
||||
- Default: `-`
|
||||
- Example: `-all,+flux/schnell,+flux-pro/kontext=FLUX.1 Kontext [pro]`
|
||||
- Example: `-all,+fal-ai/flux/schnell,+fal-ai/flux-pro/kontext=FLUX.1 Kontext [pro]`
|
||||
|
||||
The above example disables all models first, then enables `flux/schnell` and `flux-pro/kontext` (displayed as `FLUX.1 Kontext [pro]`).
|
||||
The above example disables all models first, then enables `fal-ai/flux/schnell` and `fal-ai/flux-pro/kontext` (displayed as `FLUX.1 Kontext [pro]`).
|
||||
|
||||
## BFL
|
||||
|
||||
@@ -675,4 +676,45 @@ The above example disables all models first, then enables `flux/schnell` and `fl
|
||||
|
||||
The above example disables all models first, then enables `flux-pro-1.1` and `flux-kontext-pro` (displayed as `FLUX.1 Kontext [pro]`).
|
||||
|
||||
## NewAPI
|
||||
|
||||
### `NEWAPI_API_KEY`
|
||||
|
||||
- Type: Optional
|
||||
- Description: This is the API key for your NewAPI service instance. NewAPI is a multi-provider model aggregation service that provides unified access to various AI model APIs.
|
||||
- Default: -
|
||||
- Example: `sk-xxxxxx...xxxxxx`
|
||||
|
||||
### `NEWAPI_PROXY_URL`
|
||||
|
||||
- Type: Optional
|
||||
- Description: The base URL for your NewAPI server instance. This should point to your deployed NewAPI service endpoint.
|
||||
- Default: -
|
||||
- Example: `https://your-newapi-server.com/`
|
||||
|
||||
NewAPI is a multi-provider model aggregation service that supports automatic model routing based on provider detection. It offers cost management features and provides a single endpoint for accessing models from multiple providers including OpenAI, Anthropic, Google, and more. Learn more about NewAPI at [https://github.com/Calcium-Ion/new-api](https://github.com/Calcium-Ion/new-api).
|
||||
|
||||
## Vercel AI Gateway
|
||||
|
||||
### `ENABLED_VERCELAIGATEWAY`
|
||||
|
||||
- Type: Optional
|
||||
- Description: Enables Vercel AI Gateway as a model provider by default. Set to `0` to disable the Vercel AI Gateway service.
|
||||
- Default: `1`
|
||||
- Example: `0`
|
||||
|
||||
### `VERCELAIGATEWAY_API_KEY`
|
||||
|
||||
- Type: Required
|
||||
- Description: This is the API key you applied for in the Vercel AI Gateway service.
|
||||
- Default: -
|
||||
- Example: `vck_xxxxxx...xxxxxx`
|
||||
|
||||
### `VERCELAIGATEWAY_MODEL_LIST`
|
||||
|
||||
- Type: Optional
|
||||
- Description: Used to control the Vercel AI Gateway model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model. Separate multiple entries with commas. The definition syntax follows the same rules as other providers' model lists.
|
||||
- Default: `-`
|
||||
- Example: `-all,+vercel-model-1,+vercel-model-2=vercel-special`
|
||||
|
||||
[model-list]: /docs/self-hosting/advanced/model-list
|
||||
|
||||
@@ -645,9 +645,9 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
||||
- 类型:可选
|
||||
- 描述:用来控制 FAL 模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。模型定义语法规则与其他 provider 保持一致。
|
||||
- 默认值:`-`
|
||||
- 示例:`-all,+flux/schnell,+flux-pro/kontext=FLUX.1 Kontext [pro]`
|
||||
- 示例:`-all,+fal-ai/flux/schnell,+fal-ai/flux-pro/kontext=FLUX.1 Kontext [pro]`
|
||||
|
||||
上述示例表示先禁用所有模型,再启用 `flux/schnell` 和 `flux-pro/kontext`(显示名为 `FLUX.1 Kontext [pro]`)。
|
||||
上述示例表示先禁用所有模型,再启用 `fal-ai/flux/schnell` 和 `fal-ai/flux-pro/kontext`(显示名为 `FLUX.1 Kontext [pro]`)。
|
||||
|
||||
## BFL
|
||||
|
||||
@@ -674,4 +674,50 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
|
||||
|
||||
上述示例表示先禁用所有模型,再启用 `flux-pro-1.1` 和 `flux-kontext-pro`(显示名为 `FLUX.1 Kontext [pro]`)。
|
||||
|
||||
## NewAPI
|
||||
|
||||
### `NEWAPI_API_KEY`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:这是你的 NewAPI 服务实例的 API 密钥。NewAPI 是一个多供应商模型聚合服务,提供对各种 AI 模型 API 的统一访问。
|
||||
- 默认值:-
|
||||
- 示例:`sk-xxxxxx...xxxxxx`
|
||||
|
||||
### `NEWAPI_PROXY_URL`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:你的 NewAPI 服务器实例的基础 URL。这应该指向你部署的 NewAPI 服务端点。
|
||||
- 默认值:-
|
||||
- 示例:`https://your-newapi-server.com`
|
||||
|
||||
<Callout type={'info'}>
|
||||
NewAPI
|
||||
是一个多供应商模型聚合服务,支持基于供应商检测的自动模型路由。它提供成本管理功能,并为访问包括
|
||||
OpenAI、Anthropic、Google 等多个供应商的模型提供单一端点。了解更多关于 NewAPI 的信息请访问
|
||||
[https://github.com/Calcium-Ion/new-api](https://github.com/Calcium-Ion/new-api)。
|
||||
</Callout>
|
||||
|
||||
## Vercel AI Gateway
|
||||
|
||||
### `ENABLED_VERCELAIGATEWAY`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:默认启用 Vercel AI Gateway 作为模型供应商,当设为 0 时关闭 Vercel AI Gateway 服务
|
||||
- 默认值:`1`
|
||||
- 示例:`0`
|
||||
|
||||
### `VERCELAIGATEWAY_API_KEY`
|
||||
|
||||
- 类型:必选
|
||||
- 描述:这是你在 Vercel AI Gateway 服务中申请的 API 密钥
|
||||
- 默认值:-
|
||||
- 示例:`vck_xxxxxx...xxxxxx`
|
||||
|
||||
### `VERCELAIGATEWAY_MODEL_LIST`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:用来控制 Vercel AI Gateway 模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。模型定义语法规则与其他 provider 保持一致。
|
||||
- 默认值:`-`
|
||||
- 示例:`-all,+vercel-model-1,+vercel-model-2=vercel-special`
|
||||
|
||||
[model-list]: /zh/docs/self-hosting/advanced/model-list
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Using Vercel AI Gateway in LobeChat
|
||||
description: >-
|
||||
Learn how to integrate and utilize Vercel AI Gateway's unified API in LobeChat.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Vercel AI Gateway
|
||||
- API Key
|
||||
- Web UI
|
||||
---
|
||||
|
||||
# Using Vercel AI Gateway in LobeChat
|
||||
|
||||
[Vercel AI Gateway](https://vercel.com/ai-gateway) is a unified API that provides access to 100+ AI models through a single endpoint. It offers features like budget management, usage monitoring, load balancing, and fallback handling.
|
||||
|
||||
This article will guide you on how to use Vercel AI Gateway in LobeChat.
|
||||
|
||||
<Steps>
|
||||
### Step 1: Create an API Key in Vercel AI Gateway
|
||||
|
||||
- Go to [Vercel Dashboard](https://vercel.com/dashboard)
|
||||
- Click on the **AI Gateway** tab on the left side
|
||||
- Click on **API keys** in the left sidebar
|
||||
- Click **Create key** and then **Create key** in the dialog to complete
|
||||
|
||||
### Step 2: Configure Vercel AI Gateway in LobeChat
|
||||
|
||||
- Go to the `Settings` page in LobeChat
|
||||
- Under `AI Service Provider`, find the setting for `Vercel AI Gateway`
|
||||
- Enter the API Key you obtained
|
||||
- Choose a model from Vercel AI Gateway for your AI assistant to start the conversation
|
||||
|
||||
<Callout type={'warning'}>
|
||||
During usage, you may need to pay the API service provider, so please refer to Vercel AI Gateway's
|
||||
[pricing policy](https://vercel.com/docs/ai-gateway/models).
|
||||
</Callout>
|
||||
</Steps>
|
||||
|
||||
At this point, you can start chatting using the models provided by Vercel AI Gateway in LobeChat.
|
||||
|
||||
## Model Selection
|
||||
|
||||
Vercel AI Gateway supports various model providers including:
|
||||
|
||||
- **OpenAI**: `openai/gpt-4o`, `openai/gpt-4o-mini`, `openai/o1`, etc.
|
||||
- **Anthropic**: `anthropic/claude-3-5-sonnet`, `anthropic/claude-3-opus`, etc.
|
||||
- **Google**: `google/gemini-2.5-pro`, `google/gemini-2.0-flash`, etc.
|
||||
- **DeepSeek**: `deepseek/deepseek-chat`, `deepseek/deepseek-reasoner`, etc.
|
||||
- And many more...
|
||||
|
||||
For a complete list of supported models, visit [Vercel AI Gateway Models](https://vercel.com/ai-gateway/models).
|
||||
|
||||
## API Configuration
|
||||
|
||||
Vercel AI Gateway uses OpenAI-compatible API format. The base URL is:
|
||||
|
||||
```
|
||||
https://ai-gateway.vercel.sh/v1
|
||||
```
|
||||
|
||||
You can use any OpenAI-compatible client with this endpoint and your API key.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: 在 LobeChat 中使用 Vercel AI Gateway
|
||||
description: 了解如何在 LobeChat 中集成和使用 Vercel AI Gateway 的统一 API
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Vercel AI Gateway
|
||||
- API 密钥
|
||||
- Web 界面
|
||||
---
|
||||
|
||||
# 在 LobeChat 中使用 Vercel AI Gateway
|
||||
|
||||
[Vercel AI Gateway](https://vercel.com/ai-gateway) 是一个统一的 API,通过单一端点提供对 100+ AI 模型的访问。它提供预算管理、使用监控、负载均衡和回退处理等功能。
|
||||
|
||||
本文将指导您如何在 LobeChat 中使用 Vercel AI Gateway。
|
||||
|
||||
<Steps>
|
||||
### 第一步:在 Vercel AI Gateway 中创建 API 密钥
|
||||
|
||||
- 访问 [Vercel 控制台](https://vercel.com/dashboard)
|
||||
- 点击左侧的 **AI Gateway** 标签
|
||||
- 点击左侧边栏的 **API 密钥**
|
||||
- 点击 **创建密钥**,然后在对话框中点击 **创建密钥** 完成创建
|
||||
|
||||
### 第二步:在 LobeChat 中配置 Vercel AI Gateway
|
||||
|
||||
- 进入 LobeChat 的 `设置` 页面
|
||||
- 在 `AI 服务提供商` 下,找到 `Vercel AI Gateway` 设置
|
||||
- 输入您获得的 API 密钥
|
||||
- 选择 Vercel AI Gateway 的模型,开始与 AI 助手对话
|
||||
|
||||
<Callout type={'warning'}>
|
||||
使用过程中可能需要向 API 服务提供商付费,请参考 Vercel AI Gateway 的
|
||||
[定价政策](https://vercel.com/docs/ai-gateway/models)。
|
||||
</Callout>
|
||||
</Steps>
|
||||
|
||||
至此,您可以在 LobeChat 中使用 Vercel AI Gateway 提供的模型开始聊天了。
|
||||
|
||||
## 模型选择
|
||||
|
||||
Vercel AI Gateway 支持多种模型提供商,包括:
|
||||
|
||||
- **OpenAI**: `openai/gpt-4o`、`openai/gpt-4o-mini`、`openai/o1` 等
|
||||
- **Anthropic**: `anthropic/claude-3-5-sonnet`、`anthropic/claude-3-opus` 等
|
||||
- **Google**: `google/gemini-2.5-pro`、`google/gemini-2.0-flash` 等
|
||||
- **DeepSeek**: `deepseek/deepseek-chat`、`deepseek/deepseek-reasoner` 等
|
||||
- 以及更多...
|
||||
|
||||
如需查看完整的支持模型列表,请访问 [Vercel AI Gateway 模型](https://vercel.com/ai-gateway/models)。
|
||||
|
||||
## API 配置
|
||||
|
||||
Vercel AI Gateway 使用 OpenAI 兼容的 API 格式。基础 URL 为:
|
||||
|
||||
```
|
||||
https://ai-gateway.vercel.sh/v1
|
||||
```
|
||||
|
||||
您可以使用任何 OpenAI 兼容的客户端与此端点和您的 API 密钥一起使用。
|
||||
+19
-3
@@ -70,12 +70,15 @@
|
||||
"input": {
|
||||
"addAi": "إضافة رسالة AI",
|
||||
"addUser": "إضافة رسالة مستخدم",
|
||||
"disclaimer": "قد يرتكب الذكاء الاصطناعي أخطاءً أيضًا، يرجى التحقق من المعلومات الهامة",
|
||||
"errorMsg": "فشل إرسال الرسالة، يرجى التحقق من الشبكة والمحاولة مرة أخرى: {{errorMsg}}",
|
||||
"more": "المزيد",
|
||||
"send": "إرسال",
|
||||
"sendWithCmdEnter": "اضغط {{meta}} + Enter للإرسال",
|
||||
"sendWithEnter": "اضغط Enter للإرسال",
|
||||
"sendWithCmdEnter": "اضغط <key/> للإرسال",
|
||||
"sendWithEnter": "اضغط <key/> للإرسال",
|
||||
"stop": "توقف",
|
||||
"warp": "تغيير السطر"
|
||||
"warp": "تغيير السطر",
|
||||
"warpWithKey": "اضغط على مفتاح <key/> للانتقال إلى السطر"
|
||||
},
|
||||
"intentUnderstanding": {
|
||||
"title": "جارٍ فهم وتحليل نواياك..."
|
||||
@@ -147,6 +150,11 @@
|
||||
"total": "الإجمالي المستهلك"
|
||||
}
|
||||
},
|
||||
"minimap": {
|
||||
"jumpToMessage": "الانتقال إلى الرسالة رقم {{index}}",
|
||||
"nextMessage": "الرسالة التالية",
|
||||
"previousMessage": "الرسالة السابقة"
|
||||
},
|
||||
"newAgent": "مساعد جديد",
|
||||
"pin": "تثبيت",
|
||||
"pinOff": "إلغاء التثبيت",
|
||||
@@ -232,6 +240,10 @@
|
||||
"threadMessageCount": "{{messageCount}} رسالة",
|
||||
"title": "موضوع فرعي"
|
||||
},
|
||||
"toggleWideScreen": {
|
||||
"off": "إيقاف وضع الشاشة العريضة",
|
||||
"on": "تشغيل وضع الشاشة العريضة"
|
||||
},
|
||||
"tokenDetails": {
|
||||
"chats": "رسائل المحادثة",
|
||||
"historySummary": "ملخص التاريخ",
|
||||
@@ -274,6 +286,7 @@
|
||||
"actionFiletip": "رفع ملف",
|
||||
"actionTooltip": "رفع",
|
||||
"disabled": "النموذج الحالي لا يدعم التعرف على الصور وتحليل الملفات، يرجى تغيير النموذج لاستخدامه",
|
||||
"fileNotSupported": "وضع المتصفح لا يدعم تحميل الملفات حاليًا، يدعم الصور فقط",
|
||||
"visionNotSupported": "النموذج الحالي لا يدعم التعرف البصري، يرجى تبديل النموذج لاستخدام هذه الميزة"
|
||||
},
|
||||
"preview": {
|
||||
@@ -282,6 +295,9 @@
|
||||
"pending": "يتم التحضير للتحميل...",
|
||||
"processing": "يتم معالجة الملف..."
|
||||
}
|
||||
},
|
||||
"validation": {
|
||||
"videoSizeExceeded": "لا يمكن أن يتجاوز حجم ملف الفيديو 20 ميغابايت، حجم الملف الحالي هو {{actualSize}}"
|
||||
}
|
||||
},
|
||||
"zenMode": "وضع التركيز"
|
||||
|
||||
@@ -114,6 +114,7 @@
|
||||
"reasoning": "يدعم هذا النموذج التفكير العميق",
|
||||
"search": "يدعم هذا النموذج البحث عبر الإنترنت",
|
||||
"tokens": "يدعم هذا النموذج حتى {{tokens}} رمزًا في جلسة واحدة",
|
||||
"video": "هذا النموذج يدعم التعرف على الفيديو",
|
||||
"vision": "يدعم هذا النموذج التعرف البصري"
|
||||
},
|
||||
"removed": "هذا النموذج لم يعد متوفر في القائمة، سيتم إزالته تلقائيًا إذا تم إلغاء تحديده"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"actions": {
|
||||
"expand": {
|
||||
"off": "طي",
|
||||
"on": "توسيع"
|
||||
},
|
||||
"typobar": {
|
||||
"off": "إخفاء شريط أدوات التنسيق",
|
||||
"on": "إظهار شريط أدوات التنسيق"
|
||||
}
|
||||
},
|
||||
"cancel": "إلغاء",
|
||||
"confirm": "تأكيد",
|
||||
"file": {
|
||||
"error": "خطأ: {{message}}",
|
||||
"uploading": "جاري رفع الملف..."
|
||||
},
|
||||
"image": {
|
||||
"broken": "الصورة تالفة"
|
||||
},
|
||||
"link": {
|
||||
"edit": "تعديل الرابط",
|
||||
"open": "فتح الرابط",
|
||||
"placeholder": "أدخل عنوان URL للرابط",
|
||||
"unlink": "إزالة الرابط"
|
||||
},
|
||||
"math": {
|
||||
"placeholder": "يرجى إدخال معادلة TeX"
|
||||
},
|
||||
"slash": {
|
||||
"h1": "عنوان رئيسي من المستوى الأول",
|
||||
"h2": "عنوان فرعي من المستوى الثاني",
|
||||
"h3": "عنوان فرعي من المستوى الثالث",
|
||||
"hr": "خط فاصل",
|
||||
"table": "جدول",
|
||||
"tex": "معادلة TeX"
|
||||
},
|
||||
"table": {
|
||||
"delete": "حذف الجدول",
|
||||
"deleteColumn": "حذف العمود",
|
||||
"deleteRow": "حذف الصف",
|
||||
"insertColumnLeft": "إدراج {{count}} عمودًا إلى اليسار",
|
||||
"insertColumnRight": "إدراج {{count}} عمودًا إلى اليمين",
|
||||
"insertRowAbove": "إدراج {{count}} صفًا في الأعلى",
|
||||
"insertRowBelow": "إدراج {{count}} صفًا في الأسفل"
|
||||
},
|
||||
"typobar": {
|
||||
"blockquote": "اقتباس",
|
||||
"bold": "غامق",
|
||||
"bulletList": "قائمة نقطية",
|
||||
"code": "كود مضمن",
|
||||
"codeblock": "كتلة كود",
|
||||
"italic": "مائل",
|
||||
"link": "رابط",
|
||||
"numberList": "قائمة مرقمة",
|
||||
"strikethrough": "شطب",
|
||||
"table": "جدول",
|
||||
"taskList": "قائمة المهام",
|
||||
"tex": "معادلة TeX",
|
||||
"underline": "تسطير"
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,9 @@
|
||||
"lock": "قفل نسبة العرض إلى الارتفاع",
|
||||
"unlock": "إلغاء قفل نسبة العرض إلى الارتفاع"
|
||||
},
|
||||
"cfg": {
|
||||
"label": "شدة التوجيه"
|
||||
},
|
||||
"header": {
|
||||
"desc": "وصف بسيط، ابتكر فورًا",
|
||||
"title": "الرسم"
|
||||
@@ -27,6 +30,13 @@
|
||||
"prompt": {
|
||||
"placeholder": "وصف المحتوى الذي ترغب في إنشائه"
|
||||
},
|
||||
"quality": {
|
||||
"label": "جودة الصورة",
|
||||
"options": {
|
||||
"hd": "عالي الدقة",
|
||||
"standard": "عادي"
|
||||
}
|
||||
},
|
||||
"seed": {
|
||||
"label": "البذرة",
|
||||
"random": "بذرة عشوائية"
|
||||
|
||||
@@ -294,6 +294,21 @@
|
||||
"title": "أقصى نافذة سياق",
|
||||
"unlimited": "غير محدود"
|
||||
},
|
||||
"type": {
|
||||
"extra": "أنواع النماذج المختلفة تمتلك سيناريوهات استخدام وقدرات مميزة",
|
||||
"options": {
|
||||
"chat": "محادثة",
|
||||
"embedding": "تضمين",
|
||||
"image": "توليد الصور",
|
||||
"realtime": "محادثة فورية",
|
||||
"stt": "تحويل الصوت إلى نص",
|
||||
"text2music": "تحويل النص إلى موسيقى",
|
||||
"text2video": "تحويل النص إلى فيديو",
|
||||
"tts": "تحويل النص إلى كلام"
|
||||
},
|
||||
"placeholder": "يرجى اختيار نوع النموذج",
|
||||
"title": "نوع النموذج"
|
||||
},
|
||||
"vision": {
|
||||
"extra": "سيؤدي هذا التكوين إلى فتح إعدادات تحميل الصور في التطبيق، ما إذا كان يدعم التعرف يعتمد بالكامل على النموذج نفسه، يرجى اختبار قابلية استخدام التعرف البصري لهذا النموذج بنفسك",
|
||||
"title": "دعم التعرف البصري"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user