@lobehub/chat 1.70.3 โ 1.70.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +50 -0
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/advanced/model-list.mdx +3 -3
- package/docs/self-hosting/advanced/model-list.zh-CN.mdx +3 -3
- package/docs/self-hosting/environment-variables/model-provider.mdx +17 -3
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +16 -2
- package/package.json +1 -1
- package/packages/web-crawler/src/crawImpl/naive.ts +11 -3
- package/packages/web-crawler/src/urlRules.ts +6 -0
- package/src/app/[variants]/(main)/(mobile)/me/(home)/features/Header.tsx +2 -2
- package/src/app/[variants]/(main)/settings/common/features/Theme/index.tsx +5 -3
- package/src/components/Branding/ProductLogo/Custom.tsx +4 -0
- package/src/config/modelProviders/openrouter.ts +7 -0
- package/src/const/settings/common.ts +0 -1
- package/src/database/schemas/user.ts +3 -3
- package/src/database/server/models/__tests__/plugin.test.ts +8 -8
- package/src/database/server/models/plugin.ts +20 -20
- package/src/features/User/UserPanel/ThemeButton.tsx +4 -4
- package/src/layout/GlobalProvider/AppTheme.tsx +5 -9
- package/src/libs/agent-runtime/openrouter/index.test.ts +10 -1
- package/src/services/plugin/client.test.ts +21 -21
- package/src/services/user/_deprecated.test.ts +1 -1
- package/src/services/user/client.test.ts +1 -1
- package/src/store/aiInfra/slices/aiProvider/__tests__/selectors.test.ts +249 -0
- package/src/store/global/action.test.ts +15 -0
- package/src/store/global/actions/__tests__/general.test.ts +221 -0
- package/src/store/global/actions/general.ts +10 -1
- package/src/store/global/initialState.ts +6 -0
- package/src/store/global/selectors/systemStatus.test.ts +209 -0
- package/src/store/global/selectors/systemStatus.ts +22 -21
- package/src/store/user/slices/settings/action.test.ts +1 -19
- package/src/store/user/slices/settings/action.ts +0 -5
- package/src/store/user/slices/settings/selectors/general.test.ts +5 -15
- package/src/store/user/slices/settings/selectors/general.ts +0 -6
- package/src/types/user/settings/general.ts +0 -2
package/CHANGELOG.md
CHANGED
@@ -2,6 +2,56 @@
|
|
2
2
|
|
3
3
|
# Changelog
|
4
4
|
|
5
|
+
### [Version 1.70.5](https://github.com/lobehub/lobe-chat/compare/v1.70.4...v1.70.5)
|
6
|
+
|
7
|
+
<sup>Released on **2025-03-11**</sup>
|
8
|
+
|
9
|
+
#### ๐ Bug Fixes
|
10
|
+
|
11
|
+
- **misc**: Refactor the theme implement.
|
12
|
+
|
13
|
+
<br/>
|
14
|
+
|
15
|
+
<details>
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
17
|
+
|
18
|
+
#### What's fixed
|
19
|
+
|
20
|
+
- **misc**: Refactor the theme implement, closes [#6844](https://github.com/lobehub/lobe-chat/issues/6844) ([e5c2161](https://github.com/lobehub/lobe-chat/commit/e5c2161))
|
21
|
+
|
22
|
+
</details>
|
23
|
+
|
24
|
+
<div align="right">
|
25
|
+
|
26
|
+
[](#readme-top)
|
27
|
+
|
28
|
+
</div>
|
29
|
+
|
30
|
+
### [Version 1.70.4](https://github.com/lobehub/lobe-chat/compare/v1.70.3...v1.70.4)
|
31
|
+
|
32
|
+
<sup>Released on **2025-03-11**</sup>
|
33
|
+
|
34
|
+
#### ๐ Styles
|
35
|
+
|
36
|
+
- **misc**: Support OpenRouter custom BaseURL.
|
37
|
+
|
38
|
+
<br/>
|
39
|
+
|
40
|
+
<details>
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
42
|
+
|
43
|
+
#### Styles
|
44
|
+
|
45
|
+
- **misc**: Support OpenRouter custom BaseURL ([a8089ed](https://github.com/lobehub/lobe-chat/commit/a8089ed))
|
46
|
+
|
47
|
+
</details>
|
48
|
+
|
49
|
+
<div align="right">
|
50
|
+
|
51
|
+
[](#readme-top)
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
5
55
|
### [Version 1.70.3](https://github.com/lobehub/lobe-chat/compare/v1.70.2...v1.70.3)
|
6
56
|
|
7
57
|
<sup>Released on **2025-03-11**</sup>
|
package/README.md
CHANGED
@@ -330,7 +330,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
|
330
330
|
| [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` |
|
331
331
|
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2024-12-22**</sup> | Analyze stocks and get comprehensive real-time investment data and analytics.<br/>`stock` |
|
332
332
|
|
333
|
-
> ๐ Total plugins: [<kbd>**
|
333
|
+
> ๐ Total plugins: [<kbd>**46**</kbd>](https://lobechat.com/discover/plugins)
|
334
334
|
|
335
335
|
<!-- PLUGIN LIST -->
|
336
336
|
|
package/README.zh-CN.md
CHANGED
@@ -323,7 +323,7 @@ LobeChat ็ๆไปถ็ๆ็ณป็ปๆฏๅ
ถๆ ธๅฟๅ่ฝ็้่ฆๆฉๅฑ๏ผๅฎๆๅคงๅฐ
|
|
323
323
|
| [ๅฟ
ๅบ็ฝ้กตๆ็ดข](https://lobechat.com/discover/plugin/Bingsearch-identifier)<br/><sup>By **FineHow** on **2024-12-22**</sup> | ้่ฟ BingApi ๆ็ดขไบ่็ฝไธ็ไฟกๆฏ<br/>`bingsearch` |
|
324
324
|
| [PortfolioMeta](https://lobechat.com/discover/plugin/StockData)<br/><sup>By **portfoliometa** on **2024-12-22**</sup> | ๅๆ่ก็ฅจๅนถ่ทๅๅ
จ้ข็ๅฎๆถๆ่ตๆฐๆฎๅๅๆใ<br/>`่ก็ฅจ` |
|
325
325
|
|
326
|
-
> ๐ Total plugins: [<kbd>**
|
326
|
+
> ๐ Total plugins: [<kbd>**46**</kbd>](https://lobechat.com/discover/plugins)
|
327
327
|
|
328
328
|
<!-- PLUGIN LIST -->
|
329
329
|
|
package/changelog/v1.json
CHANGED
@@ -1,4 +1,22 @@
|
|
1
1
|
[
|
2
|
+
{
|
3
|
+
"children": {
|
4
|
+
"fixes": [
|
5
|
+
"Refactor the theme implement."
|
6
|
+
]
|
7
|
+
},
|
8
|
+
"date": "2025-03-11",
|
9
|
+
"version": "1.70.5"
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"children": {
|
13
|
+
"improvements": [
|
14
|
+
"Support OpenRouter custom BaseURL."
|
15
|
+
]
|
16
|
+
},
|
17
|
+
"date": "2025-03-11",
|
18
|
+
"version": "1.70.4"
|
19
|
+
},
|
2
20
|
{
|
3
21
|
"children": {},
|
4
22
|
"date": "2025-03-11",
|
@@ -14,10 +14,10 @@ tags:
|
|
14
14
|
|
15
15
|
LobeChat supports customizing the model list during deployment. This configuration is done in the environment for each [model provider](/docs/self-hosting/environment-variables/model-provider).
|
16
16
|
|
17
|
-
You can use `+` to add a model, `-` to hide a model, and use `model name=display name<extension configuration>` to customize the display name of a model, separated by English commas. The basic syntax is as follows:
|
17
|
+
You can use `+` to add a model, `-` to hide a model, and use `model name->deploymentName=display name<extension configuration>` to customize the display name of a model, separated by English commas. The basic syntax is as follows:
|
18
18
|
|
19
19
|
```text
|
20
|
-
id=displayName<maxToken:vision:reasoning:search:fc:file>,model2,model3
|
20
|
+
id->deploymentName=displayName<maxToken:vision:reasoning:search:fc:file>,model2,model3
|
21
21
|
```
|
22
22
|
|
23
23
|
For example: `+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-0125-preview=gpt-4-turbo`
|
@@ -29,7 +29,7 @@ In the above example, it adds `qwen-7b-chat` and `glm-6b` to the model list, rem
|
|
29
29
|
Considering the diversity of model capabilities, we started to add extension configuration in version `0.147.8`, with the following rules:
|
30
30
|
|
31
31
|
```shell
|
32
|
-
id=displayName<maxToken:vision:reasoning:search:fc:file>
|
32
|
+
id->deploymentName=displayName<maxToken:vision:reasoning:search:fc:file>
|
33
33
|
```
|
34
34
|
|
35
35
|
The first value in angle brackets is designated as the `maxToken` for this model. The second value and beyond are the model's extension capabilities, separated by colons `:`, and the order is not important.
|
@@ -13,10 +13,10 @@ tags:
|
|
13
13
|
|
14
14
|
LobeChat ๆฏๆๅจ้จ็ฝฒๆถ่ชๅฎไนๆจกๅๅ่กจ๏ผ่ฏฆๆ
่ฏทๅ่ [ๆจกๅๆไพๅ](/zh/docs/self-hosting/environment-variables/model-provider) ใ
|
15
15
|
|
16
|
-
ไฝ ๅฏไปฅไฝฟ็จ `+` ๅขๅ ไธไธชๆจกๅ๏ผไฝฟ็จ `-` ๆฅ้่ไธไธชๆจกๅ๏ผไฝฟ็จ
|
16
|
+
ไฝ ๅฏไปฅไฝฟ็จ `+` ๅขๅ ไธไธชๆจกๅ๏ผไฝฟ็จ `-` ๆฅ้่ไธไธชๆจกๅ๏ผไฝฟ็จ `ๆจกๅๅ->้จ็ฝฒๅ=ๅฑ็คบๅ<ๆฉๅฑ้
็ฝฎ>` ๆฅ่ชๅฎไนๆจกๅ็ๅฑ็คบๅ๏ผ็จ่ฑๆ้ๅท้ๅผใ้่ฟ `<>` ๆฅๆทปๅ ๆฉๅฑ้
็ฝฎใๅบๆฌ่ฏญๆณๅฆไธ๏ผ
|
17
17
|
|
18
18
|
```text
|
19
|
-
id=displayName<maxToken:vision:reasoning:search:fc:file>,model2,model3
|
19
|
+
id->deploymentName=displayName<maxToken:vision:reasoning:search:fc:file>,model2,model3
|
20
20
|
```
|
21
21
|
|
22
22
|
ไพๅฆ๏ผ `+qwen-7b-chat,+glm-6b,-gpt-3.5-turbo,gpt-4-0125-preview=gpt-4-turbo`
|
@@ -28,7 +28,7 @@ id=displayName<maxToken:vision:reasoning:search:fc:file>,model2,model3
|
|
28
28
|
่่ๅฐๆจกๅ็่ฝๅๅคๆ ทๆง๏ผๆไปฌๅจ `0.147.8` ็ๆฌๅผๅงๅขๅ ๆฉๅฑๆง้
็ฝฎ๏ผๅฎ็่งๅๅฆไธ๏ผ
|
29
29
|
|
30
30
|
```shell
|
31
|
-
id=displayName<maxToken:vision:reasoning:search:fc:file>
|
31
|
+
id->deploymentName=displayName<maxToken:vision:reasoning:search:fc:file>
|
32
32
|
```
|
33
33
|
|
34
34
|
ๅฐๆฌๅท็ฌฌไธไธชๅผ็บฆๅฎไธบ่ฟไธชๆจกๅ็ `maxToken` ใ็ฌฌไบไธชๅไปฅๅไฝไธบๆจกๅ็ๆฉๅฑ่ฝๅ๏ผ่ฝๅไธ่ฝๅไน้ด็จๅๅท `:` ไฝไธบๅ้็ฌฆ๏ผ้กบๅบไธ้่ฆใ
|
@@ -94,7 +94,7 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
94
94
|
### `AZURE_MODEL_LIST`
|
95
95
|
|
96
96
|
- Type: Optional
|
97
|
-
- Description: Used to control the model list, use `+` to add a model, use `-` to hide a model, use `id->
|
97
|
+
- Description: Used to control the model list, use `+` to add a model, use `-` to hide a model, use `id->deploymentName=displayName` to customize the display name of a model, separated by commas. Definition syntax rules see [model-list][model-list]
|
98
98
|
- Default: `-`
|
99
99
|
- Example: `gpt-35-turbo->my-deploy=GPT 3.5 Turbo` ๆ `gpt-4-turbo->my-gpt4=GPT 4 Turbo<128000:vision:fc>`
|
100
100
|
|
@@ -183,6 +183,13 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
183
183
|
- Default: -
|
184
184
|
- Example: `sk-xxxxxx...xxxxxx`
|
185
185
|
|
186
|
+
### `DEEPSEEK_MODEL_LIST`
|
187
|
+
|
188
|
+
- Type: Optional
|
189
|
+
- Description: Used to control the model list, use `+` to add a model, use `-` to hide a model, use `model_name=displayName` to customize the display name of a model, separated by commas. Definition syntax rules see [model-list][model-list]
|
190
|
+
- Default: `-`
|
191
|
+
- Example: `-all,+deepseek-reasoner`
|
192
|
+
|
186
193
|
## XAI
|
187
194
|
|
188
195
|
### `XAI_API_KEY`
|
@@ -425,6 +432,13 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
425
432
|
- Default: `-`
|
426
433
|
- Example: `-all,+qwen-turbo-latest,+qwen-plus-latest`
|
427
434
|
|
435
|
+
### `QWEN_PROXY_URL`
|
436
|
+
|
437
|
+
- Type: Optional
|
438
|
+
- Description: If you manually configure the Qwen API proxy, you can use this configuration item to override the default Qwen API request base URL
|
439
|
+
- Default: `https://dashscope.aliyuncs.com/compatible-mode/v1`
|
440
|
+
- Example: `https://my-qwen-proxy.com/v1`
|
441
|
+
|
428
442
|
## Stepfun AI
|
429
443
|
|
430
444
|
### `STEPFUN_API_KEY`
|
@@ -555,9 +569,9 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
555
569
|
### `VOLCENGINE_MODEL_LIST`
|
556
570
|
|
557
571
|
- Type: Optional
|
558
|
-
- Description: Used to control the model list, use `+` to add a model, use `-` to hide a model, use `model_name=display_name` to customize the display name of a model, separated by commas. Definition syntax rules see [model-list][model-list]
|
572
|
+
- Description: Used to control the model list, use `+` to add a model, use `-` to hide a model, use `model_name->deploymentName=display_name` to customize the display name of a model, separated by commas. Definition syntax rules see [model-list][model-list]
|
559
573
|
- Default: `-`
|
560
|
-
- Example: `-all,+deepseek-r1-250120,+deepseek-v3-241226,+doubao-1-5-pro-256k-250115,+doubao-1-5-pro-32k-250115,+doubao-1-5-lite-32k-250115`
|
574
|
+
- Example: `-all,+deepseek-r1->deepseek-r1-250120,+deepseek-v3->deepseek-v3-241226,+doubao-1.5-pro-256k->doubao-1-5-pro-256k-250115,+doubao-1.5-pro-32k->doubao-1-5-pro-32k-250115,+doubao-1.5-lite-32k->doubao-1-5-lite-32k-250115`
|
561
575
|
|
562
576
|
|
563
577
|
[model-list]: /docs/self-hosting/advanced/model-list
|
@@ -181,6 +181,13 @@ LobeChat ๅจ้จ็ฝฒๆถๆไพไบไธฐๅฏ็ๆจกๅๆๅกๅ็ธๅ
ณ็็ฏๅขๅ้๏ผ
|
|
181
181
|
- ้ป่ฎคๅผ๏ผ-
|
182
182
|
- ็คบไพ๏ผ`sk-xxxxxx...xxxxxx`
|
183
183
|
|
184
|
+
### `DEEPSEEK_MODEL_LIST`
|
185
|
+
|
186
|
+
- ็ฑปๅ๏ผๅฏ้
|
187
|
+
- ๆ่ฟฐ๏ผ็จๆฅๆงๅถๆจกๅๅ่กจ๏ผไฝฟ็จ `+` ๅขๅ ไธไธชๆจกๅ๏ผไฝฟ็จ `-` ๆฅ้่ไธไธชๆจกๅ๏ผไฝฟ็จ `ๆจกๅๅ=ๅฑ็คบๅ<ๆฉๅฑ้
็ฝฎ>` ๆฅ่ชๅฎไนๆจกๅ็ๅฑ็คบๅ๏ผ็จ่ฑๆ้ๅท้ๅผใๆจกๅๅฎไน่ฏญๆณ่งๅ่ง [ๆจกๅๅ่กจ][model-list]
|
188
|
+
- ้ป่ฎคๅผ๏ผ`-`
|
189
|
+
- ็คบไพ๏ผ`-all,+deepseek-reasoner`
|
190
|
+
|
184
191
|
## XAI
|
185
192
|
|
186
193
|
### `XAI_API_KEY`
|
@@ -423,6 +430,13 @@ LobeChat ๅจ้จ็ฝฒๆถๆไพไบไธฐๅฏ็ๆจกๅๆๅกๅ็ธๅ
ณ็็ฏๅขๅ้๏ผ
|
|
423
430
|
- ้ป่ฎคๅผ๏ผ`-`
|
424
431
|
- ็คบไพ๏ผ`-all,+qwen-turbo-latest,+qwen-plus-latest`
|
425
432
|
|
433
|
+
### `QWEN_PROXY_URL`
|
434
|
+
|
435
|
+
- ็ฑปๅ๏ผๅฏ้
|
436
|
+
- ๆ่ฟฐ๏ผๅฆๆไฝ ๆๅจ้
็ฝฎไบ Qwen ๆฅๅฃไปฃ็๏ผๅฏไปฅไฝฟ็จๆญค้
็ฝฎ้กนๆฅ่ฆ็้ป่ฎค็ Qwen API ่ฏทๆฑๅบ็ก URL
|
437
|
+
- ้ป่ฎคๅผ๏ผ`https://dashscope.aliyuncs.com/compatible-mode/v1`
|
438
|
+
- ็คบไพ๏ผ`https://my-qwen-proxy.com/v1`
|
439
|
+
|
426
440
|
## Stepfun AI
|
427
441
|
|
428
442
|
### `STEPFUN_API_KEY`
|
@@ -553,8 +567,8 @@ LobeChat ๅจ้จ็ฝฒๆถๆไพไบไธฐๅฏ็ๆจกๅๆๅกๅ็ธๅ
ณ็็ฏๅขๅ้๏ผ
|
|
553
567
|
### `VOLCENGINE_MODEL_LIST`
|
554
568
|
|
555
569
|
- ็ฑปๅ๏ผๅฏ้
|
556
|
-
- ๆ่ฟฐ๏ผ็จๆฅๆงๅถๆจกๅๅ่กจ๏ผไฝฟ็จ `+` ๅขๅ ไธไธชๆจกๅ๏ผไฝฟ็จ `-` ๆฅ้่ไธไธชๆจกๅ๏ผไฝฟ็จ
|
570
|
+
- ๆ่ฟฐ๏ผ็จๆฅๆงๅถๆจกๅๅ่กจ๏ผไฝฟ็จ `+` ๅขๅ ไธไธชๆจกๅ๏ผไฝฟ็จ `-` ๆฅ้่ไธไธชๆจกๅ๏ผไฝฟ็จ `ๆจกๅๅ->้จ็ฝฒๅ=ๅฑ็คบๅ<ๆฉๅฑ้
็ฝฎ>` ๆฅ่ชๅฎไนๆจกๅ็ๅฑ็คบๅ๏ผ็จ่ฑๆ้ๅท้ๅผใๆจกๅๅฎไน่ฏญๆณ่งๅ่ง [ๆจกๅๅ่กจ][model-list]
|
557
571
|
- ้ป่ฎคๅผ๏ผ`-`
|
558
|
-
- ็คบไพ๏ผ`-all,+deepseek-r1-250120,+deepseek-v3-241226,+doubao-1-5-pro-256k-250115,+doubao-1-5-pro-32k-250115,+doubao-1-5-lite-32k-250115`
|
572
|
+
- ็คบไพ๏ผ`-all,+deepseek-r1->deepseek-r1-250120,+deepseek-v3->deepseek-v3-241226,+doubao-1.5-pro-256k->doubao-1-5-pro-256k-250115,+doubao-1.5-pro-32k->doubao-1-5-pro-32k-250115,+doubao-1.5-lite-32k->doubao-1-5-lite-32k-250115`
|
559
573
|
|
560
574
|
[model-list]: /zh/docs/self-hosting/advanced/model-list
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@lobehub/chat",
|
3
|
-
"version": "1.70.
|
3
|
+
"version": "1.70.5",
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
5
5
|
"keywords": [
|
6
6
|
"framework",
|
@@ -75,11 +75,19 @@ export const naive: CrawlImpl = async (url, { filterOptions }) => {
|
|
75
75
|
const type = res.headers.get('content-type');
|
76
76
|
|
77
77
|
if (type?.includes('application/json')) {
|
78
|
-
|
78
|
+
let content: string;
|
79
|
+
|
80
|
+
try {
|
81
|
+
const json = await res.clone().json();
|
82
|
+
content = JSON.stringify(json, null, 2);
|
83
|
+
} catch {
|
84
|
+
content = await res.text();
|
85
|
+
}
|
86
|
+
|
79
87
|
return {
|
80
|
-
content:
|
88
|
+
content: content,
|
81
89
|
contentType: 'json',
|
82
|
-
length:
|
90
|
+
length: content.length,
|
83
91
|
url,
|
84
92
|
} satisfies CrawlSuccessResult;
|
85
93
|
}
|
@@ -72,4 +72,10 @@ export const crawUrlRules: CrawlUrlRule[] = [
|
|
72
72
|
impls: ['jina'],
|
73
73
|
urlPattern: 'https://cvpr.thecvf.com(.*)',
|
74
74
|
},
|
75
|
+
// ้ฃไนฆ็จ jina
|
76
|
+
// https://github.com/lobehub/lobe-chat/issues/6879
|
77
|
+
{
|
78
|
+
impls: ['jina'],
|
79
|
+
urlPattern: 'https://(.*).feishu.cn/(.*)',
|
80
|
+
},
|
75
81
|
];
|
@@ -7,12 +7,12 @@ import { Moon, Sun } from 'lucide-react';
|
|
7
7
|
import { memo } from 'react';
|
8
8
|
|
9
9
|
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
10
|
-
import {
|
10
|
+
import { useGlobalStore } from '@/store/global';
|
11
11
|
import { mobileHeaderSticky } from '@/styles/mobileHeader';
|
12
12
|
|
13
13
|
const Header = memo(() => {
|
14
14
|
const theme = useTheme();
|
15
|
-
const switchThemeMode =
|
15
|
+
const switchThemeMode = useGlobalStore((s) => s.switchThemeMode);
|
16
16
|
|
17
17
|
return (
|
18
18
|
<MobileNavBar
|
@@ -12,8 +12,9 @@ import { FORM_STYLE } from '@/const/layoutTokens';
|
|
12
12
|
import { imageUrl } from '@/const/url';
|
13
13
|
import { Locales, localeOptions } from '@/locales/resources';
|
14
14
|
import { useGlobalStore } from '@/store/global';
|
15
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
15
16
|
import { useUserStore } from '@/store/user';
|
16
|
-
import { settingsSelectors
|
17
|
+
import { settingsSelectors } from '@/store/user/selectors';
|
17
18
|
|
18
19
|
import { ThemeSwatchesNeutral, ThemeSwatchesPrimary } from './ThemeSwatches';
|
19
20
|
|
@@ -24,8 +25,9 @@ const Theme = memo(() => {
|
|
24
25
|
|
25
26
|
const [form] = Form.useForm();
|
26
27
|
const settings = useUserStore(settingsSelectors.currentSettings, isEqual);
|
27
|
-
const themeMode =
|
28
|
-
const [
|
28
|
+
const themeMode = useGlobalStore(systemStatusSelectors.themeMode);
|
29
|
+
const [setSettings] = useUserStore((s) => [s.setSettings]);
|
30
|
+
const [setThemeMode] = useGlobalStore((s) => [s.switchThemeMode]);
|
29
31
|
|
30
32
|
useSyncSettings(form);
|
31
33
|
const [switchLocale] = useGlobalStore((s) => [s.switchLocale]);
|
@@ -104,6 +104,10 @@ const CustomLogo = memo<LobeChatProps>(({ extra, size = 32, className, style, ty
|
|
104
104
|
|
105
105
|
break;
|
106
106
|
}
|
107
|
+
default: {
|
108
|
+
logoComponent = <CustomImageLogo size={size} style={style} {...rest} />;
|
109
|
+
break;
|
110
|
+
}
|
107
111
|
}
|
108
112
|
|
109
113
|
if (!extra) return logoComponent;
|
@@ -326,10 +326,17 @@ const OpenRouter: ModelProviderCard = {
|
|
326
326
|
modelList: { showModelFetcher: true },
|
327
327
|
modelsUrl: 'https://openrouter.ai/models',
|
328
328
|
name: 'OpenRouter',
|
329
|
+
proxyUrl: {
|
330
|
+
placeholder: 'https://openrouter.ai/api/v1',
|
331
|
+
},
|
329
332
|
settings: {
|
330
333
|
// OpenRouter don't support browser request
|
331
334
|
// https://github.com/lobehub/lobe-chat/issues/5900
|
332
335
|
disableBrowserRequest: true,
|
336
|
+
|
337
|
+
proxyUrl: {
|
338
|
+
placeholder: 'https://openrouter.ai/api/v1',
|
339
|
+
},
|
333
340
|
sdkType: 'openai',
|
334
341
|
searchMode: 'params',
|
335
342
|
showModelFetcher: true,
|
@@ -51,7 +51,7 @@ export const userSettings = pgTable('user_settings', {
|
|
51
51
|
});
|
52
52
|
export type UserSettingsItem = typeof userSettings.$inferSelect;
|
53
53
|
|
54
|
-
export const
|
54
|
+
export const userInstalledPlugins = pgTable(
|
55
55
|
'user_installed_plugins',
|
56
56
|
{
|
57
57
|
userId: text('user_id')
|
@@ -71,5 +71,5 @@ export const installedPlugins = pgTable(
|
|
71
71
|
}),
|
72
72
|
);
|
73
73
|
|
74
|
-
export type NewInstalledPlugin = typeof
|
75
|
-
export type InstalledPluginItem = typeof
|
74
|
+
export type NewInstalledPlugin = typeof userInstalledPlugins.$inferInsert;
|
75
|
+
export type InstalledPluginItem = typeof userInstalledPlugins.$inferSelect;
|
@@ -3,7 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
3
3
|
|
4
4
|
import { getTestDBInstance } from '@/database/server/core/dbForTest';
|
5
5
|
|
6
|
-
import { NewInstalledPlugin,
|
6
|
+
import { NewInstalledPlugin, userInstalledPlugins, users } from '../../../schemas';
|
7
7
|
import { PluginModel } from '../plugin';
|
8
8
|
|
9
9
|
let serverDB = await getTestDBInstance();
|
@@ -44,7 +44,7 @@ describe('PluginModel', () => {
|
|
44
44
|
|
45
45
|
describe('delete', () => {
|
46
46
|
it('should delete an installed plugin by identifier', async () => {
|
47
|
-
await serverDB.insert(
|
47
|
+
await serverDB.insert(userInstalledPlugins).values({
|
48
48
|
userId,
|
49
49
|
type: 'plugin',
|
50
50
|
identifier: 'test-plugin',
|
@@ -53,14 +53,14 @@ describe('PluginModel', () => {
|
|
53
53
|
|
54
54
|
await pluginModel.delete('test-plugin');
|
55
55
|
|
56
|
-
const result = await serverDB.select().from(
|
56
|
+
const result = await serverDB.select().from(userInstalledPlugins);
|
57
57
|
expect(result).toHaveLength(0);
|
58
58
|
});
|
59
59
|
});
|
60
60
|
|
61
61
|
describe('deleteAll', () => {
|
62
62
|
it('should delete all installed plugins for the user', async () => {
|
63
|
-
await serverDB.insert(
|
63
|
+
await serverDB.insert(userInstalledPlugins).values([
|
64
64
|
{
|
65
65
|
userId,
|
66
66
|
type: 'plugin',
|
@@ -83,7 +83,7 @@ describe('PluginModel', () => {
|
|
83
83
|
|
84
84
|
await pluginModel.deleteAll();
|
85
85
|
|
86
|
-
const result = await serverDB.select().from(
|
86
|
+
const result = await serverDB.select().from(userInstalledPlugins);
|
87
87
|
expect(result).toHaveLength(1);
|
88
88
|
expect(result[0].userId).toBe('456');
|
89
89
|
});
|
@@ -91,7 +91,7 @@ describe('PluginModel', () => {
|
|
91
91
|
|
92
92
|
describe('query', () => {
|
93
93
|
it('should query installed plugins for the user', async () => {
|
94
|
-
await serverDB.insert(
|
94
|
+
await serverDB.insert(userInstalledPlugins).values([
|
95
95
|
{
|
96
96
|
userId,
|
97
97
|
type: 'plugin',
|
@@ -125,7 +125,7 @@ describe('PluginModel', () => {
|
|
125
125
|
|
126
126
|
describe('findById', () => {
|
127
127
|
it('should find an installed plugin by identifier', async () => {
|
128
|
-
await serverDB.insert(
|
128
|
+
await serverDB.insert(userInstalledPlugins).values([
|
129
129
|
{
|
130
130
|
userId,
|
131
131
|
type: 'plugin',
|
@@ -149,7 +149,7 @@ describe('PluginModel', () => {
|
|
149
149
|
|
150
150
|
describe('update', () => {
|
151
151
|
it('should update an installed plugin', async () => {
|
152
|
-
await serverDB.insert(
|
152
|
+
await serverDB.insert(userInstalledPlugins).values({
|
153
153
|
userId,
|
154
154
|
type: 'plugin',
|
155
155
|
identifier: 'test-plugin',
|
@@ -2,7 +2,7 @@ import { and, desc, eq } from 'drizzle-orm/expressions';
|
|
2
2
|
|
3
3
|
import { LobeChatDatabase } from '@/database/type';
|
4
4
|
|
5
|
-
import { InstalledPluginItem, NewInstalledPlugin,
|
5
|
+
import { InstalledPluginItem, NewInstalledPlugin, userInstalledPlugins } from '../../schemas';
|
6
6
|
|
7
7
|
export class PluginModel {
|
8
8
|
private userId: string;
|
@@ -17,11 +17,11 @@ export class PluginModel {
|
|
17
17
|
params: Pick<NewInstalledPlugin, 'type' | 'identifier' | 'manifest' | 'customParams'>,
|
18
18
|
) => {
|
19
19
|
const [result] = await this.db
|
20
|
-
.insert(
|
20
|
+
.insert(userInstalledPlugins)
|
21
21
|
.values({ ...params, createdAt: new Date(), updatedAt: new Date(), userId: this.userId })
|
22
22
|
.onConflictDoUpdate({
|
23
23
|
set: { ...params, updatedAt: new Date() },
|
24
|
-
target: [
|
24
|
+
target: [userInstalledPlugins.identifier, userInstalledPlugins.userId],
|
25
25
|
})
|
26
26
|
.returning();
|
27
27
|
|
@@ -30,40 +30,40 @@ export class PluginModel {
|
|
30
30
|
|
31
31
|
delete = async (id: string) => {
|
32
32
|
return this.db
|
33
|
-
.delete(
|
34
|
-
.where(and(eq(
|
33
|
+
.delete(userInstalledPlugins)
|
34
|
+
.where(and(eq(userInstalledPlugins.identifier, id), eq(userInstalledPlugins.userId, this.userId)));
|
35
35
|
};
|
36
36
|
|
37
37
|
deleteAll = async () => {
|
38
|
-
return this.db.delete(
|
38
|
+
return this.db.delete(userInstalledPlugins).where(eq(userInstalledPlugins.userId, this.userId));
|
39
39
|
};
|
40
40
|
|
41
41
|
query = async () => {
|
42
42
|
return this.db
|
43
43
|
.select({
|
44
|
-
createdAt:
|
45
|
-
customParams:
|
46
|
-
identifier:
|
47
|
-
manifest:
|
48
|
-
settings:
|
49
|
-
type:
|
50
|
-
updatedAt:
|
44
|
+
createdAt: userInstalledPlugins.createdAt,
|
45
|
+
customParams: userInstalledPlugins.customParams,
|
46
|
+
identifier: userInstalledPlugins.identifier,
|
47
|
+
manifest: userInstalledPlugins.manifest,
|
48
|
+
settings: userInstalledPlugins.settings,
|
49
|
+
type: userInstalledPlugins.type,
|
50
|
+
updatedAt: userInstalledPlugins.updatedAt,
|
51
51
|
})
|
52
|
-
.from(
|
53
|
-
.where(eq(
|
54
|
-
.orderBy(desc(
|
52
|
+
.from(userInstalledPlugins)
|
53
|
+
.where(eq(userInstalledPlugins.userId, this.userId))
|
54
|
+
.orderBy(desc(userInstalledPlugins.createdAt));
|
55
55
|
};
|
56
56
|
|
57
57
|
findById = async (id: string) => {
|
58
|
-
return this.db.query.
|
59
|
-
where: and(eq(
|
58
|
+
return this.db.query.userInstalledPlugins.findFirst({
|
59
|
+
where: and(eq(userInstalledPlugins.identifier, id), eq(userInstalledPlugins.userId, this.userId)),
|
60
60
|
});
|
61
61
|
};
|
62
62
|
|
63
63
|
update = async (id: string, value: Partial<InstalledPluginItem>) => {
|
64
64
|
return this.db
|
65
|
-
.update(
|
65
|
+
.update(userInstalledPlugins)
|
66
66
|
.set({ ...value, updatedAt: new Date() })
|
67
|
-
.where(and(eq(
|
67
|
+
.where(and(eq(userInstalledPlugins.identifier, id), eq(userInstalledPlugins.userId, this.userId)));
|
68
68
|
};
|
69
69
|
}
|
@@ -6,8 +6,8 @@ import { memo, useMemo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
|
8
8
|
import Menu, { type MenuProps } from '@/components/Menu';
|
9
|
-
import {
|
10
|
-
import {
|
9
|
+
import { useGlobalStore } from '@/store/global';
|
10
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
11
11
|
|
12
12
|
const themeIcons = {
|
13
13
|
auto: Monitor,
|
@@ -17,8 +17,8 @@ const themeIcons = {
|
|
17
17
|
|
18
18
|
const ThemeButton = memo<{ placement?: PopoverProps['placement'] }>(({ placement = 'right' }) => {
|
19
19
|
const theme = useTheme();
|
20
|
-
const [themeMode, switchThemeMode] =
|
21
|
-
|
20
|
+
const [themeMode, switchThemeMode] = useGlobalStore((s) => [
|
21
|
+
systemStatusSelectors.themeMode(s),
|
22
22
|
s.switchThemeMode,
|
23
23
|
]);
|
24
24
|
|
@@ -14,11 +14,9 @@ import Link from 'next/link';
|
|
14
14
|
import { ReactNode, memo, useEffect } from 'react';
|
15
15
|
|
16
16
|
import AntdStaticMethods from '@/components/AntdStaticMethods';
|
17
|
-
import {
|
18
|
-
|
19
|
-
|
20
|
-
LOBE_THEME_PRIMARY_COLOR,
|
21
|
-
} from '@/const/theme';
|
17
|
+
import { LOBE_THEME_NEUTRAL_COLOR, LOBE_THEME_PRIMARY_COLOR } from '@/const/theme';
|
18
|
+
import { useGlobalStore } from '@/store/global';
|
19
|
+
import { systemStatusSelectors } from '@/store/global/selectors';
|
22
20
|
import { useUserStore } from '@/store/user';
|
23
21
|
import { userGeneralSettingsSelectors } from '@/store/user/selectors';
|
24
22
|
import { GlobalStyle } from '@/styles';
|
@@ -103,7 +101,7 @@ const AppTheme = memo<AppThemeProps>(
|
|
103
101
|
// console.debug('server:appearance', defaultAppearance);
|
104
102
|
// console.debug('server:primaryColor', defaultPrimaryColor);
|
105
103
|
// console.debug('server:neutralColor', defaultNeutralColor);
|
106
|
-
const themeMode =
|
104
|
+
const themeMode = useGlobalStore(systemStatusSelectors.themeMode);
|
107
105
|
const { styles, cx, theme } = useStyles();
|
108
106
|
const [primaryColor, neutralColor] = useUserStore((s) => [
|
109
107
|
userGeneralSettingsSelectors.primaryColor(s),
|
@@ -120,15 +118,13 @@ const AppTheme = memo<AppThemeProps>(
|
|
120
118
|
|
121
119
|
return (
|
122
120
|
<ThemeProvider
|
121
|
+
appearance={themeMode !== 'auto' ? themeMode : undefined}
|
123
122
|
className={cx(styles.app, styles.scrollbar, styles.scrollbarPolyfill)}
|
124
123
|
customTheme={{
|
125
124
|
neutralColor: neutralColor ?? defaultNeutralColor,
|
126
125
|
primaryColor: primaryColor ?? defaultPrimaryColor,
|
127
126
|
}}
|
128
127
|
defaultAppearance={defaultAppearance}
|
129
|
-
onAppearanceChange={(appearance) => {
|
130
|
-
setCookie(LOBE_THEME_APPEARANCE, appearance);
|
131
|
-
}}
|
132
128
|
theme={{
|
133
129
|
cssVar: true,
|
134
130
|
token: {
|
@@ -30,7 +30,7 @@ beforeEach(() => {
|
|
30
30
|
});
|
31
31
|
|
32
32
|
afterEach(() => {
|
33
|
-
vi.
|
33
|
+
vi.restoreAllMocks();
|
34
34
|
});
|
35
35
|
|
36
36
|
describe('LobeOpenRouterAI', () => {
|
@@ -40,6 +40,15 @@ describe('LobeOpenRouterAI', () => {
|
|
40
40
|
expect(instance).toBeInstanceOf(LobeOpenRouterAI);
|
41
41
|
expect(instance.baseURL).toEqual(defaultBaseURL);
|
42
42
|
});
|
43
|
+
|
44
|
+
it('should correctly initialize with a custom base URL', async () => {
|
45
|
+
const instance = new LobeOpenRouterAI({
|
46
|
+
apiKey: 'test_api_key',
|
47
|
+
baseURL: 'https://api.abc.com/v1',
|
48
|
+
});
|
49
|
+
expect(instance).toBeInstanceOf(LobeOpenRouterAI);
|
50
|
+
expect(instance.baseURL).toEqual('https://api.abc.com/v1');
|
51
|
+
});
|
43
52
|
});
|
44
53
|
|
45
54
|
describe('chat', () => {
|