Compare commits

...

398 Commits

Author SHA1 Message Date
lobehubbot e5e8b80b38 📝 docs(bot): Auto sync agents & plugin to readme 2026-01-25 03:15:17 +00:00
semantic-release-bot d718de7e1a 🔖 chore(release): v1.143.3 [skip ci]
### [Version 1.143.3](https://github.com/lobehub/lobe-chat/compare/v1.143.2...v1.143.3)
<sup>Released on **2026-01-25**</sup>

#### 🐛 Bug Fixes

- **misc**: Correct claude_args quoting to prevent shell-quote parsing errors.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Correct claude_args quoting to prevent shell-quote parsing errors, closes [#10790](https://github.com/lobehub/lobe-chat/issues/10790) [#10789](https://github.com/lobehub/lobe-chat/issues/10789) ([cf197e6](https://github.com/lobehub/lobe-chat/commit/cf197e6))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2026-01-25 03:14:07 +00:00
René Wang 71de434b35 feat: Add notification card to v1 (#11736)
* feat: add ph card

* feat: add ph card

* feat: add ph card

* feat: add translation
2026-01-25 11:02:34 +08:00
YuTengjing cf197e638d 🐛 fix: correct claude_args quoting to prevent shell-quote parsing errors (#10790)
Closes #10789
2025-12-15 16:05:12 +08:00
lobehubbot fa55cd2897 📝 docs(bot): Auto sync agents & plugin to readme 2025-12-04 02:46:55 +00:00
semantic-release-bot 1b5afffe1e 🔖 chore(release): v1.143.2 [skip ci]
### [Version&nbsp;1.143.2](https://github.com/lobehub/lobe-chat/compare/v1.143.1...v1.143.2)
<sup>Released on **2025-12-04**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix React CVE.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix React CVE, closes [#10592](https://github.com/lobehub/lobe-chat/issues/10592) ([20809b5](https://github.com/lobehub/lobe-chat/commit/20809b5))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-12-04 02:45:42 +00:00
Arvin Xu 20809b5245 🐛 fix: fix React CVE (#10592)
* fix

* pin

* pin
2025-12-04 10:34:00 +08:00
lobehubbot 52b4c1271e 📝 docs(bot): Auto sync agents & plugin to readme 2025-12-02 07:32:16 +00:00
semantic-release-bot f3a48ea4db 🔖 chore(release): v1.143.1 [skip ci]
### [Version&nbsp;1.143.1](https://github.com/lobehub/lobe-chat/compare/v1.143.0...v1.143.1)
<sup>Released on **2025-12-02**</sup>

#### 🐛 Bug Fixes

- **misc**: Deepseek  interleaved thinking.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Deepseek  interleaved thinking, closes [#10550](https://github.com/lobehub/lobe-chat/issues/10550) ([73f3066](https://github.com/lobehub/lobe-chat/commit/73f3066))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-12-02 07:31:05 +00:00
Arvin Xu 73f3066b11 🐛 fix: deepseek interleaved thinking (#10550)
fix
2025-12-02 15:19:31 +08:00
lobehubbot 96784b68b5 📝 docs(bot): Auto sync agents & plugin to readme 2025-12-01 03:01:34 +00:00
semantic-release-bot eb0b8a56c7 🔖 chore(release): v1.143.0 [skip ci]
## [Version&nbsp;1.143.0](https://github.com/lobehub/lobe-chat/compare/v1.142.9...v1.143.0)
<sup>Released on **2025-12-01**</sup>

####  Features

- **misc**: Support DeepSeek Interleaved thinking.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support DeepSeek Interleaved thinking, closes [#10478](https://github.com/lobehub/lobe-chat/issues/10478) [#10219](https://github.com/lobehub/lobe-chat/issues/10219) [#10152](https://github.com/lobehub/lobe-chat/issues/10152) ([aee5d71](https://github.com/lobehub/lobe-chat/commit/aee5d71))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-12-01 03:00:20 +00:00
Arvin Xu aee5d7144f feat: support DeepSeek Interleaved thinking (#10478)
*  feat: support DeepSeek Interleaved thinking (#10219)

* 🐛 fix: add SSRF protection  (#10152)

* fix snap

---------

Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
2025-12-01 09:57:10 +08:00
lobehubbot 60924d7742 📝 docs(bot): Auto sync agents & plugin to readme 2025-11-02 13:49:18 +00:00
semantic-release-bot a2a097fbec 🔖 chore(release): v1.142.9 [skip ci]
### [Version&nbsp;1.142.9](https://github.com/lobehub/lobe-chat/compare/v1.142.8...v1.142.9)
<sup>Released on **2025-11-02**</sup>

#### 🐛 Bug Fixes

- **misc**: OIDC error when connecting to self-host instance.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: OIDC error when connecting to self-host instance, closes [#9916](https://github.com/lobehub/lobe-chat/issues/9916) ([2e2b9c4](https://github.com/lobehub/lobe-chat/commit/2e2b9c4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-11-02 13:47:55 +00:00
YuTengjing 197118347c 📝 docs: update ComfyUI documentation cover image URL (#9997) 2025-11-02 21:35:01 +08:00
Aloxaf 2e2b9c4c88 🐛 fix: OIDC error when connecting to self-host instance (#9916)
fix: oidc/consent redirect header
2025-10-31 00:25:21 +08:00
lobehubbot 7f7e571755 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-30 04:14:55 +00:00
semantic-release-bot 0df0378def 🔖 chore(release): v1.142.8 [skip ci]
### [Version&nbsp;1.142.8](https://github.com/lobehub/lobe-chat/compare/v1.142.7...v1.142.8)
<sup>Released on **2025-10-30**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-30 04:13:39 +00:00
dependabot[bot] 4376ba2bb2 👷 build(deps): bump next-auth from 5.0.0-beta.29 to 5.0.0-beta.30 (#9926)
build(deps): bump next-auth from 5.0.0-beta.29 to 5.0.0-beta.30

Bumps [next-auth](https://github.com/nextauthjs/next-auth) from 5.0.0-beta.29 to 5.0.0-beta.30.
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@5.0.0-beta.29...next-auth@5.0.0-beta.30)

---
updated-dependencies:
- dependency-name: next-auth
  dependency-version: 5.0.0-beta.30
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-30 12:02:12 +08:00
Shinji-Li 0fdf70944d 🔨 chore: support lobehub-marketPlace oidc in lobechat (#9922)
* chore: support market-oidc grant

* fix: update appEnv the env Market Base Url

* test: update test.ts & the oidc path

* feat: update the oidc-provider test
2025-10-30 09:59:04 +08:00
Arvin Xu 5db4f58d85 🔨 chore: pre-merge message group code (#9915)
* pre merge

* refactor

* clean

* fix types

* fix tests

* 🧪 test(plugin): update triggerToolCalls test expectations

- Update content expectation from LOADING_FLAT to empty string
- Add threadId and groupId fields to test assertions
- Use toHaveBeenNthCalledWith for clearer assertions
- Remove unused LOADING_FLAT import

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-29 09:44:58 +08:00
lobehubbot d4a5d4d443 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-28 16:25:03 +00:00
semantic-release-bot 267d4d80b1 🔖 chore(release): v1.142.7 [skip ci]
### [Version&nbsp;1.142.7](https://github.com/lobehub/lobe-chat/compare/v1.142.6...v1.142.7)
<sup>Released on **2025-10-28**</sup>

#### ♻ Code Refactoring

- **misc**: Change files page from RSC to SPA mode to improve performance.

#### 💄 Styles

- **aihubmix**: Update extendParams to include urlContext.
- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Change files page from RSC to SPA mode to improve performance, closes [#9846](https://github.com/lobehub/lobe-chat/issues/9846) ([f46cc50](https://github.com/lobehub/lobe-chat/commit/f46cc50))

#### Styles

* **aihubmix**: Update extendParams to include urlContext, closes [#9914](https://github.com/lobehub/lobe-chat/issues/9914) ([5a8fd85](https://github.com/lobehub/lobe-chat/commit/5a8fd85))
* **misc**: Update i18n, closes [#9907](https://github.com/lobehub/lobe-chat/issues/9907) ([d149c4d](https://github.com/lobehub/lobe-chat/commit/d149c4d))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-28 16:23:47 +00:00
Coooolfan 5a8fd853d7 💄 style(aihubmix): update extendParams to include urlContext (#9914)
 feat(aihubmix): update extendParams to include urlContext
2025-10-29 00:09:08 +08:00
LobeHub Bot d149c4dbdf 🤖 style: update i18n (#9907)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-29 00:07:48 +08:00
Shinji-Li f46cc50e15 ♻️ refactor: change files page from RSC to SPA mode to improve performance (#9846)
* feat: change files page to spa

* feat: add height 100%

* fix: delete useless code

* feat: update files preview from severs to client

* feat: change download action to detail right place

* feat: change the dir name and the path

* feat: change the useNavigate to the router outer

* style: use flex 1 to get all width

* feat: update files?filesId to get files loading

* feat: change all files and repo url to knowledge url

* feat: close the knowledge/base settings page & update createNew onSuccess into Modal

* feat: update the knowledge base open judge
2025-10-29 00:02:51 +08:00
lobehubbot 67cb0711d6 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-28 08:02:32 +00:00
semantic-release-bot 747c59c36a 🔖 chore(release): v1.142.6 [skip ci]
### [Version&nbsp;1.142.6](https://github.com/lobehub/lobe-chat/compare/v1.142.5...v1.142.6)
<sup>Released on **2025-10-28**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-28 08:01:28 +00:00
Neko 6c5caf9184 👷 build(database): improve user memory schema (#9822)
chore(database): improve user memory schema
2025-10-28 15:50:15 +08:00
lobehubbot 4684ee7f4c 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-27 10:05:01 +00:00
semantic-release-bot 09cbde3a3b 🔖 chore(release): v1.142.5 [skip ci]
### [Version&nbsp;1.142.5](https://github.com/lobehub/lobe-chat/compare/v1.142.4...v1.142.5)
<sup>Released on **2025-10-27**</sup>

#### 💄 Styles

- **misc**: Add MiniMax-M2 model.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add MiniMax-M2 model, closes [#9897](https://github.com/lobehub/lobe-chat/issues/9897) ([d6fded2](https://github.com/lobehub/lobe-chat/commit/d6fded2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-27 10:03:47 +00:00
sxjeru d6fded2973 💄 style: Add MiniMax-M2 model (#9897)
*  feat: 更新多个模型的描述、定价和能力,添加新模型支持

*  fix: 处理 <think> 标签的内容分割,优化思考状态管理

*  test: 添加测试以处理包含 </think> 标签的内容分割

* fix test
2025-10-27 17:39:20 +08:00
lobehubbot ed00394c6e 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-27 09:36:51 +00:00
semantic-release-bot 8558231037 🔖 chore(release): v1.142.4 [skip ci]
### [Version&nbsp;1.142.4](https://github.com/lobehub/lobe-chat/compare/v1.142.3...v1.142.4)
<sup>Released on **2025-10-27**</sup>

#### 💄 Styles

- **misc**: Pre render ModelSwitchPanel, The error details of the connectivity check lead to a layout problem.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Pre render ModelSwitchPanel, closes [#9499](https://github.com/lobehub/lobe-chat/issues/9499) ([840382b](https://github.com/lobehub/lobe-chat/commit/840382b))
* **misc**: The error details of the connectivity check lead to a layout problem, closes [#9872](https://github.com/lobehub/lobe-chat/issues/9872) ([ea42e60](https://github.com/lobehub/lobe-chat/commit/ea42e60))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-27 09:35:14 +00:00
Neko ef843c2b81 🔨 chore(ci): better renovate config with group and weekly strategy (#9466)
chore(ci): better revonate config with group and weekly strategy
2025-10-27 17:23:28 +08:00
EldestBard ea42e60ddb 💄 style: The error details of the connectivity check lead to a layout problem (#9872)
* give ProviderChecker's Error component a maxWidth

* add warp property to Highlighter

---------

Co-authored-by: daryl saxton <admin@dylsxtn.com>
2025-10-27 17:20:42 +08:00
sxjeru 840382b5b5 💄 style: Pre render ModelSwitchPanel (#9499)
 feat: (预渲染模型切换浮窗)支持 ActionDropdown 组件的预渲染功能
2025-10-27 17:18:17 +08:00
René Wang bf53c116dd 👷 build: Add editorData column on document table (#9899)
feat: Add rawData column
2025-10-27 17:12:55 +08:00
renovate[bot] fbb96eb4f8 Update dependency @opentelemetry/exporter-metrics-otlp-http to ^0.207.0 (#9879)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 14:27:16 +08:00
renovate[bot] 6fa438c522 Update dependency @opentelemetry/instrumentation to ^0.207.0 (#9881)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 14:27:08 +08:00
Arvin Xu 7fa6c749db 🔨 chore: pre-merge message group code (#9896)
* update

* update
2025-10-27 13:42:50 +08:00
lobehubbot b03d6cd940 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-27 02:40:38 +00:00
semantic-release-bot 01e009e89a 🔖 chore(release): v1.142.3 [skip ci]
### [Version&nbsp;1.142.3](https://github.com/lobehub/lobe-chat/compare/v1.142.2...v1.142.3)
<sup>Released on **2025-10-27**</sup>

#### 💄 Styles

- **misc**: Adjust modal setting form styles for improved layout and responsiveness, Unzip file when uploading in knowledge base [LOB-500].

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Adjust modal setting form styles for improved layout and responsiveness, closes [#9890](https://github.com/lobehub/lobe-chat/issues/9890) ([1997ec5](https://github.com/lobehub/lobe-chat/commit/1997ec5))
* **misc**: Unzip file when uploading in knowledge base [LOB-500], closes [#9854](https://github.com/lobehub/lobe-chat/issues/9854) ([e568ce6](https://github.com/lobehub/lobe-chat/commit/e568ce6))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-27 02:39:04 +00:00
bbbugg 1997ec5fe2 💄 style: adjust modal setting form styles for improved layout and responsiveness (#9890)
fix: adjust modal styles for improved layout and responsiveness
2025-10-27 10:26:57 +08:00
René Wang e568ce6f31 💄 style: Unzip file when uploading in knowledge base [LOB-500] (#9854)
* feat: Unzip file

* feat: Limit max file upload limit

* fix: Remove unused test

* opti: Update translation

* style: Adjust padding

* feat: Update translation

* fix: Test error

* fix: Test erro

* fix: Test

* fix: test error

* fix: Test

* feat: Rremove message
2025-10-27 10:25:50 +08:00
renovate[bot] 1fe6a5997b Update dependency @opentelemetry/instrumentation-http to ^0.207.0 (#9773)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 21:27:29 +08:00
renovate[bot] e75ca3026b Update dependency @opentelemetry/winston-transport to ^0.18.0 (#9884)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:01:05 +08:00
renovate[bot] a8534da718 Update dependency @opentelemetry/sdk-node to ^0.207.0 (#9883)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:00:57 +08:00
renovate[bot] 785b0e04f1 Update dependency @opentelemetry/instrumentation-pg to ^0.60.0 (#9882)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:00:49 +08:00
renovate[bot] 3e665a4f5c Update dependency @opentelemetry/exporter-trace-otlp-http to ^0.207.0 (#9880)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:00:33 +08:00
renovate[bot] 2dd134e0b8 Update dependency @opentelemetry/auto-instrumentations-node to ^0.66.0 (#9878)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 20:00:19 +08:00
lobehubbot 12577ec03c 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-26 06:14:05 +00:00
semantic-release-bot 9d87676d6a 🔖 chore(release): v1.142.2 [skip ci]
### [Version&nbsp;1.142.2](https://github.com/lobehub/lobe-chat/compare/v1.142.1...v1.142.2)
<sup>Released on **2025-10-26**</sup>

#### 💄 Styles

- **misc**: Improve provider modal height when creating custom provider.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve provider modal height when creating custom provider, closes [#9870](https://github.com/lobehub/lobe-chat/issues/9870) ([55d92c0](https://github.com/lobehub/lobe-chat/commit/55d92c0))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-26 06:12:56 +00:00
EldestBard 55d92c0241 💄 style: improve provider modal height when creating custom provider (#9870)
Remove fixed height for create new provider modal

Co-authored-by: daryl saxton <admin@dylsxtn.com>
2025-10-26 14:01:18 +08:00
Arvin Xu dec137a21e 🔨 chore: refactor ui message type (#9877)
* rename ChatMessage To UIChatMessage
refactor db message items

* rename ChatMessage To UIChatMessage
refactor db message items
2025-10-26 13:31:53 +08:00
lobehubbot 863521105e 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-26 03:45:32 +00:00
semantic-release-bot 9faf4c6b52 🔖 chore(release): v1.142.1 [skip ci]
### [Version&nbsp;1.142.1](https://github.com/lobehub/lobe-chat/compare/v1.142.0...v1.142.1)
<sup>Released on **2025-10-26**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9862](https://github.com/lobehub/lobe-chat/issues/9862) ([8d3bc91](https://github.com/lobehub/lobe-chat/commit/8d3bc91))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-26 03:44:08 +00:00
YuTengjing 932ceb218b 🔨 chore: sync some small fix changes form cloud (#9875)
* chore: sync some small fix changes form cloud

* 🐛 fix(image): fix recreateImage execution order and test assertions

- Check activeGenerationTopicId before accessing batch to prevent undefined errors
- Update test mock to include required generations array
- Use batch.generations.length in test assertions instead of hardcoded value
2025-10-26 11:31:37 +08:00
renovate[bot] 3c197360e1 Update dependency lucide-react to ^0.548.0 (#9886)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-26 11:31:08 +08:00
LobeHub Bot 8d3bc91e46 🤖 style: update i18n (#9862) 2025-10-26 11:22:37 +08:00
lobehubbot 2558b31963 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-25 16:24:48 +00:00
Arvin Xu 2ac1ff324b ♻️ chore: refactor message type (#9876)
* refactor message position

* refactor @/message to @lobechat/types

* refactor @lobechat/types

* fix lint
2025-10-26 00:13:10 +08:00
bbbugg 26533b4938 📝 docs: update "ENABLED_COMFYUI" and remove "ENABLED_BFL" "ENABLED_VERCELAIGATEWAY" in docs (#9858)
* add ENABLED_COMFYUI, ENABLED_AWS_BEDROCK, ENABLED_OPENAI and remove ENABLED_BFL and ENABLED_VERCELAIGATEWAY

* 🐛 fix: update AiHubMix links and API key documentation

* revert change AiHubMix url
2025-10-24 16:15:56 +08:00
lobehubbot 15dd7ecea4 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-24 04:21:37 +00:00
semantic-release-bot e264c3ab9d 🔖 chore(release): v1.142.0 [skip ci]
## [Version&nbsp;1.142.0](https://github.com/lobehub/lobe-chat/compare/v1.141.10...v1.142.0)
<sup>Released on **2025-10-24**</sup>

####  Features

- **misc**: Use env to control clerk allow origin feature.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Use env to control clerk allow origin feature, closes [#9863](https://github.com/lobehub/lobe-chat/issues/9863) ([490fee0](https://github.com/lobehub/lobe-chat/commit/490fee0))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-24 04:20:25 +00:00
Shinji-Li 490fee002e feat: use env to control clerk allow origin feature (#9863)
feat: use clerk allow origin feature
2025-10-24 12:08:22 +08:00
lobehubbot 55c1149d95 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-23 10:07:48 +00:00
semantic-release-bot fbc8d20f94 🔖 chore(release): v1.141.10 [skip ci]
### [Version&nbsp;1.141.10](https://github.com/lobehub/lobe-chat/compare/v1.141.9...v1.141.10)
<sup>Released on **2025-10-23**</sup>

#### 🐛 Bug Fixes

- **misc**: Loadmore not work & navbar not show in pwa.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Loadmore not work & navbar not show in pwa, closes [#9855](https://github.com/lobehub/lobe-chat/issues/9855) ([411f875](https://github.com/lobehub/lobe-chat/commit/411f875))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-23 10:06:40 +00:00
Shinji-Li 411f875584 🐛 fix: loadmore not work & navbar not show in pwa (#9855)
fix: fix pwa loadmore not work & navbar not show in pwa
2025-10-23 17:53:31 +08:00
lobehubbot 8d34065833 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-23 09:27:04 +00:00
semantic-release-bot 11fb073e2a 🔖 chore(release): v1.141.9 [skip ci]
### [Version&nbsp;1.141.9](https://github.com/lobehub/lobe-chat/compare/v1.141.8...v1.141.9)
<sup>Released on **2025-10-23**</sup>

#### 💄 Styles

- **misc**: Improve local system tools render.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve local system tools render, closes [#9853](https://github.com/lobehub/lobe-chat/issues/9853) ([295e8fc](https://github.com/lobehub/lobe-chat/commit/295e8fc))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-23 09:25:55 +00:00
Arvin Xu 295e8fc3b2 💄 style: improve local system tools render (#9853)
* refactor the local system tools render

* fix build

* pre merge intervention
2025-10-23 17:14:35 +08:00
lobehubbot b2f8b4e191 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-23 08:31:18 +00:00
semantic-release-bot 263ba692cb 🔖 chore(release): v1.141.8 [skip ci]
### [Version&nbsp;1.141.8](https://github.com/lobehub/lobe-chat/compare/v1.141.7...v1.141.8)
<sup>Released on **2025-10-23**</sup>

#### 💄 Styles

- **misc**: Improvement for Agent Team After Alpha Launch [LOB-517].

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improvement for Agent Team After Alpha Launch [LOB-517], closes [#9748](https://github.com/lobehub/lobe-chat/issues/9748) ([28245be](https://github.com/lobehub/lobe-chat/commit/28245be))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-23 08:30:13 +00:00
René Wang 28245be7f3 💄 style: Improvement for Agent Team After Alpha Launch [LOB-517] (#9748)
* feat: Create group member shortcut

* feat: Hide sub topic mode in group chat

* fix: persist stop thinking tag

* feat: ALlow quick

* fix: No fallback header

* feat: Control in lab

* fix: tag style

* fix: Hide virtual in the member add modal

* fix: Ts error

* feat: Replace string

* feat: Update strings

* fix: Cannot turn off host

* feat: Delete member after remove group

* feat: Rename varibles and files

* fix: Update test snap
2025-10-23 16:18:18 +08:00
lobehubbot f723b38fa8 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-23 03:38:33 +00:00
semantic-release-bot 5382df8cf8 🔖 chore(release): v1.141.7 [skip ci]
### [Version&nbsp;1.141.7](https://github.com/lobehub/lobe-chat/compare/v1.141.6...v1.141.7)
<sup>Released on **2025-10-23**</sup>

#### 💄 Styles

- **misc**: Allow removal of `top_p` and similar request parameters.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Allow removal of `top_p` and similar request parameters, closes [#9498](https://github.com/lobehub/lobe-chat/issues/9498) ([4c313ce](https://github.com/lobehub/lobe-chat/commit/4c313ce))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-23 03:37:10 +00:00
sxjeru 4c313ced5e 💄 style: Allow removal of top_p and similar request parameters (#9498)
*  feat: 增强参数管理,支持删除 undefined 和 null 值的参数

*  feat: 将模型参数中的 null 值转换为 undefined,以防止向 API 发送 null 值

*  feat: 更新参数处理逻辑,使用 null 表示禁用标记,优化前端与后端的参数同步

*  feat: 更新 Novita 和 SiliconCloud 模型,添加新模型并优化定价信息

* 🔧 refactor: remove deprecated model and update ID format in AI models

* 🔧 feat: update pricing for novita models and add new Ling 1T model to siliconcloud

* 🔧 feat: remove deprecated ERNIE model and add DeepSeek V3.2 Exp models to siliconcloud

---------

Co-authored-by: Arvin Xu <arvinx@foxmail.com>
2025-10-23 11:25:49 +08:00
Arvin Xu b6f1fc4a14 test: add BDD test framework and initial tests with Playwright and Cucumber (#9843)
* try with bdd test

* update

* update

* add workspace

* update

* fix

* ci

* ci

* fix

* update

* update

* update parallel

* update config

* ️ perf: increase e2e timeout to 120 seconds

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* update config

* more parallel

* fix parallel

* fix tests

* refactor to improve performance

* fix

* fix

* fix

* refactor with tsx

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-23 02:15:24 +08:00
lobehubbot 0dc112436b 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-22 16:53:21 +00:00
semantic-release-bot 24c42f6025 🔖 chore(release): v1.141.6 [skip ci]
### [Version&nbsp;1.141.6](https://github.com/lobehub/lobe-chat/compare/v1.141.5...v1.141.6)
<sup>Released on **2025-10-22**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-22 16:52:16 +00:00
Arvin Xu 48ee38cf9b 👷 build: fix build (#9847)
* fix build

* use pnpm install to keep lint stricter
2025-10-23 00:40:57 +08:00
lobehubbot 654064ff7d 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-22 15:48:53 +00:00
semantic-release-bot b5219c220a 🔖 chore(release): v1.141.5 [skip ci]
### [Version&nbsp;1.141.5](https://github.com/lobehub/lobe-chat/compare/v1.141.4...v1.141.5)
<sup>Released on **2025-10-22**</sup>

#### ♻ Code Refactoring

- **misc**: Change discover page from RSC to SPA to improve performance.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Change discover page from RSC to SPA to improve performance, closes [#9828](https://github.com/lobehub/lobe-chat/issues/9828) ([b59ee0a](https://github.com/lobehub/lobe-chat/commit/b59ee0a))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-22 15:47:41 +00:00
Shinji-Li b59ee0aabe ♻️ refactor: change discover page from RSC to SPA to improve performance (#9828)
* feat: change discord page to spa

* fix: change locals

* feat: update router change

* fix: revert some files

* feat: add model provider detail page use link

* fix: add trpc back

* feat: update e2e timeout time

* feat: change discord page to spa

* fix: change locals

* feat: update router change

* fix: revert some files

* feat: add model provider detail page use link

* fix: add trpc back

* feat: update e2e timeout time

* fix: use reactrouter-dom link replace next link
2025-10-22 23:35:49 +08:00
Arvin Xu d481315a66 👷 chore: pin bun@1.2.23 to fix vercel build (#9839)
pin bun
2025-10-22 19:13:50 +08:00
lobehubbot 4f5d1ff6af 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-22 05:05:26 +00:00
semantic-release-bot 1bd46e0346 🔖 chore(release): v1.141.4 [skip ci]
### [Version&nbsp;1.141.4](https://github.com/lobehub/lobe-chat/compare/v1.141.3...v1.141.4)
<sup>Released on **2025-10-22**</sup>

#### ♻ Code Refactoring

- **misc**: Fix model runtime cost calculate with CNY.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Fix model runtime cost calculate with CNY, closes [#9834](https://github.com/lobehub/lobe-chat/issues/9834) ([2e911ea](https://github.com/lobehub/lobe-chat/commit/2e911ea))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-22 05:04:18 +00:00
René Wang a8be073b65 💄 style: support files auto chunking (#9823)
* feat: Auto chunking

* feat: Auto chunking

* opti: Lazy loading for preview content
2025-10-22 12:52:11 +08:00
Arvin Xu 2e911ea9f5 ♻️ refactor: fix model runtime cost calculate with CNY (#9834)
* fix model runtime cost calculate

* add tests
2025-10-22 12:49:51 +08:00
lobehubbot a89f4c73b4 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-22 03:17:53 +00:00
semantic-release-bot ebf5a8f649 🔖 chore(release): v1.141.3 [skip ci]
### [Version&nbsp;1.141.3](https://github.com/lobehub/lobe-chat/compare/v1.141.2...v1.141.3)
<sup>Released on **2025-10-22**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9832](https://github.com/lobehub/lobe-chat/issues/9832) ([80b0999](https://github.com/lobehub/lobe-chat/commit/80b0999))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-22 03:16:46 +00:00
LobeHub Bot 80b0999467 🤖 style: update i18n (#9832)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-22 11:05:01 +08:00
bbbugg be952f95fd 📝 docs: update GitHub stars link and tutorial URLs in README files (#9831) 2025-10-22 11:04:38 +08:00
lobehubbot d46bb9ad8b 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 15:21:23 +00:00
semantic-release-bot 8e7f541a0b 🔖 chore(release): v1.141.2 [skip ci]
### [Version&nbsp;1.141.2](https://github.com/lobehub/lobe-chat/compare/v1.141.1...v1.141.2)
<sup>Released on **2025-10-21**</sup>

#### 💄 Styles

- **settings**: Broadcast locale changes and update switchLocale action.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **settings**: Broadcast locale changes and update switchLocale action, closes [#9620](https://github.com/lobehub/lobe-chat/issues/9620) ([0eb02ca](https://github.com/lobehub/lobe-chat/commit/0eb02ca))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 15:20:15 +00:00
𝑾𝒖𝒙𝒉 0eb02ca1c5 💄 style(settings): broadcast locale changes and update switchLocale action (#9620)
chore(locale): broadcast locale changes and update switchLocale action
2025-10-21 23:08:38 +08:00
Arvin Xu b90436421b build: pin posthog@1.278.0 to fix build (#9829) 2025-10-21 23:07:14 +08:00
lobehubbot e284330678 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 12:45:04 +00:00
semantic-release-bot ea9da27e72 🔖 chore(release): v1.141.1 [skip ci]
### [Version&nbsp;1.141.1](https://github.com/lobehub/lobe-chat/compare/v1.141.0...v1.141.1)
<sup>Released on **2025-10-21**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor context engine.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Refactor context engine, closes [#9821](https://github.com/lobehub/lobe-chat/issues/9821) ([e99f12f](https://github.com/lobehub/lobe-chat/commit/e99f12f))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 12:43:58 +00:00
Arvin Xu e99f12f840 ♻️ refactor: refactor context engine (#9821)
* refactor context engine

* fix LocalSystem issue

* refactor mcp in local
2025-10-21 20:32:42 +08:00
lobehubbot bbc037912c 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 09:01:24 +00:00
semantic-release-bot 9881b7b303 🔖 chore(release): v1.141.0 [skip ci]
## [Version&nbsp;1.141.0](https://github.com/lobehub/lobe-chat/compare/v1.140.0...v1.141.0)
<sup>Released on **2025-10-21**</sup>

####  Features

- **misc**: Add PDF export functionality to share modal.

#### 🐛 Bug Fixes

- **misc**: Ignore abort signal errors in TRPC client, slove when pwa user info have code cannot be viewed in full.

#### 💄 Styles

- **misc**: Add knowledge base mansory layout [LOB-496], improve rich text link display.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add PDF export functionality to share modal, closes [#9300](https://github.com/lobehub/lobe-chat/issues/9300) [#9299](https://github.com/lobehub/lobe-chat/issues/9299) ([2b7761c](https://github.com/lobehub/lobe-chat/commit/2b7761c))

#### What's fixed

* **misc**: Ignore abort signal errors in TRPC client, closes [#9809](https://github.com/lobehub/lobe-chat/issues/9809) [#9401](https://github.com/lobehub/lobe-chat/issues/9401) ([7f7dcfb](https://github.com/lobehub/lobe-chat/commit/7f7dcfb))
* **misc**: Slove when pwa user info have code cannot be viewed in full, closes [#9817](https://github.com/lobehub/lobe-chat/issues/9817) ([6734a47](https://github.com/lobehub/lobe-chat/commit/6734a47))

#### Styles

* **misc**: Add knowledge base mansory layout [LOB-496], closes [#9722](https://github.com/lobehub/lobe-chat/issues/9722) ([69f21da](https://github.com/lobehub/lobe-chat/commit/69f21da))
* **misc**: Improve rich text link display, closes [#9816](https://github.com/lobehub/lobe-chat/issues/9816) ([af33543](https://github.com/lobehub/lobe-chat/commit/af33543))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 08:59:56 +00:00
Arvin Xu e9de9e3b52 test: fix tests (#9818)
fix tests
2025-10-21 16:48:24 +08:00
René Wang 69f21da3e1 💄 style: add knowledge base mansory layout [LOB-496] (#9722)
* feat: Add knowlwdge base entry

* feat: Bump dayjs

* style: Mansory

* feat: Persist state in URL

* lint: Remove unesd file

* feat: Skelton

* fix: Persist view preference

* fix: Chunk label

* fix: Lint error

* fix: Activate style

* fix: Image size
2025-10-21 16:42:58 +08:00
Shinji-Li 2b7761c36e feat: add PDF export functionality to share modal (#9300)
* feat: add PDF export functionality to share modal

- Create usePdfExport hook with jsPDF and html2canvas
- Add "Export as PDF" button to screenshot tab in share modal
- Support multi-page PDFs for long conversations
- Add required dependencies: jspdf@^2.5.2 and html2canvas@^1.4.1
- Add localization support for PDF export button

Fixes #9299

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: LobeHub Bot <lobehubbot@users.noreply.github.com>

* ♻️ refactor: convert PDF export to separate tab with backend generation

- Create new SharePdf tab component with PDF.js preview
- Move PDF generation from frontend to backend via tRPC
- Add server-side PDF generation using jsPDF
- Remove old PDF export button from ShareImage component
- Add proper loading states and error handling
- Update localization for PDF tab

Co-authored-by: Shinji-Li <ONLY-yours@users.noreply.github.com>

* 🐛 fix: resolve unicorn/no-await-expression-member lint error in PDF exporter

Split await expression member access to avoid linting error in exporter.ts

Co-authored-by: Shinji-Li <ONLY-yours@users.noreply.github.com>

* feat: add i18n

* feat: use pdfkit to export a pdf

* feat: add fullscreen preview

* feat: update pdf preview styles

* feat: add i18n locales

* feat: add single pdf share modal

* feat: update css & client mode cant use pdf genertate

* fix: mobile style fixed

* fix: delete console.log & useless packagejson

* feat: use online otf link

---------

Co-authored-by: Shinji-Li <ONLY-yours@users.noreply.github.com>
2025-10-21 16:32:17 +08:00
Shinji-Li 6734a47759 🐛 fix: slove when pwa user info have code cannot be viewed in full (#9817)
fix: slove when pwa user info have code cCannot be viewed in full
2025-10-21 16:22:46 +08:00
Arvin Xu 7f7dcfbff9 🩹 fix: ignore abort signal errors in TRPC client (#9809)
- Add abort error detection in lambda client error handling link
- Prevent showing notifications for aborted requests (e.g., rapid settings updates)
- Check for various abort error patterns: 'aborted', 'AbortError', 'signal is aborted without reason'

Fixes #9401

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-21 16:17:18 +08:00
Arvin Xu af33543cba 💄 style: improve rich text link display (#9816)
* fix model runtime issue

* fix model runtime issue
2025-10-21 16:17:08 +08:00
lobehubbot 8b619f0a8e 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 07:47:09 +00:00
semantic-release-bot b81d8f79a4 🔖 chore(release): v1.140.0 [skip ci]
## [Version&nbsp;1.140.0](https://github.com/lobehub/lobe-chat/compare/v1.139.5...v1.140.0)
<sup>Released on **2025-10-21**</sup>

####  Features

- **misc**: Add ComfyUI integration Phase1(RFC-128).

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add ComfyUI integration Phase1(RFC-128), closes [#9043](https://github.com/lobehub/lobe-chat/issues/9043) ([15ffe28](https://github.com/lobehub/lobe-chat/commit/15ffe28))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 07:46:07 +00:00
Maple Gao 15ffe289f5 feat: add ComfyUI integration Phase1(RFC-128) (#9043)
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
2025-10-21 15:34:57 +08:00
lobehubbot 2606f93146 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 07:28:40 +00:00
semantic-release-bot c882e75580 🔖 chore(release): v1.139.5 [skip ci]
### [Version&nbsp;1.139.5](https://github.com/lobehub/lobe-chat/compare/v1.139.4...v1.139.5)
<sup>Released on **2025-10-21**</sup>

#### 🐛 Bug Fixes

- **desktop**: Fix desktop open error in some edge cases.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **desktop**: Fix desktop open error in some edge cases, closes [#9813](https://github.com/lobehub/lobe-chat/issues/9813) ([6334f62](https://github.com/lobehub/lobe-chat/commit/6334f62))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 07:27:27 +00:00
Arvin Xu 6334f62aa1 🐛 fix(desktop): fix desktop open error in some edge cases (#9813)
fix lock file bug
2025-10-21 15:16:10 +08:00
Shinji-Li 0af13ca057 fix: sub topic fetch branching topic id was used dynmic get (#9811)
* feat: when branching topic id was dynmic fetch

* fix: add topic id into callback dep
2025-10-21 14:16:40 +08:00
lobehubbot 68d6457659 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 05:06:36 +00:00
semantic-release-bot 3466055968 🔖 chore(release): v1.139.4 [skip ci]
### [Version&nbsp;1.139.4](https://github.com/lobehub/lobe-chat/compare/v1.139.3...v1.139.4)
<sup>Released on **2025-10-21**</sup>

#### 🐛 Bug Fixes

- **misc**: Pass threadId to messages in sendMessageInServer.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Pass threadId to messages in sendMessageInServer, closes [#9808](https://github.com/lobehub/lobe-chat/issues/9808) ([d99a3a8](https://github.com/lobehub/lobe-chat/commit/d99a3a8))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 05:05:20 +00:00
Arvin Xu d99a3a80f8 🐛 fix: pass threadId to messages in sendMessageInServer (#9808)
* fix dev hydration

* 🐛 fix: pass threadId to messages in sendMessageInServer

- Add threadId parameter to CreateMessageParams interface
- Pass threadId when creating user and assistant messages in aiChat router
- Add comprehensive tests for threadId handling and outputJSON method

This ensures thread context is properly maintained across message creation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

*  test: add comprehensive tests for addUserMessage

- Test early return when activeId is undefined
- Test message creation with files
- Test threadId propagation when activeThreadId is set
- Test input message clearing after message creation
- Test handling messages without fileList

This ensures the addUserMessage action correctly handles all scenarios including thread context.

🤖 Generated with [Claude Code](https://claude.com/claude-code)



* fix thread fix

* move

* baseline

*  test: fix and improve message integration tests

- Mock FileService to avoid S3 initialization issues
- Mock getServerDB to use test database instance
- Add test for threadId parameter in message creation
- Fix pagination test to handle variable message counts
- Fix batchCreate test to skip rowCount assertion (undefined in PGlite)
- Skip topicId validation test (not currently enforced)

All 15 integration tests now passing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


* refactor

* improve
2025-10-21 12:54:52 +08:00
renovate[bot] cc37acb30b Update actions/download-artifact action to v5 (#8740)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 12:14:18 +08:00
Arvin Xu eb708e04fe 📝 docs: fix outdated server-side database documentation (#9806)
- Update environment file setup instructions to use docker-compose/local/.env.example instead of .env.example.development
- Fix references to environment file locations in both English and Chinese documentation
- Align documentation with actual Docker Compose configuration that uses env_file: .env in docker-compose/local/ directory

Fixes #9525

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-21 11:24:12 +08:00
lobehubbot 11df190f36 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-21 02:42:55 +00:00
semantic-release-bot a094d5c04c 🔖 chore(release): v1.139.3 [skip ci]
### [Version&nbsp;1.139.3](https://github.com/lobehub/lobe-chat/compare/v1.139.2...v1.139.3)
<sup>Released on **2025-10-21**</sup>

#### 💄 Styles

- **misc**: Show message author in minimap.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Show message author in minimap, closes [#9797](https://github.com/lobehub/lobe-chat/issues/9797) ([f6daefb](https://github.com/lobehub/lobe-chat/commit/f6daefb))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-21 02:41:24 +00:00
René Wang f6daefb0a2 💄 style: Show message author in minimap (#9797)
* feat: Show message author

* fix: Use debug instead

* Update index.tsx
2025-10-21 10:30:34 +08:00
renovate[bot] b8c59be3a7 Update dependency openapi-fetch to ^0.14.0 (#5596)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 10:01:09 +08:00
renovate[bot] 284faf799b Update dependency @electron-toolkit/tsconfig to v2 (#9556)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 09:50:17 +08:00
lobehubbot 88d194c3b6 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-20 16:40:00 +00:00
semantic-release-bot 6c53a5f026 🔖 chore(release): v1.139.2 [skip ci]
### [Version&nbsp;1.139.2](https://github.com/lobehub/lobe-chat/compare/v1.139.1...v1.139.2)
<sup>Released on **2025-10-20**</sup>

#### 💄 Styles

- **misc**: Solve when desktop the sider agent list too long.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Solve when desktop the sider agent list too long, closes [#9792](https://github.com/lobehub/lobe-chat/issues/9792) ([778dea3](https://github.com/lobehub/lobe-chat/commit/778dea3))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-20 16:38:30 +00:00
renovate[bot] d430836f20 Update dependency @anthropic-ai/sdk to ^0.67.0 (#9771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-21 00:27:21 +08:00
jjangga0214 7571b66284 👷 build(deps): missing deps for model-runtime (#9782) 2025-10-21 00:26:23 +08:00
Shinji-Li 3ec394d77a fix: slove when desktop 401 not pop up many message (#9789)
* fix: slove when desktop 401 not theathore many message

* fix: fixed desktop store login judge
2025-10-21 00:23:00 +08:00
Shinji-Li 778dea3853 💄 style: solve when desktop the sider agent list too long (#9792)
fix: solve when desktop the sider agent list too long
2025-10-21 00:22:30 +08:00
Rylan Cai f599617042 🔨 chore: remove logs in message action (#9800)
🔥 chore: remove logs in message action
2025-10-20 23:58:55 +08:00
YuTengjing cc8f5d0639 🔨 chore: improve bug report template with validations and client type field (#9795)
* 🔧 feat: improve bug report template with client type field

- Rename Platform to Deployment Platform and make it optional for Desktop App users
- Fix Deployment Mode typo and improve formatting
- Add Client Type field to distinguish Web/Desktop/Mobile access methods
- Improve overall template formatting with better spacing

* 🔧 feat: add validations and PR willingness to bug report template

- Add "Willing to Submit a PR?" dropdown field to encourage contributions
- Add validation checkboxes to ensure quality issue reports
- Include checks for docs reading, duplicate issues, and concrete bugs

* ♻️ refactor: reorder bug report fields to prioritize client type

- Move Client Type to the first field for better categorization
- Move Operating System to second field
- Prioritize platform-related fields before deployment details
2025-10-20 23:54:58 +08:00
jjangga0214 32d365b544 🔨 chore(i18n): rm {{systemRole}} from ko-KR (#9793)
🐛 fix(i18n): rm {{systemRole}} from ko-KR
2025-10-20 16:10:23 +08:00
renovate[bot] 0ff41dba9c Update dependency @opentelemetry/instrumentation to ^0.206.0 (#9772)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 15:04:15 +08:00
renovate[bot] fd68cc364c Update dependency @opentelemetry/sdk-node to ^0.206.0 (#9774)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 15:04:05 +08:00
lobehubbot ce7a74242f 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-20 02:56:09 +00:00
semantic-release-bot 7f9bfda652 🔖 chore(release): v1.139.1 [skip ci]
### [Version&nbsp;1.139.1](https://github.com/lobehub/lobe-chat/compare/v1.139.0...v1.139.1)
<sup>Released on **2025-10-20**</sup>

#### ♻ Code Refactoring

- **i18n**: Rm qa.

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **i18n**: Rm qa, closes [#9783](https://github.com/lobehub/lobe-chat/issues/9783) ([6d14dfe](https://github.com/lobehub/lobe-chat/commit/6d14dfe))

#### Styles

* **misc**: Update i18n, closes [#9787](https://github.com/lobehub/lobe-chat/issues/9787) ([b43d4b2](https://github.com/lobehub/lobe-chat/commit/b43d4b2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-20 02:54:52 +00:00
jjangga0214 5f10aace5b 👷 build(deps): add transitive phantom dependencies (#9784) 2025-10-20 10:44:35 +08:00
jjangga0214 6d14dfe50d ♻️ refactor(i18n): rm qa (#9783) 2025-10-20 10:42:33 +08:00
sxjeru 12f54759df 🔨 chore: Enable webpackBuildWorker to optimize build memory (#9350)
Enable webpackBuildWorker in next.config.ts
2025-10-20 10:40:53 +08:00
LobeHub Bot b43d4b27ad 🤖 style: update i18n (#9787)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-20 10:32:04 +08:00
lobehubbot dab987f64b 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-19 12:06:08 +00:00
semantic-release-bot 2e24f72b2a 🔖 chore(release): v1.139.0 [skip ci]
## [Version&nbsp;1.139.0](https://github.com/lobehub/lobe-chat/compare/v1.138.5...v1.139.0)
<sup>Released on **2025-10-19**</sup>

####  Features

- **misc**: Support image generation for siliconcloud.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support image generation for siliconcloud, closes [#9447](https://github.com/lobehub/lobe-chat/issues/9447) ([5ebcfa5](https://github.com/lobehub/lobe-chat/commit/5ebcfa5))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-19 12:05:00 +00:00
WangYK 5ebcfa531e feat: support image generation for siliconcloud (#9447)
Co-authored-by: YuTengjing <ytj2713151713@gmail.com>
2025-10-19 19:54:11 +08:00
sxjeru 1b3f88346b 🔨 chore: Update HF provider support (#9751)
* feat: update Hugging Face integration with new model fetching logic and pricing support

* fix: format adjustments in convertOpenAIMessagesToHFFormat function and update max_tokens handling

* refactor: remove enabled property from huggingfaceChatModels and clean up related tests

* feat: 添加 Qwen3-VL-8B-Instruct 和 Qwen3-VL-8B-Thinking 模型,支持视觉理解和推理

* feat: 实现 OpenAI 消息格式转换为 Hugging Face 格式,并添加相关单元测试
2025-10-19 15:29:42 +08:00
renovate[bot] 808cb21b99 Update pnpm to v10.18.3 (#9770)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-19 15:26:09 +08:00
lobehubbot c6dd22eea6 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-18 09:21:51 +00:00
semantic-release-bot da5427ccdf 🔖 chore(release): v1.138.5 [skip ci]
### [Version&nbsp;1.138.5](https://github.com/lobehub/lobe-chat/compare/v1.138.4...v1.138.5)
<sup>Released on **2025-10-18**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor upload router into lambda and decide to remove it in V2.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Refactor upload router into lambda and decide to remove it in V2, closes [#9766](https://github.com/lobehub/lobe-chat/issues/9766) ([d1c7f41](https://github.com/lobehub/lobe-chat/commit/d1c7f41))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-18 09:20:47 +00:00
Arvin Xu d1c7f41bd4 ♻️ refactor: refactor upload router into lambda and decide to remove it in V2 (#9766)
refactor edge router and decide to remove it in V2
2025-10-18 17:09:55 +08:00
lobehubbot 6681989fe6 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-18 08:50:51 +00:00
semantic-release-bot 48a7a08110 🔖 chore(release): v1.138.4 [skip ci]
### [Version&nbsp;1.138.4](https://github.com/lobehub/lobe-chat/compare/v1.138.3...v1.138.4)
<sup>Released on **2025-10-18**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix response API tools calling issue.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix response API tools calling issue, closes [#9760](https://github.com/lobehub/lobe-chat/issues/9760) ([0596692](https://github.com/lobehub/lobe-chat/commit/0596692))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-18 08:49:45 +00:00
Arvin Xu 0596692e95 🐛 fix: fix response API tools calling issue (#9760)
* fix response tools calling

* add log

* fix

* fix tests

* fix all text and lint and types

* refactor google context builder

* add tests
2025-10-18 16:39:22 +08:00
bbbugg 59ea77d746 test: add search unit test cases for merging the user model with the base model (#9764)
test: add search unit test cases for merging the user model with the base model.
2025-10-18 15:28:35 +08:00
lobehubbot 4aa570c904 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-18 05:48:48 +00:00
semantic-release-bot c9ea5336d3 🔖 chore(release): v1.138.3 [skip ci]
### [Version&nbsp;1.138.3](https://github.com/lobehub/lobe-chat/compare/v1.138.2...v1.138.3)
<sup>Released on **2025-10-18**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix topic fetch not correct in custom agent.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix topic fetch not correct in custom agent, closes [#9761](https://github.com/lobehub/lobe-chat/issues/9761) ([ceffce2](https://github.com/lobehub/lobe-chat/commit/ceffce2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-18 05:47:20 +00:00
Arvin Xu ceffce27c3 🐛 fix: fix topic fetch not correct in custom agent (#9761)
* fix topic issue

* fix tests
2025-10-18 13:34:07 +08:00
bbbugg b56c9c51b8 🐛fix: update search settings handling based on explicit model search abilities (#9757) 2025-10-18 13:01:03 +08:00
Arvin Xu 97a6c8e172 🔨 chore: refactor the prompt engineering (#9744)
refactor the prompt engineering
2025-10-17 22:19:26 +08:00
renovate[bot] 727c92ad5b Update dependency @opentelemetry/auto-instrumentations-node to ^0.65.0 (#9669)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 17:27:40 +08:00
renovate[bot] 25e45a4cd4 Update dependency @opentelemetry/exporter-trace-otlp-http to ^0.206.0 (#9672)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 17:27:33 +08:00
renovate[bot] 296bad9fa4 Update dependency @opentelemetry/instrumentation-pg to ^0.59.0 (#9459)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 16:43:21 +08:00
renovate[bot] d8d28371d6 Update ghcr.io/grafana/xk6-client-tracing Docker tag to v0.0.9 (#9666)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 16:14:08 +08:00
renovate[bot] a588319cf1 Update dependency @opentelemetry/exporter-metrics-otlp-http to ^0.206.0 (#9670)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-17 16:13:35 +08:00
lobehubbot 527ab0add7 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-16 18:28:58 +00:00
semantic-release-bot f2230ae536 🔖 chore(release): v1.138.2 [skip ci]
### [Version&nbsp;1.138.2](https://github.com/lobehub/lobe-chat/compare/v1.138.1...v1.138.2)
<sup>Released on **2025-10-16**</sup>

#### 💄 Styles

- **misc**: Improve welcome message.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve welcome message, closes [#9747](https://github.com/lobehub/lobe-chat/issues/9747) ([c83fe13](https://github.com/lobehub/lobe-chat/commit/c83fe13))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-16 18:27:52 +00:00
Arvin Xu c83fe13d7c 💄 style: improve welcome message (#9747)
* add feishu

* update i18n

* refactor

* rename

* rename
2025-10-17 02:16:45 +08:00
lobehubbot f4b9d6795b 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-16 16:52:22 +00:00
semantic-release-bot 77bc2614a4 🔖 chore(release): v1.138.1 [skip ci]
### [Version&nbsp;1.138.1](https://github.com/lobehub/lobe-chat/compare/v1.138.0...v1.138.1)
<sup>Released on **2025-10-16**</sup>

#### 🐛 Bug Fixes

- **misc**: Automatic topic creation switch does not work.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Automatic topic creation switch does not work, closes [#9693](https://github.com/lobehub/lobe-chat/issues/9693) ([a02b301](https://github.com/lobehub/lobe-chat/commit/a02b301))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-16 16:51:11 +00:00
sxjeru a02b301904 🐛 fix: Automatic topic creation switch does not work (#9693)
*  feat: 添加自动创建主题功能,支持根据消息数量和配置阈值决定是否创建新主题

* typo

* fix test

* 删除 GLM-4.6 模型的定义

*  feat: 添加 Ring-1T 和 Ling-1T 模型定义,扩展聊天模型库

*  feat: 添加 Qwen3 VL 模型,扩展聊天模型库并更新 Vercel AI Gateway 模型定价

* fix test
2025-10-17 00:41:02 +08:00
Arvin Xu 846a7a5986 🔨 chore: support Feishu OAuth Provider (#9745)
* feat: 添加 Feishu 作为新的 OAuth 认证提供者,并更新相关配置

* docs: 添加 Feishu 应用的环境变量配置说明

* refactor: 移除 Feishu 相关的环境变量配置,对齐 auth.js 环境变量规范

* improve docs

* add feishu

---------

Co-authored-by: 赵远景 <zhaoyuanjing@shouqianba.com>
2025-10-17 00:17:19 +08:00
lobehubbot bc7aa88a8f 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-16 14:28:33 +00:00
semantic-release-bot 85c4553821 🔖 chore(release): v1.138.0 [skip ci]
## [Version&nbsp;1.138.0](https://github.com/lobehub/lobe-chat/compare/v1.137.10...v1.138.0)
<sup>Released on **2025-10-16**</sup>

####  Features

- **misc**: Support Group Chat, Mention, and Multi-Agent Orchestration with feature flag.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support Group Chat, Mention, and Multi-Agent Orchestration with feature flag, closes [#8976](https://github.com/lobehub/lobe-chat/issues/8976) ([03c2838](https://github.com/lobehub/lobe-chat/commit/03c2838))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-16 14:27:25 +00:00
René Wang 03c28385e5 feat: support Group Chat, Mention, and Multi-Agent Orchestration with feature flag (#8976)
* feat: implement group chat functionality

* fix

* update

* revert pglite mock

* fix: Add missing test

* fix mention

* fix mention

* lint: Clear unused varibles

* fix: type check

* fix: Coverage

* build: Add missing test

* fix: add mention back

* fix: Add missing test

* fix: Add test for topic

* feat: Group chat fallback style

* fix: Revert unncessary files

* fix: circular deps

* feat: tool usage

* fix: Replace debug info

* feat: Update i18n

* opti: Better prompr

* fix claude

* feat: Filter model without function calling

* fix: DM reduction

* lint: Address build error

* fix: Test error

* feat: Store model info

* style: Clean up welcome messaeg

* feat: Use new welcome message

* fix: inbox not working

* fix: inbox not working

* fix: type error

* feat: Optimize prompt

* fix: Revert unintentional changes

* lint: Remove unused code

* fix: better test

* fix: Use debug

* refact: Move normalization postion

* opti: Better prompt

* opti: Better prompt

* opti: Better prompt

* lint: Clear console.log

* fix: Update test snap

* fix: test error

* fix: Unexpectly test fail

---------

Co-authored-by: arvinxx <arvinx@foxmail.com>
2025-10-16 22:16:40 +08:00
lobehubbot e525cb2ed6 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-16 14:02:46 +00:00
semantic-release-bot ea44ef1b4c 🔖 chore(release): v1.137.10 [skip ci]
### [Version&nbsp;1.137.10](https://github.com/lobehub/lobe-chat/compare/v1.137.9...v1.137.10)
<sup>Released on **2025-10-16**</sup>

#### 💄 Styles

- **misc**: Add Claude Haiku 4.5 model.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add Claude Haiku 4.5 model, closes [#9735](https://github.com/lobehub/lobe-chat/issues/9735) ([1cfbc87](https://github.com/lobehub/lobe-chat/commit/1cfbc87))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-16 14:01:35 +00:00
sxjeru 1cfbc878bd 💄 style: Add Claude Haiku 4.5 model (#9735)
* feat: 添加 Claude Haiku 4.5 模型及其相关配置

* fix: 修复模型 ID 格式并移除 Llama 4 Maverick 模型

* 添加 doubao-seed-1.6-lite;更新模型价格区间格式,调整价格单位为百万 tokens

* fix: Invalid combination of reasoning_effort and thinking type: low + disabled

* feat: 添加 Qwen3 VL Flash 和 Qwen3 Coder 30B A3B 模型,更新定价和发布信息
2025-10-16 21:50:32 +08:00
Rdmclin2 61bbd596f0 🔨 chore: mobile related server implementation pick from mobile app (#9691)
* server: sync from feat/mobile-app (exclude apps/mobile)

* Update package.json

* chore(mobile): update mobile router imports to use lambda

* chore(mobile): refactor mobile router

* chore: format tsconfig.json

* chore(mobile): simplify mobile router

---------

Co-authored-by: Arvin Xu <arvinx@foxmail.com>
Co-authored-by: Tsuki <976499226@qq.com>
2025-10-16 11:32:58 +07:00
lobehubbot 6508e2fcaf 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-15 16:12:31 +00:00
semantic-release-bot 7cef9d88ea 🔖 chore(release): v1.137.9 [skip ci]
### [Version&nbsp;1.137.9](https://github.com/lobehub/lobe-chat/compare/v1.137.8...v1.137.9)
<sup>Released on **2025-10-15**</sup>

#### 💄 Styles

- **misc**: Improve update notification.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve update notification, closes [#9717](https://github.com/lobehub/lobe-chat/issues/9717) ([16de38a](https://github.com/lobehub/lobe-chat/commit/16de38a))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-15 16:11:22 +00:00
Arvin Xu 16de38ae32 💄 style: improve update notification (#9717)
* refactor tool source

* Revert "refactor tool source"

This reverts commit a867118a52.

* improve update notification

* improve locale
2025-10-16 00:00:35 +08:00
lobehubbot c6257f1dba 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-15 14:51:34 +00:00
semantic-release-bot b2c28d0810 🔖 chore(release): v1.137.8 [skip ci]
### [Version&nbsp;1.137.8](https://github.com/lobehub/lobe-chat/compare/v1.137.7...v1.137.8)
<sup>Released on **2025-10-15**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix duplicate tools id issue and fix link dialog issue.

#### 💄 Styles

- **misc**: Add region support for Vertex AI provider.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix duplicate tools id issue and fix link dialog issue, closes [#9731](https://github.com/lobehub/lobe-chat/issues/9731) ([0a8c80d](https://github.com/lobehub/lobe-chat/commit/0a8c80d))

#### Styles

* **misc**: Add region support for Vertex AI provider, closes [#9720](https://github.com/lobehub/lobe-chat/issues/9720) ([d17b50c](https://github.com/lobehub/lobe-chat/commit/d17b50c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-15 14:50:23 +00:00
Arvin Xu d17b50c6dc 💄 style: add region support for Vertex AI provider (#9720)
 feat: add region support for Vertex AI provider

- Add VertexAIKeyVault interface with region support
- Update UI to include region selector with 35+ regions
- Add vertexAIRegion field to ClientSecretPayload
- Update backend to use user-selected region with fallback
- Add i18n support for English and Chinese
- Fix issue with Gemini 2.5 models requiring global region
2025-10-15 22:39:36 +08:00
Arvin Xu 0a8c80dfd2 🐛 fix: fix duplicate tools id issue and fix link dialog issue (#9731)
* add

* baseline

*  test(store): add tests for discover store plugin and mcp slices

- Add comprehensive tests for discover/slices/plugin/action.ts (15 tests)
- Add comprehensive tests for discover/slices/mcp/action.ts (11 tests)
- Update test-coverage.md with new metrics and completed work
- Coverage: 74.24% overall (+26 tests, 2 new test files)
- Action files coverage: 29/40 tested (72.5%, +2 files)

Features tested:
- Plugin/MCP categories, detail, identifiers, and list fetching
- SWR key generation with locale and parameters
- SWR configuration verification
- Service integration with discoverService

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* 📝 docs(testing): add SWR hooks testing guide and subagent workflow

Testing Guide Updates:
- Add comprehensive SWR hooks testing section with examples
- Document key differences from regular action tests
- Add examples for testing SWR key generation and configuration
- Add examples for testing conditional fetching
- Update references to include SWR hook test examples

Test Coverage Guide Updates:
- Add detailed subagent workflow for parallel testing
- Document when and how to use subagents for testing
- Add complete workflow example using subagents
- Add benefits and best practices for subagent usage
- Clarify that subagents should NOT commit or update docs
- Add step-by-step guide for launching parallel subagents

Key improvements:
- Better documentation for testing SWR-based store actions
- Clear workflow for efficient parallel testing using subagents
- Single atomic commit strategy after all subagents complete
- Improved testing efficiency and organization

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* ♻️ refactor(test): fix SWR mock strategy to properly test fetcher

Previously, tests were hardcoding return values instead of calling
the fetcher function. This bypassed the actual service call logic.

Changes:
- Fix useSWR mock to call fetcher and return its Promise
- Update assertions to await Promise results
- Update testing guide with correct mock pattern
- Add explanation of why this approach is correct

Before (incorrect):
```typescript
useSWRMock.mockImplementation(((key, fetcher) => {
  fetcher?.(); // Call but ignore result
  return { data: mockData }; // Hardcoded
}) as any);
expect(result.current.data).toEqual(mockData);
```

After (correct):
```typescript
useSWRMock.mockImplementation(((key, fetcher) => {
  const data = fetcher?.(); // Get Promise from fetcher
  return { data }; // Return Promise
}) as any);
const resolvedData = await result.current.data;
expect(resolvedData).toEqual(mockData);
```

Benefits:
-  Actually tests the fetcher function
-  Mirrors real SWR behavior (data is Promise)
-  Service calls are properly verified
-  Tests are more accurate and maintainable

Updated files:
- .cursor/rules/testing-guide/zustand-store-action-test.mdc
- src/store/discover/slices/plugin/action.test.ts
- src/store/discover/slices/mcp/action.test.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* 🐛 fix(test): correct SWR mock strategy to match project standards

- Remove useSWR mocking, use real SWR implementation instead
- Only mock service methods (fetchers) with vi.spyOn
- Use waitFor for async assertions
- Update testing guide with correct SWR pattern
- Add reference to src/store/chat/slices/message/action.test.ts

This fixes the incorrect mocking approach from previous commits.
All 13 tests pass with the corrected strategy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

*  test(store): add comprehensive tests for high priority action files

- Add mcpStore action tests (41 tests, 624 LOC covered)
  - MCP plugin installation flow (normal, resume, dependencies, config)
  - Connection testing (HTTP and STDIO)
  - Plugin lifecycle management
  - Error handling and cancellation flows

- Add fileManager action tests (35 tests, 205 LOC covered)
  - File upload and processing workflows
  - Chunk embedding and parsing
  - File list management and refresh
  - SWR data fetching

Testing approach:
- Used parallel subagents for efficient development
- Followed zustand testing patterns from guide
- Proper test layering and per-test mocking
- All tests pass type-check and lint

Coverage improvement: 74.24% → ~76% (+76 tests, 2 files)
Action files: 29/40 → 31/40 tested (77.5%)

🏆 Milestone: All high priority files (>200 LOC) now have tests!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

*  test(store): complete 100% action file coverage with 160 new tests

Added comprehensive tests for all remaining 9 medium-priority action files:

Discovery Store (33 tests):
- assistant/action.ts: 10 tests (SWR hooks, categories, detail, identifiers, list)
- provider/action.ts: 11 tests (SWR hooks, detail with readme, identifiers, list with filters)
- model/action.ts: 12 tests (SWR hooks, categories, detail, identifiers, list with params)

Knowledge Base Store (29 tests):
- crud/action.ts: 19 tests (create, update, remove, refresh, loading states, SWR hooks)
- content/action.ts: 10 tests (add files, remove files, error handling)

File Store (36 tests):
- upload/action.ts: 18 tests (base64 upload, file upload with progress, type detection, KB integration)
- chunk/action.ts: 18 tests (drawer management, highlight, semantic search)

AI Infrastructure Store (23 tests):
- aiModel/action.ts: 23 tests (CRUD, batch operations, remote sync, toggle enabled, SWR hooks)

Chat Store (39 tests):
- thread/action.ts: 39 tests (CRUD, messaging, AI title generation, validation, loading states)

Testing approach:
- Used 9 parallel subagents for efficient development
- Followed zustand testing patterns from guide
- SWR hook testing for discovery slices
- Complex async flows with proper error handling
- File operations with progress callbacks
- Semantic search and RAG integration

Coverage improvement: ~76% → ~80% (+160 tests, 9 files)
Action files: 31/40 → 40/40 tested (100%)

🎉 MILESTONE: All 40 action files now have comprehensive test coverage!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix test

* fix test

* fix context-engine

* add tests

* remove

* remove tools bar

* pin bun version

* fix

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-15 22:38:53 +08:00
lobehubbot 6fd337de18 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-15 12:58:39 +00:00
semantic-release-bot 6266a85cf1 🔖 chore(release): v1.137.7 [skip ci]
### [Version&nbsp;1.137.7](https://github.com/lobehub/lobe-chat/compare/v1.137.6...v1.137.7)
<sup>Released on **2025-10-15**</sup>

#### 💄 Styles

- **misc**: Use different favicon.ico in dev mode.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Use different favicon.ico in dev mode, closes [#9723](https://github.com/lobehub/lobe-chat/issues/9723) ([2f7317b](https://github.com/lobehub/lobe-chat/commit/2f7317b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-15 12:57:27 +00:00
René Wang 2f7317b98f 🔨 style: Use different favicon.ico in dev mode (#9723) 2025-10-15 14:47:37 +02:00
Arvin Xu 317df489ce test(store): refactor generateAIChatV2 tests following V1 patterns (#9714)
- Introduce shared test helpers and fixtures
- Use TEST_IDS and TEST_CONTENT constants instead of hardcoded strings
- Organize tests by functionality (validation, message creation, RAG integration, error handling)
- Remove commented-out test code
- Maintain V2-specific features (isServerMode, aiChatService mock)
- All 28 tests passing
2025-10-14 23:48:06 +08:00
Arvin Xu ff41f4bb82 🔨 chore: improve Claude Code triage workflow (#9713)
* 🔨 chore: unify Claude Code workflows to use claude-code-action@main

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* 🔧 chore: extract issue triage prompt to separate file

- Add .claude/prompts/issue-triage.md with comprehensive triage guide
- Update workflow to read from issue-triage.md instead of inline prompt
- Simplify workflow configuration for better maintainability
- Add provider detection rules including aihubmix

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-10-14 23:05:25 +08:00
Arvin Xu 60f43d90e5 🔨 chore: unify Claude Code workflows to use claude-code-action@main (#9712)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-10-14 21:49:52 +08:00
lobehubbot b8136ac17b 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 13:43:35 +00:00
semantic-release-bot f0e387fa29 🔖 chore(release): v1.137.6 [skip ci]
### [Version&nbsp;1.137.6](https://github.com/lobehub/lobe-chat/compare/v1.137.5...v1.137.6)
<sup>Released on **2025-10-14**</sup>

#### 🐛 Bug Fixes

- **misc**: Update Claude workflows to use oauth token, vertext ai create image.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Update Claude workflows to use oauth token, closes [#9711](https://github.com/lobehub/lobe-chat/issues/9711) ([8dcb00e](https://github.com/lobehub/lobe-chat/commit/8dcb00e))
* **misc**: Vertext ai create image, closes [#9710](https://github.com/lobehub/lobe-chat/issues/9710) ([790d8fd](https://github.com/lobehub/lobe-chat/commit/790d8fd))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 13:42:24 +00:00
Arvin Xu 8dcb00e2c9 🔧 fix: update Claude workflows to use oauth token (#9711) 2025-10-14 21:31:25 +08:00
YuTengjing 790d8fd498 🐛 fix: vertext ai create image (#9710) 2025-10-14 21:26:26 +08:00
Arvin Xu 3719bf4d52 🔨 chore: add claude code powered workflows (#9709)
* add claude dedupe issue workflow

* add triage

* add auto close duplicate

* improve triage

* improve
2025-10-14 21:24:26 +08:00
Arvin Xu bba23bf5a3 test: refactor aiChat tests (#9708)
* refactor tests

* ♻️ refactor(test): improve test layering and fix type errors

Improved test architecture by fixing test layer violations:

1. **internal_coreProcessMessage tests**: Changed to spy on direct dependency `internal_fetchAIChatMessage` instead of lower-level `chatService.createAssistantMessageStream`

2. **internal_fetchAIChatMessage tests**: Fixed to mock correct service (`chatService` instead of global `fetch`)

3. **beforeEach cleanup**: Removed global `spyOnChatService()` to reduce implicit coupling - tests now spy `chatService` only when needed

4. **Type fixes**:
   - Fixed mockResolvedValue() to include required undefined arguments
   - Fixed ChatMessageError type to include required `type` property
   - Fixed MessageDispatch type guard for safer property access

Benefits:
 Clear test layers - each test only spies on direct dependencies
 Correct mocks matching actual implementation
 Coverage improved from 81.48% to 82.03%
 All 52 tests passing with no type errors
2025-10-14 20:45:14 +08:00
lobehubbot 509619ba15 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 10:27:29 +00:00
semantic-release-bot 5de9e9fea3 🔖 chore(release): v1.137.5 [skip ci]
### [Version&nbsp;1.137.5](https://github.com/lobehub/lobe-chat/compare/v1.137.4...v1.137.5)
<sup>Released on **2025-10-14**</sup>

#### 💄 Styles

- **misc**: Add imagen model to vertex ai.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add imagen model to vertex ai, closes [#9699](https://github.com/lobehub/lobe-chat/issues/9699) ([3b2a2c1](https://github.com/lobehub/lobe-chat/commit/3b2a2c1))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 10:26:28 +00:00
YuTengjing 3b2a2c1c54 💄 style: add imagen model to vertex ai (#9699)
* ♻️ refactor: rename isLocalUrl to isDesktopLocalStaticServerUrl

Rename the function to better reflect its specific purpose of checking
desktop local static server URLs (127.0.0.1 only). Update all usages
across the codebase including imports, function calls, and test cases.

*  feat(model-bank): add Vertex AI image generation models

- Add Nano Banana (Gemini 2.5 Flash Image) models
- Add Imagen 4 series (Standard, Ultra, Fast, Preview variants)
- Export shared parameters for reuse across providers

*  test(context-engine): fix mock after function rename

Update test mock from isLocalUrl to isDesktopLocalStaticServerUrl

* ♻️ refactor: use submodule imports for @lobechat/utils

- Change from barrel imports to direct submodule imports
- Update test to mock only necessary functions (imageUrlToBase64)
- Fix test URL from localhost to 127.0.0.1 for isDesktopLocalStaticServerUrl
- Update package.json exports for utils submodules

*  test: update mocks after function rename

Update test mocks from isLocalUrl to isDesktopLocalStaticServerUrl

*  test(chat): fix mocks to use submodule imports
2025-10-14 18:15:58 +08:00
lobehubbot c07d900648 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 07:52:51 +00:00
semantic-release-bot 938cf6ba71 🔖 chore(release): v1.137.4 [skip ci]
### [Version&nbsp;1.137.4](https://github.com/lobehub/lobe-chat/compare/v1.137.3...v1.137.4)
<sup>Released on **2025-10-14**</sup>

#### 🐛 Bug Fixes

- **misc**: Prevent Vertex AI JSON credentials from being split by comma.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Prevent Vertex AI JSON credentials from being split by comma, closes [#9703](https://github.com/lobehub/lobe-chat/issues/9703) [#9477](https://github.com/lobehub/lobe-chat/issues/9477) ([189081d](https://github.com/lobehub/lobe-chat/commit/189081d))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 07:51:42 +00:00
Arvin Xu 189081d546 🐛 fix: prevent Vertex AI JSON credentials from being split by comma (#9703)
- Add special case for VertexAI in getProviderAuthPayload to skip random API key selection
- Vertex AI uses JSON format credentials that contain commas, which should not be split
- Add test case to verify JSON credentials remain intact

Fixes the issue introduced in #9477 where random API key splitting broke Vertex AI authentication

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-10-14 15:00:44 +08:00
bbbugg 96e2ccd3fb 🐛fix(mobile): enable conditional fetching of AI provider list based on mobile view (#9621)
🐛fix: enable conditional fetching of AI provider list based on mobile view
2025-10-14 13:35:23 +08:00
Arvin Xu 8693d95e0d 🔨 chore: add e2e workflow (#9677)
* add e2e test

* Potential fix for code scanning alert no. 137: Workflow does not contain permissions

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* remove

* update

* fix tests

* add e2e

* update

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-10-14 13:32:52 +08:00
lobehubbot 543db87745 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 03:10:43 +00:00
semantic-release-bot e31e7b5d2d 🔖 chore(release): v1.137.3 [skip ci]
### [Version&nbsp;1.137.3](https://github.com/lobehub/lobe-chat/compare/v1.137.2...v1.137.3)
<sup>Released on **2025-10-14**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix mcp server connect issue and refactor web search implement, fix tools calling long name length >64 issue.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix mcp server connect issue and refactor web search implement, closes [#9694](https://github.com/lobehub/lobe-chat/issues/9694) ([15ebcb4](https://github.com/lobehub/lobe-chat/commit/15ebcb4))
* **misc**: Fix tools calling long name length >64 issue, closes [#9697](https://github.com/lobehub/lobe-chat/issues/9697) ([cb98604](https://github.com/lobehub/lobe-chat/commit/cb98604))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 03:09:37 +00:00
Arvin Xu 15ebcb414b 🐛 fix: fix mcp server connect issue and refactor web search implement (#9694)
* add

* clean

* refactor

* refactor

* add test

* fix style

* refactor to improve search performance

* refactor types

* refactor types

* refactor types

* fix mcp retry issue

* add more tests

* fix test and types

* fix test

* fix desktop remote streamable http

* add local

* fix tests

* update
2025-10-14 10:58:47 +08:00
Arvin Xu cb986040d5 🐛 fix: fix tools calling long name length >64 issue (#9697)
* add ToolNameResolver

* fix ToolName generate tests

* fix tests
2025-10-14 10:54:40 +08:00
lobehubbot ad0fae3c2a 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 02:28:05 +00:00
semantic-release-bot 79955e8f8d 🔖 chore(release): v1.137.2 [skip ci]
### [Version&nbsp;1.137.2](https://github.com/lobehub/lobe-chat/compare/v1.137.1...v1.137.2)
<sup>Released on **2025-10-14**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix the Worker URL cross-origin issue.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix the Worker URL cross-origin issue, closes [#9624](https://github.com/lobehub/lobe-chat/issues/9624) ([d379112](https://github.com/lobehub/lobe-chat/commit/d379112))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 02:27:04 +00:00
peerless-hero d379112d74 🐛 fix: fix the Worker URL cross-origin issue (#9624)
When the origin of the Worker script is different from the current page, reconstruct the URL relative to the current origin to avoid cross-origin errors.
2025-10-14 10:16:33 +08:00
lobehubbot c1a0868ac3 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-14 02:10:13 +00:00
semantic-release-bot af71a7933a 🔖 chore(release): v1.137.1 [skip ci]
### [Version&nbsp;1.137.1](https://github.com/lobehub/lobe-chat/compare/v1.137.0...v1.137.1)
<sup>Released on **2025-10-14**</sup>

#### 💄 Styles

- **misc**: Change the user chatItem maxWidth should use flex 1.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Change the user chatItem maxWidth should use flex 1, closes [#9689](https://github.com/lobehub/lobe-chat/issues/9689) ([cfd5221](https://github.com/lobehub/lobe-chat/commit/cfd5221))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-14 02:08:56 +00:00
Shinji-Li cfd52210d5 💄 style: change the user chatItem maxWidth should use flex 1 (#9689)
* feat: change the user chatitem max flex 1

* feat: add when editing not show actions
2025-10-14 09:58:39 +08:00
iBenzene 166d715f17 🔨 chore: add Clerk authentication related variables and some other variables in Dockerfile (#9640)
* chore(docker): add Clerk authentication related variables in Dockerfile

* chore(docker): add ENABLED_FAL, S3_ENABLE_PATH_STYLE, and S3_SET_ACL environment variables to Dockerfile

* chore(docker): add CLERK_WEBHOOK_SECRET environment variable

* revert(docker): fix indentation that was mistakenly modified due to. vscode settings

* chore(docker): add ENABLE_AUTH_PROTECTION environment variable to Dockerfiles

---------

Co-authored-by: bbbugg <daming20120101@163.com>
2025-10-14 09:53:43 +08:00
Arvin Xu 1238d7fbd5 🔨 chore: output JSON support tools calling mode (#9696)
* support tools calling in generateObject method

* fix tests
2025-10-14 09:51:38 +08:00
Arvin Xu 06507fea7f test: add model runtime testing (#9683)
* baseline test docs

* batch 2

* add more tests

* baseline 2

* baseline 3

* baseline 4

* baseline 5

* baseline 6

* baseline 7

* add core tests

* fix tests
2025-10-13 10:18:13 +08:00
Arvin Xu 4b7e838008 test: add tests for agent-runtime and mode-runtime (#9682)
* update agent runtime

* update agent runtime

* add model runtime tests

* add tests

* add more tests

* add more tests
2025-10-13 00:09:07 +08:00
lobehubbot 9e09099313 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-12 16:07:27 +00:00
semantic-release-bot e2c5e4f5a6 🔖 chore(release): v1.137.0 [skip ci]
## [Version&nbsp;1.137.0](https://github.com/lobehub/lobe-chat/compare/v1.136.13...v1.137.0)
<sup>Released on **2025-10-12**</sup>

####  Features

- **misc**: Add new setting for default image num.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add new setting for default image num, closes [#9618](https://github.com/lobehub/lobe-chat/issues/9618) ([de7368b](https://github.com/lobehub/lobe-chat/commit/de7368b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-12 16:06:18 +00:00
YuTengjing de7368bc25 feat: add new setting for default image num (#9618) 2025-10-12 23:56:17 +08:00
Arvin Xu 941b6ec4cb test: add tests for context-engine (#9679) 2025-10-12 16:31:15 +02:00
Arvin Xu 3cf7df5748 test: add tests for file-loaders packages (#9678)
add tests
2025-10-12 19:43:08 +08:00
lobehubbot d0e2acac13 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-12 08:22:18 +00:00
semantic-release-bot 7a16e7e539 🔖 chore(release): v1.136.13 [skip ci]
### [Version&nbsp;1.136.13](https://github.com/lobehub/lobe-chat/compare/v1.136.12...v1.136.13)
<sup>Released on **2025-10-12**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix input cannot send markdown.

#### 💄 Styles

- **misc**: Optimize OpenRouter modelFetch endpoint, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix input cannot send markdown, closes [#9674](https://github.com/lobehub/lobe-chat/issues/9674) ([2518d7e](https://github.com/lobehub/lobe-chat/commit/2518d7e))

#### Styles

* **misc**: Optimize OpenRouter modelFetch endpoint, closes [#9671](https://github.com/lobehub/lobe-chat/issues/9671) ([0038a64](https://github.com/lobehub/lobe-chat/commit/0038a64))
* **misc**: Update i18n, closes [#9665](https://github.com/lobehub/lobe-chat/issues/9665) ([02096ea](https://github.com/lobehub/lobe-chat/commit/02096ea))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-12 08:20:59 +00:00
Arvin Xu 2518d7eabf 🐛 fix: fix input cannot send markdown (#9674)
* fix claude json output

* refactor to remove langchain in file-loaders

* support deepseek tools calling

* use peer

* use peer

* move files

* fix test

* add local-system placeholder

* fix markdown editing

* fix markdown editing

* refactor doc parse
2025-10-12 16:11:02 +08:00
sxjeru 0038a64819 💄 style: Optimize OpenRouter modelFetch endpoint (#9671)
* 🔧 refactor: Update OpenRouter API endpoint and enhance model data structure

* 🐛 fix: 修正模型名称处理逻辑以避免不必要的前缀去除
2025-10-12 16:07:30 +08:00
LobeHub Bot 02096ea82d 🤖 style: update i18n (#9665)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-12 16:07:23 +08:00
Stella Wang d330cf4e94 🔨 chore(ci): Improve Claude translator prompt to prevent hallucination (#9673)
🐛 fix(ci): Improve Claude translator prompt to prevent hallucination

Add critical rules to prevent translation hallucination issues:
- Enforce exact preservation of original content without modification
- Ensure code blocks, error logs, and JSON appear in both sections
- Clarify that only natural language should be translated
- Add verification step to check original content accuracy
- Prevent dropping or hallucinating end-of-comment sentences

This addresses issues where the translator was hallucinating content
in the "Original Content" section and incorrectly handling large
technical blocks like error logs and JSON structures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-12 16:03:52 +08:00
renovate[bot] 5c2e1faa94 Update pnpm to v10.18.2 (#9667) 2025-10-12 05:05:20 +02:00
lobehubbot 590a9275ce 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 17:44:30 +00:00
semantic-release-bot 9242e83824 🔖 chore(release): v1.136.12 [skip ci]
### [Version&nbsp;1.136.12](https://github.com/lobehub/lobe-chat/compare/v1.136.11...v1.136.12)
<sup>Released on **2025-10-11**</sup>

#### 💄 Styles

- **misc**: Add more AWS regions, Update infini-ai models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add more AWS regions, closes [#9644](https://github.com/lobehub/lobe-chat/issues/9644) ([4a82daf](https://github.com/lobehub/lobe-chat/commit/4a82daf))
* **misc**: Update infini-ai models, closes [#9646](https://github.com/lobehub/lobe-chat/issues/9646) ([5274225](https://github.com/lobehub/lobe-chat/commit/5274225))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 17:43:26 +00:00
sxjeru 52742254d6 💄 style: Update infini-ai models (#9646)
* fix: update models and refactor InfiniAI model handling

* fix: enable models and update InfiniAI configuration

* fix: update DeepSeek model versions and descriptions in modelscope
2025-10-12 01:32:11 +08:00
edwnhoffmann 4a82daf329 💄 style: add more AWS regions (#9644)
add more regions
2025-10-12 01:32:05 +08:00
lobehubbot 84baabc1b4 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 17:29:08 +00:00
semantic-release-bot 7ef16b73f3 🔖 chore(release): v1.136.11 [skip ci]
### [Version&nbsp;1.136.11](https://github.com/lobehub/lobe-chat/compare/v1.136.10...v1.136.11)
<sup>Released on **2025-10-11**</sup>

#### 💄 Styles

- **misc**: Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities, closes [#9022](https://github.com/lobehub/lobe-chat/issues/9022) ([4e44569](https://github.com/lobehub/lobe-chat/commit/4e44569))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 17:27:57 +00:00
bbbugg 4e445690f1 💄 style: Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities (#9022)
* 🐛 fix: add searchKeywords support and imageOutput match to model parsing logic

* 🐛 fix: add imageOutputKeywords support to model parsing logic

* 🐛 fix: test

* 🐛 fix: enhance model parsing to support local enabled

* 🐛 fix: refine model parsing by removing unnecessary properties from image models

* 💄 style: update form items to support search and imageOutput abilities

* 🐛 fix: improve model parsing logic to conditionally display search options for Perplexity and Jina

* 🐛 fix: parsing imageOutput model will get other abilities

* style: add model search setting when search ability = true and remove when false

* 🐛 fix: prevent 'internal' search models use fcSearch

* 🐛 fix: update search mode handling for internal models in Controls component

* 🐛 fix: test

* 🐛 fix: refine model parsing by updating keyword matching and removing redundant checks

* 🐛 fix: enhance model search configuration by incorporating internal model checks

* 🐛 fix: streamline settings cleanup by using destructuring to remove unused properties

* 💄 style: add video support to model configuration and parsing
2025-10-12 01:17:11 +08:00
semantic-release-bot eb0c0696d0 🔖 chore(release): v1.136.10 [skip ci]
### [Version&nbsp;1.136.10](https://github.com/lobehub/lobe-chat/compare/v1.136.9...v1.136.10)
<sup>Released on **2025-10-11**</sup>

#### 💄 Styles

- **misc**: Improve search experience.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve search experience, closes [#9661](https://github.com/lobehub/lobe-chat/issues/9661) ([8624f84](https://github.com/lobehub/lobe-chat/commit/8624f84))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 17:05:57 +00:00
Arvin Xu 8624f84c8d 💄 style: improve search experience (#9661)
* add placeholder structure

* improve searching

* improve search result content

* fix tests

* improve error

* improve crawler multi page style

* improve crawler multi page content

* improve styles

* fix tests

* make url max twoline
2025-10-12 00:56:17 +08:00
lobehubbot 6db99c090d 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 10:19:02 +00:00
semantic-release-bot cd337011fa 🔖 chore(release): v1.136.9 [skip ci]
### [Version&nbsp;1.136.9](https://github.com/lobehub/lobe-chat/compare/v1.136.8...v1.136.9)
<sup>Released on **2025-10-11**</sup>

#### 💄 Styles

- **misc**: Add lab to support disable/enable rich text.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add lab to support disable/enable rich text, closes [#9652](https://github.com/lobehub/lobe-chat/issues/9652) ([658c294](https://github.com/lobehub/lobe-chat/commit/658c294))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 10:17:59 +00:00
Arvin Xu 236b8f53cc 🔨 chore: add i18n (#9657)
* add i18n

* add i18n
2025-10-11 18:08:12 +08:00
Arvin Xu 658c2945a0 💄 style: add lab to support disable/enable rich text (#9652)
* add abstract chunk prompt eval

* add labs page
2025-10-11 18:03:56 +08:00
lobehubbot f71d1f48cd 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 09:19:55 +00:00
semantic-release-bot 515dee110e 🔖 chore(release): v1.136.8 [skip ci]
### [Version&nbsp;1.136.8](https://github.com/lobehub/lobe-chat/compare/v1.136.7...v1.136.8)
<sup>Released on **2025-10-11**</sup>

#### 🐛 Bug Fixes

- **provider**: Add deepseek-v3.1-terminus to THINKING_MODELS.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **provider**: Add deepseek-v3.1-terminus to THINKING_MODELS, closes [#9653](https://github.com/lobehub/lobe-chat/issues/9653) [#9648](https://github.com/lobehub/lobe-chat/issues/9648) ([e9b5c69](https://github.com/lobehub/lobe-chat/commit/e9b5c69))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 09:18:47 +00:00
Shinji-Li e9b5c692c7 🐛 fix(provider): add deepseek-v3.1-terminus to THINKING_MODELS (#9653)
🐛 fix(nvidia): add deepseek-v3.1-terminus to THINKING_MODELS

Add missing deepseek-ai/deepseek-v3.1-terminus model to THINKING_MODELS array
to enable Deep-Think functionality for DeepSeek V3.1 Terminus via NVIDIA NIM.

Fixes #9648

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Shinji-Li <ONLY-yours@users.noreply.github.com>
2025-10-11 17:09:07 +08:00
renovate[bot] ba3c890f62 Update actions/github-script action to v8 (#9554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 16:19:15 +08:00
lobehubbot 639032e201 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 08:11:16 +00:00
semantic-release-bot 321b414931 🔖 chore(release): v1.136.7 [skip ci]
### [Version&nbsp;1.136.7](https://github.com/lobehub/lobe-chat/compare/v1.136.6...v1.136.7)
<sup>Released on **2025-10-11**</sup>

#### 🐛 Bug Fixes

- **misc**: Disable rich text in markdown editor.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Disable rich text in markdown editor, closes [#9637](https://github.com/lobehub/lobe-chat/issues/9637) ([9349ce2](https://github.com/lobehub/lobe-chat/commit/9349ce2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 08:10:14 +00:00
Arvin Xu 9349ce2d2f 🐛 fix: disable rich text in markdown editor (#9637)
* clean

* update

* add labs

* fix

* improve

* update

* fix

* hide lab

* improve workflow
2025-10-11 15:59:56 +08:00
lobehubbot 3985c13488 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 07:20:51 +00:00
semantic-release-bot 8e3494857d 🔖 chore(release): v1.136.6 [skip ci]
### [Version&nbsp;1.136.6](https://github.com/lobehub/lobe-chat/compare/v1.136.5...v1.136.6)
<sup>Released on **2025-10-11**</sup>

#### 🐛 Bug Fixes

- **bedrock**: Add parameter conflict handling for Claude 4+ models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **bedrock**: Add parameter conflict handling for Claude 4+ models, closes [#9627](https://github.com/lobehub/lobe-chat/issues/9627) [#9523](https://github.com/lobehub/lobe-chat/issues/9523) ([54b6217](https://github.com/lobehub/lobe-chat/commit/54b6217))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 07:19:42 +00:00
renovate[bot] 55d5ae91ee Update dependency happy-dom to v20 [SECURITY] (#9647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-11 15:09:05 +08:00
Arvin Xu 54b6217256 🐛 fix(bedrock): add parameter conflict handling for Claude 4+ models (#9627)
* fix(bedrock): add parameter conflict handling for Claude 4+ models

- Add logic to prevent sending both temperature and top_p for Claude 4+ models
- Matches existing implementation in Anthropic provider
- Fixes ValidationException error for Claude 4.5 models via Bedrock
- Includes support for both standard and Bedrock-specific model IDs

Fixes #9523

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🧪 test(bedrock): add parameter conflict handling tests

Add comprehensive tests for Claude 4+ models parameter conflict detection:
- Test temperature preference over top_p when both provided
- Test top_p usage when temperature not provided
- Test both parameters allowed for non-Claude-4+ models
- Test standard and Bedrock-specific model ID formats
- Test US region model IDs

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🔧 fix: make temperature optional in ChatStreamPayload interface

The temperature property should be optional to support test cases and scenarios where only top_p is provided. This resolves TypeScript error TS2741 in Bedrock provider tests.

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🔧 fix: correct test assertions to handle JSON.stringify undefined omission

- Remove undefined properties from test expectations since JSON.stringify omits them
- Fix temperature/top_p conflict test assertions for Claude 4+ models
- Ensure tests match actual JSON serialization behavior

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🔧 fix: add null safety checks for optional temperature parameter

- Added proper undefined checks before temperature arithmetic operations in anthropic and bedrock providers
- Added null checks before temperature comparisons in groq, perplexity, and search1api providers
- Resolves TS18048 errors where temperature is possibly undefined
- Maintains existing logic while satisfying TypeScript strict null checks

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* refactor with parameterResolver

* upgrade

* upgrade swr

* refactor context-builder

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-11 15:08:26 +08:00
lobehubbot 020ef51141 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-11 06:33:11 +00:00
semantic-release-bot fa07f44436 🔖 chore(release): v1.136.5 [skip ci]
### [Version&nbsp;1.136.5](https://github.com/lobehub/lobe-chat/compare/v1.136.4...v1.136.5)
<sup>Released on **2025-10-11**</sup>

#### 🐛 Bug Fixes

- **plugin-store**: Fix search functionality for old plugin store.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **plugin-store**: Fix search functionality for old plugin store, closes [#9651](https://github.com/lobehub/lobe-chat/issues/9651) [#9645](https://github.com/lobehub/lobe-chat/issues/9645) ([522fc09](https://github.com/lobehub/lobe-chat/commit/522fc09))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-11 06:32:09 +00:00
Shinji-Li 522fc09a85 🐛 fix(plugin-store): fix search functionality for old plugin store (#9651)
- Update Search component to handle different search keywords based on current tab (MCP vs Plugin)
- Add missing useEffect in Plugin List to reset list when search keywords change
- Fixes issue where typing in search bar didn't trigger plugin filtering

Fixes #9645

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Shinji-Li <ONLY-yours@users.noreply.github.com>
2025-10-11 14:22:31 +08:00
lobehubbot 72fcc7afab 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-10 09:52:19 +00:00
semantic-release-bot 05c0ef084a 🔖 chore(release): v1.136.4 [skip ci]
### [Version&nbsp;1.136.4](https://github.com/lobehub/lobe-chat/compare/v1.136.3...v1.136.4)
<sup>Released on **2025-10-10**</sup>

#### 🐛 Bug Fixes

- **misc**: Add 'gemini-2.5-flash-image' to disabled models Thinking.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Add 'gemini-2.5-flash-image' to disabled models Thinking, closes [#9633](https://github.com/lobehub/lobe-chat/issues/9633) ([771b585](https://github.com/lobehub/lobe-chat/commit/771b585))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-10 09:51:12 +00:00
Neko 23d61aebc9 🔨 chore(database): add user memory db schemas (#9371)
*  feat(database): user memory db schemas

* feat: migrations

* fix: id

* fix: syntax id

* update

* revert

---------

Co-authored-by: arvinxx <arvinx@foxmail.com>
2025-10-10 17:41:14 +08:00
bailu 771b585f3a 🐛 fix: Add 'gemini-2.5-flash-image' to disabled models Thinking (#9633)
Add 'gemini-2.5-flash-image' to disabled models Thinking
2025-10-10 17:40:06 +08:00
lobehubbot 65c8dbb525 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-10 03:19:37 +00:00
semantic-release-bot a11925fbb2 🔖 chore(release): v1.136.3 [skip ci]
### [Version&nbsp;1.136.3](https://github.com/lobehub/lobe-chat/compare/v1.136.2...v1.136.3)
<sup>Released on **2025-10-10**</sup>

#### 💄 Styles

- **misc**: Add delete & regenerate hotkeys.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add delete & regenerate hotkeys, closes [#9538](https://github.com/lobehub/lobe-chat/issues/9538) ([d948580](https://github.com/lobehub/lobe-chat/commit/d948580))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-10 03:18:33 +00:00
sxjeru d9485809a1 💄 style: Add delete & regenerate hotkeys (#9538)
* 🐛 fix(zhipu): update maxOutput and contextWindowTokens for various models

*  feat(hotkeys): add delete last message and delete & regenerate message hotkeys
2025-10-10 11:08:02 +08:00
Arvin Xu 143784b474 🔨 chore: improve code (#9626)
* improve code

* fix tests
2025-10-10 11:07:35 +08:00
lobehubbot 26504d0017 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-10 02:18:59 +00:00
semantic-release-bot fa2f9d4eb4 🔖 chore(release): v1.136.2 [skip ci]
### [Version&nbsp;1.136.2](https://github.com/lobehub/lobe-chat/compare/v1.136.1...v1.136.2)
<sup>Released on **2025-10-10**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9625](https://github.com/lobehub/lobe-chat/issues/9625) ([70d356d](https://github.com/lobehub/lobe-chat/commit/70d356d))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-10 02:17:56 +00:00
LobeHub Bot 70d356d524 🤖 style: update i18n (#9625)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-10 10:08:21 +08:00
Kim SooHyun 8d59583dca Merge commit from fork
Co-authored-by: root <root@DESKTOP-FSUSL5A.localdomain>
2025-10-10 10:06:16 +08:00
Tsuki c609c77f24 🔧 chore: export plugin module and clean up imports in toolCall (#9619) 2025-10-09 18:48:21 +08:00
lobehubbot c050c39c45 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-09 10:04:26 +00:00
semantic-release-bot ac75d30e0d 🔖 chore(release): v1.136.1 [skip ci]
### [Version&nbsp;1.136.1](https://github.com/lobehub/lobe-chat/compare/v1.136.0...v1.136.1)
<sup>Released on **2025-10-09**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-09 10:03:26 +00:00
Arvin Xu b2a1dc66d5 👷 build: Update Dockerfile.database (#9617)
Update Dockerfile.database
2025-10-09 17:53:04 +08:00
lobehubbot 954c6343d2 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-09 07:38:24 +00:00
semantic-release-bot d3afc75c59 🔖 chore(release): v1.136.0 [skip ci]
## [Version&nbsp;1.136.0](https://github.com/lobehub/lobe-chat/compare/v1.135.6...v1.136.0)
<sup>Released on **2025-10-09**</sup>

####  Features

- **misc**: Add new provider Cerebras.

#### 🐛 Bug Fixes

- **misc**: Fix standalone plugin rerender issue.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add new provider Cerebras, closes [#9559](https://github.com/lobehub/lobe-chat/issues/9559) ([9cceaad](https://github.com/lobehub/lobe-chat/commit/9cceaad))

#### What's fixed

* **misc**: Fix standalone plugin rerender issue, closes [#9611](https://github.com/lobehub/lobe-chat/issues/9611) [#9396](https://github.com/lobehub/lobe-chat/issues/9396) ([7ab30fc](https://github.com/lobehub/lobe-chat/commit/7ab30fc))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-09 07:37:10 +00:00
sxjeru 9cceaad241 feat: Add new provider Cerebras (#9559)
*  feat: 添加 Cerebras 模型支持,包括模型定义和配置

*  feat: 添加 Cerebras 模型支持,包括环境变量和模型描述

*  feat: 在 OllamaCloud 模型中添加 GLM-4.6,增强推理和编码能力

*  feat: 移除过时的 Grok 模型定义,优化模型列表
2025-10-09 15:27:12 +08:00
blueskyxi3 7ab30fc77e 🐛 fix: fix standalone plugin rerender issue (#9611)
🐛 fix: standalone 插件当发布生产后多次渲染 (#9396)

Co-authored-by: Vincent Zou <vincentzou@citictel.com>
2025-10-09 15:26:50 +08:00
Daniel Hofheinz 7ce0d1fbc4 📝 docs(dev): clarify feature organization patterns in tutorial (#9608)
- Distinguish between global (src/features/) and page-specific features
- Update code example to show page-specific pattern
- Add explanatory note on when to use each pattern

Fixes #9585
2025-10-09 10:48:52 +08:00
lobehubbot 8f1b38a24e 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-08 15:41:09 +00:00
semantic-release-bot bf57bfe59f 🔖 chore(release): v1.135.6 [skip ci]
### [Version&nbsp;1.135.6](https://github.com/lobehub/lobe-chat/compare/v1.135.5...v1.135.6)
<sup>Released on **2025-10-08**</sup>

#### 🐛 Bug Fixes

- **desktop**: Macos26 small icon.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **desktop**: Macos26 small icon, closes [#9421](https://github.com/lobehub/lobe-chat/issues/9421) ([ca03342](https://github.com/lobehub/lobe-chat/commit/ca03342))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-08 15:40:07 +00:00
YuTengjing ca033420b9 🐛 fix(desktop): macos26 small icon (#9421)
* 🐛 fix(desktop): macos26 icon small

* Revert "🐛 fix(desktop): macos26 icon small"

This reverts commit 4a4b7b230c.

*  feat(desktop): support Liquid Glass icons for macOS 26

- Add pre-generated Assets.car files for all build variants
- Configure afterPack hook to copy Assets.car during build
- Maintain backward compatibility with .icns fallback for older macOS

Reference: https://github.com/electron-userland/electron-builder/issues/9254

* docs: optimize comments

* fix: update deprecated macos-13 to macos-15-intel

* docs: optimize ai rules
2025-10-08 23:30:35 +08:00
lobehubbot d8bfec02ad 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-08 05:35:31 +00:00
semantic-release-bot 992ecdfdef 🔖 chore(release): v1.135.5 [skip ci]
### [Version&nbsp;1.135.5](https://github.com/lobehub/lobe-chat/compare/v1.135.4...v1.135.5)
<sup>Released on **2025-10-08**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9602](https://github.com/lobehub/lobe-chat/issues/9602) ([ed267a4](https://github.com/lobehub/lobe-chat/commit/ed267a4))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-08 05:34:32 +00:00
LobeHub Bot ed267a4d96 🤖 style: update i18n (#9602)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-08 13:24:29 +08:00
lobehubbot d9da405ff1 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-07 15:43:49 +00:00
semantic-release-bot f0f132696a 🔖 chore(release): v1.135.4 [skip ci]
### [Version&nbsp;1.135.4](https://github.com/lobehub/lobe-chat/compare/v1.135.3...v1.135.4)
<sup>Released on **2025-10-07**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor chat item.

#### 💄 Styles

- **misc**: Add GPT-5 pro model.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Refactor chat item, closes [#9599](https://github.com/lobehub/lobe-chat/issues/9599) ([1f36158](https://github.com/lobehub/lobe-chat/commit/1f36158))

#### Styles

* **misc**: Add GPT-5 pro model, closes [#9594](https://github.com/lobehub/lobe-chat/issues/9594) ([775f30b](https://github.com/lobehub/lobe-chat/commit/775f30b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-07 15:42:48 +00:00
sxjeru 775f30b614 💄 style: Add GPT-5 pro model (#9594)
*  feat: 添加输入缓存读取和写入定价支持,更新相关接口和定价格式

*  feat: 添加 GPT-5 pro 模型及其定价信息,更新模型集合

*  feat: 添加 GPT Image 1 Mini 模型及其定价信息,更新模型参数和定价策略

*  feat: 更新价格格式化函数,允许价格为 undefined,调整模型定价接口的可选属性

*  feat: 更新 DeepSeek V3.1 模型信息,添加新模型及其参数,调整模型 ID

*  feat: 添加 GPT-5 pro 模型,更新其能力、定价和描述信息

*  feat: 强制 gpt-5 pro 使用 high 推理等级,优化推理负担处理逻辑
2025-10-07 23:33:08 +08:00
Arvin Xu 1f36158a2f ♻️ refactor: refactor chat item (#9599)
* wip

* refactor user message

* fix user messages

* refactor

* refactor user and actions

* update

* refactor chat item

* refactor

* revert

* fix tests

* update

* fix thread display
2025-10-07 23:27:19 +08:00
lobehubbot 18bcd08327 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-07 11:27:02 +00:00
semantic-release-bot f13c4598c3 🔖 chore(release): v1.135.3 [skip ci]
### [Version&nbsp;1.135.3](https://github.com/lobehub/lobe-chat/compare/v1.135.2...v1.135.3)
<sup>Released on **2025-10-07**</sup>

#### 💄 Styles

- **misc**: Improve Korean translate.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve Korean translate, closes [#9597](https://github.com/lobehub/lobe-chat/issues/9597) ([319fbfb](https://github.com/lobehub/lobe-chat/commit/319fbfb))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-07 11:25:52 +00:00
Arvin Xu 319fbfb6fd 💄 style: improve Korean translate (#9597)
* update

* update

* update

* update

* update

* update

* update

* update

* update

* update

* update
2025-10-07 19:15:56 +08:00
renovate[bot] 892844a17f Update dependency @testing-library/jest-dom to ~6.9.0 (#9549)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-07 18:42:25 +08:00
Daniel Hofheinz 219250c7bb 📝 docs: remove outdated nextauth server database warning (#9593) 2025-10-07 06:20:17 +02:00
lobehubbot b7d51c51dd 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-06 16:29:03 +00:00
semantic-release-bot 94d447b563 🔖 chore(release): v1.135.2 [skip ci]
### [Version&nbsp;1.135.2](https://github.com/lobehub/lobe-chat/compare/v1.135.1...v1.135.2)
<sup>Released on **2025-10-06**</sup>

#### 💄 Styles

- **image**: Optimize UX and fix fal pricing.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **image**: Optimize UX and fix fal pricing, closes [#9592](https://github.com/lobehub/lobe-chat/issues/9592) ([dddbfcd](https://github.com/lobehub/lobe-chat/commit/dddbfcd))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-06 16:28:03 +00:00
YuTengjing dddbfcd094 💄 style(image): optimize UX and fix fal pricing (#9592) 2025-10-07 00:17:32 +08:00
lobehubbot 5719e4ff90 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-06 15:47:07 +00:00
semantic-release-bot 1725f7d3ee 🔖 chore(release): v1.135.1 [skip ci]
### [Version&nbsp;1.135.1](https://github.com/lobehub/lobe-chat/compare/v1.135.0...v1.135.1)
<sup>Released on **2025-10-06**</sup>

#### 💄 Styles

- **misc**: Improve styles and fix tools calling condition.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Improve styles and fix tools calling condition, closes [#9591](https://github.com/lobehub/lobe-chat/issues/9591) ([1695f2f](https://github.com/lobehub/lobe-chat/commit/1695f2f))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-06 15:46:09 +00:00
Arvin Xu 1695f2f289 💄 style: improve styles and fix tools calling condition (#9591)
* clean and refactor

* improve

* update

* update

* fix tools engine
2025-10-06 23:36:15 +08:00
lobehubbot 2ffced0773 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-06 14:32:26 +00:00
semantic-release-bot 8cc70ddf38 🔖 chore(release): v1.135.0 [skip ci]
## [Version&nbsp;1.135.0](https://github.com/lobehub/lobe-chat/compare/v1.134.7...v1.135.0)
<sup>Released on **2025-10-06**</sup>

####  Features

- **misc**: Huanyuan text-to-image 3.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Huanyuan text-to-image 3, closes [#9589](https://github.com/lobehub/lobe-chat/issues/9589) ([1dd0e5e](https://github.com/lobehub/lobe-chat/commit/1dd0e5e))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-06 14:31:23 +00:00
YuTengjing 1dd0e5efce feat: huanyuan text-to-image 3 (#9589) 2025-10-06 22:21:11 +08:00
YuTengjing 08ea8561f9 fix: qwen image inside new api (#9587) 2025-10-06 21:46:22 +08:00
lobehubbot 20be3cfb38 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-06 04:36:05 +00:00
semantic-release-bot 562ef7fd8e 🔖 chore(release): v1.134.7 [skip ci]
### [Version&nbsp;1.134.7](https://github.com/lobehub/lobe-chat/compare/v1.134.6...v1.134.7)
<sup>Released on **2025-10-06**</sup>

#### 🐛 Bug Fixes

- **security**: Sanitize Azure provider error responses to prevent API key exposure.

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **security**: Sanitize Azure provider error responses to prevent API key exposure, closes [#9583](https://github.com/lobehub/lobe-chat/issues/9583) ([af59bfe](https://github.com/lobehub/lobe-chat/commit/af59bfe))

#### Styles

* **misc**: Update i18n, closes [#9580](https://github.com/lobehub/lobe-chat/issues/9580) ([c0974ea](https://github.com/lobehub/lobe-chat/commit/c0974ea))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-06 04:34:49 +00:00
Arvin Xu af59bfe013 🔒 fix(security): Sanitize Azure provider error responses to prevent API key exposure (#9583) 2025-10-06 06:24:27 +02:00
LobeHub Bot c0974ea955 🤖 style: update i18n (#9580) 2025-10-06 06:23:38 +02:00
Arvin Xu c83d7afbe6 📝 docs: update app directory structure documentation (#9582)
- Update folder-structure.mdx and zh-CN version to reflect current Next.js 13+ App Router architecture
- Replace outdated simple desktop/mobile structure with actual complex structure
- Add documentation for (backend), [variants], @modal, and desktop route groups
- Include API architecture explanation with tRPC and REST endpoints
- Document platform organization and deployment targets

Fixes #9522

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-06 12:13:28 +08:00
Daniel Hofheinz 210a41bb8b 📝 docs: update Zustand import syntax for v5 compatibility (#9581) 2025-10-06 05:53:44 +02:00
lobehubbot e8c08335c3 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 16:20:54 +00:00
semantic-release-bot cf82bc0628 🔖 chore(release): v1.134.6 [skip ci]
### [Version&nbsp;1.134.6](https://github.com/lobehub/lobe-chat/compare/v1.134.5...v1.134.6)
<sup>Released on **2025-10-05**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 16:19:56 +00:00
Arvin Xu e702064e39 👷 build: fix docker build (#9576)
fix build
2025-10-06 00:09:58 +08:00
lobehubbot d14debc7d7 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 16:00:14 +00:00
semantic-release-bot 8231aea1c7 🔖 chore(release): v1.134.5 [skip ci]
### [Version&nbsp;1.134.5](https://github.com/lobehub/lobe-chat/compare/v1.134.4...v1.134.5)
<sup>Released on **2025-10-05**</sup>

#### 🐛 Bug Fixes

- **database**: Prevent empty array insertion in aiModel batch operations.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **database**: Prevent empty array insertion in aiModel batch operations, closes [#9491](https://github.com/lobehub/lobe-chat/issues/9491) [#9429](https://github.com/lobehub/lobe-chat/issues/9429) [#9429](https://github.com/lobehub/lobe-chat/issues/9429) ([eb50c8b](https://github.com/lobehub/lobe-chat/commit/eb50c8b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 15:59:11 +00:00
Arvin Xu af274190a8 🔨 chore: add group-messages database schema (#9543)
* add group messages

* update

* add migrations

* ♻️ refactor: refactor message group

* fix

* fix

* update schema
2025-10-05 23:49:27 +08:00
Arvin Xu eb50c8b781 🐛 fix(database): prevent empty array insertion in aiModel batch operations (#9491)
* 🐛 fix(database): prevent empty array insertion in aiModel batch operations

- Add validation to batchUpdateAiModels to return early if models array is empty
- Add validation to batchToggleAiModels to return early if models array is empty
- Add validation to updateModelsOrder to return early if sortMap array is empty
- Fixes 'values() must be called with at least one value' error when OpenRouter returns empty model list

Fixes #9429

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

*  test(database): add tests for empty array validation in aiModel batch operations

- Add test for batchUpdateAiModels with empty array returning empty result
- Add test for batchToggleAiModels with empty array returning early
- Add test for updateModelsOrder with empty sortMap returning early

These tests verify the fix for issue #9429 where empty arrays caused
"values() must be called with at least one value" database errors.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🐛 fix(test): remove invalid sort property access in aiModel test

- Remove test assertion accessing sort property on AiProviderModelListItem
- AiProviderModelListItem interface doesn't include sort property
- Fix TypeScript error: Property 'sort' does not exist on type 'AiProviderModelListItem'

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* ♻️ refactor(database): extract shared validation helper for empty arrays

- Add private isEmptyArray() helper method to AiModelModel class
- Replace duplicate empty array checks in batch methods with shared helper
- Improve code maintainability and reduce duplication
- Address Sourcery AI feedback for better code organization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

* 🐛 fix(database): respect enabled parameter in aiModel create method

The create method was forcing enabled: true regardless of input.
This fix allows explicit enabled: false while maintaining true as default.

Fixes failing test: batchToggleAiModels empty array validation.

Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Arvin Xu <arvinxx@users.noreply.github.com>
2025-10-05 23:42:39 +08:00
lobehubbot c25492e377 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 14:35:07 +00:00
semantic-release-bot 082b641270 🔖 chore(release): v1.134.4 [skip ci]
### [Version&nbsp;1.134.4](https://github.com/lobehub/lobe-chat/compare/v1.134.3...v1.134.4)
<sup>Released on **2025-10-05**</sup>

#### 💄 Styles

- **misc**: Add promptfoo to improve prompts quality.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add promptfoo to improve prompts quality, closes [#9568](https://github.com/lobehub/lobe-chat/issues/9568) ([33874c2](https://github.com/lobehub/lobe-chat/commit/33874c2))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 14:34:01 +00:00
Arvin Xu 33874c20d1 💄 style: add promptfoo to improve prompts quality (#9568)
* add promptfoo

* finish pipeline of promptfoo

* improve translate prompt

* improve summary title

* improve summary-title prompt

* refactor emoji-picker and language-detection

* refactor emoji-picker prompt

* improve emoji picker

* improve providers

* improve knowledge qa promptfoo

* improve knowledge qa prompts

* update

* update
2025-10-05 22:24:30 +08:00
Arvin Xu 0d48ebddd9 🔧 chore: fix workflows does not contain permissions (#9544)
* Potential fix for code scanning alert no. 39: Workflow does not contain permissions

* update permissions

---------
2025-10-05 20:05:10 +08:00
lobehubbot 95393ec093 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 10:57:44 +00:00
semantic-release-bot 565e0d9a39 🔖 chore(release): v1.134.3 [skip ci]
### [Version&nbsp;1.134.3](https://github.com/lobehub/lobe-chat/compare/v1.134.2...v1.134.3)
<sup>Released on **2025-10-05**</sup>

#### 🐛 Bug Fixes

- **misc**: Type not preserved when model is sorted.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Type not preserved when model is sorted, closes [#9561](https://github.com/lobehub/lobe-chat/issues/9561) ([5fe2518](https://github.com/lobehub/lobe-chat/commit/5fe2518))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 10:56:32 +00:00
sxjeru 5fe2518dae 🐛 fix: type not preserved when model is sorted (#9561) 2025-10-05 12:45:14 +02:00
lobehubbot 1cb36da520 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 09:49:07 +00:00
semantic-release-bot cdded292f4 🔖 chore(release): v1.134.2 [skip ci]
### [Version&nbsp;1.134.2](https://github.com/lobehub/lobe-chat/compare/v1.134.1...v1.134.2)
<sup>Released on **2025-10-05**</sup>

#### 💄 Styles

- **misc**: Allow switching model `type`.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Allow switching model `type`, closes [#9529](https://github.com/lobehub/lobe-chat/issues/9529) ([9b62685](https://github.com/lobehub/lobe-chat/commit/9b62685))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 09:48:01 +00:00
sxjeru 9b62685b08 💄 style: Allow switching model type (#9529) 2025-10-05 11:38:02 +02:00
renovate[bot] 249b46e5cf Update codecov/codecov-action action to v5 (#4714) 2025-10-05 11:37:16 +02:00
renovate[bot] dbe7d7ef7c Update actions/checkout action to v5 (#9553) 2025-10-05 11:36:11 +02:00
lobehubbot 1e4011e489 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-05 06:38:57 +00:00
semantic-release-bot 3a8229a2c6 🔖 chore(release): v1.134.1 [skip ci]
### [Version&nbsp;1.134.1](https://github.com/lobehub/lobe-chat/compare/v1.134.0...v1.134.1)
<sup>Released on **2025-10-05**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9546](https://github.com/lobehub/lobe-chat/issues/9546) ([ed8174f](https://github.com/lobehub/lobe-chat/commit/ed8174f))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-05 06:37:56 +00:00
LobeHub Bot ed8174fc3d 🤖 style: update i18n (#9546)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-05 14:27:53 +08:00
renovate[bot] 304e6c13d8 Update pnpm to v10.18.0 (#9552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-05 14:27:05 +08:00
lobehubbot a63485d915 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-04 19:12:57 +00:00
semantic-release-bot 01d66a9368 🔖 chore(release): v1.134.0 [skip ci]
## [Version&nbsp;1.134.0](https://github.com/lobehub/lobe-chat/compare/v1.133.6...v1.134.0)
<sup>Released on **2025-10-04**</sup>

####  Features

- **misc**: Support double-click to open multi agent window on the desktop.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Support double-click to open multi agent window on the desktop, closes [#9331](https://github.com/lobehub/lobe-chat/issues/9331) ([a060901](https://github.com/lobehub/lobe-chat/commit/a060901))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-04 19:11:56 +00:00
René Wang 794748373b 🔨 chore: enhance EdgeConfig with per-user feature flags support (#9541)
*  feat: enhance EdgeConfig with per-user feature flags support

- Add EdgeConfigKeys constants for better maintainability
- Add getFeatureFlags() and getFlagByKey() methods to EdgeConfig
- Enhance isEnabled() with debugging console logs
- Implement per-user feature flag evaluation logic
- Add EdgeConfig integration for feature flags with env var fallback
- Support feature flags as boolean or array of user IDs
- Export IFeatureFlagsState type for type safety

* update log

* refactor

* refactor schema

---------

Co-authored-by: arvinxx <arvinx@foxmail.com>
2025-10-05 02:49:36 +08:00
Shinji-Li a060901c65 feat: support double-click to open multi agent window on the desktop (#9331)
* feat: add single pannel

* feat: add openTopicInNewWindow to global windows

* feat: use ueIsSingleMode hook to replace useSearchParams judge

* feat: add session pannel double click & drag create new window

* feat: add supensed out in SideBar

* fix: update test.ts

* feat: add ts define

* feat: loading singlemode not render draggablePannel
2025-10-05 02:42:20 +08:00
lobehubbot 7a34c8babe 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-04 15:31:24 +00:00
semantic-release-bot 63e3b70681 🔖 chore(release): v1.133.6 [skip ci]
### [Version&nbsp;1.133.6](https://github.com/lobehub/lobe-chat/compare/v1.133.5...v1.133.6)
<sup>Released on **2025-10-04**</sup>

#### 🐛 Bug Fixes

- **misc**: `type` not preserved when model is disabled or sorted.

#### 💄 Styles

- **misc**: Nano banana support `aspect_ratio`.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: `type` not preserved when model is disabled or sorted, closes [#9530](https://github.com/lobehub/lobe-chat/issues/9530) ([476b897](https://github.com/lobehub/lobe-chat/commit/476b897))

#### Styles

* **misc**: Nano banana support `aspect_ratio`, closes [#9528](https://github.com/lobehub/lobe-chat/issues/9528) ([ae3ed6e](https://github.com/lobehub/lobe-chat/commit/ae3ed6e))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-04 15:30:23 +00:00
sxjeru 476b897fe0 🐛 fix: type not preserved when model is disabled or sorted (#9530)
 feat: add model type support in AiModelModel and related tests
2025-10-04 23:20:15 +08:00
sxjeru ae3ed6ec47 💄 style: nano banana support aspect_ratio (#9528)
*  feat: add nano banana model parameters and update image generation config

*  feat: 添加 Nano Banana 模型参数和图像生成定价配置
2025-10-04 23:19:59 +08:00
lobehubbot 5a69857e09 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-04 11:40:42 +00:00
semantic-release-bot e01c72cf03 🔖 chore(release): v1.133.5 [skip ci]
### [Version&nbsp;1.133.5](https://github.com/lobehub/lobe-chat/compare/v1.133.4...v1.133.5)
<sup>Released on **2025-10-04**</sup>

#### 🐛 Bug Fixes

- **misc**: Custom provider fails when client requests are enabled.

#### 💄 Styles

- **misc**: Optimized `extendParams` UI, update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Custom provider fails when client requests are enabled, closes [#9534](https://github.com/lobehub/lobe-chat/issues/9534) ([8b12fdf](https://github.com/lobehub/lobe-chat/commit/8b12fdf))

#### Styles

* **misc**: Optimized `extendParams` UI, closes [#9457](https://github.com/lobehub/lobe-chat/issues/9457) ([582f6d1](https://github.com/lobehub/lobe-chat/commit/582f6d1))
* **misc**: Update i18n, closes [#9514](https://github.com/lobehub/lobe-chat/issues/9514) ([6430f57](https://github.com/lobehub/lobe-chat/commit/6430f57))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-04 11:39:19 +00:00
renovate[bot] 95d4a3a4be Update dependency @huggingface/tasks to ^0.19.0 (#6427)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-04 19:29:11 +08:00
LobeHub Bot 6430f57665 🤖 style: update i18n (#9514)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-10-04 19:25:35 +08:00
YuTengjing 99ded4ac33 chore: sync some update (#9539)
* chore: remove legacy files

* 🔧 fix: correct and simplify tps calc

* 🔧 fix: remove unnecessary console log in createRuntime function
2025-10-04 19:23:57 +08:00
sxjeru 582f6d1fbf 💄 style: Optimized extendParams UI (#9457)
*  feat: 调整滑块参数和步长逻辑以优化用户体验

*  feat: 添加 resolveModelThinkingBudget 函数以优化 Gemini 模型思维预算逻辑

*  feat: 添加 DeepSeek V3.1 Terminus 模型

*  feat: 更新 DeepSeek 模型至 V3.2,调整参数和描述以反映最新功能

*  feat: 更新 DeepSeek 和 Hunyuan 模型,调整描述和发布时间以反映最新信息

*  feat: 添加 DeepSeek V3.2 模型及其定价信息,更新模型解析配置以支持新版本

* Update google.ts

* feat: 添加 GLM-4.6 聊天模型,增强推理能力和上下文处理
2025-10-04 19:20:58 +08:00
sxjeru 8b12fdfb82 🐛 fix: Custom provider fails when client requests are enabled (#9534)
*  fix: (启用客户端请求,自定义服务商未遵循指定请求格式) 更新 initializeWithClientStore 函数,支持通过选项对象传递 provider 和 payload,增强代码可读性

*  feat: 添加 runtimeProvider 支持,优化模型服务的提供者初始化逻辑

* add test
2025-10-04 19:15:59 +08:00
lobehubbot ba3f67f7d4 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-01 21:07:14 +00:00
semantic-release-bot 0a6d3ad3f9 🔖 chore(release): v1.133.4 [skip ci]
### [Version&nbsp;1.133.4](https://github.com/lobehub/lobe-chat/compare/v1.133.3...v1.133.4)
<sup>Released on **2025-10-01**</sup>

#### 🐛 Bug Fixes

- **misc**: OllamaCloud error.

#### 💄 Styles

- **misc**: Fix chat minimap overflow.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: OllamaCloud error, closes [#9481](https://github.com/lobehub/lobe-chat/issues/9481) ([55c45a5](https://github.com/lobehub/lobe-chat/commit/55c45a5))

#### Styles

* **misc**: Fix chat minimap overflow, closes [#9507](https://github.com/lobehub/lobe-chat/issues/9507) ([d835c33](https://github.com/lobehub/lobe-chat/commit/d835c33))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-01 21:06:07 +00:00
Arvin Xu e10aa05a11 🔨 chore: improve github workflow (#9511)
* improve workflow file

* clean

* update
2025-10-02 04:56:34 +08:00
René Wang d835c33699 💄 style: fix chat minimap overflow (#9507)
fix: Minimap overflow
2025-10-02 04:47:12 +08:00
sxjeru 55c45a5197 🐛 fix: OllamaCloud error (#9481)
* fix: update Ollama Cloud base URL and add CORS error handling

*  feat: 添加 Claude Sonnet 4.5 模型并更新其他模型的发布信息

*  feat: 更新 Novita 模型定价和输入模态,增强模型信息

* Update index.ts

* fix: 移除不必要的测试用例

*  feat: 更新智谱模型,添加 GLM-4.6,优化定价策略,移除过时模型

*  feat: 为 GLM-4.6 模型添加定价策略,优化定价单位和参数

* fix: 修复 DeepSeek 模型显示名称逻辑,确保短名称不包含 DeepSeek 时正确显示

* feat: 添加 DeepSeek V3.2 Exp 模型,提升长文本处理效率
2025-10-02 04:46:59 +08:00
lobehubbot b51839fc54 📝 docs(bot): Auto sync agents & plugin to readme 2025-10-01 19:41:54 +00:00
semantic-release-bot 1e9b05d7ce 🔖 chore(release): v1.133.3 [skip ci]
### [Version&nbsp;1.133.3](https://github.com/lobehub/lobe-chat/compare/v1.133.2...v1.133.3)
<sup>Released on **2025-10-01**</sup>

#### ♻ Code Refactoring

- **misc**: Refactor a `ssrf-safe-fetch` module.

#### 🐛 Bug Fixes

- **misc**: Fix frontend random API key config not work.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Code refactoring

* **misc**: Refactor a `ssrf-safe-fetch` module, closes [#9474](https://github.com/lobehub/lobe-chat/issues/9474) ([92da716](https://github.com/lobehub/lobe-chat/commit/92da716))

#### What's fixed

* **misc**: Fix frontend random API key config not work, closes [#9477](https://github.com/lobehub/lobe-chat/issues/9477) [#9255](https://github.com/lobehub/lobe-chat/issues/9255) ([a194d48](https://github.com/lobehub/lobe-chat/commit/a194d48))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-10-01 19:40:38 +00:00
Arvin Xu 92da716028 ♻️ refactor: refactor a ssrf-safe-fetch module (#9474)
* add ssrf-safe-fetch

* fix web-crawler

* add tests

* revert

* Update index.ts

* fix tests
2025-10-02 03:31:20 +08:00
renovate[bot] 635d0d649b Update dependency @opentelemetry/exporter-trace-otlp-http to ^0.205.0 (#9455)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 14:54:05 +08:00
renovate[bot] 3ae352b79e Update dependency @opentelemetry/instrumentation-http to ^0.205.0 (#9458)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-01 14:53:58 +08:00
Arvin Xu a194d48545 🐛 fix: fix frontend random API key config not work (#9477)
*  feat: add frontend random API key selection support

- Implement ClientApiKeyManager for random selection from comma-separated keys
- Update getProviderAuthPayload to use random key picking for all providers
- Add comprehensive tests for the new functionality
- Mirror existing server-side implementation for consistency

Closes #9255

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: LobeHub Bot <lobehubbot@users.noreply.github.com>

* fix tests

* fix implement

* fix test

* fix test
2025-10-01 05:46:24 +08:00
YuTengjing 0ee4f18d89 📝 docs(i18n): add image quality translations for multiple languages (#9495) 2025-09-30 21:17:47 +08:00
YuTengjing 49ea508cc4 🔨 chore: support chat api and create image cost caculate (#9492) 2025-09-30 21:09:56 +08:00
lobehubbot 88592d3f08 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-30 09:05:58 +00:00
semantic-release-bot 7e0b715e22 🔖 chore(release): v1.133.2 [skip ci]
### [Version&nbsp;1.133.2](https://github.com/lobehub/lobe-chat/compare/v1.133.1...v1.133.2)
<sup>Released on **2025-09-30**</sup>

#### 💄 Styles

- **misc**: Add minimap to chat list for quick navigation.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Add minimap to chat list for quick navigation, closes [#9470](https://github.com/lobehub/lobe-chat/issues/9470) ([8db47eb](https://github.com/lobehub/lobe-chat/commit/8db47eb))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-30 09:04:58 +00:00
René Wang 8db47eb816 💄 style: Add minimap to chat list for quick navigation (#9470)
* feat: Add minimap to chat list

* style: Tweak style

* style: Hover to reveal the arrow

* style: Hover to reveal the arrow

* feat: Message anchor detection

* fix: Type error

* style: Remove drag handle for topic panel

* fix: type error

* fix: Anchor position

* fix: Scroll

* feat: Add missing translation

* fix: Offset

* fix: Offset

* feat: Remove test files
2025-09-30 16:55:17 +08:00
YuTengjing 6325602480 📝 docs(rules): optimize agent rules and documentation structure (#9486) 2025-09-30 16:07:32 +08:00
lobehubbot b745f11873 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-30 04:46:57 +00:00
semantic-release-bot 7f31eba0d9 🔖 chore(release): v1.133.1 [skip ci]
### [Version&nbsp;1.133.1](https://github.com/lobehub/lobe-chat/compare/v1.133.0...v1.133.1)
<sup>Released on **2025-09-30**</sup>

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

* **misc**: Update i18n, closes [#9480](https://github.com/lobehub/lobe-chat/issues/9480) ([dfeb42c](https://github.com/lobehub/lobe-chat/commit/dfeb42c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-30 04:45:58 +00:00
LobeHub Bot dfeb42ce1c 🤖 style: update i18n (#9480)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-09-30 12:36:35 +08:00
renovate[bot] 138071d0e3 Update dependency ollama to ^0.6.0 (#9461)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-30 12:32:00 +08:00
lobehubbot 1aaa5f5152 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-29 20:33:43 +00:00
semantic-release-bot e1acbf21fd 🔖 chore(release): v1.133.0 [skip ci]
## [Version&nbsp;1.133.0](https://github.com/lobehub/lobe-chat/compare/v1.132.19...v1.133.0)
<sup>Released on **2025-09-29**</sup>

####  Features

- **misc**: Add builtin Python plugin, add Claude Sonnet 4.5 model to AI chat models.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's improved

* **misc**: Add builtin Python plugin, closes [#8873](https://github.com/lobehub/lobe-chat/issues/8873) ([fa6ef94](https://github.com/lobehub/lobe-chat/commit/fa6ef94))
* **misc**: Add Claude Sonnet 4.5 model to AI chat models, closes [#9476](https://github.com/lobehub/lobe-chat/issues/9476) ([a30a65c](https://github.com/lobehub/lobe-chat/commit/a30a65c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-29 20:32:18 +00:00
Arvin Xu 79bc08a076 🔨 chore: add claude translator and improve issue template (#9479)
* add translator

* use claude translator
2025-09-30 04:22:01 +08:00
Aloxaf fa6ef94067 feat: add builtin Python plugin (#8873)
* feat: 初步完成

* chore: type

* feat: 图片功能

* feat: 文件下载功能

* refactor: 简化代码

* chore: 清理代码

* chore: clean

* chore: 清理代码

* chore: 清理代码

* chore: 小改进

* fix: 上传完成前图片无法显示

* refactor: 增加 python-interpreter package

* chore: 清理

* feat: 传入上下文中的文件

* chore: 小优化

* chore: 中文字体

* chore: clean

* fix: 服务端部署

* fix: 重复文件检查

* test: 增加 interpreter.test.ts

* test: add worker.test.ts

* style: fix import

* test: fix

* style: fix import

* style: move env file to envs

* style: 限制代码框高度

* style: 重命名

* misc: 小修小补

* refactor: 重命名为 code-interpreter

---------

Co-authored-by: Arvin Xu <arvinx@foxmail.com>
2025-09-30 04:20:57 +08:00
YuTengjing a30a65cd4c feat: add Claude Sonnet 4.5 model to AI chat models (#9476) 2025-09-30 04:20:51 +08:00
Arvin Xu 2e6018a496 🔨 chore: fix types (#9478)
fix types
2025-09-30 03:54:12 +08:00
Arvin Xu 1776a24943 🔨 chore: add auto close issues workflow (#9473)
* add auto close issues

* update
2025-09-30 02:14:47 +08:00
renovate[bot] 27d133a417 Update dependency @opentelemetry/exporter-metrics-otlp-http to ^0.205.0 (#9454)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 18:49:24 +08:00
renovate[bot] de3478b17a Update dependency @opentelemetry/instrumentation to ^0.205.0 (#9456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 18:49:05 +08:00
renovate[bot] 694cdbea8f Update dependency @opentelemetry/sdk-node to ^0.205.0 (#9460)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 18:48:26 +08:00
lobehubbot ffbb804b3f 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-29 08:18:20 +00:00
semantic-release-bot 5947148c01 🔖 chore(release): v1.132.19 [skip ci]
### [Version&nbsp;1.132.19](https://github.com/lobehub/lobe-chat/compare/v1.132.18...v1.132.19)
<sup>Released on **2025-09-29**</sup>

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-29 08:17:23 +00:00
René Wang b0cb96e5c2 👷 build: Add virtual column to agent table (#9464) 2025-09-29 10:07:19 +02:00
lobehubbot f1d732d166 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-28 05:29:59 +00:00
semantic-release-bot c6de50e385 🔖 chore(release): v1.132.18 [skip ci]
### [Version&nbsp;1.132.18](https://github.com/lobehub/lobe-chat/compare/v1.132.17...v1.132.18)
<sup>Released on **2025-09-28**</sup>

#### 🐛 Bug Fixes

- **misc**: Refactor tools-engine and fix search token count.

#### 💄 Styles

- **misc**: Update i18n.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Refactor tools-engine and fix search token count, closes [#9448](https://github.com/lobehub/lobe-chat/issues/9448) ([e82d4b7](https://github.com/lobehub/lobe-chat/commit/e82d4b7))

#### Styles

* **misc**: Update i18n, closes [#9449](https://github.com/lobehub/lobe-chat/issues/9449) ([b04a5d7](https://github.com/lobehub/lobe-chat/commit/b04a5d7))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-28 05:28:51 +00:00
LobeHub Bot b04a5d7906 🤖 style: update i18n (#9449)
💄 style: update i18n

Co-authored-by: canisminor1990 <17870709+canisminor1990@users.noreply.github.com>
2025-09-28 13:18:26 +08:00
renovate[bot] 088cc2c56c Update pnpm to v10.17.1 (#9450)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 13:13:23 +08:00
renovate[bot] acb49c1393 Update dependency @anthropic-ai/sdk to ^0.64.0 (#9451)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-28 13:12:25 +08:00
Arvin Xu e82d4b7274 🐛 fix: refactor tools-engine and fix search token count (#9448)
* init toolsEngine

* clean implement

* refactor with tools-engine

* add more tests

* update

* rename

* refactor the tools engine

* refactor code

* refactor helpers

* fix tools is empty issue

* fix tests

* refactor to remove enabledSchema

* support defaultToolIds

* fix tests

* fix tests
2025-09-28 13:12:14 +08:00
lobehubbot 2dc03b47d6 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-27 05:58:05 +00:00
semantic-release-bot 3e64ee659e 🔖 chore(release): v1.132.17 [skip ci]
### [Version&nbsp;1.132.17](https://github.com/lobehub/lobe-chat/compare/v1.132.16...v1.132.17)
<sup>Released on **2025-09-27**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix input empty group name.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Fix input empty group name, closes [#9441](https://github.com/lobehub/lobe-chat/issues/9441) ([f653ce1](https://github.com/lobehub/lobe-chat/commit/f653ce1))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-27 05:57:02 +00:00
huangkairan f653ce1737 🐛 fix: fix input empty group name (#9441)
fix: cant input empty group name
2025-09-27 07:47:21 +02:00
Arvin Xu eeabb69088 ️ perf: fix battery usage (#9444) 2025-09-27 07:46:18 +02:00
sxjeru 356cf029dd feat: Add new provider Ollama Cloud (#9435) 2025-09-27 07:45:47 +02:00
lobehubbot 6e7b420347 📝 docs(bot): Auto sync agents & plugin to readme 2025-09-26 15:43:27 +00:00
semantic-release-bot ee464838ac 🔖 chore(release): v1.132.16 [skip ci]
### [Version&nbsp;1.132.16](https://github.com/lobehub/lobe-chat/compare/v1.132.15...v1.132.16)
<sup>Released on **2025-09-26**</sup>

#### 🐛 Bug Fixes

- **misc**: Resolve qwen-image-edit imageUrls conversion issue.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

* **misc**: Resolve qwen-image-edit imageUrls conversion issue, closes [#9414](https://github.com/lobehub/lobe-chat/issues/9414) ([ec5af1b](https://github.com/lobehub/lobe-chat/commit/ec5af1b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>
2025-09-26 15:42:22 +00:00
Maple Gao ec5af1b4c7 🐛 fix: resolve qwen-image-edit imageUrls conversion issue (#9414)
* 🔧 fix: resolve missing imageurls convertion

*  test: add comprehensive tests for qwen-image-edit imageUrls conversion

- Add tests for imageUrls array to imageUrl conversion
- Add tests for multiple elements using first element
- Add tests for empty array error handling
- Add tests for imageUrl priority over imageUrls
- Add tests for missing parameters error handling
- All 20 test cases pass (5 new + 15 existing)

---------

Co-authored-by: Arvin Xu <arvinx@foxmail.com>
2025-09-26 17:32:18 +02:00
1872 changed files with 185922 additions and 17449 deletions
+38
View File
@@ -0,0 +1,38 @@
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
description: Find duplicate GitHub issues
---
Find up to 3 likely duplicate issues for a given GitHub issue.
To do this, follow these steps precisely:
1. Use an agent to check if the Github issue (a) is closed, (b) does not need to be deduped (eg. because it is broad product feedback without a specific solution, or positive feedback), or (c) already has a duplicates comment that you made earlier. If so, do not proceed.
2. Use an agent to view a Github issue, and ask the agent to return a summary of the issue
3. Then, launch 5 parallel agents to search Github for duplicates of this issue, using diverse keywords and search approaches, using the summary from #1
4. Next, feed the results from #1 and #2 into another agent, so that it can filter out false positives, that are likely not actually duplicates of the original issue. If there are no duplicates remaining, do not proceed.
5. Finally, comment back on the issue with a list of up to three duplicate issues (or zero, if there are no likely duplicates)
Notes (be sure to tell this to your agents, too):
- Use `gh` to interact with Github, rather than web fetch
- Do not use other tools, beyond `gh` (eg. don't use other MCP servers, file edit, etc.)
- Make a todo list first
- For your comment, follow the following format precisely (assuming for this example that you found 3 suspected duplicates):
---
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
> 🤖 Generated with Claude Code
---
+253
View File
@@ -0,0 +1,253 @@
# Issue Triage Guide
This guide is used for batch triaging GitHub issues - analyzing issues and applying appropriate labels.
## Workflow
For EACH issue, follow these steps:
### Step 1: Get Available Labels (run once per batch)
```bash
gh label list --json name,description --limit 300
```
### Step 2: Get Issue Details
For each issue number, run:
```bash
gh issue view [ISSUE_NUMBER] --json number,title,body,labels,comments
```
### Step 3: Analyze and Select Labels
Extract information from the issue template and content:
#### Template Fields Mapping
- 📦 Platform field → `platform:web/desktop/mobile`
- 💻 Operating System → `os:windows/macos/linux/ios`
- 🌐 Browser → `device:pc/mobile`
- 📦 Deployment mode → `deployment:server/client/pglite`
- Platform (hosting) → `hosting:cloud/self-host/vercel/zeabur/railway`
#### Provider Detection
**IMPORTANT**: Always check issue title and body for provider mentions!
**Official Providers** (check for these keywords in title/body):
- `openai`, `gpt``provider:openai`
- `gemini``provider:gemini`
- `claude`, `anthropic``provider:claude`
- `deepseek``provider:deepseek`
- `google``provider:google`
- `ollama``provider:ollama`
- `azure``provider:azure`
- `bedrock``provider:bedrock`
- `vertex``provider:vertex`
- `groq`, `grok``provider:groq`
- `mistral``provider:mistral`
- `moonshot``provider:moonshot`
- `zhipu``provider:zhipu`
- `minimax``provider:minimax`
- `doubao``provider:doubao`
**Third-party Aggregation Providers**:
- `aihubmix`, `AIHubMix`, `AIHUBMIX``provider:aihubmix`
- Check environment variables like `AIHUBMIX_*` in issue body
**Multiple Providers**: If issue mentions multiple providers, add ALL applicable provider labels.
### Label Categories
#### a) Issue Type (select ONE if applicable)
- `💄 Design` - UI/UX design issues
- `📝 Documentation` - Documentation improvements
- `⚡️ Performance` - Performance optimization
#### b) Priority (select ONE if applicable)
- `priority:high` - Critical issues, data loss, security, maintainer mentions "urgent"/"serious"/"critical"
- `priority:medium` - Important issues affecting multiple users, significant functionality impact
- `priority:low` - Nice to have, minor issues, edge cases
**Priority Guidelines**:
- Set `priority:high` for: data loss, authentication failures, deployment blockers, critical bugs
- Set `priority:medium` for: feature bugs affecting multiple users, workflow issues
- Set `priority:low` for: cosmetic issues, feature requests, configuration questions
#### c) Platform (select ALL applicable)
- `platform:web`
- `platform:desktop`
- `platform:mobile`
#### d) Device (for platform:web, select ONE)
- `device:pc`
- `device:mobile`
#### e) Operating System (select ALL applicable)
- `os:windows`
- `os:macos`
- `os:linux`
- `os:ios`
- `os:android`
#### f) Hosting Platform (select ONE)
- `hosting:cloud` - Official LobeHub Cloud
- `hosting:self-host` - Self-hosted deployment
- `hosting:vercel` - Vercel deployment
- `hosting:zeabur` - Zeabur deployment
- `hosting:railway` - Railway deployment
#### g) Deployment Mode (select ONE if mentioned)
- `deployment:server` - Server-side database mode
- `deployment:client` - Client-side database mode
- `deployment:pglite` - PGLite mode
**Additional deployment tags**:
- `docker` - If using Docker deployment
- `electron` - If desktop/Electron specific
#### h) Model Provider (select ALL applicable)
See "Provider Detection" section above for complete list.
**IMPORTANT**: Always scan issue title and body for provider keywords!
#### i) Feature/Component (select ALL applicable)
Core Features:
- `feature:settings` - Settings and configuration
- `feature:agent` - Agent/Assistant functionality
- `feature:topic` - Topic/Conversation management
- `feature:marketplace` - Agent marketplace
File & Knowledge:
- `feature:files` - File upload/management
- `feature:knowledge-base` - Knowledge base and RAG
- `feature:export` - Export functionality
Model Capabilities:
- `feature:streaming` - Streaming responses
- `feature:tool` - Tool calling
- `feature:vision` - Vision/multimodal capabilities
- `feature:image` - AI image generation
- `feature:dalle` - DALL-E specific
- `feature:tts` - Text-to-speech
Technical:
- `feature:api` - Backend API
- `feature:auth` - Authentication/authorization
- `feature:sync` - Cloud sync functionality
- `feature:search` - Search functionality
- `feature:mcp` - MCP integration
- `feature:editor` - Lobe Editor
- `feature:markdown` - Markdown rendering
- `feature:thread` - Thread/Subtopic functionality
Collaboration:
- `feature:group-chat` - Group chat functionality
- `feature:memory` - Memory feature
- `feature:team-workspace` - Team workspace
#### j) Workflow/Status
- `Duplicate` - Only if duplicate of an OPEN issue (mention issue number)
- `needs-reproduction` - Cannot reproduce, needs more information
- `good-first-issue` - Good for first-time contributors
- `🤔 Need Reproduce` - Needs reproduction steps
### Step 4: Apply Labels
Add labels (comma-separated, no spaces after commas):
```bash
gh issue edit [ISSUE_NUMBER] --add-label "label1,label2,label3"
```
Remove "unconfirm" label if adding other labels:
```bash
gh issue edit [ISSUE_NUMBER] --remove-label "unconfirm"
```
**Important**: Combine both commands when possible for efficiency.
### Step 5: Log Summary
For each issue, provide reasoning (2-4 sentences):
- Labels applied and why
- Key factors from issue template/comments
- Provider detection reasoning (if applicable)
## Important Rules
1. **Read Carefully**: Read issue template fields AND issue body/title for complete context
2. **Provider Detection**: ALWAYS check title and body for provider keywords (including aihubmix, etc.)
3. **Multiple Categories**: Use ALL applicable labels from different categories
4. **Label Prefixes**: Always use proper prefixes (`feature:`, `provider:`, `os:`, `platform:`, etc.)
5. **Maintainer Comments**: Check maintainer comments for priority/status hints
6. **No Comments**: Only apply labels, DO NOT post comments to issues
7. **Batch Efficiency**: Process issues in parallel when possible
## Common Patterns
### Provider in Environment Variables
If issue body contains `AIHUBMIX_*`, add `provider:aihubmix`
### Multiple Provider Issues
If comparing providers (e.g., "works with OpenAI but not Gemini"), add both provider labels
### Desktop Issues
Desktop issues often need: `platform:desktop`, `electron`, specific `os:*`, and `deployment:client` or `deployment:server`
### Knowledge Base Issues
Usually need: `feature:knowledge-base`, often with `feature:files`, may need `provider:*` for embedding models
### Tool Calling Issues
Usually need: `feature:tool`, specific `provider:*`, may need `feature:mcp` if MCP-related
### Streaming Issues
Usually need: `feature:streaming`, specific `provider:*`, check for timeout/performance issues
## Example Triage
**Issue #8850**: "aihubmix 的优惠 app 没有生效"
**Analysis**:
- Title contains "aihubmix" → `provider:aihubmix`
- Template shows: Windows, Chrome, Docker, Client mode
- About API discount codes not working
**Labels Applied**:
```bash
gh issue edit 8850 --add-label "provider:aihubmix,platform:web,os:windows,deployment:client,hosting:self-host,docker"
gh issue edit 8850 --remove-label "unconfirm"
```
**Reasoning**: AIHubMix provider discount feature not working. Client mode deployment on Windows with Docker. Provider detection from title keyword "aihubmix".
+135
View File
@@ -0,0 +1,135 @@
# Team Assignment Guide
## Quick Reference by Name
- **@arvinxx**: Last resort only, mention for priority:high issues, tool calling , mcp
- **@canisminor1990**: Design, UI components, editor
- **@tjx666**: Image/video generation, vision, cloud, documentation, TTS
- **@ONLY-yours**: Performance, streaming, settings, general bugs, web platform, marketplace
- **@RiverTwilight**: Knowledge base, files (KB-related), group chat
- **@nekomeowww**: Memory, backend, deployment, DevOps
- **@sudongyuer**: Mobile app (React Native)
- **@sxjeru**: Model providers and configuration
- **@cy948**: Auth Modules
- **@rdmclin2**: Team workspace
Quick reference for assigning issues based on labels.
## Label to Team Member Mapping
### Provider Labels (provider:\*)
| Label | Owner | Notes |
| ---------------- | ------- | -------------------------------------------- |
| All `provider:*` | @sxjeru | Model configuration and provider integration |
### Platform Labels (platform:\*)
| Label | Owner | Notes |
| ------------------ | ----------- | -------------------------------------- |
| `platform:mobile` | @sudongyuer | React Native mobile app |
| `platform:desktop` | @ONLY-yours | Electron desktop client (general) |
| `platform:web` | @ONLY-yours | Web platform (unless specific feature) |
### Feature Labels (feature:\*)
| Label | Owner | Notes |
| ------------------------ | --------------- | ----------------------------------------------------------------------- |
| `feature:image` | @tjx666 | AI image generation |
| `feature:dalle` | @tjx666 | DALL-E related |
| `feature:vision` | @tjx666 | Vision/multimodal generation |
| `feature:knowledge-base` | @RiverTwilight | Knowledge base and RAG |
| `feature:files` | @RiverTwilight | File upload/management (when KB-related)<br>@ONLY-yours (general files) |
| `feature:editor` | @canisminor1990 | Lobe Editor |
| `feature:auth` | @cy948 | Authentication/authorization |
| `feature:api` | @nekomeowww | Backend API |
| `feature:streaming` | @arvinxx | Streaming response |
| `feature:settings` | @ONLY-yours | Settings and configuration |
| `feature:agent` | @ONLY-yours | Agent/Assistant |
| `feature:topic` | @ONLY-yours | Topic/Conversation management |
| `feature:thread` | @arvinxx | Thread/Subtopic |
| `feature:marketplace` | @ONLY-yours | Agent marketplace |
| `feature:tool` | @arvinxx | Tool calling |
| `feature:mcp` | @arvinxx | MCP integration |
| `feature:search` | @ONLY-yours | Search functionality |
| `feature:tts` | @tjx666 | Text-to-speech |
| `feature:export` | @ONLY-yours | Export functionality |
| `feature:group-chat` | @RiverTwilight | Group chat functionality |
| `feature:memory` | @nekomeowww | Memory feature |
| `feature:team-workspace` | @rdmclin2 | Team workspace application |
### Deployment Labels (deployment:\*)
| Label | Owner | Notes |
| ------------------ | ----------- | -------------------------- |
| All `deployment:*` | @nekomeowww | Server/client/pglite modes |
### Hosting Labels (hosting:\*)
| Label | Owner | Notes |
| ------------------- | ----------- | ---------------------- |
| `hosting:cloud` | @tjx666 | Official LobeHub Cloud |
| `hosting:self-host` | @nekomeowww | Self-hosting issues |
| `hosting:vercel` | @nekomeowww | Vercel deployment |
| `hosting:zeabur` | @nekomeowww | Zeabur deployment |
| `hosting:railway` | @nekomeowww | Railway deployment |
### Issue Type Labels
| Label | Owner | Notes |
| ------------------ | -------------------- | ---------------------------- |
| 💄 Design | @canisminor1990 | Design and styling |
| 📝 Documentation | @tjx666 | Documentation |
| ⚡️ Performance | @ONLY-yours | Performance optimization |
| 🐛 Bug | (depends on feature) | Assign based on other labels |
| 🌠 Feature Request | (depends on feature) | Assign based on other labels |
## Assignment Rules
### Priority Order (apply in order)
1. **Specific feature owner** - e.g., `feature:knowledge-base`@RiverTwilight
2. **Platform owner** - e.g., `platform:mobile`@sudongyuer
3. **Provider owner** - e.g., `provider:*`@sxjeru
4. **Component owner** - e.g., 💄 Design → @canisminor1990
5. **Infrastructure owner** - e.g., `deployment:*`@nekomeowww
6. **General maintainer** - @ONLY-yours for general bugs/issues
7. **Last resort** - @arvinxx (only if no clear owner)
### Special Cases
**Multiple labels with different owners:**
- Mention the **most specific** feature owner first
- Mention secondary owners if their input is valuable
- Example: `feature:knowledge-base` + `deployment:server`@RiverTwilight (primary), @nekomeowww (secondary)
**Priority:high issues:**
- Mention feature owner + @arvinxx
- Example: `priority:high` + `feature:image`@tjx666 @arvinxx
**No clear owner:**
- Assign to @ONLY-yours for general issues
- Only mention @arvinxx if critical and truly unclear
## Comment Templates
**Single owner:**
```
@username - This is a [feature/component] issue. Please take a look.
```
**Multiple owners:**
```
@primary @secondary - This involves [features]. Please coordinate.
```
**High priority:**
```
@owner @arvinxx - High priority [feature] issue.
```
+175
View File
@@ -0,0 +1,175 @@
---
description: Guide for adding environment variables to configure user settings
alwaysApply: false
---
# Adding Environment Variable for User Settings
Add server-side environment variables to configure default values for user settings.
**Priority**: User Custom > Server Env Var > Hardcoded Default
## Steps
### 1. Define Environment Variable
Create `src/envs/<domain>.ts`:
```typescript
import { createEnv } from '@t3-oss/env-nextjs';
import { z } from 'zod';
export const get<Domain>Config = () => {
return createEnv({
server: {
YOUR_ENV_VAR: z.coerce.number().min(MIN).max(MAX).optional(),
},
runtimeEnv: {
YOUR_ENV_VAR: process.env.YOUR_ENV_VAR,
},
});
};
export const <domain>Env = get<Domain>Config();
```
### 2. Update Type (Optional)
**Skip this step if the domain field already exists in `GlobalServerConfig`.**
Add to `packages/types/src/serverConfig.ts`:
```typescript
export interface GlobalServerConfig {
<domain>?: {
<settingName>?: <type>;
};
}
```
**Prefer reusing existing types** from `packages/types/src/user/settings` with `PartialDeep`:
```typescript
import { User<Domain>Config } from './user/settings';
export interface GlobalServerConfig {
<domain>?: PartialDeep<User<Domain>Config>;
}
```
### 3. Assemble Server Config (Optional)
**Skip this step if the domain field already exists in server config.**
In `src/server/globalConfig/index.ts`:
```typescript
import { <domain>Env } from '@/envs/<domain>';
import { cleanObject } from '@/utils/object';
export const getServerGlobalConfig = async () => {
const config: GlobalServerConfig = {
// ...
<domain>: cleanObject({
<settingName>: <domain>Env.YOUR_ENV_VAR,
}),
};
return config;
};
```
If the domain already exists, just add the new field to the existing `cleanObject()`:
```typescript
<domain>: cleanObject({
existingField: <domain>Env.EXISTING_VAR,
<settingName>: <domain>Env.YOUR_ENV_VAR, // Add this line
}),
```
### 4. Merge to User Store (Optional)
**Skip this step if the domain field already exists in `serverSettings`.**
In `src/store/user/slices/common/action.ts`, add to `serverSettings`:
```typescript
const serverSettings: PartialDeep<UserSettings> = {
defaultAgent: serverConfig.defaultAgent,
<domain>: serverConfig.<domain>, // Add this line
// ...
};
```
### 5. Update .env.example
```bash
# <Description> (range/options, default: X)
# YOUR_ENV_VAR=<example>
```
### 6. Update Documentation
Update both English and Chinese documentation:
- `docs/self-hosting/environment-variables/basic.mdx`
- `docs/self-hosting/environment-variables/basic.zh-CN.mdx`
Add new section or subsection with environment variable details (type, description, default, example, range/constraints).
## Type Reuse
**Prefer reusing existing types** from `packages/types/src/user/settings` instead of defining inline types in `serverConfig.ts`.
```typescript
// ✅ Good - reuse existing type
import { UserImageConfig } from './user/settings';
export interface GlobalServerConfig {
image?: PartialDeep<UserImageConfig>;
}
// ❌ Bad - inline type definition
export interface GlobalServerConfig {
image?: {
defaultImageNum?: number;
};
}
```
## Example: AI_IMAGE_DEFAULT_IMAGE_NUM
```typescript
// src/envs/image.ts
export const getImageConfig = () => {
return createEnv({
server: {
AI_IMAGE_DEFAULT_IMAGE_NUM: z.coerce.number().min(1).max(20).optional(),
},
runtimeEnv: {
AI_IMAGE_DEFAULT_IMAGE_NUM: process.env.AI_IMAGE_DEFAULT_IMAGE_NUM,
},
});
};
// packages/types/src/serverConfig.ts
import { UserImageConfig } from './user/settings';
export interface GlobalServerConfig {
image?: PartialDeep<UserImageConfig>;
}
// src/server/globalConfig/index.ts
image: cleanObject({
defaultImageNum: imageEnv.AI_IMAGE_DEFAULT_IMAGE_NUM,
}),
// src/store/user/slices/common/action.ts
const serverSettings: PartialDeep<UserSettings> = {
image: serverConfig.image,
// ...
};
// .env.example
# AI_IMAGE_DEFAULT_IMAGE_NUM=4
```
-176
View File
@@ -1,176 +0,0 @@
---
description:
globs: src/services/**/*,src/database/**/*,src/server/**/*
alwaysApply: false
---
# LobeChat 后端技术架构指南
本指南旨在阐述 LobeChat 项目的后端分层架构,重点介绍各核心目录的职责以及它们之间的协作方式。
## 目录结构映射
```
src/
├── server/
│ ├── routers/ # tRPC API 路由定义
│ └── services/ # 业务逻辑服务层
│ └── */impls/ # 平台特定实现
├── database/
│ ├── models/ # 数据模型 (单表 CRUD)
│ ├── repositories/ # 仓库层 (复杂查询/聚合)
│ └── schemas/ # Drizzle ORM 表定义
└── services/ # 客户端服务 (调用 tRPC 或直接访问 Model)
```
## 核心架构分层
LobeChat 的后端设计注重模块化、可测试性和灵活性,以适应不同的运行环境(如浏览器端 PGLite、服务端远程 PostgreSQL 以及 Electron 桌面应用)。
其主要分层如下:
1. 客户端服务层 (`src/services`):
- 位于 src/services/。
- 这是客户端业务逻辑的核心层,负责封装各种业务操作和数据处理逻辑。
- 环境适配: 根据不同的运行环境,服务层会选择合适的数据访问方式:
- 本地数据库模式: 直接调用 `Model` 层进行数据操作,适用于浏览器 PGLite 和本地 Electron 应用。
- 远程数据库模式: 通过 `tRPC` 客户端调用服务端 API,适用于需要云同步的场景。
- 类型转换: 对于简单的数据类型转换,直接在此层进行类型断言,如 `this.pluginModel.query() as Promise<LobeTool[]>`
- 每个服务模块通常包含 `client.ts`(本地模式)、`server.ts`(远程模式)和 `type.ts`(接口定义)文件,在实现时应该确保本地模式和远程模式业务逻辑实现一致,只是数据库不同。
2. API 接口层 (`TRPC`):
- 位于 src/server/routers/
- 使用 `tRPC` 构建类型安全的 API。Router 根据运行时环境(如 Edge Functions, Node.js Lambda)进行组织。
- 负责接收客户端请求,并将其路由到相应的 `Service` 层进行处理。
- 新建 lambda 端点时可以参考 src/server/routers/lambda/\_template.ts
3. 仓库层 (`Repositories`):
- 位于 src/database/repositories/。
- 主要处理复杂的跨表查询和数据聚合逻辑,特别是当需要从多个 `Model` 获取数据并进行组合时。
- 与 `Model` 层不同,`Repository` 层专注于复杂的业务查询场景,而不涉及简单的领域模型转换。
- 当业务逻辑涉及多表关联、复杂的数据统计或需要事务处理时,会使用 `Repository` 层。
- 如果数据操作简单(仅涉及单个 `Model`),则通常直接在 `src/services` 层调用 `Model` 并进行简单的类型断言。
4. 模型层 (`Models`):
- 位于 src/database/models/ (例如 src/database/models/plugin.ts 和 src/database/models/document.ts)。
- 提供对数据库中各个表(由 src/database/schemas/ 中的 Drizzle ORM schema 定义)的基本 CRUD (创建、读取、更新、删除) 操作和简单的查询能力。
- `Model` 类专注于单个数据表的直接操作,不涉及复杂的领域模型转换,这些转换通常在上层的 `src/services` 中通过类型断言完成。
- model(例如 Topic 层接口经常需要从对应的 schema 层导入 NewTopic 和 TopicItem
- 创建新的 model 时可以参考 src/database/models/\_template.ts
5. 数据库 (`Database`):
- 客户端模式 (浏览器/PWA): 使用 PGLite (基于 WASM 的 PostgreSQL),数据存储在用户浏览器本地。
- 服务端模式 (云部署): 使用远程 PostgreSQL 数据库。
- Electron 桌面应用:
- Electron 客户端会启动一个本地 Node.js 服务。
- 本地服务通过 `tRPC` 与 Electron 的渲染进程通信。
- 数据库选择依赖于是否开启云同步功能:
- 云同步开启: 连接到远程 PostgreSQL 数据库。
- 云同步关闭: 使用 PGLite (通过 Node.js 的 WASM 实现) 在本地存储数据。
## 数据流向说明
### 浏览器/PWA 模式
```
UI (React) → Zustand action -> Client Service → Model Layer → PGLite (本地数据库)
```
### 服务端模式
```
UI (React) → Zustand action → Client Service -> TRPC Client → TRPC Routers → Repositories/Models → Remote PostgreSQL
```
### Electron 桌面应用模式
```
UI (Electron Renderer) → Zustand action → Client Service -> TRPC Client → 本地 Node.js 服务 → TRPC Routers → Repositories/Models → PGLite/Remote PostgreSQL (取决于云同步设置)
```
## 服务层 (Server Services)
- 位于 src/server/services/。
- 核心职责是封装独立的、可复用的业务逻辑单元。这些服务应易于测试。
- 平台差异抽象: 一个关键特性是通过其内部的 `impls` 子目录(例如 src/server/services/file/impls 包含 s3.ts 和 local.ts)来抹平不同运行环境带来的差异(例如云端使用 S3 存储,桌面版使用本地文件系统)。这使得上层(如 `tRPC` routers)无需关心底层具体实现。
- 目标是使 `tRPC` router 层的逻辑尽可能纯粹,专注于请求处理和业务流程编排。
- 服务可能会调用 `Repository` 层或直接调用 `Model` 层进行数据持久化和检索,也可能调用其他服务。
## 最佳实践 (Best Practices)
### 数据库操作封装原则
**连续的数据库操作应该封装到 Model 层**
当业务逻辑涉及多个相关的数据库操作时,建议将这些操作封装到 Model 层中,而不是在上层(Service 或 Router 层)中进行多次数据库调用。
**优势:**
- **代码复用**: Client DB 环境的 service 实现和 Server DB 的 lambda 层实现可以复用相同的 Model 方法
- **事务一致性**: 相关的数据库操作可以在同一个方法中管理,便于维护数据一致性
- **性能优化**: 减少数据库连接次数,提高查询效率
- **职责清晰**: Model 层专注数据访问,上层专注业务协调
**示例:**
```typescript
// ✅ 推荐:在 Model 层封装连续的数据库操作
class GenerationBatchModel {
async delete(id: string): Promise<{ deletedBatch: BatchItem; thumbnailUrls: string[] }> {
// 1. 查询相关数据
const batchWithGenerations = await this.db.query.generationBatches.findFirst({...});
// 2. 收集需要处理的数据
const thumbnailUrls = [...];
// 3. 执行删除操作
const [deletedBatch] = await this.db.delete(generationBatches)...;
return { deletedBatch, thumbnailUrls };
}
}
// ✅ 上层使用简洁
const { thumbnailUrls } = await model.delete(id);
await fileService.deleteFiles(thumbnailUrls);
```
### 文件操作与数据库操作的执行顺序
**删除操作原则:数据库删除在前,文件删除在后**
当业务逻辑同时涉及数据库记录和文件系统操作时,应该遵循"数据库优先"的原则。
**原因:**
- **用户体验优先**: 如果先删除文件再删除数据库记录,可能出现文件已删除但数据库记录仍存在的情况,用户访问时会遇到文件不存在的错误
- **影响程度较小**: 如果先删除数据库记录再删除文件,即使文件删除失败,用户也看不到这个记录,只是造成一些存储空间浪费,对用户体验影响更小
- **数据一致性**: 数据库记录是业务逻辑的核心,应该优先保证其一致性
**示例:**
```typescript
// ✅ 推荐:先删除数据库记录,再删除文件
async deleteGeneration(id: string) {
// 1. 先删除数据库记录
const deletedGeneration = await generationModel.delete(id);
// 2. 再删除相关文件
if (deletedGeneration.asset?.thumbnailUrl) {
await fileService.deleteFile(deletedGeneration.asset.thumbnailUrl);
}
}
// ❌ 不推荐:先删除文件
async deleteGeneration(id: string) {
const generation = await generationModel.findById(id);
// 如果这里删除成功,但后面数据库删除失败,用户会遇到访问错误
await fileService.deleteFile(generation.asset.thumbnailUrl);
await generationModel.delete(id); // 可能失败
}
```
**创建操作原则:数据库创建在前,文件操作在后**
创建操作同样应该优先处理数据库记录,确保数据的一致性和完整性。
-58
View File
@@ -1,58 +0,0 @@
---
description: How to code review
globs:
alwaysApply: false
---
# Role Description
- You are a senior full-stack engineer skilled in performance optimization, security, and design systems.
- You excel at reviewing code and providing constructive feedback.
- Your task is to review submitted Git diffs **in Chinese** and return a structured review report.
- Review style: concise, direct, focused on what matters most, with actionable suggestions.
## Before the Review
Gather the modified code and context. Please strictly follow the process below:
1. Use `read_file` to read [package.json](mdc:package.json)
2. Use terminal to run command `git diff HEAD | cat` to obtain the diff and list the changed files. If you recieived empty result, run the same command once more.
3. Use `read_file` to open each changed file.
4. Use `read_file` to read [rules-attach.mdc](mdc:.cursor/rules/rules-attach.mdc). Even if you think it's unnecessary, you must read it.
5. combine changed files, step3 and `agent_requestable_workspace_rules`, list the rules which need to read
6. Use `read_file` to read the rules list in step 5
## Review
### Code Style
read [typescript.mdc](mdc:.cursor/rules/typescript.mdc) for the consolidated project code style and optimization rules.
### Code Optimization
The optimization checklist has been consolidated into [typescript.mdc](mdc:.cursor/rules/typescript.mdc): loops, debouncing/throttling, design system components, theming tokens, concurrency with `Promise.*`, minimal DB column selection, and package reuse.
### Obvious Bugs
- Do not silently swallow errors in `catch` blocks; at minimum, log them.
- Revert temporary code used only for testing (e.g., debug logs, temporary configs).
- Remove empty handlers (e.g., an empty `onClick`).
- Confirm the UI degrades gracefully for unauthenticated users.
- Don't leave any debug logs in the code (except when using the `debug` module properly).
- When using the `debug` module, avoid `import { log } from 'debug'` as it logs directly to console. Use proper debug namespaces instead.
- Check logs for sensitive information like api key, etc
## After the Review: output
1. Summary
- Start with a brief explanation of what the change set does.
- Summarize the changes for each modified file (or logical group).
2. Comments Issues
- List the most critical issues first.
- Use an ordered list, which will be convenient for me to reference later.
- For each issue:
- Mark severity tag (`❌ Must fix`, `⚠️ Should fix`, `💅 Nitpick`)
- Provode file path to the relevant file.
- Provide recommended fix
- End with a **git commit** command, instruct the author to run it.
- We use gitmoji to label commit messages, format: [emoji] <type>(<scope>): <subject>
-32
View File
@@ -1,32 +0,0 @@
---
description:
globs:
alwaysApply: true
---
# Guide to Optimize Output(Response) Rendering
## File Path and Code Symbol Rendering
- When rendering file paths, use backtick wrapping instead of markdown links so they can be parsed as clickable links in Cursor IDE.
- Good: `src/components/Button.tsx`
- Bad: [src/components/Button.tsx](src/components/Button.tsx)
- Don't use line and column number in file path, this will make file path not clickable in Cursor IDE.
- Good: `src/components/Button.tsx` `10:20` (add a space between the file path and the line and column number)
- Bad: `src/components/Button.tsx:10:20`
- When rendering functions, variables, or other code symbols, use backtick wrapping so they can be parsed as navigable links in Cursor IDE
- Good: The `useState` hook in `MyComponent`
- Bad: The useState hook in MyComponent
## Markdown Render
- don't use br tag to wrap in table cell
## Terminal Command Output
- If terminal commands don't produce output, it's likely due to paging issues. Try piping the command to `cat` to ensure full output is displayed.
- Good: `git show commit_hash -- file.txt | cat`
- Good: `git log --oneline | cat`
- Reason: Some git commands use pagers by default, which may prevent output from being captured properly
+25
View File
@@ -0,0 +1,25 @@
---
globs: packages/database/migrations/**/*
alwaysApply: false
---
# Database Migrations Guide
## Defensive Programming - Use Idempotent Clauses
Always use defensive clauses to make migrations idempotent:
```sql
-- ✅ Good: Idempotent operations
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "avatar" text;
DROP TABLE IF EXISTS "old_table";
CREATE INDEX IF NOT EXISTS "users_email_idx" ON "users" ("email");
ALTER TABLE "posts" DROP COLUMN IF EXISTS "deprecated_field";
-- ❌ Bad: Non-idempotent operations
ALTER TABLE "users" ADD COLUMN "avatar" text;
DROP TABLE "old_table";
CREATE INDEX "users_email_idx" ON "users" ("email");
```
**Important**: After modifying migration SQL (e.g., adding `IF NOT EXISTS` clauses), run `bun run db:generate-client` to update the hash in `packages/database/src/core/migrations.json`.
-8
View File
@@ -1,8 +0,0 @@
---
description:
globs: src/database/models/**/*
alwaysApply: false
---
1. first read [lobe-chat-backend-architecture.mdc](mdc:.cursor/rules/lobe-chat-backend-architecture.mdc)
2. refer to the [_template.ts](mdc:src/database/models/_template.ts) to create new model
3. if an operation involves multiple models or complex queries, consider defining it in the `repositories` layer under `src/database/repositories/`
+20 -28
View File
@@ -4,41 +4,33 @@ alwaysApply: true
## Project Description
You are developing an open-source, modern-design AI chat framework: lobe chat.
You are developing an open-source, modern-design AI chat framework: lobehub(previous lobe-chat).
Emoji logo: 🤯
Supported platforms:
- web desktop/mobile
- desktop(electron)
- mobile app(react native), coming soon
logo emoji: 🤯
## Project Technologies Stack
read [package.json](mdc:package.json) to know all npm packages you can use.
The project uses the following technologies:
- pnpm as package manager
- Next.js 15 for frontend and backend, using app router instead of pages router
- react 19, using hooks, functional components, react server components
- TypeScript programming language
- antd, `@lobehub/ui` for component framework
- Next.js 15
- react 19
- TypeScript
- `@lobehub/ui`, antd for component framework
- antd-style for css-in-js framework
- react-layout-kit for flex layout
- 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
- react-layout-kit for flex layout component
- react-i18next for i18n
- zustand for state management
- nuqs for search params management
- SWR for data fetch
- aHooks for react hooks library
- dayjs for date and time library
- dayjs for time library
- lodash-es for utility library
- fast-deep-equal for deep comparison of JavaScript objects
- zod for data validation
- TRPC for type safe backend
- PGLite for client DB and PostgreSQL for backend DB
- PGLite for client DB and Neon PostgreSQL for backend DB
- Drizzle ORM
- Vitest for testing, testing-library for react component test
- Prettier for code formatting
- ESLint for code linting
- Cursor AI for code editing and AI coding assistance
Note: All tools and libraries used are the latest versions. The application only needs to be compatible with the latest browsers;
- Vitest for testing
+104 -221
View File
@@ -5,235 +5,118 @@ alwaysApply: false
# LobeChat Project Structure
## Directory Structure
## Complete Project Structure
note: some files are not shown for simplicity.
This project uses common monorepo structure. The workspace packages name use `@lobechat/` namespace.
**note**: some not very important files are not shown for simplicity.
```plaintext
lobe-chat/
├── apps/ # Applications directory
│ └── desktop/ # Electron desktop application
│ ├── src/ # Desktop app source code
│ └── resources/ # Desktop app resources
├── docs/ # Project documentation
── development/ # Development docs
│ ├── self-hosting/ # Self-hosting docs
── usage/ # Usage guides
├── locales/ # Internationalization files (multiple locales)
│ ├── en-US/ # English (example)
└── zh-CN/ # Simplified Chinese (example)
├── packages/ # Monorepo packages directory
├── const/ # Constants definition package
├── database/ # Database related package
│ ├── electron-client-ipc/ # Electron renderer ↔ main IPC client
├── electron-server-ipc/ # Electron main process IPC server
├── model-bank/ # Built-in model presets/catalog exports
│ ├── model-runtime/ # AI model runtime package
├── types/ # TypeScript type definitions
├── utils/ # Utility functions package
├── file-loaders/ # File processing packages
│ ├── prompts/ # AI prompt management
└── web-crawler/ # Web crawling functionality
├── public/ # Static assets
├── icons/ # Application icons
── images/ # Image resources
│ └── screenshots/ # Application screenshots
├── scripts/ # Build and tool scripts
├── src/ # Main application source code (see below)
├── .cursor/ # Cursor AI configuration
├── docker-compose/ # Docker configuration
├── package.json # Project dependencies
├── pnpm-workspace.yaml # pnpm monorepo configuration
├── next.config.ts # Next.js configuration
├── drizzle.config.ts # Drizzle ORM configuration
└── tsconfig.json # TypeScript configuration
```
## Core Source Directory (`src/`)
```plaintext
src/
├── app/ # Next.js App Router routes
├── (backend)/ # Backend API routes
│ │ ├── api/ # REST API endpoints
│ │ │ ├── auth/ # Authentication endpoints
│ │ └── webhooks/ # Webhook handlers for various auth providers
│ ├── middleware/ # Request middleware
│ ├── oidc/ # OpenID Connect endpoints
│ │ ── trpc/ # tRPC API routes
│ │ ├── async/ # Async tRPC endpoints
│ │ ├── desktop/ # Desktop runtime endpoints
│ │ │ ├── edge/ # Edge runtime endpoints
│ │ │ ├── lambda/ # Lambda runtime endpoints
│ │ └── tools/ # Tools-specific endpoints
│ │ └── webapi/ # Web API endpoints
│ ├── chat/ # Chat-related APIs for various providers
│ │ ├── models/ # Model management APIs
│ ├── plugin/ # Plugin system APIs
│ │ ├── stt/ # Speech-to-text APIs
│ │ ├── text-to-image/ # Image generation APIs
│ │ ── tts/ # Text-to-speech APIs
├── [variants]/ # Page route variants
│ │ ├── (main)/ # Main application routes
│ │ │ ── chat/ # Chat interface and workspace
│ │ │ ├── discover/ # Discover page (assistants, models, providers)
│ │ ├── files/ # File management interface
│ │ │ ├── image/ # Image generation interface
│ │ │ ├── profile/ # User profile and stats
│ │ │ ── repos/ # Knowledge base repositories
│ │ │ └── settings/ # Application settings
│ └── @modal/ # Modal routes
└── manifest.ts # PWA configuration
├── components/ # Global shared components
├── Analytics/ # Analytics tracking components
│ ├── Error/ # Error handling components
│ └── Loading/ # Loading state components
── config/ # Application configuration
│ ├── featureFlags/ # Feature flags & experiments
│ └── modelProviders/ # Model provider configurations
├── features/ # Feature components (UI Layer)
│ ├── AgentSetting/ # Agent configuration and management
│ ├── ChatInput/ # Chat input with file upload and tools
│ ├── Conversation/ # Message display and interaction
│ ├── FileManager/ # File upload and knowledge base
│ └── PluginStore/ # Plugin marketplace and management
├── hooks/ # Custom React hooks
├── layout/ # Global layout components
│ ├── AuthProvider/ # Authentication provider
│ └── GlobalProvider/ # Global state provider
├── libs/ # External library integrations
│ ├── analytics/ # Analytics services integration
│ ├── next-auth/ # NextAuth.js configuration
│ └── oidc-provider/ # OIDC provider implementation
├── locales/ # Internationalization resources
│ └── default/ # Default language definitions
├── migrations/ # Client-side data migrations
├── server/ # Server-side code
│ ├── modules/ # Server modules
│ ├── routers/ # tRPC routers
│ └── services/ # Server services
├── services/ # Service layer (per-domain, client/server split)
│ ├── user/ # User services
│ │ ├── client.ts # Client DB (PGLite) implementation
│ │ └── server.ts # Server DB implementation (via tRPC)
│ ├── aiModel/ # AI model services
│ ├── session/ # Session services
│ └── message/ # Message services
├── store/ # Zustand state management
│ ├── agent/ # Agent state
│ ├── chat/ # Chat state
│ └── user/ # User state
├── styles/ # Global styles
├── tools/ # Built-in tool system
│ ├── artifacts/ # Code artifacts and preview
│ └── web-browsing/ # Web search and browsing
├── types/ # TypeScript type definitions
└── utils/ # Utility functions
├── client/ # Client-side utilities
└── server/ # Server-side utilities
```
## Key Monorepo Packages
```plaintext
packages/
├── const/ # Global constants and configurations
├── database/ # Database schemas and models
│ ├── src/models/ # Data models (CRUD operations)
│ ├── src/schemas/ # Drizzle database schemas
│ ├── src/repositories/ # Complex query layer
│ └── migrations/ # Database migration files
├── model-runtime/ # AI model runtime
│ └── src/
│ ├── openai/ # OpenAI provider integration
│ ├── anthropic/ # Anthropic provider integration
│ ├── google/ # Google AI provider integration
│ ├── ollama/ # Ollama local model integration
│ ├── types/ # Runtime type definitions
│ └── utils/ # Runtime utilities
├── types/ # Shared TypeScript type definitions
│ └── src/
│ ├── agent/ # Agent-related types
│ ├── message/ # Message and chat types
│ ├── user/ # User and session types
│ └── tool/ # Tool and plugin types
├── utils/ # Shared utility functions
│ └── src/
│ ├── client/ # Client-side utilities
│ ├── server/ # Server-side utilities
│ ├── fetch/ # HTTP request utilities
│ └── tokenizer/ # Token counting utilities
├── file-loaders/ # File loaders (PDF, DOCX, etc.)
├── prompts/ # AI prompt management
└── web-crawler/ # Web crawling functionality
├── apps/
│ └── desktop/
├── docs/
├── locales/
│ ├── en-US/
── zh-CN/
├── packages/
── const/
│ ├── context-engine/
│ ├── database/
│ ├── src/
│ │ │ ├── models/
├── schemas/
└── repositories/
│ ├── model-bank/
│ └── src/
│ └── aiModels/
│ ├── model-runtime/
│ └── src/
│ ├── core/
│ └── providers/
│ ├── types/
│ └── src/
│ │ ├── message/
│ └── user/
── utils/
├── public/
├── scripts/
├── src/
│ ├── app/
│ │ ├── (backend)/
│ │ │ ├── api/
│ │ │ │ ├── auth/
│ │ │ │ └── webhooks/
│ │ │ ├── middleware/
│ │ │ ├── oidc/
│ │ │ ├── trpc/
│ │ │ └── webapi/
│ │ │ ├── chat/
│ │ │ └── tts/
│ │ ├── [variants]/
│ │ │ ├── (main)/
│ │ │ │ ├── chat/
└── settings/
│ │ │ └── @modal/
│ │ └── manifest.ts
├── components/
│ ├── config/
│ ├── features/
│ │ ── ChatInput/
├── hooks/
├── layout/
│ │ ├── AuthProvider/
│ │ └── GlobalProvider/
├── libs/
│ │ └── oidc-provider/
├── locales/
│ │ └── default/
├── server/
│ │ ├── modules/
│ │ ├── routers/
│ │ ── async/
├── desktop/
│ │ │ ├── edge/
│ │ │ ── lambda/
│ │ └── services/
├── services/
│ │ ├── user/
│ │ │ ├── client.ts
│ │ │ ── server.ts
│ │ └── message/
├── store/
│ ├── agent/
│ │ ├── chat/
│ └── user/
│ ├── styles/
│ └── utils/
── package.json
```
## Architecture Map
- Presentation: `src/features`, `src/components`, `src/layout` — UI composition, global providers
- State: `src/store` — Zustand slices, selectors, middleware
- Client Services: `src/services/<domain>/{client|server}.ts` — client: PGLite; server: tRPC bridge
- API Routers: `src/app/(backend)/webapi` (REST), `src/app/(backend)/trpc/{edge|lambda|async|desktop|tools}`; Lambda router triggers Async router for long-running tasks (e.g., image)
- Server Services: `src/server/services` (business logic), `src/server/modules` (infra adapters)
- Data Access: `packages/database/src/{schemas,models,repositories}` — Schema (Drizzle), Model (CRUD), Repository (complex queries)
- Integrations: `src/libs` — analytics, auth, trpc, logging, runtime helpers
- UI Components: `src/components`, `src/features`
- Global providers: `src/layout`
- Zustand stores: `src/store`
- Client Services: `src/services/` cross-platform services
- clientDB: `src/services/<domain>/client.ts`
- serverDB: `src/services/<domain>/server.ts`
- API Routers:
- `src/app/(backend)/webapi` (REST)
- `src/server/routers/{edge|lambda|async|desktop|tools}` (tRPC)
- Server:
- Services(can access serverDB): `src/server/services` server-used-only services
- Modules(can't access db): `src/server/modules` (Server only Third-party Service Module)
- Database:
- Schema (Drizzle): `packages/database/src/schemas`
- Model (CRUD): `packages/database/src/models`
- Repository (bff-queries): `packages/database/src/repositories`
- Third-party Integrations: `src/libs` — analytics, oidc etc.
## Data Flow Architecture
### Unified Flow Pattern
```
UI Layer → State Management → Client Service → [Environment Branch] → Database
↓ ↓ ↓ ↓ ↓
React Zustand Environment Local/Remote PGLite/
Components Store Adaptation Routing PostgreSQL
```
### Environment-Specific Routing
| Mode | UI | Service Route | Database |
| --------------- | -------- | ---------------------- | ------------------- |
| **Browser/PWA** | React | Direct Model Access | PGLite (Local) |
| **Server** | React | tRPC → Server Services | PostgreSQL (Remote) |
| **Desktop** | Electron | tRPC → Local Node.js | PGLite/PostgreSQL\* |
_\*Depends on cloud sync configuration_
### Key Characteristics
- **Type Safety**: End-to-end type safety via tRPC and Drizzle ORM
- **Local/Remote Dual Mode**: PGLite enables user data ownership and local control
## Quick Map
- App Routes: `src/app` — UI routes (App Router) and backend routes under `(backend)`
- Web API: `src/app/(backend)/webapi` — REST-like endpoints
- tRPC Routers: `src/server/routers` — typed RPC endpoints by runtime
- Client Services: `src/services` — environment-adaptive client-side business logic
- Server Services: `src/server/services` — platform-agnostic business logic
- Database: `packages/database` — schemas/models/repositories/migrations
- State: `src/store` — Zustand stores and slices
- Integrations: `src/libs` — analytics/auth/trpc/logging/runtime helpers
- Tools: `src/tools` — built-in tool system
## Common Tasks
- Add Web API route: `src/app/(backend)/webapi/<module>/route.ts`
- Add tRPC endpoint: `src/server/routers/{edge|lambda|desktop}/...`
- Add client/server service: `src/services/<domain>/{client|server}.ts` (client: PGLite; server: tRPC)
- Add server service: `src/server/services/<domain>`
- Add a new model/provider: `src/config/modelProviders/<provider>.ts` + `packages/model-bank/src/aiModels/<provider>.ts` + `packages/model-runtime/src/<provider>/index.ts`
- Add DB schema/model/repository: `packages/database/src/{schemas|models|repositories}`
- Add Zustand slice: `src/store/<domain>/slices`
## Env Modes
- `NEXT_PUBLIC_CLIENT_DB`: selects client DB mode (e.g., `pglite`) vs server-backed
- `NEXT_PUBLIC_IS_DESKTOP_APP`: enables desktop-specific routes and behavior
- `NEXT_PUBLIC_SERVICE_MODE`: controls service routing preference (client/server)
## Boundaries
- Keep client logic in `src/services`; server-only logic stays in `src/server/services`
- Dont mix Web API (`webapi/`) with tRPC (`src/server/routers/`)
- Place business UI under `src/features`, global reusable UI under `src/components`
- **Web with ClientDB**: React UI → Client Service → Direct Model Access → PGLite (Web WASM)
- **Web with ServerDB**: React UI → Client Service → tRPC Lambda → Server Services → PostgreSQL (Remote)
- **Desktop**:
- Cloud sync disabled: Electron UI → Client Service → tRPC Lambda → Local Server Services → PGLite (Node WASM)
- Cloud sync enabled: Electron UI → Client Service → tRPC Lambda → Cloud Server Services → PostgreSQL (Remote)
@@ -4,20 +4,12 @@ globs:
alwaysApply: true
---
# 📋 Available Rules Index
# Available project rules index
The following rules are available via `read_file` from the `.cursor/rules/` directory:
## General
- `project-introduce.mdc` Project description and tech stack
- `cursor-rules.mdc` Cursor rules authoring and optimization guide
- `code-review.mdc` How to code review
All following rules are saved under `.cursor/rules/` directory:
## Backend
- `backend-architecture.mdc` Backend layer architecture and design guidelines
- `define-database-model.mdc` Database model definition guidelines
- `drizzle-schema-style-guide.mdc` Style guide for defining Drizzle ORM schemas
## Frontend
@@ -42,7 +34,6 @@ The following rules are available via `read_file` from the `.cursor/rules/` dire
## Debugging
- `debug.mdc` General debugging guide
- `debug-usage.mdc` Using the debug package and namespace conventions
## Testing
-31
View File
@@ -1,31 +0,0 @@
---
description:
globs:
alwaysApply: true
---
## System Role
You are an expert in full-stack Web development, proficient in JavaScript, TypeScript, CSS, React, Node.js, Next.js, Postgresql, Redis, S3, all kinds of network protocols.
You are an LLM expert, you are familiar with all kinds of LLM models, ai agents, ai workflow, prompt engineering and context engineering.
You are an expert in Ai art. In Ai image generation, you are proficient in Stable Diffusion and ComfyUI's architectural principles, workflows, model structures, parameter configurations, training methods, and inference optimization.
You are an expert in UI/UX design, proficient in web interaction patterns, responsive design, accessibility, and user behavior optimization. You excel at improving user retention and paid conversion rates through various interaction details.
## Problem Solving
- When modifying existing code, clearly describe the differences and reasons for the changes
- Provide alternative solutions that may be better overall or superior in specific aspects
- Provide optimization suggestions for deprecated API usage
- Cite sources whenever possible at the end, not inline
- When you provide multiple solutions, provide the recommended solution first, and note it as `Recommended`
- Express uncertainty when there might not be a correct answer, instead of take action by guessing and assuming
## Code Implementation
- Focus on maintainable over being performant
- Be sure to reference file path
- If doc links or required files are missing, ask for them before proceeding with the task rather than making assumptions
- If you're unable to get valid result when using tools, please clearly state in the output
@@ -0,0 +1,579 @@
---
description: Best practices for testing Zustand store actions
globs: "src/store/**/*.test.ts"
alwaysApply: false
---
# Zustand Store Action Testing Guide
This guide provides best practices for testing Zustand store actions, based on our proven testing patterns.
## Basic Test Structure
```typescript
import { act, renderHook } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { messageService } from '@/services/message';
import { useChatStore } from '../../store';
// Keep zustand mock as it's needed globally
vi.mock('zustand/traditional');
beforeEach(() => {
// Reset store state
vi.clearAllMocks();
useChatStore.setState(
{
activeId: 'test-session-id',
messagesMap: {},
loadingIds: [],
},
false,
);
// ✅ Setup only spies that MOST tests need
vi.spyOn(messageService, 'createMessage').mockResolvedValue('new-message-id');
// ❌ Don't setup spies that only few tests need - spy only when needed
// Setup common mock methods
act(() => {
useChatStore.setState({
refreshMessages: vi.fn(),
internal_coreProcessMessage: vi.fn(),
});
});
});
afterEach(() => {
vi.restoreAllMocks();
});
describe('action name', () => {
describe('validation', () => {
// Validation tests
});
describe('normal flow', () => {
// Happy path tests
});
describe('error handling', () => {
// Error case tests
});
});
```
## Testing Best Practices
### 1. Test Layering - Spy Direct Dependencies Only
✅ **Good**: Spy on the direct dependency
```typescript
// When testing internal_coreProcessMessage, spy its direct dependency
const fetchAIChatSpy = vi
.spyOn(result.current, 'internal_fetchAIChatMessage')
.mockResolvedValue({ isFunctionCall: false, content: 'AI response' });
```
❌ **Bad**: Spy on lower-level implementation details
```typescript
// Don't spy on services that internal_fetchAIChatMessage uses
const streamSpy = vi
.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(...);
```
**Why**: Each test should only mock its direct dependencies, not the entire call chain. This makes tests more maintainable and less brittle.
### 2. Mock Management - Minimize Global Spies
✅ **Good**: Spy only when needed
```typescript
beforeEach(() => {
// ✅ Only spy services that most tests need
vi.spyOn(messageService, 'createMessage').mockResolvedValue('new-message-id');
// ✅ Don't spy chatService globally
});
it('should process message', async () => {
// ✅ Spy chatService only in tests that need it
const streamSpy = vi.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(...);
// test logic
streamSpy.mockRestore();
});
```
❌ **Bad**: Setup all spies globally
```typescript
beforeEach(() => {
vi.spyOn(messageService, 'createMessage').mockResolvedValue('id');
vi.spyOn(chatService, 'createAssistantMessageStream').mockResolvedValue({}); // ❌ Not all tests need this
vi.spyOn(fileService, 'uploadFile').mockResolvedValue({}); // ❌ Creates implicit coupling
});
```
### 3. Service Mocking - Mock the Correct Layer
✅ **Good**: Mock the service method
```typescript
it('should fetch AI chat response', async () => {
const streamSpy = vi
.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(async ({ onMessageHandle, onFinish }) => {
await onMessageHandle?.({ type: 'text', text: 'Hello' } as any);
await onFinish?.('Hello', {});
});
// test logic
});
```
❌ **Bad**: Mock global fetch
```typescript
it('should fetch AI chat response', async () => {
global.fetch = vi.fn().mockResolvedValue(...); // ❌ Too low level
});
```
### 4. Test Organization - Use Descriptive Nesting
✅ **Good**: Clear nested structure
```typescript
describe('sendMessage', () => {
describe('validation', () => {
it('should not send when session is inactive', async () => {});
it('should not send when message is empty', async () => {});
});
describe('message creation', () => {
it('should create user message and trigger AI processing', async () => {});
it('should send message with files attached', async () => {});
});
describe('error handling', () => {
it('should handle message creation errors gracefully', async () => {});
});
});
```
❌ **Bad**: Flat structure
```typescript
describe('sendMessage', () => {
it('test 1', async () => {});
it('test 2', async () => {});
it('test 3', async () => {});
});
```
### 5. Testing Async Actions
Always wrap async operations in `act()`:
```typescript
it('should send message', async () => {
const { result } = renderHook(() => useChatStore());
await act(async () => {
await result.current.sendMessage({ message: 'Hello' });
});
expect(messageService.createMessage).toHaveBeenCalled();
});
```
### 6. State Setup - Use act() for setState
```typescript
it('should handle disabled state', async () => {
act(() => {
useChatStore.setState({ activeId: undefined });
});
const { result } = renderHook(() => useChatStore());
// test logic
});
```
### 7. Testing Complex Flows
For complex flows with multiple steps, use clear spy setup:
```typescript
it('should handle topic creation flow', async () => {
// Setup store state
act(() => {
useChatStore.setState({
activeTopicId: undefined,
messagesMap: {
'test-session-id': [
{ id: 'msg-1', role: 'user', content: 'Message 1' },
{ id: 'msg-2', role: 'assistant', content: 'Response 1' },
{ id: 'msg-3', role: 'user', content: 'Message 2' },
],
},
});
});
const { result } = renderHook(() => useChatStore());
// Spy on action dependencies
const createTopicSpy = vi.spyOn(result.current, 'createTopic')
.mockResolvedValue('new-topic-id');
const toggleLoadingSpy = vi.spyOn(result.current, 'internal_toggleMessageLoading');
// Execute
await act(async () => {
await result.current.sendMessage({ message: 'Test message' });
});
// Assert
expect(createTopicSpy).toHaveBeenCalled();
expect(toggleLoadingSpy).toHaveBeenCalledWith(true, expect.any(String));
});
```
### 8. Streaming Response Mocking
When testing streaming responses, simulate the flow properly:
```typescript
it('should handle streaming chunks', async () => {
const { result } = renderHook(() => useChatStore());
const messages = [
{ id: 'msg-1', role: 'user', content: 'Hello', sessionId: 'test-session' },
];
const streamSpy = vi
.spyOn(chatService, 'createAssistantMessageStream')
.mockImplementation(async ({ onMessageHandle, onFinish }) => {
// Simulate streaming chunks
await onMessageHandle?.({ type: 'text', text: 'Hello' } as any);
await onMessageHandle?.({ type: 'text', text: ' World' } as any);
await onFinish?.('Hello World', {});
});
await act(async () => {
await result.current.internal_fetchAIChatMessage({
messages,
messageId: 'test-message-id',
model: 'gpt-4o-mini',
provider: 'openai',
});
});
expect(result.current.internal_dispatchMessage).toHaveBeenCalled();
streamSpy.mockRestore();
});
```
### 9. Error Handling Tests
Always test error scenarios:
```typescript
it('should handle errors gracefully', async () => {
const { result } = renderHook(() => useChatStore());
vi.spyOn(messageService, 'createMessage').mockRejectedValue(
new Error('create message error'),
);
await act(async () => {
try {
await result.current.sendMessage({ message: 'Test message' });
} catch {
// Expected to throw
}
});
expect(result.current.internal_coreProcessMessage).not.toHaveBeenCalled();
});
```
### 10. Cleanup After Tests
Always restore mocks after each test:
```typescript
afterEach(() => {
vi.restoreAllMocks();
});
// For individual test cleanup:
it('should test something', async () => {
const spy = vi.spyOn(service, 'method').mockImplementation(...);
// test logic
spy.mockRestore(); // Optional: cleanup immediately after test
});
```
## Common Patterns
### Testing Store Methods That Call Other Store Methods
```typescript
it('should call internal methods', async () => {
const { result } = renderHook(() => useChatStore());
const internalMethodSpy = vi.spyOn(result.current, 'internal_method')
.mockResolvedValue();
await act(async () => {
await result.current.publicMethod();
});
expect(internalMethodSpy).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({ key: 'value' }),
);
});
```
### Testing Conditional Logic
```typescript
describe('conditional behavior', () => {
it('should execute when condition is true', async () => {
const { result } = renderHook(() => useChatStore());
vi.spyOn(result.current, 'internal_shouldUseRAG').mockReturnValue(true);
await act(async () => {
await result.current.sendMessage({ message: 'test' });
});
expect(result.current.internal_retrieveChunks).toHaveBeenCalled();
});
it('should not execute when condition is false', async () => {
const { result } = renderHook(() => useChatStore());
vi.spyOn(result.current, 'internal_shouldUseRAG').mockReturnValue(false);
await act(async () => {
await result.current.sendMessage({ message: 'test' });
});
expect(result.current.internal_retrieveChunks).not.toHaveBeenCalled();
});
});
```
### Testing AbortController
```typescript
it('should abort generation and clear loading state', () => {
const abortController = new AbortController();
act(() => {
useChatStore.setState({ chatLoadingIdsAbortController: abortController });
});
const { result } = renderHook(() => useChatStore());
const toggleLoadingSpy = vi.spyOn(result.current, 'internal_toggleChatLoading');
act(() => {
result.current.stopGenerateMessage();
});
expect(abortController.signal.aborted).toBe(true);
expect(toggleLoadingSpy).toHaveBeenCalledWith(false, undefined, expect.any(String));
});
```
## Anti-Patterns to Avoid
❌ **Don't**: Mock the entire store
```typescript
vi.mock('../../store', () => ({
useChatStore: vi.fn(() => ({
sendMessage: vi.fn(),
})),
}));
```
❌ **Don't**: Test implementation details
```typescript
// Bad: testing internal state structure
expect(result.current.messagesMap).toHaveProperty('test-session');
// Good: testing behavior
expect(result.current.refreshMessages).toHaveBeenCalled();
```
❌ **Don't**: Create tight coupling between tests
```typescript
// Bad: Tests depend on order
let messageId: string;
it('test 1', () => {
messageId = 'some-id'; // Side effect
});
it('test 2', () => {
expect(messageId).toBeDefined(); // Depends on test 1
});
```
❌ **Don't**: Over-mock services
```typescript
// Bad: Mocking everything
beforeEach(() => {
vi.mock('@/services/chat');
vi.mock('@/services/message');
vi.mock('@/services/file');
vi.mock('@/services/agent');
// ... too many global mocks
});
```
## Testing SWR Hooks in Zustand Stores
Some Zustand store slices use SWR hooks for data fetching. These require a different testing approach.
### Basic SWR Hook Test Structure
```typescript
import { renderHook, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { discoverService } from '@/services/discover';
import { globalHelpers } from '@/store/global/helpers';
import { useDiscoverStore as useStore } from '../../store';
vi.mock('zustand/traditional');
beforeEach(() => {
vi.clearAllMocks();
});
describe('SWR Hook Actions', () => {
it('should fetch data and return correct response', async () => {
const mockData = [{ id: '1', name: 'Item 1' }];
// Mock the service call (the fetcher)
vi.spyOn(discoverService, 'getPluginCategories').mockResolvedValue(mockData as any);
vi.spyOn(globalHelpers, 'getCurrentLanguage').mockReturnValue('en-US');
const params = {} as any;
const { result } = renderHook(() => useStore.getState().usePluginCategories(params));
// Use waitFor to wait for async data loading
await waitFor(() => {
expect(result.current.data).toEqual(mockData);
});
expect(discoverService.getPluginCategories).toHaveBeenCalledWith(params);
});
});
```
**Key points**:
- **DO NOT mock useSWR** - let it use the real implementation
- Only mock the **service methods** (fetchers)
- Use `waitFor` from `@testing-library/react` to wait for async operations
- Check `result.current.data` directly after waitFor completes
### Testing SWR Key Generation
```typescript
it('should generate correct SWR key with locale and params', () => {
vi.spyOn(globalHelpers, 'getCurrentLanguage').mockReturnValue('zh-CN');
const useSWRMock = vi.mocked(useSWR);
let capturedKey: string | null = null;
useSWRMock.mockImplementation(((key: string) => {
capturedKey = key;
return { data: undefined, error: undefined, isValidating: false, mutate: vi.fn() };
}) as any);
const params = { page: 2, category: 'tools' } as any;
renderHook(() => useStore.getState().usePluginList(params));
expect(capturedKey).toBe('plugin-list-zh-CN-2-tools');
});
```
### Testing SWR Configuration
```typescript
it('should have correct SWR configuration', () => {
const useSWRMock = vi.mocked(useSWR);
let capturedOptions: any = null;
useSWRMock.mockImplementation(((key: string, fetcher: any, options: any) => {
capturedOptions = options;
return { data: undefined, error: undefined, isValidating: false, mutate: vi.fn() };
}) as any);
renderHook(() => useStore.getState().usePluginIdentifiers());
expect(capturedOptions).toMatchObject({ revalidateOnFocus: false });
});
```
### Testing Conditional Fetching
```typescript
it('should not fetch when required parameter is missing', () => {
const useSWRMock = vi.mocked(useSWR);
let capturedKey: string | null = null;
useSWRMock.mockImplementation(((key: string | null) => {
capturedKey = key;
return { data: undefined, error: undefined, isValidating: false, mutate: vi.fn() };
}) as any);
// When identifier is undefined, SWR key should be null
renderHook(() => useStore.getState().usePluginDetail({ identifier: undefined }));
expect(capturedKey).toBeNull();
});
```
### Key Differences from Regular Action Tests
1. **Mock useSWR globally**: Use `vi.mock('swr')` at the top level
2. **Mock the fetcher, not the result**:
- ✅ **Correct**: `const data = fetcher?.()` - call fetcher and return its Promise
- ❌ **Wrong**: `return { data: mockData }` - hardcode the result
3. **Await Promise results**: The `data` field is a Promise, use `await result.current.data`
4. **No act() wrapper needed**: SWR hooks don't trigger React state updates in these tests
5. **Test SWR key generation**: Verify keys include locale and parameters
6. **Test configuration**: Verify revalidation and other SWR options
7. **Type assertions**: Use `as any` for test mock data where type definitions are strict
**Why this matters**:
- The fetcher (service method) is what we're testing - it must be called
- Hardcoding the return value bypasses the actual fetcher logic
- SWR returns Promises in real usage, tests should mirror this behavior
## Benefits of This Approach
✅ **Clear test layers** - Each test only spies on direct dependencies
✅ **Correct mocks** - Mocks match actual implementation
✅ **Better maintainability** - Changes to implementation require fewer test updates
✅ **Improved coverage** - Structured approach ensures all branches are tested
✅ **Reduced coupling** - Tests are independent and can run in any order
## Reference
See example implementation in:
- `src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts` (Regular actions)
- `src/store/discover/slices/plugin/action.test.ts` (SWR hooks)
- `src/store/discover/slices/mcp/action.test.ts` (SWR hooks)
+5 -61
View File
@@ -10,61 +10,11 @@ alwaysApply: false
- avoid explicit type annotations when TypeScript can infer types.
- avoid implicitly `any` variables; explicitly type when necessary (e.g., `let a: number` instead of `let a`).
- use the most accurate type possible (e.g., prefer `Record<PropertyKey, unknown>` over `object`).
- use the most accurate type possible (e.g., prefer `Record<PropertyKey, unknown>` over `object` and `any`).
- prefer `interface` over `type` for object shapes (e.g., React component props). Keep `type` for unions, intersections, and utility types.
- prefer `as const satisfies XyzInterface` over plain `as const` when suitable.
- prefer `@ts-expect-error` over `@ts-ignore`
- prefer `Record<string, any>` over `any`
- **Avoid unnecessary null checks**: Before adding `xxx !== null`, `?.`, `??`, or `!.`, read the type definition to confirm the necessary. **Example:**
```typescript
// ❌ Wrong: budget.spend and budget.maxBudget is number, not number | null
if (budget.spend !== null && budget.maxBudget !== null && budget.spend >= budget.maxBudget) {
// ...
}
// ✅ Right
if (budget.spend >= budget.maxBudget) {
// ...
}
```
- **Avoid redundant runtime checks**: Don't add runtime validation for conditions already guaranteed by types or previous checks. Trust the type system and calling contract. **Example:**
```typescript
// ❌ Wrong: Adding impossible-to-fail checks
const due = await db.query.budgets.findMany({
where: and(isNotNull(budgets.budgetDuration)), // Already filtered non-null
});
const result = due.map(b => {
const nextReset = computeNextResetAt(b.budgetResetAt!, b.budgetDuration!);
if (!nextReset) { // This check is impossible to fail
throw new Error(`Unexpected null nextResetAt`);
}
return nextReset;
});
// ✅ Right: Trust the contract
const due = await db.query.budgets.findMany({
where: and(isNotNull(budgets.budgetDuration)),
});
const result = due.map(b => computeNextResetAt(b.budgetResetAt!, b.budgetDuration!));
```
- **Avoid meaningless null/undefined parameters**: Don't accept null/undefined for parameters that have no business meaning when null. Design strict function contracts. **Example:**
```typescript
// ❌ Wrong: Function accepts meaningless null input
function computeNextResetAt(currentResetAt: Date, durationStr: string | null): Date | null {
if (!durationStr) return null; // Why accept null if it just returns null?
}
// ✅ Right: Strict contract, clear responsibility
function computeNextResetAt(currentResetAt: Date, durationStr: string): Date {
// Function has single clear purpose, caller ensures valid input
}
```
- prefer `@ts-expect-error` over `@ts-ignore` over `as any`
- Avoid meaningless null/undefined parameters; design strict function contracts.
## Imports and Modules
@@ -79,16 +29,11 @@ alwaysApply: false
## Code Structure and Readability
- Refactor repeated logic into reusable functions.
- Prefer object destructuring when accessing and using properties.
- Use consistent, descriptive naming; avoid obscure abbreviations.
- Use semantically meaningful variable, function, and class names.
- Replace magic numbers or strings with well-named constants.
- Keep meaningful code comments; do not remove them when applying edits. Update comments when behavior changes.
- Ensure JSDoc comments accurately reflect the implementation.
- Look for opportunities to simplify or modernize code with the latest JavaScript/TypeScript features where it improves clarity.
- Defer formatting to tooling; ignore purely formatting-only issues and autofixable lint problems.
- Respect project Prettier settings.
## UI and Theming
@@ -100,15 +45,14 @@ alwaysApply: false
## Performance
- Prefer `for…of` loops to index-based `for` loops when feasible.
- Decide whether callbacks should be debounced or throttled based on UX and performance needs.
- Reuse existing npm packages rather than reinventing the wheel (e.g., `lodash-es/omit`).
- Reuse existing utils inside `packages/utils` or installed npm packages rather than reinventing the wheel.
- Query only the required columns from a database rather than selecting entire rows.
## Time and Consistency
- Instead of calling `Date.now()` multiple times, assign it to a constant once and reuse it to ensure consistency and improve readability.
## Some logging rules
## Logging
- Never log user private information like api key, etc
- Don't use `import { log } from 'debug'` to log messages, because it will directly log the message to the console.
+21
View File
@@ -13,6 +13,17 @@
# Default is '0' (enabled)
# ENABLED_CSP=1
# SSRF Protection Settings
# Set to '1' to allow connections to private IP addresses (disable SSRF protection)
# WARNING: Only enable this in trusted environments
# Default is '0' (SSRF protection enabled)
# SSRF_ALLOW_PRIVATE_IP_ADDRESS=0
# Whitelist of allowed private IP addresses (comma-separated)
# Only takes effect when SSRF_ALLOW_PRIVATE_IP_ADDRESS is '0'
# Example: Allow specific internal servers while keeping SSRF protection
# SSRF_ALLOW_IP_ADDRESS_LIST=192.168.1.100,10.0.0.50
########################################
########## AI Provider Service #########
########################################
@@ -169,6 +180,13 @@ OPENAI_API_KEY=sk-xxxxxxxxx
# FAL_API_KEY=fal-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
########################################
######### AI Image Settings ############
########################################
# Default image generation count (range: 1-20, default: 4)
# AI_IMAGE_DEFAULT_IMAGE_NUM=4
### Nebius ###
# NEBIUS_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
@@ -249,6 +267,9 @@ OPENAI_API_KEY=sk-xxxxxxxxx
# you need to config the clerk webhook secret key if you want to use the clerk with database
#CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxxxxxxxxxx
# Clear allow origin https://clerk.com/docs/guides/dashboard/dns-domains/satellite-domains
# Authentication across different domains , use,to splite different origin
# NEXT_PUBLIC_CLERK_AUTH_ALLOW_ORIGINS='https://market.lobehub.com,https://lobehub.com'
# NextAuth related configurations
# NEXT_PUBLIC_ENABLE_NEXT_AUTH=1
+67 -23
View File
@@ -5,34 +5,17 @@ type: Bug
body:
- type: dropdown
attributes:
label: '📦 Platform'
label: '📱 Client Type'
description: 'Select how you are accessing LobeChat'
multiple: true
options:
- 'Official Preview'
- 'Official Cloud'
- 'Vercel'
- 'Zeabur'
- 'Sealos'
- 'Netlify'
- 'Self hosting Docker'
- 'Web (Desktop Browser)'
- 'Web (Mobile Browser)'
- 'Desktop App (Electron)'
- 'Mobile App (React Native)'
- 'Other'
validations:
required: true
- type: dropdown
attributes:
label: '📦 Deploymenet mode'
multiple: true
options:
- 'client db (lobe-chat image)'
- 'client pgelite db (lobe-chat-pglite image)'
- 'server db(lobe-chat-database image)'
validations:
required: true
- type: input
attributes:
label: '📌 Version'
validations:
required: true
- type: dropdown
attributes:
@@ -48,6 +31,39 @@ body:
- 'Other'
validations:
required: true
- type: dropdown
attributes:
label: '📦 Deployment Platform'
multiple: true
options:
- 'Official Cloud'
- 'Vercel'
- 'Zeabur'
- 'Sealos'
- 'Netlify'
- 'Self hosting Docker'
- 'Other'
validations:
required: false
- type: dropdown
attributes:
label: '🔧 Deployment Mode'
multiple: true
options:
- 'client db (lobe-chat image)'
- 'client pgelite db (lobe-chat-pglite image)'
- 'server db (lobe-chat-database image)'
validations:
required: true
- type: input
attributes:
label: '📌 Version'
validations:
required: true
- type: dropdown
attributes:
label: '🌐 Browser'
@@ -60,21 +76,49 @@ body:
- 'Other'
validations:
required: true
- type: textarea
attributes:
label: '🐛 Bug Description'
description: A clear and concise description of the bug, if the above option is `Other`, please also explain in detail.
validations:
required: true
- type: textarea
attributes:
label: '📷 Recurrence Steps'
description: A clear and concise description of how to recurrence.
- type: textarea
attributes:
label: '🚦 Expected Behavior'
description: A clear and concise description of what you expected to happen.
- type: textarea
attributes:
label: '📝 Additional Information'
description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here.
- type: dropdown
attributes:
label: '🛠️ Willing to Submit a PR?'
description: Would you be willing to submit a pull request to fix this bug?
options:
- 'Yes, I am willing to submit a PR'
- 'No, but I am happy to help test the fix'
validations:
required: false
- type: checkboxes
attributes:
label: '✅ Validations'
description: Before submitting the issue, please make sure you do the following
options:
- label: Read the [docs](https://lobehub.com/zh/docs).
required: true
- label: Check that there isn't [already an issue](https://github.com/lobehub/lobe-chat/issues) that reports the same bug to avoid creating a duplicate.
required: true
- label: Make sure this is a LobeChat issue and not a third-party library or provider issue.
required: true
- label: Check that this is a concrete bug. For Q&A, please use [GitHub Discussions](https://github.com/lobehub/lobe-chat/discussions) or join our [Discord Server](https://discord.gg/rGHwKq4R).
required: true
@@ -1,87 +0,0 @@
name: '🐛 反馈缺陷'
description: '反馈一个问题缺陷'
labels: ['unconfirm']
type: Bug
body:
- type: markdown
attributes:
value: |
在创建新的 Issue 之前,请先[搜索已有问题](https://github.com/lobehub/lobe-chat/issues),如果发现已有类似的问题,请给它 **👍 点赞**,这样可以帮助我们更快地解决问题。
如果你在使用过程中遇到问题,可以尝试以下方式获取帮助:
- 在 [GitHub Discussions](https://github.com/lobehub/lobe-chat/discussions) 的版块发起讨论。
- 在 [LobeChat 社区](https://discord.gg/AYFPHvv2jT) 提问,与其他用户交流。
- type: dropdown
attributes:
label: '📦 部署环境'
multiple: true
options:
- 'Official Preview'
- 'Official Cloud'
- 'Vercel'
- 'Zeabur'
- 'Sealos'
- 'Netlify'
- 'Docker'
- 'Other'
validations:
required: true
- type: dropdown
attributes:
label: '📦 部署模式'
multiple: true
options:
- '客户端模式(lobe-chat 镜像)'
- '客户端 Pglite 模式(lobe-chat-pglite 镜像)'
- '服务端模式(lobe-chat-database 镜像)'
validations:
required: true
- type: input
attributes:
label: '📌 软件版本'
validations:
required: true
- type: dropdown
attributes:
label: '💻 系统环境'
multiple: true
options:
- 'Windows'
- 'macOS'
- 'Ubuntu'
- 'Other Linux'
- 'iOS'
- 'Android'
- 'Other'
validations:
required: true
- type: dropdown
attributes:
label: '🌐 浏览器'
multiple: true
options:
- 'Chrome'
- 'Edge'
- 'Safari'
- 'Firefox'
- 'Other'
validations:
required: true
- type: textarea
attributes:
label: '🐛 问题描述'
description: 请提供一个清晰且简洁的问题描述,若上述选项为`Other`,也请详细说明。
validations:
required: true
- type: textarea
attributes:
label: '📷 复现步骤'
description: 请提供一个清晰且简洁的描述,说明如何复现问题。
- type: textarea
attributes:
label: '🚦 期望结果'
description: 请提供一个清晰且简洁的描述,说明您期望发生什么。
- type: textarea
attributes:
label: '📝 补充信息'
description: 如果您的问题需要进一步说明,或者您遇到的问题无法在一个简单的示例中复现,请在这里添加更多信息。
@@ -1,21 +0,0 @@
name: '🌠 功能需求'
description: '提出需求或建议'
title: '[Request] '
type: Feature
body:
- type: textarea
attributes:
label: '🥰 需求描述'
description: 请添加一个清晰且简洁的问题描述,阐述您希望通过这个功能需求解决的问题。
validations:
required: true
- type: textarea
attributes:
label: '🧐 解决方案'
description: 请清晰且简洁地描述您想要的解决方案。
validations:
required: true
- type: textarea
attributes:
label: '📝 补充信息'
description: 在这里添加关于问题的任何其他背景信息。
+4 -4
View File
@@ -1,7 +1,7 @@
contact_links:
- name: Ask a question for self-hosting | 咨询自部署问题
- name: Ask a question for self-hosting
url: https://github.com/lobehub/lobe-chat/discussions/new?category=self-hosting-%E7%A7%81%E6%9C%89%E5%8C%96%E9%83%A8%E7%BD%B2
about: Please post questions, and ideas in discussions. | 请在讨论区发布问题和想法。
- name: Questions and ideas | 其他问题和想法
about: Please post questions, and ideas in discussions.
- name: Questions and ideas
url: https://github.com/lobehub/lobe-chat/discussions/new/choose
about: Please post questions, and ideas in discussions. | 请在讨论区发布问题和想法。
about: Please post questions, and ideas in discussions.
+29 -3
View File
@@ -1,4 +1,4 @@
#### 💻 变更类型 | Change Type
#### 💻 Change Type
<!-- For change type, change [ ] to [x]. -->
@@ -12,10 +12,36 @@
- [ ] 📝 docs
- [ ] 🔨 chore
#### 🔀 变更说明 | Description of Change
#### 🔗 Related Issue
<!-- Link to the issue that is fixed by this PR -->
<!-- Example: Fixes #123, Closes #456, Related to #789 -->
#### 🔀 Description of Change
<!-- Thank you for your Pull Request. Please provide a description above. -->
#### 📝 补充信息 | Additional Information
#### 🧪 How to Test
<!-- Please describe how you tested your changes -->
<!-- For AI features, please include test prompts or scenarios -->
- [ ] Tested locally
- [ ] Added/updated tests
- [ ] No tests needed
#### 📸 Screenshots / Videos
<!-- If this PR includes UI changes, please provide screenshots or videos -->
| Before | After |
| ------ | ----- |
| ... | ... |
#### 📝 Additional Information
<!-- Add any other context about the Pull Request here. -->
<!-- Breaking changes? Migration guide? Performance impact? -->
+260
View File
@@ -0,0 +1,260 @@
#!/usr/bin/env bun
declare global {
// @ts-ignore
// eslint-disable-next-line no-var
var process: {
env: Record<string, string | undefined>;
};
}
interface GitHubIssue {
created_at: string;
number: number;
title: string;
user: { id: number };
}
interface GitHubComment {
body: string;
created_at: string;
id: number;
user: { id: number; type: string };
}
interface GitHubReaction {
content: string;
user: { id: number };
}
async function githubRequest<T>(
endpoint: string,
token: string,
method: string = 'GET',
body?: any,
): Promise<T> {
const response = await fetch(`https://api.github.com${endpoint}`, {
headers: {
'Accept': 'application/vnd.github.v3+json',
'Authorization': `Bearer ${token}`,
'User-Agent': 'auto-close-duplicates-script',
...(body && { 'Content-Type': 'application/json' }),
},
method,
...(body && { body: JSON.stringify(body) }),
});
if (!response.ok) {
throw new Error(`GitHub API request failed: ${response.status} ${response.statusText}`);
}
return response.json();
}
function extractDuplicateIssueNumber(commentBody: string): number | null {
// Try to match #123 format first
let match = commentBody.match(/#(\d+)/);
if (match) {
return parseInt(match[1], 10);
}
// Try to match GitHub issue URL format: https://github.com/owner/repo/issues/123
match = commentBody.match(/github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/);
if (match) {
return parseInt(match[1], 10);
}
return null;
}
async function closeIssueAsDuplicate(
owner: string,
repo: string,
issueNumber: number,
duplicateOfNumber: number,
token: string,
): Promise<void> {
await githubRequest(`/repos/${owner}/${repo}/issues/${issueNumber}`, token, 'PATCH', {
labels: ['duplicate'],
state: 'closed',
state_reason: 'duplicate',
});
await githubRequest(`/repos/${owner}/${repo}/issues/${issueNumber}/comments`, token, 'POST', {
body: `This issue has been automatically closed as a duplicate of #${duplicateOfNumber}.
If this is incorrect, please re-open this issue or create a new one.
🤖 Generated with [Claude Code](https://claude.ai/code)`,
});
}
async function autoCloseDuplicates(): Promise<void> {
console.log('[DEBUG] Starting auto-close duplicates script');
const token = process.env.GITHUB_TOKEN;
if (!token) {
throw new Error('GITHUB_TOKEN environment variable is required');
}
console.log('[DEBUG] GitHub token found');
const owner = process.env.GITHUB_REPOSITORY_OWNER || 'lobehub';
const repo = process.env.GITHUB_REPOSITORY_NAME || 'lobe-chat';
console.log(`[DEBUG] Repository: ${owner}/${repo}`);
const threeDaysAgo = new Date();
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
console.log(`[DEBUG] Checking for duplicate comments older than: ${threeDaysAgo.toISOString()}`);
console.log('[DEBUG] Fetching open issues created more than 3 days ago...');
const allIssues: GitHubIssue[] = [];
let page = 1;
const perPage = 100;
// eslint-disable-next-line no-constant-condition
while (true) {
const pageIssues: GitHubIssue[] = await githubRequest(
`/repos/${owner}/${repo}/issues?state=open&per_page=${perPage}&page=${page}`,
token,
);
if (pageIssues.length === 0) break;
// Filter for issues created more than 3 days ago
const oldEnoughIssues = pageIssues.filter(
(issue) => new Date(issue.created_at) <= threeDaysAgo,
);
allIssues.push(...oldEnoughIssues);
page++;
// Safety limit to avoid infinite loops
if (page > 20) break;
}
const issues = allIssues;
console.log(`[DEBUG] Found ${issues.length} open issues`);
let processedCount = 0;
let candidateCount = 0;
for (const issue of issues) {
processedCount++;
console.log(
`[DEBUG] Processing issue #${issue.number} (${processedCount}/${issues.length}): ${issue.title}`,
);
console.log(`[DEBUG] Fetching comments for issue #${issue.number}...`);
const comments: GitHubComment[] = await githubRequest(
`/repos/${owner}/${repo}/issues/${issue.number}/comments`,
token,
);
console.log(`[DEBUG] Issue #${issue.number} has ${comments.length} comments`);
const dupeComments = comments.filter(
(comment) =>
comment.body.includes('Found') &&
comment.body.includes('possible duplicate') &&
comment.user.type === 'Bot',
);
console.log(
`[DEBUG] Issue #${issue.number} has ${dupeComments.length} duplicate detection comments`,
);
if (dupeComments.length === 0) {
console.log(`[DEBUG] Issue #${issue.number} - no duplicate comments found, skipping`);
continue;
}
const lastDupeComment = dupeComments.at(-1);
// @ts-ignore
const dupeCommentDate = new Date(lastDupeComment.created_at);
console.log(
`[DEBUG] Issue #${
issue.number
} - most recent duplicate comment from: ${dupeCommentDate.toISOString()}`,
);
if (dupeCommentDate > threeDaysAgo) {
console.log(`[DEBUG] Issue #${issue.number} - duplicate comment is too recent, skipping`);
continue;
}
console.log(
`[DEBUG] Issue #${issue.number} - duplicate comment is old enough (${Math.floor(
(Date.now() - dupeCommentDate.getTime()) / (1000 * 60 * 60 * 24),
)} days)`,
);
const commentsAfterDupe = comments.filter(
(comment) => new Date(comment.created_at) > dupeCommentDate,
);
console.log(
`[DEBUG] Issue #${issue.number} - ${commentsAfterDupe.length} comments after duplicate detection`,
);
if (commentsAfterDupe.length > 0) {
console.log(
`[DEBUG] Issue #${issue.number} - has activity after duplicate comment, skipping`,
);
continue;
}
console.log(`[DEBUG] Issue #${issue.number} - checking reactions on duplicate comment...`);
const reactions: GitHubReaction[] = await githubRequest(
// @ts-ignore
`/repos/${owner}/${repo}/issues/comments/${lastDupeComment.id}/reactions`,
token,
);
console.log(
`[DEBUG] Issue #${issue.number} - duplicate comment has ${reactions.length} reactions`,
);
const authorThumbsDown = reactions.some(
(reaction) => reaction.user.id === issue.user.id && reaction.content === '-1',
);
console.log(
`[DEBUG] Issue #${issue.number} - author thumbs down reaction: ${authorThumbsDown}`,
);
if (authorThumbsDown) {
console.log(
`[DEBUG] Issue #${issue.number} - author disagreed with duplicate detection, skipping`,
);
continue;
}
// @ts-ignore
const duplicateIssueNumber = extractDuplicateIssueNumber(lastDupeComment.body);
if (!duplicateIssueNumber) {
console.log(
`[DEBUG] Issue #${issue.number} - could not extract duplicate issue number from comment, skipping`,
);
continue;
}
candidateCount++;
const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`;
try {
console.log(
`[INFO] Auto-closing issue #${issue.number} as duplicate of #${duplicateIssueNumber}: ${issueUrl}`,
);
await closeIssueAsDuplicate(owner, repo, issue.number, duplicateIssueNumber, token);
console.log(
`[SUCCESS] Successfully closed issue #${issue.number} as duplicate of #${duplicateIssueNumber}`,
);
} catch (error) {
console.error(`[ERROR] Failed to close issue #${issue.number} as duplicate: ${error}`);
}
}
console.log(
`[DEBUG] Script completed. Processed ${processedCount} issues, found ${candidateCount} candidates for auto-close`,
);
}
// eslint-disable-next-line unicorn/prefer-top-level-await
autoCloseDuplicates().catch(console.error);
// Make it a module
export {};
+78
View File
@@ -0,0 +1,78 @@
// @ts-check
/**
* Lock closed issues after 7 days of inactivity
* @param {object} github - GitHub API client
* @param {object} context - GitHub Actions context
*/
module.exports = async ({ github, context }) => {
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const lockComment = `This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.`;
let page = 1;
let hasMore = true;
let totalLocked = 0;
while (hasMore) {
// Get closed issues (pagination)
const { data: issues } = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'closed',
sort: 'updated',
direction: 'asc',
per_page: 100,
page: page,
});
if (issues.length === 0) {
hasMore = false;
break;
}
for (const issue of issues) {
// Skip if already locked
if (issue.locked) continue;
// Skip pull requests
if (issue.pull_request) continue;
// Check if updated more than 7 days ago
const updatedAt = new Date(issue.updated_at);
if (updatedAt > sevenDaysAgo) {
// Since issues are sorted by updated_at ascending,
// once we hit a recent issue, all remaining will be recent too
hasMore = false;
break;
}
try {
// Add comment before locking
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: lockComment,
});
// Lock the issue
await github.rest.issues.lock({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
lock_reason: 'resolved',
});
totalLocked++;
console.log(`Locked issue #${issue.number}: ${issue.title}`);
} catch (error) {
console.error(`Failed to lock issue #${issue.number}: ${error.message}`);
}
}
page++;
}
console.log(`Total issues locked: ${totalLocked}`);
};
@@ -0,0 +1,33 @@
name: Claude Issue Dedupe
description: Automatically dedupe GitHub issues using Claude Code
on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue_number:
description: 'Issue number to process for duplicate detection'
required: true
type: string
jobs:
claude-dedupe-issues:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Run Claude Code slash command
uses: anthropics/claude-code-action@main
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
allowed_non_write_users: "*"
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: '/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}'
+65
View File
@@ -0,0 +1,65 @@
name: Claude Issue Triage
description: Automatically triage GitHub issues using Claude Code
on:
issues:
types: [opened, labeled]
jobs:
triage-issue:
runs-on: ubuntu-latest
timeout-minutes: 10
# Only run on issue opened, or when "trigger:triage" label is added
if: github.event.action == 'opened' || (github.event.action == 'labeled' && github.event.label.name == 'trigger:triage')
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Copy triage prompts
run: |
mkdir -p /tmp/claude-prompts
cp .claude/prompts/team-assignment.md /tmp/claude-prompts/
cp .claude/prompts/issue-triage.md /tmp/claude-prompts/
- name: Run Claude Code for Issue Triage
uses: anthropics/claude-code-action@main
with:
github_token: ${{ secrets.GH_TOKEN }}
allowed_non_write_users: "*"
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: '--allowed-tools "Bash(gh *),Read"'
prompt: |
You're an issue triage assistant for GitHub issues. Your task is to analyze issues, apply appropriate labels, and mention the responsible team member.
REPOSITORY: ${{ github.repository }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
## Instructions
Follow the complete triage guide located at:
```bash
cat /tmp/claude-prompts/issue-triage.md
```
Read the team assignment guide for determining team members:
```bash
cat /tmp/claude-prompts/team-assignment.md
```
**IMPORTANT**:
- Follow ALL steps in the issue-triage.md guide
- Apply labels according to the guide's rules
- Post a mention comment to the appropriate team member(s) based on team-assignment.md
- Replace [ISSUE_NUMBER] with: ${{ github.event.issue.number }}
**Start the triage process now.**
- name: Remove trigger label
if: github.event.action == 'labeled' && github.event.label.name == 'trigger:triage'
run: |
gh issue edit ${{ github.event.issue.number }} --remove-label "trigger:triage"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
+120
View File
@@ -0,0 +1,120 @@
name: Claude Translator
concurrency:
group: translator-${{ github.event.comment.id || github.event.issue.number || github.event.review.id }}
cancel-in-progress: false
on:
issues:
types: [opened]
issue_comment:
types: [created, edited]
pull_request_review:
types: [submitted, edited]
pull_request_review_comment:
types: [created, edited]
jobs:
translate:
if: |
(github.event_name == 'issues') ||
(github.event_name == 'issue_comment' && github.event.sender.type != 'Bot') ||
(github.event_name == 'pull_request_review' && github.event.sender.type != 'Bot') ||
(github.event_name == 'pull_request_review_comment' && github.event.sender.type != 'Bot')
runs-on: ubuntu-latest
permissions:
contents: read
# update issues/comments
issues: write
pull-requests: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
with:
fetch-depth: 1
- name: Run Claude for translation
uses: anthropics/claude-code-action@main
id: claude
with:
# Warning: Permissions should have been controlled by workflow permission.
# Now `contents: read` is safe for files, but we could make a fine-grained token to control it.
# See: https://github.com/anthropics/claude-code-action/blob/main/docs/security.md
github_token: ${{ secrets.GH_TOKEN }}
allowed_non_write_users: "*"
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
claude_args: '--allowed-tools "Bash(gh issue:*),Bash(gh api:repos/*/issues:*),Bash(gh api:repos/*/pulls/*/reviews/*),Bash(gh api:repos/*/pulls/comments/*)"'
prompt: |
You are a multilingual translation assistant. You need to respond to the following four types of GitHub Webhook events:
- issues
- issue_comment
- pull_request_review
- pull_request_review_comment
Please complete the following tasks:
1. Retrieve complete information for the current event.
- If the current event is 'issues', get the issue information.
- If the current event is 'issue_comment', get the comment information.
- If the current event is 'pull_request_review', get the review information.
- If the current event is 'pull_request_review_comment', get the comment information.
2. Intelligently detect content.
- If the retrieved information is already translated content following the format requirements, check if the translation matches the original content. If not, retranslate to match and follow the format requirements.
- If the retrieved information is untranslated content, check its language. If not in English, translate to English.
- If the retrieved information is partially translated to English, translate it completely to English.
- If the retrieved information contains references to already translated content, clean the referenced content to contain only English. Referenced content should not include "This xxx was translated by Claude" and "Original Content" etc.
- If the retrieved information contains other types of references (i.e., references to non-Claude translated content), keep them as-is without translation.
- If the retrieved information is email reply content, place email content references at the end during translation. Include only the reply content itself in both original and translated content, without email content references.
- If the retrieved information doesn't need any processing, skip the task.
3. Format requirements:
- Title: English translation (if non-English)
- Content format:
[Translated content]
---
> This issue/comment/review was translated by Claude.
<details>
<summary>Original Content</summary>
[Original content]
</details>
4. CRITICAL RULES to prevent hallucination and ensure accuracy:
- The "Original Content" section MUST contain the EXACT, UNMODIFIED original text byte-for-byte. NEVER add, remove, modify, or hallucinate ANY content in this section.
- Code blocks, error logs, JSON structures, and other technical content MUST appear in BOTH the translated section AND the original content section WITHOUT ANY MODIFICATION.
- When translating content with code/logs/JSON:
* Copy the code/logs/JSON blocks identically to both sections
* Only translate the natural language text (e.g., Chinese, Japanese) surrounding the code blocks
* Keep all technical content (URLs, variable names, error messages in English) unchanged
- ALWAYS verify the "Original Content" section matches the source text exactly before updating
- If you detect any discrepancy, retrieve the original content again to ensure accuracy
- Pay special attention to the end of comments - do not drop or hallucinate the last sentences
5. Update using gh tool:
- Choose the correct command based on the Event type in environment information:
- If Event is 'issues': gh issue edit [ISSUE_NUMBER] --title "[English title]" --body "[Translated content + Original content]"
- If Event is 'issue_comment': gh api -X PATCH /repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }} -f body="[Translated content + Original content]"
- If Event is 'pull_request_review': gh api -X PUT /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews/${{ github.event.review.id }} -f body="[Translated content]"
- If Event is 'pull_request_review_comment': gh api -X PATCH /repos/${{ github.repository }}/pulls/comments/${{ github.event.comment.id }} -f body="[Translated content + Original content]"
<environment_context>
- Event: ${{ github.event_name }}
- Issue Number: ${{ github.event.issue.number }}
- Repository: ${{ github.repository }}
- (Review) Comment ID: ${{ github.event.comment.id || 'N/A' }}
- Pull Request Number: ${{ github.event.pull_request.number || 'N/A' }}
- Review ID: ${{ github.event.review.id || 'N/A' }}
</environment_context>
Use the following command to get complete information:
gh issue view ${{ github.event.issue.number }} --json title,body,comments
+18 -18
View File
@@ -18,8 +18,8 @@ env:
jobs:
test:
name: Code quality check
# 添加 PR label 触发条件,只有添加了 Build Desktop 标签的 PR 才会触发构建
if: contains(github.event.pull_request.labels.*.name, 'Build Desktop')
# 添加 PR label 触发条件,只有添加了 trigger:build-desktop 标签的 PR 才会触发构建
if: contains(github.event.pull_request.labels.*.name, 'trigger:build-desktop')
runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
steps:
- name: Checkout base
@@ -36,7 +36,7 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
- name: Install deps
run: bun i
@@ -51,7 +51,7 @@ jobs:
version:
name: Determine version
# 与 test job 相同的触发条件
if: contains(github.event.pull_request.labels.*.name, 'Build Desktop')
if: contains(github.event.pull_request.labels.*.name, 'trigger:build-desktop')
runs-on: ubuntu-latest
outputs:
# 输出版本信息,供后续 job 使用
@@ -95,7 +95,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, macos-13, windows-2025, ubuntu-latest]
os: [macos-latest, macos-15-intel, windows-2025, ubuntu-latest]
steps:
- uses: actions/checkout@v5
with:
@@ -129,11 +129,11 @@ jobs:
run: npm run desktop:build
env:
# 设置更新通道,PR构建为nightly,否则为stable
UPDATE_CHANNEL: 'nightly'
UPDATE_CHANNEL: "nightly"
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
# 默认添加一个加密 SECRET
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
# macOS 签名和公证配置
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -152,10 +152,10 @@ jobs:
run: npm run desktop:build
env:
# 设置更新通道,PR构建为nightly,否则为stable
UPDATE_CHANNEL: 'nightly'
UPDATE_CHANNEL: "nightly"
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }}
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_BASE_URL }}
# 将 TEMP 和 TMP 目录设置到 C 盘
@@ -168,10 +168,10 @@ jobs:
run: npm run desktop:build
env:
# 设置更新通道,PR构建为nightly,否则为stable
UPDATE_CHANNEL: 'nightly'
UPDATE_CHANNEL: "nightly"
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_PROJECT_ID }}
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_NIGHTLY_DESKTOP_BASE_URL }}
@@ -188,7 +188,7 @@ jobs:
else
ARCH_SUFFIX="x64"
fi
mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml"
echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)"
ls -la latest-mac-*.yml
@@ -234,11 +234,11 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
# 下载所有平台的构建产物
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: release
pattern: release-*
@@ -287,7 +287,7 @@ jobs:
# 下载合并后的构建产物
- name: Download merged artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: merged-release-pr
path: release
+4 -2
View File
@@ -1,4 +1,6 @@
name: Publish Database Docker Image
permissions:
contents: read
on:
workflow_dispatch:
@@ -20,7 +22,7 @@ jobs:
# 添加 PR label 触发条件
if: |
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
github.event_name != 'pull_request'
strategy:
@@ -116,7 +118,7 @@ jobs:
fetch-depth: 0
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: /tmp/digests
pattern: digest-*
+4 -2
View File
@@ -1,4 +1,6 @@
name: Publish Docker Pglite Image
permissions:
contents: read
on:
workflow_dispatch:
@@ -20,7 +22,7 @@ jobs:
# 添加 PR label 触发条件
if: |
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
github.event_name != 'pull_request'
strategy:
@@ -116,7 +118,7 @@ jobs:
fetch-depth: 0
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: /tmp/digests
pattern: digest-*
+4 -2
View File
@@ -1,4 +1,6 @@
name: Publish Docker Image
permissions:
contents: read
on:
workflow_dispatch:
@@ -20,7 +22,7 @@ jobs:
# 添加 PR label 触发条件
if: |
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
github.event_name != 'pull_request'
strategy:
@@ -116,7 +118,7 @@ jobs:
fetch-depth: 0
- name: Download digests
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: /tmp/digests
pattern: digest-*
+52
View File
@@ -0,0 +1,52 @@
name: E2E CI
permissions:
contents: read
on:
pull_request:
push:
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
e2e:
name: Test Web App
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.2.23
- name: Install dependencies (bun)
run: bun install
- name: Install Playwright browsers (with system deps)
run: bunx playwright install --with-deps chromium
- name: Run E2E tests
env:
PORT: 3010
run: bun run e2e
- name: Upload Cucumber HTML report (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: cucumber-report
path: e2e/reports
if-no-files-found: ignore
- name: Upload screenshots (on failure)
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-screenshots
path: e2e/screenshots
if-no-files-found: ignore
@@ -0,0 +1,30 @@
name: Auto-close duplicate issues
description: Auto-closes issues that are duplicates of existing issues
on:
schedule:
- cron: "0 2 * * *"
workflow_dispatch:
jobs:
auto-close-duplicates:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
issues: write
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Auto-close duplicate issues
run: bun run .github/scripts/auto-close-duplicates.ts
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
+4 -9
View File
@@ -28,8 +28,7 @@ jobs:
👀 @{{ author }}
Thank you for raising an issue. We will investigate into the matter and get back to you as soon as possible.
Please make sure you have given us as much context as possible.\
非常感谢您提交 issue。我们会尽快调查此事,并尽快回复您。 请确保您已经提供了尽可能多的背景信息。
Please make sure you have given us as much context as possible.
- name: Auto Comment on Issues Closed
uses: wow-actions/auto-comment@v1
with:
@@ -37,8 +36,7 @@ jobs:
issuesClosed: |
✅ @{{ author }}
This issue is closed, If you have any questions, you can comment and reply.\
此问题已经关闭。如果您有任何问题,可以留言并回复。
This issue is closed, If you have any questions, you can comment and reply.
- name: Auto Comment on Pull Request Opened
uses: wow-actions/auto-comment@v1
with:
@@ -48,9 +46,7 @@ jobs:
Thank you for raising your pull request and contributing to our Community
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.
If you encounter any problems, please feel free to connect with us.\
非常感谢您提出拉取请求并为我们的社区做出贡献,请确保您已经遵循了我们的贡献指南,我们会尽快审查它。
如果您遇到任何问题,请随时与我们联系。
If you encounter any problems, please feel free to connect with us.
- name: Auto Comment on Pull Request Merged
uses: actions-cool/pr-welcome@main
if: github.event.pull_request.merged == true
@@ -59,8 +55,7 @@ jobs:
comment: |
❤️ Great PR @${{ github.event.pull_request.user.login }} ❤️
The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our [discord](https://discord.com/invite/AYFPHvv2jT) and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.\
项目的成长离不开用户反馈和贡献,感谢您的贡献! 如果您对 LobeHub 开发者社区感兴趣,请加入我们的 [discord](https://discord.com/invite/AYFPHvv2jT),然后私信 @arvinxx 或 @canisminor1990。他们会邀请您加入我们的私密开发者频道。我们将会讨论关于 Lobe Chat 的开发,分享和讨论全球范围内的 AI 消息。
The growth of project is inseparable from user feedback and contribution, thanks for your contribution! If you are interesting with the lobehub developer community, please join our [discord](https://discord.com/invite/AYFPHvv2jT) and then dm @arvinxx or @canisminor1990. They will invite you to our private developer channel. We are talking about the lobe-chat development or sharing ai newsletter around the world.
emoji: 'hooray'
pr-emoji: '+1, heart'
- name: Remove inactive
+3 -6
View File
@@ -38,8 +38,7 @@ jobs:
body: |
👋 @{{ author }}
<br/>
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
由于该 issue 被标记为已修复,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
Since the issue was labeled with `✅ Fixed`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
@@ -50,8 +49,7 @@ jobs:
body: |
👋 @{{ author }}
<br/>
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
由于该 issue 被标记为需要更多信息,却 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
Since the issue was labeled with `🤔 Need Reproduce`, but no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
- name: need reproduce
uses: actions-cool/issues-helper@v3
with:
@@ -62,5 +60,4 @@ jobs:
body: |
👋 @{{ github.event.issue.user.login }}
<br/>
Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.\
由于该 issue 被标记为暂不处理,同时 3 天未收到回应。现关闭 issue,若有任何问题,可评论回复。
Since the issue was labeled with `🙅🏻‍♀️ WON'T DO`, and no response in 3 days. This issue will be closed. If you have any questions, you can comment and reply.
-14
View File
@@ -1,14 +0,0 @@
name: Issue Translate
on:
issue_comment:
types: [created]
issues:
types: [opened]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: usthe/issues-translate-action@v2.7
with:
BOT_GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
+26
View File
@@ -0,0 +1,26 @@
name: "Lock Stale Issues"
on:
schedule:
- cron: "0 1 * * *"
workflow_dispatch:
permissions:
issues: write
concurrency:
group: lock-threads
jobs:
lock-closed-issues:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
- name: Lock closed issues after 7 days of inactivity
uses: actions/github-script@v8
with:
script: |
const lockScript = require('./.github/scripts/lock-closed-issues.js');
await lockScript({ github, context });
+13 -13
View File
@@ -15,7 +15,7 @@ permissions: read-all
jobs:
test:
name: Code quality check
# 添加 PR label 触发条件,只有添加了 Build Desktop 标签的 PR 才会触发构建
# 添加 PR label 触发条件,只有添加了 trigger:build-desktop 标签的 PR 才会触发构建
runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
steps:
- name: Checkout base
@@ -32,7 +32,7 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
- name: Install deps
run: bun i
@@ -82,7 +82,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, macos-13, windows-2025, ubuntu-latest]
os: [macos-latest, macos-15-intel, windows-2025, ubuntu-latest]
steps:
- uses: actions/checkout@v5
with:
@@ -116,9 +116,9 @@ jobs:
run: npm run desktop:build
env:
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
# 默认添加一个加密 SECRET
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
# macOS 签名和公证配置
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
@@ -137,8 +137,8 @@ jobs:
run: npm run desktop:build
env:
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_BETA_DESKTOP_PROJECT_ID }}
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_BETA_DESKTOP_BASE_URL }}
@@ -152,8 +152,8 @@ jobs:
run: npm run desktop:build
env:
APP_URL: http://localhost:3015
DATABASE_URL: 'postgresql://postgres@localhost:5432/postgres'
KEY_VAULTS_SECRET: 'oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE='
DATABASE_URL: "postgresql://postgres@localhost:5432/postgres"
KEY_VAULTS_SECRET: "oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE="
NEXT_PUBLIC_DESKTOP_PROJECT_ID: ${{ secrets.UMAMI_BETA_DESKTOP_PROJECT_ID }}
NEXT_PUBLIC_DESKTOP_UMAMI_BASE_URL: ${{ secrets.UMAMI_BETA_DESKTOP_BASE_URL }}
@@ -170,7 +170,7 @@ jobs:
else
ARCH_SUFFIX="x64"
fi
mv latest-mac.yml "latest-mac-${ARCH_SUFFIX}.yml"
echo "✅ Renamed latest-mac.yml to latest-mac-${ARCH_SUFFIX}.yml (detected: $SYSTEM_ARCH)"
ls -la latest-mac-*.yml
@@ -216,11 +216,11 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
# 下载所有平台的构建产物
- name: Download artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
path: release
pattern: release-*
@@ -262,7 +262,7 @@ jobs:
steps:
# 下载合并后的构建产物
- name: Download merged artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v5
with:
name: merged-release
path: release
+7 -1
View File
@@ -1,4 +1,10 @@
name: Release CI
permissions:
contents: write
issues: write
pull-requests: write
on:
push:
branches:
@@ -35,7 +41,7 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
- name: Install deps
run: bun i
@@ -1,4 +1,6 @@
name: Database Schema Visualization CI
permissions:
contents: read
on:
push:
+2 -2
View File
@@ -50,5 +50,5 @@ jobs:
![](https://github-production-user-asset-6210df.s3.amazonaws.com/17870709/273954625-df80c890-0822-4ac2-95e6-c990785cbed5.png)
[lobechat]: https://github.com/lobehub/lobe-chat
[tutorial-zh-CN]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync.zh-CN
[tutorial-en-US]: https://github.com/lobehub/lobe-chat/wiki/Upstream-Sync
[tutorial-zh-CN]: https://lobehub.com/zh/docs/self-hosting/advanced/upstream-sync
[tutorial-en-US]: https://lobehub.com/docs/self-hosting/advanced/upstream-sync
+13 -14
View File
@@ -18,6 +18,7 @@ jobs:
- web-crawler
- electron-server-ipc
- utils
- python-interpreter
- context-engine
- agent-runtime
@@ -44,7 +45,7 @@ jobs:
run: bun run --filter @lobechat/${{ matrix.package }} test:coverage
- name: Upload ${{ matrix.package }} coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/${{ matrix.package }}/coverage/lcov.info
@@ -70,7 +71,7 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
- name: Install deps
run: bun i
@@ -79,7 +80,7 @@ jobs:
run: bun run --filter ${{ matrix.package }} test:coverage
- name: Upload ${{ matrix.package }} coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/${{ matrix.package }}/coverage/lcov.info
@@ -103,7 +104,7 @@ jobs:
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
bun-version: 1.2.23
- name: Install deps
run: bun i
@@ -112,7 +113,7 @@ jobs:
run: bun run test-app:coverage
- name: Upload App Coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/app/lcov.info
@@ -144,26 +145,24 @@ jobs:
node-version: 22
package-manager-cache: false
- name: Install bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ secrets.BUN_VERSION }}
- name: Install pnpm
uses: pnpm/action-setup@v4
- name: Install deps
run: bun i
run: pnpm i
- name: Lint
run: bun run lint
run: npm run lint
- name: Test Client DB
run: bun run --filter @lobechat/database test:client-db
run: pnpm --filter @lobechat/database test:client-db
env:
KEY_VAULTS_SECRET: LA7n9k3JdEcbSgml2sxfw+4TV1AzaaFU5+R176aQz4s=
S3_PUBLIC_DOMAIN: https://example.com
APP_URL: https://home.com
- name: Test Coverage
run: bun run --filter @lobechat/database test:coverage
run: pnpm --filter @lobechat/database test:coverage
env:
DATABASE_TEST_URL: postgresql://postgres:postgres@localhost:5432/postgres
DATABASE_DRIVER: node
@@ -173,7 +172,7 @@ jobs:
APP_URL: https://home.com
- name: Upload Database coverage to Codecov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/database/coverage/lcov.info
+1
View File
@@ -117,3 +117,4 @@ CLAUDE.local.md
prd
GEMINI.md
e2e/reports
+1 -1
View File
@@ -25,7 +25,7 @@ module.exports = defineConfig({
],
temperature: 0,
saveImmediately: true,
modelName: 'gpt-4.1-mini',
modelName: 'chatgpt-4o-latest',
experimental: {
jsonMode: true,
},
-2
View File
@@ -4,8 +4,6 @@ resolution-mode=highest
ignore-workspace-root-check=true
enable-pre-post-scripts=true
# Load dotenv files for all the npm scripts
node-options="--require dotenv-expand/config"
public-hoist-pattern[]=*@umijs/lint*
public-hoist-pattern[]=*changelog*
View File
View File
View File
+2 -1
View File
@@ -11,6 +11,7 @@
{ "rule": "prettier/prettier", "severity": "off" },
{ "rule": "react/jsx-sort-props", "severity": "off" },
{ "rule": "sort-keys-fix/sort-keys-fix", "severity": "off" },
{ "rule": "simple-import-sort/exports", "severity": "off" },
{ "rule": "typescript-sort-keys/interface", "severity": "off" }
],
"eslint.validate": [
@@ -81,7 +82,7 @@
"**/src/config/modelProviders/*.ts": "${filename} • provider",
"**/packages/model-bank/src/aiModels/*.ts": "${filename} • model",
"**/packages/model-runtime/src/*/index.ts": "${dirname} • runtime",
"**/packages/model-runtime/src/providers/*/index.ts": "${dirname} • runtime",
"**/src/server/services/*/index.ts": "${dirname} • server/service",
"**/src/server/routers/lambda/*.ts": "${filename} • lambda",
+31 -52
View File
@@ -44,21 +44,7 @@ The project follows a well-organized monorepo structure:
#### TypeScript
- Follow strict TypeScript practices for type safety and code quality
- Use proper type annotations
- Prefer interfaces over types for object shapes
- Use generics for reusable components
#### React Components
- Use functional components with hooks
#### Database Schema
- Follow Drizzle ORM naming conventions
- Use plural snake_case for table names
- Implement proper foreign key relationships
- Follow the schema style guide
### Testing Strategy
@@ -67,64 +53,57 @@ The project follows a well-organized monorepo structure:
**Commands**:
- Web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'`
- Packages: `cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path-pattern]'`
- Packages: `cd packages/[package-name] && bunx vitest run --silent='passed-only' '[file-path-pattern]'` (each subpackage contains its own vitest.config.mts)
**Important Notes**:
- Wrap file paths in single quotes to avoid shell expansion
- Never run `bun run test` - this runs all tests and takes ~10 minutes
- If a test fails twice, stop and ask for help
- Always add tests for new code
- Never run `bun run test` - this runs all tests and takes \~10 minutes
### Type Checking
- Use `bun run type-check` to check for type errors
- Ensure all TypeScript errors are resolved before committing
### Internationalization
### i18n
- Add new keys to `src/locales/default/namespace.ts`
- Translate at least `zh-CN` files for development preview
- Use hierarchical nested objects, not flat keys
- Don't run `pnpm i18n` manually (handled by CI)
- **Keys**: Add to `src/locales/default/namespace.ts`
- **Dev**: Translate `locales/zh-CN/namespace.json` locale file only for preview
- DON'T run `pnpm i18n`, let CI auto handle it
## Available Development Rules
## Project Rules Index
The project provides comprehensive rules in `.cursor/rules/` directory:
All following rules are saved under `.cursor/rules/` directory:
### Core Development
### Backend
- `backend-architecture.mdc` - Three-layer architecture and data flow
- `react-component.mdc` - Component patterns and UI library usage
- `drizzle-schema-style-guide.mdc` - Database schema conventions
- `define-database-model.mdc` - Model templates and CRUD patterns
- `i18n.mdc` - Internationalization workflow
- `drizzle-schema-style-guide.mdc` Style guide for defining Drizzle ORM schemas
### State Management & UI
### Frontend
- `zustand-slice-organization.mdc` - Store organization patterns
- `zustand-action-patterns.mdc` - Action implementation patterns
- `packages/react-layout-kit.mdc` - Flex layout component usage
- `react-component.mdc` React component style guide and conventions
- `i18n.mdc` Internationalization guide using react-i18next
- `typescript.mdc` TypeScript code style guide
- `packages/react-layout-kit.mdc` Usage guide for react-layout-kit
### Testing & Quality
### State Management
- `testing-guide/testing-guide.mdc` - Comprehensive testing strategy
- `code-review.mdc` - Code review process and standards
- `zustand-action-patterns.mdc` Recommended patterns for organizing Zustand actions
- `zustand-slice-organization.mdc` Best practices for structuring Zustand slices
### Desktop (Electron)
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
- `desktop-local-tools-implement.mdc` - Tool integration workflow
- `desktop-menu-configuration.mdc` - Menu system configuration
- `desktop-window-management.mdc` - Window management patterns
- `desktop-controller-tests.mdc` - Controller testing guide
- `desktop-feature-implementation.mdc` Implementing new Electron desktop features
- `desktop-controller-tests.mdc` Desktop controller unit testing guide
- `desktop-local-tools-implement.mdc` Workflow to add new desktop local tools
- `desktop-menu-configuration.mdc` Desktop menu configuration guide
- `desktop-window-management.mdc` Desktop window management guide
## Best Practices
### Debugging
- **Conservative for existing code, modern approaches for new features**
- **Code Language**: Use Chinese for files with existing Chinese comments, American English for new files
- Always add tests for new functionality
- Follow the established patterns in the codebase
- Use proper error handling and logging
- Implement proper accessibility features
- Consider internationalization from the start
- `debug-usage.mdc` Using the debug package and namespace conventions
### Testing
- `testing-guide/testing-guide.mdc` Comprehensive testing guide for Vitest
- `testing-guide/electron-ipc-test.mdc` Electron IPC interface testing strategy
- `testing-guide/db-model-test.mdc` Database Model testing guide
+2301
View File
File diff suppressed because it is too large Load Diff
+5 -46
View File
@@ -31,28 +31,18 @@ This repository adopts a monorepo structure.
see @.cursor/rules/typescript.mdc
### Modify Code Rules
- **Code Language**:
- For files with existing Chinese comments: Continue using Chinese to maintain consistency
- For new files or files without Chinese comments: MUST use American English.
- eg: new react tsx file and new test file
- Conservative for existing code, modern approaches for new features
### Testing
Testing work follows the Rule-Aware Task Execution system above.
- **Required Rule**: `testing-guide/testing-guide.mdc`
- **Required Rule**: read `@.cursor/rules/testing-guide/testing-guide.mdc` before writing tests
- **Command**:
- web: `bunx vitest run --silent='passed-only' '[file-path-pattern]'`
- packages(eg: database): `cd packages/database && bunx vitest run --silent='passed-only' '[file-path-pattern]'`
**Important**:
- wrapped the file path in single quotes to avoid shell expansion
- wrap the file path in single quotes to avoid shell expansion
- Never run `bun run test` etc to run tests, this will run all tests and cost about 10mins
- If try to fix the same test twice, but still failed, stop and ask for help.
- If trying to fix the same test twice, but still failed, stop and ask for help.
### Typecheck
@@ -61,40 +51,9 @@ Testing work follows the Rule-Aware Task Execution system above.
### i18n
- **Keys**: Add to `src/locales/default/namespace.ts`
- **Dev**: Translate `locales/zh-CN/namespace.json` locale file only for preview
- **Dev**: Translate `locales/zh-CN/namespace.json` and `locales/en-US/namespace.json` locales file only for dev preview
- DON'T run `pnpm i18n`, let CI auto handle it
## Rules Index
Some useful rules of this project. Read them when needed.
**IMPORTANT**: All rule files referenced in this document are located in the `.cursor/rules/` directory. Throughout this document, rule files are referenced by their filename only for brevity.
### 📋 Complete Rule Files
**Core Development**
- `backend-architecture.mdc` - Three-layer architecture, data flow
- `react-component.mdc` - antd-style, Lobe UI usage
- `drizzle-schema-style-guide.mdc` - Schema naming, patterns
- `define-database-model.mdc` - Model templates, CRUD patterns
- `i18n.mdc` - Internationalization workflow
**State & UI**
- `zustand-slice-organization.mdc` - Store organization
- `zustand-action-patterns.mdc` - Action patterns
- `packages/react-layout-kit.mdc` - flex layout components usage
**Testing & Quality**
- `testing-guide/testing-guide.mdc` - Test strategy, mock patterns
- `code-review.mdc` - Review process and standards
**Desktop (Electron)**
- `desktop-feature-implementation.mdc` - Main/renderer process patterns
- `desktop-local-tools-implement.mdc` - Tool integration workflow
- `desktop-menu-configuration.mdc` - App menu, context menu, tray menu
- `desktop-window-management.mdc` - Window creation, state management, multi-window
- `desktop-controller-tests.mdc` - Controller unit testing guide
Some useful project rules are listed in @.cursor/rules/rules-index.mdc
+8 -4
View File
@@ -142,7 +142,8 @@ ENV ACCESS_CODE="" \
DEFAULT_AGENT_CONFIG="" \
SYSTEM_AGENT="" \
FEATURE_FLAGS="" \
PROXY_URL=""
PROXY_URL="" \
ENABLE_AUTH_PROTECTION=""
# Model Variables
ENV \
@@ -155,7 +156,7 @@ ENV \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
ENABLED_AWS_BEDROCK="" AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
# Azure OpenAI
AZURE_API_KEY="" AZURE_API_VERSION="" AZURE_ENDPOINT="" AZURE_MODEL_LIST="" \
# Baichuan
@@ -164,6 +165,9 @@ ENV \
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
# Cohere
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
# ComfyUI
ENABLED_COMFYUI="" COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
# DeepSeek
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
# Fireworks AI
@@ -205,7 +209,7 @@ ENV \
# Ollama
ENABLED_OLLAMA="" OLLAMA_MODEL_LIST="" OLLAMA_PROXY_URL="" \
# OpenAI
OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
ENABLED_OPENAI="" OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
# OpenRouter
OPENROUTER_API_KEY="" OPENROUTER_MODEL_LIST="" \
# Perplexity
@@ -255,7 +259,7 @@ ENV \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
ENABLED_FAL="" FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST="" \
# Vercel AI Gateway
+23 -6
View File
@@ -39,6 +39,8 @@ ARG USE_CN_MIRROR
ARG NEXT_PUBLIC_BASE_PATH
ARG NEXT_PUBLIC_SERVICE_MODE
ARG NEXT_PUBLIC_ENABLE_NEXT_AUTH
ARG NEXT_PUBLIC_ENABLE_CLERK_AUTH
ARG NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_SENTRY_DSN
ARG NEXT_PUBLIC_ANALYTICS_POSTHOG
ARG NEXT_PUBLIC_POSTHOG_HOST
@@ -53,6 +55,9 @@ ENV NEXT_PUBLIC_BASE_PATH="${NEXT_PUBLIC_BASE_PATH}" \
ENV NEXT_PUBLIC_SERVICE_MODE="${NEXT_PUBLIC_SERVICE_MODE:-server}" \
NEXT_PUBLIC_ENABLE_NEXT_AUTH="${NEXT_PUBLIC_ENABLE_NEXT_AUTH:-1}" \
NEXT_PUBLIC_ENABLE_CLERK_AUTH="${NEXT_PUBLIC_ENABLE_CLERK_AUTH:-0}" \
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}" \
CLERK_WEBHOOK_SECRET="whsec_xxx" \
APP_URL="http://app.com" \
DATABASE_DRIVER="node" \
DATABASE_URL="postgres://postgres:password@localhost:5432/postgres" \
@@ -166,7 +171,8 @@ ENV ACCESS_CODE="" \
DEFAULT_AGENT_CONFIG="" \
SYSTEM_AGENT="" \
FEATURE_FLAGS="" \
PROXY_URL=""
PROXY_URL="" \
ENABLE_AUTH_PROTECTION=""
# Database
ENV KEY_VAULTS_SECRET="" \
@@ -178,13 +184,19 @@ ENV NEXT_AUTH_SECRET="" \
NEXT_AUTH_SSO_PROVIDERS="" \
NEXTAUTH_URL=""
# Clerk
ENV CLERK_SECRET_KEY="" \
CLERK_WEBHOOK_SECRET=""
# S3
ENV NEXT_PUBLIC_S3_DOMAIN="" \
S3_PUBLIC_DOMAIN="" \
S3_ACCESS_KEY_ID="" \
S3_BUCKET="" \
S3_ENDPOINT="" \
S3_SECRET_ACCESS_KEY=""
S3_SECRET_ACCESS_KEY="" \
S3_ENABLE_PATH_STYLE="" \
S3_SET_ACL=""
# Model Variables
ENV \
@@ -197,7 +209,7 @@ ENV \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
ENABLED_AWS_BEDROCK="" AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
# Azure OpenAI
AZURE_API_KEY="" AZURE_API_VERSION="" AZURE_ENDPOINT="" AZURE_MODEL_LIST="" \
# Baichuan
@@ -206,6 +218,9 @@ ENV \
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
# Cohere
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
# ComfyUI
ENABLED_COMFYUI="" COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
# DeepSeek
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
# Fireworks AI
@@ -247,7 +262,7 @@ ENV \
# Ollama
ENABLED_OLLAMA="" OLLAMA_MODEL_LIST="" OLLAMA_PROXY_URL="" \
# OpenAI
OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
ENABLED_OPENAI="" OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
# OpenRouter
OPENROUTER_API_KEY="" OPENROUTER_MODEL_LIST="" \
# Perplexity
@@ -297,11 +312,13 @@ ENV \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
ENABLED_FAL="" FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST="" \
# Vercel AI Gateway
VERCELAIGATEWAY_API_KEY="" VERCELAIGATEWAY_MODEL_LIST=""
VERCELAIGATEWAY_API_KEY="" VERCELAIGATEWAY_MODEL_LIST="" \
# Cerebras
CEREBRAS_API_KEY="" CEREBRAS_MODEL_LIST=""
USER nextjs
+8 -4
View File
@@ -144,7 +144,8 @@ ENV ACCESS_CODE="" \
DEFAULT_AGENT_CONFIG="" \
SYSTEM_AGENT="" \
FEATURE_FLAGS="" \
PROXY_URL=""
PROXY_URL="" \
ENABLE_AUTH_PROTECTION=""
# Model Variables
ENV \
@@ -157,7 +158,7 @@ ENV \
# Anthropic
ANTHROPIC_API_KEY="" ANTHROPIC_MODEL_LIST="" ANTHROPIC_PROXY_URL="" \
# Amazon Bedrock
AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
ENABLED_AWS_BEDROCK="" AWS_ACCESS_KEY_ID="" AWS_SECRET_ACCESS_KEY="" AWS_REGION="" AWS_BEDROCK_MODEL_LIST="" \
# Azure OpenAI
AZURE_API_KEY="" AZURE_API_VERSION="" AZURE_ENDPOINT="" AZURE_MODEL_LIST="" \
# Baichuan
@@ -166,6 +167,9 @@ ENV \
CLOUDFLARE_API_KEY="" CLOUDFLARE_BASE_URL_OR_ACCOUNT_ID="" CLOUDFLARE_MODEL_LIST="" \
# Cohere
COHERE_API_KEY="" COHERE_MODEL_LIST="" COHERE_PROXY_URL="" \
# ComfyUI
ENABLED_COMFYUI="" COMFYUI_BASE_URL="" COMFYUI_AUTH_TYPE="" \
COMFYUI_API_KEY="" COMFYUI_USERNAME="" COMFYUI_PASSWORD="" COMFYUI_CUSTOM_HEADERS="" \
# DeepSeek
DEEPSEEK_API_KEY="" DEEPSEEK_MODEL_LIST="" \
# Fireworks AI
@@ -207,7 +211,7 @@ ENV \
# Ollama
ENABLED_OLLAMA="" OLLAMA_MODEL_LIST="" OLLAMA_PROXY_URL="" \
# OpenAI
OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
ENABLED_OPENAI="" OPENAI_API_KEY="" OPENAI_MODEL_LIST="" OPENAI_PROXY_URL="" \
# OpenRouter
OPENROUTER_API_KEY="" OPENROUTER_MODEL_LIST="" \
# Perplexity
@@ -253,7 +257,7 @@ ENV \
# 302.AI
AI302_API_KEY="" AI302_MODEL_LIST="" \
# FAL
FAL_API_KEY="" FAL_MODEL_LIST="" \
ENABLED_FAL="" FAL_API_KEY="" FAL_MODEL_LIST="" \
# BFL
BFL_API_KEY="" BFL_MODEL_LIST="" \
# Vercel AI Gateway
+11 -12
View File
@@ -250,7 +250,7 @@ We have implemented support for the following model service providers:
- **[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.
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: Run serverless GPU-powered machine learning models on Cloudflare's global network.
<details><summary><kbd>See more providers (+32)</kbd></summary>
<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.
@@ -275,7 +275,6 @@ We have implemented support for the following model service providers:
- **[SenseNova](https://lobechat.com/discover/provider/sensenova)**: SenseNova, backed by SenseTime's robust infrastructure, offers efficient and user-friendly full-stack large model services.
- **[Stepfun](https://lobechat.com/discover/provider/stepfun)**: StepFun's large model possesses industry-leading multimodal and complex reasoning capabilities, supporting ultra-long text understanding and powerful autonomous scheduling search engine functions.
- **[Baichuan](https://lobechat.com/discover/provider/baichuan)**: Baichuan Intelligence is a company focused on the research and development of large AI models, with its models excelling in domestic knowledge encyclopedias, long text processing, and generative creation tasks in Chinese, surpassing mainstream foreign models. Baichuan Intelligence also possesses industry-leading multimodal capabilities, performing excellently in multiple authoritative evaluations. Its models include Baichuan 4, Baichuan 3 Turbo, and Baichuan 3 Turbo 128k, each optimized for different application scenarios, providing cost-effective solutions.
- **[Minimax](https://lobechat.com/discover/provider/minimax)**: MiniMax is a general artificial intelligence technology company established in 2021, dedicated to co-creating intelligence with users. MiniMax has independently developed general large models of different modalities, including trillion-parameter MoE text models, voice models, and image models, and has launched applications such as Conch AI.
- **[InternLM](https://lobechat.com/discover/provider/internlm)**: An open-source organization dedicated to the research and development of large model toolchains. It provides an efficient and user-friendly open-source platform for all AI developers, making cutting-edge large models and algorithm technologies easily accessible.
- **[Higress](https://lobechat.com/discover/provider/higress)**: Higress is a cloud-native API gateway that was developed internally at Alibaba to address the issues of Tengine reload affecting long-lived connections and the insufficient load balancing capabilities for gRPC/Dubbo.
- **[Gitee AI](https://lobechat.com/discover/provider/giteeai)**: Gitee AI's Serverless API provides AI developers with an out of the box large model inference API service.
@@ -287,7 +286,7 @@ We have implemented support for the following model service providers:
</details>
> 📊 Total providers: [<kbd>**42**</kbd>](https://lobechat.com/discover/providers)
> 📊 Total providers: [<kbd>**41**</kbd>](https://lobechat.com/discover/providers)
<!-- PROVIDER LIST -->
@@ -382,14 +381,14 @@ In addition, these plugins are not limited to news aggregation, but can also ext
<!-- PLUGIN LIST -->
| Recent Submits | Description |
| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [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` |
| [Tongyi wanxiang Image Generator](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | This plugin uses Alibaba's Tongyi Wanxiang model to generate images based on text prompts.<br/>`image` `tongyi` `wanxiang` |
| Recent Submits | Description |
| -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| [Shopping tools](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2026-01-12**</sup> | Search for products on eBay & AliExpress, find eBay events & coupons. Get prompt examples.<br/>`shopping` `e-bay` `ali-express` `coupons` |
| [SEO Assistant](https://lobechat.com/discover/plugin/seo_assistant)<br/><sup>By **webfx** on **2026-01-12**</sup> | The SEO Assistant can generate search engine keyword information in order to aid the creation of content.<br/>`seo` `keyword` |
| [Video Captions](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | Convert Youtube links into transcribed text, enable asking questions, create chapters, and summarize its content.<br/>`video-to-text` `youtube` |
| [WeatherGPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | Get current weather information for a specific location.<br/>`weather` |
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
> 📊 Total plugins: [<kbd>**40**</kbd>](https://lobechat.com/discover/plugins)
<!-- PLUGIN LIST -->
@@ -424,8 +423,8 @@ Our marketplace is not just a showcase platform but also a collaborative space.
| 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` |
| [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` |
| [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>**505**</kbd> ](https://lobechat.com/discover/assistants)
@@ -902,7 +901,7 @@ This project is [LobeHub Community License](./LICENSE) licensed.
[github-release-shield]: https://img.shields.io/github/v/release/lobehub/lobe-chat?color=369eff&labelColor=black&logo=github&style=flat-square
[github-releasedate-link]: https://github.com/lobehub/lobe-chat/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/lobehub/lobe-chat?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/lobehub/lobe-chat/network/stargazers
[github-stars-link]: https://github.com/lobehub/lobe-chat/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/lobehub/lobe-chat?color=ffcb47&labelColor=black&style=flat-square
[github-trending-shield]: https://trendshift.io/api/badge/repositories/2256
[github-trending-url]: https://trendshift.io/repositories/2256
+11 -12
View File
@@ -250,7 +250,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
- **[HuggingFace](https://lobechat.com/discover/provider/huggingface)**: HuggingFace Inference API 提供了一种快速且免费的方式,让您可以探索成千上万种模型,适用于各种任务。无论您是在为新应用程序进行原型设计,还是在尝试机器学习的功能,这个 API 都能让您即时访问多个领域的高性能模型。
- **[Cloudflare Workers AI](https://lobechat.com/discover/provider/cloudflare)**: 在 Cloudflare 的全球网络上运行由无服务器 GPU 驱动的机器学习模型。
<details><summary><kbd>See more providers (+32)</kbd></summary>
<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 初创公司的快速发展。
@@ -275,7 +275,6 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
- **[SenseNova](https://lobechat.com/discover/provider/sensenova)**: 商汤日日新,依托商汤大装置的强大的基础支撑,提供高效易用的全栈大模型服务。
- **[Stepfun](https://lobechat.com/discover/provider/stepfun)**: 阶级星辰大模型具备行业领先的多模态及复杂推理能力,支持超长文本理解和强大的自主调度搜索引擎功能。
- **[Baichuan](https://lobechat.com/discover/provider/baichuan)**: 百川智能是一家专注于人工智能大模型研发的公司,其模型在国内知识百科、长文本处理和生成创作等中文任务上表现卓越,超越了国外主流模型。百川智能还具备行业领先的多模态能力,在多项权威评测中表现优异。其模型包括 Baichuan 4、Baichuan 3 Turbo 和 Baichuan 3 Turbo 128k 等,分别针对不同应用场景进行优化,提供高性价比的解决方案。
- **[Minimax](https://lobechat.com/discover/provider/minimax)**: MiniMax 是 2021 年成立的通用人工智能科技公司,致力于与用户共创智能。MiniMax 自主研发了不同模态的通用大模型,其中包括万亿参数的 MoE 文本大模型、语音大模型以及图像大模型。并推出了海螺 AI 等应用。
- **[InternLM](https://lobechat.com/discover/provider/internlm)**: 致力于大模型研究与开发工具链的开源组织。为所有 AI 开发者提供高效、易用的开源平台,让最前沿的大模型与算法技术触手可及
- **[Higress](https://lobechat.com/discover/provider/higress)**: Higress 是一款云原生 API 网关,在阿里内部为解决 Tengine reload 对长连接业务有损,以及 gRPC/Dubbo 负载均衡能力不足而诞生。
- **[Gitee AI](https://lobechat.com/discover/provider/giteeai)**: Gitee AI 的 Serverless API 为 AI 开发者提供开箱即用的大模型推理 API 服务。
@@ -287,7 +286,7 @@ LobeChat 支持文件上传与知识库功能,你可以上传文件、图片
</details>
> 📊 Total providers: [<kbd>**42**</kbd>](https://lobechat.com/discover/providers)
> 📊 Total providers: [<kbd>**41**</kbd>](https://lobechat.com/discover/providers)
<!-- PROVIDER LIST -->
@@ -375,14 +374,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
<!-- PLUGIN LIST -->
| 最近新增 | 描述 |
| ---------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| [网页](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/>`网络` `搜索` |
| [通义万象图像生成器](https://lobechat.com/discover/plugin/alps-tongyi-image)<br/><sup>By **YoungTx** on **2024-08-09**</sup> | 此插件使用阿里巴巴的通义万象模型根据文本提示生成图像。<br/>`图像` `通义` `万象` |
| 最近新增 | 描述 |
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| [购物工具](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2026-01-12**</sup> | 在 eBay 和 AliExpress 上搜索产品,查找 eBay 活动和优惠券。获取快速示例。<br/>`购物` `e-bay` `ali-express` `优惠券` |
| [SEO 助手](https://lobechat.com/discover/plugin/seo_assistant)<br/><sup>By **webfx** on **2026-01-12**</sup> | SEO 助手可以生成搜索引擎关键词信息,以帮助创建内容。<br/>`seo` `关键词` |
| [视频字幕](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | 将 Youtube 链接转换为转录文本,使其能够提问,创建章节,并总结其内容。<br/>`视频转文字` `you-tube` |
| [天气 GPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | 获取特定位置的当前天气信息。<br/>`天气` |
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
> 📊 Total plugins: [<kbd>**40**</kbd>](https://lobechat.com/discover/plugins)
<!-- PLUGIN LIST -->
@@ -413,8 +412,8 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
| 最近新增 | 描述 |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| [海龟汤主持人](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/>`学术写作` `研究` `正式风格` |
| [美食评论员🍟](https://lobechat.com/discover/assistant/food-reviewer)<br/><sup>By **[renhai-lab](https://github.com/renhai-lab)** 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>**505**</kbd> ](https://lobechat.com/discover/assistants)
@@ -923,7 +922,7 @@ This project is [LobeHub Community License](./LICENSE) licensed.
[github-release-shield]: https://img.shields.io/github/v/release/lobehub/lobe-chat?color=369eff&labelColor=black&logo=github&style=flat-square
[github-releasedate-link]: https://github.com/lobehub/lobe-chat/releases
[github-releasedate-shield]: https://img.shields.io/github/release-date/lobehub/lobe-chat?labelColor=black&style=flat-square
[github-stars-link]: https://github.com/lobehub/lobe-chat/network/stargazers
[github-stars-link]: https://github.com/lobehub/lobe-chat/stargazers
[github-stars-shield]: https://img.shields.io/github/stars/lobehub/lobe-chat?color=ffcb47&labelColor=black&style=flat-square
[github-trending-shield]: https://trendshift.io/api/badge/repositories/2256
[github-trending-url]: https://trendshift.io/repositories/2256
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+42
View File
@@ -1,5 +1,7 @@
const dotenv = require('dotenv');
const fs = require('node:fs/promises');
const os = require('node:os');
const path = require('node:path');
dotenv.config();
@@ -32,11 +34,50 @@ const getProtocolScheme = () => {
const protocolScheme = getProtocolScheme();
// Determine icon file based on version type
const getIconFileName = () => {
if (isNightly) return 'Icon-nightly';
if (isBeta) return 'Icon-beta';
return 'Icon';
};
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration
*/
const config = {
/**
* AfterPack hook to copy pre-generated Liquid Glass Assets.car for macOS 26+
* @see https://github.com/electron-userland/electron-builder/issues/9254
* @see https://github.com/MultiboxLabs/flow-browser/pull/159
* @see https://github.com/electron/packager/pull/1806
*/
afterPack: async (context) => {
// Only process macOS builds
if (context.electronPlatformName !== 'darwin') {
return;
}
const iconFileName = getIconFileName();
const assetsCarSource = path.join(__dirname, 'build', `${iconFileName}.Assets.car`);
const resourcesPath = path.join(
context.appOutDir,
`${context.packager.appInfo.productFilename}.app`,
'Contents',
'Resources',
);
const assetsCarDest = path.join(resourcesPath, 'Assets.car');
try {
await fs.access(assetsCarSource);
await fs.copyFile(assetsCarSource, assetsCarDest);
console.log(`✅ Copied Liquid Glass icon: ${iconFileName}.Assets.car`);
} catch {
// Non-critical: Assets.car not found or copy failed
// App will use fallback .icns icon on all macOS versions
console.log(`⏭️ Skipping Assets.car (not found or copy failed)`);
}
},
appId: isNightly
? 'com.lobehub.lobehub-desktop-nightly'
: isBeta
@@ -81,6 +122,7 @@ const config = {
compression: 'maximum',
entitlementsInherit: 'build/entitlements.mac.plist',
extendInfo: {
CFBundleIconName: 'AppIcon',
CFBundleURLTypes: [
{
CFBundleURLName: 'LobeHub Protocol',
+2 -1
View File
@@ -39,7 +39,7 @@
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
"@electron-toolkit/eslint-config-ts": "^3.0.0",
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/tsconfig": "^1.0.1",
"@electron-toolkit/tsconfig": "^2.0.0",
"@electron-toolkit/utils": "^4.0.0",
"@lobechat/electron-client-ipc": "workspace:*",
"@lobechat/electron-server-ipc": "workspace:*",
@@ -59,6 +59,7 @@
"electron-store": "^8.2.0",
"electron-vite": "^3.0.0",
"execa": "^9.5.2",
"fast-glob": "^3.3.3",
"fix-path": "^5.0.0",
"http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6",
+51
View File
@@ -46,4 +46,55 @@ export const appBrowsers = {
},
} satisfies Record<string, BrowserWindowOpts>;
// Window templates for multi-instance windows
export interface WindowTemplate {
allowMultipleInstances: boolean;
// Include common BrowserWindow options
autoHideMenuBar?: boolean;
baseIdentifier: string;
basePath: string;
devTools?: boolean;
height?: number;
keepAlive?: boolean;
minWidth?: number;
parentIdentifier?: string;
showOnInit?: boolean;
title?: string;
titleBarStyle?: 'hidden' | 'default' | 'hiddenInset' | 'customButtonsOnHover';
vibrancy?:
| 'appearance-based'
| 'content'
| 'fullscreen-ui'
| 'header'
| 'hud'
| 'menu'
| 'popover'
| 'selection'
| 'sheet'
| 'sidebar'
| 'titlebar'
| 'tooltip'
| 'under-page'
| 'under-window'
| 'window';
width?: number;
}
export const windowTemplates = {
chatSingle: {
allowMultipleInstances: true,
autoHideMenuBar: true,
baseIdentifier: 'chatSingle',
basePath: '/chat',
height: 600,
keepAlive: false, // Multi-instance windows don't need to stay alive
minWidth: 400,
parentIdentifier: 'chat',
titleBarStyle: 'hidden',
vibrancy: 'under-window',
width: 900,
},
} satisfies Record<string, WindowTemplate>;
export type AppBrowsersIdentifiers = keyof typeof appBrowsers;
export type WindowTemplateIdentifiers = keyof typeof windowTemplates;
@@ -1,7 +1,11 @@
import { InterceptRouteParams } from '@lobechat/electron-client-ipc';
import { InterceptRouteParams, OpenSettingsWindowOptions } from '@lobechat/electron-client-ipc';
import { extractSubPath, findMatchingRoute } from '~common/routes';
import { AppBrowsersIdentifiers, BrowsersIdentifiers } from '@/appBrowsers';
import {
AppBrowsersIdentifiers,
BrowsersIdentifiers,
WindowTemplateIdentifiers,
} from '@/appBrowsers';
import { IpcClientEventSender } from '@/types/ipcClientEvent';
import { ControllerModule, ipcClientEvent, shortcut } from './index';
@@ -14,11 +18,16 @@ export default class BrowserWindowsCtr extends ControllerModule {
}
@ipcClientEvent('openSettingsWindow')
async openSettingsWindow(tab?: string) {
console.log('[BrowserWindowsCtr] Received request to open settings window', tab);
async openSettingsWindow(options?: string | OpenSettingsWindowOptions) {
const normalizedOptions: OpenSettingsWindowOptions =
typeof options === 'string' || options === undefined
? { tab: typeof options === 'string' ? options : undefined }
: options;
console.log('[BrowserWindowsCtr] Received request to open settings window', normalizedOptions);
try {
await this.app.browserManager.showSettingsWindowWithTab(tab);
await this.app.browserManager.showSettingsWindowWithTab(normalizedOptions);
return { success: true };
} catch (error) {
@@ -68,15 +77,37 @@ export default class BrowserWindowsCtr extends ControllerModule {
try {
if (matchedRoute.targetWindow === BrowsersIdentifiers.settings) {
const subPath = extractSubPath(path, matchedRoute.pathPrefix);
const extractedSubPath = extractSubPath(path, matchedRoute.pathPrefix);
const sanitizedSubPath =
extractedSubPath && !extractedSubPath.startsWith('?') ? extractedSubPath : undefined;
let searchParams: Record<string, string> | undefined;
try {
const url = new URL(params.url);
const entries = Array.from(url.searchParams.entries());
if (entries.length > 0) {
searchParams = entries.reduce<Record<string, string>>((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}
} catch (error) {
console.warn(
'[BrowserWindowsCtr] Failed to parse URL for settings route interception:',
params.url,
error,
);
}
await this.app.browserManager.showSettingsWindowWithTab(subPath);
await this.app.browserManager.showSettingsWindowWithTab({
searchParams,
tab: sanitizedSubPath,
});
return {
intercepted: true,
path,
source,
subPath,
subPath: sanitizedSubPath,
targetWindow: matchedRoute.targetWindow,
};
} else {
@@ -100,6 +131,77 @@ export default class BrowserWindowsCtr extends ControllerModule {
}
}
/**
* Create a new multi-instance window
*/
@ipcClientEvent('createMultiInstanceWindow')
async createMultiInstanceWindow(params: {
path: string;
templateId: WindowTemplateIdentifiers;
uniqueId?: string;
}) {
try {
console.log('[BrowserWindowsCtr] Creating multi-instance window:', params);
const result = this.app.browserManager.createMultiInstanceWindow(
params.templateId,
params.path,
params.uniqueId,
);
// Show the window
result.browser.show();
return {
success: true,
windowId: result.identifier,
};
} catch (error) {
console.error('[BrowserWindowsCtr] Failed to create multi-instance window:', error);
return {
error: error.message,
success: false,
};
}
}
/**
* Get all windows by template
*/
@ipcClientEvent('getWindowsByTemplate')
async getWindowsByTemplate(templateId: string) {
try {
const windowIds = this.app.browserManager.getWindowsByTemplate(templateId);
return {
success: true,
windowIds,
};
} catch (error) {
console.error('[BrowserWindowsCtr] Failed to get windows by template:', error);
return {
error: error.message,
success: false,
};
}
}
/**
* Close all windows by template
*/
@ipcClientEvent('closeWindowsByTemplate')
async closeWindowsByTemplate(templateId: string) {
try {
this.app.browserManager.closeWindowsByTemplate(templateId);
return { success: true };
} catch (error) {
console.error('[BrowserWindowsCtr] Failed to close windows by template:', error);
return {
error: error.message,
success: false,
};
}
}
/**
* Open target window and navigate to specified sub-path
*/
+279 -52
View File
@@ -1,4 +1,10 @@
import {
EditLocalFileParams,
EditLocalFileResult,
GlobFilesParams,
GlobFilesResult,
GrepContentParams,
GrepContentResult,
ListLocalFileParams,
LocalMoveFilesResultItem,
LocalReadFileParams,
@@ -13,10 +19,10 @@ import {
} from '@lobechat/electron-client-ipc';
import { SYSTEM_FILES_TO_IGNORE, loadFile } from '@lobechat/file-loaders';
import { shell } from 'electron';
import * as fs from 'node:fs';
import { rename as renamePromise } from 'node:fs/promises';
import fg from 'fast-glob';
import { Stats, constants } from 'node:fs';
import { access, mkdir, readFile, readdir, rename, stat, writeFile } from 'node:fs/promises';
import * as path from 'node:path';
import { promisify } from 'node:util';
import FileSearchService from '@/services/fileSearchSrv';
import { FileResult, SearchOptions } from '@/types/fileSearch';
@@ -25,40 +31,15 @@ import { createLogger } from '@/utils/logger';
import { ControllerModule, ipcClientEvent } from './index';
// 创建日志记录器
// Create logger
const logger = createLogger('controllers:LocalFileCtr');
const statPromise = promisify(fs.stat);
const readdirPromise = promisify(fs.readdir);
const renamePromiseFs = promisify(fs.rename);
const accessPromise = promisify(fs.access);
const writeFilePromise = promisify(fs.writeFile);
export default class LocalFileCtr extends ControllerModule {
private get searchService() {
return this.app.getService(FileSearchService);
}
/**
* Handle IPC event for local file search
*/
@ipcClientEvent('searchLocalFiles')
async handleLocalFilesSearch(params: LocalSearchFilesParams): Promise<FileResult[]> {
logger.debug('Received file search request:', { keywords: params.keywords });
const options: Omit<SearchOptions, 'keywords'> = {
limit: 30,
};
try {
const results = await this.searchService.search(params.keywords, options);
logger.debug('File search completed', { count: results.length });
return results;
} catch (error) {
logger.error('File search failed:', error);
return [];
}
}
// ==================== File Operation ====================
@ipcClientEvent('openLocalFile')
async handleOpenLocalFile({ path: filePath }: OpenLocalFileParams): Promise<{
@@ -102,7 +83,7 @@ export default class LocalFileCtr extends ControllerModule {
const results: LocalReadFileResult[] = [];
for (const filePath of paths) {
// 初始化结果对象
// Initialize result object
logger.debug('Reading single file:', { filePath });
const result = await this.readFile({ path: filePath });
results.push(result);
@@ -158,7 +139,7 @@ export default class LocalFileCtr extends ControllerModule {
};
try {
const stats = await statPromise(filePath);
const stats = await stat(filePath);
if (stats.isDirectory()) {
logger.warn('Attempted to read directory content:', { filePath });
result.content = 'This is a directory and cannot be read as plain text.';
@@ -197,7 +178,7 @@ export default class LocalFileCtr extends ControllerModule {
const results: FileResult[] = [];
try {
const entries = await readdirPromise(dirPath);
const entries = await readdir(dirPath);
logger.debug('Directory entries retrieved successfully:', {
dirPath,
entriesCount: entries.length,
@@ -212,7 +193,7 @@ export default class LocalFileCtr extends ControllerModule {
const fullPath = path.join(dirPath, entry);
try {
const stats = await statPromise(fullPath);
const stats = await stat(fullPath);
const isDirectory = stats.isDirectory();
results.push({
createdTime: stats.birthtime,
@@ -260,7 +241,7 @@ export default class LocalFileCtr extends ControllerModule {
return [];
}
// 逐个处理移动请求
// Process each move request
for (const item of items) {
const { oldPath: sourcePath, newPath } = item;
const logPrefix = `[Moving file ${sourcePath} -> ${newPath}]`;
@@ -272,7 +253,7 @@ export default class LocalFileCtr extends ControllerModule {
success: false,
};
// 基本验证
// Basic validation
if (!sourcePath || !newPath) {
logger.error(`${logPrefix} Parameter validation failed: source or target path is empty`);
resultItem.error = 'Both oldPath and newPath are required for each item.';
@@ -281,9 +262,9 @@ export default class LocalFileCtr extends ControllerModule {
}
try {
// 检查源是否存在
// Check if source exists
try {
await accessPromise(sourcePath, fs.constants.F_OK);
await access(sourcePath, constants.F_OK);
logger.debug(`${logPrefix} Source file exists`);
} catch (accessError: any) {
if (accessError.code === 'ENOENT') {
@@ -297,28 +278,28 @@ export default class LocalFileCtr extends ControllerModule {
}
}
// 检查目标路径是否与源路径相同
// Check if target path is the same as source path
if (path.normalize(sourcePath) === path.normalize(newPath)) {
logger.info(`${logPrefix} Source and target paths are identical, skipping move`);
resultItem.success = true;
resultItem.newPath = newPath; // 即使未移动,也报告目标路径
resultItem.newPath = newPath; // Report target path even if not moved
results.push(resultItem);
continue;
}
// LBYL: 确保目标目录存在
// LBYL: Ensure target directory exists
const targetDir = path.dirname(newPath);
makeSureDirExist(targetDir);
logger.debug(`${logPrefix} Ensured target directory exists: ${targetDir}`);
// 执行移动 (rename)
await renamePromiseFs(sourcePath, newPath);
// Execute move (rename)
await rename(sourcePath, newPath);
resultItem.success = true;
resultItem.newPath = newPath;
logger.info(`${logPrefix} Move successful`);
} catch (error) {
logger.error(`${logPrefix} Move failed:`, error);
// 使用与 handleMoveFile 类似的错误处理逻辑
// Use similar error handling logic as handleMoveFile
let errorMessage = (error as Error).message;
if ((error as any).code === 'ENOENT')
errorMessage = `Source path not found: ${sourcePath}.`;
@@ -334,7 +315,7 @@ export default class LocalFileCtr extends ControllerModule {
errorMessage = `The target directory ${newPath} is not empty (relevant on some systems if target exists and is a directory).`;
else if ((error as any).code === 'EEXIST')
errorMessage = `An item already exists at the target path: ${newPath}.`;
// 保留来自访问检查或目录检查的更具体错误
// Keep more specific errors from access or directory checks
else if (
!errorMessage.startsWith('Source path not found') &&
!errorMessage.startsWith('Permission denied accessing source path') &&
@@ -411,9 +392,9 @@ export default class LocalFileCtr extends ControllerModule {
};
}
// Perform the rename operation using fs.promises.rename directly
// Perform the rename operation using rename directly
try {
await renamePromise(currentPath, newPath);
await rename(currentPath, newPath);
logger.info(`${logPrefix} Rename successful: ${currentPath} -> ${newPath}`);
// Optionally return the newPath if frontend needs it
// return { success: true, newPath: newPath };
@@ -444,7 +425,7 @@ export default class LocalFileCtr extends ControllerModule {
const logPrefix = `[Writing file ${filePath}]`;
logger.debug(`${logPrefix} Starting to write file`, { contentLength: content?.length });
// 验证参数
// Validate parameters
if (!filePath) {
logger.error(`${logPrefix} Parameter validation failed: path is empty`);
return { error: 'Path cannot be empty', success: false };
@@ -456,14 +437,14 @@ export default class LocalFileCtr extends ControllerModule {
}
try {
// 确保目标目录存在
// Ensure target directory exists (use async to avoid blocking main thread)
const dirname = path.dirname(filePath);
logger.debug(`${logPrefix} Creating directory: ${dirname}`);
fs.mkdirSync(dirname, { recursive: true });
await mkdir(dirname, { recursive: true });
// 写入文件内容
// Write file content
logger.debug(`${logPrefix} Starting to write content to file`);
await writeFilePromise(filePath, content, 'utf8');
await writeFile(filePath, content, 'utf8');
logger.info(`${logPrefix} File written successfully`, {
path: filePath,
size: content.length,
@@ -478,4 +459,250 @@ export default class LocalFileCtr extends ControllerModule {
};
}
}
// ==================== Search & Find ====================
/**
* Handle IPC event for local file search
*/
@ipcClientEvent('searchLocalFiles')
async handleLocalFilesSearch(params: LocalSearchFilesParams): Promise<FileResult[]> {
logger.debug('Received file search request:', { keywords: params.keywords });
const options: Omit<SearchOptions, 'keywords'> = {
limit: 30,
};
try {
const results = await this.searchService.search(params.keywords, options);
logger.debug('File search completed', { count: results.length });
return results;
} catch (error) {
logger.error('File search failed:', error);
return [];
}
}
@ipcClientEvent('grepContent')
async handleGrepContent(params: GrepContentParams): Promise<GrepContentResult> {
const {
pattern,
path: searchPath = process.cwd(),
output_mode = 'files_with_matches',
} = params;
const logPrefix = `[grepContent: ${pattern}]`;
logger.debug(`${logPrefix} Starting content search`, { output_mode, searchPath });
try {
const regex = new RegExp(
pattern,
`g${params['-i'] ? 'i' : ''}${params.multiline ? 's' : ''}`,
);
// Determine files to search
let filesToSearch: string[] = [];
const stats = await stat(searchPath);
if (stats.isFile()) {
filesToSearch = [searchPath];
} else {
// Use glob pattern if provided, otherwise search all files
const globPattern = params.glob || '**/*';
filesToSearch = await fg(globPattern, {
absolute: true,
cwd: searchPath,
dot: true,
ignore: ['**/node_modules/**', '**/.git/**'],
});
// Filter by type if provided
if (params.type) {
const ext = `.${params.type}`;
filesToSearch = filesToSearch.filter((file) => file.endsWith(ext));
}
}
logger.debug(`${logPrefix} Found ${filesToSearch.length} files to search`);
const matches: string[] = [];
let totalMatches = 0;
for (const filePath of filesToSearch) {
try {
const fileStats = await stat(filePath);
if (!fileStats.isFile()) continue;
const content = await readFile(filePath, 'utf8');
const lines = content.split('\n');
switch (output_mode) {
case 'files_with_matches': {
if (regex.test(content)) {
matches.push(filePath);
totalMatches++;
if (params.head_limit && matches.length >= params.head_limit) break;
}
break;
}
case 'content': {
const matchedLines: string[] = [];
for (let i = 0; i < lines.length; i++) {
if (regex.test(lines[i])) {
const contextBefore = params['-B'] || params['-C'] || 0;
const contextAfter = params['-A'] || params['-C'] || 0;
const startLine = Math.max(0, i - contextBefore);
const endLine = Math.min(lines.length - 1, i + contextAfter);
for (let j = startLine; j <= endLine; j++) {
const lineNum = params['-n'] ? `${j + 1}:` : '';
matchedLines.push(`${filePath}:${lineNum}${lines[j]}`);
}
totalMatches++;
}
}
matches.push(...matchedLines);
if (params.head_limit && matches.length >= params.head_limit) break;
break;
}
case 'count': {
const fileMatches = (content.match(regex) || []).length;
if (fileMatches > 0) {
matches.push(`${filePath}:${fileMatches}`);
totalMatches += fileMatches;
}
break;
}
}
} catch (error) {
logger.debug(`${logPrefix} Skipping file ${filePath}:`, error);
}
}
logger.info(`${logPrefix} Search completed`, {
matchCount: matches.length,
totalMatches,
});
return {
matches: params.head_limit ? matches.slice(0, params.head_limit) : matches,
success: true,
total_matches: totalMatches,
};
} catch (error) {
logger.error(`${logPrefix} Grep failed:`, error);
return {
matches: [],
success: false,
total_matches: 0,
};
}
}
@ipcClientEvent('globLocalFiles')
async handleGlobFiles({
path: searchPath = process.cwd(),
pattern,
}: GlobFilesParams): Promise<GlobFilesResult> {
const logPrefix = `[globFiles: ${pattern}]`;
logger.debug(`${logPrefix} Starting glob search`, { searchPath });
try {
const files = await fg(pattern, {
absolute: true,
cwd: searchPath,
dot: true,
onlyFiles: false,
stats: true,
});
// Sort by modification time (most recent first)
const sortedFiles = (files as unknown as Array<{ path: string; stats: Stats }>)
.sort((a, b) => b.stats.mtime.getTime() - a.stats.mtime.getTime())
.map((f) => f.path);
logger.info(`${logPrefix} Glob completed`, { fileCount: sortedFiles.length });
return {
files: sortedFiles,
success: true,
total_files: sortedFiles.length,
};
} catch (error) {
logger.error(`${logPrefix} Glob failed:`, error);
return {
files: [],
success: false,
total_files: 0,
};
}
}
// ==================== File Editing ====================
@ipcClientEvent('editLocalFile')
async handleEditFile({
file_path: filePath,
new_string,
old_string,
replace_all = false,
}: EditLocalFileParams): Promise<EditLocalFileResult> {
const logPrefix = `[editFile: ${filePath}]`;
logger.debug(`${logPrefix} Starting file edit`, { replace_all });
try {
// Read file content
const content = await readFile(filePath, 'utf8');
// Check if old_string exists
if (!content.includes(old_string)) {
logger.error(`${logPrefix} Old string not found in file`);
return {
error: 'The specified old_string was not found in the file',
replacements: 0,
success: false,
};
}
// Perform replacement
let newContent: string;
let replacements: number;
if (replace_all) {
const regex = new RegExp(old_string.replaceAll(/[$()*+.?[\\\]^{|}]/g, '\\$&'), 'g');
const matches = content.match(regex);
replacements = matches ? matches.length : 0;
newContent = content.replaceAll(old_string, new_string);
} else {
// Replace only first occurrence
const index = content.indexOf(old_string);
if (index === -1) {
return {
error: 'Old string not found',
replacements: 0,
success: false,
};
}
newContent =
content.slice(0, index) + new_string + content.slice(index + old_string.length);
replacements = 1;
}
// Write back to file
await writeFile(filePath, newContent, 'utf8');
logger.info(`${logPrefix} File edited successfully`, { replacements });
return {
replacements,
success: true,
};
} catch (error) {
logger.error(`${logPrefix} Edit failed:`, error);
return {
error: (error as Error).message,
replacements: 0,
success: false,
};
}
}
}
@@ -77,6 +77,7 @@ export default class SystemController extends ControllerModule {
// 更新i18n实例的语言
await this.app.i18n.changeLanguage(locale === 'auto' ? app.getLocale() : locale);
this.app.browserManager.broadcastToAllWindows('localeChanged', { locale });
return { success: true };
}
@@ -64,7 +64,7 @@ describe('BrowserWindowsCtr', () => {
it('should show the settings window with the specified tab', async () => {
const tab = 'appearance';
const result = await browserWindowsCtr.openSettingsWindow(tab);
expect(mockShowSettingsWindowWithTab).toHaveBeenCalledWith(tab);
expect(mockShowSettingsWindowWithTab).toHaveBeenCalledWith({ tab });
expect(result).toEqual({ success: true });
});
@@ -120,11 +120,11 @@ describe('BrowserWindowsCtr', () => {
it('should show settings window if matched route target is settings', async () => {
const params: InterceptRouteParams = {
...baseParams,
path: '/settings?active=common',
url: 'app://host/settings?active=common',
path: '/settings/provider',
url: 'app://host/settings/provider?active=provider&provider=ollama',
};
const matchedRoute = { targetWindow: BrowsersIdentifiers.settings, pathPrefix: '/settings' };
const subPath = 'common';
const subPath = 'provider';
(findMatchingRoute as Mock).mockReturnValue(matchedRoute);
(extractSubPath as Mock).mockReturnValue(subPath);
@@ -132,7 +132,10 @@ describe('BrowserWindowsCtr', () => {
expect(findMatchingRoute).toHaveBeenCalledWith(params.path);
expect(extractSubPath).toHaveBeenCalledWith(params.path, matchedRoute.pathPrefix);
expect(mockShowSettingsWindowWithTab).toHaveBeenCalledWith(subPath);
expect(mockShowSettingsWindowWithTab).toHaveBeenCalledWith({
searchParams: { active: 'provider', provider: 'ollama' },
tab: subPath,
});
expect(result).toEqual({
intercepted: true,
path: params.path,
@@ -170,11 +173,11 @@ describe('BrowserWindowsCtr', () => {
it('should return error if processing route interception fails for settings', async () => {
const params: InterceptRouteParams = {
...baseParams,
path: '/settings?active=general',
path: '/settings',
url: 'app://host/settings?active=general',
};
const matchedRoute = { targetWindow: BrowsersIdentifiers.settings, pathPrefix: '/settings' };
const subPath = 'general';
const subPath = undefined;
const errorMessage = 'Processing error for settings';
(findMatchingRoute as Mock).mockReturnValue(matchedRoute);
(extractSubPath as Mock).mockReturnValue(subPath);
@@ -182,6 +185,10 @@ describe('BrowserWindowsCtr', () => {
const result = await browserWindowsCtr.interceptRoute(params);
expect(mockShowSettingsWindowWithTab).toHaveBeenCalledWith({
searchParams: { active: 'general' },
tab: subPath,
});
expect(result).toEqual({
error: errorMessage,
intercepted: false,
@@ -0,0 +1,392 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { App } from '@/core/App';
import LocalFileCtr from '../LocalFileCtr';
// Mock logger
vi.mock('@/utils/logger', () => ({
createLogger: () => ({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
}));
// Mock file-loaders
vi.mock('@lobechat/file-loaders', () => ({
SYSTEM_FILES_TO_IGNORE: ['.DS_Store', 'Thumbs.db'],
loadFile: vi.fn(),
}));
// Mock electron
vi.mock('electron', () => ({
shell: {
openPath: vi.fn(),
},
}));
// Mock fast-glob
vi.mock('fast-glob', () => ({
default: vi.fn(),
}));
// Mock node:fs/promises and node:fs
vi.mock('node:fs/promises', () => ({
stat: vi.fn(),
readdir: vi.fn(),
rename: vi.fn(),
access: vi.fn(),
writeFile: vi.fn(),
readFile: vi.fn(),
mkdir: vi.fn(),
}));
vi.mock('node:fs', () => ({
Stats: class Stats {},
constants: {
F_OK: 0,
},
stat: vi.fn(),
readdir: vi.fn(),
rename: vi.fn(),
access: vi.fn(),
writeFile: vi.fn(),
readFile: vi.fn(),
}));
// Mock FileSearchService
const mockSearchService = {
search: vi.fn(),
};
// Mock makeSureDirExist
vi.mock('@/utils/file-system', () => ({
makeSureDirExist: vi.fn(),
}));
const mockApp = {
getService: vi.fn(() => mockSearchService),
} as unknown as App;
describe('LocalFileCtr', () => {
let localFileCtr: LocalFileCtr;
let mockShell: any;
let mockFg: any;
let mockLoadFile: any;
let mockFsPromises: any;
beforeEach(async () => {
vi.clearAllMocks();
// Import mocks
mockShell = (await import('electron')).shell;
mockFg = (await import('fast-glob')).default;
mockLoadFile = (await import('@lobechat/file-loaders')).loadFile;
mockFsPromises = await import('node:fs/promises');
localFileCtr = new LocalFileCtr(mockApp);
});
describe('handleOpenLocalFile', () => {
it('should open file successfully', async () => {
vi.mocked(mockShell.openPath).mockResolvedValue('');
const result = await localFileCtr.handleOpenLocalFile({ path: '/test/file.txt' });
expect(result).toEqual({ success: true });
expect(mockShell.openPath).toHaveBeenCalledWith('/test/file.txt');
});
it('should return error when opening file fails', async () => {
const error = new Error('Failed to open');
vi.mocked(mockShell.openPath).mockRejectedValue(error);
const result = await localFileCtr.handleOpenLocalFile({ path: '/test/file.txt' });
expect(result).toEqual({ success: false, error: 'Failed to open' });
});
});
describe('handleOpenLocalFolder', () => {
it('should open directory when isDirectory is true', async () => {
vi.mocked(mockShell.openPath).mockResolvedValue('');
const result = await localFileCtr.handleOpenLocalFolder({
path: '/test/folder',
isDirectory: true,
});
expect(result).toEqual({ success: true });
expect(mockShell.openPath).toHaveBeenCalledWith('/test/folder');
});
it('should open parent directory when isDirectory is false', async () => {
vi.mocked(mockShell.openPath).mockResolvedValue('');
const result = await localFileCtr.handleOpenLocalFolder({
path: '/test/folder/file.txt',
isDirectory: false,
});
expect(result).toEqual({ success: true });
expect(mockShell.openPath).toHaveBeenCalledWith('/test/folder');
});
it('should return error when opening folder fails', async () => {
const error = new Error('Failed to open folder');
vi.mocked(mockShell.openPath).mockRejectedValue(error);
const result = await localFileCtr.handleOpenLocalFolder({
path: '/test/folder',
isDirectory: true,
});
expect(result).toEqual({ success: false, error: 'Failed to open folder' });
});
});
describe('readFile', () => {
it('should read file successfully with default location', async () => {
const mockFileContent = 'line1\nline2\nline3\nline4\nline5';
vi.mocked(mockLoadFile).mockResolvedValue({
content: mockFileContent,
filename: 'test.txt',
fileType: 'txt',
createdTime: new Date('2024-01-01'),
modifiedTime: new Date('2024-01-02'),
});
const result = await localFileCtr.readFile({ path: '/test/file.txt' });
expect(result.filename).toBe('test.txt');
expect(result.fileType).toBe('txt');
expect(result.totalLineCount).toBe(5);
expect(result.content).toBe(mockFileContent);
});
it('should read file with custom location range', async () => {
const mockFileContent = 'line1\nline2\nline3\nline4\nline5';
vi.mocked(mockLoadFile).mockResolvedValue({
content: mockFileContent,
filename: 'test.txt',
fileType: 'txt',
createdTime: new Date('2024-01-01'),
modifiedTime: new Date('2024-01-02'),
});
const result = await localFileCtr.readFile({ path: '/test/file.txt', loc: [1, 3] });
expect(result.content).toBe('line2\nline3');
expect(result.lineCount).toBe(2);
expect(result.totalLineCount).toBe(5);
});
it('should handle file read error', async () => {
vi.mocked(mockLoadFile).mockRejectedValue(new Error('File not found'));
const result = await localFileCtr.readFile({ path: '/test/missing.txt' });
expect(result.content).toContain('Error accessing or processing file');
expect(result.lineCount).toBe(0);
expect(result.charCount).toBe(0);
});
});
describe('readFiles', () => {
it('should read multiple files successfully', async () => {
vi.mocked(mockLoadFile).mockResolvedValue({
content: 'file content',
filename: 'test.txt',
fileType: 'txt',
createdTime: new Date('2024-01-01'),
modifiedTime: new Date('2024-01-02'),
});
const result = await localFileCtr.readFiles({
paths: ['/test/file1.txt', '/test/file2.txt'],
});
expect(result).toHaveLength(2);
expect(mockLoadFile).toHaveBeenCalledTimes(2);
});
});
describe('handleWriteFile', () => {
it('should write file successfully', async () => {
vi.mocked(mockFsPromises.mkdir).mockResolvedValue(undefined);
vi.mocked(mockFsPromises.writeFile).mockResolvedValue(undefined);
const result = await localFileCtr.handleWriteFile({
path: '/test/file.txt',
content: 'test content',
});
expect(result).toEqual({ success: true });
});
it('should return error when path is empty', async () => {
const result = await localFileCtr.handleWriteFile({
path: '',
content: 'test content',
});
expect(result).toEqual({ success: false, error: 'Path cannot be empty' });
});
it('should return error when content is undefined', async () => {
const result = await localFileCtr.handleWriteFile({
path: '/test/file.txt',
content: undefined as any,
});
expect(result).toEqual({ success: false, error: 'Content cannot be empty' });
});
it('should handle write error', async () => {
vi.mocked(mockFsPromises.mkdir).mockResolvedValue(undefined);
vi.mocked(mockFsPromises.writeFile).mockRejectedValue(new Error('Write failed'));
const result = await localFileCtr.handleWriteFile({
path: '/test/file.txt',
content: 'test content',
});
expect(result).toEqual({ success: false, error: 'Failed to write file: Write failed' });
});
});
describe('handleRenameFile', () => {
it('should rename file successfully', async () => {
vi.mocked(mockFsPromises.rename).mockResolvedValue(undefined);
const result = await localFileCtr.handleRenameFile({
path: '/test/old.txt',
newName: 'new.txt',
});
expect(result).toEqual({ success: true, newPath: '/test/new.txt' });
expect(mockFsPromises.rename).toHaveBeenCalledWith('/test/old.txt', '/test/new.txt');
});
it('should skip rename when paths are identical', async () => {
const result = await localFileCtr.handleRenameFile({
path: '/test/file.txt',
newName: 'file.txt',
});
expect(result).toEqual({ success: true, newPath: '/test/file.txt' });
expect(mockFsPromises.rename).not.toHaveBeenCalled();
});
it('should reject invalid new name with path separators', async () => {
const result = await localFileCtr.handleRenameFile({
path: '/test/old.txt',
newName: '../new.txt',
});
expect(result.success).toBe(false);
expect(result.error).toContain('Invalid new name');
});
it('should reject invalid new name with special characters', async () => {
const result = await localFileCtr.handleRenameFile({
path: '/test/old.txt',
newName: 'new:file.txt',
});
expect(result.success).toBe(false);
expect(result.error).toContain('Invalid new name');
});
it('should handle file not found error', async () => {
const error: any = new Error('File not found');
error.code = 'ENOENT';
vi.mocked(mockFsPromises.rename).mockRejectedValue(error);
const result = await localFileCtr.handleRenameFile({
path: '/test/old.txt',
newName: 'new.txt',
});
expect(result.success).toBe(false);
expect(result.error).toContain('File or directory not found');
});
it('should handle file already exists error', async () => {
const error: any = new Error('File exists');
error.code = 'EEXIST';
vi.mocked(mockFsPromises.rename).mockRejectedValue(error);
const result = await localFileCtr.handleRenameFile({
path: '/test/old.txt',
newName: 'new.txt',
});
expect(result.success).toBe(false);
expect(result.error).toContain('already exists');
});
});
describe('handleLocalFilesSearch', () => {
it('should search files successfully', async () => {
const mockResults = [
{
name: 'test.txt',
path: '/test/test.txt',
isDirectory: false,
size: 100,
type: 'txt',
},
];
mockSearchService.search.mockResolvedValue(mockResults);
const result = await localFileCtr.handleLocalFilesSearch({ keywords: 'test' });
expect(result).toEqual(mockResults);
expect(mockSearchService.search).toHaveBeenCalledWith('test', { limit: 30 });
});
it('should return empty array on search error', async () => {
mockSearchService.search.mockRejectedValue(new Error('Search failed'));
const result = await localFileCtr.handleLocalFilesSearch({ keywords: 'test' });
expect(result).toEqual([]);
});
});
describe('handleGlobFiles', () => {
it('should glob files successfully', async () => {
const mockFiles = [
{ path: '/test/file1.txt', stats: { mtime: new Date('2024-01-02') } },
{ path: '/test/file2.txt', stats: { mtime: new Date('2024-01-01') } },
];
vi.mocked(mockFg).mockResolvedValue(mockFiles);
const result = await localFileCtr.handleGlobFiles({
pattern: '*.txt',
path: '/test',
});
expect(result.success).toBe(true);
expect(result.files).toEqual(['/test/file1.txt', '/test/file2.txt']);
expect(result.total_files).toBe(2);
});
it('should handle glob error', async () => {
vi.mocked(mockFg).mockRejectedValue(new Error('Glob failed'));
const result = await localFileCtr.handleGlobFiles({
pattern: '*.txt',
});
expect(result).toEqual({
success: false,
files: [],
total_files: 0,
});
});
});
});
+26 -1
View File
@@ -1,11 +1,12 @@
import { ElectronIPCEventHandler, ElectronIPCServer } from '@lobechat/electron-server-ipc';
import { Session, app, ipcMain, protocol } from 'electron';
import { macOS, windows } from 'electron-is';
import { pathExistsSync, remove } from 'fs-extra';
import os from 'node:os';
import { join } from 'node:path';
import { name } from '@/../../package.json';
import { buildDir, nextStandaloneDir } from '@/const/dir';
import { buildDir, LOCAL_DATABASE_DIR, nextStandaloneDir } from '@/const/dir';
import { isDev } from '@/const/env';
import { IControlModule } from '@/controllers';
import { IServiceModule } from '@/services';
@@ -129,6 +130,9 @@ export class App {
this.initDevBranding();
// Clean up stale database lock file before starting IPC server
await this.cleanupDatabaseLock();
// ==============
await this.ipcServer.start();
logger.debug('IPC server started');
@@ -371,6 +375,27 @@ export class App {
}
};
/**
* Clean up stale database lock file from previous crashes or abnormal exits
*/
private cleanupDatabaseLock = async () => {
try {
const dbPath = join(this.appStoragePath, LOCAL_DATABASE_DIR);
const lockPath = `${dbPath}.lock`;
if (pathExistsSync(lockPath)) {
logger.info(`Cleaning up stale database lock file: ${lockPath}`);
await remove(lockPath);
logger.info('Database lock file removed successfully');
} else {
logger.debug('No database lock file found, skipping cleanup');
}
} catch (error) {
logger.error('Failed to cleanup database lock file:', error);
// Non-fatal error, allow application to continue
}
};
private registerNextHandler() {
logger.debug('Registering Next.js handler');
const handler = createHandler({
@@ -0,0 +1,282 @@
import { app } from 'electron';
import { pathExistsSync, remove } from 'fs-extra';
import { join } from 'node:path';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { LOCAL_DATABASE_DIR } from '@/const/dir';
// Mock electron modules
vi.mock('electron', () => ({
app: {
getAppPath: vi.fn(() => '/mock/app/path'),
getLocale: vi.fn(() => 'en-US'),
getPath: vi.fn(() => '/mock/user/path'),
requestSingleInstanceLock: vi.fn(() => true),
whenReady: vi.fn(() => Promise.resolve()),
on: vi.fn(),
commandLine: {
appendSwitch: vi.fn(),
},
dock: {
setIcon: vi.fn(),
},
exit: vi.fn(),
},
ipcMain: {
handle: vi.fn(),
},
nativeTheme: {
on: vi.fn(),
shouldUseDarkColors: false,
},
protocol: {
registerSchemesAsPrivileged: vi.fn(),
},
}));
// Mock logger
vi.mock('@/utils/logger', () => ({
createLogger: () => ({
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
}),
}));
// Mock fs-extra module
vi.mock('fs-extra', async () => {
const actual = await vi.importActual('fs-extra');
return {
...actual,
pathExistsSync: vi.fn(),
remove: vi.fn(),
};
});
// Mock common/routes
vi.mock('~common/routes', () => ({
findMatchingRoute: vi.fn(),
extractSubPath: vi.fn(),
}));
// Mock other dependencies
vi.mock('electron-is', () => ({
macOS: vi.fn(() => false),
windows: vi.fn(() => false),
}));
vi.mock('fix-path', () => ({
default: vi.fn(),
}));
vi.mock('@/const/env', () => ({
isDev: false,
}));
vi.mock('@/const/dir', () => ({
buildDir: '/mock/build',
nextStandaloneDir: '/mock/standalone',
LOCAL_DATABASE_DIR: 'lobehub-local-db',
appStorageDir: '/mock/storage/path',
userDataDir: '/mock/user/data',
DB_SCHEMA_HASH_FILENAME: 'lobehub-local-db-schema-hash',
FILE_STORAGE_DIR: 'file-storage',
INSTALL_PLUGINS_DIR: 'plugins',
LOCAL_STORAGE_URL_PREFIX: '/lobe-desktop-file',
}));
vi.mock('@lobechat/electron-server-ipc', () => ({
ElectronIPCServer: vi.fn().mockImplementation(() => ({
start: vi.fn().mockResolvedValue(undefined),
})),
}));
// Mock all infrastructure managers
vi.mock('../infrastructure/I18nManager', () => ({
I18nManager: vi.fn().mockImplementation(() => ({
init: vi.fn().mockResolvedValue(undefined),
})),
}));
vi.mock('../infrastructure/StoreManager', () => ({
StoreManager: vi.fn().mockImplementation(() => ({
get: vi.fn((key) => {
if (key === 'storagePath') return '/mock/storage/path';
return undefined;
}),
set: vi.fn(),
})),
}));
vi.mock('../infrastructure/StaticFileServerManager', () => ({
StaticFileServerManager: vi.fn().mockImplementation(() => ({
initialize: vi.fn().mockResolvedValue(undefined),
destroy: vi.fn(),
})),
}));
vi.mock('../infrastructure/UpdaterManager', () => ({
UpdaterManager: vi.fn().mockImplementation(() => ({
initialize: vi.fn().mockResolvedValue(undefined),
})),
}));
vi.mock('../infrastructure/ProtocolManager', () => ({
ProtocolManager: vi.fn().mockImplementation(() => ({
initialize: vi.fn(),
processPendingUrls: vi.fn().mockResolvedValue(undefined),
})),
}));
vi.mock('../browser/BrowserManager', () => ({
BrowserManager: vi.fn().mockImplementation(() => ({
initializeBrowsers: vi.fn(),
getIdentifierByWebContents: vi.fn(),
})),
}));
vi.mock('../ui/MenuManager', () => ({
MenuManager: vi.fn().mockImplementation(() => ({
initialize: vi.fn(),
})),
}));
vi.mock('../ui/ShortcutManager', () => ({
ShortcutManager: vi.fn().mockImplementation(() => ({
initialize: vi.fn(),
})),
}));
vi.mock('../ui/TrayManager', () => ({
TrayManager: vi.fn().mockImplementation(() => ({
initializeTrays: vi.fn(),
destroyAll: vi.fn(),
})),
}));
vi.mock('@/utils/next-electron-rsc', () => ({
createHandler: vi.fn(() => ({
createInterceptor: vi.fn(),
registerCustomHandler: vi.fn(),
})),
}));
// Mock controllers and services
vi.mock('../../controllers/*Ctr.ts', () => ({}));
vi.mock('../../services/*Srv.ts', () => ({}));
// Import after mocks are set up
import { App } from '../App';
describe('App - Database Lock Cleanup', () => {
let appInstance: App;
let mockLockPath: string;
beforeEach(() => {
vi.clearAllMocks();
// Mock glob imports to return empty arrays
(import.meta as any).glob = vi.fn(() => ({}));
mockLockPath = join('/mock/storage/path', LOCAL_DATABASE_DIR) + '.lock';
});
afterEach(() => {
vi.clearAllMocks();
});
describe('bootstrap - database lock cleanup', () => {
it('should remove stale lock file if it exists during bootstrap', async () => {
// Setup: simulate existing lock file
vi.mocked(pathExistsSync).mockReturnValue(true);
vi.mocked(remove).mockResolvedValue(undefined);
// Create app instance
appInstance = new App();
// Call bootstrap which should trigger cleanup
await appInstance.bootstrap();
// Verify: lock file check was called
expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
// Verify: lock file was removed
expect(remove).toHaveBeenCalledWith(mockLockPath);
});
it('should not attempt to remove lock file if it does not exist', async () => {
// Setup: no lock file exists
vi.mocked(pathExistsSync).mockReturnValue(false);
// Create app instance
appInstance = new App();
// Call bootstrap
await appInstance.bootstrap();
// Verify: lock file check was called
expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
// Verify: remove was NOT called since file doesn't exist
expect(remove).not.toHaveBeenCalled();
});
it('should continue bootstrap even if lock cleanup fails', async () => {
// Setup: simulate lock file exists but cleanup fails
vi.mocked(pathExistsSync).mockReturnValue(true);
vi.mocked(remove).mockRejectedValue(new Error('Permission denied'));
// Create app instance
appInstance = new App();
// Bootstrap should not throw even if cleanup fails
await expect(appInstance.bootstrap()).resolves.not.toThrow();
// Verify: cleanup was attempted
expect(pathExistsSync).toHaveBeenCalledWith(mockLockPath);
expect(remove).toHaveBeenCalledWith(mockLockPath);
});
it('should clean up lock file before starting IPC server', async () => {
// Setup
vi.mocked(pathExistsSync).mockReturnValue(true);
const callOrder: string[] = [];
vi.mocked(remove).mockImplementation(async () => {
callOrder.push('remove');
});
// Mock IPC server start to track call order
const { ElectronIPCServer } = await import('@lobechat/electron-server-ipc');
const mockStart = vi.fn().mockImplementation(() => {
callOrder.push('ipcServer.start');
return Promise.resolve();
});
vi.mocked(ElectronIPCServer).mockImplementation(
() =>
({
start: mockStart,
}) as any,
);
// Create app instance and bootstrap
appInstance = new App();
await appInstance.bootstrap();
// Verify: cleanup happens before IPC server starts
expect(callOrder).toEqual(['remove', 'ipcServer.start']);
});
});
describe('appStoragePath', () => {
it('should return storage path from store manager', () => {
appInstance = new App();
const storagePath = appInstance.appStoragePath;
expect(storagePath).toBe('/mock/storage/path');
});
});
});
+36 -1
View File
@@ -336,7 +336,6 @@ export default class Browser {
vibrancy: 'sidebar',
visualEffectState: 'active',
webPreferences: {
backgroundThrottling: false,
contextIsolation: true,
preload: join(preloadDir, 'index.js'),
},
@@ -359,6 +358,9 @@ export default class Browser {
session: browserWindow.webContents.session,
});
// Setup CORS bypass for local file server
this.setupCORSBypass(browserWindow);
logger.debug(`[${this.identifier}] Initiating placeholder and URL loading sequence.`);
this.loadPlaceholder().then(() => {
this.loadUrl(path).catch((e) => {
@@ -492,4 +494,37 @@ export default class Browser {
logger.debug(`[${this.identifier}] Manually reapplying visual effects via Browser.`);
this.applyVisualEffects();
}
/**
* Setup CORS bypass for local file server (127.0.0.1:*)
* This is needed for Electron to access files from the local static file server
*/
private setupCORSBypass(browserWindow: BrowserWindow): void {
logger.debug(`[${this.identifier}] Setting up CORS bypass for local file server`);
const session = browserWindow.webContents.session;
// Intercept response headers to add CORS headers
session.webRequest.onHeadersReceived((details, callback) => {
const url = details.url;
// Only modify headers for local file server requests (127.0.0.1)
if (url.includes('127.0.0.1') || url.includes('lobe-desktop-file')) {
const responseHeaders = details.responseHeaders || {};
// Add CORS headers
responseHeaders['Access-Control-Allow-Origin'] = ['*'];
responseHeaders['Access-Control-Allow-Methods'] = ['GET, POST, PUT, DELETE, OPTIONS'];
responseHeaders['Access-Control-Allow-Headers'] = ['*'];
callback({
responseHeaders,
});
} else {
callback({ responseHeaders: details.responseHeaders });
}
});
logger.debug(`[${this.identifier}] CORS bypass setup completed`);
}
}
@@ -1,9 +1,18 @@
import { MainBroadcastEventKey, MainBroadcastParams } from '@lobechat/electron-client-ipc';
import {
MainBroadcastEventKey,
MainBroadcastParams,
OpenSettingsWindowOptions,
} from '@lobechat/electron-client-ipc';
import { WebContents } from 'electron';
import { createLogger } from '@/utils/logger';
import { AppBrowsersIdentifiers, appBrowsers } from '../../appBrowsers';
import {
AppBrowsersIdentifiers,
WindowTemplateIdentifiers,
appBrowsers,
windowTemplates,
} from '../../appBrowsers';
import type { App } from '../App';
import type { BrowserWindowOpts } from './Browser';
import Browser from './Browser';
@@ -14,9 +23,9 @@ const logger = createLogger('core:BrowserManager');
export class BrowserManager {
app: App;
browsers: Map<AppBrowsersIdentifiers, Browser> = new Map();
browsers: Map<string, Browser> = new Map();
private webContentsMap = new Map<WebContents, AppBrowsersIdentifiers>();
private webContentsMap = new Map<WebContents, string>();
constructor(app: App) {
logger.debug('Initializing BrowserManager');
@@ -51,26 +60,47 @@ export class BrowserManager {
};
broadcastToWindow = <T extends MainBroadcastEventKey>(
identifier: AppBrowsersIdentifiers,
identifier: string,
event: T,
data: MainBroadcastParams<T>,
) => {
logger.debug(`Broadcasting event ${event} to window: ${identifier}`);
this.browsers.get(identifier).broadcast(event, data);
this.browsers.get(identifier)?.broadcast(event, data);
};
/**
* Display the settings window and navigate to a specific tab
* @param tab Settings window sub-path tab
*/
async showSettingsWindowWithTab(tab?: string) {
logger.debug(`Showing settings window with tab: ${tab || 'default'}`);
// common is the main path for settings route
if (tab && tab !== 'common') {
const browser = await this.redirectToPage('settings', tab);
async showSettingsWindowWithTab(options?: OpenSettingsWindowOptions) {
const tab = options?.tab;
const searchParams = options?.searchParams;
const query = new URLSearchParams();
if (searchParams) {
Object.entries(searchParams).forEach(([key, value]) => {
if (value !== undefined) query.set(key, value);
});
}
if (tab && tab !== 'common' && !query.has('active')) {
query.set('active', tab);
}
const queryString = query.toString();
const activeTab = query.get('active') ?? tab;
logger.debug(
`Showing settings window with navigation: active=${activeTab || 'default'}, query=${
queryString || 'none'
}`,
);
if (queryString) {
const browser = await this.redirectToPage('settings', undefined, queryString);
// make provider page more large
if (tab.startsWith('provider/')) {
if (activeTab?.startsWith('provider')) {
logger.debug('Resizing window for provider settings');
browser.setWindowSize({ height: 1000, width: 1400 });
browser.moveToCenter();
@@ -87,21 +117,32 @@ export class BrowserManager {
* @param identifier Window identifier
* @param subPath Sub-path, such as 'agent', 'about', etc.
*/
async redirectToPage(identifier: AppBrowsersIdentifiers, subPath?: string) {
async redirectToPage(identifier: string, subPath?: string, search?: string) {
try {
// Ensure window is retrieved or created
const browser = this.retrieveByIdentifier(identifier);
browser.hide();
const baseRoute = appBrowsers[identifier].path;
// Handle both static and dynamic windows
let baseRoute: string;
if (identifier in appBrowsers) {
baseRoute = appBrowsers[identifier as AppBrowsersIdentifiers].path;
} else {
// For dynamic windows, extract base route from the browser options
const browserOptions = browser.options;
baseRoute = browserOptions.path;
}
// Build complete URL path
const fullPath = subPath ? `${baseRoute}/${subPath}` : baseRoute;
const normalizedSearch =
search && search.length > 0 ? (search.startsWith('?') ? search : `?${search}`) : '';
const fullUrl = `${fullPath}${normalizedSearch}`;
logger.debug(`Redirecting to: ${fullPath}`);
logger.debug(`Redirecting to: ${fullUrl}`);
// Load URL and show window
await browser.loadUrl(fullPath);
await browser.loadUrl(fullUrl);
browser.show();
return browser;
@@ -114,13 +155,81 @@ export class BrowserManager {
/**
* get Browser by identifier
*/
retrieveByIdentifier(identifier: AppBrowsersIdentifiers) {
retrieveByIdentifier(identifier: string) {
const browser = this.browsers.get(identifier);
if (browser) return browser;
logger.debug(`Browser ${identifier} not found, initializing new instance`);
return this.retrieveOrInitialize(appBrowsers[identifier]);
// Check if it's a static browser
if (identifier in appBrowsers) {
logger.debug(`Browser ${identifier} not found, initializing new instance`);
return this.retrieveOrInitialize(appBrowsers[identifier as AppBrowsersIdentifiers]);
}
throw new Error(`Browser ${identifier} not found and is not a static browser`);
}
/**
* Create a multi-instance window from template
* @param templateId Template identifier
* @param path Full path with query parameters
* @param uniqueId Optional unique identifier, will be generated if not provided
* @returns The window identifier and Browser instance
*/
createMultiInstanceWindow(
templateId: WindowTemplateIdentifiers,
path: string,
uniqueId?: string,
) {
const template = windowTemplates[templateId];
if (!template) {
throw new Error(`Window template ${templateId} not found`);
}
// Generate unique identifier
const windowId =
uniqueId ||
`${template.baseIdentifier}_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
// Create browser options from template
const browserOpts: BrowserWindowOpts = {
...template,
identifier: windowId,
path: path,
};
logger.debug(`Creating multi-instance window: ${windowId} with path: ${path}`);
const browser = this.retrieveOrInitialize(browserOpts);
return {
browser: browser,
identifier: windowId,
};
}
/**
* Get all windows based on template
* @param templateId Template identifier
* @returns Array of window identifiers matching the template
*/
getWindowsByTemplate(templateId: string): string[] {
const prefix = `${templateId}_`;
return Array.from(this.browsers.keys()).filter((id) => id.startsWith(prefix));
}
/**
* Close all windows based on template
* @param templateId Template identifier
*/
closeWindowsByTemplate(templateId: string): void {
const windowIds = this.getWindowsByTemplate(templateId);
windowIds.forEach((id) => {
const browser = this.browsers.get(id);
if (browser) {
browser.close();
}
});
}
/**
@@ -144,7 +253,7 @@ export class BrowserManager {
* @param options Browser window options
*/
private retrieveOrInitialize(options: BrowserWindowOpts) {
let browser = this.browsers.get(options.identifier as AppBrowsersIdentifiers);
let browser = this.browsers.get(options.identifier);
if (browser) {
logger.debug(`Retrieved existing browser: ${options.identifier}`);
return browser;
@@ -153,7 +262,7 @@ export class BrowserManager {
logger.debug(`Creating new browser: ${options.identifier}`);
browser = new Browser(options, this.app);
const identifier = options.identifier as AppBrowsersIdentifiers;
const identifier = options.identifier;
this.browsers.set(identifier, browser);
// 记录 WebContents 和 identifier 的映射
@@ -165,33 +274,32 @@ export class BrowserManager {
});
browser.browserWindow.on('show', () => {
if (browser.webContents)
this.webContentsMap.set(browser.webContents, browser.identifier as AppBrowsersIdentifiers);
if (browser.webContents) this.webContentsMap.set(browser.webContents, browser.identifier);
});
return browser;
}
closeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
const browser = this.browsers.get(identifier);
browser?.close();
}
minimizeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
const browser = this.browsers.get(identifier);
browser?.browserWindow.minimize();
}
maximizeWindow(identifier: string) {
const browser = this.browsers.get(identifier as AppBrowsersIdentifiers);
if (browser.browserWindow.isMaximized()) {
const browser = this.browsers.get(identifier);
if (browser?.browserWindow.isMaximized()) {
browser?.browserWindow.unmaximize();
} else {
browser?.browserWindow.maximize();
}
}
getIdentifierByWebContents(webContents: WebContents): AppBrowsersIdentifiers | null {
getIdentifierByWebContents(webContents: WebContents): string | null {
return this.webContentsMap.get(webContents) || null;
}
@@ -9,6 +9,21 @@ import type { App } from '../App';
const logger = createLogger('core:StaticFileServerManager');
const getAllowedOrigin = (rawOrigin?: string) => {
if (!rawOrigin) return '*';
try {
const url = new URL(rawOrigin);
const normalizedOrigin = `${url.protocol}//${url.host}`;
return url.hostname === 'localhost' || url.hostname === '127.0.0.1' ? normalizedOrigin : '*';
} catch {
const normalizedOrigin = rawOrigin.replace(/\/$/, '');
return normalizedOrigin.includes('localhost') || normalizedOrigin.includes('127.0.0.1')
? normalizedOrigin
: '*';
}
};
export class StaticFileServerManager {
private app: App;
private fileService: FileService;
@@ -126,16 +141,38 @@ export class StaticFileServerManager {
return;
}
// 获取请求的 Origin 并设置 CORS
const origin = req.headers.origin || req.headers.referer;
const allowedOrigin = getAllowedOrigin(origin);
// 处理 CORS 预检请求
if (req.method === 'OPTIONS') {
res.writeHead(204, {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Origin': allowedOrigin,
'Access-Control-Max-Age': '86400',
});
res.end();
return;
}
const url = new URL(req.url, `http://127.0.0.1:${this.serverPort}`);
logger.debug(`Processing HTTP file request: ${req.url}`);
logger.debug(`Request method: ${req.method}`);
logger.debug(`Request headers: ${JSON.stringify(req.headers)}`);
// 提取文件路径:从 /desktop-file/path/to/file.png 中提取相对路径
let filePath = decodeURIComponent(url.pathname.slice(1)); // 移除开头的 /
logger.debug(`Initial file path after decode: ${filePath}`);
// 如果路径以 desktop-file/ 开头,则移除该前缀
const prefixWithoutSlash = LOCAL_STORAGE_URL_PREFIX.slice(1) + '/'; // 移除开头的 / 并添加结尾的 /
logger.debug(`Prefix to remove: ${prefixWithoutSlash}`);
if (filePath.startsWith(prefixWithoutSlash)) {
filePath = filePath.slice(prefixWithoutSlash.length);
logger.debug(`File path after removing prefix: ${filePath}`);
}
if (!filePath) {
@@ -148,7 +185,12 @@ export class StaticFileServerManager {
}
// 使用 FileService 获取文件
const fileResult = await this.fileService.getFile(`desktop://${filePath}`);
const desktopPath = `desktop://${filePath}`;
logger.debug(`Attempting to get file: ${desktopPath}`);
const fileResult = await this.fileService.getFile(desktopPath);
logger.debug(
`File retrieved successfully, mime type: ${fileResult.mimeType}, size: ${fileResult.content.byteLength} bytes`,
);
// 再次检查响应状态
if (res.destroyed || res.headersSent) {
@@ -158,11 +200,8 @@ export class StaticFileServerManager {
// 设置响应头
res.writeHead(200, {
// 缓存一年
'Access-Control-Allow-Origin': 'http://localhost:*',
'Access-Control-Allow-Origin': allowedOrigin,
'Cache-Control': 'public, max-age=31536000',
// 允许 localhost 的任意端口
'Content-Length': Buffer.byteLength(fileResult.content),
'Content-Type': fileResult.mimeType,
});
@@ -173,16 +212,27 @@ export class StaticFileServerManager {
logger.debug(`HTTP file served successfully: desktop://${filePath}`);
} catch (error) {
logger.error(`Error serving HTTP file: ${error}`);
logger.error(`Error stack: ${error.stack}`);
// 检查响应是否仍然可写
if (!res.destroyed && !res.headersSent) {
try {
// 获取请求的 Origin 并设置 CORS(错误响应也需要!)
const origin = req.headers.origin || req.headers.referer;
const allowedOrigin = getAllowedOrigin(origin);
// 判断是否是文件未找到错误
if (error.name === 'FileNotFoundError') {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.writeHead(404, {
'Access-Control-Allow-Origin': allowedOrigin,
'Content-Type': 'text/plain',
});
res.end('File Not Found');
} else {
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.writeHead(500, {
'Access-Control-Allow-Origin': allowedOrigin,
'Content-Type': 'text/plain',
});
res.end('Internal Server Error');
}
} catch (writeError) {
+617
View File
@@ -1,4 +1,621 @@
[
{
"children": {
"fixes": ["Correct claude_args quoting to prevent shell-quote parsing errors."]
},
"date": "2026-01-25",
"version": "1.143.3"
},
{
"children": {
"fixes": ["Fix React CVE."]
},
"date": "2025-12-04",
"version": "1.143.2"
},
{
"children": {
"fixes": ["Deepseek interleaved thinking."]
},
"date": "2025-12-02",
"version": "1.143.1"
},
{
"children": {
"features": ["Support DeepSeek Interleaved thinking."]
},
"date": "2025-12-01",
"version": "1.143.0"
},
{
"children": {
"fixes": ["OIDC error when connecting to self-host instance."]
},
"date": "2025-11-02",
"version": "1.142.9"
},
{
"children": {},
"date": "2025-10-30",
"version": "1.142.8"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-28",
"version": "1.142.7"
},
{
"children": {},
"date": "2025-10-28",
"version": "1.142.6"
},
{
"children": {
"improvements": ["Add MiniMax-M2 model."]
},
"date": "2025-10-27",
"version": "1.142.5"
},
{
"children": {
"improvements": [
"Pre render ModelSwitchPanel, The error details of the connectivity check lead to a layout problem."
]
},
"date": "2025-10-27",
"version": "1.142.4"
},
{
"children": {
"improvements": [
"Adjust modal setting form styles for improved layout and responsiveness, Unzip file when uploading in knowledge base [LOB-500]."
]
},
"date": "2025-10-27",
"version": "1.142.3"
},
{
"children": {
"improvements": ["Improve provider modal height when creating custom provider."]
},
"date": "2025-10-26",
"version": "1.142.2"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-26",
"version": "1.142.1"
},
{
"children": {
"features": ["Use env to control clerk allow origin feature."]
},
"date": "2025-10-24",
"version": "1.142.0"
},
{
"children": {
"fixes": ["Loadmore not work & navbar not show in pwa."]
},
"date": "2025-10-23",
"version": "1.141.10"
},
{
"children": {
"improvements": ["Improve local system tools render."]
},
"date": "2025-10-23",
"version": "1.141.9"
},
{
"children": {
"improvements": ["Improvement for Agent Team After Alpha Launch [LOB-517]."]
},
"date": "2025-10-23",
"version": "1.141.8"
},
{
"children": {
"improvements": ["Allow removal of top_p and similar request parameters."]
},
"date": "2025-10-23",
"version": "1.141.7"
},
{
"children": {},
"date": "2025-10-22",
"version": "1.141.6"
},
{
"children": {
"improvements": ["Change discover page from RSC to SPA to improve performance."]
},
"date": "2025-10-22",
"version": "1.141.5"
},
{
"children": {
"improvements": ["Fix model runtime cost calculate with CNY."]
},
"date": "2025-10-22",
"version": "1.141.4"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-22",
"version": "1.141.3"
},
{
"children": {},
"date": "2025-10-21",
"version": "1.141.2"
},
{
"children": {
"improvements": ["Refactor context engine."]
},
"date": "2025-10-21",
"version": "1.141.1"
},
{
"children": {
"features": ["Add PDF export functionality to share modal."],
"fixes": [
"Ignore abort signal errors in TRPC client, slove when pwa user info have code cannot be viewed in full."
],
"improvements": [
"Add knowledge base mansory layout [LOB-496], improve rich text link display."
]
},
"date": "2025-10-21",
"version": "1.141.0"
},
{
"children": {
"features": ["Add ComfyUI integration Phase1(RFC-128)."]
},
"date": "2025-10-21",
"version": "1.140.0"
},
{
"children": {},
"date": "2025-10-21",
"version": "1.139.5"
},
{
"children": {
"fixes": ["Pass threadId to messages in sendMessageInServer."]
},
"date": "2025-10-21",
"version": "1.139.4"
},
{
"children": {
"improvements": ["Show message author in minimap."]
},
"date": "2025-10-21",
"version": "1.139.3"
},
{
"children": {
"improvements": ["Solve when desktop the sider agent list too long."]
},
"date": "2025-10-20",
"version": "1.139.2"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-20",
"version": "1.139.1"
},
{
"children": {
"features": ["Support image generation for siliconcloud."]
},
"date": "2025-10-19",
"version": "1.139.0"
},
{
"children": {
"improvements": ["Refactor upload router into lambda and decide to remove it in V2."]
},
"date": "2025-10-18",
"version": "1.138.5"
},
{
"children": {
"fixes": ["Fix response API tools calling issue."]
},
"date": "2025-10-18",
"version": "1.138.4"
},
{
"children": {
"fixes": ["Fix topic fetch not correct in custom agent."]
},
"date": "2025-10-18",
"version": "1.138.3"
},
{
"children": {
"improvements": ["Improve welcome message."]
},
"date": "2025-10-16",
"version": "1.138.2"
},
{
"children": {
"fixes": ["Automatic topic creation switch does not work."]
},
"date": "2025-10-16",
"version": "1.138.1"
},
{
"children": {
"features": ["Support Group Chat, Mention, and Multi-Agent Orchestration with feature flag."]
},
"date": "2025-10-16",
"version": "1.138.0"
},
{
"children": {
"improvements": ["Add Claude Haiku 4.5 model."]
},
"date": "2025-10-16",
"version": "1.137.10"
},
{
"children": {
"improvements": ["Improve update notification."]
},
"date": "2025-10-15",
"version": "1.137.9"
},
{
"children": {
"fixes": ["Fix duplicate tools id issue and fix link dialog issue."],
"improvements": ["Add region support for Vertex AI provider."]
},
"date": "2025-10-15",
"version": "1.137.8"
},
{
"children": {
"improvements": ["Use different favicon.ico in dev mode."]
},
"date": "2025-10-15",
"version": "1.137.7"
},
{
"children": {
"fixes": ["Update Claude workflows to use oauth token, vertext ai create image."]
},
"date": "2025-10-14",
"version": "1.137.6"
},
{
"children": {
"improvements": ["Add imagen model to vertex ai."]
},
"date": "2025-10-14",
"version": "1.137.5"
},
{
"children": {
"fixes": ["Prevent Vertex AI JSON credentials from being split by comma."]
},
"date": "2025-10-14",
"version": "1.137.4"
},
{
"children": {
"fixes": [
"Fix mcp server connect issue and refactor web search implement, fix tools calling long name length >64 issue."
]
},
"date": "2025-10-14",
"version": "1.137.3"
},
{
"children": {
"fixes": ["Fix the Worker URL cross-origin issue."]
},
"date": "2025-10-14",
"version": "1.137.2"
},
{
"children": {
"improvements": ["Change the user chatItem maxWidth should use flex 1."]
},
"date": "2025-10-14",
"version": "1.137.1"
},
{
"children": {
"features": ["Add new setting for default image num."]
},
"date": "2025-10-12",
"version": "1.137.0"
},
{
"children": {
"fixes": ["Fix input cannot send markdown."],
"improvements": ["Optimize OpenRouter modelFetch endpoint, update i18n."]
},
"date": "2025-10-12",
"version": "1.136.13"
},
{
"children": {
"improvements": ["Add more AWS regions, Update infini-ai models."]
},
"date": "2025-10-11",
"version": "1.136.12"
},
{
"children": {
"improvements": [
"Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities."
]
},
"date": "2025-10-11",
"version": "1.136.11"
},
{
"children": {
"improvements": ["Improve search experience."]
},
"date": "2025-10-11",
"version": "1.136.10"
},
{
"children": {
"improvements": ["Add lab to support disable/enable rich text."]
},
"date": "2025-10-11",
"version": "1.136.9"
},
{
"children": {},
"date": "2025-10-11",
"version": "1.136.8"
},
{
"children": {
"fixes": ["Disable rich text in markdown editor."]
},
"date": "2025-10-11",
"version": "1.136.7"
},
{
"children": {},
"date": "2025-10-11",
"version": "1.136.6"
},
{
"children": {},
"date": "2025-10-11",
"version": "1.136.5"
},
{
"children": {
"fixes": ["Add 'gemini-2.5-flash-image' to disabled models Thinking."]
},
"date": "2025-10-10",
"version": "1.136.4"
},
{
"children": {
"improvements": ["Add delete & regenerate hotkeys."]
},
"date": "2025-10-10",
"version": "1.136.3"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-10",
"version": "1.136.2"
},
{
"children": {},
"date": "2025-10-09",
"version": "1.136.1"
},
{
"children": {
"features": ["Add new provider Cerebras."],
"fixes": ["Fix standalone plugin rerender issue."]
},
"date": "2025-10-09",
"version": "1.136.0"
},
{
"children": {},
"date": "2025-10-08",
"version": "1.135.6"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-08",
"version": "1.135.5"
},
{
"children": {
"improvements": ["Add GPT-5 pro model."]
},
"date": "2025-10-07",
"version": "1.135.4"
},
{
"children": {
"improvements": ["Improve Korean translate."]
},
"date": "2025-10-07",
"version": "1.135.3"
},
{
"children": {},
"date": "2025-10-06",
"version": "1.135.2"
},
{
"children": {
"improvements": ["Improve styles and fix tools calling condition."]
},
"date": "2025-10-06",
"version": "1.135.1"
},
{
"children": {
"features": ["Huanyuan text-to-image 3."]
},
"date": "2025-10-06",
"version": "1.135.0"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-06",
"version": "1.134.7"
},
{
"children": {},
"date": "2025-10-05",
"version": "1.134.6"
},
{
"children": {},
"date": "2025-10-05",
"version": "1.134.5"
},
{
"children": {
"improvements": ["Add promptfoo to improve prompts quality."]
},
"date": "2025-10-05",
"version": "1.134.4"
},
{
"children": {
"fixes": ["Type not preserved when model is sorted."]
},
"date": "2025-10-05",
"version": "1.134.3"
},
{
"children": {
"improvements": ["Allow switching model type."]
},
"date": "2025-10-05",
"version": "1.134.2"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-10-05",
"version": "1.134.1"
},
{
"children": {
"features": ["Support double-click to open multi agent window on the desktop."]
},
"date": "2025-10-04",
"version": "1.134.0"
},
{
"children": {
"fixes": ["type not preserved when model is disabled or sorted."],
"improvements": ["Nano banana support aspect_ratio."]
},
"date": "2025-10-04",
"version": "1.133.6"
},
{
"children": {
"fixes": ["Custom provider fails when client requests are enabled."],
"improvements": ["Optimized extendParams UI, update i18n."]
},
"date": "2025-10-04",
"version": "1.133.5"
},
{
"children": {
"fixes": ["OllamaCloud error."],
"improvements": ["Fix chat minimap overflow."]
},
"date": "2025-10-01",
"version": "1.133.4"
},
{
"children": {
"improvements": ["Refactor a ssrf-safe-fetch module."],
"fixes": ["Fix frontend random API key config not work."]
},
"date": "2025-10-01",
"version": "1.133.3"
},
{
"children": {
"improvements": ["Add minimap to chat list for quick navigation."]
},
"date": "2025-09-30",
"version": "1.133.2"
},
{
"children": {
"improvements": ["Update i18n."]
},
"date": "2025-09-30",
"version": "1.133.1"
},
{
"children": {
"features": ["Add builtin Python plugin, add Claude Sonnet 4.5 model to AI chat models."]
},
"date": "2025-09-29",
"version": "1.133.0"
},
{
"children": {},
"date": "2025-09-29",
"version": "1.132.19"
},
{
"children": {
"fixes": ["Refactor tools-engine and fix search token count."],
"improvements": ["Update i18n."]
},
"date": "2025-09-28",
"version": "1.132.18"
},
{
"children": {
"fixes": ["Fix input empty group name."]
},
"date": "2025-09-27",
"version": "1.132.17"
},
{
"children": {
"fixes": ["Resolve qwen-image-edit imageUrls conversion issue."]
},
"date": "2025-09-26",
"version": "1.132.16"
},
{
"children": {
"fixes": ["Add proxyUrl configuration for NEW API provider."]
+1 -1
View File
@@ -230,7 +230,7 @@ services:
otel-tracing-test:
profiles:
- otel-test
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
image: ghcr.io/grafana/xk6-client-tracing:v0.0.9
container_name: lobe-otel-tracing-test
network_mode: 'service:network-service'
restart: always
@@ -151,7 +151,7 @@ services:
otel-tracing-test:
profiles:
- otel-test
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
image: ghcr.io/grafana/xk6-client-tracing:v0.0.9
container_name: lobe-otel-tracing-test
network_mode: 'service:network-service'
restart: always
@@ -149,7 +149,7 @@ services:
otel-tracing-test:
profiles:
- otel-test
image: ghcr.io/grafana/xk6-client-tracing:v0.0.7
image: ghcr.io/grafana/xk6-client-tracing:v0.0.9
container_name: lobe-otel-tracing-test
network_mode: 'service:network-service'
restart: always
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,998 @@
---
title: ComfyUI 扩展开发指南
description: 学习如何为 LobeChat ComfyUI 集成添加新模型、工作流和功能扩展
tags:
- ComfyUI
- 开发指南
- 模型扩展
- 工作流开发
---
# ComfyUI 扩展开发指南
本指南基于实际代码实现,帮助开发者扩展 LobeChat 的 ComfyUI 集成功能。
## 架构概览
LobeChat ComfyUI 集成采用四层服务架构,围绕 `LobeComfyUI` 主类构建:
```plaintext
packages/model-runtime/src/providers/comfyui/
├── index.ts # LobeComfyUI 主类入口
├── services/ # 四大核心服务
│ ├── comfyuiClient.ts # ComfyUIClientService - 客户端和认证
│ ├── modelResolver.ts # ModelResolverService - 模型解析
│ ├── workflowBuilder.ts # WorkflowBuilderService - 工作流构建
│ └── imageService.ts # ImageService - 图像生成
├── config/ # 配置系统
│ ├── modelRegistry.ts # 主模型注册表(222个模型)
│ ├── fluxModelRegistry.ts # 130个FLUX模型配置
│ ├── sdModelRegistry.ts # 92个SD系列模型配置
│ ├── systemComponents.ts # VAE/CLIP/T5/LoRA/ControlNet组件
│ └── workflowRegistry.ts # 工作流路由配置
├── workflows/ # 工作流实现
│ ├── flux-dev.ts # FLUX Dev 20步工作流
│ ├── flux-schnell.ts # FLUX Schnell 4步快速工作流
│ ├── flux-kontext.ts # FLUX Kontext 填充工作流
│ ├── sd35.ts # SD3.5 外部编码器工作流
│ ├── simple-sd.ts # 通用SD工作流
│ └── index.ts # 工作流导出
├── utils/ # 工具层
│ ├── staticModelLookup.ts # 模型查找函数
│ ├── workflowDetector.ts # 模型架构检测
│ ├── promptSplitter.ts # FLUX双提示词分割
│ ├── seedGenerator.ts # 随机种子生成
│ ├── cacheManager.ts # TTL缓存管理
│ └── workflowUtils.ts # 工作流工具函数
└── errors/ # 错误处理
├── base.ts # 基础错误类
├── modelResolverError.ts # 模型解析错误
├── workflowError.ts # 工作流错误
└── servicesError.ts # 服务错误
src/server/services/comfyui/ # 服务端实现
├── core/ # 核心服务器服务
│ ├── comfyUIAuthService.ts # 认证服务
│ ├── comfyUIClientService.ts # 客户端服务
│ ├── comfyUIConnectionService.ts # 连接服务
│ ├── errorHandlerService.ts # 错误处理服务
│ ├── imageService.ts # 图像生成服务
│ ├── modelResolverService.ts # 模型解析服务
│ └── workflowBuilderService.ts # 工作流构建服务
├── config/ # 服务器端配置
│ ├── constants.ts # 常量和默认值
│ ├── modelRegistry.ts # 模型注册表
│ ├── fluxModelRegistry.ts # FLUX模型
│ ├── sdModelRegistry.ts # SD模型
│ ├── systemComponents.ts # 系统组件
│ └── workflowRegistry.ts # 工作流注册表
├── workflows/ # 服务端工作流实现
│ ├── flux-dev.ts # FLUX Dev 工作流
│ ├── flux-schnell.ts # FLUX Schnell 工作流
│ ├── flux-kontext.ts # FLUX Kontext 工作流
│ ├── sd35.ts # SD3.5 工作流
│ └── simple-sd.ts # Simple SD 工作流
├── utils/ # 服务器工具
│ ├── cacheManager.ts # 缓存管理
│ ├── componentInfo.ts # 组件信息
│ ├── imageResizer.ts # 图像调整
│ ├── promptSplitter.ts # 提示词分割
│ ├── staticModelLookup.ts # 模型查找
│ ├── weightDType.ts # 权重数据类型工具
│ ├── workflowDetector.ts # 工作流检测
│ └── workflowUtils.ts # 工作流工具
└── errors/ # 服务器错误处理
├── base.ts # 基础错误类
├── configError.ts # 配置错误
├── modelResolverError.ts # 模型解析器错误
├── servicesError.ts # 服务错误
├── utilsError.ts # 工具错误
└── workflowError.ts # 工作流错误
packages/model-runtime/src/utils/ # 共享工具
└── comfyuiErrorParser.ts # 客户端/服务器统一错误解析器
```
### 核心服务架构
`LobeComfyUI` 主类初始化四个核心服务:
```typescript
// packages/model-runtime/src/providers/comfyui/index.ts
export class LobeComfyUI implements LobeRuntimeAI, AuthenticatedImageRuntime {
constructor(options: ComfyUIKeyVault = {}) {
// 1. 客户端服务 - 处理认证和API调用
this.clientService = new ComfyUIClientService(options);
// 2. 模型解析服务 - 模型查找和组件选择
const modelResolverService = new ModelResolverService(this.clientService);
// 3. 工作流构建服务 - 路由和构建工作流
const workflowBuilderService = new WorkflowBuilderService({
clientService: this.clientService,
modelResolverService: modelResolverService,
});
// 4. 图像服务 - 统一的图像生成入口
this.imageService = new ImageService(
this.clientService,
modelResolverService,
workflowBuilderService,
);
}
}
```
## 认证系统
ComfyUI 集成支持四种认证方式,由 `ComfyUIClientService` 内的 `AuthManager` 处理:
### 支持的认证类型
```typescript
interface ComfyUIKeyVault {
baseURL: string;
authType?: 'none' | 'basic' | 'bearer' | 'custom';
// Basic Auth
username?: string;
password?: string;
// Bearer Token
apiKey?: string;
// Custom Headers
customHeaders?: Record<string, string>;
}
```
### 认证配置示例
```typescript
// 无认证
const comfyUI = new LobeComfyUI({
baseURL: 'http://localhost:8000',
authType: 'none'
});
// 基础认证
const comfyUI = new LobeComfyUI({
baseURL: 'https://your-comfyui-server.com',
authType: 'basic',
username: 'your-username',
password: 'your-password'
});
// Bearer Token
const comfyUI = new LobeComfyUI({
baseURL: 'https://your-comfyui-server.com',
authType: 'bearer',
apiKey: 'your-api-key'
});
// 自定义头部
const comfyUI = new LobeComfyUI({
baseURL: 'https://your-comfyui-server.com',
authType: 'custom',
customHeaders: {
'X-API-Key': 'your-custom-key',
'Authorization': 'Custom your-token'
}
});
```
## WebAPI 路由
ComfyUI 提供了用于图像生成的 REST WebAPI 路由,支持常规认证和内部服务认证:
### 路由详情
```typescript
// src/app/(backend)/webapi/create-image/comfyui/route.ts
export const runtime = 'nodejs';
export const maxDuration = 300; // 最长5分钟
// POST /api/create-image/comfyui
{
model: string; // 模型标识符
params: { // 生成参数
prompt: string;
width?: number;
height?: number;
// ... 其他参数
};
options?: { // 可选生成选项
// ... 额外选项
};
}
```
### 认证中间件
WebAPI 路由使用 `checkAuth` 中间件进行认证:
```typescript
import { checkAuth } from '@/app/(backend)/middleware/auth';
// 路由自动验证 JWT 令牌
// 并将认证上下文传递给 tRPC 调用器
```
### 错误处理
WebAPI 路由提供结构化的错误响应:
```typescript
// 从 TRPCError 的 cause 中提取 AgentRuntimeError
if (agentError && 'errorType' in agentError) {
// 将 errorType 转换为适当的 HTTP 状态码
// 401 对应 InvalidProviderAPIKey
// 403 对应 PermissionDenied
// 404 对应 NotFound
// 500+ 对应服务器错误
}
```
## 添加新模型
### 1. 理解模型注册表结构
模型配置存储在配置文件中:
```typescript
// packages/model-runtime/src/providers/comfyui/config/modelRegistry.ts
export interface ModelConfig {
modelFamily: 'FLUX' | 'SD1' | 'SDXL' | 'SD3';
priority: number; // 1=官方, 2=企业, 3=社区
recommendedDtype?: 'default' | 'fp8_e4m3fn' | 'fp8_e5m2';
variant: string; // 模型变体标识符
}
```
### 2. 添加 FLUX 模型
在 `fluxModelRegistry.ts` 中添加新模型:
```typescript
// packages/model-runtime/src/providers/comfyui/config/fluxModelRegistry.ts
export const FLUX_MODEL_REGISTRY: Record<string, ModelConfig> = {
// 现有模型...
// 添加新的FLUX Dev模型
'your-custom-flux-dev.safetensors': {
modelFamily: 'FLUX',
priority: 2, // 企业级模型
variant: 'dev',
recommendedDtype: 'default',
},
// 添加量化版本
'your-custom-flux-dev-fp8.safetensors': {
modelFamily: 'FLUX',
priority: 2,
variant: 'dev',
recommendedDtype: 'fp8_e4m3fn',
},
};
```
### 3. 添加 SD 系列模型
在 `sdModelRegistry.ts` 中添加:
```typescript
// packages/model-runtime/src/providers/comfyui/config/sdModelRegistry.ts
export const SD_MODEL_REGISTRY: Record<string, ModelConfig> = {
// 现有模型...
// 添加新的SD3.5模型
'your-custom-sd35.safetensors': {
modelFamily: 'SD3',
priority: 2,
variant: 'sd35',
recommendedDtype: 'default',
},
};
```
### 4. 更新模型 ID 映射(可选)
如果需要为前端提供友好的模型 ID,在 `modelRegistry.ts` 中添加映射:
```typescript
// packages/model-runtime/src/providers/comfyui/config/modelRegistry.ts
export const MODEL_ID_VARIANT_MAP: Record<string, string> = {
// 现有映射...
// 添加新模型的友好ID
'my-custom-flux': 'dev', // 映射到dev变体
'my-custom-sd35': 'sd35', // 映射到sd35变体
};
```
## 创建新工作流
### 工作流创建原理
**重要:工作流节点结构来自 ComfyUI 原生导出**
1. 在 ComfyUI 界面中设计工作流
2. 使用 "Export (API Format)" 导出 JSON
3. 将 JSON 结构复制到 TypeScript 文件
4. 使用`PromptBuilder`包装并参数化
### 1. 从 ComfyUI 导出工作流
在 ComfyUI 界面中:
1. 拖拽节点构建所需工作流
2. 连接各节点的输入输出
3. 右键点击空白处 → "Export (API Format)"
4. 复制生成的 JSON 结构
### 2. 工作流文件模板
创建新文件 `workflows/your-workflow.ts`
```typescript
import { PromptBuilder } from '@saintno/comfyui-sdk';
import type { WorkflowContext } from '../services/workflowBuilder';
import { generateUniqueSeeds } from '../utils/seedGenerator';
import { getWorkflowFilenamePrefix } from '../utils/workflowUtils';
/**
* 构建自定义工作流
* @param modelFileName - 模型文件名
* @param params - 生成参数
* @param context - 工作流上下文
*/
export async function buildYourCustomWorkflow(
modelFileName: string,
params: Record<string, any>,
context: WorkflowContext,
): Promise<PromptBuilder<any, any, any>> {
// 从ComfyUI "Export (API Format)" 获得的JSON结构
const workflow = {
'1': {
_meta: { title: 'Load Checkpoint' },
class_type: 'CheckpointLoaderSimple',
inputs: {
ckpt_name: modelFileName,
},
},
'2': {
_meta: { title: 'CLIP Text Encode' },
class_type: 'CLIPTextEncode',
inputs: {
clip: ['1', 1], // 连接到节点1的CLIP输出
text: params.prompt,
},
},
'3': {
_meta: { title: 'Empty Latent' },
class_type: 'EmptyLatentImage',
inputs: {
width: params.width,
height: params.height,
batch_size: 1,
},
},
'4': {
_meta: { title: 'KSampler' },
class_type: 'KSampler',
inputs: {
model: ['1', 0], // 连接到节点1的MODEL输出
positive: ['2', 0], // 连接到节点2的CONDITIONING输出
negative: ['2', 0], // 可以配置负面提示词
latent_image: ['3', 0],
seed: params.seed ?? generateUniqueSeeds(1)[0],
steps: params.steps,
cfg: params.cfg,
sampler_name: 'euler',
scheduler: 'normal',
denoise: 1.0,
},
},
'5': {
_meta: { title: 'VAE Decode' },
class_type: 'VAEDecode',
inputs: {
samples: ['4', 0],
vae: ['1', 2], // 连接到节点1的VAE输出
},
},
'6': {
_meta: { title: 'Save Image' },
class_type: 'SaveImage',
inputs: {
filename_prefix: getWorkflowFilenamePrefix('buildYourCustomWorkflow', context.variant),
images: ['5', 0],
},
},
};
// 使用PromptBuilder包装静态JSON
const builder = new PromptBuilder(
workflow,
['width', 'height', 'steps', 'cfg', 'seed'], // 输入参数
['images'], // 输出参数
);
// 设置输出节点
builder.setOutputNode('images', '6');
// 设置输入节点路径
builder.setInputNode('width', '3.inputs.width');
builder.setInputNode('height', '3.inputs.height');
builder.setInputNode('steps', '4.inputs.steps');
builder.setInputNode('cfg', '4.inputs.cfg');
builder.setInputNode('seed', '4.inputs.seed');
// 设置参数值
builder
.input('width', params.width)
.input('height', params.height)
.input('steps', params.steps)
.input('cfg', params.cfg)
.input('seed', params.seed ?? generateUniqueSeeds(1)[0]);
return builder;
}
```
### 3. 注册新工作流
在 `workflowRegistry.ts` 中添加工作流映射:
```typescript
// packages/model-runtime/src/providers/comfyui/config/workflowRegistry.ts
import { buildYourCustomWorkflow } from '../workflows/your-workflow';
export const VARIANT_WORKFLOW_MAP: Record<string, WorkflowBuilder> = {
// 现有映射...
// 添加新工作流
'your-variant': buildYourCustomWorkflow,
};
```
### 4. 实际工作流示例
参考 `flux-dev.ts` 的真实实现:
```typescript
// packages/model-runtime/src/providers/comfyui/workflows/flux-dev.ts (简化版)
export async function buildFluxDevWorkflow(
modelFileName: string,
params: Record<string, any>,
context: WorkflowContext,
): Promise<PromptBuilder<any, any, any>> {
// 获取所需组件
const selectedT5Model = await context.modelResolverService.getOptimalComponent('t5', 'FLUX');
const selectedVAE = await context.modelResolverService.getOptimalComponent('vae', 'FLUX');
const selectedCLIP = await context.modelResolverService.getOptimalComponent('clip', 'FLUX');
// 处理双提示词分割
const { t5xxlPrompt, clipLPrompt } = splitPromptForDualCLIP(params.prompt);
// 静态工作流定义(来自ComfyUI导出)
const workflow = {
'1': {
class_type: 'DualCLIPLoader',
inputs: {
clip_name1: selectedT5Model,
clip_name2: selectedCLIP,
type: 'flux',
},
},
// ... 更多节点
};
// 参数注入(必须在workflow文件内完成)
workflow['5'].inputs.clip_l = clipLPrompt;
workflow['5'].inputs.t5xxl = t5xxlPrompt;
workflow['4'].inputs.width = params.width;
workflow['4'].inputs.height = params.height;
// 创建并配置PromptBuilder
const builder = new PromptBuilder(workflow, inputs, outputs);
// 配置输入输出映射...
return builder;
}
```
## 系统组件管理
### 组件配置结构
所有系统组件(VAE、CLIP、T5、LoRA、ControlNet)统一配置在 `systemComponents.ts`
```typescript
// packages/model-runtime/src/providers/comfyui/config/systemComponents.ts
export interface ComponentConfig {
modelFamily: string; // 模型家族
priority: number; // 1=必需, 2=标准, 3=可选
type: string; // 组件类型
compatibleVariants?: string[]; // 兼容变体(LoRA/ControlNet
controlnetType?: string; // ControlNet类型
}
export const SYSTEM_COMPONENTS: Record<string, ComponentConfig> = {
// VAE组件
'ae.safetensors': {
modelFamily: 'FLUX',
priority: 1,
type: 'vae',
},
// CLIP组件
'clip_l.safetensors': {
modelFamily: 'FLUX',
priority: 1,
type: 'clip',
},
// T5编码器
't5xxl_fp16.safetensors': {
modelFamily: 'FLUX',
priority: 1,
type: 't5',
},
// LoRA适配器
'realism_lora.safetensors': {
compatibleVariants: ['dev'],
modelFamily: 'FLUX',
priority: 1,
type: 'lora',
},
// ControlNet模型
'flux-controlnet-canny-v3.safetensors': {
compatibleVariants: ['dev'],
controlnetType: 'canny',
modelFamily: 'FLUX',
priority: 1,
type: 'controlnet',
},
};
```
### 添加新组件
```typescript
// 添加新的LoRA
'your-custom-lora.safetensors': {
compatibleVariants: ['dev', 'schnell'],
modelFamily: 'FLUX',
priority: 2,
type: 'lora',
},
// 添加新的ControlNet
'your-controlnet-pose.safetensors': {
compatibleVariants: ['dev'],
controlnetType: 'pose',
modelFamily: 'FLUX',
priority: 2,
type: 'controlnet',
},
```
### 组件查询 API
```typescript
import { getAllComponentsWithNames, getOptimalComponent } from '../config/systemComponents';
// 获取最优组件
const bestVAE = getOptimalComponent('vae', 'FLUX');
const bestT5 = getOptimalComponent('t5', 'FLUX');
// 查询特定类型的组件
const availableLoras = getAllComponentsWithNames({
type: 'lora',
modelFamily: 'FLUX',
compatibleVariant: 'dev'
});
// 查询ControlNet
const cannyControlNets = getAllComponentsWithNames({
type: 'controlnet',
controlnetType: 'canny',
modelFamily: 'FLUX'
});
```
## 模型解析和查找
### ModelResolverService 工作原理
```typescript
// packages/model-runtime/src/providers/comfyui/services/modelResolver.ts
export class ModelResolverService {
async resolveModelFileName(modelId: string): Promise<string | undefined> {
// 1. 清理模型ID
const cleanId = modelId.replace(/^comfyui\//, '');
// 2. 检查模型ID映射
const mappedVariant = MODEL_ID_VARIANT_MAP[cleanId];
if (mappedVariant) {
const prioritizedModels = getModelsByVariant(mappedVariant);
const serverModels = await this.getAvailableModelFiles();
// 按优先级查找第一个可用模型
for (const filename of prioritizedModels) {
if (serverModels.includes(filename)) {
return filename;
}
}
}
// 3. 直接注册表查找
if (MODEL_REGISTRY[cleanId]) {
return cleanId;
}
// 4. 检查服务器文件存在性
if (isModelFile(cleanId)) {
const serverModels = await this.getAvailableModelFiles();
if (serverModels.includes(cleanId)) {
return cleanId;
}
}
return undefined;
}
}
```
### 模型查找示例
```typescript
// 实际使用示例
const resolver = new ModelResolverService(clientService);
// 友好ID查找
const fluxDevFile = await resolver.resolveModelFileName('flux-dev');
// 返回: 'flux1-dev.safetensors' (如果存在)
// 直接文件名查找
const directFile = await resolver.resolveModelFileName('my-custom-model.safetensors');
// 返回: 'my-custom-model.safetensors' (如果存在)
// 变体查找
const devModels = getModelsByVariant('dev');
console.log(devModels.slice(0, 3));
// 输出: ['flux1-dev.safetensors', 'flux1-dev-fp8.safetensors', ...]
```
## 错误处理
### 错误类型层次
```plaintext
// packages/model-runtime/src/providers/comfyui/errors/
ComfyUIInternalError // 基础错误
├── ModelResolverError // 模型解析错误
├── WorkflowError // 工作流错误
├── ServicesError // 服务错误
└── UtilsError // 工具错误
```
### 错误处理示例
```typescript
import { ModelResolverError, WorkflowError } from '../errors';
try {
const result = await comfyUI.createImage({
model: 'nonexistent-model',
params: { prompt: '测试' }
});
} catch (error) {
if (error instanceof ModelResolverError) {
console.log('模型解析失败:', error.message);
console.log('错误原因:', error.reason);
console.log('错误详情:', error.details);
} else if (error instanceof WorkflowError) {
console.log('工作流错误:', error.message);
}
}
```
### 统一错误解析器
客户端和服务器端错误处理可使用共享的错误解析器:
```typescript
// packages/model-runtime/src/utils/comfyuiErrorParser.ts
import { parseComfyUIErrorMessage, cleanComfyUIErrorMessage } from '../utils/comfyuiErrorParser';
// 解析错误消息并确定错误类型
const { error, errorType } = parseComfyUIErrorMessage(rawError);
// 清理 ComfyUI 格式的错误消息
const cleanMessage = cleanComfyUIErrorMessage(errorMessage);
```
错误解析器处理:
- HTTP 状态码映射到错误类型
- 服务器端错误增强
- 模型文件缺失检测
- 网络错误识别
- 工作流验证错误
## 测试架构与开发
### 测试架构概述
ComfyUI 集成使用了统一的测试架构,确保测试的可维护性和定制友好性。该架构包括:
- **统一 Mock 系统**:集中管理所有外部依赖的模拟
- **参数化测试**:自动适应新模型,无需修改现有测试
- **夹具系统**:从配置文件中获取测试数据,确保准确性
- **覆盖率目标**ComfyUI 模块维持 97%+ 覆盖率
### 测试文件结构
```plaintext
packages/model-runtime/src/providers/comfyui/__tests__/
├── setup/
│ └── unifiedMocks.ts # 统一Mock配置
├── fixtures/
│ ├── parameters.fixture.ts # 参数测试夹具
│ └── workflow.fixture.ts # 工作流测试夹具
├── integration/
│ ├── parameterMapping.test.ts # 参数映射集成测试
│ └── workflowBuilder.test.ts # 工作流构建测试
├── services/ # 各服务单元测试
└── workflows/ # 工作流单元测试
```
### 添加新模型测试
当添加新模型时,测试会自动识别并运行相应的参数映射测试。你只需要:
#### 1. 在模型配置中添加参数架构
```typescript
// packages/model-bank/src/aiModels/comfyui.ts
export const myNewModelParamsSchema = {
prompt: { type: 'string', required: true },
steps: { type: 'number', default: 20, min: 1, max: 150 },
cfg: { type: 'number', default: 7.0, min: 1.0, max: 30.0 }
};
```
#### 2. 创建工作流构建器
```typescript
// packages/model-runtime/src/providers/comfyui/workflows/myNewModel.ts
export async function buildMyNewModelWorkflow(
modelName: string,
params: MyNewModelParams,
context: ComfyUIContext
) {
const workflow = { /* 工作流定义 */ };
// 参数注入
workflow['1'].inputs.prompt = params.prompt;
workflow['2'].inputs.steps = params.steps;
return workflow;
}
```
#### 3. 在夹具中注册模型
```typescript
// packages/model-runtime/src/providers/comfyui/__tests__/fixtures/parameters.fixture.ts
import {
myNewModelParamsSchema,
// ... 其他架构
} from '../../../../../model-bank/src/aiModels/comfyui';
export const parametersFixture = {
models: {
'my-new-model': {
schema: myNewModelParamsSchema,
defaults: {
steps: myNewModelParamsSchema.steps.default,
cfg: myNewModelParamsSchema.cfg.default,
},
boundaries: {
min: { steps: myNewModelParamsSchema.steps.min },
max: { steps: myNewModelParamsSchema.steps.max }
}
}
}
};
```
### 测试最佳实践
#### 使用统一 Mock 系统
```typescript
import { setupAllMocks } from '../setup/unifiedMocks';
describe('MyTest', () => {
const mocks = setupAllMocks();
beforeEach(() => {
vi.clearAllMocks();
});
});
```
#### 编写参数映射测试
参数映射测试会自动运行,验证前端参数正确注入到工作流中:
```typescript
// 测试会自动包含新注册的模型
describe.each(
Object.entries(models).filter(([name]) => workflowBuilders[name])
)(
'%s parameter mapping',
(modelName, modelConfig) => {
it('should map schema parameters to workflow', async () => {
const params = {
prompt: 'test prompt',
...modelConfig.defaults,
};
const workflow = await builder(`${modelName}.safetensors`, params, mockContext);
expect(workflow).toBeDefined();
});
}
);
```
#### 定制友好的测试原则
- **不测试工作流结构**:工作流是 ComfyUI 官方格式,只测试参数映射
- **使用配置驱动的数据**:测试数据来自模型配置文件,确保一致性
- **避免脆性断言**:不检查具体的节点 ID 或内部结构
- **支持扩展**:新增模型应该只影响覆盖率,不破坏现有测试
### 运行测试
```bash
# 运行 ComfyUI 相关测试
cd packages/model-runtime
bunx vitest run --silent='passed-only' 'src/comfyui'
# 查看覆盖率
bunx vitest run --coverage 'src/comfyui'
# 运行特定测试文件
bunx vitest run 'src/comfyui/__tests__/integration/parameterMapping.test.ts'
```
### 覆盖率目标
- **整体覆盖率**ComfyUI 模块维持 97%+ 覆盖率
- **核心功能**:100% 分支覆盖率
- **新增功能**:保持或提升现有覆盖率水平
## 开发和测试
### 1. 本地开发设置
```bash
# 启动ComfyUI调试模式
DEBUG=lobe-image:* pnpm dev
```
### 2. 测试新功能
```typescript
// 创建测试文件
import { buildYourCustomWorkflow } from './your-workflow';
describe('Custom Workflow', () => {
test('should build workflow correctly', async () => {
const mockContext = {
clientService: mockClientService,
modelResolverService: mockModelResolver,
};
const workflow = await buildYourCustomWorkflow(
'test-model.safetensors',
{ prompt: '测试', width: 512, height: 512 },
mockContext
);
expect(workflow).toBeDefined();
// 验证工作流结构...
});
});
```
### 3. 模型配置测试
```typescript
import { getModelConfig, getAllModelNames } from '../config/modelRegistry';
describe('Model Registry', () => {
test('should find new model', () => {
const config = getModelConfig('your-new-model.safetensors');
expect(config).toBeDefined();
expect(config?.variant).toBe('dev');
expect(config?.modelFamily).toBe('FLUX');
});
});
```
## 完整使用示例
### 基础图像生成
```typescript
import { LobeComfyUI } from '@/libs/model-runtime/comfyui';
const comfyUI = new LobeComfyUI({
baseURL: 'http://localhost:8000',
authType: 'none'
});
// FLUX Dev模型生成
const result = await comfyUI.createImage({
model: 'flux-dev',
params: {
prompt: '美丽的风景画,高质量,详细',
width: 1024,
height: 1024,
steps: 20,
cfg: 3.5,
seed: -1
}
});
console.log('生成图像URL:', result.imageUrl);
```
### SD3.5 模型使用
```typescript
// SD3.5会自动检测可用编码器
const sd35Result = await comfyUI.createImage({
model: 'stable-diffusion-35',
params: {
prompt: '未来主义城市景观',
width: 1344,
height: 768,
steps: 28,
cfg: 4.5
}
});
```
### 企业优化模型
```typescript
// 系统会自动选择最佳可用变体(如FP8量化版本)
const optimizedResult = await comfyUI.createImage({
model: 'flux-dev',
params: {
prompt: '专业商务肖像',
width: 768,
height: 1024,
steps: 15 // FP8模型可以用更少步数
}
});
```
## 注意事项
- 确保 ComfyUI 服务正常运行并可访问
- 检查所有必需的模型文件是否已正确安装
- 注意模型文件的命名规范和路径配置
- 定期检查和更新工作流配置以支持新功能
- 注意不同模型系列的参数差异和兼容性
- 添加新模型时,请遵循测试架构指南确保测试完整性
- 在提交代码前务必运行相关测试确保覆盖率达标
通过遵循这些指南,开发者可以有效地在 LobeChat 中使用和扩展 ComfyUI 功能,为用户提供强大的图像生成和处理能力。
@@ -5,7 +5,9 @@ LobeChat is built on the Next.js framework and uses TypeScript as the primary de
1. Routing: Define routes (`src/app`).
2. Data Structure: Define data structures (`src/types`).
3. Business Logic Implementation: Zustand store (`src/store`).
4. Page Display: Write static components/pages (`src/app/<new-page>/features/<new-feature>.tsx`).
4. Page Display: Write static components/pages. Create features in:
- `src/features/<feature-name>/` for **shared global features** (used across multiple pages)
- `src/app/<new-page>/features/<feature-name>/` for **page-specific features** (only used in this page)
5. Function Binding: Bind the store with page triggers (`const [state, function] = useNewStore(s => [s.state, s.function])`).
Taking the "Chat Messages" feature as an example, here are the brief steps to implement this feature:
@@ -60,7 +62,8 @@ export const useChatStore = create<ChatState>((set) => ({
In `src/app/<new-page>/features/<new-feature>.tsx`, we need to create a new page or component to display "Chat Messages". In this file, we can use the Zustand Store created earlier and Ant Design components to build the UI:
```jsx
// src/features/chat/index.tsx
// src/app/chat/features/ChatPage/index.tsx
// Note: Use src/app/<page>/features/ for page-specific components
import { List, Typography } from 'antd';
import { useChatStore } from 'src/store/chatStore';
@@ -82,6 +85,12 @@ const ChatPage = () => {
export default ChatPage;
```
> **Note on Feature Organization**: LobeChat uses two patterns for organizing features:
> - **Global features** (`src/features/`): Shared components like `ChatInput`, `Conversation` used across the app
> - **Page-specific features** (`src/app/<page>/features/`): Components used only within a specific page route
>
> Choose based on reusability. If unsure, start with page-specific and refactor to global if needed elsewhere.
## 5. Function Binding
In a page or component, we need to bind the Zustand Store's state and methods to the UI. In the example above, we have already bound the `messages` state to the `dataSource` property of the list. Now, we also need a method to add new messages. We can define this method in the Zustand Store and then use it in the page or component:
@@ -5,7 +5,9 @@ LobeChat 基于 Next.js 框架构建,使用 TypeScript 作为主要开发语
1. 路由:定义路由 (`src/app`)
2. 数据结构: 定义数据结构 ( `src/types` )
3. 业务功能实现: zustand store (`src/store`)
4. 页面展示:书写静态组件 / 页面 (`src/app/<new-page>/features/<new-feature>.tsx`)
4. 页面展示:书写静态组件 / 页面。根据以下方式创建功能组件:
- `src/features/<feature-name>/` 用于 **全局共享功能**(跨多个页面使用)
- `src/app/<new-page>/features/<feature-name>/` 用于 **页面专属功能**(仅在当前页面使用)
5. 功能绑定:绑定 store 与页面的触发 (`const [state,function]= useNewStore(s=>[s.state,s.function])`)
我们以 "会话消息" 功能为例,以下是实现这个功能的简要步骤:
@@ -42,7 +44,7 @@ export type ChatMessage = {
```ts
// src/store/chatStore.ts
import create from 'zustand';
import { create } from 'zustand';
type ChatState = {
messages: ChatMessage[];
@@ -60,7 +62,8 @@ export const useChatStore = create<ChatState>((set) => ({
在 `src/app/<new-page>/features/<new-feature>.tsx` 中,我们需要创建一个新的页面或组件来显示 "会话消息"。在这个文件中,我们可以使用上面创建的 Zustand Store,以及 Ant Design 的组件来构建 UI
```jsx
// src/features/chat/index.tsx
// src/app/chat/features/ChatPage/index.tsx
// 注意:使用 src/app/<page>/features/ 放置页面专属组件
import { List, Typography } from 'antd';
import { useChatStore } from 'src/store/chatStore';
@@ -82,6 +85,12 @@ const ChatPage = () => {
export default ChatPage;
```
> **关于功能组件组织方式的说明**:LobeChat 使用两种模式来组织功能组件:
> - **全局功能**`src/features/`):跨应用共享的组件,如 `ChatInput`、`Conversation` 等
> - **页面专属功能**`src/app/<page>/features/`):仅在特定页面路由中使用的组件
>
> 根据可复用性选择合适的方式。如果不确定,可以先放在页面专属位置,需要时再重构为全局共享。
## 5. 功能绑定
在页面或组件中,我们需要将 Zustand Store 的状态和方法绑定到 UI 上。在上面的示例中,我们已经将 `messages` 状态绑定到了列表的 `dataSource` 属性上。现在,我们还需要一个方法来添加新的消息。我们可以在 Zustand Store 中定义这个方法,然后在页面或组件中使用它:
+67 -16
View File
@@ -4,37 +4,88 @@ The directory structure of LobeChat is as follows:
```bash
src
├── app # Main logic and state management related code for the application
├── app # Next.js App Router implementation with route groups and API routes
├── components # Reusable UI components
├── config # Application configuration files, including client-side and server-side environment variables
├── const # Used to define constants, such as action types, route names, etc.
├── features # Function modules related to business functions, such as agent settings, plugin development pop-ups, etc.
├── hooks # Custom utility hooks reused throughout the application
├── layout # Application layout components, such as navigation bars, sidebars, etc.
├── libs # Third-party integrations (analytics, OIDC, etc.)
├── locales # Internationalization language files
├── server # Server-side modules and services
├── services # Encapsulated backend service interfaces, such as HTTP requests
├── store # Zustand store for state management
├── styles # Global styles and CSS-in-JS configurations
├── types # TypeScript type definition files
└── utils # Common utility functions
```
## app
In the `app` folder, we organize each route page according to the app router's [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to separately handle the implementation of desktop and mobile code. Taking the file structure of the `welcome` page as an example:
The `app` directory follows Next.js 13+ App Router conventions with a sophisticated architecture using [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to organize backend services, platform variants, and application routes:
```bash
welcome
├── (desktop) # Desktop implementation
│ ├── features # Desktop-specific features
│ ├── index.tsx # Main entry file for desktop
│ └── layout.desktop.tsx # Desktop layout component
├── (mobile) # Mobile implementation
│ ├── features # Mobile-specific features
│ ├── index.tsx # Main entry file for mobile
└── layout.mobile.tsx # Mobile layout component
├── features # This folder contains features code shared by both desktop and mobile, such as the Banner component
└── Banner
└── page.tsx # This is the main entry file for the page, used to load desktop or mobile code based on the device type
app
├── (backend)/ # Backend API routes and services
│ ├── api/ # REST API endpoints
│ ├── auth/ # Authentication routes
│ └── webhooks/ # Webhook handlers
│ ├── middleware/ # Request middleware
│ ├── oidc/ # OpenID Connect routes
│ ├── trpc/ # tRPC API endpoints
│ ├── async/ # Async tRPC routes
│ │ ├── desktop/ # Desktop-specific tRPC routes
│ ├── edge/ # Edge runtime tRPC routes
│ │ ├── lambda/ # Lambda tRPC routes
│ │ └── tools/ # Tools tRPC routes
│ └── webapi/ # Web API endpoints
│ ├── chat/ # Chat-related APIs
│ ├── models/ # Model management APIs
│ ├── tts/ # Text-to-speech APIs
│ └── ...
├── [variants]/ # Platform and device variants
│ ├── (auth)/ # Authentication pages
│ │ ├── login/
│ │ ├── signup/
│ │ └── next-auth/
│ ├── (main)/ # Main application routes
│ │ ├── (mobile)/ # Mobile-specific routes
│ │ │ └── me/ # Mobile profile pages
│ │ ├── _layout/ # Layout components
│ │ ├── chat/ # Chat interface
│ │ ├── discover/ # Discovery pages
│ │ ├── files/ # File management
│ │ ├── image/ # Image generation
│ │ ├── profile/ # User profile
│ │ ├── repos/ # Repository management
│ │ └── settings/ # Application settings
│ └── @modal/ # Parallel modal routes
│ ├── (.)changelog/
│ └── _layout/
├── desktop/ # Desktop-specific routes
│ └── devtools/
├── manifest.ts # PWA manifest
├── robots.tsx # Robots.txt generation
├── sitemap.tsx # Sitemap generation
└── sw.ts # Service worker
```
In this way, we can clearly distinguish and manage desktop and mobile code, while also easily reusing code required on both devices, thereby improving development efficiency and maintaining code cleanliness and maintainability.
### Architecture Explanation
**Route Groups:**
- `(backend)` - Contains all server-side API routes, middleware, and backend services
- `[variants]` - Dynamic route group handling different platform variants and main application pages
- `@modal` - Parallel routes for modal dialogs using Next.js parallel routing
**Platform Organization:**
- The architecture supports multiple platforms (web, desktop, mobile) through route organization
- Desktop-specific routes are in the `desktop/` directory
- Mobile-specific routes are organized under `(main)/(mobile)/`
- Shared layouts and components are in `_layout/` directories
**API Architecture:**
- REST APIs in `(backend)/api/` and `(backend)/webapi/`
- tRPC endpoints organized by runtime environment (edge, lambda, async, desktop)
- Authentication and OIDC handling in dedicated route groups
This architecture provides clear separation of concerns while maintaining flexibility for different deployment targets and runtime environments.
@@ -4,37 +4,88 @@ LobeChat 的文件夹目录架构如下:
```bash
src
├── app # 应用主要逻辑和状态管理相关的代码
├── app # Next.js App Router 实现,包含路由组和 API 路由
├── components # 可复用的 UI 组件
├── config # 应用的配置文件,包含客户端环境变量与服务端环境变量
├── const # 用于定义常量,如 action 类型、路由名等
├── features # 与业务功能相关的功能模块,如 Agent 设置、插件开发弹窗等
├── hooks # 全应用复用自定义的工具 Hooks
├── layout # 应用的布局组件,如导航栏、侧边栏等
├── libs # 第三方集成(分析、OIDC 等)
├── locales # 国际化的语言文件
├── server # 服务端模块和服务
├── services # 封装的后端服务接口,如 HTTP 请求
├── store # 用于状态管理的 zustand store
├── styles # 全局样式和 CSS-in-JS 配置
├── types # TypeScript 的类型定义文件
└── utils # 通用的工具函数
```
## app
`app` 文件夹中,我们将每个路由页面按照 app router 的 [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 进行组织,以此来分别处理桌面端和移动端的代码实现。以 `welcome` 页面的文件结构为例
`app` 目录遵循 Next.js 13+ App Router 约定,采用复杂的架构,使用 [路由组](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 来组织后端服务、平台变体和应用路由
```bash
welcome
├── (desktop) # 桌面端实现
│ ├── features # 桌面端特有的功能
│ ├── index.tsx # 桌面端的主入口文件
│ └── layout.desktop.tsx # 桌面端的布局组件
├── (mobile) # 移动端实现
│ ├── features # 移动端特有的功能
│ ├── index.tsx # 移动端的主入口文件
└── layout.mobile.tsx # 移动端的布局组件
├── features # 此文件夹包含双端共享的特性代码,如 Banner 组件
└── Banner
└── page.tsx # 此为页面的主入口文件,用于根据设备类型选择加载桌面端或移动端的代码
app
├── (backend)/ # 后端 API 路由和服务
│ ├── api/ # REST API 端点
│ ├── auth/ # 身份验证路由
│ └── webhooks/ # Webhook 处理器
│ ├── middleware/ # 请求中间件
│ ├── oidc/ # OpenID Connect 路由
│ ├── trpc/ # tRPC API 端点
│ ├── async/ # 异步 tRPC 路由
│ │ ├── desktop/ # 桌面端专用 tRPC 路由
│ ├── edge/ # Edge 运行时 tRPC 路由
│ │ ├── lambda/ # Lambda tRPC 路由
│ │ └── tools/ # 工具 tRPC 路由
│ └── webapi/ # Web API 端点
│ ├── chat/ # 聊天相关 API
│ ├── models/ # 模型管理 API
│ ├── tts/ # 文本转语音 API
│ └── ...
├── [variants]/ # 平台和设备变体
│ ├── (auth)/ # 身份验证页面
│ │ ├── login/
│ │ ├── signup/
│ │ └── next-auth/
│ ├── (main)/ # 主应用路由
│ │ ├── (mobile)/ # 移动端专用路由
│ │ │ └── me/ # 移动端个人资料页面
│ │ ├── _layout/ # 布局组件
│ │ ├── chat/ # 聊天界面
│ │ ├── discover/ # 发现页面
│ │ ├── files/ # 文件管理
│ │ ├── image/ # 图像生成
│ │ ├── profile/ # 用户资料
│ │ ├── repos/ # 仓库管理
│ │ └── settings/ # 应用设置
│ └── @modal/ # 并行模态框路由
│ ├── (.)changelog/
│ └── _layout/
├── desktop/ # 桌面端专用路由
│ └── devtools/
├── manifest.ts # PWA 清单
├── robots.tsx # Robots.txt 生成
├── sitemap.tsx # 站点地图生成
└── sw.ts # Service Worker
```
通过这种方式,我们可以清晰地区分和管理桌面端和移动端的代码,同时也能方便地复用在两种设备上都需要的代码,从而提高开发效率并保持代码的整洁和可维护性。
### 架构说明
**路由组:**
- `(backend)` - 包含所有服务端 API 路由、中间件和后端服务
- `[variants]` - 处理不同平台变体和主应用页面的动态路由组
- `@modal` - 使用 Next.js 并行路由的模态框对话框并行路由
**平台组织:**
- 架构通过路由组织支持多个平台(Web、桌面端、移动端)
- 桌面端专用路由位于 `desktop/` 目录中
- 移动端专用路由组织在 `(main)/(mobile)/` 下
- 共享布局和组件位于 `_layout/` 目录中
**API 架构:**
- `(backend)/api/` 和 `(backend)/webapi/` 中的 REST API
- 按运行时环境组织的 tRPC 端点(edge、lambda、async、desktop
- 专用路由组中的身份验证和 OIDC 处理
这种架构在保持不同部署目标和运行时环境灵活性的同时,提供了清晰的关注点分离。
@@ -11,13 +11,13 @@ But here is the easier approach that can reduce your pain.
### Environment Configuration
First, copy the example environment file to create your development configuration:
First, copy the example environment file to create your Docker Compose configuration:
```bash
cp .env.example.development .env.development
cp docker-compose/local/.env.example docker-compose/local/.env
```
This file contains all necessary environment variables for server-side database mode and configures:
Edit `docker-compose/local/.env` as needed for your development setup. This file contains all necessary environment variables for the Docker services and configures:
- **Service Mode**: `NEXT_PUBLIC_SERVICE_MODE=server`
- **Database**: PostgreSQL with connection string
@@ -72,7 +72,7 @@ When working with image generation features (text-to-image, image-to-image), the
### Image Generation Configuration
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `.env.example.development`. No additional setup is required.
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `docker-compose/local/.env.example`. No additional setup is required.
### Image Generation Architecture
@@ -84,7 +84,7 @@ The image generation feature requires:
### Storage Configuration
The `.env.example.development` file includes all necessary S3 environment variables:
The `docker-compose/local/.env.example` file includes all necessary S3 environment variables:
```bash
# S3 Storage Configuration (MinIO for local development)
@@ -11,13 +11,13 @@ LobeChat 提供了内置的客户端数据库体验。
### 环境配置
首先,复制示例环境文件来创建你的开发配置:
首先,复制示例环境文件来创建你的 Docker Compose 配置:
```bash
cp .env.example.development .env.development
cp docker-compose/local/.env.example docker-compose/local/.env
```
此文件包含服务端数据库模式所需的所有环境变量,配置了:
根据需要编辑 `docker-compose/local/.env` 文件以适应你的开发设置。此文件包含 Docker 服务所需的所有环境变量,配置了:
- **服务模式**: `NEXT_PUBLIC_SERVICE_MODE=server`
- **数据库**: 带连接字符串的 PostgreSQL
@@ -72,7 +72,7 @@ docker-compose -f docker-compose.development.yml ps
### 图像生成配置
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `.env.example.development` 中的所有必要环境变量。无需额外配置。
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `docker-compose/local/.env.example` 中的所有必要环境变量。无需额外配置。
### 图像生成架构
@@ -84,7 +84,7 @@ docker-compose -f docker-compose.development.yml ps
### 存储配置
`.env.example.development` 文件包含所有必要的 S3 环境变量:
`docker-compose/local/.env.example` 文件包含所有必要的 S3 环境变量:
```bash
# S3 存储配置(本地开发使用 MinIO)
+153
View File
@@ -16,6 +16,7 @@ table agents {
provider text
system_role text
tts jsonb
virtual boolean [default: false]
opening_message text
opening_questions text[] [default: `[]`]
accessed_at "timestamp with time zone" [not null, default: `now()`]
@@ -192,6 +193,7 @@ table documents {
file_id text
user_id text [not null]
client_id text
editor_data 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()`]
@@ -317,6 +319,24 @@ table message_chunks {
}
}
table message_groups {
id varchar(255) [pk, not null]
topic_id text
user_id text [not null]
parent_group_id varchar(255)
parent_message_id text
title varchar(255)
description text
client_id varchar(255)
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
(client_id, user_id) [name: 'message_groups_client_id_user_id_unique', unique]
}
}
table message_plugins {
id text [pk, not null]
tool_call_id text
@@ -410,6 +430,7 @@ table messages {
agent_id text
group_id text
target_id text
message_group_id varchar(255)
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
@@ -925,6 +946,7 @@ table user_settings {
system_agent jsonb
default_agent jsonb
tool jsonb
image jsonb
}
table users {
@@ -945,6 +967,129 @@ table users {
updated_at "timestamp with time zone" [not null, default: `now()`]
}
table user_memories {
id varchar(255) [pk, not null]
user_id text
memory_category varchar(255)
memory_layer varchar(255)
memory_type varchar(255)
metadata jsonb
tags text[]
title varchar(255)
summary text
summary_vector_1024 vector(1024)
details text
details_vector_1024 vector(1024)
status varchar(255)
accessed_count bigint [default: 0]
last_accessed_at "timestamp with time zone" [not null]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
summary_vector_1024 [name: 'user_memories_summary_vector_1024_index']
details_vector_1024 [name: 'user_memories_details_vector_1024_index']
}
}
table user_memories_contexts {
id varchar(255) [pk, not null]
user_id text
user_memory_ids jsonb
metadata jsonb
tags text[]
associated_objects jsonb
associated_subjects jsonb
title text
title_vector vector(1024)
description text
description_vector vector(1024)
type varchar(255)
current_status text
score_impact numeric [default: 0]
score_urgency numeric [default: 0]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
title_vector [name: 'user_memories_contexts_title_vector_index']
description_vector [name: 'user_memories_contexts_description_vector_index']
type [name: 'user_memories_contexts_type_index']
}
}
table user_memories_experiences {
id varchar(255) [pk, not null]
user_id text
user_memory_id varchar(255)
metadata jsonb
tags text[]
type varchar(255)
situation text
situation_vector vector(1024)
reasoning text
possible_outcome text
action text
action_vector vector(1024)
key_learning text
key_learning_vector vector(1024)
score_confidence real [default: 0]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
situation_vector [name: 'user_memories_experiences_situation_vector_index']
action_vector [name: 'user_memories_experiences_action_vector_index']
key_learning_vector [name: 'user_memories_experiences_key_learning_vector_index']
type [name: 'user_memories_experiences_type_index']
}
}
table user_memories_identities {
id varchar(255) [pk, not null]
user_id text
user_memory_id varchar(255)
metadata jsonb
tags text[]
type varchar(255)
description text
description_vector vector(1024)
episodic_date "timestamp with time zone"
relationship varchar(255)
role text
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
description_vector [name: 'user_memories_identities_description_vector_index']
type [name: 'user_memories_identities_type_index']
}
}
table user_memories_preferences {
id varchar(255) [pk, not null]
user_id text
user_memory_id varchar(255)
metadata jsonb
tags text[]
conclusion_directives text
conclusion_directives_vector vector(1024)
type varchar(255)
suggestions text
score_priority numeric [default: 0]
accessed_at "timestamp with time zone" [not null, default: `now()`]
created_at "timestamp with time zone" [not null, default: `now()`]
updated_at "timestamp with time zone" [not null, default: `now()`]
indexes {
conclusion_directives_vector [name: 'user_memories_preferences_conclusion_directives_vector_index']
}
}
ref: agents_files.file_id > files.id
ref: agents_files.agent_id > agents.id
@@ -995,6 +1140,12 @@ ref: generations.generation_batch_id > generation_batches.id
ref: generations.async_task_id - async_tasks.id
ref: message_groups.user_id - users.id
ref: message_groups.topic_id - topics.id
ref: message_groups.parent_group_id > message_groups.id
ref: messages_files.file_id > files.id
ref: messages_files.message_id > messages.id
@@ -1007,6 +1158,8 @@ ref: messages.topic_id - topics.id
ref: threads.source_message_id - messages.id
ref: messages.message_group_id > message_groups.id
ref: sessions.group_id - session_groups.id
ref: topic_documents.document_id > documents.id
@@ -0,0 +1,399 @@
# 集成测试指南
## 概述
集成测试验证多个模块协同工作的正确性,确保完整的调用链路(Router → Service → Model → Database)正常运行。
## 为什么需要集成测试?
即使单元测试覆盖率很高(80%+),仍可能出现集成问题:
### 常见问题示例
```typescript
// ❌ 问题:参数在调用链中丢失
// Router 层
const messageId = await messageModel.create({
content: 'test',
sessionId: 'xxx',
topicId: 'yyy', // ← 传入了 topicId
});
// Model 层(假设实现有问题)
async create(data) {
return this.db.insert(messages).values({
content: data.content,
sessionId: data.sessionId,
// ❌ 忘记传递 topicId
});
}
// 结果:单元测试通过(因为 mock 了 Model),但实际运行时 topicId 丢失
```
### 集成测试能发现的问题
1. **参数传递遗漏**: containerId、threadId、topicId 等在调用链中丢失
2. **数据库约束**: 外键关系、级联删除等在 mock 中无法验证
3. **事务完整性**: 跨表操作的原子性
4. **权限验证**: 跨用户访问控制
5. **真实场景**: 模拟用户的完整操作流程
## 运行集成测试
```bash
# 运行所有集成测试
pnpm test:integration
# 运行特定文件
pnpm vitest tests/integration/routers/message.integration.test.ts
# 监听模式
pnpm vitest tests/integration --watch
# 生成覆盖率报告
pnpm test:integration --coverage
```
## 目录结构
```
tests/integration/
├── README.md # 集成测试说明
├── setup.ts # 通用设置和工具函数
└── routers/ # Router 层集成测试
├── message.integration.test.ts # Message Router 测试
├── session.integration.test.ts # Session Router 测试
├── topic.integration.test.ts # Topic Router 测试
└── chat-flow.integration.test.ts # 完整聊天流程测试
```
## 编写集成测试
### 基本模板
```typescript
// @vitest-environment node
import { eq } from 'drizzle-orm';
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { getTestDB } from '@/database/models/__tests__/_util';
import { messages, sessions, users } from '@/database/schemas';
import { LobeChatDatabase } from '@/database/type';
import { messageRouter } from '@/server/routers/lambda/message';
import { cleanupTestUser, createTestContext, createTestUser } from '../setup';
describe('Your Feature Integration Tests', () => {
let serverDB: LobeChatDatabase;
let userId: string;
beforeEach(async () => {
// 1. 获取测试数据库
serverDB = await getTestDB();
// 2. 创建测试用户
userId = await createTestUser(serverDB);
// 3. 准备其他测试数据
// ...
});
afterEach(async () => {
// 清理测试数据
await cleanupTestUser(serverDB, userId);
});
it('should do something', async () => {
// 1. 创建 tRPC caller
const caller = messageRouter.createCaller(createTestContext(userId));
// 2. 执行操作
const result = await caller.someMethod({
/* params */
});
// 3. 验证结果
expect(result).toBeDefined();
// 4. 🔥 关键:从数据库验证
const [dbRecord] = await serverDB.select().from(messages).where(eq(messages.id, result));
expect(dbRecord).toMatchObject({
// 验证所有关键字段
});
});
});
```
### 最佳实践
#### 1. 测试完整的调用链路
```typescript
it('should create message with correct associations', async () => {
const caller = messageRouter.createCaller(createTestContext(userId));
// 执行操作
const messageId = await caller.createMessage({
content: 'Test',
sessionId: testSessionId,
topicId: testTopicId,
});
// ✅ 从数据库验证,而不是只验证返回值
const [message] = await serverDB.select().from(messages).where(eq(messages.id, messageId));
expect(message.sessionId).toBe(testSessionId);
expect(message.topicId).toBe(testTopicId);
expect(message.userId).toBe(userId);
});
```
#### 2. 测试级联操作
```typescript
it('should cascade delete messages when session is deleted', async () => {
const sessionCaller = sessionRouter.createCaller(createTestContext(userId));
const messageCaller = messageRouter.createCaller(createTestContext(userId));
// 创建 session 和 messages
const sessionId = await sessionCaller.createSession({
/* ... */
});
await messageCaller.createMessage({ sessionId /* ... */ });
// 删除 session
await sessionCaller.removeSession({ id: sessionId });
// ✅ 验证相关消息也被删除
const remainingMessages = await serverDB
.select()
.from(messages)
.where(eq(messages.sessionId, sessionId));
expect(remainingMessages).toHaveLength(0);
});
```
#### 3. 测试跨 Router 协作
```typescript
it('should handle complete chat flow', async () => {
const sessionCaller = sessionRouter.createCaller(createTestContext(userId));
const topicCaller = topicRouter.createCaller(createTestContext(userId));
const messageCaller = messageRouter.createCaller(createTestContext(userId));
// 1. 创建 session
const sessionId = await sessionCaller.createSession({
/* ... */
});
// 2. 创建 topic
const topicId = await topicCaller.createTopic({ sessionId /* ... */ });
// 3. 创建 message
const messageId = await messageCaller.createMessage({
sessionId,
topicId,
/* ... */
});
// ✅ 验证完整的关联关系
const [message] = await serverDB.select().from(messages).where(eq(messages.id, messageId));
expect(message.sessionId).toBe(sessionId);
expect(message.topicId).toBe(topicId);
});
```
#### 4. 测试错误场景
```typescript
it('should prevent cross-user access', async () => {
// 用户 A 创建 session
const sessionId = await sessionRouter.createCaller(createTestContext(userA)).createSession({
/* ... */
});
// 用户 B 尝试访问
const callerB = messageRouter.createCaller(createTestContext(userB));
// ✅ 应该抛出错误
await expect(
callerB.createMessage({
sessionId,
content: 'Unauthorized',
}),
).rejects.toThrow();
});
```
#### 5. 测试并发场景
```typescript
it('should handle concurrent operations', async () => {
const caller = messageRouter.createCaller(createTestContext(userId));
// 并发创建多个消息
const promises = Array.from({ length: 10 }, (_, i) =>
caller.createMessage({
content: `Message ${i}`,
sessionId: testSessionId,
}),
);
const messageIds = await Promise.all(promises);
// ✅ 验证所有消息都创建成功且唯一
expect(messageIds).toHaveLength(10);
expect(new Set(messageIds).size).toBe(10);
});
```
### 数据隔离
每个测试用例应该独立,不依赖其他测试:
```typescript
beforeEach(async () => {
// 为每个测试创建新的数据
userId = await createTestUser(serverDB);
testSessionId = await createTestSession(serverDB, userId);
});
afterEach(async () => {
// 清理测试数据
await cleanupTestUser(serverDB, userId);
});
```
### 测试命名
使用清晰的命名描述测试意图:
```typescript
// ✅ 好的命名
it('should create message with correct sessionId and topicId');
it('should cascade delete messages when session is deleted');
it('should prevent cross-user access to messages');
// ❌ 不好的命名
it('test message creation');
it('test delete');
```
## 与单元测试的区别
| 维度 | 单元测试 | 集成测试 |
| ------- | --------- | ------- |
| **范围** | 单个函数 / 类 | 多个模块协作 |
| **依赖** | Mock 外部依赖 | 使用真实依赖 |
| **数据库** | Mock | 真实测试数据库 |
| **速度** | 快(毫秒级) | 慢(秒级) |
| **数量** | 多(60% | 少(30% |
| **目的** | 验证逻辑正确性 | 验证集成正确性 |
## 测试金字塔
```
/\
/E2E\ ← 10% (关键业务流程)
/------\
/ 集成 \ ← 30% (API 集成测试) ⭐ 本指南重点
/----------\
/ 单元测试 \ ← 60% (已有 80%+)
/--------------\
```
## 覆盖目标
### 优先级 P0(必须覆盖)
- ✅ 跨层级的 ID 传递(sessionId、topicId、containerId、threadId
- ✅ 权限验证(用户只能访问自己的资源)
- ✅ 级联删除(删除 session 时相关数据也删除)
- ✅ 外键约束(不能创建不存在的关联)
### 优先级 P1(应该覆盖)
- 并发场景(多个请求同时操作)
- 分页查询(正确的数据分页)
- 搜索功能(关键词搜索)
- 批量操作(批量创建 / 删除)
### 优先级 P2(可以覆盖)
- 统计功能(计数、排名)
- 复杂查询(多条件筛选)
- 性能测试(大量数据场景)
## 调试技巧
### 1. 查看测试数据库状态
```typescript
it('debug test', async () => {
// 执行操作
await caller.createMessage({
/* ... */
});
// 打印数据库状态
const allMessages = await serverDB.select().from(messages);
console.log('All messages:', allMessages);
});
```
### 2. 使用 Drizzle Studio
```bash
# 启动 Drizzle Studio 查看测试数据库
pnpm db:studio
```
### 3. 保留测试数据
```typescript
afterEach(async () => {
// 临时注释掉清理代码,保留数据用于调试
// await cleanupTestUser(serverDB, userId);
});
```
## 常见问题
### Q: 集成测试很慢怎么办?
A:
1. 只测试关键路径,不要过度测试
2. 使用 `test.concurrent` 并行执行独立的测试
3. 优化测试数据准备,避免重复创建
### Q: 测试之间相互影响怎么办?
A:
1. 确保每个测试使用独立的 userId
2. 在 `afterEach` 中彻底清理数据
3. 使用事务隔离(如果数据库支持)
### Q: 如何测试需要认证的 API?
A: 使用 `createTestContext(userId)` 创建带认证信息的上下文:
```typescript
const caller = messageRouter.createCaller(createTestContext(userId));
```
## 参考资料
- [Vitest 文档](https://vitest.dev/)
- [Drizzle ORM 文档](https://orm.drizzle.team/)
- [tRPC 测试指南](https://trpc.io/docs/server/testing)
- [测试金字塔](https://martinfowler.com/articles/practical-test-pyramid.html)
## 贡献
欢迎补充更多集成测试用例!请参考现有测试文件的风格。
+25 -15
View File
@@ -31,20 +31,30 @@ You can achieve various feature combinations using the above configuration synta
their default values).
</Callout>
| Configuration Item | Description | Default Value |
| ------------------------- | ----------------------------------------------- | ------------- |
| `webrtc_sync` | Enables WebRTC sync functionality. | Disabled |
| `language_model_settings` | Enables language model settings. | Enabled |
| `openai_api_key` | Allows users to customize the OpenAI API Key. | Enabled |
| `openai_proxy_url` | Allows users to customize the OpenAI proxy URL. | Enabled |
| `create_session` | Allows users to create sessions. | Enabled |
| `edit_agent` | Allows users to edit assistants. | Enabled |
| `dalle` | Enables the DALL-E functionality. | Enabled |
| `check_updates` | Allows checking for updates. | Enabled |
| `welcome_suggest` | Displays welcome suggestions. | Enabled |
| `market` | Enables the assistant market functionality. | Enabled |
| `speech_to_text` | Enables speech-to-text functionality. | Enabled |
| `knowledge_base` | Enables the knowledge base functionality. | Enabled |
| `clerk_sign_up` | Enables the Clerk SignUp functionality. | Enabled |
| Configuration Item | Description | Default Value |
| ------------------------- | -------------------------------------------------------------------------------------------------------- | ------------- |
| `check_updates` | Allows checking for updates. | Enabled |
| `pin_list` | Controls pinned agent list display in sidebar. | Disabled |
| `language_model_settings` | Enables language model settings. | Enabled |
| `provider_settings` | Controls model provider settings display. | Enabled |
| `openai_api_key` | Allows users to customize the OpenAI API Key. | Enabled |
| `openai_proxy_url` | Allows users to customize the OpenAI proxy URL. | Enabled |
| `api_key_manage` | Controls access to API key management page (/profile/apikey). | Disabled |
| `create_session` | Allows users to create sessions. | Enabled |
| `edit_agent` | Allows users to edit assistants. | Enabled |
| `plugins` | Controls plugin functionality in chat and agent settings. | Enabled |
| `dalle` | Enables the DALL-E functionality. | Enabled |
| `ai_image` | Controls AI image generation feature and page (/image). | Enabled |
| `speech_to_text` | Enables speech-to-text functionality. | Enabled |
| `token_counter` | Reserved for token counter display. | Enabled |
| `welcome_suggest` | Displays welcome suggestions. | Enabled |
| `changelog` | Controls changelog modal/page display. | Enabled |
| `clerk_sign_up` | Enables the Clerk SignUp functionality. | Enabled |
| `market` | Enables the assistant market functionality. | Enabled |
| `knowledge_base` | Enables the knowledge base functionality. | Enabled |
| `rag_eval` | Controls RAG evaluation feature (/repos/\[id]/evals). | Disabled |
| `cloud_promotion` | Controls cloud service promotion link display in user menu. | Disabled |
| `commercial_hide_github` | Hides GitHub-related links in settings footer (requires commercial license). | Disabled |
| `commercial_hide_docs` | Hides documentation and help menu including changelog, docs, and feedback (requires commercial license). | Disabled |
You can always check the [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) to get the latest list of feature flags.
@@ -28,20 +28,30 @@ tags:
关键字,你需要手动控制所有的功能标志(否则它们会采用对应的默认值)。
</Callout>
| 配置项 | 解释 | 默认值 |
| ------------------------- | ----------------------- | --- |
| `webrtc_sync` | 启用 WebRTC 同步功能。 | 关闭 |
| `language_model_settings` | 启用语言模型设置。 | 开启 |
| `openai_api_key` | 允许用户自定义 OpenAI API Key。 | 开启 |
| `openai_proxy_url` | 允许用户自定义 OpenAI 代理 URL。 | 开启 |
| `create_session` | 允许用户创建会话。 | 开启 |
| `edit_agent` | 允许用户编辑助手。 | 开启 |
| `dalle` | 启用 DALL-E 功能。 | 开启 |
| `check_updates` | 允许检查更新。 | 开启 |
| `welcome_suggest` | 显示欢迎建议。 | 开启 |
| `market` | 启用助手市场功能。 | 开启 |
| `speech_to_text` | 启用语音转文本功能。 | 开启 |
| `knowledge_base` | 启用知识库功能。 | 开启 |
| `clerk_sign_up` | 启用 Clerk 注册功能。 | 开启 |
| 配置项 | 解释 | 默认值 |
| ------------------------- | ------------------------------------ | --- |
| `check_updates` | 允许检查更新。 | 开启 |
| `pin_list` | 控制侧边栏中置顶助手列表的显示。 | 关闭 |
| `language_model_settings` | 启用语言模型设置。 | 开启 |
| `provider_settings` | 控制模型供应商设置的显示。 | 开启 |
| `openai_api_key` | 允许用户自定义 OpenAI API Key。 | 开启 |
| `openai_proxy_url` | 允许用户自定义 OpenAI 代理 URL。 | 开启 |
| `api_key_manage` | 控制 API 密钥管理页面 (/profile/apikey) 的访问。 | 关闭 |
| `create_session` | 允许用户创建会话。 | 开启 |
| `edit_agent` | 允许用户编辑助手。 | 开启 |
| `plugins` | 控制聊天和助手设置中的插件功能。 | 开启 |
| `dalle` | 启用 DALL-E 功能。 | 开启 |
| `ai_image` | 控制 AI 图像生成功能和页面 (/image)。 | 开启 |
| `speech_to_text` | 启用语音转文本功能。 | 开启 |
| `token_counter` | 保留用于令牌计数器显示。 | 开启 |
| `welcome_suggest` | 显示欢迎建议。 | 开启 |
| `changelog` | 控制更新日志弹窗 / 页面的显示。 | 开启 |
| `clerk_sign_up` | 启用 Clerk 注册功能。 | 开启 |
| `market` | 启用助手市场功能。 | 开启 |
| `knowledge_base` | 启用知识库功能。 | 开启 |
| `rag_eval` | 控制 RAG 评估功能 (/repos/\[id]/evals)。 | 关闭 |
| `cloud_promotion` | 控制用户菜单中云服务推广链接的显示。 | 关闭 |
| `commercial_hide_github` | 隐藏设置页面底部的 GitHub 相关链接(需要商业授权)。 | 关闭 |
| `commercial_hide_docs` | 隐藏文档和帮助菜单,包括更新日志、文档和反馈(需要商业授权)。 | 关闭 |
你可以随时检查 [featureFlags](https://github.com/lobehub/lobe-chat/blob/main/src/config/featureFlags/schema.ts) 以获取最新的特性标志列表。
+11
View File
@@ -26,6 +26,17 @@ For example: `+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-0125-preview=gpt-4-turb
In the above example, it adds `qwen-7b-chat` and `glm-6b` to the model list, removes `gpt-3.5-turbo` from the list, and displays the model name of `gpt-4-0125-preview` as `gpt-4-turbo`. If you want to disable all models first and then enable specific models, you can use `-all,+gpt-3.5-turbo`, which means only enabling `gpt-3.5-turbo`.
### -all: Hide all models
- Description: `-all` means hiding all built-in models first. Its usually combined with `+` to only enable the models you explicitly specify.
- Example:
```text
-all,+gpt-3.5-turbo,+gpt-4-0125-preview=gpt-4-turbo
```
This enables only gpt-3.5-turbo and gpt-4-turbo while hiding other models.
## Extension Capabilities
Considering the diversity of model capabilities, we started to add extension configuration in version `0.147.8`, with the following rules:
@@ -25,6 +25,17 @@ id->deploymentName=displayName<maxToken:vision:reasoning:search:fc:file:imageOut
上面示例表示增加 `qwen-7b-chat` 和 `glm-6b` 到模型列表,而从列表中删除 `gpt-3.5-turbo`,并将 `gpt-4-0125-preview` 模型名字展示为 `gpt-4-turbo`。如果你想先禁用所有模型,再启用指定模型,可以使用 `-all,+gpt-3.5-turbo`,则表示仅启用 `gpt-3.5-turbo`。
### -all:隐藏所有模型
- 描述:`-all` 表示先隐藏所有内置模型。通常与 `+` 组合使用,用于只启用你显式指定的模型。
- 示例:
```text
-all,+gpt-3.5-turbo,+gpt-4-0125-preview=gpt-4-turbo
```
仅启用 gpt-3.5-turbo 和 gpt-4-turbo,而其他模型都隐藏。
## 扩展能力
考虑到模型的能力多样性,我们在 `0.147.8` 版本开始增加扩展性配置,它的规则如下:
@@ -3,6 +3,7 @@ title: LobeChat Authentication Service Environment Variables
description: >-
Explore the essential environment variables for configuring authentication services in LobeChat, including OAuth SSO, NextAuth settings, and provider-specific details.
tags:
- Authentication Service
- OAuth SSO
@@ -279,6 +280,22 @@ LobeChat provides a complete authentication service capability when deployed. Th
- Default: `-`
- Example: `https://your-instance.okta.com`
### Feishu
#### `AUTH_FEISHU_APP_ID`
- Type: Required
- Description: App ID of the Feishu application.
- Default: `-`
- Example: `cli_9f7b1e1e1e1e1e1e`
#### `AUTH_FEISHU_APP_SECRET`
- Type: Required
- Description: App Secret of the Feishu application.
- Default: `-`
- Example: `AlHxxX1e1e1e1e1e1e1e1e1e1e1e1e1e`
### Generic OIDC
#### `AUTH_GENERIC_OIDC_ID`
@@ -127,16 +127,62 @@ For specific content, please refer to the [Feature Flags](/docs/self-hosting/adv
### `SSRF_ALLOW_PRIVATE_IP_ADDRESS`
- Type: Optional
- Description: Allow to connect private IP address. In a trusted environment, it can be set to true to turn off SSRF protection.
- Description: Controls whether to allow connections to private IP addresses. Set to `1` to disable SSRF protection and allow all private IP addresses. In a trusted environment (e.g., internal network), this can be enabled to allow access to internal resources.
- Default: `0`
- Example: `1` or `0`
<Callout type="warning">
**Security Notice**: Enabling this option will disable SSRF protection and allow connections to private
IP addresses (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, etc.). Only enable this in
trusted environments where you need to access internal network resources.
</Callout>
**Use Cases**:
LobeChat performs SSRF security checks in the following scenarios:
1. **Image/Video URL to Base64 Conversion**: When processing media messages (e.g., vision models, multimodal models), LobeChat converts image and video URLs to base64 format. This check prevents malicious users from accessing internal network resources.
Examples:
- Image: A user sends an image message with URL `http://192.168.1.100/admin/secrets.png`
- Video: A user sends a video message with URL `http://10.0.0.50/internal/meeting.mp4`
Without SSRF protection, these requests could expose internal network resources.
2. **Web Crawler**: When using web crawling features to fetch external content.
3. **Proxy Requests**: When proxying external API requests.
**Configuration Examples**:
```bash
# Scenario 1: Public deployment (recommended)
# Block all private IP addresses for security
SSRF_ALLOW_PRIVATE_IP_ADDRESS=0
# Scenario 2: Internal deployment
# Allow all private IP addresses to access internal image servers
SSRF_ALLOW_PRIVATE_IP_ADDRESS=1
# Scenario 3: Hybrid deployment (most common)
# Block private IPs by default, but allow specific trusted internal servers
SSRF_ALLOW_PRIVATE_IP_ADDRESS=0
SSRF_ALLOW_IP_ADDRESS_LIST=192.168.1.100,10.0.0.50
```
### `SSRF_ALLOW_IP_ADDRESS_LIST`
- Type: Optional
- Description: Allow private IP address list, multiple IP addresses are separated by commas. Only when `SSRF_ALLOW_PRIVATE_IP_ADDRESS` is `0`, it takes effect.
- Description: Whitelist of allowed IP addresses, separated by commas. Only takes effect when `SSRF_ALLOW_PRIVATE_IP_ADDRESS` is `0`. Use this to allow specific internal IP addresses while keeping SSRF protection enabled for other private IPs.
- Default: -
- Example: `198.18.1.62,224.0.0.3`
- Example: `192.168.1.100,10.0.0.50,172.16.0.10`
**Common Use Cases**:
- Allow access to internal image storage server: `192.168.1.100`
- Allow access to internal API gateway: `10.0.0.50`
- Allow access to internal documentation server: `172.16.0.10`
### `ENABLE_AUTH_PROTECTION`
@@ -152,6 +198,18 @@ For specific content, please refer to the [Feature Flags](/docs/self-hosting/adv
- Default: -
- Example: `https://cdn.example.com`
## AI Image
### `AI_IMAGE_DEFAULT_IMAGE_NUM`
- Type: Optional
- Description: Sets the default number of images to generate for AI image generation. Users can still override this value in their settings.
- Default: `4`
- Example: `6`
- Range: `1-20`
This environment variable allows administrators to customize the default image generation count for their deployment. The value must be between 1 and 20. If not set, it defaults to 4. Users can still adjust this value in their personal settings.
## Plugin Service
### `PLUGINS_INDEX_URL`
@@ -123,16 +123,61 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
### `SSRF_ALLOW_PRIVATE_IP_ADDRESS`
- 类型:可选
- 描述:是否允许连接私有 IP 地址。在可信环境中可以设置为 true 来关闭 SSRF 防护
- 描述:控制是否允许连接私有 IP 地址。设置为 `1` 时将关闭 SSRF 防护并允许所有私有 IP 地址。在可信环境(如内网部署)中,可以启用此选项以访问内部资源
- 默认值:`0`
- 示例:`1` or `0`
- 示例:`1` `0`
<Callout type="warning">
**安全提示**:启用此选项将关闭 SSRF 防护,允许连接私有 IP 地址段(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16
等)。仅在需要访问内网资源的可信环境中启用。
</Callout>
**应用场景**
LobeChat 会在以下场景执行 SSRF 安全检查:
1. **图片 / 视频 URL 转 Base64**:在处理媒体消息时(例如视觉模型、多模态模型),LobeChat 会将图片和视频 URL 转换为 base64 格式。此检查可防止恶意用户通过媒体 URL 访问内网资源。
举例:
- 图片:用户发送图片消息,URL 为 `http://192.168.1.100/admin/secrets.png`
- 视频:用户发送视频消息,URL 为 `http://10.0.0.50/internal/meeting.mp4`
若无 SSRF 防护,这些请求可能导致内网资源泄露。
2. **网页爬取**:使用网页爬取功能获取外部内容时。
3. **代理请求**:代理外部 API 请求时。
**配置示例**
```bash
# 场景 1:公网部署(推荐)
# 阻止所有私有 IP 访问,保证安全
SSRF_ALLOW_PRIVATE_IP_ADDRESS=0
# 场景 2:内网部署
# 允许所有私有 IP,可访问内网图片服务器等资源
SSRF_ALLOW_PRIVATE_IP_ADDRESS=1
# 场景 3:混合部署(最常见)
# 默认阻止私有 IP,但允许特定可信的内网服务器
SSRF_ALLOW_PRIVATE_IP_ADDRESS=0
SSRF_ALLOW_IP_ADDRESS_LIST=192.168.1.100,10.0.0.50
```
### `SSRF_ALLOW_IP_ADDRESS_LIST`
- 类型:可选
- 说明:允许的私有 IP 地址列表,多个 IP 地址用逗号分隔。仅在 `SSRF_ALLOW_PRIVATE_IP_ADDRESS` 为 `0` 时生效。
- 描述:允许访问的 IP 地址白名单,多个 IP 地址用逗号分隔。仅在 `SSRF_ALLOW_PRIVATE_IP_ADDRESS` 为 `0` 时生效。使用此选项可以在保持 SSRF 防护的同时,允许访问特定的内网 IP 地址。
- 默认值:-
- 示例:`198.18.1.62,224.0.0.3`
- 示例:`192.168.1.100,10.0.0.50,172.16.0.10`
**常见使用场景**
- 允许访问内网图片存储服务器:`192.168.1.100`
- 允许访问内网 API 网关:`10.0.0.50`
- 允许访问内网文档服务器:`172.16.0.10`
### `ENABLE_AUTH_PROTECTION`
@@ -148,6 +193,18 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
- 默认值:-
- 示例:`https://cdn.example.com`
## AI 图像
### `AI_IMAGE_DEFAULT_IMAGE_NUM`
- 类型:可选
- 描述:设置 AI 图像生成的默认图片数量。用户仍可在个人设置中覆盖此值。
- 默认值:`4`
- 示例:`6`
- 范围:`1-20`
此环境变量允许管理员为其部署自定义默认图片生成数量。值必须在 1 到 20 之间。如果未设置,默认为 4。用户仍可在个人设置中调整此值。
## 插件服务
### `PLUGINS_INDEX_URL`
@@ -651,15 +651,67 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
The above example disables all models first, then enables `fal-ai/flux/schnell` and `fal-ai/flux-pro/kontext` (displayed as `FLUX.1 Kontext [pro]`).
## BFL
## ComfyUI
### `ENABLED_BFL`
### `ENABLED_COMFYUI`
- Type: Optional
- Description: Enables BFL as a model provider by default. Set to `0` to disable the BFL service.
- Description: Enables ComfyUI as a model provider by default. Set to `0` to disable the ComfyUI service.
- Default: `1`
- Example: `0`
### `COMFYUI_BASE_URL`
- Type: Optional
- Description: The base URL address of the ComfyUI service
- Default: `http://localhost:8188`
- Example: `http://192.168.1.100:8188` or `https://my-comfyui-server.com`
### `COMFYUI_AUTH_TYPE`
- Type: Optional
- Description: The authentication type for ComfyUI, supporting 4 authentication methods
- `none`: No authentication (default)
- `basic`: Basic authentication (username + password)
- `bearer`: Bearer Token authentication (API key)
- `custom`: Custom request header authentication
- Default: `none`
- Example: `basic`
### `COMFYUI_API_KEY`
- Type: Optional
- Description: The API key used when the authentication type is `bearer`
- Default: -
- Example: `sk-xxxxxx...xxxxxx`
### `COMFYUI_USERNAME`
- Type: Optional
- Description: The username used when the authentication type is `basic`
- Default: -
- Example: `admin`
### `COMFYUI_PASSWORD`
- Type: Optional
- Description: The password used when the authentication type is `basic`
- Default: -
- Example: `password123`
### `COMFYUI_CUSTOM_HEADERS`
- Type: Optional
- Description: Custom request headers used when the authentication type is `custom`, requires JSON format string
- Default: -
- Example: `{"X-Auth-Token": "your-token", "X-Custom-Header": "value"}`
<Callout type={'info'}>
ComfyUI supports multiple authentication methods. Please choose the appropriate authentication type and corresponding authentication parameters according to your ComfyUI service configuration. If your ComfyUI service has no authentication set up, you can skip configuring authentication-related environment variables.
</Callout>
## BFL
### `BFL_API_KEY`
- Type: Required
@@ -696,13 +748,6 @@ NewAPI is a multi-provider model aggregation service that supports automatic mod
## Vercel AI Gateway
### `ENABLED_VERCELAIGATEWAY`
- Type: Optional
- Description: Enables Vercel AI Gateway as a model provider by default. Set to `0` to disable the Vercel AI Gateway service.
- Default: `1`
- Example: `0`
### `VERCELAIGATEWAY_API_KEY`
- Type: Required
@@ -717,4 +762,36 @@ NewAPI is a multi-provider model aggregation service that supports automatic mod
- Default: `-`
- Example: `-all,+vercel-model-1,+vercel-model-2=vercel-special`
## Cerebras
### `CEREBRAS_API_KEY`
- Type: Required
- Description: This is the API key you applied for in the Cerebras service.
- Default: -
- Example: `csk-xxxxxx...xxxxxx`
### `CEREBRAS_MODEL_LIST`
- Type: Optional
- Description: Used to control the Cerebras model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model. Separate multiple entries with commas. The definition syntax follows the same rules as other providers' model lists.
- Default: `-`
- Example: `-all,+cerebras-model-1,+cerebras-model-2=cerebras-special`
## AiHubMix
### `AIHUBMIX_API_KEY`
- Type: Required
- Description: This is the API key you applied for in the AiHubMix service.
- Default: -
- Example: `sk-xxxxxx...xxxxxx`
### `AIHUBMIX_MODEL_LIST`
- Type: Optional
- Description: Used to control the AiHubMix model list. Use `+` to add a model, `-` to hide a model, and `model_name=display_name` to customize the display name of a model. Separate multiple entries with commas. The definition syntax follows the same rules as other providers' model lists.
- Default: `-`
- Example: `-all,+claude-opus-4-1-20250805,+claude-opus-4-20250514=claude-opus-4`
[model-list]: /docs/self-hosting/advanced/model-list
@@ -165,6 +165,65 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
- 默认值:`us-east-1`
- 示例:`us-east-1`
## ComfyUI
### `ENABLED_COMFYUI`
- 类型:可选
- 描述:默认启用 ComfyUI 作为模型供应商,当设为 0 时关闭 ComfyUI 服务
- 默认值:`1`
- 示例:`0`
### `COMFYUI_BASE_URL`
- 类型:可选
- 描述:ComfyUI 服务的基础 URL 地址
- 默认值:`http://localhost:8000`
- 示例:`http://192.168.1.100:8000` 或 `https://my-comfyui-server.com`
### `COMFYUI_AUTH_TYPE`
- 类型:可选
- 描述:ComfyUI 的认证类型,支持 4 种认证方式
- `none`: 无认证(默认)
- `basic`: 基础认证(用户名 + 密码)
- `bearer`: Bearer Token 认证(API 密钥)
- `custom`: 自定义请求头认证
- 默认值:`none`
- 示例:`basic`
### `COMFYUI_API_KEY`
- 类型:可选
- 描述:当认证类型为 `bearer` 时使用的 API 密钥
- 默认值:-
- 示例:`sk-xxxxxx...xxxxxx`
### `COMFYUI_USERNAME`
- 类型:可选
- 描述:当认证类型为 `basic` 时使用的用户名
- 默认值:-
- 示例:`admin`
### `COMFYUI_PASSWORD`
- 类型:可选
- 描述:当认证类型为 `basic` 时使用的密码
- 默认值:-
- 示例:`password123`
### `COMFYUI_CUSTOM_HEADERS`
- 类型:可选
- 描述:当认证类型为 `custom` 时使用的自定义请求头,需要使用 JSON 格式字符串
- 默认值:-
- 示例:`{"X-Auth-Token": "your-token", "X-Custom-Header": "value"}`
<Callout type={'info'}>
ComfyUI 支持多种认证方式,请根据您的 ComfyUI 服务配置选择合适的认证类型和相应的认证参数。如果您的 ComfyUI 服务没有设置认证,可以不配置认证相关的环境变量。
</Callout>
## DeepSeek AI
### `DEEPSEEK_PROXY_URL`
@@ -651,13 +710,6 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
## BFL
### `ENABLED_BFL`
- 类型:可选
- 描述:默认启用 BFL 作为模型供应商,当设为 0 时关闭 BFL 服务
- 默认值:`1`
- 示例:`0`
### `BFL_API_KEY`
- 类型:必选
@@ -699,13 +751,6 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
## Vercel AI Gateway
### `ENABLED_VERCELAIGATEWAY`
- 类型:可选
- 描述:默认启用 Vercel AI Gateway 作为模型供应商,当设为 0 时关闭 Vercel AI Gateway 服务
- 默认值:`1`
- 示例:`0`
### `VERCELAIGATEWAY_API_KEY`
- 类型:必选
@@ -720,4 +765,36 @@ LobeChat 在部署时提供了丰富的模型服务商相关的环境变量,
- 默认值:`-`
- 示例:`-all,+vercel-model-1,+vercel-model-2=vercel-special`
## Cerebras
### `CEREBRAS_API_KEY`
- 类型:必选
- 描述:这是你在 Cerebras 服务中申请的 API 密钥
- 默认值:-
- 示例:`csk-xxxxxx...xxxxxx`
### `CEREBRAS_MODEL_LIST`
- 类型:可选
- 描述:用来控制 Cerebras 模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。模型定义语法规则与其他 provider 保持一致。
- 默认值:`-`
- 示例:`-all,+cerebras-model-1,+cerebras-model-2=cerebras-special`
## AiHubMix
### `AIHUBMIX_API_KEY`
- 类型:必选
- 描述:这是你在 AiHubMix 服务中申请的 API 密钥
- 默认值:-
- 示例:`sk-xxxxxx...xxxxxx`
### `AIHUBMIX_MODEL_LIST`
- 类型:可选
- 描述:用来控制 AiHubMix 模型列表,使用 `+` 增加一个模型,使用 `-` 来隐藏一个模型,使用 `模型名=展示名` 来自定义模型的展示名,用英文逗号隔开。模型定义语法规则与其他 provider 保持一致。
- 默认值:`-`
- 示例:`-all,+claude-opus-4-1-20250805,+claude-opus-4-20250514=claude-opus-4`
[model-list]: /zh/docs/self-hosting/advanced/model-list
+52
View File
@@ -0,0 +1,52 @@
---
title: Agent Team
description: Turn any conversation into a team effort. Multiple AI agents collaborate naturally to give you richer, more insightful responses.
tags:
- Agent Team
- Multi-Agent
- AI Orchestration
- Agent Coordination
---
# Agent Team
<Image alt={'Agent Team'} cover src={'https://github.com/user-attachments/assets/6e5599f6-1e39-4b14-a218-4f5ffa2c306a'} />
Sometimes one perspective isn't enough. Agent Team brings together multiple AI agents, each with their own expertise, to collaborate on your conversations. Richer discussions, diverse viewpoints, and solutions you wouldn't get from any single agent.
## Highlights
- Multiple assistants with specialized knowledge work together, each contributing their strengths
- A built-in host ensures the agent team conversation runs smoothly and organized
- Private messaging allows seamless coordination between assistants
- You get comprehensive answers from multiple perspectives
- Ready to use with your own assistants or rich agent team templates
## Use Cases
**Learning and Research**: Different assistants gather different materials through different tools, then come together for spontaneous discussion.
**Entertainment**: Multiplayer language games like Werewolf, Model United Nations, and Turtle Soup.
**Brainstorming**: Diverse perspectives spark better ideas.
**Problem Solving**: Benefit from insights across different professional fields, with different tools and MCPs, agent team allows you to have the perfect AI team.
## Quick Start
Click the "Create Agent Team" button, you can choose to create directly from preset agent team templates, or select your own assistants to form an agent team.
You can use @ to mention a team member in the agent team, or click their avatar to send them a private message. Everything works just like in a real chat room.
### Interrupt and Resume Agent Team
You can interrupt the host's thinking at any time, and the agent team will "pause" after interruption. You can start the moderator at any time, and the agent team will continue.
Of course, conversations may also stop naturally.
### Advanced Options
- Agent Team Speed: Customize the response speed of the agent team
- Custom Moderator: Guide the moderator's behavior according to specific needs
Agent Team transforms how you interact with AI. Instead of getting one answer, you participate in a conversation—complete with different viewpoints, collaborative problem-solving, and profound insights that emerge when AI agents work together.

Some files were not shown because too many files have changed in this diff Show More