mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-20 06:15:58 +00:00
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ed61d87da | |||
| 45919e6db8 | |||
| 01b411cbc3 | |||
| 72d6287b70 | |||
| 04cad62627 | |||
| 56f4e98b97 | |||
| 7ddf3a0040 | |||
| 9c4c91019d | |||
| 91022bfa04 | |||
| 8dc2aef559 | |||
| 02932330c3 | |||
| fc99fc3fa9 | |||
| 34f061b51a | |||
| b2e6777c81 | |||
| afd59004f9 | |||
| 390d0b807f | |||
| f3fa286f25 | |||
| a072b53311 | |||
| fa8f4ff83a | |||
| eaf7f1b9c1 | |||
| 07197e7a51 | |||
| bf032dde07 | |||
| 7f37f29411 | |||
| eb45ed8647 | |||
| c7ae78bfb7 | |||
| b7ca447946 | |||
| e11bf29d0f | |||
| 53f9c3b279 | |||
| 9e980cbf28 | |||
| 7d718b2fe3 | |||
| ed716c27f2 | |||
| f433aca05f | |||
| 13e1cafb62 | |||
| e02e74c9a4 | |||
| 63f482a132 | |||
| 160678e7e0 | |||
| a89c2ec1bc | |||
| 1225f12e1a | |||
| e1481e60fb | |||
| 416a4b1212 | |||
| 580961e6e4 | |||
| 35d3577af7 | |||
| d4baae5df0 | |||
| be476a5b29 | |||
| 4ed67f39f7 | |||
| 3f4e935301 | |||
| 7eb4a7f173 | |||
| 9d0bb1f230 | |||
| f07d9122c5 | |||
| df9b7df385 | |||
| 987b633336 | |||
| b7042b6542 | |||
| b346d4bcbf | |||
| 780ee82b12 | |||
| 31c6a51d7f | |||
| dfe4699769 | |||
| 0c6b8858eb | |||
| 24ae38aae4 | |||
| d40d7e0c00 | |||
| 18920c579b | |||
| 155d5804b9 | |||
| 82d11b45a6 | |||
| fef3e5f9f3 | |||
| 0fe7319b6a | |||
| c3cb37498a | |||
| 80aad1de42 | |||
| 86f7e8b64e | |||
| c215384cb3 | |||
| 87a5cbc5c9 | |||
| 44b6b01ca5 | |||
| c8a8c7be3d | |||
| f5e5465981 | |||
| d9c5e7b142 | |||
| 2d6b9ae6b3 | |||
| 35fd5ce705 | |||
| c193e657fb | |||
| 176e49ab6a | |||
| 07e2b063e2 | |||
| 08f5d733cd | |||
| 2d8338b044 | |||
| 5c90905fbb | |||
| 03625e8c29 | |||
| b1c72fd476 | |||
| e971ff3110 | |||
| 2ff3efa630 | |||
| f63b137428 | |||
| 0c5c975c6f | |||
| b86dc9bbf9 | |||
| 01db0b634c | |||
| b064fe3f98 | |||
| 826d724ce7 | |||
| f016058af5 | |||
| 5babf02460 | |||
| ca417f86d5 | |||
| ec094d06d9 | |||
| 6a9e4f6f45 | |||
| 4514081f3a | |||
| 8bf3eedacf | |||
| f948631edf | |||
| 3feebb4827 | |||
| 2d1babc11e | |||
| d34ecab318 | |||
| c7d4399949 | |||
| fab795e549 | |||
| a1e1681321 | |||
| ea76c1170d | |||
| 89f9df7199 | |||
| d49e442dee | |||
| 5842a1858e | |||
| 61c2c3c361 | |||
| 64fe6d9dc6 | |||
| 7f77a5ca0e | |||
| 16ae521659 | |||
| ad47d4d553 | |||
| cb9bf10562 | |||
| 88c4362768 | |||
| 4f6eb27b3d | |||
| a0c062fa99 | |||
| da79815faa | |||
| 7ebf981567 | |||
| accbc7c5a1 | |||
| 935343e11e | |||
| 6bd13d890f | |||
| c96d9effeb | |||
| 55a1a49dc0 | |||
| c05ba4ff9e | |||
| 53e17841fa | |||
| ee96f02130 | |||
| b39df20840 | |||
| 39ef8be0c7 | |||
| 6a55be3ed8 | |||
| 0545de3cb5 | |||
| 41a017808d | |||
| 01d6bcf99f | |||
| e45e31c810 | |||
| 84af825d24 | |||
| b132e66248 | |||
| 9c4e2c08a4 | |||
| 3e35924313 | |||
| 60f649d4f2 | |||
| 15b24f1f34 | |||
| f4b8561cee | |||
| 366337f4ac | |||
| 93d677c925 | |||
| 7a061084cb | |||
| bf85be2f68 | |||
| 1c04736225 | |||
| 619ce9fc8b | |||
| a7d254dece | |||
| 4c083e6768 | |||
| bdfa44bb56 | |||
| 747ea363a7 | |||
| 8df955888c | |||
| c79f1b989d | |||
| c786e41a6c | |||
| 828dcdf842 | |||
| b2983f062e | |||
| 9d81cdca36 | |||
| af37bf7a9a | |||
| 8953523575 | |||
| ac4b61af91 | |||
| 8106426980 | |||
| dd4ab3fc88 | |||
| b52a94ae01 | |||
| 066f11a8b4 | |||
| 0996be513a | |||
| 9b671d0698 | |||
| a26499f0bf | |||
| 17431106bb | |||
| 8ead7c5437 | |||
| c931c5218b | |||
| 602e10faca | |||
| c751f932a9 | |||
| edb62cecc5 | |||
| 7331e8a70e | |||
| 261e2ca0bf | |||
| 61dc847589 | |||
| d97aa49ddd | |||
| 5c2e5f7288 | |||
| 2d82fccf3f | |||
| f91db428ed | |||
| 43ab03a261 | |||
| 3be609c3aa | |||
| f405b03fa4 | |||
| 2a56269c4d | |||
| 3baa966235 | |||
| 7b9d8a1db0 | |||
| b835c84d79 | |||
| 04ac3535e0 | |||
| 04ca9f4672 | |||
| 97a90f5b43 | |||
| 7f6ccf2922 | |||
| d5b94eaf8f | |||
| 7c001eb91e | |||
| 5bf0921246 | |||
| fc7c579e55 | |||
| 6cb41eb888 | |||
| 4b11e5ae33 | |||
| 039be1d1b6 | |||
| b91ca8c903 | |||
| 2331492e63 | |||
| 26512d7edb | |||
| 0725f94443 | |||
| 36cc29b3fa | |||
| d20e7a3f61 | |||
| 792a9b6b8c | |||
| cb84c3e16c | |||
| 95fd588d43 | |||
| 63082377dc | |||
| 8701fa61b4 | |||
| decf04922a | |||
| 3752739248 | |||
| 1355a2e6a4 | |||
| bebe7a3675 | |||
| b19b4fa53c | |||
| 52ed73d5aa | |||
| 95994f4596 | |||
| ed5bb5f15d | |||
| 332f25301c | |||
| dc25c7d202 | |||
| 66118cc9cf | |||
| d3dafe1911 | |||
| 0cae8151d0 | |||
| 2c85a30c9d | |||
| f0a7193879 | |||
| 2a811d0a85 | |||
| 215a96e1b6 | |||
| 65b06de485 | |||
| 23fade3cf8 | |||
| 3e02c2502e | |||
| 6c11726dc9 | |||
| 3866090478 | |||
| 5054d41f4f | |||
| ee660d64a6 | |||
| 6160b9eeeb | |||
| 309f97322c | |||
| c94e97c45a | |||
| 2778eca8ac | |||
| 308103a3ea | |||
| 169e59869a | |||
| 5237b34ca7 | |||
| e0d46bb751 | |||
| 28cdafb388 | |||
| 4fc885cc90 | |||
| a85520dc9c | |||
| aef19f46c1 | |||
| 58d356c477 | |||
| cf58628995 | |||
| 5b42ee2499 | |||
| 1ec8defec9 | |||
| 907c9ef9b0 | |||
| d61a9f5998 | |||
| 62de6f0020 | |||
| b15933cbf9 | |||
| 7b91dfddd5 | |||
| 2f28a93d89 | |||
| eedeb75346 | |||
| 9f07e4c975 | |||
| c3f76ccc7e | |||
| 7385eb911a | |||
| e0442b83a1 | |||
| ca2292b538 | |||
| e64085f7d3 | |||
| 2eb198c687 | |||
| 750b26acea | |||
| 6521243f07 | |||
| 321a2e809d | |||
| c5307bf969 | |||
| 2ae0e9ed56 | |||
| a7f2347f1b | |||
| a47ddc5159 | |||
| 09621b3a91 | |||
| 4522c93d1d | |||
| f503c53dda | |||
| 90a6f686d6 | |||
| d4e974e11b | |||
| 7cfe0377ce | |||
| 5ec7c8d8ec | |||
| d0bfe6d709 | |||
| 0bd952e3fb | |||
| 7b8f5339e0 | |||
| d6d2129737 | |||
| 48a30566a9 | |||
| 99e72bbe3b | |||
| 1511c7531d | |||
| ed2ce8d32f | |||
| 2acd21e949 | |||
| f45d4a651f | |||
| 585e386074 | |||
| dcd4b31178 | |||
| fb70932fe8 | |||
| e68da28110 | |||
| f0cce15aae | |||
| 68b38379b8 | |||
| a9db54810a | |||
| 7f2f30feb9 | |||
| 9c9f1c3c16 | |||
| d9e1336c07 | |||
| 58cedfaa5d | |||
| 26e9584f5f | |||
| db800d2b63 | |||
| 286f96ad37 | |||
| a16fa0265b | |||
| a4d7a47569 | |||
| 678608fc8b | |||
| bdb02b2058 | |||
| f177fd0982 | |||
| 5ec717244f | |||
| 63c3a7188e | |||
| 3c3cc7513e | |||
| 5b223200fb | |||
| 98f2dba5f0 | |||
| cd84241709 | |||
| 8416feca99 | |||
| 324dbc548d | |||
| 9d89b97b23 | |||
| 660a5ad167 | |||
| 2e1b37dcce | |||
| a11ebff552 | |||
| 432c28d193 | |||
| 4cacacda67 | |||
| f1d59864c1 | |||
| 01eaf8d903 | |||
| 5244f22bfb | |||
| b4b426f842 | |||
| fca25e1a12 | |||
| b7a700c562 | |||
| a216cf38b5 | |||
| 81241d8e11 | |||
| dff4b7bd17 | |||
| dfdb904fcb | |||
| f58c6e127c | |||
| bf5174635c | |||
| f34c4de11f | |||
| 6ec26ebd80 | |||
| 38f9cb3ed6 | |||
| d3141302dd | |||
| f7cda68d6b | |||
| 9b4f950a03 | |||
| 081088cb36 | |||
| 353e729c74 | |||
| 5486663541 | |||
| 2916e4f568 | |||
| 10d38e032c | |||
| 4f6e2862bc | |||
| 4e04399e39 | |||
| 2d369bfbb6 | |||
| 3fd7fe2d5e | |||
| 457b645a7e | |||
| 292e63e270 | |||
| 28c3c61b20 | |||
| af8a05b3dc | |||
| 92598c00e5 | |||
| 9f9026fb13 | |||
| 5b0154f3d1 | |||
| 4ebd143bcf | |||
| 8deb00cfd3 | |||
| a2e10a09fe | |||
| fb5fdb2971 | |||
| e2b5ed3de7 | |||
| b96947fe6d |
@@ -0,0 +1,100 @@
|
||||
---
|
||||
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` 层进行数据持久化和检索,也可能调用其他服务。
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
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
|
||||
|
||||
- Ensure JSDoc comments accurately reflect the implementation; update them when needed.
|
||||
- Look for opportunities to simplify or modernize code with the latest JavaScript/TypeScript features.
|
||||
- Prefer `async`/`await` over callbacks or chained `.then` promises.
|
||||
- Use consistent, descriptive naming—avoid obscure abbreviations.
|
||||
- Replace magic numbers or strings with well-named constants.
|
||||
- Use semantically meaningful variable, function, and class names.
|
||||
- Ignore purely formatting issues and other autofixable lint problems.
|
||||
|
||||
### Code Optimization
|
||||
|
||||
- Prefer `for…of` loops to index-based `for` loops when feasible.
|
||||
- Decide whether callbacks should be **debounced** or **throttled**.
|
||||
- Use components from `@lobehub/ui`, Ant Design, or the existing design system instead of raw HTML tags (e.g., `Button` vs. `button`).
|
||||
- reuse npm packages already installed (e.g., `lodash/omit`) rather than reinventing the wheel.
|
||||
- Design for dark mode and mobile responsiveness:
|
||||
- Use the `antd-style` token system instead of hard-coded colors.
|
||||
- Select the proper component variants.
|
||||
- Performance considerations:
|
||||
- Where safe, convert sequential async flows to concurrent ones with `Promise.all`, `Promise.race`, etc.
|
||||
- Query only the required columns from a database rather than selecting entire rows.
|
||||
|
||||
### 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.
|
||||
|
||||
## 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>
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
description: cursor rules writing and optimization guide
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
当你编写或修改 Cursor Rule 时,请遵循以下准则:
|
||||
|
||||
- 当你知道 rule 的文件名时,使用 `read_file` 而不是 `fetch_rules` 去读取它们,它们都在项目根目录的 `.cursor/rules/` 文件夹下
|
||||
|
||||
- 代码示例
|
||||
- 示例应尽量精简,仅保留演示核心
|
||||
- 删除与示例无关的导入/导出语句,但保留必要的导入
|
||||
- 同一文件存在多个示例时,若前文已演示模块导入,后续示例可省略重复导入
|
||||
- 无需书写 `export`
|
||||
- 可省略与演示无关或重复的 props、配置对象属性、try/catch、CSS 等代码
|
||||
- 删除无关注释,保留有助理解的注释
|
||||
|
||||
- 格式
|
||||
- 修改前请先确认原始文档语言,并保持一致
|
||||
- 无序列表统一使用 `-`
|
||||
- 列表末尾的句号是多余的
|
||||
- 非必要不使用加粗、行内代码等样式,Rule 主要供 LLM 阅读
|
||||
- 避免中英文逐句对照。若括号内容为示例而非翻译,可保留
|
||||
|
||||
- Review
|
||||
- 修正 Markdown 语法问题
|
||||
- 纠正错别字
|
||||
- 指出示例与说明不一致之处
|
||||
@@ -0,0 +1,94 @@
|
||||
---
|
||||
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](mdc:src/components/Button.tsx)
|
||||
|
||||
- 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
|
||||
|
||||
## Mermaid Diagram Generation: Strict Syntax Validation Checklist
|
||||
|
||||
Before producing any Mermaid diagram, you **must** compare your final code line-by-line against every rule in the following checklist to ensure 100% compliance. **This is a hard requirement and takes precedence over other stylistic suggestions.** Please follow these action steps:
|
||||
|
||||
1. Plan the Mermaid diagram logic in your mind.
|
||||
2. Write the Mermaid code.
|
||||
3. **Carefully review your code line-by-line against the entire checklist below.**
|
||||
4. Fix any aspect of your code that doesn't comply.
|
||||
5. Use the `validateMermaid` tool to check your code for syntax errors. Only proceed if validation passes.
|
||||
6. Output the final, compliant, and copy-ready Mermaid code block.
|
||||
7. Immediately after the Mermaid code block, output:
|
||||
I have checked that the Mermaid syntax fully complies with the validation checklist.
|
||||
|
||||
---
|
||||
|
||||
### Checklist Details
|
||||
|
||||
#### Rule 1: Edge Labels – Must Be Plain Text Only
|
||||
> **Essence:** Anything inside `|...|` must contain pure, unformatted text. Absolutely NO Markdown, list markers, or parentheses/brackets allowed—these often cause rendering failures.
|
||||
|
||||
- **✅ Do:** `A -->|Process plain text data| B`
|
||||
- **❌ Don't:** `A -->|1. Ordered list item| B` (No numbered lists)
|
||||
- **❌ Don't:** `CC --"1. fetch('/api/...')"--> API` (No square brackets)
|
||||
- **❌ Don't:** `A -->|- Unordered list item| B` (No hyphen lists)
|
||||
- **❌ Don't:** `A -->|Transform (important)| B` (No parentheses)
|
||||
- **❌ Don't:** `A -->|Transform [important]| B` (No square brackets)
|
||||
|
||||
#### Rule 2: Node Definition – Handle Special Characters with Care
|
||||
> **Essence:** When node text or subgraph titles contain special characters like `()` or `[]`, wrap the text in quotes to avoid conflicts with Mermaid shape syntax.
|
||||
|
||||
- **When your node text includes parentheses (e.g., 'React (JSX)'):**
|
||||
- **✅ Do:** `I_REACT["<b>React component (JSX)</b>"]` (Quotes wrap all text)
|
||||
- **❌ Don't:** `I_REACT(<b>React component (JSX)</b>)` (Wrong, Mermaid parses this as a shape)
|
||||
- **❌ Don't:** `subgraph Plugin Features (Plugins)` (Wrong, subgraph titles with parentheses must also be wrapped in quotes)
|
||||
|
||||
#### Rule 3: Double Quotes in Text – Must Be Escaped
|
||||
> **Essence:** Use `"` for double quotes **inside node text**.
|
||||
|
||||
- **✅ Do:** `A[This node contains "quotes"]`
|
||||
- **❌ Don't:** `A[This node contains "quotes"]`
|
||||
|
||||
#### Rule 4: All Formatting Must Use HTML Tags (NOT Markdown!)
|
||||
> **Essence:** For newlines, bold, and other text formatting in nodes, use HTML tags only. Markdown is not supported.
|
||||
|
||||
- **✅ Do (robust):** `A["<b>Bold</b> and <code>code</code><br>This is a new line"]`
|
||||
- **❌ Don't (not rendered):** `C["# This is a heading"]`
|
||||
- **❌ Don't (not rendered):** ``C["`const` means constant"]``
|
||||
- **⚠️ Warning (unreliable):** `B["Markdown **bold** might sometimes work but DON'T rely on it"]`
|
||||
|
||||
#### Rule 5: No HTML Tags for Participants and Message Labels (Sequence Diagrams)
|
||||
> **Important Addition:**
|
||||
> In Mermaid sequence diagrams, you MUST NOT use any HTML tags (such as `<b>`, `<code>`, etc.) in:
|
||||
> - `participant` display names (`as` part)
|
||||
> - Message labels (the text after `:` in diagram flows)
|
||||
>
|
||||
> These tags are generally not rendered—they may appear as-is or cause compatibility issues.
|
||||
|
||||
- **✅ Do:** `participant A as Client`
|
||||
- **❌ Don't:** `participant A as <b>Client</b>`
|
||||
- **✅ Do:** `A->>B: 1. Establish connection`
|
||||
- **❌ Don't:** `A->>B: 1. <code>Establish connection</code>`
|
||||
|
||||
---
|
||||
|
||||
**Validate each Mermaid code block by running it through the `validateMermaid` tool before delivering your output!**
|
||||
@@ -0,0 +1,84 @@
|
||||
---
|
||||
description: 包含添加 debug 日志请求时
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Debug 包使用指南
|
||||
|
||||
本项目使用 [debug](mdc:https:/github.com/debug-js/debug) 包进行调试日志记录。使用此规则来确保团队成员统一调试日志格式。
|
||||
|
||||
## 基本用法
|
||||
|
||||
1. 导入 debug 包:
|
||||
|
||||
```typescript
|
||||
import debug from 'debug';
|
||||
```
|
||||
|
||||
2. 创建一个命名空间的日志记录器:
|
||||
|
||||
```typescript
|
||||
// 格式: lobe:[模块]:[子模块]
|
||||
const log = debug('lobe-[模块名]:[子模块名]');
|
||||
```
|
||||
|
||||
3. 使用日志记录器:
|
||||
|
||||
```typescript
|
||||
log('简单消息');
|
||||
log('带变量的消息: %O', object);
|
||||
log('格式化数字: %d', number);
|
||||
```
|
||||
|
||||
## 命名空间约定
|
||||
|
||||
- 桌面应用相关: `lobe-desktop:[模块]`
|
||||
- 服务端相关: `lobe-server:[模块]`
|
||||
- 客户端相关: `lobe-client:[模块]`
|
||||
- 路由相关: `lobe-[类型]-router:[模块]`
|
||||
|
||||
## 格式说明符
|
||||
|
||||
- `%O` - 对象展开(推荐用于复杂对象)
|
||||
- `%o` - 对象
|
||||
- `%s` - 字符串
|
||||
- `%d` - 数字
|
||||
|
||||
## 示例
|
||||
|
||||
查看 [market/index.ts](mdc:src/server/routers/edge/market/index.ts) 中的使用示例:
|
||||
|
||||
```typescript
|
||||
import debug from 'debug';
|
||||
|
||||
const log = debug('lobe-edge-router:market');
|
||||
|
||||
log('getAgent input: %O', input);
|
||||
```
|
||||
|
||||
## 启用调试
|
||||
|
||||
要在开发时启用调试输出,需设置环境变量:
|
||||
|
||||
### 在浏览器中
|
||||
|
||||
在控制台执行:
|
||||
```javascript
|
||||
localStorage.debug = 'lobe-*'
|
||||
```
|
||||
|
||||
### 在 Node.js 环境中
|
||||
|
||||
```bash
|
||||
DEBUG=lobe-* npm run dev
|
||||
# 或者
|
||||
DEBUG=lobe-* pnpm dev
|
||||
```
|
||||
|
||||
### 在 Electron 应用中
|
||||
|
||||
可以在主进程和渲染进程启动前设置环境变量:
|
||||
|
||||
```typescript
|
||||
process.env.DEBUG = 'lobe-*';
|
||||
```
|
||||
@@ -0,0 +1,193 @@
|
||||
---
|
||||
description: Debug 调试指南
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# Debug 调试指南
|
||||
|
||||
## 💡 调试流程概览
|
||||
|
||||
当遇到问题时,请按照以下优先级进行处理:
|
||||
|
||||
1. **快速判断** - 对于熟悉的错误,直接提供解决方案
|
||||
2. **信息收集** - 使用工具搜索相关代码和配置
|
||||
3. **网络搜索** - 查找现有解决方案
|
||||
4. **定位调试** - 添加日志进行问题定位
|
||||
5. **临时方案** - 如果找不到根本解决方案,提供临时解决方案
|
||||
6. **解决实施** - 提供可维护的最终解决方案
|
||||
|
||||
## 🔍 错误信息分析
|
||||
|
||||
### 错误来源识别
|
||||
|
||||
错误信息可能来自:
|
||||
|
||||
- **Terminal 输出** - 构建、运行时错误
|
||||
- **浏览器控制台** - 前端 JavaScript 错误
|
||||
- **开发工具** - ESLint、TypeScript、测试框架等
|
||||
- **服务器日志** - API、数据库连接等后端错误
|
||||
- **截图或文本** - 用户直接提供的错误信息
|
||||
|
||||
## 🛠️ 信息收集工具
|
||||
|
||||
### 代码搜索工具
|
||||
|
||||
使用以下工具收集相关信息,并根据场景选择最合适的工具:
|
||||
|
||||
- **`codebase_search` (语义搜索)**
|
||||
- **何时使用**: 当你不确定具体的代码实现,想要寻找相关概念、功能或逻辑时。
|
||||
- **示例**: `查询"文件上传"功能的实现`
|
||||
- **`grep_search` (精确/正则搜索)**
|
||||
- **何时使用**: 当你知道要查找的确切字符串、函数名、变量名或一个特定的模式时。
|
||||
- **示例**: `查找所有使用了 'useState' 的地方`
|
||||
- **`file_search` (文件搜索)**
|
||||
- **何时使用**: 当你知道文件名的一部分,需要快速定位文件时。
|
||||
- **示例**: `查找 'Button.tsx' 组件`
|
||||
- **`read_file` (内容读取)**
|
||||
- **何时使用**: 在定位到具体文件后,用于查看其完整内容和上下文。
|
||||
- **`web_search` (网络搜索)**
|
||||
- **何时使用**: 当错误信息可能与第三方库、API 或常见问题相关时,用于获取外部信息。
|
||||
|
||||
### 环境与依赖检查
|
||||
|
||||
- **检查 `package.json`**: 查看 `scripts` 了解项目如何运行、构建和测试。查看 `dependencies` 和 `devDependencies` 确认库版本,版本冲突有时是问题的根源。
|
||||
- **运行测试**: 使用 `ni vitest` 运行单元测试和集成测试,这可以快速定位功能回归或组件错误。
|
||||
|
||||
### 项目特定搜索目标
|
||||
|
||||
针对 lobe-chat 项目,重点关注:
|
||||
|
||||
- **配置文件**: [package.json](mdc:package.json), [next.config.mjs](mdc:next.config.mjs)
|
||||
- **核心功能**: `src/features/` 下的相关模块
|
||||
- **状态管理**: `src/store/` 下的 Zustand stores
|
||||
- **数据库**: `src/database/` 和 `src/migrations/`
|
||||
- **类型定义**: `src/types/` 下的类型文件
|
||||
- **服务层**: `src/services/` 下的 API 服务
|
||||
- **启动流程**: [apps/desktop/src/main/core/App.ts](mdc:apps/desktop/src/main/core/App.ts) - 了解应用启动流程
|
||||
|
||||
## 🌐 网络搜索策略
|
||||
|
||||
### 搜索顺序优先级
|
||||
|
||||
1. **和问题相关的项目的 github issue**
|
||||
|
||||
2. **技术社区**
|
||||
- Stack Overflow
|
||||
- GitHub Discussions
|
||||
- Reddit
|
||||
|
||||
3. **官方文档**
|
||||
- 使用 `mcp_context7_resolve-library-id` 和 `mcp_context7_get-library-docs` 工具
|
||||
- 查阅官方文档网站
|
||||
|
||||
### 搜索关键词策略
|
||||
|
||||
- **错误信息**: 完整的错误消息
|
||||
- **技术栈**: "Next.js 15" + "error message"
|
||||
- **上下文**: 添加功能相关的关键词
|
||||
|
||||
## 🔧 问题定位与结构化思考
|
||||
|
||||
如果问题比较复杂,我们要按照先定位问题,再解决问题的大方向进行。
|
||||
|
||||
### 结构化思考工具
|
||||
|
||||
对于复杂或多步骤的调试任务,使用 `mcp_sequential-thinking_sequentialthinking` 工具来结构化思考过程。这有助于:
|
||||
|
||||
- **分解问题**: 将大问题拆解成可管理的小步骤。
|
||||
- **清晰追踪**: 记录每一步的发现和决策,避免遗漏。
|
||||
- **自我修正**: 在过程中评估和调整调试路径。
|
||||
|
||||
### 日志调试
|
||||
|
||||
在问题产生的路径上添加日志,可以简单使用 `console.log` 或者参考 [debug-usage.mdc](mdc:.cursor/rules/debug-usage.mdc) 使用 `debug` 模块。添加完日志后,请求我运行相关的代码并提供关键输出和错误信息。
|
||||
|
||||
### 引导式交互调试
|
||||
|
||||
虽然我无法直接操作浏览器开发者工具,但我可以引导你进行交互式调试:
|
||||
|
||||
1. **设置断点**: 我会告诉你可以在哪些关键代码行设置断点。
|
||||
2. **检查变量**: 我会请你在断点处检查特定变量的值或 `props`/`state`。
|
||||
3. **分析调用栈**: 我会请你提供调用栈信息,以帮助理解代码执行流程。
|
||||
|
||||
## 💡 临时解决方案策略
|
||||
|
||||
当无法找到根本解决方案时,提供临时解决方案:
|
||||
|
||||
### 临时方案准则
|
||||
|
||||
- **快速修复** - 优先让功能可用
|
||||
- **最小修改** - 减少对现有代码的影响
|
||||
- **清晰标记** - 明确标注这是临时方案
|
||||
- **后续计划** - 说明后续如何找到更好的解决方案
|
||||
|
||||
### 临时方案模板
|
||||
|
||||
```markdown
|
||||
## 临时解决方案 ⚠️
|
||||
|
||||
**问题**: [简要描述问题]
|
||||
|
||||
**临时修复**:
|
||||
[具体的临时修复步骤]
|
||||
|
||||
**风险说明**:
|
||||
|
||||
- [可能的副作用或限制]
|
||||
- [需要注意的事项]
|
||||
|
||||
**后续计划**:
|
||||
|
||||
- [ ] 深入调研根本原因
|
||||
- [ ] 寻找更优雅的解决方案
|
||||
- [ ] 监控是否有其他影响
|
||||
```
|
||||
|
||||
## ✅ 解决方案准则
|
||||
|
||||
### 方案质量标准
|
||||
|
||||
提供的解决方案应该:
|
||||
|
||||
- **✅ 低侵入性** - 最小化对现有代码的修改
|
||||
- **✅ 可维护性** - 易于理解和后续维护
|
||||
- **✅ 类型安全** - 符合 TypeScript 规范
|
||||
- **✅ 最佳实践** - 遵循项目的编码规范
|
||||
- **✅ 测试友好** - 便于编写和运行测试
|
||||
- **❌ 避免长期 Hack** - 临时方案可以 hack,但要明确标注
|
||||
|
||||
### 解决方案模板
|
||||
|
||||
```markdown
|
||||
## 问题原因
|
||||
|
||||
[简要说明问题产生的根本原因]
|
||||
|
||||
## 解决方案
|
||||
|
||||
[详细的解决步骤]
|
||||
|
||||
## 代码修改
|
||||
|
||||
[具体的代码变更]
|
||||
|
||||
## 验证方法
|
||||
|
||||
[如何验证问题已解决]
|
||||
|
||||
## 预防措施
|
||||
|
||||
[如何避免类似问题再次发生]
|
||||
```
|
||||
|
||||
## 🔄 迭代调试流程
|
||||
|
||||
如果初次解决方案无效:
|
||||
|
||||
1. **重新收集信息** - 基于新的错误信息搜索
|
||||
2. **深入代码分析** - 查看更多相关代码文件
|
||||
3. **运行相关测试** - 编写或运行一个失败的测试来稳定复现问题。
|
||||
4. **扩大搜索范围** - 搜索更广泛的相关问题
|
||||
5. **请求更多日志** - 添加更详细的调试信息
|
||||
6. **提供临时方案** - 如果根本解决方案复杂,先提供临时修复
|
||||
7. **分解问题** - 将复杂问题拆解为更小的子问题
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
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,188 @@
|
||||
---
|
||||
description: 桌面端测试
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# 桌面端控制器单元测试指南
|
||||
|
||||
## 测试框架与目录结构
|
||||
|
||||
LobeChat 桌面端使用 Vitest 作为测试框架。控制器的单元测试应放置在对应控制器文件同级的 `__tests__` 目录下,并以原控制器文件名加 `.test.ts` 作为文件名。
|
||||
|
||||
```
|
||||
apps/desktop/src/main/controllers/
|
||||
├── __tests__/
|
||||
│ ├── index.test.ts
|
||||
│ ├── MenuCtr.test.ts
|
||||
│ └── ...
|
||||
├── McpCtr.ts
|
||||
├── MenuCtr.ts
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 测试文件基本结构
|
||||
|
||||
```typescript
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { App } from '@/core/App';
|
||||
|
||||
import YourController from '../YourControllerName';
|
||||
|
||||
// 模拟依赖
|
||||
vi.mock('依赖模块', () => ({
|
||||
依赖函数: vi.fn(),
|
||||
}));
|
||||
|
||||
// 模拟 App 实例
|
||||
const mockApp = {
|
||||
// 按需模拟必要的 App 属性和方法
|
||||
} as unknown as App;
|
||||
|
||||
describe('YourController', () => {
|
||||
let controller: YourController;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
controller = new YourController(mockApp);
|
||||
});
|
||||
|
||||
describe('方法名', () => {
|
||||
it('测试场景描述', async () => {
|
||||
// 准备测试数据
|
||||
|
||||
// 执行被测方法
|
||||
const result = await controller.方法名(参数);
|
||||
|
||||
// 验证结果
|
||||
expect(result).toMatchObject(预期结果);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## 模拟外部依赖
|
||||
|
||||
### 模拟模块函数
|
||||
|
||||
```typescript
|
||||
const mockFunction = vi.fn();
|
||||
|
||||
vi.mock('module-name', () => ({
|
||||
functionName: mockFunction,
|
||||
}));
|
||||
```
|
||||
|
||||
### 模拟 Node.js 核心模块
|
||||
|
||||
例如模拟 `child_process.exec` 和 `util.promisify`:
|
||||
|
||||
```typescript
|
||||
// 存储模拟的 exec 实现
|
||||
const mockExecImpl = vi.fn();
|
||||
|
||||
// 模拟 child_process.exec
|
||||
vi.mock('child_process', () => ({
|
||||
exec: vi.fn((cmd, callback) => {
|
||||
return mockExecImpl(cmd, callback);
|
||||
}),
|
||||
}));
|
||||
|
||||
// 模拟 util.promisify
|
||||
vi.mock('util', () => ({
|
||||
promisify: vi.fn((fn) => {
|
||||
return async (cmd: string) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
mockExecImpl(cmd, (error: Error | null, result: any) => {
|
||||
if (error) reject(error);
|
||||
else resolve(result);
|
||||
});
|
||||
});
|
||||
};
|
||||
}),
|
||||
}));
|
||||
```
|
||||
|
||||
## 编写有效的测试用例
|
||||
|
||||
### 测试分类
|
||||
|
||||
将测试用例分为不同类别,每个类别测试一个特定场景:
|
||||
|
||||
```typescript
|
||||
// 成功场景
|
||||
it('应该成功完成操作', async () => {});
|
||||
|
||||
// 边界条件
|
||||
it('应该处理边界情况', async () => {});
|
||||
|
||||
// 错误处理
|
||||
it('应该优雅地处理错误', async () => {});
|
||||
```
|
||||
|
||||
### 设置测试数据
|
||||
|
||||
```typescript
|
||||
// 模拟返回值
|
||||
mockExecImpl.mockImplementation((cmd: string, callback: any) => {
|
||||
if (cmd === '命令') {
|
||||
callback(null, { stdout: '成功输出' });
|
||||
} else {
|
||||
callback(new Error('错误信息'), null);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 断言
|
||||
|
||||
使用 Vitest 的断言函数验证结果:
|
||||
|
||||
```typescript
|
||||
// 检查基本值
|
||||
expect(result.success).toBe(true);
|
||||
|
||||
// 检查对象部分匹配
|
||||
expect(result.data).toMatchObject({
|
||||
key: 'value',
|
||||
});
|
||||
|
||||
// 检查数组
|
||||
expect(result.items).toHaveLength(2);
|
||||
expect(result.items[0].name).toBe('expectedName');
|
||||
|
||||
// 检查函数调用
|
||||
expect(mockFunction).toHaveBeenCalledWith(expectedArgs);
|
||||
expect(mockFunction).toHaveBeenCalledTimes(1);
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **隔离测试**:确保每个测试互不影响,使用 `beforeEach` 重置模拟和状态
|
||||
2. **全面覆盖**:测试正常流程、边界条件和错误处理
|
||||
3. **清晰命名**:测试名称应清晰描述测试内容和预期结果
|
||||
4. **避免测试实现细节**:测试应该关注行为而非实现细节,使代码重构不会破坏测试
|
||||
5. **模拟外部依赖**:使用 `vi.mock()` 模拟所有外部依赖,减少测试的不确定性
|
||||
|
||||
## 示例:测试 IPC 事件处理方法
|
||||
|
||||
```typescript
|
||||
it('应该正确处理 IPC 事件', async () => {
|
||||
// 模拟依赖
|
||||
mockSomething.mockReturnValue({ result: 'success' });
|
||||
|
||||
// 调用 IPC 方法
|
||||
const result = await controller.ipcMethodName({
|
||||
param1: 'value1',
|
||||
param2: 'value2',
|
||||
});
|
||||
|
||||
// 验证结果
|
||||
expect(result).toEqual({
|
||||
success: true,
|
||||
data: { result: 'success' },
|
||||
});
|
||||
|
||||
// 验证依赖调用
|
||||
expect(mockSomething).toHaveBeenCalledWith('value1', 'value2');
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,154 @@
|
||||
---
|
||||
description: 当要做 electron 相关工作时
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
**桌面端新功能实现指南**
|
||||
|
||||
## 桌面端应用架构概述
|
||||
|
||||
LobeChat 桌面端基于 Electron 框架构建,采用主进程-渲染进程架构:
|
||||
|
||||
1. **主进程 (Main Process)**:
|
||||
- 位置:`apps/desktop/src/main`
|
||||
- 职责:控制应用生命周期、系统API交互、窗口管理、后台服务
|
||||
|
||||
2. **渲染进程 (Renderer Process)**:
|
||||
- 复用 Web 端代码,位于 `src` 目录
|
||||
- 通过 IPC 与主进程通信
|
||||
|
||||
3. **预加载脚本 (Preload)**:
|
||||
- 位置:`apps/desktop/src/preload`
|
||||
- 职责:安全地暴露主进程功能给渲染进程
|
||||
|
||||
## 添加新桌面端功能流程
|
||||
|
||||
### 1. 确定功能需求与设计
|
||||
|
||||
首先确定新功能的需求和设计,包括:
|
||||
- 功能描述和用例
|
||||
- 是否需要系统级API(如文件系统、网络等)
|
||||
- UI/UX设计(如必要)
|
||||
- 与现有功能的交互方式
|
||||
|
||||
### 2. 在主进程中实现核心功能
|
||||
|
||||
1. **创建控制器 (Controller)**
|
||||
- 位置:`apps/desktop/src/main/controllers/`
|
||||
- 示例:创建 `NewFeatureCtr.ts`
|
||||
- 规范:按 `_template.ts` 模板格式实现
|
||||
- 注册:在 `apps/desktop/src/main/controllers/index.ts` 导出
|
||||
|
||||
2. **定义 IPC 事件处理器**
|
||||
- 使用 `@ipcClientEvent('eventName')` 装饰器注册事件处理函数
|
||||
- 处理函数应接收前端传递的参数并返回结果
|
||||
- 处理可能的错误情况
|
||||
|
||||
3. **实现业务逻辑**
|
||||
- 可能需要调用 Electron API 或 Node.js 原生模块
|
||||
- 对于复杂功能,可以创建专门的服务类 (`services/`)
|
||||
|
||||
### 3. 定义 IPC 通信类型
|
||||
|
||||
1. **在共享类型定义中添加新类型**
|
||||
- 位置:`packages/electron-client-ipc/src/types.ts`
|
||||
- 添加参数类型接口(如 `NewFeatureParams`)
|
||||
- 添加返回结果类型接口(如 `NewFeatureResult`)
|
||||
|
||||
### 4. 在渲染进程实现前端功能
|
||||
|
||||
1. **创建服务层**
|
||||
- 位置:`src/services/electron/`
|
||||
- 添加服务方法调用 IPC
|
||||
- 使用 `dispatch` 或 `invoke` 函数
|
||||
|
||||
```typescript
|
||||
// src/services/electron/newFeatureService.ts
|
||||
import { dispatch } from '@lobechat/electron-client-ipc';
|
||||
import { NewFeatureParams } from 'types';
|
||||
|
||||
export const newFeatureService = async (params: NewFeatureParams) => {
|
||||
return dispatch('newFeatureEventName', params);
|
||||
};
|
||||
```
|
||||
|
||||
2. **实现 Store Action**
|
||||
- 位置:`src/store/`
|
||||
- 添加状态更新逻辑和错误处理
|
||||
|
||||
3. **添加 UI 组件**
|
||||
- 根据需要在适当位置添加UI组件
|
||||
- 通过 Store 或 Service 层调用功能
|
||||
|
||||
### 5. 如果是新增内置工具,遵循工具实现流程
|
||||
|
||||
参考 [desktop-local-tools-implement.mdc](mdc:desktop-local-tools-implement.mdc) 了解更多关于添加内置工具的详细步骤。
|
||||
|
||||
### 6. 添加测试
|
||||
|
||||
1. **单元测试**
|
||||
- 位置:`apps/desktop/src/main/controllers/__tests__/`
|
||||
- 测试主进程组件功能
|
||||
|
||||
2. **集成测试**
|
||||
- 测试 IPC 通信和功能完整流程
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **安全性考虑**
|
||||
- 谨慎处理用户数据和文件系统访问
|
||||
- 适当验证和清理输入数据
|
||||
- 限制暴露给渲染进程的API范围
|
||||
|
||||
2. **性能优化**
|
||||
- 对于耗时操作,考虑使用异步方法
|
||||
- 大型数据传输考虑分批处理
|
||||
|
||||
3. **用户体验**
|
||||
- 为长时间操作添加进度指示
|
||||
- 提供适当的错误反馈
|
||||
- 考虑操作的可撤销性
|
||||
|
||||
4. **代码组织**
|
||||
- 遵循项目现有的命名和代码风格约定
|
||||
- 为新功能添加适当的文档和注释
|
||||
- 功能模块化,避免过度耦合
|
||||
|
||||
## 示例:实现系统通知功能
|
||||
|
||||
```typescript
|
||||
// apps/desktop/src/main/controllers/NotificationCtr.ts
|
||||
import { BrowserWindow, Notification } from 'electron';
|
||||
import { ipcClientEvent } from 'electron-client-ipc';
|
||||
|
||||
interface ShowNotificationParams {
|
||||
title: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export class NotificationCtr {
|
||||
@ipcClientEvent('showNotification')
|
||||
async handleShowNotification({ title, body }: ShowNotificationParams) {
|
||||
try {
|
||||
if (!Notification.isSupported()) {
|
||||
return { success: false, error: 'Notifications not supported' };
|
||||
}
|
||||
|
||||
const notification = new Notification({
|
||||
title,
|
||||
body,
|
||||
});
|
||||
|
||||
notification.show();
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error('Failed to show notification:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Unknown error'
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,197 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
**桌面端菜单配置指南**
|
||||
|
||||
## 菜单系统概述
|
||||
|
||||
LobeChat 桌面应用有三种主要的菜单类型:
|
||||
|
||||
1. **应用菜单 (App Menu)**:显示在应用窗口顶部(macOS)或窗口标题栏(Windows/Linux)
|
||||
2. **上下文菜单 (Context Menu)**:右键点击时显示的菜单
|
||||
3. **托盘菜单 (Tray Menu)**:点击系统托盘图标显示的菜单
|
||||
|
||||
## 菜单相关文件结构
|
||||
|
||||
```
|
||||
apps/desktop/src/main/
|
||||
├── menus/ # 菜单定义
|
||||
│ ├── appMenu.ts # 应用菜单配置
|
||||
│ ├── contextMenu.ts # 上下文菜单配置
|
||||
│ └── factory.ts # 菜单工厂函数
|
||||
├── controllers/
|
||||
│ ├── MenuCtr.ts # 菜单控制器
|
||||
│ └── TrayMenuCtr.ts # 托盘菜单控制器
|
||||
```
|
||||
|
||||
## 菜单配置流程
|
||||
|
||||
### 1. 应用菜单配置
|
||||
|
||||
应用菜单在 `apps/desktop/src/main/menus/appMenu.ts` 中定义:
|
||||
|
||||
1. **导入依赖**
|
||||
```typescript
|
||||
import { app, BrowserWindow, Menu, MenuItem, MenuItemConstructorOptions } from 'electron';
|
||||
import { is } from 'electron-util';
|
||||
```
|
||||
|
||||
2. **定义菜单项**
|
||||
- 使用 `MenuItemConstructorOptions` 类型定义菜单结构
|
||||
- 每个菜单项可以包含:label, accelerator (快捷键), role, submenu, click 等属性
|
||||
|
||||
3. **创建菜单工厂函数**
|
||||
```typescript
|
||||
export const createAppMenu = (win: BrowserWindow) => {
|
||||
const template = [
|
||||
// 定义菜单项...
|
||||
];
|
||||
|
||||
return Menu.buildFromTemplate(template);
|
||||
};
|
||||
```
|
||||
|
||||
4. **注册菜单**
|
||||
- 在 `MenuCtr.ts` 控制器中使用 `Menu.setApplicationMenu(menu)` 设置应用菜单
|
||||
|
||||
### 2. 上下文菜单配置
|
||||
|
||||
上下文菜单通常在特定元素上右键点击时显示:
|
||||
|
||||
1. **在主进程中定义菜单模板**
|
||||
```typescript
|
||||
// apps/desktop/src/main/menus/contextMenu.ts
|
||||
export const createContextMenu = () => {
|
||||
const template = [
|
||||
// 定义菜单项...
|
||||
];
|
||||
|
||||
return Menu.buildFromTemplate(template);
|
||||
};
|
||||
```
|
||||
|
||||
2. **在适当的事件处理器中显示菜单**
|
||||
```typescript
|
||||
const menu = createContextMenu();
|
||||
menu.popup();
|
||||
```
|
||||
|
||||
### 3. 托盘菜单配置
|
||||
|
||||
托盘菜单在 `TrayMenuCtr.ts` 中配置:
|
||||
|
||||
1. **创建托盘图标**
|
||||
```typescript
|
||||
this.tray = new Tray(trayIconPath);
|
||||
```
|
||||
|
||||
2. **定义托盘菜单**
|
||||
```typescript
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{ label: '显示主窗口', click: this.showMainWindow },
|
||||
{ type: 'separator' },
|
||||
{ label: '退出', click: () => app.quit() },
|
||||
]);
|
||||
```
|
||||
|
||||
3. **设置托盘菜单**
|
||||
```typescript
|
||||
this.tray.setContextMenu(contextMenu);
|
||||
```
|
||||
|
||||
## 多语言支持
|
||||
|
||||
为菜单添加多语言支持:
|
||||
|
||||
1. **导入本地化工具**
|
||||
```typescript
|
||||
import { i18n } from '../locales';
|
||||
```
|
||||
|
||||
2. **使用翻译函数**
|
||||
```typescript
|
||||
const template = [
|
||||
{
|
||||
label: i18n.t('menu.file'),
|
||||
submenu: [
|
||||
{ label: i18n.t('menu.new'), click: createNew },
|
||||
// ...
|
||||
]
|
||||
},
|
||||
// ...
|
||||
];
|
||||
```
|
||||
|
||||
3. **在语言切换时更新菜单**
|
||||
在 `MenuCtr.ts` 中监听语言变化事件并重新创建菜单
|
||||
|
||||
## 添加新菜单项流程
|
||||
|
||||
1. **确定菜单位置**
|
||||
- 决定添加到哪个菜单(应用菜单、上下文菜单或托盘菜单)
|
||||
- 确定在菜单中的位置(主菜单项或子菜单项)
|
||||
|
||||
2. **定义菜单项**
|
||||
```typescript
|
||||
const newMenuItem: MenuItemConstructorOptions = {
|
||||
label: '新功能',
|
||||
accelerator: 'CmdOrCtrl+N',
|
||||
click: (_, window) => {
|
||||
// 处理点击事件
|
||||
if (window) window.webContents.send('trigger-new-feature');
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **添加到菜单模板**
|
||||
将新菜单项添加到相应的菜单模板中
|
||||
|
||||
4. **对于与渲染进程交互的功能**
|
||||
- 使用 `window.webContents.send()` 发送 IPC 消息到渲染进程
|
||||
- 在渲染进程中监听该消息并处理
|
||||
|
||||
## 菜单项启用/禁用控制
|
||||
|
||||
动态控制菜单项状态:
|
||||
|
||||
1. **保存对菜单项的引用**
|
||||
```typescript
|
||||
this.menuItems = {};
|
||||
const menu = Menu.buildFromTemplate(template);
|
||||
this.menuItems.newFeature = menu.getMenuItemById('new-feature');
|
||||
```
|
||||
|
||||
2. **根据条件更新状态**
|
||||
```typescript
|
||||
updateMenuState(state) {
|
||||
if (this.menuItems.newFeature) {
|
||||
this.menuItems.newFeature.enabled = state.canUseNewFeature;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **使用标准角色**
|
||||
- 尽可能使用 Electron 预定义的角色(如 `role: 'copy'`)以获得本地化和一致的行为
|
||||
|
||||
2. **平台特定菜单**
|
||||
- 使用 `process.platform` 检查为不同平台提供不同菜单
|
||||
```typescript
|
||||
if (process.platform === 'darwin') {
|
||||
template.unshift({ role: 'appMenu' });
|
||||
}
|
||||
```
|
||||
|
||||
3. **快捷键冲突**
|
||||
- 避免与系统快捷键冲突
|
||||
- 使用 `CmdOrCtrl` 代替 `Ctrl` 以支持 macOS 和 Windows/Linux
|
||||
|
||||
4. **保持菜单简洁**
|
||||
- 避免过多嵌套的子菜单
|
||||
- 将相关功能分组在一起
|
||||
|
||||
5. **添加分隔符**
|
||||
- 使用 `{ type: 'separator' }` 在逻辑上分隔不同组的菜单项
|
||||
@@ -0,0 +1,296 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
**桌面端窗口管理指南**
|
||||
|
||||
## 窗口管理概述
|
||||
|
||||
LobeChat 桌面应用使用 Electron 的 `BrowserWindow` 管理应用窗口。主要的窗口管理功能包括:
|
||||
|
||||
1. **窗口创建和配置**
|
||||
2. **窗口状态管理**(大小、位置、最大化等)
|
||||
3. **多窗口协调**
|
||||
4. **窗口事件处理**
|
||||
|
||||
## 相关文件结构
|
||||
|
||||
```
|
||||
apps/desktop/src/main/
|
||||
├── appBrowsers.ts # 窗口管理的核心文件
|
||||
├── controllers/
|
||||
│ └── BrowserWindowsCtr.ts # 窗口控制器
|
||||
└── modules/
|
||||
└── browserWindowManager.ts # 窗口管理模块
|
||||
```
|
||||
|
||||
## 窗口管理流程
|
||||
|
||||
### 1. 窗口创建
|
||||
|
||||
在 `appBrowsers.ts` 或 `BrowserWindowsCtr.ts` 中定义窗口创建逻辑:
|
||||
|
||||
```typescript
|
||||
export const createMainWindow = () => {
|
||||
const mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
minWidth: 600,
|
||||
minHeight: 400,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
// 其他窗口配置项...
|
||||
});
|
||||
|
||||
// 加载应用内容
|
||||
if (isDev) {
|
||||
mainWindow.loadURL('http://localhost:3000');
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
mainWindow.loadFile(path.join(__dirname, '../../renderer/index.html'));
|
||||
}
|
||||
|
||||
return mainWindow;
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 窗口状态管理
|
||||
|
||||
实现窗口状态持久化保存和恢复:
|
||||
|
||||
1. **保存窗口状态**
|
||||
```typescript
|
||||
const saveWindowState = (window: BrowserWindow) => {
|
||||
if (!window.isMinimized() && !window.isMaximized()) {
|
||||
const position = window.getPosition();
|
||||
const size = window.getSize();
|
||||
|
||||
settings.set('windowState', {
|
||||
x: position[0],
|
||||
y: position[1],
|
||||
width: size[0],
|
||||
height: size[1],
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
2. **恢复窗口状态**
|
||||
```typescript
|
||||
const restoreWindowState = (window: BrowserWindow) => {
|
||||
const savedState = settings.get('windowState');
|
||||
|
||||
if (savedState) {
|
||||
window.setBounds({
|
||||
x: savedState.x,
|
||||
y: savedState.y,
|
||||
width: savedState.width,
|
||||
height: savedState.height,
|
||||
});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. **监听窗口事件**
|
||||
```typescript
|
||||
window.on('close', () => saveWindowState(window));
|
||||
window.on('moved', () => saveWindowState(window));
|
||||
window.on('resized', () => saveWindowState(window));
|
||||
```
|
||||
|
||||
### 3. 实现多窗口管理
|
||||
|
||||
对于需要多窗口支持的功能:
|
||||
|
||||
1. **跟踪窗口**
|
||||
```typescript
|
||||
export class WindowManager {
|
||||
private windows: Map<string, BrowserWindow> = new Map();
|
||||
|
||||
createWindow(id: string, options: BrowserWindowConstructorOptions) {
|
||||
const window = new BrowserWindow(options);
|
||||
this.windows.set(id, window);
|
||||
|
||||
window.on('closed', () => {
|
||||
this.windows.delete(id);
|
||||
});
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
getWindow(id: string) {
|
||||
return this.windows.get(id);
|
||||
}
|
||||
|
||||
getAllWindows() {
|
||||
return Array.from(this.windows.values());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. **窗口间通信**
|
||||
```typescript
|
||||
// 从一个窗口向另一个窗口发送消息
|
||||
sendMessageToWindow(targetWindowId, channel, data) {
|
||||
const targetWindow = this.getWindow(targetWindowId);
|
||||
if (targetWindow) {
|
||||
targetWindow.webContents.send(channel, data);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 窗口与渲染进程通信
|
||||
|
||||
通过 IPC 实现窗口操作:
|
||||
|
||||
1. **在主进程中注册 IPC 处理器**
|
||||
```typescript
|
||||
// BrowserWindowsCtr.ts
|
||||
@ipcClientEvent('minimizeWindow')
|
||||
handleMinimizeWindow() {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow) {
|
||||
focusedWindow.minimize();
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@ipcClientEvent('maximizeWindow')
|
||||
handleMaximizeWindow() {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow) {
|
||||
if (focusedWindow.isMaximized()) {
|
||||
focusedWindow.restore();
|
||||
} else {
|
||||
focusedWindow.maximize();
|
||||
}
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@ipcClientEvent('closeWindow')
|
||||
handleCloseWindow() {
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
if (focusedWindow) {
|
||||
focusedWindow.close();
|
||||
}
|
||||
return { success: true };
|
||||
}
|
||||
```
|
||||
|
||||
2. **在渲染进程中调用**
|
||||
```typescript
|
||||
// src/services/electron/windowService.ts
|
||||
import { dispatch } from '@lobechat/electron-client-ipc';
|
||||
|
||||
export const windowService = {
|
||||
minimize: () => dispatch('minimizeWindow'),
|
||||
maximize: () => dispatch('maximizeWindow'),
|
||||
close: () => dispatch('closeWindow'),
|
||||
};
|
||||
```
|
||||
|
||||
### 5. 自定义窗口控制 (无边框窗口)
|
||||
|
||||
对于自定义窗口标题栏:
|
||||
|
||||
1. **创建无边框窗口**
|
||||
```typescript
|
||||
const window = new BrowserWindow({
|
||||
frame: false,
|
||||
titleBarStyle: 'hidden',
|
||||
// 其他选项...
|
||||
});
|
||||
```
|
||||
|
||||
2. **在渲染进程中实现拖拽区域**
|
||||
```css
|
||||
/* CSS */
|
||||
.titlebar {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.titlebar-button {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **性能考虑**
|
||||
- 避免创建过多窗口
|
||||
- 使用 `show: false` 创建窗口,在内容加载完成后再显示,避免白屏
|
||||
|
||||
2. **安全性**
|
||||
- 始终设置适当的 `webPreferences` 确保安全
|
||||
```typescript
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: true,
|
||||
}
|
||||
```
|
||||
|
||||
3. **跨平台兼容性**
|
||||
- 考虑不同操作系统的窗口行为差异
|
||||
- 使用 `process.platform` 为不同平台提供特定实现
|
||||
|
||||
4. **崩溃恢复**
|
||||
- 监听 `webContents.on('crashed')` 事件处理崩溃
|
||||
- 提供崩溃恢复选项
|
||||
|
||||
5. **内存管理**
|
||||
- 确保窗口关闭时清理所有相关资源
|
||||
- 使用 `window.on('closed')` 而不是 `window.on('close')` 进行最终清理
|
||||
|
||||
## 示例:创建设置窗口
|
||||
|
||||
```typescript
|
||||
// apps/desktop/src/main/controllers/BrowserWindowsCtr.ts
|
||||
|
||||
@ipcClientEvent('openSettings')
|
||||
handleOpenSettings() {
|
||||
// 检查设置窗口是否已经存在
|
||||
if (this.settingsWindow && !this.settingsWindow.isDestroyed()) {
|
||||
// 如果窗口已存在,将其置于前台
|
||||
this.settingsWindow.focus();
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
// 创建新窗口
|
||||
this.settingsWindow = new BrowserWindow({
|
||||
width: 800,
|
||||
height: 600,
|
||||
title: 'Settings',
|
||||
parent: this.mainWindow, // 设置父窗口,使其成为模态窗口
|
||||
modal: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '../preload/index.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
},
|
||||
});
|
||||
|
||||
// 加载设置页面
|
||||
if (isDev) {
|
||||
this.settingsWindow.loadURL('http://localhost:3000/settings');
|
||||
} else {
|
||||
this.settingsWindow.loadFile(
|
||||
path.join(__dirname, '../../renderer/index.html'),
|
||||
{ hash: 'settings' }
|
||||
);
|
||||
}
|
||||
|
||||
// 监听窗口关闭事件
|
||||
this.settingsWindow.on('closed', () => {
|
||||
this.settingsWindow = null;
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
description:
|
||||
globs: src/database/schemas/*
|
||||
alwaysApply: false
|
||||
---
|
||||
# Drizzle ORM Schema Style Guide for lobe-chat
|
||||
|
||||
This document outlines the conventions and best practices for defining PostgreSQL Drizzle ORM schemas within the lobe-chat project.
|
||||
|
||||
## Configuration
|
||||
|
||||
- Drizzle configuration is managed in [drizzle.config.ts](mdc:drizzle.config.ts)
|
||||
- Schema files are located in the src/database/schemas/ directory
|
||||
- Migration files are output to `src/database/migrations/`
|
||||
- The project uses `postgresql` dialect with `strict: true`
|
||||
|
||||
## Helper Functions
|
||||
|
||||
Commonly used column definitions, especially for timestamps, are centralized in [src/database/schemas/_helpers.ts](mdc:src/database/schemas/_helpers.ts):
|
||||
- `timestamptz(name: string)`: Creates a timestamp column with timezone
|
||||
- `createdAt()`, `updatedAt()`, `accessedAt()`: Helper functions for standard timestamp columns
|
||||
- `timestamps`: An object `{ createdAt, updatedAt, accessedAt }` for easy inclusion in table definitions
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
- **Table Names**: Use plural snake_case (e.g., `users`, `agents`, `session_groups`)
|
||||
- **Column Names**: Use snake_case (e.g., `user_id`, `created_at`, `background_color`)
|
||||
|
||||
## Column Definitions
|
||||
|
||||
### Primary Keys (PKs)
|
||||
- Typically `text('id')` (or `varchar('id')` for some OIDC tables)
|
||||
- Often use `.$defaultFn(() => idGenerator('table_name'))` for automatic ID generation with meaningful prefixes
|
||||
- **ID Prefix Purpose**: Makes it easy for users and developers to distinguish different entity types at a glance
|
||||
- For internal/system tables that users don't need to see, can use `uuid` or auto-increment keys
|
||||
- Composite PKs are defined using `primaryKey({ columns: [t.colA, t.colB] })`
|
||||
|
||||
### Foreign Keys (FKs)
|
||||
- Defined using `.references(() => otherTable.id, { onDelete: 'cascade' | 'set null' | 'no action' })`
|
||||
- FK columns are usually named `related_table_singular_name_id` (e.g., `user_id` references `users.id`)
|
||||
- Most tables include a `user_id` column referencing `users.id` with `onDelete: 'cascade'`
|
||||
|
||||
### Timestamps
|
||||
- Consistently use the `...timestamps` spread from [_helpers.ts](mdc:src/database/schemas/_helpers.ts) for `created_at`, `updated_at`, and `accessed_at` columns
|
||||
|
||||
### Default Values
|
||||
- `.$defaultFn(() => expression)` for dynamic defaults (e.g., `idGenerator()`, `randomSlug()`)
|
||||
- `.default(staticValue)` for static defaults (e.g., `boolean('enabled').default(true)`)
|
||||
|
||||
### Indexes
|
||||
- Defined in the table's second argument: `pgTable('name', {...columns}, (t) => ({ indexName: indexType().on(...) }))`
|
||||
- Use `uniqueIndex()` for unique constraints and `index()` for non-unique indexes
|
||||
- Naming pattern: `table_name_column(s)_idx` or `table_name_column(s)_unique`
|
||||
- Many tables feature a `clientId: text('client_id')` column, often part of a composite unique index with `user_id`
|
||||
|
||||
### Data Types
|
||||
- Common types: `text`, `varchar`, `jsonb`, `boolean`, `integer`, `uuid`, `pgTable`
|
||||
- For `jsonb` fields, specify the TypeScript type using `.$type<MyType>()` for better type safety
|
||||
|
||||
## Zod Schemas & Type Inference
|
||||
|
||||
- Utilize `drizzle-zod` to generate Zod schemas for validation:
|
||||
- `createInsertSchema(tableName)`
|
||||
- `createSelectSchema(tableName)` (less common)
|
||||
- Export inferred types: `export type NewEntity = typeof tableName.$inferInsert;` and `export type EntityItem = typeof tableName.$inferSelect;`
|
||||
|
||||
## Relations
|
||||
|
||||
- Table relationships are defined centrally in [src/database/schemas/relations.ts](mdc:src/database/schemas/relations.ts) using the `relations()` utility from `drizzle-orm`
|
||||
|
||||
## Code Style & Structure
|
||||
|
||||
- **File Organization**: Each main database entity typically has its own schema file (e.g., [user.ts](mdc:src/database/schemas/user.ts), [agent.ts](mdc:src/database/schemas/agent.ts))
|
||||
- All schemas are re-exported from [src/database/schemas/index.ts](mdc:src/database/schemas/index.ts)
|
||||
- **ESLint**: Files often start with `/* eslint-disable sort-keys-fix/sort-keys-fix */`
|
||||
- **Comments**: Use JSDoc-style comments to explain the purpose of tables and complex columns, fields that are self-explanatory do not require jsdoc explanations, such as id, user_id, etc.
|
||||
|
||||
## Example Pattern
|
||||
|
||||
```typescript
|
||||
// From src/database/schemas/agent.ts
|
||||
export const agents = pgTable(
|
||||
'agents',
|
||||
{
|
||||
id: text('id')
|
||||
.primaryKey()
|
||||
.$defaultFn(() => idGenerator('agents'))
|
||||
.notNull(),
|
||||
slug: varchar('slug', { length: 100 })
|
||||
.$defaultFn(() => randomSlug(4))
|
||||
.unique(),
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
clientId: text('client_id'),
|
||||
chatConfig: jsonb('chat_config').$type<LobeAgentChatConfig>(),
|
||||
...timestamps,
|
||||
},
|
||||
// return array instead of object, the object style is deprecated
|
||||
(t) => [
|
||||
uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId),
|
||||
],
|
||||
);
|
||||
|
||||
export const insertAgentSchema = createInsertSchema(agents);
|
||||
export type NewAgent = typeof agents.$inferInsert;
|
||||
export type AgentItem = typeof agents.$inferSelect;
|
||||
```
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### 1. userId + clientId Pattern (Legacy)
|
||||
Some existing tables include both fields for different purposes:
|
||||
|
||||
```typescript
|
||||
// Example from agents table (legacy pattern)
|
||||
userId: text('user_id')
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.notNull(),
|
||||
clientId: text('client_id'),
|
||||
|
||||
// Usually with a composite unique index
|
||||
clientIdUnique: uniqueIndex('agents_client_id_user_id_unique').on(t.clientId, t.userId),
|
||||
```
|
||||
|
||||
- **`userId`**: Server-side user association, ensures data belongs to specific user
|
||||
- **`clientId`**: Unique key for import/export operations, supports data migration between instances
|
||||
- **Current Status**: New tables should NOT include `clientId` unless specifically needed for import/export functionality
|
||||
- **Note**: This pattern is being phased out for new features to simplify the schema
|
||||
|
||||
### 2. Junction Tables (Many-to-Many Relationships)
|
||||
Use composite primary keys for relationship tables:
|
||||
|
||||
```typescript
|
||||
// Example: agents_knowledge_bases (from agent.ts)
|
||||
export const agentsKnowledgeBases = pgTable(
|
||||
'agents_knowledge_bases',
|
||||
{
|
||||
agentId: text('agent_id').references(() => agents.id, { onDelete: 'cascade' }).notNull(),
|
||||
knowledgeBaseId: text('knowledge_base_id').references(() => knowledgeBases.id, { onDelete: 'cascade' }).notNull(),
|
||||
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }).notNull(),
|
||||
enabled: boolean('enabled').default(true),
|
||||
...timestamps,
|
||||
},
|
||||
(t) => [
|
||||
primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
|
||||
],
|
||||
);
|
||||
```
|
||||
|
||||
**Pattern**: `{entity1}Id` + `{entity2}Id` as composite PK, plus `userId` for ownership
|
||||
|
||||
### 3. OIDC Tables Special Patterns
|
||||
OIDC tables use `varchar` IDs instead of `text` with custom generators:
|
||||
|
||||
```typescript
|
||||
// Example from oidc.ts
|
||||
export const oidcAuthorizationCodes = pgTable('oidc_authorization_codes', {
|
||||
id: varchar('id', { length: 255 }).primaryKey(), // varchar not text
|
||||
data: jsonb('data').notNull(),
|
||||
expiresAt: timestamptz('expires_at').notNull(),
|
||||
// ... other fields
|
||||
});
|
||||
```
|
||||
|
||||
**Reason**: OIDC standards expect specific ID formats and lengths
|
||||
|
||||
### 4. File Processing with Async Tasks
|
||||
File-related tables reference async task IDs for background processing:
|
||||
|
||||
```typescript
|
||||
// Example from files table
|
||||
export const files = pgTable('files', {
|
||||
// ... other fields
|
||||
chunkTaskId: uuid('chunk_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
||||
embeddingTaskId: uuid('embedding_task_id').references(() => asyncTasks.id, { onDelete: 'set null' }),
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
**Purpose**:
|
||||
- Track file chunking progress (breaking files into smaller pieces)
|
||||
- Track embedding generation progress (converting text to vectors)
|
||||
- Allow querying task status and handling failures
|
||||
|
||||
### 5. Slug Pattern (Legacy)
|
||||
Some entities include auto-generated slugs - this is legacy code:
|
||||
|
||||
```typescript
|
||||
slug: varchar('slug', { length: 100 })
|
||||
.$defaultFn(() => randomSlug(4))
|
||||
.unique(),
|
||||
|
||||
// Often with composite unique constraint
|
||||
slugUserIdUnique: uniqueIndex('slug_user_id_unique').on(t.slug, t.userId),
|
||||
```
|
||||
|
||||
**Current usage**: Only used to identify default agents/sessions (legacy pattern)
|
||||
**Future refactor**: Will likely be replaced with `isDefault: boolean()` field
|
||||
**Note**: Avoid using slugs for new features - prefer explicit boolean flags for status tracking
|
||||
|
||||
By following these guidelines, maintain consistency, type safety, and maintainability across database schema definitions.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
description:
|
||||
globs: src/locales/**/*
|
||||
alwaysApply: false
|
||||
---
|
||||
read [i18n.mdc](mdc:.cursor/rules/i18n/i18n.mdc)
|
||||
@@ -0,0 +1,181 @@
|
||||
---
|
||||
description: i18n workflow and troubleshooting
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# LobeChat 国际化指南
|
||||
|
||||
## 架构概览
|
||||
|
||||
LobeChat 使用 react-i18next 进行国际化,采用良好的命名空间架构:
|
||||
|
||||
- 默认语言:中文(zh-CN),作为源语言
|
||||
- 支持语言:18 种语言,包括英语、日语、韩语、阿拉伯语等
|
||||
- 框架:react-i18next 配合 Next.js app router
|
||||
- 翻译自动化:@lobehub/i18n-cli 用于自动翻译,配置文件:.i18nrc.js
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
src/locales/
|
||||
├── default/ # 源语言文件(zh-CN)
|
||||
│ ├── index.ts # 命名空间导出
|
||||
│ ├── common.ts # 通用翻译
|
||||
│ ├── chat.ts # 聊天相关翻译
|
||||
│ ├── setting.ts # 设置翻译
|
||||
│ └── ... # 其他命名空间文件
|
||||
└── resources.ts # 类型定义和语言配置
|
||||
|
||||
locales/ # 翻译文件
|
||||
├── en-US/ # 英语翻译
|
||||
│ ├── common.json # 通用翻译
|
||||
│ ├── chat.json # 聊天翻译
|
||||
│ ├── setting.json # 设置翻译
|
||||
│ └── ... # 其他命名空间 JSON 文件
|
||||
├── ja-JP/ # 日语翻译
|
||||
│ ├── common.json
|
||||
│ ├── chat.json
|
||||
│ └── ...
|
||||
└── ... # 其他语言文件夹
|
||||
```
|
||||
|
||||
## 添加新翻译的工作流程
|
||||
|
||||
### 1. 添加新的翻译键
|
||||
|
||||
第一步:在 src/locales/default 目录下的相应命名空间文件中添加翻译键
|
||||
|
||||
```typescript
|
||||
// 示例:src/locales/default/common.ts
|
||||
export default {
|
||||
// ... 现有键
|
||||
newFeature: {
|
||||
title: "新功能标题",
|
||||
description: "功能描述文案",
|
||||
button: "操作按钮",
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
第二步:如果创建新命名空间,需要在 src/locales/default/index.ts 中导出
|
||||
|
||||
```typescript
|
||||
import newNamespace from "./newNamespace";
|
||||
|
||||
const resources = {
|
||||
// ... 现有命名空间
|
||||
newNamespace,
|
||||
} as const;
|
||||
```
|
||||
|
||||
### 2. 翻译过程
|
||||
|
||||
开发模式:
|
||||
|
||||
一般情况下不需要你帮我跑自动翻译工具,跑一次很久,需要的时候我会自己跑。
|
||||
但是为了立马能看到效果,还是需要先翻译 `locales/zh-CN/namespace.json`,不需要翻译其它语言。
|
||||
|
||||
生产模式:
|
||||
|
||||
```bash
|
||||
# 为所有语言生成翻译
|
||||
npm run i18n
|
||||
```
|
||||
|
||||
## 在组件中使用
|
||||
|
||||
### 基本用法
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
const MyComponent = () => {
|
||||
const { t } = useTranslation("common");
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t("newFeature.title")}</h1>
|
||||
<p>{t("newFeature.description")}</p>
|
||||
<button>{t("newFeature.button")}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 带参数的用法
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation("common");
|
||||
|
||||
<p>{t("welcome.message", { name: "John" })}</p>;
|
||||
|
||||
// 对应的语言文件:
|
||||
// welcome: { message: '欢迎 {{name}} 使用!' }
|
||||
```
|
||||
|
||||
### 多个命名空间
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation(['common', 'chat']);
|
||||
|
||||
<button>{t('common:save')}</button>
|
||||
<span>{t('chat:typing')}</span>
|
||||
```
|
||||
|
||||
## 类型安全
|
||||
|
||||
项目使用 TypeScript 实现类型安全的翻译,类型从 src/locales/resources.ts 自动生成:
|
||||
|
||||
```typescript
|
||||
import type { DefaultResources, NS, Locales } from "@/locales/resources";
|
||||
|
||||
// 可用类型:
|
||||
// - NS: 可用命名空间键 ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: 支持的语言代码 ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
|
||||
const namespace: NS = "common";
|
||||
const locale: Locales = "en-US";
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 命名空间组织
|
||||
|
||||
- common: 共享 UI 元素(按钮、标签、操作)
|
||||
- chat: 聊天特定功能
|
||||
- setting: 配置和设置
|
||||
- error: 错误消息和处理
|
||||
- [feature]: 功能特定或页面特定的命名空间
|
||||
- components: 可复用组件文案
|
||||
|
||||
### 2. 键命名约定
|
||||
|
||||
```typescript
|
||||
// ✅ 好:层次结构
|
||||
export default {
|
||||
modal: {
|
||||
confirm: {
|
||||
title: "确认操作",
|
||||
message: "确定要执行此操作吗?",
|
||||
actions: {
|
||||
confirm: "确认",
|
||||
cancel: "取消",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ❌ 避免:扁平结构
|
||||
export default {
|
||||
modalConfirmTitle: "确认操作",
|
||||
modalConfirmMessage: "确定要执行此操作吗?",
|
||||
};
|
||||
```
|
||||
|
||||
## 故障排除
|
||||
|
||||
### 缺少翻译键
|
||||
|
||||
- 检查键是否存在于 src/locales/default/namespace.ts 中
|
||||
- 确保在组件中正确导入命名空间
|
||||
- 确保新命名空间已在 src/locales/default/index.ts 中导出
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
description: react flex layout package `react-layout-kit` usage
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# React Layout Kit 使用指南
|
||||
|
||||
react-layout-kit 是一个功能丰富的 React flex 布局组件库,在 lobe-chat 项目中被广泛使用。以下是重点组件的使用方法:
|
||||
|
||||
## Flexbox 组件
|
||||
|
||||
Flexbox 是最常用的布局组件,用于创建弹性布局,类似于 CSS 的 display: flex。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```jsx
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
|
||||
// 默认垂直布局
|
||||
<Flexbox>
|
||||
<div>子元素1</div>
|
||||
<div>子元素2</div>
|
||||
</Flexbox>
|
||||
|
||||
// 水平布局
|
||||
<Flexbox horizontal>
|
||||
<div>左侧元素</div>
|
||||
<div>右侧元素</div>
|
||||
</Flexbox>
|
||||
```
|
||||
|
||||
### 常用属性
|
||||
|
||||
- horizontal: 布尔值,设置为水平方向布局
|
||||
- flex: 数值或字符串,控制 flex 属性
|
||||
- gap: 数值,设置子元素之间的间距
|
||||
- align: 对齐方式,如 'center', 'flex-start' 等
|
||||
- justify: 主轴对齐方式,如 'space-between', 'center' 等
|
||||
- padding: 内边距值
|
||||
- paddingInline: 水平内边距值
|
||||
- paddingBlock: 垂直内边距值
|
||||
- width/height: 设置宽高,通常用 '100%' 或具体像素值
|
||||
- style: 自定义样式对象
|
||||
|
||||
### 实际应用示例
|
||||
|
||||
```jsx
|
||||
// 经典三栏布局
|
||||
<Flexbox horizontal height={'100%'} width={'100%'}>
|
||||
{/* 左侧边栏 */}
|
||||
<Flexbox
|
||||
width={260}
|
||||
style={{
|
||||
borderRight: `1px solid ${theme.colorBorderSecondary}`,
|
||||
height: '100%',
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
<SidebarContent />
|
||||
</Flexbox>
|
||||
|
||||
{/* 中间内容区 */}
|
||||
<Flexbox flex={1} style={{ height: '100%' }}>
|
||||
{/* 主要内容 */}
|
||||
<Flexbox flex={1} padding={24} style={{ overflowY: 'auto' }}>
|
||||
<MainContent />
|
||||
</Flexbox>
|
||||
|
||||
{/* 底部区域 */}
|
||||
<Flexbox
|
||||
style={{
|
||||
borderTop: `1px solid ${theme.colorBorderSecondary}`,
|
||||
padding: '16px 24px',
|
||||
}}
|
||||
>
|
||||
<Footer />
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
```
|
||||
|
||||
## Center 组件
|
||||
|
||||
Center 是对 Flexbox 的封装,使子元素水平和垂直居中。
|
||||
|
||||
### 基本用法
|
||||
|
||||
```jsx
|
||||
<Center width={'100%'} height={'100%'}>
|
||||
<Content />
|
||||
</Center>
|
||||
```
|
||||
|
||||
Center 组件继承了 Flexbox 的所有属性,同时默认设置了居中对齐。主要用于快速创建居中布局。
|
||||
|
||||
### 实际应用示例
|
||||
|
||||
```jsx
|
||||
// 登录页面居中布局
|
||||
<Flexbox height={'100%'} width={'100%'}>
|
||||
<Center height={'100%'} width={'100%'}>
|
||||
<LoginForm />
|
||||
</Center>
|
||||
</Flexbox>
|
||||
|
||||
// 图标居中显示
|
||||
<Center className={styles.icon} flex={'none'} height={40} width={40}>
|
||||
<Icon icon={icon} size={24} />
|
||||
</Center>
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
- 使用 flex={1} 让组件填充可用空间
|
||||
- 使用 gap 代替传统的 margin 设置元素间距
|
||||
- 嵌套 Flexbox 创建复杂布局
|
||||
- 设置 overflow: 'auto' 使内容可滚动
|
||||
- 使用 horizontal 创建水平布局,默认为垂直布局
|
||||
- 与 antd-style 的 useTheme hook 配合使用创建主题响应式的布局
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
## Project Description
|
||||
|
||||
You are developing an open-source, modern-design AI chat framework: lobe chat.
|
||||
|
||||
Emoji logo: 🤯
|
||||
|
||||
|
||||
## Project Technologies Stack
|
||||
|
||||
read [package.json](mdc:package.json) to know all npm packages you can use.
|
||||
read [folder-structure.mdx](mdc:docs/development/basic/folder-structure.mdx) to learn project structure.
|
||||
|
||||
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
|
||||
- antd-style for css-in-js framework
|
||||
- react-layout-kit for flex layout
|
||||
- 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
|
||||
- aHooks for react hooks library
|
||||
- dayjs for date and 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;
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
description:
|
||||
globs: *.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
# react component 编写指南
|
||||
|
||||
- 如果要写复杂样式的话用 antd-style ,简单的话可以用 style 属性直接写内联样式
|
||||
- 如果需要 flex 布局或者居中布局应该使用 react-layout-kit 的 Flexbox 和 Center 组件
|
||||
- 选择组件时优先顺序应该是 src/components > 安装的组件 package > lobe-ui > antd
|
||||
|
||||
## antd-style token system
|
||||
|
||||
### 访问 token system 的两种方式
|
||||
|
||||
#### 使用 antd-style 的 useTheme hook
|
||||
|
||||
```tsx
|
||||
import { useTheme } from 'antd-style';
|
||||
|
||||
const MyComponent = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
color: theme.colorPrimary,
|
||||
backgroundColor: theme.colorBgContainer,
|
||||
padding: theme.padding,
|
||||
borderRadius: theme.borderRadius
|
||||
}}>
|
||||
使用主题 token 的组件
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用 antd-style 的 createStyles
|
||||
|
||||
```tsx
|
||||
const useStyles = createStyles(({ css, token }) => {
|
||||
return {
|
||||
container: css`
|
||||
background-color: ${token.colorBgContainer};
|
||||
border-radius: ${token.borderRadius}px;
|
||||
padding: ${token.padding}px;
|
||||
color: ${token.colorText};
|
||||
`,
|
||||
title: css`
|
||||
font-size: ${token.fontSizeLG}px;
|
||||
font-weight: ${token.fontWeightStrong};
|
||||
margin-bottom: ${token.marginSM}px;
|
||||
`,
|
||||
content: css`
|
||||
font-size: ${token.fontSize}px;
|
||||
line-height: ${token.lineHeight};
|
||||
`
|
||||
};
|
||||
});
|
||||
|
||||
const Card: FC<CardProps> = ({ title, content }) => {
|
||||
const { styles } = useStyles();
|
||||
|
||||
return (
|
||||
<Flexbox className={styles.container}>
|
||||
<div className={styles.title}>{title}</div>
|
||||
<div className={styles.content}>{content}</div>
|
||||
</Flexbox>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 一些你经常会忘记使用的 token
|
||||
|
||||
请注意使用下面的 token 而不是 css 字面值。可以访问 https://ant.design/docs/react/customize-theme-cn 了解所有 token
|
||||
|
||||
- 动画类
|
||||
- token.motionDurationMid
|
||||
- token.motionEaseInOut
|
||||
- 包围盒属性
|
||||
- token.paddingSM
|
||||
- token.marginLG
|
||||
|
||||
|
||||
## Lobe UI 包含的组件
|
||||
|
||||
- 不知道 @lobehub/ui 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜,大部分组件都是在 antd 的基础上扩展了属性
|
||||
- 具体用法不懂可以联网搜索,例如 ActionIcon 就爬取 https://ui.lobehub.com/components/action-icon
|
||||
|
||||
- General
|
||||
ActionIcon
|
||||
ActionIconGroup
|
||||
Block
|
||||
Button
|
||||
Icon
|
||||
- Data Display
|
||||
Avatar
|
||||
Collapse
|
||||
FileTypeIcon
|
||||
FluentEmoji
|
||||
GuideCard
|
||||
Highlighter
|
||||
Hotkey
|
||||
Image
|
||||
List
|
||||
Markdown
|
||||
MaterialFileTypeIcon
|
||||
Mermaid
|
||||
Segmented
|
||||
Snippet
|
||||
SortableList
|
||||
Tag
|
||||
Tooltip
|
||||
Video
|
||||
- Data Entry
|
||||
AutoComplete
|
||||
CodeEditor
|
||||
ColorSwatches
|
||||
CopyButton
|
||||
DatePicker
|
||||
EditableText
|
||||
EmojiPicker
|
||||
Form
|
||||
FormModal
|
||||
HotkeyInput
|
||||
ImageSelect
|
||||
Input
|
||||
SearchBar
|
||||
Select
|
||||
SliderWithInput
|
||||
ThemeSwitch
|
||||
- Feedback
|
||||
Alert
|
||||
Drawer
|
||||
Modal
|
||||
- Layout
|
||||
DraggablePanel
|
||||
Footer
|
||||
Grid
|
||||
Header
|
||||
Layout
|
||||
MaskShadow
|
||||
ScrollShadow
|
||||
- Navigation
|
||||
Burger
|
||||
Dropdown
|
||||
Menu
|
||||
SideNav
|
||||
Tabs
|
||||
Toc
|
||||
- Theme
|
||||
ConfigProvider
|
||||
FontLoader
|
||||
ThemeProvider
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
description:
|
||||
globs:
|
||||
alwaysApply: true
|
||||
---
|
||||
# LobeChat Cursor Rules System Guide
|
||||
|
||||
This document explains how the LobeChat project's Cursor rules system works and serves as an index for manually accessible rules.
|
||||
|
||||
## 🎯 Core Principle
|
||||
|
||||
**All rules are equal** - there are no priorities or "recommendations" between different rule sources. You should follow all applicable rules simultaneously.
|
||||
|
||||
## 📚 Four Ways to Access Rules
|
||||
|
||||
### 1. **Always Applied Rules** - `always_applied_workspace_rules`
|
||||
- **What**: Core project guidelines that are always active
|
||||
- **Content**: Project tech stack, basic coding standards, output formatting rules
|
||||
- **Access**: No tools needed - automatically provided in every conversation
|
||||
|
||||
### 2. **Dynamic Context Rules** - `cursor_rules_context`
|
||||
- **What**: Rules automatically matched based on files referenced in the conversation
|
||||
- **Trigger**: Only when user **explicitly @ mentions files** or **opens files in Cursor**
|
||||
- **Content**: May include brief descriptions or full rule content, depending on relevance
|
||||
- **Access**: No tools needed - automatically updated when files are referenced
|
||||
|
||||
### 3. **Agent Requestable Rules** - `agent_requestable_workspace_rules`
|
||||
- **What**: Detailed operational guides that can be requested on-demand
|
||||
- **Access**: Use `fetch_rules` tool with rule names
|
||||
- **Examples**: `debug`, `i18n/i18n`, `code-review`
|
||||
|
||||
### 4. **Manual Rules Index** - This file + `read_file`
|
||||
- **What**: Additional rules not covered by the above mechanisms
|
||||
- **Why needed**: Cursor's rule system only supports "agent request" or "auto attach" modes
|
||||
- **Access**: Use `read_file` tool to read specific `.mdc` files
|
||||
|
||||
## 🔧 When to Use `read_file` for Rules
|
||||
|
||||
Use `read_file` to access rules from the index below when:
|
||||
|
||||
1. **Gap identification**: You determine a rule is needed for the current task
|
||||
2. **No auto-trigger**: The rule isn't provided in `cursor_rules_context` (because relevant files weren't @ mentioned)
|
||||
3. **Not agent-requestable**: The rule isn't available via `fetch_rules`
|
||||
|
||||
## 📋 Available Rules Index
|
||||
|
||||
The following rules are available via `read_file` from the `.cursor/rules/` directory:
|
||||
|
||||
- `backend-architecture.mdc` – Backend layer architecture and design guidelines
|
||||
- `zustand-action-patterns.mdc` – Recommended patterns for organizing Zustand actions
|
||||
- `zustand-slice-organization.mdc` – Best practices for structuring Zustand slices
|
||||
- `drizzle-schema-style-guide.mdc` – Style guide for defining Drizzle ORM schemas
|
||||
- `react-component.mdc` – React component style guide and conventions
|
||||
|
||||
## ❌ Common Misunderstandings to Avoid
|
||||
|
||||
1. **"Priority confusion"**: There's no hierarchy between rule sources - they're complementary, not competitive
|
||||
2. **"Dynamic expectations"**: `cursor_rules_context` only updates when you @ files - it won't automatically include rules for tasks you're thinking about
|
||||
3. **"Tool redundancy"**: Each access method serves a different purpose - they're not alternatives to choose from
|
||||
|
||||
## 🛠️ Practical Workflow
|
||||
|
||||
```
|
||||
1. Start with always_applied_workspace_rules (automatic)
|
||||
2. Check cursor_rules_context for auto-matched rules (automatic)
|
||||
3. If you need specific guides: fetch_rules (manual)
|
||||
4. If you identify gaps: consult this index → read_file (manual)
|
||||
```
|
||||
|
||||
## Example Decision Flow
|
||||
|
||||
**Scenario**: Working on a new Zustand store slice
|
||||
1. Follow always_applied_workspace_rules ✅
|
||||
2. If store files were @ mentioned → use cursor_rules_context rules ✅
|
||||
3. Need detailed Zustand guidance → `read_file('.cursor/rules/zustand-slice-organization.mdc')` ✅
|
||||
4. All rules apply simultaneously - no conflicts ✅
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
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, all kinds of network protocols.
|
||||
|
||||
You are an expert in LLM and 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
|
||||
|
||||
- Before formulating any response, you must first gather context by using tools like codebase_search, grep_search, file_search, web_search, fetch_rules, context7, and read_file to avoid making assumptions.
|
||||
- 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
|
||||
- Admit when you don't know something instead of guessing
|
||||
|
||||
## Code Implementation
|
||||
|
||||
- Write correct, up-to-date, bug-free, fully functional, secure, maintainable and efficient code
|
||||
- First, think step-by-step: describe your plan in detailed pseudocode before implementation
|
||||
- Confirm the plan before writing code
|
||||
- Focus on maintainable over being performant
|
||||
- Leave NO TODOs, placeholders, or missing pieces
|
||||
- Be sure to reference file names
|
||||
- When you notice I have manually modified the code, that was definitely on purpose and do not revert them
|
||||
- If documentation links or required files are missing, ask for them before proceeding with the task rather than making assumptions
|
||||
- If you're unable to access or retrieve content from websites, please inform me immediately and request the specific information needed rather than making assumptions
|
||||
- You can use emojis, npm packages like `chalk`/`chalk-animation`/`terminal-link`/`gradient-string`/`log-symbols`/`boxen`/`consola`/`@clack/prompts` to create beautiful terminal output
|
||||
- Don't run `tsc --noEmit` to check ts syntax error, because our project is very large and the validate very slow
|
||||
@@ -0,0 +1,881 @@
|
||||
---
|
||||
description:
|
||||
globs: *.test.ts,*.test.tsx
|
||||
alwaysApply: false
|
||||
---
|
||||
---
|
||||
type: agent-requested
|
||||
title: 测试指南 - LobeChat Testing Guide
|
||||
description: LobeChat 项目的 Vitest 测试环境配置、运行方式、修复原则指南
|
||||
---
|
||||
|
||||
# 测试指南 - LobeChat Testing Guide
|
||||
|
||||
## 🧪 测试环境概览
|
||||
|
||||
LobeChat 项目使用 Vitest 测试库,配置了两种不同的测试环境:
|
||||
|
||||
### 客户端测试环境 (DOM Environment)
|
||||
|
||||
- **配置文件**: [vitest.config.ts](mdc:vitest.config.ts)
|
||||
- **环境**: Happy DOM (浏览器环境模拟)
|
||||
- **数据库**: PGLite (浏览器环境的 PostgreSQL)
|
||||
- **用途**: 测试前端组件、客户端逻辑、React 组件等
|
||||
- **设置文件**: [tests/setup.ts](mdc:tests/setup.ts)
|
||||
|
||||
### 服务端测试环境 (Node Environment)
|
||||
|
||||
- **配置文件**: [vitest.config.server.ts](mdc:vitest.config.server.ts)
|
||||
- **环境**: Node.js
|
||||
- **数据库**: 真实的 PostgreSQL 数据库
|
||||
- **并发限制**: 单线程运行 (`singleFork: true`)
|
||||
- **用途**: 测试数据库模型、服务端逻辑、API 端点等
|
||||
- **设置文件**: [tests/setup-db.ts](mdc:tests/setup-db.ts)
|
||||
|
||||
## 🚀 测试运行命令
|
||||
|
||||
### package.json 脚本说明
|
||||
|
||||
查看 [package.json](mdc:package.json) 中的测试相关脚本:
|
||||
|
||||
```json
|
||||
{
|
||||
"test": "npm run test-app && npm run test-server",
|
||||
"test-app": "vitest run --config vitest.config.ts",
|
||||
"test-app:coverage": "vitest run --config vitest.config.ts --coverage",
|
||||
"test-server": "vitest run --config vitest.config.server.ts",
|
||||
"test-server:coverage": "vitest run --config vitest.config.server.ts --coverage"
|
||||
}
|
||||
```
|
||||
|
||||
### 推荐的测试运行方式
|
||||
|
||||
#### ✅ 正确的命令格式
|
||||
|
||||
```bash
|
||||
# 运行所有客户端测试
|
||||
npx vitest run --config vitest.config.ts
|
||||
|
||||
# 运行所有服务端测试
|
||||
npx vitest run --config vitest.config.server.ts
|
||||
|
||||
# 运行特定测试文件 (支持模糊匹配)
|
||||
npx vitest run --config vitest.config.ts basic
|
||||
npx vitest run --config vitest.config.ts user.test.ts
|
||||
|
||||
# 运行特定文件的特定行号
|
||||
npx vitest run --config vitest.config.ts src/utils/helper.test.ts:25
|
||||
npx vitest run --config vitest.config.ts basic/foo.test.ts:10,basic/foo.test.ts:25
|
||||
|
||||
# 过滤特定测试用例名称
|
||||
npx vitest -t "test case name" --config vitest.config.ts
|
||||
|
||||
# 组合使用文件和测试名称过滤
|
||||
npx vitest run --config vitest.config.ts filename.test.ts -t "specific test"
|
||||
```
|
||||
|
||||
#### ❌ 避免的命令格式
|
||||
|
||||
```bash
|
||||
# ❌ 不要使用 pnpm test xxx (这不是有效的 vitest 命令)
|
||||
pnpm test some-file
|
||||
|
||||
# ❌ 不要使用裸 vitest (会进入 watch 模式)
|
||||
vitest test-file.test.ts
|
||||
|
||||
# ❌ 不要混淆测试环境
|
||||
npx vitest run --config vitest.config.server.ts client-component.test.ts
|
||||
```
|
||||
|
||||
### 关键运行参数说明
|
||||
|
||||
- **`vitest run`**: 运行一次测试然后退出 (避免 watch 模式)
|
||||
- **`vitest`**: 默认进入 watch 模式,持续监听文件变化
|
||||
- **`--config`**: 指定配置文件,选择正确的测试环境
|
||||
- **`-t`**: 过滤测试用例名称,支持正则表达式
|
||||
- **`--coverage`**: 生成测试覆盖率报告
|
||||
|
||||
## 🔧 测试修复原则
|
||||
|
||||
### 核心原则 ⚠️
|
||||
|
||||
1. **充分阅读测试代码**: 在修复测试之前,必须完整理解测试的意图和实现
|
||||
2. **测试优先修复**: 如果是测试本身写错了,修改测试而不是实现代码
|
||||
3. **专注单一问题**: 只修复指定的测试,不要添加额外测试或功能
|
||||
4. **不自作主张**: 不要因为发现其他问题就直接修改,先提出再讨论
|
||||
|
||||
### 测试修复流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph "阶段一:分析与复现"
|
||||
A[开始:收到测试失败报告] --> B[定位并运行失败的测试];
|
||||
B --> C{是否能在本地复现?};
|
||||
C -->|否| D[检查测试环境/配置/依赖];
|
||||
C -->|是| E[分析:阅读测试代码、错误日志、Git 历史];
|
||||
end
|
||||
|
||||
subgraph "阶段二:诊断与调试"
|
||||
E --> F[建立假设:问题出在测试、代码还是环境?];
|
||||
F --> G["调试:使用 console.log 或 debugger 深入检查"];
|
||||
G --> H{假设是否被证实?};
|
||||
H -->|否, 重新假设| F;
|
||||
end
|
||||
|
||||
subgraph "阶段三:修复与验证"
|
||||
H -->|是| I{确定根本原因};
|
||||
I -->|测试逻辑错误| J[修复测试代码];
|
||||
I -->|实现代码 Bug| K[修复实现代码];
|
||||
I -->|环境/配置问题| L[修复配置或依赖];
|
||||
J --> M[验证修复:重新运行失败的测试];
|
||||
K --> M;
|
||||
L --> M;
|
||||
M --> N{测试是否通过?};
|
||||
N -->|否, 修复无效| F;
|
||||
N -->|是| O[扩大验证:运行当前文件内所有测试];
|
||||
O --> P{是否全部通过?};
|
||||
P -->|否, 引入新问题| F;
|
||||
end
|
||||
|
||||
subgraph "阶段四:总结"
|
||||
P -->|是| Q[完成:撰写修复总结];
|
||||
end
|
||||
|
||||
D --> F;
|
||||
```
|
||||
|
||||
### 修复完成后的总结
|
||||
|
||||
测试修复完成后,应该提供简要说明,包括:
|
||||
|
||||
1. **错误原因分析**: 说明测试失败的根本原因
|
||||
- 测试逻辑错误
|
||||
- 实现代码bug
|
||||
- 环境配置问题
|
||||
- 依赖变更导致的问题
|
||||
|
||||
2. **修复方法说明**: 简述采用的修复方式
|
||||
- 修改了哪些文件
|
||||
- 采用了什么解决方案
|
||||
- 为什么选择这种修复方式
|
||||
|
||||
**示例格式**:
|
||||
|
||||
```markdown
|
||||
## 测试修复总结
|
||||
|
||||
**错误原因**: 测试中的 mock 数据格式与实际 API 返回格式不匹配,导致断言失败。
|
||||
|
||||
**修复方法**: 更新了测试文件中的 mock 数据结构,使其与最新的 API 响应格式保持一致。具体修改了 `user.test.ts` 中的 `mockUserData` 对象结构。
|
||||
```
|
||||
|
||||
## 📂 测试文件组织
|
||||
|
||||
### 文件命名约定
|
||||
|
||||
- **客户端测试**: `*.test.ts`, `*.test.tsx` (任意位置)
|
||||
- **服务端测试**: `src/database/models/**/*.test.ts`, `src/database/server/**/*.test.ts` (限定路径)
|
||||
|
||||
### 测试文件组织风格
|
||||
|
||||
项目采用 **测试文件与源文件同目录** 的组织风格:
|
||||
|
||||
- 测试文件放在对应源文件的同一目录下
|
||||
- 命名格式:`原文件名.test.ts` 或 `原文件名.test.tsx`
|
||||
|
||||
例如:
|
||||
|
||||
```
|
||||
src/components/Button/
|
||||
├── index.tsx # 源文件
|
||||
└── index.test.tsx # 测试文件
|
||||
```
|
||||
|
||||
## 🛠️ 测试调试技巧
|
||||
|
||||
### 运行失败测试的步骤
|
||||
|
||||
1. **确定测试类型**: 查看文件路径确定使用哪个配置
|
||||
2. **运行单个测试**: 使用 `-t` 参数隔离问题
|
||||
3. **检查错误日志**: 仔细阅读错误信息和堆栈跟踪
|
||||
4. **查看最近修改记录**: 检查相关文件的最近变更情况
|
||||
5. **添加调试日志**: 在测试中添加 `console.log` 了解执行流程
|
||||
|
||||
### Electron IPC 接口测试策略 🖥️
|
||||
|
||||
对于涉及 Electron IPC 接口的测试,由于提供真实的 Electron 环境比较复杂,采用 **Mock 返回值** 的方式进行测试。
|
||||
|
||||
#### 基本 Mock 设置
|
||||
|
||||
```typescript
|
||||
import { vi } from "vitest";
|
||||
import { electronIpcClient } from "@/server/modules/ElectronIPCClient";
|
||||
|
||||
// Mock Electron IPC 客户端
|
||||
vi.mock("@/server/modules/ElectronIPCClient", () => ({
|
||||
electronIpcClient: {
|
||||
getFilePathById: vi.fn(),
|
||||
deleteFiles: vi.fn(),
|
||||
// 根据需要添加其他 IPC 方法
|
||||
},
|
||||
}));
|
||||
```
|
||||
|
||||
#### 在测试中设置 Mock 行为
|
||||
|
||||
```typescript
|
||||
beforeEach(() => {
|
||||
// 重置所有 Mock
|
||||
vi.resetAllMocks();
|
||||
|
||||
// 设置默认的 Mock 返回值
|
||||
vi.mocked(electronIpcClient.getFilePathById).mockResolvedValue(
|
||||
"/path/to/file.txt"
|
||||
);
|
||||
vi.mocked(electronIpcClient.deleteFiles).mockResolvedValue({
|
||||
success: true,
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
#### 测试不同场景的示例
|
||||
|
||||
```typescript
|
||||
it("应该处理文件删除成功的情况", async () => {
|
||||
// 设置成功场景的 Mock
|
||||
vi.mocked(electronIpcClient.deleteFiles).mockResolvedValue({
|
||||
success: true,
|
||||
});
|
||||
|
||||
const result = await service.deleteFiles(["desktop://file1.txt"]);
|
||||
|
||||
expect(electronIpcClient.deleteFiles).toHaveBeenCalledWith([
|
||||
"desktop://file1.txt",
|
||||
]);
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("应该处理文件删除失败的情况", async () => {
|
||||
// 设置失败场景的 Mock
|
||||
vi.mocked(electronIpcClient.deleteFiles).mockRejectedValue(
|
||||
new Error("删除失败")
|
||||
);
|
||||
|
||||
const result = await service.deleteFiles(["desktop://file1.txt"]);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.errors).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
#### Mock 策略的优势
|
||||
|
||||
1. **环境简化**: 避免了复杂的 Electron 环境搭建
|
||||
2. **测试可控**: 可以精确控制 IPC 调用的返回值和行为
|
||||
3. **场景覆盖**: 容易测试各种成功/失败场景
|
||||
4. **执行速度**: Mock 调用比真实 IPC 调用更快
|
||||
|
||||
#### 注意事项
|
||||
|
||||
- **Mock 准确性**: 确保 Mock 的行为与真实 IPC 接口行为一致
|
||||
- **类型安全**: 使用 `vi.mocked()` 确保类型安全
|
||||
- **Mock 重置**: 在 `beforeEach` 中重置 Mock 状态,避免测试间干扰
|
||||
- **调用验证**: 不仅要验证返回值,还要验证 IPC 方法是否被正确调用
|
||||
|
||||
### 检查最近修改记录 🔍
|
||||
|
||||
为了更好地判断测试失败的根本原因,需要**系统性地检查相关文件的修改历史**。这是问题定位的关键步骤。
|
||||
|
||||
#### 第一步:确定需要检查的文件范围
|
||||
|
||||
1. **测试文件本身**: `path/to/component.test.ts`
|
||||
2. **对应的实现文件**: `path/to/component.ts` 或 `path/to/component/index.ts`
|
||||
3. **相关依赖文件**: 测试或实现中导入的其他模块
|
||||
|
||||
#### 第二步:检查当前工作目录状态
|
||||
|
||||
```bash
|
||||
# 查看所有未提交的修改状态
|
||||
git status
|
||||
|
||||
# 重点关注测试文件和实现文件是否有未提交的修改
|
||||
git status | grep -E "(test|spec)"
|
||||
```
|
||||
|
||||
#### 第三步:检查未提交的修改内容
|
||||
|
||||
```bash
|
||||
# 查看测试文件的未提交修改 (工作区 vs 暂存区)
|
||||
git diff path/to/component.test.ts | cat
|
||||
|
||||
# 查看对应实现文件的未提交修改
|
||||
git diff path/to/component.ts | cat
|
||||
|
||||
# 查看已暂存但未提交的修改
|
||||
git diff --cached path/to/component.test.ts | cat
|
||||
git diff --cached path/to/component.ts | cat
|
||||
```
|
||||
|
||||
#### 第四步:检查提交历史和时间相关性
|
||||
|
||||
**首先查看提交时间,判断修改的时效性**:
|
||||
|
||||
```bash
|
||||
# 查看测试文件的最近提交历史,包含提交时间
|
||||
git log --pretty=format:"%h %ad %s" --date=relative -5 path/to/component.test.ts | cat
|
||||
|
||||
# 查看实现文件的最近提交历史,包含提交时间
|
||||
git log --pretty=format:"%h %ad %s" --date=relative -5 path/to/component.ts | cat
|
||||
|
||||
# 查看详细的提交时间(ISO格式,便于精确判断)
|
||||
git log --pretty=format:"%h %ad %an %s" --date=iso -3 path/to/component.ts | cat
|
||||
git log --pretty=format:"%h %ad %an %s" --date=iso -3 path/to/component.test.ts | cat
|
||||
```
|
||||
|
||||
**判断提交的参考价值**:
|
||||
|
||||
1. **最近提交(24小时内)**: 🔴 **高度相关** - 很可能是导致测试失败的直接原因
|
||||
2. **近期提交(1-7天内)**: 🟡 **中等相关** - 可能相关,需要仔细分析修改内容
|
||||
3. **较早提交(超过1周)**: ⚪ **低相关性** - 除非是重大重构,否则不太可能是直接原因
|
||||
|
||||
#### 第五步:基于时间相关性查看具体修改内容
|
||||
|
||||
**根据提交时间的远近,优先查看最近的修改**:
|
||||
|
||||
```bash
|
||||
# 如果有24小时内的提交,重点查看这些修改
|
||||
git show HEAD -- path/to/component.test.ts | cat
|
||||
git show HEAD -- path/to/component.ts | cat
|
||||
|
||||
# 查看次新的提交(如果最新提交时间较远)
|
||||
git show HEAD~1 -- path/to/component.ts | cat
|
||||
git show <recent-commit-hash> -- path/to/component.ts | cat
|
||||
|
||||
# 对比最近两次提交的差异
|
||||
git diff HEAD~1 HEAD -- path/to/component.ts | cat
|
||||
```
|
||||
|
||||
#### 第六步:分析修改与测试失败的关系
|
||||
|
||||
基于修改记录和时间相关性判断:
|
||||
|
||||
1. **最近修改了实现代码**:
|
||||
|
||||
```bash
|
||||
# 重点检查实现逻辑的变化
|
||||
git diff HEAD~1 path/to/component.ts | cat
|
||||
```
|
||||
|
||||
- 很可能是实现代码的变更导致测试失败
|
||||
- 检查实现逻辑是否正确
|
||||
- 确认测试是否需要相应更新
|
||||
|
||||
2. **最近修改了测试代码**:
|
||||
|
||||
```bash
|
||||
# 重点检查测试逻辑的变化
|
||||
git diff HEAD~1 path/to/component.test.ts | cat
|
||||
```
|
||||
|
||||
- 可能是测试本身写错了
|
||||
- 检查测试逻辑和断言是否正确
|
||||
- 确认测试是否符合实现的预期行为
|
||||
|
||||
3. **两者都有最近修改**:
|
||||
|
||||
```bash
|
||||
# 对比两个文件的修改时间
|
||||
git log --pretty=format:"%ad %f" --date=iso -1 path/to/component.ts | cat
|
||||
git log --pretty=format:"%ad %f" --date=iso -1 path/to/component.test.ts | cat
|
||||
```
|
||||
|
||||
- 需要综合分析两者的修改
|
||||
- 确定哪个修改更可能导致问题
|
||||
- 优先检查时间更近的修改
|
||||
|
||||
4. **都没有最近修改**:
|
||||
- 可能是依赖变更或环境问题
|
||||
- 检查 `package.json`、配置文件等的修改
|
||||
- 查看是否有全局性的代码重构
|
||||
|
||||
#### 修改记录检查示例
|
||||
|
||||
```bash
|
||||
# 完整的检查流程示例
|
||||
echo "=== 检查文件修改状态 ==="
|
||||
git status | grep component
|
||||
|
||||
echo "=== 检查未提交修改 ==="
|
||||
git diff src/components/Button/index.test.tsx | cat
|
||||
git diff src/components/Button/index.tsx | cat
|
||||
|
||||
echo "=== 检查提交历史和时间 ==="
|
||||
git log --pretty=format:"%h %ad %s" --date=relative -3 src/components/Button/index.test.tsx | cat
|
||||
git log --pretty=format:"%h %ad %s" --date=relative -3 src/components/Button/index.tsx | cat
|
||||
|
||||
echo "=== 根据时间优先级查看修改内容 ==="
|
||||
# 如果有24小时内的提交,重点查看
|
||||
git show HEAD -- src/components/Button/index.tsx | cat
|
||||
```
|
||||
|
||||
## 🗃️ 数据库 Model 测试指南
|
||||
|
||||
### 测试环境选择 💡
|
||||
|
||||
数据库 Model 层通过环境变量控制数据库类型,在两种测试环境下有不同的数据库后端:客户端环境 (PGLite) 和 服务端环境 (PostgreSQL)
|
||||
|
||||
### ⚠️ 双环境验证要求
|
||||
|
||||
**对于所有 Model 测试,必须在两个环境下都验证通过**:
|
||||
|
||||
#### 完整验证流程
|
||||
|
||||
```bash
|
||||
# 1. 先在客户端环境测试(快速验证)
|
||||
npx vitest run --config vitest.config.ts src/database/models/__tests__/myModel.test.ts
|
||||
|
||||
# 2. 再在服务端环境测试(兼容性验证)
|
||||
npx vitest run --config vitest.config.server.ts src/database/models/__tests__/myModel.test.ts
|
||||
```
|
||||
|
||||
### 创建新 Model 测试的最佳实践 📋
|
||||
|
||||
#### 1. 参考现有实现和测试模板
|
||||
|
||||
创建新 Model 测试前,**必须先参考现有的实现模式**:
|
||||
|
||||
- **Model 实现参考**:
|
||||
- **测试模板参考**:
|
||||
- **复杂示例参考**:
|
||||
|
||||
#### 2. 用户权限检查 - 安全第一 🔒
|
||||
|
||||
这是**最关键的安全要求**。所有涉及用户数据的操作都必须包含用户权限检查:
|
||||
|
||||
**❌ 错误示例 - 存在安全漏洞**:
|
||||
|
||||
```typescript
|
||||
// 危险:缺少用户权限检查,任何用户都能操作任何数据
|
||||
update = async (id: string, data: Partial<MyModel>) => {
|
||||
return this.db
|
||||
.update(myTable)
|
||||
.set(data)
|
||||
.where(eq(myTable.id, id)) // ❌ 只检查 ID,没有检查 userId
|
||||
.returning();
|
||||
};
|
||||
```
|
||||
|
||||
**✅ 正确示例 - 安全的实现**:
|
||||
|
||||
```typescript
|
||||
// 安全:必须同时匹配 ID 和 userId
|
||||
update = async (id: string, data: Partial<MyModel>) => {
|
||||
return this.db
|
||||
.update(myTable)
|
||||
.set(data)
|
||||
.where(
|
||||
and(
|
||||
eq(myTable.id, id),
|
||||
eq(myTable.userId, this.userId) // ✅ 用户权限检查
|
||||
)
|
||||
)
|
||||
.returning();
|
||||
};
|
||||
```
|
||||
|
||||
**必须进行用户权限检查的方法**:
|
||||
|
||||
- `update()` - 更新操作
|
||||
- `delete()` - 删除操作
|
||||
- `findById()` - 查找特定记录
|
||||
- 任何涉及特定记录的查询或修改操作
|
||||
|
||||
#### 3. 测试文件结构和必测场景
|
||||
|
||||
**基本测试结构**:
|
||||
|
||||
```typescript
|
||||
// @vitest-environment node
|
||||
describe("MyModel", () => {
|
||||
describe("create", () => {
|
||||
it("should create a new record");
|
||||
it("should handle edge cases");
|
||||
});
|
||||
|
||||
describe("queryAll", () => {
|
||||
it("should return records for current user only");
|
||||
it("should handle empty results");
|
||||
});
|
||||
|
||||
describe("update", () => {
|
||||
it("should update own records");
|
||||
it("should NOT update other users records"); // 🔒 安全测试
|
||||
});
|
||||
|
||||
describe("delete", () => {
|
||||
it("should delete own records");
|
||||
it("should NOT delete other users records"); // 🔒 安全测试
|
||||
});
|
||||
|
||||
describe("user isolation", () => {
|
||||
it("should enforce user data isolation"); // 🔒 核心安全测试
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**必须测试的安全场景** 🔒:
|
||||
|
||||
```typescript
|
||||
it("should not update records of other users", async () => {
|
||||
// 创建其他用户的记录
|
||||
const [otherUserRecord] = await serverDB
|
||||
.insert(myTable)
|
||||
.values({ userId: "other-user", data: "original" })
|
||||
.returning();
|
||||
|
||||
// 尝试更新其他用户的记录
|
||||
const result = await myModel.update(otherUserRecord.id, { data: "hacked" });
|
||||
|
||||
// 应该返回 undefined 或空数组(因为权限检查失败)
|
||||
expect(result).toBeUndefined();
|
||||
|
||||
// 验证原始数据未被修改
|
||||
const unchanged = await serverDB.query.myTable.findFirst({
|
||||
where: eq(myTable.id, otherUserRecord.id),
|
||||
});
|
||||
expect(unchanged?.data).toBe("original"); // 数据应该保持不变
|
||||
});
|
||||
```
|
||||
|
||||
#### 4. Mock 外部依赖服务
|
||||
|
||||
如果 Model 依赖外部服务(如 FileService),需要正确 Mock:
|
||||
|
||||
**设置 Mock**:
|
||||
|
||||
```typescript
|
||||
// 在文件顶部设置 Mock
|
||||
const mockGetFullFileUrl = vi.fn();
|
||||
vi.mock("@/server/services/file", () => ({
|
||||
FileService: vi.fn().mockImplementation(() => ({
|
||||
getFullFileUrl: mockGetFullFileUrl,
|
||||
})),
|
||||
}));
|
||||
|
||||
// 在 beforeEach 中重置和配置 Mock
|
||||
beforeEach(async () => {
|
||||
vi.clearAllMocks();
|
||||
mockGetFullFileUrl.mockImplementation(
|
||||
(url: string) => `https://example.com/${url}`
|
||||
);
|
||||
});
|
||||
```
|
||||
|
||||
**验证 Mock 调用**:
|
||||
|
||||
```typescript
|
||||
it("should process URLs through FileService", async () => {
|
||||
// ... 测试逻辑
|
||||
|
||||
// 验证 Mock 被正确调用
|
||||
expect(mockGetFullFileUrl).toHaveBeenCalledWith("expected-url");
|
||||
expect(mockGetFullFileUrl).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
```
|
||||
|
||||
#### 5. 数据库状态管理
|
||||
|
||||
**正确的数据清理模式**:
|
||||
|
||||
```typescript
|
||||
const userId = "test-user";
|
||||
const otherUserId = "other-user";
|
||||
|
||||
beforeEach(async () => {
|
||||
// 清理用户表(级联删除相关数据)
|
||||
await serverDB.delete(users);
|
||||
|
||||
// 创建测试用户
|
||||
await serverDB.insert(users).values([{ id: userId }, { id: otherUserId }]);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// 清理测试数据
|
||||
await serverDB.delete(users);
|
||||
});
|
||||
```
|
||||
|
||||
#### 6. 测试数据类型和外键约束处理 ⚠️
|
||||
|
||||
**必须使用 Schema 导出的类型**:
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用 schema 导出的类型
|
||||
import { NewGenerationBatch, NewGeneration } from '../../schemas';
|
||||
|
||||
const testBatch: NewGenerationBatch = {
|
||||
userId,
|
||||
generationTopicId: 'test-topic-id',
|
||||
provider: 'test-provider',
|
||||
model: 'test-model',
|
||||
prompt: 'Test prompt for image generation',
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
config: { /* ... */ },
|
||||
};
|
||||
|
||||
const testGeneration: NewGeneration = {
|
||||
id: 'test-gen-id',
|
||||
generationBatchId: 'test-batch-id',
|
||||
asyncTaskId: null, // 处理外键约束
|
||||
fileId: null, // 处理外键约束
|
||||
seed: 12345,
|
||||
userId,
|
||||
};
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:没有类型声明或使用错误类型
|
||||
const testBatch = { // 缺少类型声明
|
||||
generationTopicId: 'test-topic-id',
|
||||
// ...
|
||||
};
|
||||
|
||||
const testGeneration = { // 缺少类型声明
|
||||
asyncTaskId: 'invalid-uuid', // 外键约束错误
|
||||
fileId: 'non-existent-file', // 外键约束错误
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
**外键约束处理策略**:
|
||||
|
||||
1. **使用 null 值**: 对于可选的外键字段,使用 null 避免约束错误
|
||||
2. **创建关联记录**: 如果需要测试关联关系,先创建被引用的记录
|
||||
3. **理解约束关系**: 了解哪些字段有外键约束,避免引用不存在的记录
|
||||
|
||||
```typescript
|
||||
// 外键约束处理示例
|
||||
beforeEach(async () => {
|
||||
// 清理数据库
|
||||
await serverDB.delete(users);
|
||||
|
||||
// 创建测试用户
|
||||
await serverDB.insert(users).values([{ id: userId }]);
|
||||
|
||||
// 如果需要测试文件关联,创建文件记录
|
||||
if (needsFileAssociation) {
|
||||
await serverDB.insert(files).values({
|
||||
id: 'test-file-id',
|
||||
userId,
|
||||
name: 'test.jpg',
|
||||
url: 'test-url',
|
||||
size: 1024,
|
||||
fileType: 'image/jpeg',
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**排序测试的可预测性**:
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用明确的时间戳确保排序结果可预测
|
||||
it('should find batches by topic id in correct order', async () => {
|
||||
const oldDate = new Date('2024-01-01T10:00:00Z');
|
||||
const newDate = new Date('2024-01-02T10:00:00Z');
|
||||
|
||||
const batch1 = { ...testBatch, prompt: 'First batch', userId, createdAt: oldDate };
|
||||
const batch2 = { ...testBatch, prompt: 'Second batch', userId, createdAt: newDate };
|
||||
|
||||
await serverDB.insert(generationBatches).values([batch1, batch2]);
|
||||
|
||||
const results = await generationBatchModel.findByTopicId(testTopic.id);
|
||||
|
||||
expect(results[0].prompt).toBe('Second batch'); // 最新优先 (desc order)
|
||||
expect(results[1].prompt).toBe('First batch');
|
||||
});
|
||||
```
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:依赖数据库的默认时间戳,结果不可预测
|
||||
it('should find batches by topic id', async () => {
|
||||
const batch1 = { ...testBatch, prompt: 'First batch', userId };
|
||||
const batch2 = { ...testBatch, prompt: 'Second batch', userId };
|
||||
|
||||
await serverDB.insert(generationBatches).values([batch1, batch2]);
|
||||
|
||||
// 插入顺序和数据库时间戳可能不一致,导致测试不稳定
|
||||
const results = await generationBatchModel.findByTopicId(testTopic.id);
|
||||
expect(results[0].prompt).toBe('Second batch'); // 可能失败
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
### 常见问题和解决方案 💡
|
||||
|
||||
#### 问题 1:权限检查缺失导致安全漏洞
|
||||
|
||||
**现象**: 测试失败,用户能修改其他用户的数据
|
||||
**解决**: 在 Model 的 `update` 和 `delete` 方法中添加 `and(eq(table.id, id), eq(table.userId, this.userId))`
|
||||
|
||||
#### 问题 2:Mock 未生效或验证失败
|
||||
|
||||
**现象**: `undefined is not a spy` 错误
|
||||
**解决**: 检查 Mock 设置位置和方式,确保在测试文件顶部设置,在 `beforeEach` 中重置
|
||||
|
||||
#### 问题 3:测试数据污染
|
||||
|
||||
**现象**: 测试间相互影响,结果不稳定
|
||||
**解决**: 在 `beforeEach` 和 `afterEach` 中正确清理数据库状态
|
||||
|
||||
#### 问题 4:外部依赖导致测试失败
|
||||
|
||||
**现象**: 因为真实的外部服务调用导致测试不稳定
|
||||
**解决**: Mock 所有外部依赖,使测试更可控和快速
|
||||
|
||||
#### 问题 5:外键约束违反导致测试失败
|
||||
|
||||
**现象**: `insert or update on table "xxx" violates foreign key constraint`
|
||||
**解决**:
|
||||
- 将可选外键字段设为 `null` 而不是无效的字符串值
|
||||
- 或者先创建被引用的记录,再创建当前记录
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:无效的外键值
|
||||
const testData = {
|
||||
asyncTaskId: 'invalid-uuid', // 表中不存在此记录
|
||||
fileId: 'non-existent-file', // 表中不存在此记录
|
||||
};
|
||||
|
||||
// ✅ 正确:使用 null 值
|
||||
const testData = {
|
||||
asyncTaskId: null, // 避免外键约束
|
||||
fileId: null, // 避免外键约束
|
||||
};
|
||||
|
||||
// ✅ 或者:先创建被引用的记录
|
||||
beforeEach(async () => {
|
||||
const [asyncTask] = await serverDB.insert(asyncTasks).values({
|
||||
id: 'valid-task-id',
|
||||
status: 'pending',
|
||||
type: 'generation',
|
||||
}).returning();
|
||||
|
||||
const testData = {
|
||||
asyncTaskId: asyncTask.id, // 使用有效的外键值
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
#### 问题 6:排序测试结果不一致
|
||||
|
||||
**现象**: 相同的测试有时通过,有时失败,特别是涉及排序的测试
|
||||
**解决**: 使用明确的时间戳,不要依赖数据库的默认时间戳
|
||||
|
||||
```typescript
|
||||
// ❌ 错误:依赖插入顺序和默认时间戳
|
||||
await serverDB.insert(table).values([data1, data2]); // 时间戳不可预测
|
||||
|
||||
// ✅ 正确:明确指定时间戳
|
||||
const oldDate = new Date('2024-01-01T10:00:00Z');
|
||||
const newDate = new Date('2024-01-02T10:00:00Z');
|
||||
await serverDB.insert(table).values([
|
||||
{ ...data1, createdAt: oldDate },
|
||||
{ ...data2, createdAt: newDate },
|
||||
]);
|
||||
```
|
||||
|
||||
#### 问题 7:Mock 验证失败或调用次数不匹配
|
||||
|
||||
**现象**: `expect(mockFunction).toHaveBeenCalledWith(...)` 失败
|
||||
**解决**:
|
||||
- 检查 Mock 函数的实际调用参数和期望参数是否完全匹配
|
||||
- 确认 Mock 在正确的时机被重置和配置
|
||||
- 使用 `toHaveBeenCalledTimes()` 验证调用次数
|
||||
|
||||
```typescript
|
||||
// 在 beforeEach 中正确配置 Mock
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks(); // 重置所有 Mock
|
||||
|
||||
mockGetFullFileUrl.mockImplementation((url: string) => `https://example.com/${url}`);
|
||||
mockTransformGeneration.mockResolvedValue({
|
||||
id: 'test-id',
|
||||
// ... 其他字段
|
||||
});
|
||||
});
|
||||
|
||||
// 测试中验证 Mock 调用
|
||||
it('should call FileService with correct parameters', async () => {
|
||||
await model.someMethod();
|
||||
|
||||
// 验证调用参数
|
||||
expect(mockGetFullFileUrl).toHaveBeenCalledWith('expected-url');
|
||||
// 验证调用次数
|
||||
expect(mockGetFullFileUrl).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
```
|
||||
|
||||
### Model 测试检查清单 ✅
|
||||
|
||||
创建 Model 测试时,请确保以下各项都已完成:
|
||||
|
||||
#### 🔧 基础配置
|
||||
- [ ] **双环境验证** - 在客户端环境 (vitest.config.ts) 和服务端环境 (vitest.config.server.ts) 下都测试通过
|
||||
- [ ] 参考了 `_template.ts` 和现有 Model 的实现模式
|
||||
- [ ] **使用正确的 Schema 类型** - 测试数据使用 `NewXxx` 类型声明,如 `NewGenerationBatch`、`NewGeneration`
|
||||
|
||||
#### 🔒 安全测试
|
||||
- [ ] **所有涉及用户数据的操作都包含用户权限检查**
|
||||
- [ ] 包含了用户权限隔离的安全测试
|
||||
- [ ] 测试了用户无法访问其他用户数据的场景
|
||||
|
||||
#### 🗃️ 数据处理
|
||||
- [ ] **正确处理外键约束** - 使用 `null` 值或先创建被引用记录
|
||||
- [ ] **排序测试使用明确时间戳** - 不依赖数据库默认时间,确保结果可预测
|
||||
- [ ] 在 `beforeEach` 和 `afterEach` 中正确管理数据库状态
|
||||
- [ ] 所有测试都能独立运行且互不干扰
|
||||
|
||||
#### 🎭 Mock 和外部依赖
|
||||
- [ ] 正确 Mock 了外部依赖服务 (如 FileService、GenerationModel)
|
||||
- [ ] 在 `beforeEach` 中重置和配置 Mock
|
||||
- [ ] 验证了 Mock 服务的调用参数和次数
|
||||
- [ ] 测试了外部服务错误场景的处理
|
||||
|
||||
#### 📋 测试覆盖
|
||||
- [ ] 测试覆盖了所有主要方法 (create, query, update, delete)
|
||||
- [ ] 测试了边界条件和错误场景
|
||||
- [ ] 包含了空结果处理的测试
|
||||
- [ ] **确认两个环境下的测试结果一致**
|
||||
|
||||
#### 🚨 常见问题检查
|
||||
- [ ] 没有外键约束违反错误
|
||||
- [ ] 排序测试结果稳定可预测
|
||||
- [ ] Mock 验证无失败
|
||||
- [ ] 无测试数据污染问题
|
||||
|
||||
### 安全警告 ⚠️
|
||||
|
||||
**数据库 Model 层是安全的第一道防线**。如果 Model 层缺少用户权限检查:
|
||||
|
||||
1. **任何用户都能访问和修改其他用户的数据**
|
||||
2. **即使上层有权限检查,也可能被绕过**
|
||||
3. **可能导致严重的数据泄露和安全事故**
|
||||
|
||||
因此,**每个涉及用户数据的 Model 方法都必须包含用户权限检查,且必须有对应的安全测试来验证这些检查的有效性**。
|
||||
|
||||
## 🎯 总结
|
||||
|
||||
修复测试时,记住以下关键点:
|
||||
|
||||
- **使用正确的命令**: `npx vitest run --config [config-file]`
|
||||
- **理解测试意图**: 先读懂测试再修复
|
||||
- **查看最近修改**: 检查相关文件的 git 修改记录,判断问题根源
|
||||
- **选择正确环境**: 客户端测试用 `vitest.config.ts`,服务端用 `vitest.config.server.ts`
|
||||
- **专注单一问题**: 只修复当前的测试失败
|
||||
- **验证修复结果**: 确保修复后测试通过且无副作用
|
||||
- **提供修复总结**: 说明错误原因和修复方法
|
||||
- **Model 测试安全第一**: 必须包含用户权限检查和对应的安全测试
|
||||
- **Model 双环境验证**: 必须在 PGLite 和 PostgreSQL 两个环境下都验证通过
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
description:
|
||||
globs: *.ts,*.tsx,*.mts
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
TypeScript Code Style Guide:
|
||||
|
||||
- Avoid explicit type annotations when TypeScript can infer types.
|
||||
- Avoid defining `any` type variables (e.g., `let a: number;` instead of `let a;`).
|
||||
- Use the most accurate type possible (e.g., use `Record<PropertyKey, unknown>` instead of `object`).
|
||||
- Prefer `interface` over `type` (e.g., define react component props).
|
||||
- Use `as const satisfies XyzInterface` instead of `as const` when suitable
|
||||
- import index.ts module(directory module) like `@/db/index` instead of `@/db`
|
||||
- Instead of calling Date.now() multiple times, assign it to a constant once and reuse it. This ensures consistency and improves readability
|
||||
- Always refactor repeated logic into a reusable function
|
||||
- Don't remove meaningful code comments, be sure to keep original comments when providing applied code
|
||||
- Update the code comments when needed after you modify the related code
|
||||
- Please respect my prettier preferences when you provide code
|
||||
@@ -0,0 +1,360 @@
|
||||
---
|
||||
description:
|
||||
globs: src/store/**
|
||||
alwaysApply: false
|
||||
---
|
||||
# LobeChat Zustand Action 组织模式
|
||||
|
||||
本文档详细说明了 LobeChat 项目中 Zustand Action 的组织方式、命名规范和实现模式,特别关注乐观更新与后端服务的集成。
|
||||
|
||||
## Action 类型分层
|
||||
|
||||
LobeChat 的 Action 采用分层架构,明确区分不同职责:
|
||||
|
||||
### 1. Public Actions
|
||||
对外暴露的主要接口,供 UI 组件调用:
|
||||
- 命名:动词形式(`createTopic`, `sendMessage`, `updateTopicTitle`)
|
||||
- 职责:参数验证、流程编排、调用 internal actions
|
||||
- 示例:[src/store/chat/slices/topic/action.ts](mdc:src/store/chat/slices/topic/action.ts)
|
||||
|
||||
```typescript
|
||||
// Public Action 示例
|
||||
createTopic: async () => {
|
||||
const { activeId, internal_createTopic } = get();
|
||||
const messages = chatSelectors.activeBaseChats(get());
|
||||
|
||||
if (messages.length === 0) return;
|
||||
|
||||
const topicId = await internal_createTopic({
|
||||
sessionId: activeId,
|
||||
title: t('defaultTitle', { ns: 'topic' }),
|
||||
messages: messages.map((m) => m.id),
|
||||
});
|
||||
|
||||
return topicId;
|
||||
},
|
||||
```
|
||||
|
||||
### 2. Internal Actions (`internal_*`)
|
||||
内部实现细节,处理核心业务逻辑:
|
||||
- 命名:`internal_` 前缀 + 动词(`internal_createTopic`, `internal_updateMessageContent`)
|
||||
- 职责:乐观更新、服务调用、错误处理、状态同步
|
||||
- 不应该被 UI 组件直接调用
|
||||
|
||||
```typescript
|
||||
// Internal Action 示例 - 乐观更新模式
|
||||
internal_createTopic: async (params) => {
|
||||
const tmpId = Date.now().toString();
|
||||
|
||||
// 1. 立即更新前端状态(乐观更新)
|
||||
get().internal_dispatchTopic(
|
||||
{ type: 'addTopic', value: { ...params, id: tmpId } },
|
||||
'internal_createTopic',
|
||||
);
|
||||
get().internal_updateTopicLoading(tmpId, true);
|
||||
|
||||
// 2. 调用后端服务
|
||||
const topicId = await topicService.createTopic(params);
|
||||
get().internal_updateTopicLoading(tmpId, false);
|
||||
|
||||
// 3. 刷新数据确保一致性
|
||||
get().internal_updateTopicLoading(topicId, true);
|
||||
await get().refreshTopic();
|
||||
get().internal_updateTopicLoading(topicId, false);
|
||||
|
||||
return topicId;
|
||||
},
|
||||
```
|
||||
|
||||
### 3. Dispatch Methods (`internal_dispatch*`)
|
||||
专门处理状态更新的方法:
|
||||
- 命名:`internal_dispatch` + 实体名(`internal_dispatchTopic`, `internal_dispatchMessage`)
|
||||
- 职责:调用 reducer、更新 Zustand store、处理状态对比
|
||||
|
||||
```typescript
|
||||
// Dispatch Method 示例
|
||||
internal_dispatchTopic: (payload, action) => {
|
||||
const nextTopics = topicReducer(topicSelectors.currentTopics(get()), payload);
|
||||
const nextMap = { ...get().topicMaps, [get().activeId]: nextTopics };
|
||||
|
||||
if (isEqual(nextMap, get().topicMaps)) return;
|
||||
|
||||
set({ topicMaps: nextMap }, false, action ?? n(`dispatchTopic/${payload.type}`));
|
||||
},
|
||||
```
|
||||
|
||||
## 何时使用 Reducer 模式 vs. 简单 `set`
|
||||
|
||||
### 使用 Reducer 模式的场景
|
||||
|
||||
适用于复杂的数据结构管理,特别是:
|
||||
- 管理对象列表或映射(如 `messagesMap`, `topicMaps`)
|
||||
- 需要乐观更新的场景
|
||||
- 状态转换逻辑复杂
|
||||
- 需要类型安全的 action payload
|
||||
|
||||
```typescript
|
||||
// Reducer 模式示例 - 复杂消息状态管理
|
||||
export const messagesReducer = (state: ChatMessage[], payload: MessageDispatch): ChatMessage[] => {
|
||||
switch (payload.type) {
|
||||
case 'updateMessage': {
|
||||
return produce(state, (draftState) => {
|
||||
const index = draftState.findIndex((i) => i.id === payload.id);
|
||||
if (index < 0) return;
|
||||
draftState[index] = merge(draftState[index], {
|
||||
...payload.value,
|
||||
updatedAt: Date.now()
|
||||
});
|
||||
});
|
||||
}
|
||||
case 'createMessage': {
|
||||
return produce(state, (draftState) => {
|
||||
draftState.push({
|
||||
...payload.value,
|
||||
id: payload.id,
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
meta: {}
|
||||
});
|
||||
});
|
||||
}
|
||||
// ...其他复杂状态转换
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 使用简单 `set` 的场景
|
||||
|
||||
适用于简单状态更新:
|
||||
- 切换布尔值
|
||||
- 更新简单字符串/数字
|
||||
- 设置单一状态字段
|
||||
|
||||
```typescript
|
||||
// 简单 set 示例
|
||||
updateInputMessage: (message) => {
|
||||
if (isEqual(message, get().inputMessage)) return;
|
||||
set({ inputMessage: message }, false, n('updateInputMessage'));
|
||||
},
|
||||
|
||||
togglePortal: (open?: boolean) => {
|
||||
set({ showPortal: open ?? !get().showPortal }, false, 'togglePortal');
|
||||
},
|
||||
```
|
||||
|
||||
## 乐观更新实现模式
|
||||
|
||||
乐观更新是 LobeChat 中的核心模式,用于提供流畅的用户体验:
|
||||
|
||||
### 标准乐观更新流程
|
||||
|
||||
```typescript
|
||||
// 完整的乐观更新示例
|
||||
internal_updateMessageContent: async (id, content, extra) => {
|
||||
const { internal_dispatchMessage, refreshMessages } = get();
|
||||
|
||||
// 1. 立即更新前端状态(乐观更新)
|
||||
internal_dispatchMessage({
|
||||
id,
|
||||
type: 'updateMessage',
|
||||
value: { content },
|
||||
});
|
||||
|
||||
// 2. 调用后端服务
|
||||
await messageService.updateMessage(id, {
|
||||
content,
|
||||
tools: extra?.toolCalls ? internal_transformToolCalls(extra.toolCalls) : undefined,
|
||||
// ...其他字段
|
||||
});
|
||||
|
||||
// 3. 刷新确保数据一致性
|
||||
await refreshMessages();
|
||||
},
|
||||
```
|
||||
|
||||
### 创建操作的乐观更新
|
||||
|
||||
```typescript
|
||||
internal_createMessage: async (message, context) => {
|
||||
const { internal_createTmpMessage, refreshMessages, internal_toggleMessageLoading } = get();
|
||||
|
||||
let tempId = context?.tempMessageId;
|
||||
if (!tempId) {
|
||||
// 创建临时消息用于乐观更新
|
||||
tempId = internal_createTmpMessage(message);
|
||||
internal_toggleMessageLoading(true, tempId);
|
||||
}
|
||||
|
||||
try {
|
||||
const id = await messageService.createMessage(message);
|
||||
if (!context?.skipRefresh) {
|
||||
await refreshMessages();
|
||||
}
|
||||
internal_toggleMessageLoading(false, tempId);
|
||||
return id;
|
||||
} catch (e) {
|
||||
internal_toggleMessageLoading(false, tempId);
|
||||
// 错误处理:更新消息错误状态
|
||||
internal_dispatchMessage({
|
||||
id: tempId,
|
||||
type: 'updateMessage',
|
||||
value: { error: { type: ChatErrorType.CreateMessageError, message: e.message } },
|
||||
});
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
### 删除操作模式(不使用乐观更新)
|
||||
|
||||
删除操作通常不适合乐观更新,因为:
|
||||
- 删除是破坏性操作,错误恢复复杂
|
||||
- 用户对删除操作的即时反馈期望较低
|
||||
- 删除失败时恢复原状态会造成困惑
|
||||
|
||||
```typescript
|
||||
// 删除操作的标准模式 - 无乐观更新
|
||||
removeGenerationTopic: async (id: string) => {
|
||||
const { internal_removeGenerationTopic } = get();
|
||||
await internal_removeGenerationTopic(id);
|
||||
},
|
||||
|
||||
internal_removeGenerationTopic: async (id: string) => {
|
||||
// 1. 显示加载状态
|
||||
get().internal_updateGenerationTopicLoading(id, true);
|
||||
|
||||
try {
|
||||
// 2. 直接调用后端服务
|
||||
await generationTopicService.deleteTopic(id);
|
||||
|
||||
// 3. 刷新数据获取最新状态
|
||||
await get().refreshGenerationTopics();
|
||||
} finally {
|
||||
// 4. 确保清除加载状态(无论成功或失败)
|
||||
get().internal_updateGenerationTopicLoading(id, false);
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
删除操作的特点:
|
||||
- 直接调用服务,不预先更新状态
|
||||
- 依赖 loading 状态提供用户反馈
|
||||
- 操作完成后刷新整个列表确保一致性
|
||||
- 使用 `try/finally` 确保 loading 状态总是被清理
|
||||
|
||||
## 加载状态管理模式
|
||||
|
||||
LobeChat 使用统一的加载状态管理模式:
|
||||
|
||||
### 数组式加载状态
|
||||
|
||||
```typescript
|
||||
// 在 initialState.ts 中定义
|
||||
export interface ChatMessageState {
|
||||
messageLoadingIds: string[]; // 消息加载状态
|
||||
messageEditingIds: string[]; // 消息编辑状态
|
||||
chatLoadingIds: string[]; // 对话生成状态
|
||||
}
|
||||
|
||||
// 在 action 中管理
|
||||
internal_toggleMessageLoading: (loading, id) => {
|
||||
set({
|
||||
messageLoadingIds: toggleBooleanList(get().messageLoadingIds, id, loading),
|
||||
}, false, `internal_toggleMessageLoading/${loading ? 'start' : 'end'}`);
|
||||
},
|
||||
```
|
||||
|
||||
### 统一的加载状态工具
|
||||
|
||||
```typescript
|
||||
// 通用的加载状态切换工具
|
||||
internal_toggleLoadingArrays: (key, loading, id, action) => {
|
||||
const abortControllerKey = `${key}AbortController`;
|
||||
|
||||
if (loading) {
|
||||
const abortController = new AbortController();
|
||||
set({
|
||||
[abortControllerKey]: abortController,
|
||||
[key]: toggleBooleanList(get()[key] as string[], id!, loading),
|
||||
}, false, action);
|
||||
return abortController;
|
||||
} else {
|
||||
set({
|
||||
[abortControllerKey]: undefined,
|
||||
[key]: id ? toggleBooleanList(get()[key] as string[], id, loading) : [],
|
||||
}, false, action);
|
||||
}
|
||||
},
|
||||
```
|
||||
|
||||
## SWR 集成模式
|
||||
|
||||
LobeChat 使用 SWR 进行数据获取和缓存管理:
|
||||
|
||||
### Hook 式数据获取
|
||||
|
||||
```typescript
|
||||
// 在 action.ts 中定义 SWR hook
|
||||
useFetchMessages: (enable, sessionId, activeTopicId) =>
|
||||
useClientDataSWR<ChatMessage[]>(
|
||||
enable ? [SWR_USE_FETCH_MESSAGES, sessionId, activeTopicId] : null,
|
||||
async ([, sessionId, topicId]) => messageService.getMessages(sessionId, topicId),
|
||||
{
|
||||
onSuccess: (messages, key) => {
|
||||
const nextMap = {
|
||||
...get().messagesMap,
|
||||
[messageMapKey(sessionId, activeTopicId)]: messages,
|
||||
};
|
||||
|
||||
if (get().messagesInit && isEqual(nextMap, get().messagesMap)) return;
|
||||
|
||||
set({ messagesInit: true, messagesMap: nextMap }, false, n('useFetchMessages'));
|
||||
},
|
||||
},
|
||||
),
|
||||
```
|
||||
|
||||
### 缓存失效和刷新
|
||||
|
||||
```typescript
|
||||
// 刷新数据的标准模式
|
||||
refreshMessages: async () => {
|
||||
await mutate([SWR_USE_FETCH_MESSAGES, get().activeId, get().activeTopicId]);
|
||||
},
|
||||
|
||||
refreshTopic: async () => {
|
||||
return mutate([SWR_USE_FETCH_TOPIC, get().activeId]);
|
||||
},
|
||||
```
|
||||
|
||||
## 命名规范总结
|
||||
|
||||
### Action 命名模式
|
||||
- Public Actions: 动词形式,描述用户意图
|
||||
- `createTopic`, `sendMessage`, `regenerateMessage`
|
||||
- Internal Actions: `internal_` + 动词,描述内部操作
|
||||
- `internal_createTopic`, `internal_updateMessageContent`
|
||||
- Dispatch Methods: `internal_dispatch` + 实体名
|
||||
- `internal_dispatchTopic`, `internal_dispatchMessage`
|
||||
- Toggle Methods: `internal_toggle` + 状态名
|
||||
- `internal_toggleMessageLoading`, `internal_toggleChatLoading`
|
||||
|
||||
### 状态命名模式
|
||||
- ID 数组: `[entity]LoadingIds`, `[entity]EditingIds`
|
||||
- 映射结构: `[entity]Maps`, `[entity]Map`
|
||||
- 当前激活: `active[Entity]Id`
|
||||
- 初始化标记: `[entity]sInit`
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. 合理使用乐观更新:
|
||||
- ✅ 适用:创建、更新操作(用户交互频繁)
|
||||
- ❌ 避免:删除操作(破坏性操作,错误恢复复杂)
|
||||
2. 加载状态管理:使用统一的加载状态数组管理并发操作
|
||||
3. 类型安全:为所有 action payload 定义 TypeScript 接口
|
||||
4. SWR 集成:使用 SWR 管理数据获取和缓存失效
|
||||
5. AbortController:为长时间运行的操作提供取消能力
|
||||
6. 操作模式选择:
|
||||
- 创建/更新:乐观更新 + 最终一致性
|
||||
- 删除:加载状态 + 服务调用 + 数据刷新
|
||||
|
||||
这套 Action 组织模式确保了代码的一致性、可维护性,并提供了优秀的用户体验。
|
||||
@@ -0,0 +1,300 @@
|
||||
---
|
||||
description:
|
||||
globs: src/store/**
|
||||
alwaysApply: false
|
||||
---
|
||||
# LobeChat Zustand Store Slice 组织架构
|
||||
|
||||
本文档描述了 LobeChat 项目中 Zustand Store 的模块化 Slice 组织方式,展示如何通过分片架构管理复杂的应用状态。
|
||||
|
||||
## 顶层 Store 结构
|
||||
|
||||
LobeChat 的 `chat` store (`src/store/chat/`) 采用模块化的 slice 结构来组织状态和逻辑。
|
||||
|
||||
### 关键聚合文件
|
||||
|
||||
- `src/store/chat/initialState.ts`: 聚合所有 slice 的初始状态
|
||||
- `src/store/chat/store.ts`: 定义顶层的 `ChatStore`,组合所有 slice 的 actions
|
||||
- `src/store/chat/selectors.ts`: 统一导出所有 slice 的 selectors
|
||||
- `src/store/chat/helpers.ts`: 提供聊天相关的辅助函数
|
||||
|
||||
### Store 聚合模式
|
||||
|
||||
```typescript
|
||||
// src/store/chat/initialState.ts
|
||||
import { ChatTopicState, initialTopicState } from './slices/topic/initialState';
|
||||
import { ChatMessageState, initialMessageState } from './slices/message/initialState';
|
||||
import { ChatAIChatState, initialAiChatState } from './slices/aiChat/initialState';
|
||||
|
||||
export type ChatStoreState = ChatTopicState &
|
||||
ChatMessageState &
|
||||
ChatAIChatState &
|
||||
// ...其他 slice states
|
||||
|
||||
export const initialState: ChatStoreState = {
|
||||
...initialMessageState,
|
||||
...initialTopicState,
|
||||
...initialAiChatState,
|
||||
// ...其他 initial slice states
|
||||
};
|
||||
```
|
||||
|
||||
```typescript
|
||||
// src/store/chat/store.ts
|
||||
import { ChatMessageAction, chatMessage } from './slices/message/action';
|
||||
import { ChatTopicAction, chatTopic } from './slices/topic/action';
|
||||
import { ChatAIChatAction, chatAiChat } from './slices/aiChat/actions';
|
||||
|
||||
export interface ChatStoreAction
|
||||
extends ChatMessageAction,
|
||||
ChatTopicAction,
|
||||
ChatAIChatAction,
|
||||
// ...其他 slice actions
|
||||
|
||||
const createStore: StateCreator<ChatStore, [['zustand/devtools', never]]> = (...params) => ({
|
||||
...initialState,
|
||||
...chatMessage(...params),
|
||||
...chatTopic(...params),
|
||||
...chatAiChat(...params),
|
||||
// ...其他 slice action creators
|
||||
});
|
||||
|
||||
export const useChatStore = createWithEqualityFn<ChatStore>()(
|
||||
subscribeWithSelector(devtools(createStore)),
|
||||
shallow,
|
||||
);
|
||||
```
|
||||
|
||||
## 单个 Slice 的标准结构
|
||||
|
||||
每个 slice 位于 `src/store/chat/slices/[sliceName]/` 目录下:
|
||||
|
||||
```
|
||||
src/store/chat/slices/
|
||||
└── [sliceName]/ # 例如 message, topic, aiChat, builtinTool
|
||||
├── action.ts # 定义 actions (或者是一个 actions/ 目录)
|
||||
├── initialState.ts # 定义 state 结构和初始值
|
||||
├── reducer.ts # (可选) 如果使用 reducer 模式
|
||||
├── selectors.ts # 定义 selectors
|
||||
└── index.ts # (可选) 重新导出模块内容
|
||||
```
|
||||
|
||||
### 文件职责说明
|
||||
|
||||
1. `initialState.ts`:
|
||||
- 定义 slice 的 TypeScript 状态接口
|
||||
- 提供初始状态默认值
|
||||
|
||||
```typescript
|
||||
// 典型的 initialState.ts 结构
|
||||
export interface ChatTopicState {
|
||||
activeTopicId?: string;
|
||||
topicMaps: Record<string, ChatTopic[]>; // 核心数据结构
|
||||
topicsInit: boolean;
|
||||
topicLoadingIds: string[];
|
||||
// ...其他状态字段
|
||||
}
|
||||
|
||||
export const initialTopicState: ChatTopicState = {
|
||||
activeTopicId: undefined,
|
||||
topicMaps: {},
|
||||
topicsInit: false,
|
||||
topicLoadingIds: [],
|
||||
// ...其他初始值
|
||||
};
|
||||
```
|
||||
|
||||
2. `reducer.ts` (复杂状态使用):
|
||||
- 定义纯函数 reducer,处理同步状态转换
|
||||
- 使用 `immer` 确保不可变更新
|
||||
|
||||
```typescript
|
||||
// 典型的 reducer.ts 结构
|
||||
import { produce } from 'immer';
|
||||
|
||||
interface AddChatTopicAction {
|
||||
type: 'addTopic';
|
||||
value: CreateTopicParams & { id?: string };
|
||||
}
|
||||
|
||||
interface UpdateChatTopicAction {
|
||||
id: string;
|
||||
type: 'updateTopic';
|
||||
value: Partial<ChatTopic>;
|
||||
}
|
||||
|
||||
export type ChatTopicDispatch = AddChatTopicAction | UpdateChatTopicAction;
|
||||
|
||||
export const topicReducer = (state: ChatTopic[] = [], payload: ChatTopicDispatch): ChatTopic[] => {
|
||||
switch (payload.type) {
|
||||
case 'addTopic': {
|
||||
return produce(state, (draftState) => {
|
||||
draftState.unshift({
|
||||
...payload.value,
|
||||
id: payload.value.id ?? Date.now().toString(),
|
||||
createdAt: Date.now(),
|
||||
});
|
||||
});
|
||||
}
|
||||
case 'updateTopic': {
|
||||
return produce(state, (draftState) => {
|
||||
const index = draftState.findIndex((topic) => topic.id === payload.id);
|
||||
if (index !== -1) {
|
||||
draftState[index] = { ...draftState[index], ...payload.value };
|
||||
}
|
||||
});
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
3. `selectors.ts`:
|
||||
- 提供状态查询和计算函数
|
||||
- 供 UI 组件使用的状态订阅接口
|
||||
- 重要: 使用 `export const xxxSelectors` 模式聚合所有 selectors
|
||||
|
||||
```typescript
|
||||
// 典型的 selectors.ts 结构
|
||||
import { ChatStoreState } from '../../initialState';
|
||||
|
||||
const currentTopics = (s: ChatStoreState): ChatTopic[] | undefined =>
|
||||
s.topicMaps[s.activeId];
|
||||
|
||||
const currentActiveTopic = (s: ChatStoreState): ChatTopic | undefined => {
|
||||
return currentTopics(s)?.find((topic) => topic.id === s.activeTopicId);
|
||||
};
|
||||
|
||||
const getTopicById = (id: string) => (s: ChatStoreState): ChatTopic | undefined =>
|
||||
currentTopics(s)?.find((topic) => topic.id === id);
|
||||
|
||||
// 核心模式:使用 xxxSelectors 聚合导出
|
||||
export const topicSelectors = {
|
||||
currentActiveTopic,
|
||||
currentTopics,
|
||||
getTopicById,
|
||||
// ...其他 selectors
|
||||
};
|
||||
```
|
||||
|
||||
## 特殊 Slice 组织模式
|
||||
|
||||
### 复杂 Actions 的子目录结构 (aiChat Slice)
|
||||
|
||||
当 slice 的 actions 过于复杂时,可以拆分到子目录:
|
||||
|
||||
```
|
||||
src/store/chat/slices/aiChat/
|
||||
├── actions/
|
||||
│ ├── generateAIChat.ts # AI 对话生成
|
||||
│ ├── rag.ts # RAG 检索增强生成
|
||||
│ ├── memory.ts # 对话记忆管理
|
||||
│ └── index.ts # 聚合所有 actions
|
||||
├── initialState.ts
|
||||
├── selectors.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
参考:`src/store/chat/slices/aiChat/actions/`
|
||||
|
||||
### 工具类 Slice (builtinTool)
|
||||
|
||||
管理多种内置工具的状态:
|
||||
|
||||
```
|
||||
src/store/chat/slices/builtinTool/
|
||||
├── actions/
|
||||
│ ├── dalle.ts # DALL-E 图像生成
|
||||
│ ├── search.ts # 搜索功能
|
||||
│ ├── localFile.ts # 本地文件操作
|
||||
│ └── index.ts
|
||||
├── initialState.ts
|
||||
├── selectors.ts
|
||||
└── index.ts
|
||||
```
|
||||
|
||||
参考:`src/store/chat/slices/builtinTool/`
|
||||
|
||||
## 状态设计模式
|
||||
|
||||
### 1. Map 结构用于关联数据
|
||||
```typescript
|
||||
// 以 sessionId 为 key,管理多个会话的数据
|
||||
topicMaps: Record<string, ChatTopic[]>
|
||||
messagesMap: Record<string, ChatMessage[]>
|
||||
```
|
||||
|
||||
### 2. 数组用于加载状态管理
|
||||
```typescript
|
||||
// 管理多个并发操作的加载状态
|
||||
messageLoadingIds: string[]
|
||||
topicLoadingIds: string[]
|
||||
chatLoadingIds: string[]
|
||||
```
|
||||
|
||||
### 3. 可选字段用于当前活动项
|
||||
```typescript
|
||||
// 当前激活的实体 ID
|
||||
activeId: string
|
||||
activeTopicId?: string
|
||||
activeThreadId?: string
|
||||
```
|
||||
|
||||
## Slice 集成到顶层 Store
|
||||
|
||||
### 1. 状态聚合
|
||||
```typescript
|
||||
// 在 initialState.ts 中
|
||||
export type ChatStoreState = ChatTopicState &
|
||||
ChatMessageState &
|
||||
ChatAIChatState &
|
||||
// ...其他 slice states
|
||||
```
|
||||
|
||||
### 2. Action 接口聚合
|
||||
```typescript
|
||||
// 在 store.ts 中
|
||||
export interface ChatStoreAction
|
||||
extends ChatMessageAction,
|
||||
ChatTopicAction,
|
||||
ChatAIChatAction,
|
||||
// ...其他 slice actions
|
||||
```
|
||||
|
||||
### 3. Selector 统一导出
|
||||
```typescript
|
||||
// 在 selectors.ts 中 - 统一聚合 selectors
|
||||
export { chatSelectors } from './slices/message/selectors';
|
||||
export { topicSelectors } from './slices/topic/selectors';
|
||||
export { aiChatSelectors } from './slices/aiChat/selectors';
|
||||
|
||||
// 每个 slice 的 selectors.ts 都使用 xxxSelectors 模式:
|
||||
// export const chatSelectors = { ... }
|
||||
// export const topicSelectors = { ... }
|
||||
// export const aiChatSelectors = { ... }
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. Slice 划分原则:
|
||||
- 按功能领域划分(message, topic, aiChat 等)
|
||||
- 每个 slice 管理相关的状态和操作
|
||||
- 避免 slice 之间的强耦合
|
||||
|
||||
2. 文件命名规范:
|
||||
- 使用小驼峰命名 slice 目录
|
||||
- 文件名使用一致的模式(action.ts, selectors.ts 等)
|
||||
- 复杂 actions 时使用 actions/ 子目录
|
||||
|
||||
3. 状态结构设计:
|
||||
- 扁平化的状态结构,避免深层嵌套
|
||||
- 使用 Map 结构管理列表数据
|
||||
- 分离加载状态和业务数据
|
||||
|
||||
4. 类型安全:
|
||||
- 为每个 slice 定义清晰的 TypeScript 接口
|
||||
- 使用 Zustand 的 StateCreator 确保类型一致性
|
||||
- 在顶层聚合时保持类型安全
|
||||
|
||||
这种模块化的 slice 组织方式使得大型应用的状态管理变得清晰、可维护,并且易于扩展。
|
||||
@@ -0,0 +1,6 @@
|
||||
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
||||
locales/
|
||||
apps/desktop/resources/locales/
|
||||
**/__snapshots__/
|
||||
**/fixtures/
|
||||
src/database/migrations/
|
||||
@@ -232,3 +232,10 @@ OPENAI_API_KEY=sk-xxxxxxxxx
|
||||
|
||||
# Specify the Embedding model and Reranker model(unImplemented)
|
||||
# DEFAULT_FILES_CONFIG="embedding_model=openai/embedding-text-3-small,reranker_model=cohere/rerank-english-v3.0,query_mode=full_text"
|
||||
|
||||
########################################
|
||||
########## MCP Service Config ##########
|
||||
########################################
|
||||
|
||||
# MCP tool call timeout (milliseconds)
|
||||
# MCP_TOOL_TIMEOUT=60000
|
||||
|
||||
@@ -19,6 +19,8 @@ config.rules['unicorn/no-array-for-each'] = 0;
|
||||
config.rules['unicorn/prefer-number-properties'] = 0;
|
||||
config.rules['unicorn/prefer-query-selector'] = 0;
|
||||
config.rules['unicorn/no-array-callback-reference'] = 0;
|
||||
// FIXME: Linting error in src/app/[variants]/(main)/chat/features/Migration/DBReader.ts, the fundamental solution should be upgrading typescript-eslint
|
||||
config.rules['@typescript-eslint/no-useless-constructor'] = 0;
|
||||
|
||||
config.overrides = [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/**
|
||||
* Create or update GitHub issue when i18n workflow fails
|
||||
* Usage: node create-failure-issue.js
|
||||
*/
|
||||
|
||||
module.exports = async ({ github, context, core }) => {
|
||||
const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
|
||||
const timestamp = new Date().toISOString();
|
||||
const date = timestamp.split('T')[0];
|
||||
|
||||
// Get error details from environment variables
|
||||
const errorDetails = {
|
||||
validateEnv: process.env.ERROR_VALIDATE_ENV || '',
|
||||
rebaseAttempt: process.env.ERROR_REBASE_ATTEMPT || '',
|
||||
createBranch: process.env.ERROR_CREATE_BRANCH || '',
|
||||
installDeps: process.env.ERROR_INSTALL_DEPS || '',
|
||||
runI18n: process.env.ERROR_RUN_I18N || '',
|
||||
commitPush: process.env.ERROR_COMMIT_PUSH || '',
|
||||
};
|
||||
|
||||
// Get step conclusions from environment variables
|
||||
const stepStatus = {
|
||||
validateEnv: process.env.STEP_VALIDATE_ENV || 'Not run',
|
||||
checkBranch: process.env.STEP_CHECK_BRANCH || 'Not run',
|
||||
rebaseAttempt: process.env.STEP_REBASE_ATTEMPT || 'Not run',
|
||||
createBranch: process.env.STEP_CREATE_BRANCH || 'Not run',
|
||||
installDeps: process.env.STEP_INSTALL_DEPS || 'Not run',
|
||||
runI18n: process.env.STEP_RUN_I18N || 'Not run',
|
||||
commitPush: process.env.STEP_COMMIT_PUSH || 'Not run',
|
||||
createPr: process.env.STEP_CREATE_PR || 'Not run',
|
||||
};
|
||||
|
||||
// Find the first non-empty error
|
||||
const mainError =
|
||||
Object.values(errorDetails).find((error) => error && error.trim()) || 'Unknown error occurred';
|
||||
|
||||
// Determine error category for better troubleshooting
|
||||
const getErrorCategory = (error) => {
|
||||
if (error.includes('API') || error.includes('authentication')) return 'API/Authentication';
|
||||
if (error.includes('network') || error.includes('timeout')) return 'Network/Connectivity';
|
||||
if (error.includes('dependencies') || error.includes('bun')) return 'Dependencies';
|
||||
if (error.includes('git') || error.includes('branch') || error.includes('rebase'))
|
||||
return 'Git Operations';
|
||||
if (error.includes('permission') || error.includes('token')) return 'Permissions';
|
||||
return 'General';
|
||||
};
|
||||
|
||||
const errorCategory = getErrorCategory(mainError);
|
||||
|
||||
const issueTitle = `🚨 Daily i18n Update Failed - ${date}`;
|
||||
|
||||
const issueBody = `## 🚨 Automated i18n Update Failure
|
||||
|
||||
**Timestamp:** ${timestamp}
|
||||
**Workflow Run:** [#${context.runNumber}](${runUrl})
|
||||
**Repository:** ${context.repo.owner}/${context.repo.repo}
|
||||
**Branch:** ${context.ref}
|
||||
**Commit:** ${context.sha}
|
||||
|
||||
## ❌ Error Details
|
||||
**Primary Error:** ${mainError}
|
||||
**Category:** ${errorCategory}
|
||||
|
||||
## 🔍 Step Status
|
||||
| Step | Status |
|
||||
|------|--------|
|
||||
| Environment Validation | ${stepStatus.validateEnv} |
|
||||
| Branch Check | ${stepStatus.checkBranch} |
|
||||
| Rebase Attempt | ${stepStatus.rebaseAttempt} |
|
||||
| Branch Creation | ${stepStatus.createBranch} |
|
||||
| Dependencies | ${stepStatus.installDeps} |
|
||||
| i18n Update | ${stepStatus.runI18n} |
|
||||
| Git Operations | ${stepStatus.commitPush} |
|
||||
| PR Creation | ${stepStatus.createPr} |
|
||||
|
||||
## 🔧 Environment Info
|
||||
- **Runner OS:** ${process.env.RUNNER_OS || 'Unknown'}
|
||||
- **Bun Version:** ${process.env.BUN_VERSION || 'Default'}
|
||||
- **Workflow:** \`${context.workflow}\`
|
||||
|
||||
## 📋 Debug Information
|
||||
Debug logs have been uploaded as artifacts and will be available for 7 days.
|
||||
|
||||
${getErrorCategoryHelp(errorCategory)}
|
||||
|
||||
## 🛠️ General Troubleshooting Steps
|
||||
1. Check if all required secrets are properly configured
|
||||
2. Verify OpenAI API quota and billing status
|
||||
3. Review the workflow run logs for detailed error messages
|
||||
4. Check if there are any ongoing GitHub API issues
|
||||
5. Manually trigger the workflow to retry
|
||||
|
||||
## 📊 Workflow Statistics
|
||||
- **Run Number:** ${context.runNumber}
|
||||
- **Run ID:** ${context.runId}
|
||||
- **Event:** ${context.eventName}
|
||||
|
||||
---
|
||||
**Auto-generated by:** [\`${context.workflow}\`](${runUrl})
|
||||
**Labels:** automated, bug, i18n, workflow-failure, ${errorCategory.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
||||
|
||||
try {
|
||||
// Search for existing open issues with similar title
|
||||
const existingIssues = await github.rest.issues.listForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: 'automated,workflow-failure',
|
||||
state: 'open',
|
||||
per_page: 50,
|
||||
});
|
||||
|
||||
const todayPrefix = `🚨 Daily i18n Update Failed - ${date}`;
|
||||
const existingIssue = existingIssues.data.find((issue) => issue.title.startsWith(todayPrefix));
|
||||
|
||||
if (existingIssue) {
|
||||
// Update existing issue with comment
|
||||
const commentBody = `## 🔄 Additional Failure
|
||||
|
||||
**Timestamp:** ${timestamp}
|
||||
**Workflow Run:** [#${context.runNumber}](${runUrl})
|
||||
**Error Category:** ${errorCategory}
|
||||
**Error:** ${mainError}
|
||||
|
||||
Same issue occurred again. Please investigate the recurring problem.
|
||||
|
||||
### Quick Actions
|
||||
- [ ] Check API quotas and billing
|
||||
- [ ] Verify network connectivity
|
||||
- [ ] Review recent changes that might cause conflicts
|
||||
- [ ] Consider manual intervention
|
||||
|
||||
---
|
||||
*This is failure #${(await getFailureCount(github, context, existingIssue.number)) + 1} for today.*`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: existingIssue.number,
|
||||
body: commentBody,
|
||||
});
|
||||
|
||||
// Add priority label if this is a recurring issue
|
||||
const failureCount = await getFailureCount(github, context, existingIssue.number);
|
||||
if (failureCount >= 2) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: existingIssue.number,
|
||||
labels: ['priority-high', 'recurring'],
|
||||
});
|
||||
}
|
||||
|
||||
core.info(`✅ Updated existing issue #${existingIssue.number}`);
|
||||
core.setOutput('issue-number', existingIssue.number);
|
||||
core.setOutput('issue-url', existingIssue.html_url);
|
||||
core.setOutput('action', 'updated');
|
||||
} else {
|
||||
// Create new issue
|
||||
const labels = [
|
||||
'automated',
|
||||
'bug',
|
||||
'i18n',
|
||||
'workflow-failure',
|
||||
errorCategory.toLowerCase().replace(/[^a-z0-9]/g, '-'),
|
||||
];
|
||||
|
||||
const issue = await github.rest.issues.create({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
title: issueTitle,
|
||||
body: issueBody,
|
||||
labels: labels,
|
||||
});
|
||||
|
||||
core.info(`✅ Created new issue #${issue.data.number}`);
|
||||
core.setOutput('issue-number', issue.data.number);
|
||||
core.setOutput('issue-url', issue.data.html_url);
|
||||
core.setOutput('action', 'created');
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(`Failed to create or update issue: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get category-specific help text
|
||||
*/
|
||||
function getErrorCategoryHelp(category) {
|
||||
const helpTexts = {
|
||||
'API/Authentication': `
|
||||
### 🔑 API/Authentication Issues
|
||||
- Verify \`OPENAI_API_KEY\` is correctly set and valid
|
||||
- Check if API key has sufficient quota/credits
|
||||
- Ensure \`GH_TOKEN\` has necessary permissions
|
||||
- Test API connectivity manually`,
|
||||
|
||||
'Network/Connectivity': `
|
||||
### 🌐 Network/Connectivity Issues
|
||||
- Check if OpenAI API is experiencing outages
|
||||
- Verify proxy settings if using \`OPENAI_PROXY_URL\`
|
||||
- Retry the workflow as this might be temporary
|
||||
- Check GitHub Actions service status`,
|
||||
|
||||
'Dependencies': `
|
||||
### 📦 Dependencies Issues
|
||||
- Verify \`bun\` version compatibility
|
||||
- Check for package.json changes that might affect dependencies
|
||||
- Clear cache and retry installation
|
||||
- Review recent dependency updates`,
|
||||
|
||||
'Git Operations': `
|
||||
### 🔧 Git Operations Issues
|
||||
- Check for conflicting changes in target branch
|
||||
- Verify repository permissions
|
||||
- Review recent commits that might cause conflicts
|
||||
- Manual branch cleanup might be required`,
|
||||
|
||||
'Permissions': `
|
||||
### 🔐 Permissions Issues
|
||||
- Verify \`GH_TOKEN\` has \`repo\` and \`issues\` permissions
|
||||
- Check if token can create/update PRs and branches
|
||||
- Ensure token hasn't expired
|
||||
- Review repository settings and branch protection rules`,
|
||||
|
||||
'General': `
|
||||
### 🔍 General Issues
|
||||
- Review detailed error logs in workflow run
|
||||
- Check for recent changes in codebase
|
||||
- Verify all environment variables are set
|
||||
- Consider running workflow manually with debug enabled`,
|
||||
};
|
||||
|
||||
return helpTexts[category] || helpTexts['General'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Count how many times this issue has failed today
|
||||
*/
|
||||
async function getFailureCount(github, context, issueNumber) {
|
||||
try {
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
return comments.data.filter(
|
||||
(comment) =>
|
||||
comment.body.includes('Additional Failure') && comment.created_at.startsWith(today),
|
||||
).length;
|
||||
} catch (error) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
name: Daily i18n Update
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
# Add permissions configuration
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
update-i18n:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
|
||||
steps:
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config --global user.name "lobehubbot"
|
||||
git config --global user.email "i@lobehub.com"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: ${{ secrets.BUN_VERSION }}
|
||||
|
||||
- name: Install deps
|
||||
run: bun i
|
||||
|
||||
- name: Update i18n
|
||||
run: bun run i18n
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_PROXY_URL: ${{ secrets.OPENAI_PROXY_URL }}
|
||||
|
||||
- name: create pull request
|
||||
id: cpr
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
add-paths: |
|
||||
locales/**/*.json
|
||||
labels: |
|
||||
i18n
|
||||
automated
|
||||
style
|
||||
branch: style/auto-i18n
|
||||
delete-branch: true
|
||||
title: '🤖 style: update i18n'
|
||||
commit-message: '💄 style: update i18n'
|
||||
body: |
|
||||
This PR was automatically generated by the i18n update workflow.
|
||||
Please review the changes and merge if everything looks good.
|
||||
|
||||
## 🤖 Automation Info
|
||||
- Workflow: `${{ github.workflow }}`
|
||||
- Run ID: `${{ github.run_id }}`
|
||||
- Commit: `${{ github.sha }}`
|
||||
|
||||
<details>
|
||||
<summary>i18n Update Log</summary>
|
||||
```bash
|
||||
$(cat i18n_update.log)
|
||||
```
|
||||
</details>
|
||||
+2
-1
@@ -24,7 +24,8 @@ module.exports = defineConfig({
|
||||
'fa-IR',
|
||||
],
|
||||
temperature: 0,
|
||||
modelName: 'gpt-4o-mini',
|
||||
saveImmediately: true,
|
||||
modelName: 'gpt-4.1-mini',
|
||||
experimental: {
|
||||
jsonMode: true,
|
||||
},
|
||||
|
||||
+2553
File diff suppressed because it is too large
Load Diff
@@ -186,6 +186,8 @@ ENV \
|
||||
MINIMAX_API_KEY="" MINIMAX_MODEL_LIST="" \
|
||||
# Mistral
|
||||
MISTRAL_API_KEY="" MISTRAL_MODEL_LIST="" \
|
||||
# ModelScope
|
||||
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
|
||||
# Moonshot
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Novita
|
||||
@@ -224,6 +226,8 @@ ENV \
|
||||
TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
|
||||
# Upstage
|
||||
UPSTAGE_API_KEY="" UPSTAGE_MODEL_LIST="" \
|
||||
# v0 (Vercel)
|
||||
V0_API_KEY="" V0_MODEL_LIST="" \
|
||||
# vLLM
|
||||
VLLM_API_KEY="" VLLM_MODEL_LIST="" VLLM_PROXY_URL="" \
|
||||
# Wenxin
|
||||
|
||||
@@ -230,6 +230,8 @@ ENV \
|
||||
MINIMAX_API_KEY="" MINIMAX_MODEL_LIST="" \
|
||||
# Mistral
|
||||
MISTRAL_API_KEY="" MISTRAL_MODEL_LIST="" \
|
||||
# ModelScope
|
||||
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
|
||||
# Moonshot
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Novita
|
||||
@@ -268,6 +270,8 @@ ENV \
|
||||
TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
|
||||
# Upstage
|
||||
UPSTAGE_API_KEY="" UPSTAGE_MODEL_LIST="" \
|
||||
# v0 (Vercel)
|
||||
V0_API_KEY="" V0_MODEL_LIST="" \
|
||||
# vLLM
|
||||
VLLM_API_KEY="" VLLM_MODEL_LIST="" VLLM_PROXY_URL="" \
|
||||
# Wenxin
|
||||
|
||||
@@ -188,6 +188,8 @@ ENV \
|
||||
MINIMAX_API_KEY="" MINIMAX_MODEL_LIST="" \
|
||||
# Mistral
|
||||
MISTRAL_API_KEY="" MISTRAL_MODEL_LIST="" \
|
||||
# ModelScope
|
||||
MODELSCOPE_API_KEY="" MODELSCOPE_MODEL_LIST="" MODELSCOPE_PROXY_URL="" \
|
||||
# Moonshot
|
||||
MOONSHOT_API_KEY="" MOONSHOT_MODEL_LIST="" MOONSHOT_PROXY_URL="" \
|
||||
# Novita
|
||||
@@ -222,6 +224,8 @@ ENV \
|
||||
TOGETHERAI_API_KEY="" TOGETHERAI_MODEL_LIST="" \
|
||||
# Upstage
|
||||
UPSTAGE_API_KEY="" UPSTAGE_MODEL_LIST="" \
|
||||
# v0 (Vercel)
|
||||
V0_API_KEY="" V0_MODEL_LIST="" \
|
||||
# vLLM
|
||||
VLLM_API_KEY="" VLLM_MODEL_LIST="" VLLM_PROXY_URL="" \
|
||||
# Wenxin
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
# Lobe Chat
|
||||
|
||||
An open-source, modern-design ChatGPT/LLMs UI/Framework.<br/>
|
||||
Supports speech-synthesis, multi-modal, and extensible ([function call][docs-functionc-call]) plugin system.<br/>
|
||||
An open-source, modern design ChatGPT/LLMs UI/framework.<br/>
|
||||
Supports speech synthesis, multi-modal, and extensible ([function call][docs-function-call]) plugin system.<br/>
|
||||
One-click **FREE** deployment of your private OpenAI ChatGPT/Claude/Gemini/Groq/Ollama chat application.
|
||||
|
||||
**English** · [简体中文](./README.zh-CN.md) · [Official Site][official-site] · [Changelog][changelog] · [Documents][docs] · [Blog][blog] · [Feedback][github-issues-link]
|
||||
@@ -39,7 +39,7 @@ One-click **FREE** deployment of your private OpenAI ChatGPT/Claude/Gemini/Groq/
|
||||
|
||||
<sup>Pioneering the new age of thinking and creating. Built for you, the Super Individual.</sup>
|
||||
|
||||
[![][github-trending-shield]][github-trending-url]
|
||||
[![][github-trending-shield]][github-trending-url] <br /> <br /> <a href="https://vercel.com/oss"> <img alt="Vercel OSS Program" src="https://vercel.com/oss/program-badge.svg" /> </a>
|
||||
|
||||
![][image-overview]
|
||||
|
||||
@@ -52,22 +52,26 @@ One-click **FREE** deployment of your private OpenAI ChatGPT/Claude/Gemini/Groq/
|
||||
|
||||
- [👋🏻 Getting Started & Join Our Community](#-getting-started--join-our-community)
|
||||
- [✨ Features](#-features)
|
||||
- [`1` Chain of Thought](#1-chain-of-thought)
|
||||
- [`2` Branching Conversations](#2-branching-conversations)
|
||||
- [`3` Artifacts Support](#3-artifacts-support)
|
||||
- [`4` File Upload /Knowledge Base](#4-file-upload-knowledge-base)
|
||||
- [`5` Multi-Model Service Provider Support](#5-multi-model-service-provider-support)
|
||||
- [`6` Local Large Language Model (LLM) Support](#6-local-large-language-model-llm-support)
|
||||
- [`7` Model Visual Recognition](#7-model-visual-recognition)
|
||||
- [`8` TTS & STT Voice Conversation](#8-tts--stt-voice-conversation)
|
||||
- [`9` Text to Image Generation](#9-text-to-image-generation)
|
||||
- [`10` Plugin System (Function Calling)](#10-plugin-system-function-calling)
|
||||
- [`11` Agent Market (GPTs)](#11-agent-market-gpts)
|
||||
- [`12` Support Local / Remote Database](#12-support-local--remote-database)
|
||||
- [`13` Support Multi-User Management](#13-support-multi-user-management)
|
||||
- [`14` Progressive Web App (PWA)](#14-progressive-web-app-pwa)
|
||||
- [`15` Mobile Device Adaptation](#15-mobile-device-adaptation)
|
||||
- [`16` Custom Themes](#16-custom-themes)
|
||||
- [✨ MCP Plugin One-Click Installation](#-mcp-plugin-one-click-installation)
|
||||
- [🏪 MCP Marketplace](#-mcp-marketplace)
|
||||
- [🖥️ Desktop App](#️-desktop-app)
|
||||
- [🌐 Smart Internet Search](#-smart-internet-search)
|
||||
- [Chain of Thought](#chain-of-thought)
|
||||
- [Branching Conversations](#branching-conversations)
|
||||
- [Artifacts Support](#artifacts-support)
|
||||
- [File Upload /Knowledge Base](#file-upload-knowledge-base)
|
||||
- [Multi-Model Service Provider Support](#multi-model-service-provider-support)
|
||||
- [Local Large Language Model (LLM) Support](#local-large-language-model-llm-support)
|
||||
- [Model Visual Recognition](#model-visual-recognition)
|
||||
- [TTS & STT Voice Conversation](#tts--stt-voice-conversation)
|
||||
- [Text to Image Generation](#text-to-image-generation)
|
||||
- [Plugin System (Function Calling)](#plugin-system-function-calling)
|
||||
- [Agent Market (GPTs)](#agent-market-gpts)
|
||||
- [Support Local / Remote Database](#support-local--remote-database)
|
||||
- [Support Multi-User Management](#support-multi-user-management)
|
||||
- [Progressive Web App (PWA)](#progressive-web-app-pwa)
|
||||
- [Mobile Device Adaptation](#mobile-device-adaptation)
|
||||
- [Custom Themes](#custom-themes)
|
||||
- [`*` What's more](#-whats-more)
|
||||
- [⚡️ Performance](#️-performance)
|
||||
- [🛳 Self Hosting](#-self-hosting)
|
||||
@@ -114,9 +118,59 @@ Whether for users or professional developers, LobeHub will be your AI Agent play
|
||||
|
||||
## ✨ Features
|
||||
|
||||
Transform your AI experience with LobeChat's powerful features designed for seamless connectivity, enhanced productivity, and unlimited creativity.
|
||||
|
||||
![][image-feat-mcp]
|
||||
|
||||
### ✨ MCP Plugin One-Click Installation
|
||||
|
||||
**Seamlessly Connect Your AI to the World**
|
||||
|
||||
Unlock the full potential of your AI by enabling smooth, secure, and dynamic interactions with external tools, data sources, and services. LobeChat's MCP (Model Context Protocol) plugin system breaks down the barriers between your AI and the digital ecosystem, allowing for unprecedented connectivity and functionality.
|
||||
|
||||
Transform your conversations into powerful workflows by connecting to databases, APIs, file systems, and more. Experience the freedom of AI that truly understands and interacts with your world.
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
![][image-feat-mcp-market]
|
||||
|
||||
### 🏪 MCP Marketplace
|
||||
|
||||
**Discover, Connect, Extend**
|
||||
|
||||
Browse a growing library of MCP plugins to expand your AI's capabilities and streamline your workflows effortlessly. Visit [lobehub.com/mcp](https://lobehub.com/mcp) to explore the MCP Marketplace, which offers a curated collection of integrations that enhance your AI's ability to work with various tools and services.
|
||||
|
||||
From productivity tools to development environments, discover new ways to extend your AI's reach and effectiveness. Connect with the community and find the perfect plugins for your specific needs.
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
![][image-feat-desktop]
|
||||
|
||||
### 🖥️ Desktop App
|
||||
|
||||
**Peak Performance, Zero Distractions**
|
||||
|
||||
Get the full LobeChat experience without browser limitations—lightweight, focused, and always ready to go. Our desktop application provides a dedicated environment for your AI interactions, ensuring optimal performance and minimal distractions.
|
||||
|
||||
Experience faster response times, better resource management, and a more stable connection to your AI assistant. The desktop app is designed for users who demand the best performance from their AI tools.
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
![][image-feat-web-search]
|
||||
|
||||
### 🌐 Smart Internet Search
|
||||
|
||||
**Online Knowledge On Demand**
|
||||
|
||||
With real-time internet access, your AI keeps up with the world—news, data, trends, and more. Stay informed and get the most current information available, enabling your AI to provide accurate and up-to-date responses.
|
||||
|
||||
Access live information, verify facts, and explore current events without leaving your conversation. Your AI becomes a gateway to the world's knowledge, always current and comprehensive.
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
[![][image-feat-cot]][docs-feat-cot]
|
||||
|
||||
### `1` [Chain of Thought][docs-feat-cot]
|
||||
### [Chain of Thought][docs-feat-cot]
|
||||
|
||||
Experience AI reasoning like never before. Watch as complex problems unfold step by step through our innovative Chain of Thought (CoT) visualization. This breakthrough feature provides unprecedented transparency into AI's decision-making process, allowing you to observe how conclusions are reached in real-time.
|
||||
|
||||
@@ -126,7 +180,7 @@ By breaking down complex reasoning into clear, logical steps, you can better und
|
||||
|
||||
[![][image-feat-branch]][docs-feat-branch]
|
||||
|
||||
### `2` [Branching Conversations][docs-feat-branch]
|
||||
### [Branching Conversations][docs-feat-branch]
|
||||
|
||||
Introducing a more natural and flexible way to chat with AI. With Branch Conversations, your discussions can flow in multiple directions, just like human conversations do. Create new conversation branches from any message, giving you the freedom to explore different paths while preserving the original context.
|
||||
|
||||
@@ -141,7 +195,7 @@ This groundbreaking feature transforms linear conversations into dynamic, tree-l
|
||||
|
||||
[![][image-feat-artifacts]][docs-feat-artifacts]
|
||||
|
||||
### `3` [Artifacts Support][docs-feat-artifacts]
|
||||
### [Artifacts Support][docs-feat-artifacts]
|
||||
|
||||
Experience the power of Claude Artifacts, now integrated into LobeChat. This revolutionary feature expands the boundaries of AI-human interaction, enabling real-time creation and visualization of diverse content formats.
|
||||
|
||||
@@ -155,7 +209,7 @@ Create and visualize with unprecedented flexibility:
|
||||
|
||||
[![][image-feat-knowledgebase]][docs-feat-knowledgebase]
|
||||
|
||||
### `4` [File Upload /Knowledge Base][docs-feat-knowledgebase]
|
||||
### [File Upload /Knowledge Base][docs-feat-knowledgebase]
|
||||
|
||||
LobeChat supports file upload and knowledge base functionality. You can upload various types of files including documents, images, audio, and video, as well as create knowledge bases, making it convenient for users to manage and search for files. Additionally, you can utilize files and knowledge base features during conversations, enabling a richer dialogue experience.
|
||||
|
||||
@@ -173,7 +227,7 @@ LobeChat supports file upload and knowledge base functionality. You can upload v
|
||||
|
||||
[![][image-feat-privoder]][docs-feat-provider]
|
||||
|
||||
### `5` [Multi-Model Service Provider Support][docs-feat-provider]
|
||||
### [Multi-Model Service Provider Support][docs-feat-provider]
|
||||
|
||||
In the continuous development of LobeChat, we deeply understand the importance of diversity in model service providers for meeting the needs of the community when providing AI conversation services. Therefore, we have expanded our support to multiple model service providers, rather than being limited to a single one, in order to offer users a more diverse and rich selection of conversations.
|
||||
|
||||
@@ -191,14 +245,13 @@ We have implemented support for the following model service providers:
|
||||
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock is a service provided by Amazon AWS, focusing on delivering advanced AI language and visual models for enterprises. Its model family includes Anthropic's Claude series, Meta's Llama 3.1 series, and more, offering a range of options from lightweight to high-performance, supporting tasks such as text generation, conversation, and image processing for businesses of varying scales and needs.
|
||||
- **[Google](https://lobechat.com/discover/provider/google)**: Google's Gemini series represents its most advanced, versatile AI models, developed by Google DeepMind, designed for multimodal capabilities, supporting seamless understanding and processing of text, code, images, audio, and video. Suitable for various environments from data centers to mobile devices, it significantly enhances the efficiency and applicability of AI models.
|
||||
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek is a company focused on AI technology research and application, with its latest model DeepSeek-V2.5 integrating general dialogue and code processing capabilities, achieving significant improvements in human preference alignment, writing tasks, and instruction following.
|
||||
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc.
|
||||
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: The HuggingFace Inference API provides a fast and free way for you to explore thousands of models for various tasks. Whether you are prototyping for a new application or experimenting with the capabilities of machine learning, this API gives you instant access to high-performance models across multiple domains.
|
||||
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter is a service platform providing access to various cutting-edge large model interfaces, supporting OpenAI, Anthropic, LLaMA, and more, suitable for diverse development and application needs. Users can flexibly choose the optimal model and pricing based on their requirements, enhancing the AI experience.
|
||||
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: Run serverless GPU-powered machine learning models on Cloudflare's global network.
|
||||
- **[GitHub](https://lobechat.com/discover/provider/github)**: With GitHub Models, developers can become AI engineers and leverage the industry's leading AI models.
|
||||
|
||||
<details><summary><kbd>See more providers (+31)</kbd></summary>
|
||||
|
||||
- **[GitHub](https://lobechat.com/discover/provider/github)**: With GitHub Models, developers can become AI engineers and leverage the industry's leading AI models.
|
||||
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI is a platform providing a variety of large language models and AI image generation API services, flexible, reliable, and cost-effective. It supports the latest open-source models like Llama3 and Mistral, offering a comprehensive, user-friendly, and auto-scaling API solution for generative AI application development, suitable for the rapid growth of AI startups.
|
||||
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO supports stable and cost-efficient open-source LLM APIs, such as DeepSeek, Llama, Qwen etc.
|
||||
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI is dedicated to achieving leading performance through innovative AI models, offering extensive customization capabilities, including rapid scaling support and intuitive deployment processes to meet various enterprise needs.
|
||||
@@ -206,10 +259,11 @@ We have implemented support for the following model service providers:
|
||||
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq's LPU inference engine has excelled in the latest independent large language model (LLM) benchmarks, redefining the standards for AI solutions with its remarkable speed and efficiency. Groq represents instant inference speed, demonstrating strong performance in cloud-based deployments.
|
||||
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity is a leading provider of conversational generation models, offering various advanced Llama 3.1 models that support both online and offline applications, particularly suited for complex natural language processing tasks.
|
||||
- **[Mistral](https://lobechat.com/discover/provider/mistral)**: Mistral provides advanced general, specialized, and research models widely used in complex reasoning, multilingual tasks, and code generation. Through functional calling interfaces, users can integrate custom functionalities for specific applications.
|
||||
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope is a model-as-a-service platform launched by Alibaba Cloud, offering a wide range of AI models and inference services.
|
||||
- **[Ai21Labs](https://lobechat.com/discover/provider/ai21)**: AI21 Labs builds foundational models and AI systems for enterprises, accelerating the application of generative AI in production.
|
||||
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage focuses on developing AI models for various business needs, including Solar LLM and document AI, aiming to achieve artificial general intelligence (AGI) for work. It allows for the creation of simple conversational agents through Chat API and supports functional calling, translation, embedding, and domain-specific applications.
|
||||
- **[xAI](https://lobechat.com/discover/provider/xai)**: xAI is a company dedicated to building artificial intelligence to accelerate human scientific discovery. Our mission is to advance our collective understanding of the universe.
|
||||
- **[Qwen](https://lobechat.com/discover/provider/qwen)**: Tongyi Qianwen is a large-scale language model independently developed by Alibaba Cloud, featuring strong natural language understanding and generation capabilities. It can answer various questions, create written content, express opinions, and write code, playing a role in multiple fields.
|
||||
- **[xAI (Grok)](https://lobechat.com/discover/provider/xai)**: xAI is a company dedicated to building artificial intelligence to accelerate human scientific discovery. Our mission is to advance our collective understanding of the universe.
|
||||
- **[Aliyun Bailian](https://lobechat.com/discover/provider/qwen)**: Tongyi Qianwen is a large-scale language model independently developed by Alibaba Cloud, featuring strong natural language understanding and generation capabilities. It can answer various questions, create written content, express opinions, and write code, playing a role in multiple fields.
|
||||
- **[Wenxin](https://lobechat.com/discover/provider/wenxin)**: An enterprise-level one-stop platform for large model and AI-native application development and services, providing the most comprehensive and user-friendly toolchain for the entire process of generative artificial intelligence model development and application development.
|
||||
- **[Hunyuan](https://lobechat.com/discover/provider/hunyuan)**: A large language model developed by Tencent, equipped with powerful Chinese creative capabilities, logical reasoning abilities in complex contexts, and reliable task execution skills.
|
||||
- **[ZhiPu](https://lobechat.com/discover/provider/zhipu)**: Zhipu AI offers an open platform for multimodal and language models, supporting a wide range of AI application scenarios, including text processing, image understanding, and programming assistance.
|
||||
@@ -246,7 +300,7 @@ At the same time, we are also planning to support more model service providers.
|
||||
|
||||
[![][image-feat-local]][docs-feat-local]
|
||||
|
||||
### `6` [Local Large Language Model (LLM) Support][docs-feat-local]
|
||||
### [Local Large Language Model (LLM) Support][docs-feat-local]
|
||||
|
||||
To meet the specific needs of users, LobeChat also supports the use of local models based on [Ollama](https://ollama.ai), allowing users to flexibly use their own or third-party models.
|
||||
|
||||
@@ -262,7 +316,7 @@ To meet the specific needs of users, LobeChat also supports the use of local mod
|
||||
|
||||
[![][image-feat-vision]][docs-feat-vision]
|
||||
|
||||
### `7` [Model Visual Recognition][docs-feat-vision]
|
||||
### [Model Visual Recognition][docs-feat-vision]
|
||||
|
||||
LobeChat now supports OpenAI's latest [`gpt-4-vision`](https://platform.openai.com/docs/guides/vision) model with visual recognition capabilities,
|
||||
a multimodal intelligence that can perceive visuals. Users can easily upload or drag and drop images into the dialogue box,
|
||||
@@ -280,7 +334,7 @@ Whether it's sharing images in daily use or interpreting images within specific
|
||||
|
||||
[![][image-feat-tts]][docs-feat-tts]
|
||||
|
||||
### `8` [TTS & STT Voice Conversation][docs-feat-tts]
|
||||
### [TTS & STT Voice Conversation][docs-feat-tts]
|
||||
|
||||
LobeChat supports Text-to-Speech (TTS) and Speech-to-Text (STT) technologies, enabling our application to convert text messages into clear voice outputs,
|
||||
allowing users to interact with our conversational agent as if they were talking to a real person. Users can choose from a variety of voices to pair with the agent.
|
||||
@@ -297,7 +351,7 @@ Users can choose the voice that suits their personal preferences or specific sce
|
||||
|
||||
[![][image-feat-t2i]][docs-feat-t2i]
|
||||
|
||||
### `9` [Text to Image Generation][docs-feat-t2i]
|
||||
### [Text to Image Generation][docs-feat-t2i]
|
||||
|
||||
With support for the latest text-to-image generation technology, LobeChat now allows users to invoke image creation tools directly within conversations with the agent. By leveraging the capabilities of AI tools such as [`DALL-E 3`](https://openai.com/dall-e-3), [`MidJourney`](https://www.midjourney.com/), and [`Pollinations`](https://pollinations.ai/), the agents are now equipped to transform your ideas into images.
|
||||
|
||||
@@ -311,7 +365,7 @@ This enables a more private and immersive creative process, allowing for the sea
|
||||
|
||||
[![][image-feat-plugin]][docs-feat-plugin]
|
||||
|
||||
### `10` [Plugin System (Function Calling)][docs-feat-plugin]
|
||||
### [Plugin System (Function Calling)][docs-feat-plugin]
|
||||
|
||||
The plugin ecosystem of LobeChat is an important extension of its core functionality, greatly enhancing the practicality and flexibility of the LobeChat assistant.
|
||||
|
||||
@@ -329,12 +383,12 @@ 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-03-23**</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-05-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` |
|
||||
|
||||
> 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
|
||||
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
@@ -346,7 +400,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
||||
|
||||
[![][image-feat-agent]][docs-feat-agent]
|
||||
|
||||
### `11` [Agent Market (GPTs)][docs-feat-agent]
|
||||
### [Agent Market (GPTs)][docs-feat-agent]
|
||||
|
||||
In LobeChat Agent Marketplace, creators can discover a vibrant and innovative community that brings together a multitude of well-designed agents,
|
||||
which not only play an important role in work scenarios but also offer great convenience in learning processes.
|
||||
@@ -366,14 +420,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
||||
|
||||
<!-- AGENT LIST -->
|
||||
|
||||
| Recent Submits | Description |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [学术论文综述专家](https://lobechat.com/discover/assistant/academic-paper-overview)<br/><sup>By **[arvinxx](https://github.com/arvinxx)** on **2025-03-11**</sup> | 擅长高质量文献检索与分析的学术研究助手<br/>`学术研究` `文献检索` `数据分析` `信息提取` `咨询` |
|
||||
| [Cron Expression Assistant](https://lobechat.com/discover/assistant/crontab-generate)<br/><sup>By **[edgesider](https://github.com/edgesider)** on **2025-02-17**</sup> | Crontab Expression Generator<br/>`crontab` `time-expression` `trigger-time` `generator` `technical-assistance` |
|
||||
| [Xiao Zhi French Translation Assistant](https://lobechat.com/discover/assistant/xiao-zhi-french-translation-asst-v-1)<br/><sup>By **[WeR-Best](https://github.com/WeR-Best)** on **2025-02-10**</sup> | A friendly, professional, and empathetic AI assistant for French translation<br/>`ai-assistant` `french-translation` `cross-cultural-communication` `creativity` |
|
||||
| [Investment Assistant](https://lobechat.com/discover/assistant/graham-investmentassi)<br/><sup>By **[farsightlin](https://github.com/farsightlin)** on **2025-02-06**</sup> | Helps users calculate the data needed for valuation<br/>`investment` `valuation` `financial-analysis` `calculator` |
|
||||
| Recent Submits | Description |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| [Turtle Soup Host](https://lobechat.com/discover/assistant/lateral-thinking-puzzle)<br/><sup>By **[CSY2022](https://github.com/CSY2022)** on **2025-06-19**</sup> | A turtle soup host needs to provide the scenario, the complete story (truth of the event), and the key point (the condition for guessing correctly).<br/>`turtle-soup` `reasoning` `interaction` `puzzle` `role-playing` |
|
||||
| [Gourmet Reviewer🍟](https://lobechat.com/discover/assistant/food-reviewer)<br/><sup>By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17**</sup> | Food critique expert<br/>`gourmet` `review` `writing` |
|
||||
| [Academic Writing Assistant](https://lobechat.com/discover/assistant/academic-writing-assistant)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2025-06-17**</sup> | Expert in academic research paper writing and formal documentation<br/>`academic-writing` `research` `formal-style` |
|
||||
| [Minecraft Senior Developer](https://lobechat.com/discover/assistant/java-development)<br/><sup>By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17**</sup> | Expert in advanced Java development and Minecraft mod and server plugin development<br/>`development` `programming` `minecraft` `java` |
|
||||
|
||||
> 📊 Total agents: [<kbd>**488**</kbd> ](https://lobechat.com/discover/assistants)
|
||||
> 📊 Total agents: [<kbd>**505**</kbd> ](https://lobechat.com/discover/assistants)
|
||||
|
||||
<!-- AGENT LIST -->
|
||||
|
||||
@@ -385,7 +439,7 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
||||
|
||||
[![][image-feat-database]][docs-feat-database]
|
||||
|
||||
### `12` [Support Local / Remote Database][docs-feat-database]
|
||||
### [Support Local / Remote Database][docs-feat-database]
|
||||
|
||||
LobeChat supports the use of both server-side and local databases. Depending on your needs, you can choose the appropriate deployment solution:
|
||||
|
||||
@@ -402,7 +456,7 @@ Regardless of which database you choose, LobeChat can provide you with an excell
|
||||
|
||||
[![][image-feat-auth]][docs-feat-auth]
|
||||
|
||||
### `13` [Support Multi-User Management][docs-feat-auth]
|
||||
### [Support Multi-User Management][docs-feat-auth]
|
||||
|
||||
LobeChat supports multi-user management and provides two main user authentication and management solutions to meet different needs:
|
||||
|
||||
@@ -420,7 +474,7 @@ Regardless of which user management solution you choose, LobeChat can provide yo
|
||||
|
||||
[![][image-feat-pwa]][docs-feat-pwa]
|
||||
|
||||
### `14` [Progressive Web App (PWA)][docs-feat-pwa]
|
||||
### [Progressive Web App (PWA)][docs-feat-pwa]
|
||||
|
||||
We deeply understand the importance of providing a seamless experience for users in today's multi-device environment.
|
||||
Therefore, we have adopted Progressive Web Application ([PWA](https://support.google.com/chrome/answer/9658361)) technology,
|
||||
@@ -447,7 +501,7 @@ providing smooth animations, responsive layouts, and adapting to different devic
|
||||
|
||||
[![][image-feat-mobile]][docs-feat-mobile]
|
||||
|
||||
### `15` [Mobile Device Adaptation][docs-feat-mobile]
|
||||
### [Mobile Device Adaptation][docs-feat-mobile]
|
||||
|
||||
We have carried out a series of optimization designs for mobile devices to enhance the user's mobile experience. Currently, we are iterating on the mobile user experience to achieve smoother and more intuitive interactions. If you have any suggestions or ideas, we welcome you to provide feedback through GitHub Issues or Pull Requests.
|
||||
|
||||
@@ -459,7 +513,7 @@ We have carried out a series of optimization designs for mobile devices to enhan
|
||||
|
||||
[![][image-feat-theme]][docs-feat-theme]
|
||||
|
||||
### `16` [Custom Themes][docs-feat-theme]
|
||||
### [Custom Themes][docs-feat-theme]
|
||||
|
||||
As a design-engineering-oriented application, LobeChat places great emphasis on users' personalized experiences,
|
||||
hence introducing flexible and diverse theme modes, including a light mode for daytime and a dark mode for nighttime.
|
||||
@@ -627,7 +681,7 @@ This project provides some additional configuration items set with environment v
|
||||
|
||||
## 🧩 Plugins
|
||||
|
||||
Plugins provide a means to extend the [Function Calling][docs-functionc-call] capabilities of LobeChat. They can be used to introduce new function calls and even new ways to render message results. If you are interested in plugin development, please refer to our [📘 Plugin Development Guide][docs-plugin-dev] in the Wiki.
|
||||
Plugins provide a means to extend the [Function Calling][docs-function-call] capabilities of LobeChat. They can be used to introduce new function calls and even new ways to render message results. If you are interested in plugin development, please refer to our [📘 Plugin Development Guide][docs-plugin-dev] in the Wiki.
|
||||
|
||||
- [lobe-chat-plugins][lobe-chat-plugins]: This is the plugin index for LobeChat. It accesses index.json from this repository to display a list of available plugins for LobeChat to the user.
|
||||
- [chat-plugin-template][chat-plugin-template]: This is the plugin template for LobeChat plugin development.
|
||||
@@ -821,7 +875,7 @@ This project is [Apache 2.0](./LICENSE) licensed.
|
||||
[docs-feat-theme]: https://lobehub.com/docs/usage/features/theme
|
||||
[docs-feat-tts]: https://lobehub.com/docs/usage/features/tts
|
||||
[docs-feat-vision]: https://lobehub.com/docs/usage/features/vision
|
||||
[docs-functionc-call]: https://lobehub.com/blog/openai-function-call
|
||||
[docs-function-call]: https://lobehub.com/blog/openai-function-call
|
||||
[docs-lighthouse]: https://github.com/lobehub/lobe-chat/wiki/Lighthouse
|
||||
[docs-plugin-dev]: https://lobehub.com/docs/usage/plugins/development
|
||||
[docs-self-hosting]: https://lobehub.com/docs/self-hosting/start
|
||||
@@ -858,8 +912,11 @@ This project is [Apache 2.0](./LICENSE) licensed.
|
||||
[image-feat-branch]: https://github.com/user-attachments/assets/92f72082-02bd-4835-9c54-b089aad7fd41
|
||||
[image-feat-cot]: https://github.com/user-attachments/assets/f74f1139-d115-4e9c-8c43-040a53797a5e
|
||||
[image-feat-database]: https://github.com/user-attachments/assets/f1697c8b-d1fb-4dac-ba05-153c6295d91d
|
||||
[image-feat-desktop]: https://github.com/user-attachments/assets/a7bac8d3-ea96-4000-bb39-fadc9b610f96
|
||||
[image-feat-knowledgebase]: https://github.com/user-attachments/assets/7da7a3b2-92fd-4630-9f4e-8560c74955ae
|
||||
[image-feat-local]: https://github.com/user-attachments/assets/1239da50-d832-4632-a7ef-bd754c0f3850
|
||||
[image-feat-mcp]: https://github.com/user-attachments/assets/1be85d36-3975-4413-931f-27e05e440995
|
||||
[image-feat-mcp-market]: https://github.com/user-attachments/assets/bb114f9f-24c5-4000-a984-c10d187da5a0
|
||||
[image-feat-mobile]: https://github.com/user-attachments/assets/32cf43c4-96bd-4a4c-bfb6-59acde6fe380
|
||||
[image-feat-plugin]: https://github.com/user-attachments/assets/66a891ac-01b6-4e3f-b978-2eb07b489b1b
|
||||
[image-feat-privoder]: https://github.com/user-attachments/assets/e553e407-42de-4919-977d-7dbfcf44a821
|
||||
@@ -868,6 +925,7 @@ This project is [Apache 2.0](./LICENSE) licensed.
|
||||
[image-feat-theme]: https://github.com/user-attachments/assets/b47c39f1-806f-492b-8fcb-b0fa973937c1
|
||||
[image-feat-tts]: https://github.com/user-attachments/assets/50189597-2cc3-4002-b4c8-756a52ad5c0a
|
||||
[image-feat-vision]: https://github.com/user-attachments/assets/18574a1f-46c2-4cbc-af2c-35a86e128a07
|
||||
[image-feat-web-search]: https://github.com/user-attachments/assets/cfdc48ac-b5f8-4a00-acee-db8f2eba09ad
|
||||
[image-overview]: https://github.com/user-attachments/assets/dbfaa84a-2c82-4dd9-815c-5be616f264a4
|
||||
[image-star]: https://github.com/user-attachments/assets/c3b482e7-cef5-4e94-bef9-226900ecfaab
|
||||
[issues-link]: https://img.shields.io/github/issues/lobehub/lobe-chat.svg?style=flat
|
||||
|
||||
+105
-49
@@ -52,22 +52,26 @@
|
||||
|
||||
- [👋🏻 开始使用 & 交流](#-开始使用--交流)
|
||||
- [✨ 特性一览](#-特性一览)
|
||||
- [`1` 思维链 (CoT)](#1-思维链-cot)
|
||||
- [`2` 分支对话](#2-分支对话)
|
||||
- [`3` 支持白板 (Artifacts)](#3-支持白板-artifacts)
|
||||
- [`4` 文件上传 / 知识库](#4-文件上传--知识库)
|
||||
- [`5` 多模型服务商支持](#5-多模型服务商支持)
|
||||
- [`6` 支持本地大语言模型 (LLM)](#6-支持本地大语言模型-llm)
|
||||
- [`7` 模型视觉识别 (Model Visual)](#7-模型视觉识别-model-visual)
|
||||
- [`8` TTS & STT 语音会话](#8-tts--stt-语音会话)
|
||||
- [`9` Text to Image 文生图](#9-text-to-image-文生图)
|
||||
- [`10` 插件系统 (Tools Calling)](#10-插件系统-tools-calling)
|
||||
- [`11` 助手市场 (GPTs)](#11-助手市场-gpts)
|
||||
- [`12` 支持本地 / 远程数据库](#12-支持本地--远程数据库)
|
||||
- [`13` 支持多用户管理](#13-支持多用户管理)
|
||||
- [`14` 渐进式 Web 应用 (PWA)](#14-渐进式-web-应用-pwa)
|
||||
- [`15` 移动设备适配](#15-移动设备适配)
|
||||
- [`16` 自定义主题](#16-自定义主题)
|
||||
- [✨ MCP 插件一键安装](#-mcp-插件一键安装)
|
||||
- [🏪 MCP 市场](#-mcp-市场)
|
||||
- [🖥️ 桌面应用](#️-桌面应用)
|
||||
- [🌐 智能联网搜索](#-智能联网搜索)
|
||||
- [思维链 (CoT)](#思维链-cot)
|
||||
- [分支对话](#分支对话)
|
||||
- [支持白板 (Artifacts)](#支持白板-artifacts)
|
||||
- [文件上传 / 知识库](#文件上传--知识库)
|
||||
- [多模型服务商支持](#多模型服务商支持)
|
||||
- [支持本地大语言模型 (LLM)](#支持本地大语言模型-llm)
|
||||
- [模型视觉识别 (Model Visual)](#模型视觉识别-model-visual)
|
||||
- [TTS & STT 语音会话](#tts--stt-语音会话)
|
||||
- [Text to Image 文生图](#text-to-image-文生图)
|
||||
- [插件系统 (Tools Calling)](#插件系统-tools-calling)
|
||||
- [助手市场 (GPTs)](#助手市场-gpts)
|
||||
- [支持本地 / 远程数据库](#支持本地--远程数据库)
|
||||
- [支持多用户管理](#支持多用户管理)
|
||||
- [渐进式 Web 应用 (PWA)](#渐进式-web-应用-pwa)
|
||||
- [移动设备适配](#移动设备适配)
|
||||
- [自定义主题](#自定义主题)
|
||||
- [`*` 更多特性](#-更多特性)
|
||||
- [⚡️ 性能测试](#️-性能测试)
|
||||
- [🛳 开箱即用](#-开箱即用)
|
||||
@@ -114,9 +118,59 @@
|
||||
|
||||
## ✨ 特性一览
|
||||
|
||||
通过 LobeChat 的强大功能,体验为无缝连接、提升效率和无限创意而设计的全新 AI 体验。
|
||||
|
||||
### ✨ MCP 插件一键安装
|
||||
|
||||
[](https://lobehub.com/mcp)
|
||||
|
||||
**无缝连接你的 AI 与世界**
|
||||
|
||||
通过启用与外部工具、数据源和服务的平滑、安全和动态交互,释放你的 AI 的全部潜力。基于 MCP(模型上下文协议)的插件系统打破了 AI 与数字生态系统之间的壁垒,实现了前所未有的连接性和功能性。
|
||||
|
||||
将对话转化为强大的工作流程,连接数据库、API、文件系统等。体验真正理解并与你的世界互动的 AI Agent。
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
### 🏪 MCP 市场
|
||||
|
||||
![][image-feat-mcp-market]
|
||||
|
||||
**发现、连接、扩展**
|
||||
|
||||
浏览不断增长的 MCP 插件库,轻松扩展你的 AI 能力并简化工作流程。访问 [lobehub.com/mcp](https://lobehub.com/mcp) 探索 MCP 市场,提供精选的集成集合,增强你的 AI 与各种工具和服务协作的能力。
|
||||
|
||||
从生产力工具到开发环境,发现扩展 AI 覆盖范围和效率的新方式。与社区连接,找到满足特定需求的完美插件。
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
### 🖥️ 桌面应用
|
||||
|
||||
![][image-feat-desktop]
|
||||
|
||||
**巅峰性能,零干扰**
|
||||
|
||||
获得完整的 LobeChat 体验,摆脱浏览器限制 —— 轻量级、专注且随时就绪。我们的桌面应用程序为你的 AI 交互提供专用环境,确保最佳性能和最小干扰。
|
||||
|
||||
体验更快的响应时间、更好的资源管理和与 AI 助手的更稳定连接。桌面应用专为要求 AI 工具最佳性能的用户设计。
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
### 🌐 智能联网搜索
|
||||
|
||||
![][image-feat-web-search]
|
||||
|
||||
**在线知识,按需获取**
|
||||
|
||||
通过实时联网访问,你的 AI 与世界保持同步 —— 新闻、数据、趋势等。保持信息更新,获取最新可用信息,使你的 AI 能够提供准确和最新的回复。
|
||||
|
||||
访问实时信息,验证事实,探索当前事件,无需离开对话。你的 AI 成为通向世界知识的门户,始终保持最新和全面。
|
||||
|
||||
[![][back-to-top]](#readme-top)
|
||||
|
||||
[![][image-feat-cot]][docs-feat-cot]
|
||||
|
||||
### `1` [思维链 (CoT)][docs-feat-cot]
|
||||
### [思维链 (CoT)][docs-feat-cot]
|
||||
|
||||
体验前所未有的 AI 推理过程。通过创新的思维链(CoT)可视化功能,您可以实时观察复杂问题是如何一步步被解析的。这项突破性的功能为 AI 的决策过程提供了前所未有的透明度,让您能够清晰地了解结论是如何得出的。
|
||||
|
||||
@@ -126,7 +180,7 @@
|
||||
|
||||
[![][image-feat-branch]][docs-feat-branch]
|
||||
|
||||
### `2` [分支对话][docs-feat-branch]
|
||||
### [分支对话][docs-feat-branch]
|
||||
|
||||
为您带来更自然、更灵活的 AI 对话方式。通过分支对话功能,您的讨论可以像人类对话一样自然延伸。在任意消息处创建新的对话分支,让您在保留原有上下文的同时,自由探索不同的对话方向。
|
||||
|
||||
@@ -141,7 +195,7 @@
|
||||
|
||||
[![][image-feat-artifacts]][docs-feat-artifacts]
|
||||
|
||||
### `3` [支持白板 (Artifacts)][docs-feat-artifacts]
|
||||
### [支持白板 (Artifacts)][docs-feat-artifacts]
|
||||
|
||||
体验集成于 LobeChat 的 Claude Artifacts 能力。这项革命性功能突破了 AI 人机交互的边界,让您能够实时创建和可视化各种格式的内容。
|
||||
|
||||
@@ -155,7 +209,7 @@
|
||||
|
||||
[![][image-feat-knowledgebase]][docs-feat-knowledgebase]
|
||||
|
||||
### `4` [文件上传 / 知识库][docs-feat-knowledgebase]
|
||||
### [文件上传 / 知识库][docs-feat-knowledgebase]
|
||||
|
||||
LobeChat 支持文件上传与知识库功能,你可以上传文件、图片、音频、视频等多种类型的文件,以及创建知识库,方便用户管理和查找文件。同时在对话中使用文件和知识库功能,实现更加丰富的对话体验。
|
||||
|
||||
@@ -173,7 +227,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
||||
|
||||
[![][image-feat-privoder]][docs-feat-provider]
|
||||
|
||||
### `5` [多模型服务商支持][docs-feat-provider]
|
||||
### [多模型服务商支持][docs-feat-provider]
|
||||
|
||||
在 LobeChat 的不断发展过程中,我们深刻理解到在提供 AI 会话服务时模型服务商的多样性对于满足社区需求的重要性。因此,我们不再局限于单一的模型服务商,而是拓展了对多种模型服务商的支持,以便为用户提供更为丰富和多样化的会话选择。
|
||||
|
||||
@@ -191,14 +245,13 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
||||
- **[Bedrock](https://lobechat.com/discover/provider/bedrock)**: Bedrock 是亚马逊 AWS 提供的一项服务,专注于为企业提供先进的 AI 语言模型和视觉模型。其模型家族包括 Anthropic 的 Claude 系列、Meta 的 Llama 3.1 系列等,涵盖从轻量级到高性能的多种选择,支持文本生成、对话、图像处理等多种任务,适用于不同规模和需求的企业应用。
|
||||
- **[Google](https://lobechat.com/discover/provider/google)**: Google 的 Gemini 系列是其最先进、通用的 AI 模型,由 Google DeepMind 打造,专为多模态设计,支持文本、代码、图像、音频和视频的无缝理解与处理。适用于从数据中心到移动设备的多种环境,极大提升了 AI 模型的效率与应用广泛性。
|
||||
- **[DeepSeek](https://lobechat.com/discover/provider/deepseek)**: DeepSeek 是一家专注于人工智能技术研究和应用的公司,其最新模型 DeepSeek-V3 多项评测成绩超越 Qwen2.5-72B 和 Llama-3.1-405B 等开源模型,性能对齐领军闭源模型 GPT-4o 与 Claude-3.5-Sonnet。
|
||||
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO 派欧云提供稳定、高性价比的开源模型 API 服务,支持 DeepSeek 全系列、Llama、Qwen 等行业领先大模型。
|
||||
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: HuggingFace Inference API 提供了一种快速且免费的方式,让您可以探索成千上万种模型,适用于各种任务。无论您是在为新应用程序进行原型设计,还是在尝试机器学习的功能,这个 API 都能让您即时访问多个领域的高性能模型。
|
||||
- **[OpenRouter](https://lobechat.com/discover/provider/openrouter)**: OpenRouter 是一个提供多种前沿大模型接口的服务平台,支持 OpenAI、Anthropic、LLaMA 及更多,适合多样化的开发和应用需求。用户可根据自身需求灵活选择最优的模型和价格,助力 AI 体验的提升。
|
||||
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: 在 Cloudflare 的全球网络上运行由无服务器 GPU 驱动的机器学习模型。
|
||||
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过 GitHub 模型,开发人员可以成为 AI 工程师,并使用行业领先的 AI 模型进行构建。
|
||||
|
||||
<details><summary><kbd>See more providers (+31)</kbd></summary>
|
||||
|
||||
- **[GitHub](https://lobechat.com/discover/provider/github)**: 通过 GitHub 模型,开发人员可以成为 AI 工程师,并使用行业领先的 AI 模型进行构建。
|
||||
- **[Novita](https://lobechat.com/discover/provider/novita)**: Novita AI 是一个提供多种大语言模型与 AI 图像生成的 API 服务的平台,灵活、可靠且具有成本效益。它支持 Llama3、Mistral 等最新的开源模型,并为生成式 AI 应用开发提供了全面、用户友好且自动扩展的 API 解决方案,适合 AI 初创公司的快速发展。
|
||||
- **[PPIO](https://lobechat.com/discover/provider/ppio)**: PPIO 派欧云提供稳定、高性价比的开源模型 API 服务,支持 DeepSeek 全系列、Llama、Qwen 等行业领先大模型。
|
||||
- **[Together AI](https://lobechat.com/discover/provider/togetherai)**: Together AI 致力于通过创新的 AI 模型实现领先的性能,提供广泛的自定义能力,包括快速扩展支持和直观的部署流程,满足企业的各种需求。
|
||||
@@ -206,10 +259,11 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
||||
- **[Groq](https://lobechat.com/discover/provider/groq)**: Groq 的 LPU 推理引擎在最新的独立大语言模型(LLM)基准测试中表现卓越,以其惊人的速度和效率重新定义了 AI 解决方案的标准。Groq 是一种即时推理速度的代表,在基于云的部署中展现了良好的性能。
|
||||
- **[Perplexity](https://lobechat.com/discover/provider/perplexity)**: Perplexity 是一家领先的对话生成模型提供商,提供多种先进的 Llama 3.1 模型,支持在线和离线应用,特别适用于复杂的自然语言处理任务。
|
||||
- **[Mistral](https://lobechat.com/discover/provider/mistral)**: Mistral 提供先进的通用、专业和研究型模型,广泛应用于复杂推理、多语言任务、代码生成等领域,通过功能调用接口,用户可以集成自定义功能,实现特定应用。
|
||||
- **[ModelScope](https://lobechat.com/discover/provider/modelscope)**: ModelScope 是阿里云推出的模型即服务平台,提供丰富的 AI 模型和推理服务。
|
||||
- **[Ai21Labs](https://lobechat.com/discover/provider/ai21)**: AI21 Labs 为企业构建基础模型和人工智能系统,加速生成性人工智能在生产中的应用。
|
||||
- **[Upstage](https://lobechat.com/discover/provider/upstage)**: Upstage 专注于为各种商业需求开发 AI 模型,包括 Solar LLM 和文档 AI,旨在实现工作的人造通用智能(AGI)。通过 Chat API 创建简单的对话代理,并支持功能调用、翻译、嵌入以及特定领域应用。
|
||||
- **[xAI](https://lobechat.com/discover/provider/xai)**: xAI 是一家致力于构建人工智能以加速人类科学发现的公司。我们的使命是推动我们对宇宙的共同理解。
|
||||
- **[Qwen](https://lobechat.com/discover/provider/qwen)**: 通义千问是阿里云自主研发的超大规模语言模型,具有强大的自然语言理解和生成能力。它可以回答各种问题、创作文字内容、表达观点看法、撰写代码等,在多个领域发挥作用。
|
||||
- **[xAI (Grok)](https://lobechat.com/discover/provider/xai)**: xAI 是一家致力于构建人工智能以加速人类科学发现的公司。我们的使命是推动我们对宇宙的共同理解。
|
||||
- **[Aliyun Bailian](https://lobechat.com/discover/provider/qwen)**: 通义千问是阿里云自主研发的超大规模语言模型,具有强大的自然语言理解和生成能力。它可以回答各种问题、创作文字内容、表达观点看法、撰写代码等,在多个领域发挥作用。
|
||||
- **[Wenxin](https://lobechat.com/discover/provider/wenxin)**: 企业级一站式大模型与 AI 原生应用开发及服务平台,提供最全面易用的生成式人工智能模型开发、应用开发全流程工具链
|
||||
- **[Hunyuan](https://lobechat.com/discover/provider/hunyuan)**: 由腾讯研发的大语言模型,具备强大的中文创作能力,复杂语境下的逻辑推理能力,以及可靠的任务执行能力
|
||||
- **[ZhiPu](https://lobechat.com/discover/provider/zhipu)**: 智谱 AI 提供多模态与语言模型的开放平台,支持广泛的 AI 应用场景,包括文本处理、图像理解与编程辅助等。
|
||||
@@ -246,7 +300,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
||||
|
||||
[![][image-feat-local]][docs-feat-local]
|
||||
|
||||
### `6` [支持本地大语言模型 (LLM)][docs-feat-local]
|
||||
### [支持本地大语言模型 (LLM)][docs-feat-local]
|
||||
|
||||
为了满足特定用户的需求,LobeChat 还基于 [Ollama](https://ollama.ai) 支持了本地模型的使用,让用户能够更灵活地使用自己的或第三方的模型。
|
||||
|
||||
@@ -262,7 +316,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
|
||||
|
||||
[![][image-feat-vision]][docs-feat-vision]
|
||||
|
||||
### `7` [模型视觉识别 (Model Visual)][docs-feat-vision]
|
||||
### [模型视觉识别 (Model Visual)][docs-feat-vision]
|
||||
|
||||
LobeChat 已经支持 OpenAI 最新的 [`gpt-4-vision`](https://platform.openai.com/docs/guides/vision) 支持视觉识别的模型,这是一个具备视觉识别能力的多模态应用。
|
||||
用户可以轻松上传图片或者拖拽图片到对话框中,助手将能够识别图片内容,并在此基础上进行智能对话,构建更智能、更多元化的聊天场景。
|
||||
@@ -277,7 +331,7 @@ LobeChat 已经支持 OpenAI 最新的 [`gpt-4-vision`](https://platform.openai.
|
||||
|
||||
[![][image-feat-tts]][docs-feat-tts]
|
||||
|
||||
### `8` [TTS & STT 语音会话][docs-feat-tts]
|
||||
### [TTS & STT 语音会话][docs-feat-tts]
|
||||
|
||||
LobeChat 支持文字转语音(Text-to-Speech,TTS)和语音转文字(Speech-to-Text,STT)技术,这使得我们的应用能够将文本信息转化为清晰的语音输出,用户可以像与真人交谈一样与我们的对话助手进行交流。
|
||||
用户可以从多种声音中选择,给助手搭配合适的音源。 同时,对于那些倾向于听觉学习或者想要在忙碌中获取信息的用户来说,TTS 提供了一个极佳的解决方案。
|
||||
@@ -292,7 +346,7 @@ LobeChat 支持文字转语音(Text-to-Speech,TTS)和语音转文字(Spe
|
||||
|
||||
[![][image-feat-t2i]][docs-feat-t2i]
|
||||
|
||||
### `9` [Text to Image 文生图][docs-feat-t2i]
|
||||
### [Text to Image 文生图][docs-feat-t2i]
|
||||
|
||||
支持最新的文本到图片生成技术,LobeChat 现在能够让用户在与助手对话中直接调用文生图工具进行创作。
|
||||
通过利用 [`DALL-E 3`](https://openai.com/dall-e-3)、[`MidJourney`](https://www.midjourney.com/) 和 [`Pollinations`](https://pollinations.ai/) 等 AI 工具的能力, 助手们现在可以将你的想法转化为图像。
|
||||
@@ -306,7 +360,7 @@ LobeChat 支持文字转语音(Text-to-Speech,TTS)和语音转文字(Spe
|
||||
|
||||
[![][image-feat-plugin]][docs-feat-plugin]
|
||||
|
||||
### `10` [插件系统 (Tools Calling)][docs-feat-plugin]
|
||||
### [插件系统 (Tools Calling)][docs-feat-plugin]
|
||||
|
||||
LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地增强了 ChatGPT 的实用性和灵活性。
|
||||
|
||||
@@ -322,12 +376,12 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
| 最近新增 | 描述 |
|
||||
| -------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-03-23**</sup> | 分析股票并获取全面的实时投资数据和分析。<br/>`股票` |
|
||||
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2025-05-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/>`网络` `搜索` |
|
||||
|
||||
> 📊 Total plugins: [<kbd>**43**</kbd>](https://lobechat.com/discover/plugins)
|
||||
> 📊 Total plugins: [<kbd>**42**</kbd>](https://lobechat.com/discover/plugins)
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
@@ -339,7 +393,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
[![][image-feat-agent]][docs-feat-agent]
|
||||
|
||||
### `11` [助手市场 (GPTs)][docs-feat-agent]
|
||||
### [助手市场 (GPTs)][docs-feat-agent]
|
||||
|
||||
在 LobeChat 的助手市场中,创作者们可以发现一个充满活力和创新的社区,它汇聚了众多精心设计的助手,这些助手不仅在工作场景中发挥着重要作用,也在学习过程中提供了极大的便利。
|
||||
我们的市场不仅是一个展示平台,更是一个协作的空间。在这里,每个人都可以贡献自己的智慧,分享个人开发的助手。
|
||||
@@ -355,14 +409,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
<!-- AGENT LIST -->
|
||||
|
||||
| 最近新增 | 描述 |
|
||||
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| [学术论文综述专家](https://lobechat.com/discover/assistant/academic-paper-overview)<br/><sup>By **[arvinxx](https://github.com/arvinxx)** on **2025-03-11**</sup> | 擅长高质量文献检索与分析的学术研究助手<br/>`学术研究` `文献检索` `数据分析` `信息提取` `咨询` |
|
||||
| [Cron 表达式助手](https://lobechat.com/discover/assistant/crontab-generate)<br/><sup>By **[edgesider](https://github.com/edgesider)** on **2025-02-17**</sup> | Crontab 表达式生成<br/>`crontab` `时间表达` `触发时间` `生成器` `技术辅助` |
|
||||
| [小智法语翻译助手](https://lobechat.com/discover/assistant/xiao-zhi-french-translation-asst-v-1)<br/><sup>By **[WeR-Best](https://github.com/WeR-Best)** on **2025-02-10**</sup> | 友好、专业、富有同理心的法语翻译 AI 助手<br/>`ai助手` `法语翻译` `跨文化交流` `创造力` |
|
||||
| [投资小助手](https://lobechat.com/discover/assistant/graham-investmentassi)<br/><sup>By **[farsightlin](https://github.com/farsightlin)** on **2025-02-06**</sup> | 帮助用户计算估值所需的一些数据<br/>`投资` `估值` `财务分析` `计算器` |
|
||||
| 最近新增 | 描述 |
|
||||
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| [海龟汤主持人](https://lobechat.com/discover/assistant/lateral-thinking-puzzle)<br/><sup>By **[CSY2022](https://github.com/CSY2022)** on **2025-06-19**</sup> | 一个海龟汤主持人,需要自己提供汤面,汤底与关键点(猜中的判定条件)。<br/>`海龟汤` `推理` `互动` `谜题` `角色扮演` |
|
||||
| [美食评论员🍟](https://lobechat.com/discover/assistant/food-reviewer)<br/><sup>By **[renhai-lab](https://github.com/renhai-lab)** on **2025-06-17**</sup> | 美食评价专家<br/>`美食` `评价` `写作` |
|
||||
| [学术写作助手](https://lobechat.com/discover/assistant/academic-writing-assistant)<br/><sup>By **[swarfte](https://github.com/swarfte)** on **2025-06-17**</sup> | 专业的学术研究论文写作和正式文档编写专家<br/>`学术写作` `研究` `正式风格` |
|
||||
| [Minecraft 资深开发者](https://lobechat.com/discover/assistant/java-development)<br/><sup>By **[iamyuuk](https://github.com/iamyuuk)** on **2025-06-17**</sup> | 擅长高级 Java 开发及 Minecraft 开发<br/>`开发` `编程` `minecraft` `java` |
|
||||
|
||||
> 📊 Total agents: [<kbd>**488**</kbd> ](https://lobechat.com/discover/assistants)
|
||||
> 📊 Total agents: [<kbd>**505**</kbd> ](https://lobechat.com/discover/assistants)
|
||||
|
||||
<!-- AGENT LIST -->
|
||||
|
||||
@@ -374,7 +428,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
[![][image-feat-database]][docs-feat-database]
|
||||
|
||||
### `12` [支持本地 / 远程数据库][docs-feat-database]
|
||||
### [支持本地 / 远程数据库][docs-feat-database]
|
||||
|
||||
LobeChat 支持同时使用服务端数据库和本地数据库。根据您的需求,您可以选择合适的部署方案:
|
||||
|
||||
@@ -391,7 +445,7 @@ LobeChat 支持同时使用服务端数据库和本地数据库。根据您的
|
||||
|
||||
[![][image-feat-auth]][docs-feat-auth]
|
||||
|
||||
### `13` [支持多用户管理][docs-feat-auth]
|
||||
### [支持多用户管理][docs-feat-auth]
|
||||
|
||||
LobeChat 支持多用户管理,提供了两种主要的用户认证和管理方案,以满足不同需求:
|
||||
|
||||
@@ -409,7 +463,7 @@ LobeChat 支持多用户管理,提供了两种主要的用户认证和管理
|
||||
|
||||
[![][image-feat-pwa]][docs-feat-pwa]
|
||||
|
||||
### `14` [渐进式 Web 应用 (PWA)][docs-feat-pwa]
|
||||
### [渐进式 Web 应用 (PWA)][docs-feat-pwa]
|
||||
|
||||
我们深知在当今多设备环境下为用户提供无缝体验的重要性。为此,我们采用了渐进式 Web 应用 [PWA](https://support.google.com/chrome/answer/9658361) 技术,
|
||||
这是一种能够将网页应用提升至接近原生应用体验的现代 Web 技术。通过 PWA,LobeChat 能够在桌面和移动设备上提供高度优化的用户体验,同时保持轻量级和高性能的特点。
|
||||
@@ -422,7 +476,6 @@ LobeChat 支持多用户管理,提供了两种主要的用户认证和管理
|
||||
> - 在电脑上运行 Chrome 或 Edge 浏览器 .
|
||||
> - 访问 LobeChat 网页 .
|
||||
> - 在地址栏的右上角,单击 <kbd>安装</kbd> 图标 .
|
||||
> - 根据屏幕上的指示完成 PWA 的安装 .
|
||||
|
||||
<div align="right">
|
||||
|
||||
@@ -432,7 +485,7 @@ LobeChat 支持多用户管理,提供了两种主要的用户认证和管理
|
||||
|
||||
[![][image-feat-mobile]][docs-feat-mobile]
|
||||
|
||||
### `15` [移动设备适配][docs-feat-mobile]
|
||||
### [移动设备适配][docs-feat-mobile]
|
||||
|
||||
针对移动设备进行了一系列的优化设计,以提升用户的移动体验。目前,我们正在对移动端的用户体验进行版本迭代,以实现更加流畅和直观的交互。如果您有任何建议或想法,我们非常欢迎您通过 GitHub Issues 或者 Pull Requests 提供反馈。
|
||||
|
||||
@@ -444,7 +497,7 @@ LobeChat 支持多用户管理,提供了两种主要的用户认证和管理
|
||||
|
||||
[![][image-feat-theme]][docs-feat-theme]
|
||||
|
||||
### `16` [自定义主题][docs-feat-theme]
|
||||
### [自定义主题][docs-feat-theme]
|
||||
|
||||
作为设计工程师出身,LobeChat 在界面设计上充分考虑用户的个性化体验,因此引入了灵活多变的主题模式,其中包括日间的亮色模式和夜间的深色模式。
|
||||
除了主题模式的切换,还提供了一系列的颜色定制选项,允许用户根据自己的喜好来调整应用的主题色彩。无论是想要沉稳的深蓝,还是希望活泼的桃粉,或者是专业的灰白,用户都能够在 LobeChat 中找到匹配自己风格的颜色选择。
|
||||
@@ -531,7 +584,7 @@ LobeChat 提供了 Vercel 的 自托管版本 和 [Docker 镜像][docker-release
|
||||
|
||||
#### 保持更新
|
||||
|
||||
如果你根据 README 中的一键部署步骤部署了自己的项目,你可能会发现总是被提示 “有可用更新”。这是因为 Vercel 默认为你创建新项目而非 fork 本项目,这将导致无法准确检测更新。
|
||||
如果你根据 README 中的一键部署步骤部署了自己的项目,你可能会发现总是被提示 "有可用更新"。这是因为 Vercel 默认为你创建新项目而非 fork 本项目,这将导致无法准确检测更新。
|
||||
|
||||
> \[!TIP]
|
||||
>
|
||||
@@ -545,9 +598,9 @@ LobeChat 提供了 Vercel 的 自托管版本 和 [Docker 镜像][docker-release
|
||||
[![][docker-size-shield]][docker-size-link]
|
||||
[![][docker-pulls-shield]][docker-pulls-link]
|
||||
|
||||
We provide a Docker image for deploying the LobeChat service on your own private device. Use the following command to start the LobeChat service:
|
||||
我们提供了一个用于在您自己的私有设备上部署 LobeChat 服务的 Docker 镜像。请使用以下命令启动 LobeChat 服务:
|
||||
|
||||
1. create a folder to for storage files
|
||||
1. 创建一个用于存储文件的文件夹
|
||||
|
||||
```fish
|
||||
$ mkdir lobe-chat-db && cd lobe-chat-db
|
||||
@@ -880,8 +933,10 @@ This project is [Apache 2.0](./LICENSE) licensed.
|
||||
[image-feat-branch]: https://github.com/user-attachments/assets/92f72082-02bd-4835-9c54-b089aad7fd41
|
||||
[image-feat-cot]: https://github.com/user-attachments/assets/f74f1139-d115-4e9c-8c43-040a53797a5e
|
||||
[image-feat-database]: https://github.com/user-attachments/assets/f1697c8b-d1fb-4dac-ba05-153c6295d91d
|
||||
[image-feat-desktop]: https://github.com/user-attachments/assets/a7bac8d3-ea96-4000-bb39-fadc9b610f96
|
||||
[image-feat-knowledgebase]: https://github.com/user-attachments/assets/7da7a3b2-92fd-4630-9f4e-8560c74955ae
|
||||
[image-feat-local]: https://github.com/user-attachments/assets/1239da50-d832-4632-a7ef-bd754c0f3850
|
||||
[image-feat-mcp-market]: https://github.com/user-attachments/assets/bb114f9f-24c5-4000-a984-c10d187da5a0
|
||||
[image-feat-mobile]: https://github.com/user-attachments/assets/32cf43c4-96bd-4a4c-bfb6-59acde6fe380
|
||||
[image-feat-plugin]: https://github.com/user-attachments/assets/66a891ac-01b6-4e3f-b978-2eb07b489b1b
|
||||
[image-feat-privoder]: https://github.com/user-attachments/assets/e553e407-42de-4919-977d-7dbfcf44a821
|
||||
@@ -890,6 +945,7 @@ This project is [Apache 2.0](./LICENSE) licensed.
|
||||
[image-feat-theme]: https://github.com/user-attachments/assets/b47c39f1-806f-492b-8fcb-b0fa973937c1
|
||||
[image-feat-tts]: https://github.com/user-attachments/assets/50189597-2cc3-4002-b4c8-756a52ad5c0a
|
||||
[image-feat-vision]: https://github.com/user-attachments/assets/18574a1f-46c2-4cbc-af2c-35a86e128a07
|
||||
[image-feat-web-search]: https://github.com/user-attachments/assets/cfdc48ac-b5f8-4a00-acee-db8f2eba09ad
|
||||
[image-overview]: https://github.com/user-attachments/assets/dbfaa84a-2c82-4dd9-815c-5be616f264a4
|
||||
[image-star]: https://github.com/user-attachments/assets/c3b482e7-cef5-4e94-bef9-226900ecfaab
|
||||
[issues-link]: https://img.shields.io/github/issues/lobehub/lobe-chat.svg?style=flat
|
||||
|
||||
@@ -2,3 +2,4 @@ lockfile=false
|
||||
shamefully-hoist=true
|
||||
electron_mirror=https://npmmirror.com/mirrors/electron/
|
||||
electron_builder_binaries_mirror=https://npmmirror.com/mirrors/electron-builder-binaries/
|
||||
ignore-workspace-root-check=true
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
# Claude Code Integration
|
||||
|
||||
This document describes the Claude Code SDK integration in LobeChat Desktop application.
|
||||
|
||||
## Overview
|
||||
|
||||
Claude Code SDK enables running Claude Code as a subprocess, providing AI-powered coding assistance capabilities. The integration supports:
|
||||
|
||||
- **Multi-turn conversations** with context retention
|
||||
- **File operations** (read/write)
|
||||
- **Code execution** through bash commands
|
||||
- **Session management** and continuation
|
||||
- **Real-time streaming** responses
|
||||
- **Cost tracking** per session
|
||||
|
||||
## Accessing Claude Code
|
||||
|
||||
In the LobeChat Desktop application, you can access Claude Code through:
|
||||
|
||||
1. **Sidebar Navigation**: Click the code icon (`</>`) in the sidebar (desktop only)
|
||||
2. **Direct URL**: Navigate to `/claude-code` in the application
|
||||
|
||||
The Claude Code interface provides:
|
||||
- A code editor for writing prompts
|
||||
- Real-time streaming message display
|
||||
- Session management with history
|
||||
- Cost tracking and usage statistics
|
||||
|
||||
## Architecture
|
||||
|
||||
### Components
|
||||
|
||||
1. **IPC Layer** (`packages/electron-client-ipc`)
|
||||
- Type definitions for Claude Code events
|
||||
- IPC event interfaces for main/render communication
|
||||
|
||||
2. **Main Process Controller** (`apps/desktop/src/main/controllers/ClaudeCodeCtr.ts`)
|
||||
- Handles Claude Code SDK integration
|
||||
- Manages streaming sessions and abort controllers
|
||||
- Tracks session history
|
||||
|
||||
3. **React Hook** (`src/hooks/useClaudeCode.ts`)
|
||||
- Provides easy-to-use interface for React components
|
||||
- Handles IPC communication with main process
|
||||
- Manages streaming state and events
|
||||
|
||||
4. **UI Page** (`src/app/[variants]/(main)/claude-code/`)
|
||||
- User interface for interacting with Claude Code
|
||||
- Query editor with syntax highlighting
|
||||
- Session management interface
|
||||
- Real-time message streaming display
|
||||
|
||||
## Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Install Claude Code SDK dependency:
|
||||
```bash
|
||||
npm install @anthropic-ai/claude-code
|
||||
```
|
||||
|
||||
2. Set up authentication:
|
||||
```bash
|
||||
# Option 1: Anthropic API Key
|
||||
export ANTHROPIC_API_KEY="your-api-key"
|
||||
|
||||
# Option 2: Amazon Bedrock
|
||||
export CLAUDE_CODE_USE_BEDROCK=1
|
||||
# Configure AWS credentials
|
||||
|
||||
# Option 3: Google Vertex AI
|
||||
export CLAUDE_CODE_USE_VERTEX=1
|
||||
# Configure Google Cloud credentials
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Basic Query
|
||||
|
||||
```typescript
|
||||
import { useClaudeCode } from '@/hooks/useClaudeCode';
|
||||
|
||||
const MyComponent = () => {
|
||||
const { query, isLoading } = useClaudeCode();
|
||||
|
||||
const handleQuery = async () => {
|
||||
const result = await query('Write a function to calculate Fibonacci numbers', {
|
||||
maxTurns: 3,
|
||||
outputFormat: 'json',
|
||||
});
|
||||
|
||||
console.log(result.messages);
|
||||
console.log(result.sessionId);
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
### Streaming Query
|
||||
|
||||
```typescript
|
||||
const { startStreamingQuery, isLoading } = useClaudeCode({
|
||||
onStreamMessage: (message) => {
|
||||
console.log('New message:', message);
|
||||
},
|
||||
onStreamComplete: (sessionId) => {
|
||||
console.log('Stream completed:', sessionId);
|
||||
},
|
||||
onStreamError: (error) => {
|
||||
console.error('Stream error:', error);
|
||||
},
|
||||
});
|
||||
|
||||
const handleStream = async () => {
|
||||
await startStreamingQuery('Build a React component', {
|
||||
maxTurns: 5,
|
||||
outputFormat: 'stream-json',
|
||||
allowedTools: ['Read', 'Write', 'Bash'],
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### Session Management
|
||||
|
||||
```typescript
|
||||
const { recentSessions, fetchRecentSessions, clearSession } = useClaudeCode();
|
||||
|
||||
// Get recent sessions
|
||||
await fetchRecentSessions();
|
||||
|
||||
// Continue a previous session
|
||||
await startStreamingQuery('Continue', {
|
||||
resumeSessionId: session.sessionId,
|
||||
});
|
||||
|
||||
// Clear a session
|
||||
await clearSession(sessionId);
|
||||
```
|
||||
|
||||
## IPC Events
|
||||
|
||||
### Client Dispatch Events (Renderer → Main)
|
||||
|
||||
- `claudeCodeQuery` - Execute a Claude Code query
|
||||
- `claudeCodeStreamStart` - Start a streaming query
|
||||
- `claudeCodeStreamStop` - Stop an active stream
|
||||
- `claudeCodeCreateAbortController` - Create abort controller
|
||||
- `claudeCodeAbort` - Trigger abort
|
||||
- `claudeCodeGetRecentSessions` - Get session history
|
||||
- `claudeCodeClearSession` - Clear a specific session
|
||||
- `claudeCodeCheckAvailability` - Check if Claude Code is available
|
||||
|
||||
### Broadcast Events (Main → Renderer)
|
||||
|
||||
- `claudeCodeStreamMessage` - Stream message event
|
||||
- `claudeCodeStreamComplete` - Stream completion event
|
||||
- `claudeCodeStreamError` - Stream error event
|
||||
|
||||
## Configuration Options
|
||||
|
||||
```typescript
|
||||
interface ClaudeCodeOptions {
|
||||
maxTurns?: number; // Maximum conversation turns
|
||||
systemPrompt?: string; // Override system prompt
|
||||
appendSystemPrompt?: string; // Append to system prompt
|
||||
cwd?: string; // Working directory
|
||||
allowedTools?: string[] | string; // Allowed tools
|
||||
disallowedTools?: string[] | string; // Disallowed tools
|
||||
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
|
||||
outputFormat?: 'text' | 'json' | 'stream-json';
|
||||
inputFormat?: 'text' | 'stream-json';
|
||||
mcpConfig?: string; // MCP configuration file path
|
||||
permissionPromptTool?: string; // MCP tool for permissions
|
||||
verbose?: boolean; // Enable verbose logging
|
||||
continueLastSession?: boolean; // Continue last session
|
||||
resumeSessionId?: string; // Resume specific session
|
||||
}
|
||||
```
|
||||
|
||||
## Message Types
|
||||
|
||||
```typescript
|
||||
interface ClaudeCodeMessage {
|
||||
type: 'assistant' | 'user' | 'system' | 'result';
|
||||
message?: any;
|
||||
session_id?: string;
|
||||
subtype?: string;
|
||||
duration_ms?: number;
|
||||
duration_api_ms?: number;
|
||||
is_error?: boolean;
|
||||
num_turns?: number;
|
||||
result?: string;
|
||||
total_cost_usd?: number;
|
||||
apiKeySource?: string;
|
||||
cwd?: string;
|
||||
tools?: string[];
|
||||
mcp_servers?: Array<{ name: string; status: string }>;
|
||||
model?: string;
|
||||
permissionMode?: 'default' | 'acceptEdits' | 'bypassPermissions' | 'plan';
|
||||
}
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always check availability** before using Claude Code
|
||||
2. **Handle errors gracefully** - both sync and async errors
|
||||
3. **Use abort controllers** for long-running operations
|
||||
4. **Monitor costs** through session tracking
|
||||
5. **Clean up sessions** when no longer needed
|
||||
6. **Set appropriate tool permissions** based on use case
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Claude Code not available
|
||||
|
||||
1. Check if running in Electron desktop app
|
||||
2. Verify API key is set correctly
|
||||
3. Check environment variables
|
||||
|
||||
### Streaming not working
|
||||
|
||||
1. Ensure proper event listeners are set up
|
||||
2. Check for abort controller conflicts
|
||||
3. Verify stream ID is unique
|
||||
|
||||
### Session continuation fails
|
||||
|
||||
1. Check if session ID is valid
|
||||
2. Ensure session hasn't been cleared
|
||||
3. Verify prompt is appropriate for continuation
|
||||
@@ -24,7 +24,7 @@
|
||||
"lint": "eslint --cache ",
|
||||
"pg-server": "bun run scripts/pglite-server.ts",
|
||||
"start": "electron-vite preview",
|
||||
"typecheck": "tsc --noEmit -p tsconfig.json"
|
||||
"typecheck": "tsgo --noEmit -p tsconfig.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"electron-updater": "^6.6.2",
|
||||
@@ -45,9 +45,10 @@
|
||||
"@types/resolve": "^1.20.6",
|
||||
"@types/semver": "^7.7.0",
|
||||
"@types/set-cookie-parser": "^2.4.10",
|
||||
"@typescript/native-preview": "latest",
|
||||
"consola": "^3.1.0",
|
||||
"cookie": "^1.0.2",
|
||||
"electron": "^36.2.0",
|
||||
"electron": "^37.2.0",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-is": "^3.0.0",
|
||||
"electron-log": "^5.3.3",
|
||||
|
||||
@@ -13,7 +13,7 @@ export const appBrowsers = {
|
||||
identifier: 'chat',
|
||||
keepAlive: true,
|
||||
minWidth: 400,
|
||||
path: '/chat',
|
||||
path: '/claude-code',
|
||||
showOnInit: true,
|
||||
titleBarStyle: 'hidden',
|
||||
vibrancy: 'under-window',
|
||||
@@ -26,6 +26,7 @@ export const appBrowsers = {
|
||||
identifier: 'devtools',
|
||||
maximizable: false,
|
||||
minWidth: 400,
|
||||
parentIdentifier: 'chat',
|
||||
path: '/desktop/devtools',
|
||||
titleBarStyle: 'hiddenInset',
|
||||
vibrancy: 'under-window',
|
||||
@@ -37,6 +38,7 @@ export const appBrowsers = {
|
||||
identifier: 'settings',
|
||||
keepAlive: true,
|
||||
minWidth: 600,
|
||||
parentIdentifier: 'chat',
|
||||
path: '/settings',
|
||||
titleBarStyle: 'hidden',
|
||||
vibrancy: 'under-window',
|
||||
|
||||
@@ -217,12 +217,15 @@ export default class AuthCtr extends ControllerModule {
|
||||
} else {
|
||||
// Handle protocol callback via second-instance event on Windows and Linux
|
||||
logger.debug('Registering second-instance event handler for Windows/Linux');
|
||||
app.on('second-instance', (event, commandLine) => {
|
||||
app.on('second-instance', async (event, commandLine) => {
|
||||
// Find the URL from command line arguments
|
||||
const url = commandLine.find((arg) => arg.startsWith(`${protocolPrefix}://`));
|
||||
if (url) {
|
||||
logger.info(`Found URL from second-instance command line arguments: ${url}`);
|
||||
this.handleAuthCallback(url);
|
||||
const { success } = await this.handleAuthCallback(url);
|
||||
if (success) {
|
||||
this.app.browserManager.getMainWindow().show();
|
||||
}
|
||||
} else {
|
||||
logger.warn('Protocol URL not found in second-instance command line arguments');
|
||||
}
|
||||
|
||||
@@ -0,0 +1,361 @@
|
||||
import {
|
||||
ClaudeCodeMessage,
|
||||
ClaudeCodeOptions,
|
||||
ClaudeCodeQueryParams,
|
||||
ClaudeCodeQueryResult,
|
||||
ClaudeCodeSessionInfo,
|
||||
ClaudeCodeStreamingParams,
|
||||
} from '@lobechat/electron-client-ipc';
|
||||
import { app } from 'electron';
|
||||
import { join } from 'node:path';
|
||||
|
||||
import { createClaudeCodeModule } from '@/modules/claudeCode';
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import { ControllerModule, ipcClientEvent } from './index';
|
||||
|
||||
const logger = createLogger('controllers:ClaudeCodeCtr');
|
||||
|
||||
interface StreamingSession {
|
||||
abortController: AbortController;
|
||||
sessionId?: string;
|
||||
streamId: string;
|
||||
}
|
||||
|
||||
export default class ClaudeCodeCtr extends ControllerModule {
|
||||
private claudeCodeModule = createClaudeCodeModule({
|
||||
debugMode: Boolean(process.env.DEBUG),
|
||||
});
|
||||
private streamingSessions = new Map<string, StreamingSession>();
|
||||
private abortControllers = new Map<string, AbortController>();
|
||||
private sessionHistory = new Map<string, ClaudeCodeSessionInfo>();
|
||||
|
||||
/**
|
||||
* 检查 Claude Code 是否可用
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeCheckAvailability')
|
||||
async checkAvailability(): Promise<{
|
||||
apiKeySource?: string;
|
||||
available: boolean;
|
||||
error?: string;
|
||||
version?: string;
|
||||
}> {
|
||||
try {
|
||||
return await this.claudeCodeModule.checkAvailability();
|
||||
} catch (error) {
|
||||
logger.error('Error checking Claude Code availability:', error);
|
||||
return {
|
||||
available: false,
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 Claude Code 查询
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeQuery')
|
||||
async executeQuery(params: ClaudeCodeQueryParams): Promise<ClaudeCodeQueryResult> {
|
||||
try {
|
||||
logger.info('Executing Claude Code query:', params.prompt);
|
||||
|
||||
const abortController = params.abortSignal
|
||||
? this.abortControllers.get(params.abortSignal)
|
||||
: new AbortController();
|
||||
|
||||
const messages: ClaudeCodeMessage[] = [];
|
||||
let sessionId: string | undefined;
|
||||
|
||||
const queryParams = {
|
||||
abortController,
|
||||
options: this.buildOptions(params.options),
|
||||
prompt: params.prompt,
|
||||
};
|
||||
|
||||
for await (const message of this.claudeCodeModule.query(queryParams)) {
|
||||
messages.push(message);
|
||||
|
||||
if (message.session_id) {
|
||||
sessionId = message.session_id;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新会话历史
|
||||
if (sessionId) {
|
||||
this.updateSessionHistory(sessionId, messages);
|
||||
}
|
||||
|
||||
return {
|
||||
messages,
|
||||
sessionId: sessionId || '',
|
||||
success: true,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error executing Claude Code query:', error);
|
||||
return {
|
||||
error: error.message,
|
||||
messages: [],
|
||||
sessionId: '',
|
||||
success: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始流式查询
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeStreamStart')
|
||||
async startStreamingQuery(
|
||||
params: ClaudeCodeStreamingParams,
|
||||
): Promise<{ error?: string; success: boolean }> {
|
||||
try {
|
||||
logger.info('Starting streaming Claude Code query:', params.streamId);
|
||||
|
||||
const abortController = params.abortSignal
|
||||
? this.abortControllers.get(params.abortSignal)
|
||||
: new AbortController();
|
||||
|
||||
const session: StreamingSession = {
|
||||
abortController,
|
||||
streamId: params.streamId,
|
||||
};
|
||||
|
||||
this.streamingSessions.set(params.streamId, session);
|
||||
|
||||
// 在后台执行流式查询
|
||||
this.executeStreamingQuery(params, abortController);
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error starting streaming query:', error);
|
||||
return { error: error.message, success: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止流式查询
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeStreamStop')
|
||||
async stopStreamingQuery(streamId: string): Promise<{ success: boolean }> {
|
||||
try {
|
||||
logger.info('Stopping streaming query:', streamId);
|
||||
|
||||
const session = this.streamingSessions.get(streamId);
|
||||
if (session) {
|
||||
session.abortController.abort();
|
||||
this.streamingSessions.delete(streamId);
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error stopping streaming query:', error);
|
||||
return { success: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 AbortController
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeCreateAbortController')
|
||||
createAbortController(): { signalId: string } {
|
||||
const signalId = `abort-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
||||
const abortController = new AbortController();
|
||||
this.abortControllers.set(signalId, abortController);
|
||||
|
||||
logger.debug('Created AbortController:', signalId);
|
||||
|
||||
// 清理过期的 AbortController(30分钟后)
|
||||
setTimeout(
|
||||
() => {
|
||||
this.abortControllers.delete(signalId);
|
||||
},
|
||||
30 * 60 * 1000,
|
||||
);
|
||||
|
||||
return { signalId };
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发 abort
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeAbort')
|
||||
abort(signalId: string): { success: boolean } {
|
||||
try {
|
||||
const abortController = this.abortControllers.get(signalId);
|
||||
if (abortController) {
|
||||
abortController.abort();
|
||||
this.abortControllers.delete(signalId);
|
||||
logger.debug('Aborted signal:', signalId);
|
||||
return { success: true };
|
||||
}
|
||||
return { success: false };
|
||||
} catch (error) {
|
||||
logger.error('Error aborting:', error);
|
||||
return { success: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近的会话列表
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeGetRecentSessions')
|
||||
getRecentSessions(): ClaudeCodeSessionInfo[] {
|
||||
const sessions = Array.from(this.sessionHistory.values());
|
||||
// 按最后活跃时间排序
|
||||
sessions.sort((a, b) => b.lastActiveAt - a.lastActiveAt);
|
||||
// 返回最近 20 个会话
|
||||
return sessions.slice(0, 20);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除指定会话
|
||||
*/
|
||||
@ipcClientEvent('claudeCodeClearSession')
|
||||
clearSession(sessionId: string): { success: boolean } {
|
||||
try {
|
||||
this.sessionHistory.delete(sessionId);
|
||||
logger.debug('Cleared session:', sessionId);
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
logger.error('Error clearing session:', error);
|
||||
return { success: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行流式查询(后台)
|
||||
*/
|
||||
private async executeStreamingQuery(
|
||||
params: ClaudeCodeStreamingParams,
|
||||
abortController: AbortController,
|
||||
) {
|
||||
try {
|
||||
const { streamId } = params;
|
||||
let sessionId: string | undefined;
|
||||
let messageCount = 0;
|
||||
|
||||
logger.debug('Starting streaming query execution for stream:', streamId);
|
||||
|
||||
const queryParams = {
|
||||
abortController,
|
||||
options: this.buildOptions(params.options),
|
||||
prompt: params.prompt,
|
||||
};
|
||||
|
||||
try {
|
||||
for await (const message of this.claudeCodeModule.query(queryParams)) {
|
||||
messageCount++;
|
||||
|
||||
logger.debug(`Stream ${streamId} - Message ${messageCount}:`, message.type);
|
||||
logger.debug('output message:', message);
|
||||
|
||||
// 广播消息到渲染进程
|
||||
this.app.browserManager.broadcastToAllWindows('claudeCodeStreamMessage', {
|
||||
message,
|
||||
streamId,
|
||||
});
|
||||
|
||||
if (message.session_id) {
|
||||
sessionId = message.session_id;
|
||||
const session = this.streamingSessions.get(streamId);
|
||||
if (session) {
|
||||
session.sessionId = sessionId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug(`Stream ${streamId} completed with ${messageCount} messages`);
|
||||
} catch (queryError) {
|
||||
logger.error('Error in Claude Code query:', queryError);
|
||||
throw queryError;
|
||||
}
|
||||
|
||||
// 更新会话历史
|
||||
if (sessionId) {
|
||||
// 这里我们不存储所有消息,只更新会话信息
|
||||
const existingSession = this.sessionHistory.get(sessionId);
|
||||
if (existingSession) {
|
||||
existingSession.lastActiveAt = Date.now();
|
||||
existingSession.turnCount++;
|
||||
} else {
|
||||
this.sessionHistory.set(sessionId, {
|
||||
createdAt: Date.now(),
|
||||
lastActiveAt: Date.now(),
|
||||
sessionId,
|
||||
turnCount: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 广播完成事件
|
||||
this.app.browserManager.broadcastToAllWindows('claudeCodeStreamComplete', {
|
||||
sessionId: sessionId || '',
|
||||
streamId,
|
||||
});
|
||||
|
||||
logger.debug('Stream completed successfully:', streamId);
|
||||
|
||||
// 清理
|
||||
this.streamingSessions.delete(streamId);
|
||||
} catch (error) {
|
||||
logger.error('Error in streaming query:', error);
|
||||
|
||||
// 广播错误事件
|
||||
this.app.browserManager.broadcastToAllWindows('claudeCodeStreamError', {
|
||||
error: error.message,
|
||||
streamId: params.streamId,
|
||||
});
|
||||
|
||||
// 清理
|
||||
this.streamingSessions.delete(params.streamId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建选项对象
|
||||
*/
|
||||
private buildOptions(options?: ClaudeCodeOptions): ClaudeCodeOptions {
|
||||
const defaultOptions: ClaudeCodeOptions = {
|
||||
maxTurns: 5,
|
||||
outputFormat: 'stream-json',
|
||||
};
|
||||
|
||||
if (!options) {
|
||||
return defaultOptions;
|
||||
}
|
||||
|
||||
// 处理选项
|
||||
const processedOptions: ClaudeCodeOptions = { ...defaultOptions, ...options };
|
||||
|
||||
// 如果提供了 mcpConfig 路径,确保它是绝对路径
|
||||
if (options.mcpConfig && !join(options.mcpConfig).startsWith('/')) {
|
||||
processedOptions.mcpConfig = join(app.getPath('userData'), options.mcpConfig);
|
||||
}
|
||||
|
||||
return processedOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新会话历史
|
||||
*/
|
||||
private updateSessionHistory(sessionId: string, messages: ClaudeCodeMessage[]) {
|
||||
const resultMessage = messages.find((m) => m.type === 'result');
|
||||
|
||||
const existingSession = this.sessionHistory.get(sessionId);
|
||||
if (existingSession) {
|
||||
existingSession.lastActiveAt = Date.now();
|
||||
existingSession.turnCount++;
|
||||
if (resultMessage?.total_cost_usd) {
|
||||
existingSession.totalCost = (existingSession.totalCost || 0) + resultMessage.total_cost_usd;
|
||||
}
|
||||
} else {
|
||||
this.sessionHistory.set(sessionId, {
|
||||
createdAt: Date.now(),
|
||||
lastActiveAt: Date.now(),
|
||||
sessionId,
|
||||
totalCost: resultMessage?.total_cost_usd,
|
||||
turnCount: 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,27 @@
|
||||
import { ElectronAppState } from '@lobechat/electron-client-ipc';
|
||||
import { app, shell, systemPreferences } from 'electron';
|
||||
import { ElectronAppState, ThemeMode } from '@lobechat/electron-client-ipc';
|
||||
import { app, nativeTheme, shell, systemPreferences } from 'electron';
|
||||
import { macOS } from 'electron-is';
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import process from 'node:process';
|
||||
|
||||
import { DB_SCHEMA_HASH_FILENAME, LOCAL_DATABASE_DIR, userDataDir } from '@/const/dir';
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import { ControllerModule, ipcClientEvent, ipcServerEvent } from './index';
|
||||
|
||||
const logger = createLogger('controllers:SystemCtr');
|
||||
|
||||
export default class SystemController extends ControllerModule {
|
||||
private systemThemeListenerInitialized = false;
|
||||
|
||||
/**
|
||||
* Initialize system theme listener when app is ready
|
||||
*/
|
||||
afterAppReady() {
|
||||
this.initializeSystemThemeListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the 'getDesktopAppState' IPC request.
|
||||
* Gathers essential application and system information.
|
||||
@@ -26,6 +38,7 @@ export default class SystemController extends ControllerModule {
|
||||
isMac: platform === 'darwin',
|
||||
isWindows: platform === 'win32',
|
||||
platform: platform as 'darwin' | 'win32' | 'linux',
|
||||
systemAppearance: nativeTheme.shouldUseDarkColors ? 'dark' : 'light',
|
||||
userPath: {
|
||||
// User Paths (ensure keys match UserPathData / DesktopAppState interface)
|
||||
desktop: app.getPath('desktop'),
|
||||
@@ -68,6 +81,11 @@ export default class SystemController extends ControllerModule {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@ipcClientEvent('updateThemeMode')
|
||||
async updateThemeModeHandler(themeMode: ThemeMode) {
|
||||
this.app.browserManager.broadcastToAllWindows('themeChanged', { themeMode });
|
||||
}
|
||||
|
||||
@ipcServerEvent('getDatabasePath')
|
||||
async getDatabasePath() {
|
||||
return join(this.app.appStoragePath, LOCAL_DATABASE_DIR);
|
||||
@@ -95,4 +113,37 @@ export default class SystemController extends ControllerModule {
|
||||
private get DB_SCHEMA_HASH_PATH() {
|
||||
return join(this.app.appStoragePath, DB_SCHEMA_HASH_FILENAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize system theme listener to monitor OS theme changes
|
||||
*/
|
||||
private initializeSystemThemeListener() {
|
||||
if (this.systemThemeListenerInitialized) {
|
||||
logger.debug('System theme listener already initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('Initializing system theme listener');
|
||||
|
||||
// Get initial system theme
|
||||
const initialDarkMode = nativeTheme.shouldUseDarkColors;
|
||||
const initialSystemTheme: ThemeMode = initialDarkMode ? 'dark' : 'light';
|
||||
logger.info(`Initial system theme: ${initialSystemTheme}`);
|
||||
|
||||
// Listen for system theme changes
|
||||
nativeTheme.on('updated', () => {
|
||||
const isDarkMode = nativeTheme.shouldUseDarkColors;
|
||||
const systemTheme: ThemeMode = isDarkMode ? 'dark' : 'light';
|
||||
|
||||
logger.info(`System theme changed to: ${systemTheme}`);
|
||||
|
||||
// Broadcast system theme change to all renderer processes
|
||||
this.app.browserManager.broadcastToAllWindows('systemThemeChanged', {
|
||||
themeMode: systemTheme,
|
||||
});
|
||||
});
|
||||
|
||||
this.systemThemeListenerInitialized = true;
|
||||
logger.info('System theme listener initialized successfully');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,6 +186,10 @@ export class App {
|
||||
}
|
||||
});
|
||||
|
||||
// refs: https://github.com/lobehub/lobe-chat/pull/7883
|
||||
// https://github.com/electron/electron/issues/46538#issuecomment-2808806722
|
||||
app.commandLine.appendSwitch('gtk-version', '3');
|
||||
|
||||
app.commandLine.appendSwitch('enable-features', this.chromeFlags.join(','));
|
||||
|
||||
logger.debug('Waiting for app to be ready');
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { MainBroadcastEventKey, MainBroadcastParams } from '@lobechat/electron-client-ipc';
|
||||
import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain, nativeTheme } from 'electron';
|
||||
import {
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
ipcMain,
|
||||
nativeTheme,
|
||||
screen,
|
||||
} from 'electron';
|
||||
import os from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
@@ -19,6 +25,7 @@ export interface BrowserWindowOpts extends BrowserWindowConstructorOptions {
|
||||
*/
|
||||
identifier: string;
|
||||
keepAlive?: boolean;
|
||||
parentIdentifier?: string;
|
||||
path: string;
|
||||
showOnInit?: boolean;
|
||||
title?: string;
|
||||
@@ -145,9 +152,41 @@ export default class Browser {
|
||||
|
||||
show() {
|
||||
logger.debug(`Showing window: ${this.identifier}`);
|
||||
if (!this._browserWindow.isDestroyed()) this.determineWindowPosition();
|
||||
|
||||
this.browserWindow.show();
|
||||
}
|
||||
|
||||
private determineWindowPosition() {
|
||||
const { parentIdentifier } = this.options;
|
||||
|
||||
if (parentIdentifier) {
|
||||
// todo: fix ts type
|
||||
const parentWin = this.app.browserManager.retrieveByIdentifier(parentIdentifier as any);
|
||||
if (parentWin) {
|
||||
logger.debug(`[${this.identifier}] Found parent window: ${parentIdentifier}`);
|
||||
|
||||
const display = screen.getDisplayNearestPoint(parentWin.browserWindow.getContentBounds());
|
||||
if (display) {
|
||||
const {
|
||||
workArea: { x, y, width: displayWidth, height: displayHeight },
|
||||
} = display;
|
||||
|
||||
const { width, height } = this._browserWindow.getContentBounds();
|
||||
logger.debug(
|
||||
`[${this.identifier}] Display bounds: x=${x}, y=${y}, width=${displayWidth}, height=${displayHeight}`,
|
||||
);
|
||||
|
||||
// Calculate new position
|
||||
const newX = Math.floor(Math.max(x + (displayWidth - width) / 2, x));
|
||||
const newY = Math.floor(Math.max(y + (displayHeight - height) / 2, y));
|
||||
logger.debug(`[${this.identifier}] Calculated position: x=${newX}, y=${newY}`);
|
||||
this._browserWindow.setPosition(newX, newY, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
logger.debug(`Hiding window: ${this.identifier}`);
|
||||
this.browserWindow.hide();
|
||||
|
||||
@@ -0,0 +1,353 @@
|
||||
import { ClaudeCodeMessage } from '@lobechat/electron-client-ipc';
|
||||
import { type ChildProcess, spawn } from 'node:child_process';
|
||||
import { type Interface, createInterface } from 'node:readline';
|
||||
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import {
|
||||
ClaudeCodeImpl,
|
||||
ClaudeCodeProcessOptions,
|
||||
ClaudeCodeQueryParams,
|
||||
ClaudeCodeRuntimeConfig,
|
||||
} from './type';
|
||||
|
||||
const logger = createLogger('modules:claude-code');
|
||||
|
||||
/**
|
||||
* Claude Code Service Implementation
|
||||
*/
|
||||
export class ClaudeCodeServiceImpl extends ClaudeCodeImpl {
|
||||
private activeProcesses = new Map<string, ChildProcess>();
|
||||
private config: ClaudeCodeRuntimeConfig;
|
||||
|
||||
constructor(config: ClaudeCodeRuntimeConfig = {}) {
|
||||
super();
|
||||
this.config = {
|
||||
debugMode: config.debugMode ?? Boolean(process.env.DEBUG),
|
||||
maxMemoryUsage: config.maxMemoryUsage ?? 1024 * 1024 * 1024, // 1GB
|
||||
timeoutMs: config.timeoutMs ?? 30 * 60 * 1000, // 30 minutes
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute Claude Code query
|
||||
*/
|
||||
async *query(params: ClaudeCodeQueryParams): AsyncGenerator<ClaudeCodeMessage> {
|
||||
const processId = this.generateProcessId();
|
||||
let childProcess: ChildProcess | null = null;
|
||||
let readline: Interface | null = null;
|
||||
|
||||
// Set entrypoint environment variable
|
||||
if (!process.env.CLAUDE_CODE_ENTRYPOINT) {
|
||||
process.env.CLAUDE_CODE_ENTRYPOINT = 'sdk-ts';
|
||||
}
|
||||
|
||||
// Build process options
|
||||
const processOptions = this.buildProcessOptions(params);
|
||||
|
||||
// Spawn child process using claude command directly
|
||||
childProcess = spawn('claude', processOptions.args, {
|
||||
cwd: processOptions.cwd,
|
||||
env: { ...process.env, ...processOptions.env },
|
||||
signal: params.abortController?.signal,
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
// Register process
|
||||
this.activeProcesses.set(processId, childProcess);
|
||||
|
||||
// Handle process cleanup
|
||||
const cleanup = () => {
|
||||
if (childProcess && !childProcess.killed) {
|
||||
childProcess.kill('SIGTERM');
|
||||
}
|
||||
this.activeProcesses.delete(processId);
|
||||
};
|
||||
|
||||
// Setup abort handling
|
||||
params.abortController?.signal.addEventListener('abort', cleanup);
|
||||
process.on('exit', cleanup);
|
||||
|
||||
// Handle stdin
|
||||
if (typeof params.prompt === 'string') {
|
||||
childProcess.stdin?.end();
|
||||
} else {
|
||||
// Handle stream input if needed
|
||||
this.streamToStdin(params.prompt, childProcess.stdin, params.abortController);
|
||||
}
|
||||
|
||||
// Handle stderr in debug mode
|
||||
if (this.config.debugMode && childProcess.stderr) {
|
||||
childProcess.stderr.on('data', (data) => {
|
||||
logger.debug('Claude Code stderr:', data.toString());
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Handle process errors
|
||||
let processError: Error | null = null;
|
||||
childProcess.on('error', (error) => {
|
||||
processError = new Error(`Failed to spawn Claude Code process: ${error.message}`);
|
||||
});
|
||||
|
||||
// Create a promise to wait for process completion
|
||||
const processExitPromise = new Promise<void>((resolve, reject) => {
|
||||
childProcess!.on('close', (code) => {
|
||||
if (params.abortController?.signal.aborted) {
|
||||
reject(new Error('Claude Code process aborted by user'));
|
||||
} else if (code !== 0) {
|
||||
reject(new Error(`Claude Code process exited with code ${code}`));
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Create readline interface for stdout and yield messages
|
||||
if (childProcess.stdout) {
|
||||
readline = createInterface({ input: childProcess.stdout });
|
||||
|
||||
try {
|
||||
for await (const line of readline) {
|
||||
if (processError) {
|
||||
throw processError;
|
||||
}
|
||||
|
||||
if (line.trim()) {
|
||||
try {
|
||||
const message = JSON.parse(line);
|
||||
yield message;
|
||||
} catch (parseError) {
|
||||
logger.error('Failed to parse JSON line:', line, parseError);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
readline.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for process to complete
|
||||
await processExitPromise;
|
||||
} finally {
|
||||
// Cleanup
|
||||
if (readline) {
|
||||
readline.close();
|
||||
}
|
||||
cleanup();
|
||||
params.abortController?.signal.removeEventListener('abort', cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Claude Code availability
|
||||
*/
|
||||
async checkAvailability(): Promise<{
|
||||
apiKeySource?: string;
|
||||
available: boolean;
|
||||
error?: string;
|
||||
version?: string;
|
||||
}> {
|
||||
try {
|
||||
// Check environment variables
|
||||
const apiKey = process.env.ANTHROPIC_API_KEY;
|
||||
const useBedrock = process.env.CLAUDE_CODE_USE_BEDROCK === '1';
|
||||
const useVertex = process.env.CLAUDE_CODE_USE_VERTEX === '1';
|
||||
|
||||
if (!apiKey && !useBedrock && !useVertex) {
|
||||
return {
|
||||
available: false,
|
||||
error:
|
||||
'No API credentials found. Please set ANTHROPIC_API_KEY or configure third-party provider.',
|
||||
};
|
||||
}
|
||||
|
||||
let apiKeySource = 'unknown';
|
||||
if (apiKey) apiKeySource = 'anthropic';
|
||||
else if (useBedrock) apiKeySource = 'bedrock';
|
||||
else if (useVertex) apiKeySource = 'vertex';
|
||||
|
||||
// Check if claude command exists
|
||||
const claudeExists = await this.checkClaudeCommandExists();
|
||||
if (!claudeExists) {
|
||||
return {
|
||||
available: false,
|
||||
error: 'Claude CLI command not found. Please install Claude CLI first.',
|
||||
};
|
||||
}
|
||||
|
||||
// Get version
|
||||
const version = await this.getVersion();
|
||||
|
||||
return {
|
||||
apiKeySource,
|
||||
available: true,
|
||||
version,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error checking Claude Code availability:', error);
|
||||
return {
|
||||
available: false,
|
||||
error: error.message,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Claude Code executable path
|
||||
*/
|
||||
async getExecutablePath(): Promise<string> {
|
||||
// Return claude command directly
|
||||
return 'claude';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
cleanup(): void {
|
||||
// Kill all active processes
|
||||
for (const [processId, childProcess] of this.activeProcesses) {
|
||||
if (!childProcess.killed) {
|
||||
childProcess.kill('SIGTERM');
|
||||
}
|
||||
}
|
||||
this.activeProcesses.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if claude command exists
|
||||
*/
|
||||
private async checkClaudeCommandExists(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const testProcess = spawn('which', ['claude'], { stdio: 'pipe' });
|
||||
|
||||
testProcess.on('close', (code) => {
|
||||
resolve(code === 0);
|
||||
});
|
||||
|
||||
testProcess.on('error', () => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build process options from query parameters
|
||||
*/
|
||||
private buildProcessOptions(params: ClaudeCodeQueryParams): ClaudeCodeProcessOptions {
|
||||
const args = ['--output-format', 'stream-json'];
|
||||
|
||||
if (this.config.debugMode) {
|
||||
args.push('--verbose');
|
||||
}
|
||||
|
||||
const options = params.options || {};
|
||||
|
||||
// Add options to args
|
||||
if (options.systemPrompt) {
|
||||
args.push('--system-prompt', options.systemPrompt);
|
||||
}
|
||||
if (options.appendSystemPrompt) {
|
||||
args.push('--append-system-prompt', options.appendSystemPrompt);
|
||||
}
|
||||
if (options.maxTurns) {
|
||||
args.push('--max-turns', options.maxTurns.toString());
|
||||
}
|
||||
if (options.permissionPromptTool) {
|
||||
args.push('--permission-prompt-tool', options.permissionPromptTool);
|
||||
}
|
||||
if (options.continueLastSession) {
|
||||
args.push('--continue');
|
||||
}
|
||||
if (options.resumeSessionId) {
|
||||
args.push('--resume', options.resumeSessionId);
|
||||
}
|
||||
if (options.allowedTools) {
|
||||
const tools = Array.isArray(options.allowedTools)
|
||||
? options.allowedTools.join(',')
|
||||
: options.allowedTools;
|
||||
args.push('--allowedTools', tools);
|
||||
}
|
||||
if (options.disallowedTools) {
|
||||
const tools = Array.isArray(options.disallowedTools)
|
||||
? options.disallowedTools.join(',')
|
||||
: options.disallowedTools;
|
||||
args.push('--disallowedTools', tools);
|
||||
}
|
||||
if (options.mcpConfig) {
|
||||
args.push('--mcp-config', options.mcpConfig);
|
||||
}
|
||||
if (options.permissionMode && options.permissionMode !== 'default') {
|
||||
args.push('--permission-mode', options.permissionMode);
|
||||
}
|
||||
|
||||
// Add prompt
|
||||
if (typeof params.prompt === 'string') {
|
||||
args.push('--print', params.prompt.trim());
|
||||
} else {
|
||||
args.push('--input-format', 'stream-json');
|
||||
}
|
||||
|
||||
return {
|
||||
args,
|
||||
cwd: options.cwd,
|
||||
env: {},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique process ID
|
||||
*/
|
||||
private generateProcessId(): string {
|
||||
return `claude-code-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream input to stdin
|
||||
*/
|
||||
private async streamToStdin(
|
||||
stream: any,
|
||||
stdin: any,
|
||||
abortController?: AbortController,
|
||||
): Promise<void> {
|
||||
try {
|
||||
for await (const message of stream) {
|
||||
if (abortController?.signal.aborted) break;
|
||||
stdin.write(JSON.stringify(message) + '\n');
|
||||
}
|
||||
stdin.end();
|
||||
} catch (error) {
|
||||
logger.error('Error streaming to stdin:', error);
|
||||
stdin.end();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Claude Code version
|
||||
*/
|
||||
private async getVersion(): Promise<string> {
|
||||
return new Promise((resolve) => {
|
||||
const versionProcess = spawn('claude', ['--version'], { stdio: 'pipe' });
|
||||
|
||||
let output = '';
|
||||
versionProcess.stdout?.on('data', (data) => {
|
||||
output += data.toString();
|
||||
});
|
||||
|
||||
versionProcess.on('close', (code) => {
|
||||
if (code === 0) {
|
||||
// Extract version from output
|
||||
const versionMatch = output.match(/(\d+\.\d+\.\d+)/);
|
||||
resolve(versionMatch ? versionMatch[1] : 'unknown');
|
||||
} else {
|
||||
resolve('unknown');
|
||||
}
|
||||
});
|
||||
|
||||
versionProcess.on('error', () => {
|
||||
resolve('unknown');
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ClaudeCodeServiceImpl } from './impl';
|
||||
import { ClaudeCodeImpl, ClaudeCodeRuntimeConfig } from './type';
|
||||
|
||||
/**
|
||||
* Create Claude Code module instance
|
||||
*/
|
||||
export const createClaudeCodeModule = (config?: ClaudeCodeRuntimeConfig): ClaudeCodeImpl => {
|
||||
return new ClaudeCodeServiceImpl(config);
|
||||
};
|
||||
|
||||
// Export types and implementation
|
||||
export type {
|
||||
ClaudeCodeProcessOptions,
|
||||
ClaudeCodeProcessResult,
|
||||
ClaudeCodeQueryParams,
|
||||
ClaudeCodeRuntimeConfig,
|
||||
ClaudeCodeStreamingParams,
|
||||
} from './type';
|
||||
@@ -0,0 +1,74 @@
|
||||
import { ClaudeCodeMessage, ClaudeCodeOptions } from '@lobechat/electron-client-ipc';
|
||||
|
||||
/**
|
||||
* Claude Code Query Parameters
|
||||
*/
|
||||
export interface ClaudeCodeQueryParams {
|
||||
abortController?: AbortController;
|
||||
options?: ClaudeCodeOptions;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code Streaming Parameters
|
||||
*/
|
||||
export interface ClaudeCodeStreamingParams extends ClaudeCodeQueryParams {
|
||||
streamId: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code Service Implementation Abstract Class
|
||||
*/
|
||||
export abstract class ClaudeCodeImpl {
|
||||
/**
|
||||
* Execute Claude Code query
|
||||
* @param params Query parameters
|
||||
* @returns AsyncGenerator of ClaudeCodeMessage
|
||||
*/
|
||||
abstract query(params: ClaudeCodeQueryParams): AsyncGenerator<ClaudeCodeMessage>;
|
||||
|
||||
/**
|
||||
* Check Claude Code availability
|
||||
* @returns Promise with availability status
|
||||
*/
|
||||
abstract checkAvailability(): Promise<{
|
||||
apiKeySource?: string;
|
||||
available: boolean;
|
||||
error?: string;
|
||||
version?: string;
|
||||
}>;
|
||||
/**
|
||||
* Clean up resources
|
||||
*/
|
||||
abstract cleanup(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code Process Options
|
||||
*/
|
||||
export interface ClaudeCodeProcessOptions {
|
||||
args: string[];
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
executable?: string;
|
||||
executableArgs?: string[];
|
||||
pathToClaudeCodeExecutable?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code Process Result
|
||||
*/
|
||||
export interface ClaudeCodeProcessResult {
|
||||
exitCode: number;
|
||||
killed: boolean;
|
||||
signal?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Claude Code Runtime Configuration
|
||||
*/
|
||||
export interface ClaudeCodeRuntimeConfig {
|
||||
debugMode?: boolean;
|
||||
maxMemoryUsage?: number;
|
||||
timeoutMs?: number;
|
||||
}
|
||||
@@ -58,7 +58,7 @@ export const createRequest = async ({
|
||||
|
||||
for (const cookie of cookies) {
|
||||
const { name, value } = cookie;
|
||||
cookiesHeader.push(serializeCookie(name, value)); // ...(options as any)?
|
||||
cookiesHeader.push(serializeCookie(name, value));
|
||||
}
|
||||
|
||||
req.headers.cookie = cookiesHeader.join('; ');
|
||||
@@ -305,20 +305,27 @@ export function createHandler({
|
||||
);
|
||||
|
||||
for (const cookie of cookies) {
|
||||
const expires = cookie.expires
|
||||
? cookie.expires.getTime()
|
||||
: cookie.maxAge
|
||||
? Date.now() + cookie.maxAge * 1000
|
||||
: undefined;
|
||||
let expirationDate: number | undefined;
|
||||
|
||||
if (expires && expires < Date.now()) {
|
||||
if (cookie.expires) {
|
||||
// expires 是 Date 对象,转换为秒级时间戳
|
||||
expirationDate = Math.floor(cookie.expires.getTime() / 1000);
|
||||
} else if (cookie.maxAge) {
|
||||
// maxAge 是秒数,计算过期时间戳
|
||||
expirationDate = Math.floor(Date.now() / 1000) + cookie.maxAge;
|
||||
}
|
||||
|
||||
// 如果都没有,则为 session cookie,不设置 expirationDate
|
||||
|
||||
// 检查是否已过期
|
||||
if (expirationDate && expirationDate < Math.floor(Date.now() / 1000)) {
|
||||
await session.cookies.remove(request.url, cookie.name);
|
||||
continue;
|
||||
}
|
||||
|
||||
await session.cookies.set({
|
||||
domain: cookie.domain,
|
||||
expirationDate: expires,
|
||||
expirationDate,
|
||||
httpOnly: cookie.httpOnly,
|
||||
name: cookie.name,
|
||||
path: cookie.path,
|
||||
|
||||
@@ -1,4 +1,707 @@
|
||||
[
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Integrate Amazon Cognito for user authentication."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.9"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add google search grounding for Vertex AI."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Replace utility-types with type-fest."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Fix: solve the loading was strange spin when switch show."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add grok-4-0709 model from xAI, fix theme issue in desktop."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.4"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Implement data analytics event tracking framework."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix locale hydration error in SSR."]
|
||||
},
|
||||
"date": "2025-07-10",
|
||||
"version": "1.97.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add MCP marketplace and mcp plugin one-click installation in desktop."]
|
||||
},
|
||||
"date": "2025-07-08",
|
||||
"version": "1.97.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add MCP_TOOL_TIMEOUT env and improve debug usage guide."]
|
||||
},
|
||||
"date": "2025-07-08",
|
||||
"version": "1.96.20"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Pin officeparser@5.1.1 to fix server error."]
|
||||
},
|
||||
"date": "2025-07-07",
|
||||
"version": "1.96.19"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Change the wrong github checkmodel name."],
|
||||
"improvements": ["Files hello pages should scroll."]
|
||||
},
|
||||
"date": "2025-07-06",
|
||||
"version": "1.96.18"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-07-03",
|
||||
"version": "1.96.17"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-07-03",
|
||||
"version": "1.96.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Wrong Gemini 2.5 Pro thinkbudget."]
|
||||
},
|
||||
"date": "2025-07-02",
|
||||
"version": "1.96.15"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Migrate to @google/genai SDK for Google Gemini API and Vertex AI."]
|
||||
},
|
||||
"date": "2025-07-01",
|
||||
"version": "1.96.14"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-07-01",
|
||||
"version": "1.96.13"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Pin antd@5.26.2 to fix build error."],
|
||||
"improvements": ["Add DeepResearch models from OpenAI."]
|
||||
},
|
||||
"date": "2025-06-30",
|
||||
"version": "1.96.12"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-06-28",
|
||||
"version": "1.96.11"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix desktop chunk issue."]
|
||||
},
|
||||
"date": "2025-06-28",
|
||||
"version": "1.96.10"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Google Gemini tools declarations."]
|
||||
},
|
||||
"date": "2025-06-23",
|
||||
"version": "1.96.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Optimized Gemini thinkingBudget configuration."]
|
||||
},
|
||||
"date": "2025-06-23",
|
||||
"version": "1.96.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add blockAds & stealth params for Browserless."]
|
||||
},
|
||||
"date": "2025-06-23",
|
||||
"version": "1.96.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-06-23",
|
||||
"version": "1.96.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Remove unsupported parameters of Hunyuan."]
|
||||
},
|
||||
"date": "2025-06-22",
|
||||
"version": "1.96.5"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-06-22",
|
||||
"version": "1.96.4"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-06-22",
|
||||
"version": "1.96.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-06-22",
|
||||
"version": "1.96.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix MiniMax-M1 reasoning tag missing."]
|
||||
},
|
||||
"date": "2025-06-21",
|
||||
"version": "1.96.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add v0 (Vercel) provider support."],
|
||||
"fixes": ["Fix inputTemplate behavior."]
|
||||
},
|
||||
"date": "2025-06-20",
|
||||
"version": "1.96.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add Brave & Google PSE & Kagi as build-in Search Provider."]
|
||||
},
|
||||
"date": "2025-06-20",
|
||||
"version": "1.95.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-06-20",
|
||||
"version": "1.94.17"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Correctly pass reasoning.summary."]
|
||||
},
|
||||
"date": "2025-06-19",
|
||||
"version": "1.94.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update model card for Gemini 2.5 Pro via OpenRouter."]
|
||||
},
|
||||
"date": "2025-06-19",
|
||||
"version": "1.94.15"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update i18n."]
|
||||
},
|
||||
"date": "2025-06-19",
|
||||
"version": "1.94.14"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": [
|
||||
"Update Gemini 2.5 Pro, Flash GA models. Add Gemini 2.5 Flash-Lite Preview model."
|
||||
]
|
||||
},
|
||||
"date": "2025-06-18",
|
||||
"version": "1.94.13"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add MiniMax-M1 model."]
|
||||
},
|
||||
"date": "2025-06-18",
|
||||
"version": "1.94.12"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Enhance the multi-display window opening experience."]
|
||||
},
|
||||
"date": "2025-06-17",
|
||||
"version": "1.94.11"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Improve chat selectors and enhance topic handling logic."]
|
||||
},
|
||||
"date": "2025-06-15",
|
||||
"version": "1.94.10"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add kimi-thinking-preview model from Moonshot."]
|
||||
},
|
||||
"date": "2025-06-15",
|
||||
"version": "1.94.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Correctly handle reasoning_effort."]
|
||||
},
|
||||
"date": "2025-06-15",
|
||||
"version": "1.94.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add Doubao Seed 1.6 model."]
|
||||
},
|
||||
"date": "2025-06-12",
|
||||
"version": "1.94.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Abort the Gemini request correctly & Add openai o3-pro."]
|
||||
},
|
||||
"date": "2025-06-12",
|
||||
"version": "1.94.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support web_search_preview & fix some bug form OpenAI Response API."]
|
||||
},
|
||||
"date": "2025-06-12",
|
||||
"version": "1.94.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Transition animation switch."]
|
||||
},
|
||||
"date": "2025-06-11",
|
||||
"version": "1.94.4"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-06-11",
|
||||
"version": "1.94.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Update Gemini range of thinkingBudget."]
|
||||
},
|
||||
"date": "2025-06-11",
|
||||
"version": "1.94.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Update pplx abilities tags, support vision."]
|
||||
},
|
||||
"date": "2025-06-10",
|
||||
"version": "1.94.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support google sso as auth provider."],
|
||||
"fixes": ["Restore reasoningEffort in setting."]
|
||||
},
|
||||
"date": "2025-06-10",
|
||||
"version": "1.94.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor branding info."]
|
||||
},
|
||||
"date": "2025-06-10",
|
||||
"version": "1.93.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Refactor <think> & </think> handling."]
|
||||
},
|
||||
"date": "2025-06-09",
|
||||
"version": "1.93.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix openai default Responses API issue."]
|
||||
},
|
||||
"date": "2025-06-08",
|
||||
"version": "1.93.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support OpenAI Responses API mode."]
|
||||
},
|
||||
"date": "2025-06-08",
|
||||
"version": "1.93.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix client s3 getObject throw error."],
|
||||
"improvements": ["Support OpenRouter Claude 4 reasoning."]
|
||||
},
|
||||
"date": "2025-06-08",
|
||||
"version": "1.92.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add support to azureopenai embedding."]
|
||||
},
|
||||
"date": "2025-06-07",
|
||||
"version": "1.92.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve {{username}} placeholder variable, Update Gemini & Qwen models."]
|
||||
},
|
||||
"date": "2025-06-07",
|
||||
"version": "1.92.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support placeholder variables in prompts and input."],
|
||||
"fixes": ["Some web search bugs."],
|
||||
"improvements": ["Support Vertex AI thought summaries."]
|
||||
},
|
||||
"date": "2025-06-06",
|
||||
"version": "1.92.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Correct deepseek R1 fc support display."],
|
||||
"improvements": ["Add openAI websearch and claude 4 to modelproviders."]
|
||||
},
|
||||
"date": "2025-06-05",
|
||||
"version": "1.91.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add Volcengine & OpenAI-like Provider (e.g. oneapi) model fetch support."]
|
||||
},
|
||||
"date": "2025-06-05",
|
||||
"version": "1.91.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve loading state."]
|
||||
},
|
||||
"date": "2025-06-04",
|
||||
"version": "1.91.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add more provider support for search & crawl."],
|
||||
"improvements": ["Update modelscope models."]
|
||||
},
|
||||
"date": "2025-06-03",
|
||||
"version": "1.91.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Rename the createOpenAICompatibleRuntime."],
|
||||
"fixes": ["Update the clerk middleware to support route protection."]
|
||||
},
|
||||
"date": "2025-06-02",
|
||||
"version": "1.90.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Enable deploymentName for Aliyun Bailian."]
|
||||
},
|
||||
"date": "2025-06-01",
|
||||
"version": "1.90.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support web_search tool for MiniMax & Zhipu."]
|
||||
},
|
||||
"date": "2025-06-01",
|
||||
"version": "1.90.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": [
|
||||
"Disable LaTeX and Mermaid rendering in SystemRoleContent to prevent lag caused by massive rendering tasks when switching topics, fix DeepSeek new R1 Search error."
|
||||
],
|
||||
"improvements": [
|
||||
"Use default deployment name when parseModelString doesn't contain deployment name."
|
||||
]
|
||||
},
|
||||
"date": "2025-06-01",
|
||||
"version": "1.90.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support protect page."]
|
||||
},
|
||||
"date": "2025-06-01",
|
||||
"version": "1.90.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Support ModelScope Provider."]
|
||||
},
|
||||
"date": "2025-06-01",
|
||||
"version": "1.89.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["+ in the welcome message can be clicked to create an assistant."]
|
||||
},
|
||||
"date": "2025-05-31",
|
||||
"version": "1.88.23"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support 01.ai proxy url."]
|
||||
},
|
||||
"date": "2025-05-31",
|
||||
"version": "1.88.22"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": [
|
||||
"Enhanced reasoning_effort Slider Component, Update Hunyuan models & deepseek-r1-0528."
|
||||
]
|
||||
},
|
||||
"date": "2025-05-31",
|
||||
"version": "1.88.21"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Agent automatic completion meta not working error."]
|
||||
},
|
||||
"date": "2025-05-31",
|
||||
"version": "1.88.20"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support Web Search Tools and Beta Header from Anthropic."]
|
||||
},
|
||||
"date": "2025-05-30",
|
||||
"version": "1.88.19"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": [
|
||||
"Close historySummary correctly, Enable thinking output only for supported Gemini thinking models."
|
||||
]
|
||||
},
|
||||
"date": "2025-05-29",
|
||||
"version": "1.88.18"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Increase the history limit."]
|
||||
},
|
||||
"date": "2025-05-29",
|
||||
"version": "1.88.17"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Cmd + click chat tab not open new tab."],
|
||||
"improvements": ["Update GitHub models."]
|
||||
},
|
||||
"date": "2025-05-29",
|
||||
"version": "1.88.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add fc ability to deepseek-reasoner model."]
|
||||
},
|
||||
"date": "2025-05-29",
|
||||
"version": "1.88.15"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Cant invoke the application after OIDC authorization in Windows 11."]
|
||||
},
|
||||
"date": "2025-05-28",
|
||||
"version": "1.88.14"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-05-27",
|
||||
"version": "1.88.13"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support adjust thinkingBudget in gemini 2.5 flash."]
|
||||
},
|
||||
"date": "2025-05-27",
|
||||
"version": "1.88.12"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Correct model name SenseChat-5-1202."]
|
||||
},
|
||||
"date": "2025-05-26",
|
||||
"version": "1.88.11"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2025-05-26",
|
||||
"version": "1.88.10"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Auto sync theme mode in desktop."]
|
||||
},
|
||||
"date": "2025-05-26",
|
||||
"version": "1.88.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Allow SliderWithInput to have no input limit."]
|
||||
},
|
||||
"date": "2025-05-26",
|
||||
"version": "1.88.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix chat header in the desktop."]
|
||||
},
|
||||
"date": "2025-05-26",
|
||||
"version": "1.88.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix draggable issue with agent header."],
|
||||
"improvements": ["Fix a few typos in the model tooltips."]
|
||||
},
|
||||
"date": "2025-05-25",
|
||||
"version": "1.88.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support share single message."]
|
||||
},
|
||||
"date": "2025-05-25",
|
||||
"version": "1.88.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve thread flicker when first-time loading."]
|
||||
},
|
||||
"date": "2025-05-25",
|
||||
"version": "1.88.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix message refresh 401 on desktop."],
|
||||
"improvements": ["Add gemini & hunyuan & Claude models."]
|
||||
},
|
||||
"date": "2025-05-25",
|
||||
"version": "1.88.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Add live search support for xAI."]
|
||||
},
|
||||
"date": "2025-05-24",
|
||||
"version": "1.88.2"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["User nickName & username selector in desktop."],
|
||||
"improvements": ["Support Gemini 2.5 thought reasoning."]
|
||||
},
|
||||
"date": "2025-05-24",
|
||||
"version": "1.88.1"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"features": ["Add claude 4 series."],
|
||||
"fixes": [
|
||||
"Fix missing email field to user, update agent config of client db will override old config."
|
||||
]
|
||||
},
|
||||
"date": "2025-05-23",
|
||||
"version": "1.88.0"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Resolve InputNumber display overlap issue."]
|
||||
},
|
||||
"date": "2025-05-23",
|
||||
"version": "1.87.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["'top_p' is not supported with o4-mini, pin zustand version to avoid type error."]
|
||||
},
|
||||
"date": "2025-05-22",
|
||||
"version": "1.87.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Bump @lobehub/ui to 2.1.7."]
|
||||
},
|
||||
"date": "2025-05-21",
|
||||
"version": "1.87.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve tools display."]
|
||||
},
|
||||
"date": "2025-05-21",
|
||||
"version": "1.87.6"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix desktop open issue on linux like Fedora42."]
|
||||
},
|
||||
"date": "2025-05-20",
|
||||
"version": "1.87.5"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Supported SenseNova v6 models correctly & update Gemini models."]
|
||||
},
|
||||
"date": "2025-05-18",
|
||||
"version": "1.87.4"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Clean code with new antd api."]
|
||||
},
|
||||
"date": "2025-05-17",
|
||||
"version": "1.87.3"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Support Doubao 1.5 Thinking Vision Pro model."]
|
||||
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
|
||||
@@ -33,7 +33,7 @@ services:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
restart: always
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
ports:
|
||||
- '9000:9000'
|
||||
|
||||
@@ -18,7 +18,7 @@ services:
|
||||
restart: always
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
ports:
|
||||
- '9000:9000'
|
||||
|
||||
+16
-16
@@ -8,10 +8,10 @@
|
||||
# ref: https://github.com/lobehub/lobe-chat/pull/5247
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS
|
||||
SED_COMMAND="sed -i ''"
|
||||
SED_INPLACE_ARGS=('-i' '')
|
||||
else
|
||||
# not macOS
|
||||
SED_COMMAND="sed -i"
|
||||
SED_INPLACE_ARGS=('-i')
|
||||
fi
|
||||
|
||||
# ======================
|
||||
@@ -519,12 +519,12 @@ section_configurate_host() {
|
||||
if [[ "$ask_result" == "y" ]]; then
|
||||
PROTOCOL="https"
|
||||
# Replace all http with https
|
||||
$SED_COMMAND "s#http://#https://#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#http://#https://#" .env
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if sed is installed
|
||||
if ! command -v $SED_COMMAND &> /dev/null ; then
|
||||
if ! command -v sed "${SED_INPLACE_ARGS[@]}" &> /dev/null ; then
|
||||
echo "sed" $(show_message "tips_no_executable")
|
||||
exit 1
|
||||
fi
|
||||
@@ -553,7 +553,7 @@ section_configurate_host() {
|
||||
ask "(auth.example.com)"
|
||||
CASDOOR_HOST="$ask_result"
|
||||
# Setup callback url for Casdoor
|
||||
$SED_COMMAND "s/"example.com"/${LOBE_HOST}/" init_data.json
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s/"example.com"/${LOBE_HOST}/" init_data.json
|
||||
;;
|
||||
1)
|
||||
DEPLOY_MODE="ip"
|
||||
@@ -566,7 +566,7 @@ section_configurate_host() {
|
||||
MINIO_HOST="${HOST}:9000"
|
||||
CASDOOR_HOST="${HOST}:8000"
|
||||
# Setup callback url for Casdoor
|
||||
$SED_COMMAND "s/"localhost:3210"/${LOBE_HOST}/" init_data.json
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s/"localhost:3210"/${LOBE_HOST}/" init_data.json
|
||||
;;
|
||||
*)
|
||||
echo "Invalid deploy mode: $ask_result"
|
||||
@@ -575,14 +575,14 @@ section_configurate_host() {
|
||||
esac
|
||||
|
||||
# lobe host
|
||||
$SED_COMMAND "s#^APP_URL=.*#APP_URL=$PROTOCOL://$LOBE_HOST#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^APP_URL=.*#APP_URL=$PROTOCOL://$LOBE_HOST#" .env
|
||||
# auth related
|
||||
$SED_COMMAND "s#^AUTH_URL=.*#AUTH_URL=$PROTOCOL://$LOBE_HOST/api/auth#" .env
|
||||
$SED_COMMAND "s#^AUTH_CASDOOR_ISSUER=.*#AUTH_CASDOOR_ISSUER=$PROTOCOL://$CASDOOR_HOST#" .env
|
||||
$SED_COMMAND "s#^origin=.*#origin=$PROTOCOL://$CASDOOR_HOST#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^AUTH_URL=.*#AUTH_URL=$PROTOCOL://$LOBE_HOST/api/auth#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^AUTH_CASDOOR_ISSUER=.*#AUTH_CASDOOR_ISSUER=$PROTOCOL://$CASDOOR_HOST#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^origin=.*#origin=$PROTOCOL://$CASDOOR_HOST#" .env
|
||||
# s3 related
|
||||
$SED_COMMAND "s#^S3_PUBLIC_DOMAIN=.*#S3_PUBLIC_DOMAIN=$PROTOCOL://$MINIO_HOST#" .env
|
||||
$SED_COMMAND "s#^S3_ENDPOINT=.*#S3_ENDPOINT=$PROTOCOL://$MINIO_HOST#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^S3_PUBLIC_DOMAIN=.*#S3_PUBLIC_DOMAIN=$PROTOCOL://$MINIO_HOST#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^S3_ENDPOINT=.*#S3_ENDPOINT=$PROTOCOL://$MINIO_HOST#" .env
|
||||
|
||||
|
||||
# Check if env modified success
|
||||
@@ -641,12 +641,12 @@ section_regenerate_secrets() {
|
||||
echo $(show_message "security_secrect_regenerate_failed") "CASDOOR_SECRET"
|
||||
else
|
||||
# Search and replace the value of CASDOOR_SECRET in .env
|
||||
$SED_COMMAND "s#^AUTH_CASDOOR_SECRET=.*#AUTH_CASDOOR_SECRET=${CASDOOR_SECRET}#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^AUTH_CASDOOR_SECRET=.*#AUTH_CASDOOR_SECRET=${CASDOOR_SECRET}#" .env
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "AUTH_CASDOOR_SECRET in \`.env\`"
|
||||
fi
|
||||
# replace `clientSecrect` in init_data.json
|
||||
$SED_COMMAND "s#dbf205949d704de81b0b5b3603174e23fbecc354#${CASDOOR_SECRET}#" init_data.json
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#dbf205949d704de81b0b5b3603174e23fbecc354#${CASDOOR_SECRET}#" init_data.json
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "AUTH_CASDOOR_SECRET in \`init_data.json\`"
|
||||
fi
|
||||
@@ -660,7 +660,7 @@ section_regenerate_secrets() {
|
||||
CASDOOR_PASSWORD="123"
|
||||
else
|
||||
# replace `password` in init_data.json
|
||||
$SED_COMMAND "s/"123"/${CASDOOR_PASSWORD}/" init_data.json
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s/"123"/${CASDOOR_PASSWORD}/" init_data.json
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "CASDOOR_PASSWORD in \`init_data.json\`"
|
||||
fi
|
||||
@@ -672,7 +672,7 @@ section_regenerate_secrets() {
|
||||
MINIO_ROOT_PASSWORD="YOUR_MINIO_PASSWORD"
|
||||
else
|
||||
# Search and replace the value of S3_SECRET_ACCESS_KEY in .env
|
||||
$SED_COMMAND "s#^MINIO_ROOT_PASSWORD=.*#MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}#" .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^MINIO_ROOT_PASSWORD=.*#MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}#" .env
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "MINIO_ROOT_PASSWORD in \`.env\`"
|
||||
fi
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: LobeChat Plugin Ecosystem - Functionality Extensions and Development Resources
|
||||
description: >-
|
||||
Discover how the LobeChat plugin ecosystem enhances the utility and
|
||||
flexibility of the LobeChat assistant, along with the development resources
|
||||
and plugin development guidelines provided.
|
||||
Discover how the LobeChat plugin ecosystem enhances the utility and flexibility of the LobeChat assistant, along with the development resources and plugin development guidelines provided.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Plugins
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
---
|
||||
title: >-
|
||||
LobeChat Supports Multimodal Interaction: Visual Recognition Enhances
|
||||
Intelligent Dialogue
|
||||
LobeChat Supports Multimodal Interaction: Visual Recognition Enhances Intelligent Dialogue
|
||||
|
||||
description: >-
|
||||
LobeChat supports various large language models with visual recognition
|
||||
capabilities, allowing users to upload or drag and drop images. The assistant
|
||||
will recognize the content and engage in intelligent dialogue, creating a more
|
||||
intelligent and diverse chat environment.
|
||||
LobeChat supports various large language models with visual recognition capabilities, allowing users to upload or drag and drop images. The assistant will recognize the content and engage in intelligent dialogue, creating a more intelligent and diverse chat environment.
|
||||
|
||||
tags:
|
||||
- Visual Recognition
|
||||
- LobeChat
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: LobeChat Text-to-Image Generation Technology
|
||||
description: >-
|
||||
LobeChat supports Text-to-Speech (TTS) and Speech-to-Text (STT) technologies,
|
||||
offering high-quality voice options for a personalized communication
|
||||
experience. Learn more about Lobe TTS Toolkit.
|
||||
LobeChat supports Text-to-Speech (TTS) and Speech-to-Text (STT) technologies, offering high-quality voice options for a personalized communication experience. Learn more about Lobe TTS Toolkit.
|
||||
|
||||
tags:
|
||||
- TTS
|
||||
- STT
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
---
|
||||
title: 'LobeChat Text-to-Image: Text-to-Image Generation Technology'
|
||||
description: >-
|
||||
LobeChat now supports the latest text-to-image generation technology, allowing
|
||||
users to directly invoke the text-to-image tool during conversations with the
|
||||
assistant for creative purposes. By utilizing AI tools such as DALL-E 3,
|
||||
MidJourney, and Pollinations, assistants can turn your ideas into images,
|
||||
making the creative process more intimate and immersive.
|
||||
LobeChat now supports the latest text-to-image generation technology, allowing users to directly invoke the text-to-image tool during conversations with the assistant for creative purposes. By utilizing AI tools such as DALL-E 3, MidJourney, and Pollinations, assistants can turn your ideas into images, making the creative process more intimate and immersive.
|
||||
|
||||
tags:
|
||||
- Text-to-Image
|
||||
- LobeChat
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat 文生图:文本转图片生成技术
|
||||
description: >-
|
||||
LobeChat 现在支持最新的文本到图片生成技术,让用户可以在与助手对话中直接调用文生图工具进行创作。利用 DALL-E 3、MidJourney 和
|
||||
Pollinations 等 AI 工具,助手们可以将你的想法转化为图像,让创作过程更私密和沉浸式。
|
||||
LobeChat 现在支持最新的文本到图片生成技术,让用户可以在与助手对话中直接调用文生图工具进行创作。利用 DALL-E 3、MidJourney 和 Pollinations 等 AI 工具,助手们可以将你的想法转化为图像,让创作过程更私密和沉浸式。
|
||||
|
||||
tags:
|
||||
- Text to Image
|
||||
- 文生图
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat Supports Multi-User Management with Clerk and Next-Auth
|
||||
description: >-
|
||||
LobeChat offers various user authentication and management solutions,
|
||||
including Clerk and Next-Auth, to meet the diverse needs of different users.
|
||||
LobeChat offers various user authentication and management solutions, including Clerk and Next-Auth, to meet the diverse needs of different users.
|
||||
|
||||
tags:
|
||||
- User Management
|
||||
- Next-Auth
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: 'LobeChat 1.0: New Architecture and New Possibilities'
|
||||
description: >-
|
||||
LobeChat 1.0 brings a brand-new architecture and features for server-side
|
||||
databases and user authentication management, opening up new possibilities. On
|
||||
this basis, LobeChat Cloud has entered beta testing.
|
||||
LobeChat 1.0 brings a brand-new architecture and features for server-side databases and user authentication management, opening up new possibilities. On this basis, LobeChat Cloud has entered beta testing.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Version 1.0
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat 1.0:新的架构与新的可能
|
||||
description: >-
|
||||
LobeChat 1.0 带来了服务端数据库、用户鉴权管理的全新架构与特性,开启了新的可能 。在此基础上, LobeChat Cloud 开启 Beta
|
||||
版测试。
|
||||
LobeChat 1.0 带来了服务端数据库、用户鉴权管理的全新架构与特性,开启了新的可能 。在此基础上, LobeChat Cloud 开启 Beta 版测试。
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- 服务端数据库
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: 'LobeChat Fully Enters the GPT-4 Era: GPT-4o Mini Officially Launched'
|
||||
description: >-
|
||||
LobeChat v1.6 has been released with support for GPT-4o mini, while LobeChat
|
||||
Cloud services have been fully upgraded to provide users with a more powerful
|
||||
AI conversation experience.
|
||||
LobeChat v1.6 has been released with support for GPT-4o mini, while LobeChat Cloud services have been fully upgraded to provide users with a more powerful AI conversation experience.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- GPT-4o Mini
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat 全面进入 GPT-4 时代:GPT-4o mini 正式上线
|
||||
description: >-
|
||||
LobeChat v1.6 重磅发布 GPT-4o mini 支持,同时 LobeChat Cloud 服务全面升级默认模型,为用户带来更强大的 AI
|
||||
对话体验。
|
||||
LobeChat v1.6 重磅发布 GPT-4o mini 支持,同时 LobeChat Cloud 服务全面升级默认模型,为用户带来更强大的 AI 对话体验。
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- GPT-4o mini
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: LobeChat Database Docker Image Official Release
|
||||
description: >-
|
||||
LobeChat v1.8.0 launches the official database Docker image, supporting cloud
|
||||
data synchronization and user management, along with comprehensive
|
||||
self-deployment documentation.
|
||||
LobeChat v1.8.0 launches the official database Docker image, supporting cloud data synchronization and user management, along with comprehensive self-deployment documentation.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Docker Image
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
---
|
||||
title: >-
|
||||
LobeChat Launches Knowledge Base Feature: A New Experience in Intelligent File
|
||||
Management and Dialogue
|
||||
LobeChat Launches Knowledge Base Feature: A New Experience in Intelligent File Management and Dialogue
|
||||
|
||||
description: >-
|
||||
LobeChat introduces a brand new knowledge base feature that supports all types
|
||||
of file management, intelligent vectorization, and file dialogue, making
|
||||
knowledge management and information retrieval easier and smarter.
|
||||
LobeChat introduces a brand new knowledge base feature that supports all types of file management, intelligent vectorization, and file dialogue, making knowledge management and information retrieval easier and smarter.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Knowledge Base
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat Perfectly Adapts to OpenAI O1 Series Models
|
||||
description: >-
|
||||
LobeChat v1.17.0 now supports OpenAI's latest o1-preview and o1-mini models,
|
||||
bringing users enhanced coding and mathematical capabilities.
|
||||
LobeChat v1.17.0 now supports OpenAI's latest o1-preview and o1-mini models, bringing users enhanced coding and mathematical capabilities.
|
||||
|
||||
tags:
|
||||
- OpenAI O1
|
||||
- LobeChat
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: 'Major Update: LobeChat Enters the Era of Artifacts'
|
||||
description: >-
|
||||
LobeChat v1.19 brings significant updates, including full feature support for
|
||||
Claude Artifacts, a brand new discovery page design, and support for GitHub
|
||||
Models providers, greatly enhancing the capabilities of the AI assistant.
|
||||
LobeChat v1.19 brings significant updates, including full feature support for Claude Artifacts, a brand new discovery page design, and support for GitHub Models providers, greatly enhancing the capabilities of the AI assistant.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- AI Assistant
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: 重磅更新:LobeChat 迎来 Artifacts 时代
|
||||
description: >-
|
||||
LobeChat v1.19 带来了重大更新,包括 Claude Artifacts 完整特性支持、全新的发现页面设计,以及 GitHub Models
|
||||
服务商支持,让 AI 助手的能力得到显著提升。
|
||||
LobeChat v1.19 带来了重大更新,包括 Claude Artifacts 完整特性支持、全新的发现页面设计,以及 GitHub Models 服务商支持,让 AI 助手的能力得到显著提升。
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- Artifacts
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: LobeChat Introduces Persistent Assistant Sidebar Feature
|
||||
description: >-
|
||||
LobeChat v1.26.0 launches the persistent assistant sidebar feature, supporting
|
||||
quick key switching for easy access to frequently used assistants,
|
||||
significantly enhancing efficiency.
|
||||
LobeChat v1.26.0 launches the persistent assistant sidebar feature, supporting quick key switching for easy access to frequently used assistants, significantly enhancing efficiency.
|
||||
|
||||
tags:
|
||||
- Persistent Assistant
|
||||
- Sidebar Feature
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
---
|
||||
title: LobeChat Supports Sharing Conversations in Text Format (Markdown/JSON)
|
||||
description: >-
|
||||
LobeChat v1.28.0 introduces support for exporting conversations in Markdown
|
||||
and OpenAI format JSON, making it easy to convert conversation content into
|
||||
note materials, development debugging data, and training corpora,
|
||||
significantly enhancing the reusability of conversation content.
|
||||
LobeChat v1.28.0 introduces support for exporting conversations in Markdown and OpenAI format JSON, making it easy to convert conversation content into note materials, development debugging data, and training corpora, significantly enhancing the reusability of conversation content.
|
||||
|
||||
tags:
|
||||
- Text Format Export
|
||||
- Markdown Export
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat 支持分享对话为文本格式(Markdown/JSON)
|
||||
description: >-
|
||||
LobeChat v1.28.0 新增 Markdown 和 OpenAI 格式 JSON
|
||||
导出支持,让对话内容能轻松转化为笔记素材、开发调试数据和训练语料,显著提升对话内容的复用价值。
|
||||
LobeChat v1.28.0 新增 Markdown 和 OpenAI 格式 JSON 导出支持,让对话内容能轻松转化为笔记素材、开发调试数据和训练语料,显著提升对话内容的复用价值。
|
||||
|
||||
tags:
|
||||
- 对话内容
|
||||
- Markdown导出
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: New Model Providers Added to LobeChat in November
|
||||
description: >-
|
||||
LobeChat model providers now support Gitee AI, InternLM (ShuSheng PuYu), xAI,
|
||||
and Cloudflare WorkersAI
|
||||
LobeChat model providers now support Gitee AI, InternLM (ShuSheng PuYu), xAI, and Cloudflare WorkersAI
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- AI Model Providers
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat Supports Branching Conversations
|
||||
description: >-
|
||||
LobeChat now allows you to create new conversation branches from any message,
|
||||
freeing your thoughts.
|
||||
LobeChat now allows you to create new conversation branches from any message, freeing your thoughts.
|
||||
|
||||
tags:
|
||||
- Branching Conversations
|
||||
- LobeChat
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat Supports User Data Statistics and Activity Sharing
|
||||
description: >-
|
||||
LobeChat now supports multi-dimensional user data statistics and activity
|
||||
sharing
|
||||
LobeChat now supports multi-dimensional user data statistics and activity sharing
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- User Statistics
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: LobeChat Launches New AI Provider Management System
|
||||
description: >-
|
||||
LobeChat has revamped its AI Provider Management System, now supporting custom
|
||||
AI providers and models.
|
||||
LobeChat has revamped its AI Provider Management System, now supporting custom AI providers and models.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- AI Provider
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---
|
||||
title: >-
|
||||
LobeChat Integrates DeepSeek R1, Bringing a Revolutionary Chain of Thought
|
||||
Experience
|
||||
LobeChat Integrates DeepSeek R1, Bringing a Revolutionary Chain of Thought Experience
|
||||
|
||||
description: >-
|
||||
LobeChat v1.49.12 fully supports the DeepSeek R1 model, providing users with
|
||||
an unprecedented interactive experience in the chain of thought.
|
||||
LobeChat v1.49.12 fully supports the DeepSeek R1 model, providing users with an unprecedented interactive experience in the chain of thought.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- DeepSeek
|
||||
|
||||
@@ -120,8 +120,7 @@ authorize --> jwt --> session
|
||||
|
||||
#### 自定义 `session` 返回
|
||||
|
||||
如果您想在 `session` 中携带更多关于 `profile` 及 `account` 的信息,根据上面提到的 `Auth.js` 数据处理顺序,那必须先将该信息复制到 `token` 上。
|
||||
示例:把用户头像 URL:`profile.picture` 添加到`session` 中:
|
||||
如果您想在 `session` 中携带更多关于 `profile` 及 `account` 的信息,根据上面提到的 `Auth.js` 数据处理顺序,那必须先将该信息复制到 `token` 上。示例:把用户头像 URL:`profile.picture` 添加到`session` 中:
|
||||
|
||||
```diff
|
||||
callbacks: {
|
||||
|
||||
@@ -233,8 +233,7 @@ AgentRuntime is a core abstraction layer in Lobe Chat that encapsulates a unifie
|
||||
|
||||
**Adapter Implementation Examples**:
|
||||
|
||||
1. **OpenRouter Adapter**:
|
||||
OpenRouter is a unified API that allows access to AI models from multiple providers. Lobe Chat implements support for OpenRouter through an adapter:
|
||||
1. **OpenRouter Adapter**: OpenRouter is a unified API that allows access to AI models from multiple providers. Lobe Chat implements support for OpenRouter through an adapter:
|
||||
|
||||
```ts
|
||||
// OpenRouter adapter implementation
|
||||
@@ -272,8 +271,7 @@ AgentRuntime is a core abstraction layer in Lobe Chat that encapsulates a unifie
|
||||
}
|
||||
```
|
||||
|
||||
2. **Google Gemini Adapter**:
|
||||
Gemini is Google's large language model. Lobe Chat supports Gemini series models through a dedicated adapter:
|
||||
2. **Google Gemini Adapter**: Gemini is Google's large language model. Lobe Chat supports Gemini series models through a dedicated adapter:
|
||||
|
||||
```ts
|
||||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||||
|
||||
@@ -233,8 +233,7 @@ AgentRuntime 是 Lobe Chat 中的一个核心抽象层,它封装了与不同 A
|
||||
|
||||
**适配器实现示例**:
|
||||
|
||||
1. **OpenRouter 适配器**:
|
||||
OpenRouter 是一个统一 API,可以通过它访问多个模型提供商的 AI 模型。Lobe Chat 通过适配器实现对 OpenRouter 的支持:
|
||||
1. **OpenRouter 适配器**:OpenRouter 是一个统一 API,可以通过它访问多个模型提供商的 AI 模型。Lobe Chat 通过适配器实现对 OpenRouter 的支持:
|
||||
|
||||
```ts
|
||||
// OpenRouter 适配器实现
|
||||
@@ -272,8 +271,7 @@ AgentRuntime 是 Lobe Chat 中的一个核心抽象层,它封装了与不同 A
|
||||
}
|
||||
```
|
||||
|
||||
2. **Google Gemini 适配器**:
|
||||
Gemini 是 Google 的大语言模型,Lobe Chat 通过专门的适配器支持 Gemini 系列模型:
|
||||
2. **Google Gemini 适配器**:Gemini 是 Google 的大语言模型,Lobe Chat 通过专门的适配器支持 Gemini 系列模型:
|
||||
|
||||
```ts
|
||||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||||
|
||||
@@ -94,6 +94,7 @@ table ai_providers {
|
||||
key_vaults text
|
||||
source varchar(20)
|
||||
settings jsonb
|
||||
config jsonb
|
||||
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()`]
|
||||
@@ -621,6 +622,55 @@ table rag_eval_evaluation_records {
|
||||
updated_at "timestamp with time zone" [not null, default: `now()`]
|
||||
}
|
||||
|
||||
table rbac_permissions {
|
||||
id integer [pk, not null]
|
||||
code text [not null, unique]
|
||||
name text [not null]
|
||||
description text
|
||||
category text [not null]
|
||||
is_active boolean [not null, default: true]
|
||||
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()`]
|
||||
}
|
||||
|
||||
table rbac_role_permissions {
|
||||
role_id integer [not null]
|
||||
permission_id integer [not null]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
|
||||
indexes {
|
||||
(role_id, permission_id) [pk]
|
||||
role_id [name: 'rbac_role_permissions_role_id_idx']
|
||||
permission_id [name: 'rbac_role_permissions_permission_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table rbac_roles {
|
||||
id integer [pk, not null]
|
||||
name text [not null, unique]
|
||||
display_name text [not null]
|
||||
description text
|
||||
is_system boolean [not null, default: false]
|
||||
is_active boolean [not null, default: true]
|
||||
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()`]
|
||||
}
|
||||
|
||||
table rbac_user_roles {
|
||||
user_id text [not null]
|
||||
role_id integer [not null]
|
||||
created_at "timestamp with time zone" [not null, default: `now()`]
|
||||
expires_at "timestamp with time zone"
|
||||
|
||||
indexes {
|
||||
(user_id, role_id) [pk]
|
||||
user_id [name: 'rbac_user_roles_user_id_idx']
|
||||
role_id [name: 'rbac_user_roles_role_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
table agents_to_sessions {
|
||||
agent_id text [not null]
|
||||
session_id text [not null]
|
||||
@@ -814,4 +864,4 @@ ref: topic_documents.document_id > documents.id
|
||||
|
||||
ref: topic_documents.topic_id > topics.id
|
||||
|
||||
ref: topics.session_id - sessions.id
|
||||
ref: topics.session_id - sessions.id
|
||||
@@ -19,16 +19,16 @@ In the directory structure of `src/locales`, the `default` folder contains the o
|
||||
src/locales
|
||||
├── create.ts
|
||||
├── default
|
||||
│ ├── chat.ts
|
||||
│ ├── common.ts
|
||||
│ ├── error.ts
|
||||
│ ├── index.ts
|
||||
│ ├── market.ts
|
||||
│ ├── migration.ts
|
||||
│ ├── plugin.ts
|
||||
│ ├── setting.ts
|
||||
│ ├── tool.ts
|
||||
│ └── welcome.ts
|
||||
│ ├── chat.ts
|
||||
│ ├── common.ts
|
||||
│ ├── error.ts
|
||||
│ ├── index.ts
|
||||
│ ├── market.ts
|
||||
│ ├── migration.ts
|
||||
│ ├── plugin.ts
|
||||
│ ├── setting.ts
|
||||
│ ├── tool.ts
|
||||
│ └── welcome.ts
|
||||
└── resources.ts
|
||||
```
|
||||
|
||||
|
||||
+10
-10
@@ -19,16 +19,16 @@
|
||||
src/locales
|
||||
├── create.ts
|
||||
├── default
|
||||
│ ├── chat.ts
|
||||
│ ├── common.ts
|
||||
│ ├── error.ts
|
||||
│ ├── index.ts
|
||||
│ ├── market.ts
|
||||
│ ├── migration.ts
|
||||
│ ├── plugin.ts
|
||||
│ ├── setting.ts
|
||||
│ ├── tool.ts
|
||||
│ └── welcome.ts
|
||||
│ ├── chat.ts
|
||||
│ ├── common.ts
|
||||
│ ├── error.ts
|
||||
│ ├── index.ts
|
||||
│ ├── market.ts
|
||||
│ ├── migration.ts
|
||||
│ ├── plugin.ts
|
||||
│ ├── setting.ts
|
||||
│ ├── tool.ts
|
||||
│ └── welcome.ts
|
||||
└── resources.ts
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Integrating Data Analytics Services in LobeChat for User Usage Analysis
|
||||
description: >-
|
||||
Learn how to integrate free/open-source data analytics services in LobeChat to
|
||||
collect user usage data efficiently.
|
||||
Learn how to integrate free/open-source data analytics services in LobeChat to collect user usage data efficiently.
|
||||
|
||||
tags:
|
||||
- LobeChat
|
||||
- data analytics
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: LobeChat Authentication Service Configuration
|
||||
description: >-
|
||||
Learn how to configure external authentication services using Clerk or Next
|
||||
Auth for centralized user authorization management. Supported authentication
|
||||
services include Auth0, Azure ID, etc.
|
||||
Learn how to configure external authentication services using Clerk or Next Auth for centralized user authorization management. Supported authentication services include Auth0, Azure ID, etc.
|
||||
|
||||
tags:
|
||||
- Authentication Service
|
||||
- Next Auth
|
||||
@@ -54,6 +53,8 @@ Currently supported identity verification services include:
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/logto'} title={'Logto'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/keycloak'} title={'Keycloak'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/google'} title={'Google'} />
|
||||
</Cards>
|
||||
|
||||
Click on the links to view the corresponding platform's configuration documentation.
|
||||
@@ -76,6 +77,7 @@ The order corresponds to the display order of the SSO providers.
|
||||
| Microsoft Entra ID | `microsoft-entra-id` |
|
||||
| ZITADEL | `zitadel` |
|
||||
| Keycloak | `keycloak` |
|
||||
| Google | `google` |
|
||||
|
||||
## Other SSO Providers
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Configure Clerk Authentication Service - Step-by-Step Guide
|
||||
description: >-
|
||||
Learn how to set up Clerk authentication with environment variables and
|
||||
webhooks.
|
||||
Learn how to set up Clerk authentication with environment variables and webhooks.
|
||||
|
||||
tags:
|
||||
- Clerk Authentication
|
||||
- Environment Variables
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configure Auth0 Identity Verification Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Auth0 Identity Verification Service for LobeChat for
|
||||
your organization, including creating applications, adding users, and
|
||||
configuring environment variables.
|
||||
Learn how to configure Auth0 Identity Verification Service for LobeChat for your organization, including creating applications, adding users, and configuring environment variables.
|
||||
|
||||
tags:
|
||||
- Auth0
|
||||
- Identity Verification
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Authelia Authentication Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Authelia authentication service in LobeChat, including
|
||||
creating a provider, configuring environment variables, and deploying
|
||||
LobeChat. Detailed steps and necessary environment variable settings.
|
||||
Learn how to configure Authelia authentication service in LobeChat, including creating a provider, configuring environment variables, and deploying LobeChat. Detailed steps and necessary environment variable settings.
|
||||
|
||||
tags:
|
||||
- Authelia Configuration
|
||||
- Single Sign-On (SSO)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Authentik Authentication Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Authentik for Single Sign-On (SSO) for LobeChat,
|
||||
including creating an application provider, setting environment variables, and
|
||||
deployment instructions.
|
||||
Learn how to configure Authentik for Single Sign-On (SSO) for LobeChat, including creating an application provider, setting environment variables, and deployment instructions.
|
||||
|
||||
tags:
|
||||
- Authentik Configuration
|
||||
- Single Sign-On (SSO)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Casdoor Authentication Service in LobeChat
|
||||
description: >-
|
||||
Learn how to configure the Casdoor authentication service in LobeChat,
|
||||
including deployment, creation, permission settings, and environment
|
||||
variables.
|
||||
Learn how to configure the Casdoor authentication service in LobeChat, including deployment, creation, permission settings, and environment variables.
|
||||
|
||||
tags:
|
||||
- Casdoor Authentication
|
||||
- Environment Variable Configuration
|
||||
@@ -88,9 +87,6 @@ If you are deploying using a public network, the following assumptions apply:
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
```
|
||||
|
||||
@@ -101,7 +97,8 @@ If you are deploying using a public network, the following assumptions apply:
|
||||
Go to `Identity` -> `Applications`, select the `LobeChat` application, and set `Allow Register` to `false`.
|
||||
|
||||
<Callout type={'warning'}>
|
||||
Disabling user registration is necessary to prevent users from registering through the Casdoor login page.
|
||||
Disabling user registration is necessary to prevent users from registering through the Casdoor
|
||||
login page.
|
||||
</Callout>
|
||||
|
||||
### Configure Webhook (Optional)
|
||||
|
||||
@@ -85,9 +85,6 @@ tags:
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
```
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Cloudflare Zero Trust Authentication Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Cloudflare Zero Trust for Single Sign-On (SSO) for
|
||||
LobeChat, including creating an application provider, setting environment
|
||||
variables, and deployment instructions.
|
||||
Learn how to configure Cloudflare Zero Trust for Single Sign-On (SSO) for LobeChat, including creating an application provider, setting environment variables, and deployment instructions.
|
||||
|
||||
tags:
|
||||
- Cloudflare Zero Trust
|
||||
- Single Sign-On (SSO)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: 在 LobeChat 中配置 Cloudflare Zero Trust 身份验证服务
|
||||
description: >-
|
||||
学习如何在 LobeChat 中配置 Cloudflare Zero Trust 身份验证服务,包括创建提供程序、配置环境变量和部署
|
||||
LobeChat。详细步骤和必要环境变量设置。
|
||||
学习如何在 LobeChat 中配置 Cloudflare Zero Trust 身份验证服务,包括创建提供程序、配置环境变量和部署 LobeChat。详细步骤和必要环境变量设置。
|
||||
|
||||
tags:
|
||||
- Cloudflare Zero Trust
|
||||
- 身份验证
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Github Authentication Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Github authentication service for LobeChat, including
|
||||
creating a Github provider, setting up environment variables, and deploying
|
||||
LobeChat.
|
||||
Learn how to configure Github authentication service for LobeChat, including creating a Github provider, setting up environment variables, and deploying LobeChat.
|
||||
|
||||
tags:
|
||||
- Github authentication
|
||||
- LobeChat
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
---
|
||||
|
||||
title: Configuration of Google SSO Authentication Service for LobeChat
|
||||
description: >-
|
||||
Learn how to configure Google SSO Authentication Service for LobeChat,
|
||||
create OAuth applications, add users, and set up environment variables for seamless integration.
|
||||
tags:
|
||||
|
||||
* Google SSO
|
||||
* Authentication Service
|
||||
* Google Cloud
|
||||
* OAuth
|
||||
* SSO
|
||||
* Environment Variables
|
||||
* LobeChat
|
||||
|
||||
---
|
||||
|
||||
# Configuration of Google SSO Authentication Service
|
||||
|
||||
<Steps>
|
||||
### Create a Google Cloud OAuth 2.0 Client
|
||||
|
||||
In your [Google Cloud Console][google-cloud-console], navigate to **APIs & Services > Credentials**.
|
||||
|
||||
Click on **Create Credentials** and select **OAuth client ID**.
|
||||
|
||||
If you haven't already set up a consent screen, you will be prompted to do so. Complete the OAuth consent screen setup (specify app name, support email, and add authorized users if needed).
|
||||
|
||||
Select **Web application** as the application type.
|
||||
|
||||
In the **Authorized redirect URIs** section, enter:
|
||||
|
||||
```bash
|
||||
https://your-domain/api/auth/callback/google
|
||||
```
|
||||
|
||||
\<Callout type={'info'}>
|
||||
\- You can add or modify redirect URIs after registration, but make sure the URL matches your deployed LobeChat instance.
|
||||
\- Replace "your-domain" with your actual domain. </Callout>
|
||||
|
||||
Click **Create**.
|
||||
|
||||
After creation, copy the **Client ID** and **Client Secret**.
|
||||
|
||||
<Image alt="Google OAuth Setup" inStep src="https://developers.google.com/static/identity/images/gsi/web/gcs-signin-2.png" />
|
||||
|
||||
### Add Users (Optional for Internal Use Only)
|
||||
|
||||
If your application is in **Testing** or **Internal** publishing status, add user emails in the OAuth consent screen under **Test users**.
|
||||
Users not added here will not be able to authenticate.
|
||||
|
||||
### Configure Environment Variables
|
||||
|
||||
When deploying LobeChat, configure the following environment variables:
|
||||
|
||||
| Environment Variable | Type | Description |
|
||||
| ------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `NEXT_AUTH_SECRET` | Required | Key to encrypt Auth.js session tokens. Generate using: `openssl rand -base64 32` |
|
||||
| `NEXT_AUTH_SSO_PROVIDERS` | Required | Select the single sign-on provider for LobeChat. Use `google` for Google SSO. |
|
||||
| `AUTH_GOOGLE_ID` | Required | Client ID from Google Cloud OAuth. |
|
||||
| `AUTH_GOOGLE_SECRET` | Required | Client Secret from Google Cloud OAuth. |
|
||||
| `NEXTAUTH_URL` | Required | Specifies the callback address for Auth.js when performing OAuth authentication. E.g. `https://your-domain/api/auth` |
|
||||
|
||||
\<Callout type={'tip'}>
|
||||
See [📘 environment variables](/docs/self-hosting/environment-variable#google) for more details on these variables. </Callout> </Steps>
|
||||
|
||||
<Callout>
|
||||
After successful deployment, users can sign in to LobeChat using their Google accounts (those added as Test Users, if not in production).
|
||||
</Callout>
|
||||
|
||||
## Advanced Configuration
|
||||
|
||||
See the [Google Identity Platform Documentation][google-identity-docs] for advanced options, scopes, and consent screen configuration.
|
||||
|
||||
## Related Resources
|
||||
|
||||
* [Quickstart: Configure a Google OAuth client][google-oauth-quickstart]
|
||||
|
||||
[google-cloud-console]: https://console.cloud.google.com/apis/credentials
|
||||
[google-oauth-quickstart]: https://developers.google.com/identity/protocols/oauth2/web-server#creatingcred
|
||||
[google-identity-docs]: https://developers.google.com/identity
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: Configuring Keycloak Authentication Service in LobeChat
|
||||
description: >-
|
||||
Learn how to configure the Keycloak authentication service in LobeChat,
|
||||
including deployment, creation, permission settings, and environment
|
||||
variables.
|
||||
Learn how to configure the Keycloak authentication service in LobeChat, including deployment, creation, permission settings, and environment variables.
|
||||
|
||||
tags:
|
||||
- Keycloak Authentication
|
||||
- Environment Variable Configuration
|
||||
@@ -16,7 +15,8 @@ tags:
|
||||
[Keycloak](https://www.keycloak.org/) is an open-source identity and access management solution that provides single sign-on, identity brokering, and social login features, suitable for modern applications and services.
|
||||
|
||||
<Callout type={'tip'}>
|
||||
If you want to privately deploy Keycloak, we recommend using it together with LobeChat via Docker Compose deployment for easier service management.
|
||||
If you want to privately deploy Keycloak, we recommend using it together with LobeChat via Docker
|
||||
Compose deployment for easier service management.
|
||||
</Callout>
|
||||
|
||||
## Keycloak Configuration Process
|
||||
@@ -37,10 +37,12 @@ If you deploy using a public network, this guide assumes:
|
||||
Access your privately deployed Keycloak admin console (default is `http://localhost:8080/admin`) and log in with the administrator account.
|
||||
|
||||
1. Create a new Realm
|
||||
|
||||
- Click the dropdown menu in the upper left corner and select "Create Realm"
|
||||
- Enter a name, such as "LobeChat", then click "Create"
|
||||
|
||||
2. Create a Client
|
||||
|
||||
- Select "Clients" from the left menu, then click "Create client"
|
||||
- Fill in the following information:
|
||||
- Client ID: `lobechat`
|
||||
@@ -65,6 +67,7 @@ If you deploy using a public network, this guide assumes:
|
||||
### Configure Users and Roles (Optional)
|
||||
|
||||
1. Create Users
|
||||
|
||||
- Select "Users" from the left menu, then click "Add user"
|
||||
- Fill in the user information and click "Create"
|
||||
- On the user details page, switch to the "Credentials" tab
|
||||
@@ -87,7 +90,8 @@ If you deploy using a public network, this guide assumes:
|
||||
4. Click "Save" to save the settings
|
||||
|
||||
<Callout type={'warning'}>
|
||||
If registration is not disabled, anyone might be able to register and log in to your application. Please configure according to your security requirements.
|
||||
If registration is not disabled, anyone might be able to register and log in to your application.
|
||||
Please configure according to your security requirements.
|
||||
</Callout>
|
||||
|
||||
### Configure Environment Variables
|
||||
@@ -116,4 +120,6 @@ If you deploy using a public network, this guide assumes:
|
||||
</Callout>
|
||||
</Steps>
|
||||
|
||||
<Callout type={'info'}>After successful deployment, users will be able to authenticate through Keycloak and use LobeChat.</Callout>
|
||||
<Callout type={'info'}>
|
||||
After successful deployment, users will be able to authenticate through Keycloak and use LobeChat.
|
||||
</Callout>
|
||||
|
||||
@@ -13,7 +13,8 @@ tags:
|
||||
[Keycloak](https://www.keycloak.org/) 是一个开源的身份和访问管理解决方案,提供单点登录、身份代理和社交登录等功能,适用于现代应用和服务。
|
||||
|
||||
<Callout type={'tip'}>
|
||||
若你想要私有部署 Keycloak,我们建议你将之与 LobeChat 一同使用 Docker Compose 部署,这样可以更方便地管理服务。
|
||||
若你想要私有部署 Keycloak,我们建议你将之与 LobeChat 一同使用 Docker Compose
|
||||
部署,这样可以更方便地管理服务。
|
||||
</Callout>
|
||||
|
||||
## Keycloak 配置流程
|
||||
@@ -34,10 +35,12 @@ tags:
|
||||
访问你私有部署的 Keycloak 管理控制台(默认为 `http://localhost:8080/admin`),使用管理员账号登录。
|
||||
|
||||
1. 创建新领域(Realm)
|
||||
|
||||
- 点击左上角的下拉菜单,选择 "Create Realm"
|
||||
- 输入名称,例如 "LobeChat",然后点击 "Create"
|
||||
|
||||
2. 创建客户端(Client)
|
||||
|
||||
- 在左侧菜单中选择 "Clients",然后点击 "Create client"
|
||||
- 填写以下信息:
|
||||
- Client ID: `lobechat`
|
||||
@@ -62,6 +65,7 @@ tags:
|
||||
### 配置用户和角色(可选)
|
||||
|
||||
1. 创建用户
|
||||
|
||||
- 在左侧菜单中选择 "Users",然后点击 "Add user"
|
||||
- 填写用户信息,点击 "Create"
|
||||
- 在用户详情页,切换到 "Credentials" 选项卡
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
---
|
||||
title: Configuring Logto Authentication Service in LobeChat
|
||||
description: >-
|
||||
Learn how to configure Logto authentication service in LobeChat, including
|
||||
deployment, creation, setting permissions, and environment variables.
|
||||
Learn how to configure Logto authentication service in LobeChat, including deployment, creation, setting permissions, and environment variables.
|
||||
|
||||
tags:
|
||||
- Logto Authentication
|
||||
- Environment Variable Configuration
|
||||
@@ -86,23 +86,17 @@ If you are using Logto Cloud, assume its endpoint domain is `https://example.log
|
||||
|
||||
If you encounter issues during the Logto deployment process, refer to the following common problems:
|
||||
|
||||
- `Only roles with the xxx attribute may create roles`:
|
||||
Check your database user's permissions and ensure that the user in your Logto database has the `admin` role to create roles.
|
||||
- `Only roles with the xxx attribute may create roles`: Check your database user's permissions and ensure that the user in your Logto database has the `admin` role to create roles.
|
||||
|
||||
- Error executing `logto db seed` on third-party databases like `Neon`:
|
||||
Try using the `logto db seed --encrypt-base-role` command.
|
||||
- Error executing `logto db seed` on third-party databases like `Neon`: Try using the `logto db seed --encrypt-base-role` command.
|
||||
|
||||
- Database seeding failed:
|
||||
Try skipping the seeding process with the `--skip-seed` parameter.
|
||||
- Database seeding failed: Try skipping the seeding process with the `--skip-seed` parameter.
|
||||
|
||||
- `Error: role xxx already exists`:
|
||||
Delete the existing role in the database.
|
||||
- `Error: role xxx already exists`: Delete the existing role in the database.
|
||||
|
||||
- Database migration failed after a version upgrade:
|
||||
Try using the command `npx @logto/cli db alteration deploy $version` (e.g., `npx @logto/cli db alteration deploy 1.22.0`).
|
||||
- Database migration failed after a version upgrade: Try using the command `npx @logto/cli db alteration deploy $version` (e.g., `npx @logto/cli db alteration deploy 1.22.0`).
|
||||
|
||||
- I am using Docker deployment and want a one-click upgrade:
|
||||
Execute the custom command in the container: `sh -c "npm run cli db seed -- --swe --encrypt-base-role" && npx @logto/cli db alteration deploy $version && npm start`
|
||||
- I am using Docker deployment and want a one-click upgrade: Execute the custom command in the container: `sh -c "npm run cli db seed -- --swe --encrypt-base-role" && npx @logto/cli db alteration deploy $version && npm start`
|
||||
|
||||
<Callout type={'info'}>
|
||||
After successful deployment, users will be able to authenticate via Logto and use LobeChat.
|
||||
|
||||
@@ -83,22 +83,16 @@ tags:
|
||||
|
||||
若你在部署 Logto 过程中遇到问题,可以参考以下常见问题:
|
||||
|
||||
- `Only roles with the xxx attribute may create roles`:
|
||||
请检查你的数据库用户权限,确保你的 Logto 数据库中的用户具有 `admin` 角色,以便创建角色。
|
||||
- `Only roles with the xxx attribute may create roles`:请检查你的数据库用户权限,确保你的 Logto 数据库中的用户具有 `admin` 角色,以便创建角色。
|
||||
|
||||
- 在第三方数据库例如 `Neon` 上执行`logto db seed`出错:
|
||||
尝试使用`logto db seed --encrypt-base-role`命令。
|
||||
- 在第三方数据库例如 `Neon` 上执行`logto db seed`出错:尝试使用`logto db seed --encrypt-base-role`命令。
|
||||
|
||||
- 数据库播种失败:
|
||||
请尝试使用`--skip-seed`参数跳过播种。
|
||||
- 数据库播种失败:请尝试使用`--skip-seed`参数跳过播种。
|
||||
|
||||
- `Error: role xxx already exists`:
|
||||
在数据库中删除已存在的角色即可。
|
||||
- `Error: role xxx already exists`:在数据库中删除已存在的角色即可。
|
||||
|
||||
- 版本升级后,数据库迁移失败:
|
||||
请尝试使用` npx @logto/cli db alteration deploy $version`命令 (例如`npx @logto/cli db alteration deploy 1.22.0`)
|
||||
- 版本升级后,数据库迁移失败:请尝试使用` npx @logto/cli db alteration deploy $version`命令 (例如`npx @logto/cli db alteration deploy 1.22.0`)
|
||||
|
||||
- 我使用 docker 部署 希望一键升级:
|
||||
在容器中执行自定义命令:`sh -c "npm run cli db seed -- --swe --encrypt-base-role" && npx @logto/cli db alteration deploy $version && npm start`
|
||||
- 我使用 docker 部署 希望一键升级:在容器中执行自定义命令:`sh -c "npm run cli db seed -- --swe --encrypt-base-role" && npx @logto/cli db alteration deploy $version && npm start`
|
||||
|
||||
<Callout type={'info'}>部署成功后,用户将可以通过 Logto 身份认证并使用 LobeChat。</Callout>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user