@lobehub/chat 0.116.4 → 0.117.0
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/.env.example +10 -3
- package/CHANGELOG.md +58 -0
- package/docs/Deployment/Environment-Variable.md +20 -1
- package/docs/Deployment/Environment-Variable.zh-CN.md +21 -0
- package/package.json +1 -1
- package/src/app/api/plugin/gateway/route.ts +8 -2
- package/src/app/api/plugin/gateway/settings.test.ts +105 -0
- package/src/app/api/plugin/gateway/settings.ts +29 -0
- package/src/app/chat/features/TopicListContent/TopicSearchBar/index.tsx +1 -1
- package/src/config/server.ts +4 -0
- package/src/features/ChatInput/useChatInput.ts +6 -2
package/.env.example
CHANGED
|
@@ -37,9 +37,16 @@ OPENAI_API_KEY=sk-xxxxxxxxx
|
|
|
37
37
|
############ Market Service ############
|
|
38
38
|
########################################
|
|
39
39
|
|
|
40
|
-
# The LobeChat plugins market index url
|
|
41
|
-
# PLUGINS_INDEX_URL=https://chat-plugins.lobehub.com
|
|
42
|
-
|
|
43
40
|
# The LobeChat agents market index url
|
|
44
41
|
# AGENTS_INDEX_URL=https://chat-agents.lobehub.com
|
|
45
42
|
|
|
43
|
+
########################################
|
|
44
|
+
############ Plugin Service ############
|
|
45
|
+
########################################
|
|
46
|
+
|
|
47
|
+
# The LobeChat plugins store index url
|
|
48
|
+
# PLUGINS_INDEX_URL=https://chat-plugins.lobehub.com
|
|
49
|
+
|
|
50
|
+
# set the plugin settings
|
|
51
|
+
# the format is `plugin-identifier:key1=value1;key2=value2`, multiple settings fields are separated by semicolons `;`, multiple plugin settings are separated by commas `,`.
|
|
52
|
+
# PLUGIN_SETTINGS=search-engine:SERPAPI_API_KEY=xxxxx
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,64 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.117.0](https://github.com/lobehub/lobe-chat/compare/v0.116.5...v0.117.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-12-27**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Support plugin settings env.
|
|
12
|
+
|
|
13
|
+
#### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
- **misc**: Improve topic search experience.
|
|
16
|
+
|
|
17
|
+
<br/>
|
|
18
|
+
|
|
19
|
+
<details>
|
|
20
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
21
|
+
|
|
22
|
+
#### What's improved
|
|
23
|
+
|
|
24
|
+
- **misc**: Support plugin settings env, closes [#821](https://github.com/lobehub/lobe-chat/issues/821) ([efd9dc9](https://github.com/lobehub/lobe-chat/commit/efd9dc9))
|
|
25
|
+
|
|
26
|
+
#### What's fixed
|
|
27
|
+
|
|
28
|
+
- **misc**: Improve topic search experience, closes [#828](https://github.com/lobehub/lobe-chat/issues/828) ([ad55f1c](https://github.com/lobehub/lobe-chat/commit/ad55f1c))
|
|
29
|
+
|
|
30
|
+
</details>
|
|
31
|
+
|
|
32
|
+
<div align="right">
|
|
33
|
+
|
|
34
|
+
[](#readme-top)
|
|
35
|
+
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
### [Version 0.116.5](https://github.com/lobehub/lobe-chat/compare/v0.116.4...v0.116.5)
|
|
39
|
+
|
|
40
|
+
<sup>Released on **2023-12-27**</sup>
|
|
41
|
+
|
|
42
|
+
#### 🐛 Bug Fixes
|
|
43
|
+
|
|
44
|
+
- **misc**: Fix input box losing focus after sending a message on the desktop.
|
|
45
|
+
|
|
46
|
+
<br/>
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
50
|
+
|
|
51
|
+
#### What's fixed
|
|
52
|
+
|
|
53
|
+
- **misc**: Fix input box losing focus after sending a message on the desktop, closes [#830](https://github.com/lobehub/lobe-chat/issues/830) ([d491af9](https://github.com/lobehub/lobe-chat/commit/d491af9))
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
<div align="right">
|
|
58
|
+
|
|
59
|
+
[](#readme-top)
|
|
60
|
+
|
|
61
|
+
</div>
|
|
62
|
+
|
|
5
63
|
### [Version 0.116.4](https://github.com/lobehub/lobe-chat/compare/v0.116.3...v0.116.4)
|
|
6
64
|
|
|
7
65
|
<sup>Released on **2023-12-26**</sup>
|
|
@@ -93,7 +93,26 @@ If you need to use Azure OpenAI to provide model services, you can refer to the
|
|
|
93
93
|
- Description: The index address of the LobeChat plugin market. If you have deployed the plugin market service yourself, you can use this variable to override the default plugin market address
|
|
94
94
|
- Default: `https://chat-plugins.lobehub.com`
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
### `PLUGINS_SETTINGS`
|
|
97
|
+
|
|
98
|
+
- Type: Optional
|
|
99
|
+
- Description: Used to set the plugin settings, the format is `plugin-identifier:key1=value1;key2=value2`, multiple settings fields are separated by semicolons `;`, multiple plugin settings are separated by commas `,`.
|
|
100
|
+
- Default: `-`
|
|
101
|
+
- Example:`search-engine:SERPAPI_API_KEY=xxxxx,plugin-2:key1=value1;key2=value2`
|
|
102
|
+
|
|
103
|
+
The above example adds `search-engine` plugin settings, and sets the `SERPAPI_API_KEY` of the `search-engine` plugin to `xxxxx`, and sets the `key1` of the `plugin-2` plugin to `value1`, and `key2` to `value2`. The generated plugin settings configuration is as follows:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"plugin-2": {
|
|
108
|
+
"key1": "value1",
|
|
109
|
+
"key2": "value2"
|
|
110
|
+
},
|
|
111
|
+
"search-engine": {
|
|
112
|
+
"SERPAPI_API_KEY": "xxxxx"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
97
116
|
|
|
98
117
|
## Agent Service
|
|
99
118
|
|
|
@@ -91,6 +91,27 @@ LobeChat 在部署时提供了一些额外的配置项,使用环境变量进
|
|
|
91
91
|
- 描述:LobeChat 插件市场的索引地址,如果你自行部署了插件市场的服务,可以使用该变量来覆盖默认的插件市场地址
|
|
92
92
|
- 默认值:`https://chat-plugins.lobehub.com`
|
|
93
93
|
|
|
94
|
+
### `PLUGINS_SETTINGS`
|
|
95
|
+
|
|
96
|
+
- 类型:可选
|
|
97
|
+
- 描述:用于配置插件的设置,使用 `插件名:设置字段=设置值` 的格式来配置插件的设置,多个设置字段用英文分号 `;` 隔开,多个插件设置使用英文逗号`,`隔开。
|
|
98
|
+
- 默认值:`-`
|
|
99
|
+
- 示例:`search-engine:SERPAPI_API_KEY=xxxxx,plugin-2:key1=value1;key2=value2`
|
|
100
|
+
|
|
101
|
+
上述示例表示设置 `search-engine` 插件的 `SERPAPI_API_KEY` 为 `xxxxx`,设置 `plugin-2` 的 `key1` 为 `value1`,`key2` 为 `value2`。生成的插件设置配置如下:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"plugin-2": {
|
|
106
|
+
"key1": "value1",
|
|
107
|
+
"key2": "value2"
|
|
108
|
+
},
|
|
109
|
+
"search-engine": {
|
|
110
|
+
"SERPAPI_API_KEY": "xxxxx"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
94
115
|
## 角色服务
|
|
95
116
|
|
|
96
117
|
### `AGENTS_INDEX_URL`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.117.0",
|
|
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",
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { createGatewayOnEdgeRuntime } from '@lobehub/chat-plugins-gateway';
|
|
2
2
|
|
|
3
|
+
import { parserPluginSettings } from '@/app/api/plugin/gateway/settings';
|
|
3
4
|
import { getServerConfig } from '@/config/server';
|
|
4
5
|
|
|
5
|
-
const pluginsIndexUrl = getServerConfig()
|
|
6
|
+
const { PLUGINS_INDEX_URL: pluginsIndexUrl, PLUGIN_SETTINGS } = getServerConfig();
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
const defaultPluginSettings = parserPluginSettings(PLUGIN_SETTINGS);
|
|
9
|
+
|
|
10
|
+
export const POST = createGatewayOnEdgeRuntime({
|
|
11
|
+
defaultPluginSettings,
|
|
12
|
+
pluginsIndexUrl,
|
|
13
|
+
});
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { parserPluginSettings } from './settings';
|
|
4
|
+
|
|
5
|
+
describe('parserPluginSettings', () => {
|
|
6
|
+
it('should return an empty object when input is undefined', () => {
|
|
7
|
+
expect(parserPluginSettings()).toEqual({});
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('should return an empty object when input is an empty string', () => {
|
|
11
|
+
expect(parserPluginSettings('')).toEqual({});
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should parse plugin settings from a well-formed string', () => {
|
|
15
|
+
const input = 'plugin1:key1=value1;key2=value2,plugin2:key3=value3';
|
|
16
|
+
const expected = {
|
|
17
|
+
plugin1: { key1: 'value1', key2: 'value2' },
|
|
18
|
+
plugin2: { key3: 'value3' },
|
|
19
|
+
};
|
|
20
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should handle strings with Chinese commas', () => {
|
|
24
|
+
const input = 'plugin1:key1=value1;key2=value2,plugin2:key3=value3';
|
|
25
|
+
const expected = {
|
|
26
|
+
plugin1: { key1: 'value1', key2: 'value2' },
|
|
27
|
+
plugin2: { key3: 'value3' },
|
|
28
|
+
};
|
|
29
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should ignore empty segments', () => {
|
|
33
|
+
const input = 'plugin1:key1=value1;key2=value2,,,plugin2:key3=value3';
|
|
34
|
+
const expected = {
|
|
35
|
+
plugin1: { key1: 'value1', key2: 'value2' },
|
|
36
|
+
plugin2: { key3: 'value3' },
|
|
37
|
+
};
|
|
38
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should merge settings for the same pluginId', () => {
|
|
42
|
+
const input = 'plugin1:key1=value1;key2=value2,plugin1:key3=value3;key4=value4';
|
|
43
|
+
const expected = {
|
|
44
|
+
plugin1: { key1: 'value1', key2: 'value2', key3: 'value3', key4: 'value4' },
|
|
45
|
+
};
|
|
46
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should override previous values if the same key appears again for the same pluginId', () => {
|
|
50
|
+
const input = 'plugin1:key1=value1;key2=value2,plugin1:key2=newValue2;key3=value3';
|
|
51
|
+
const expected = {
|
|
52
|
+
plugin1: { key1: 'value1', key2: 'newValue2', key3: 'value3' },
|
|
53
|
+
};
|
|
54
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('error senses', () => {
|
|
58
|
+
it('should ignore settings with incorrect key-value format', () => {
|
|
59
|
+
const input = 'plugin1:key1=value1;incorrectFormat,plugin2:key2=value2';
|
|
60
|
+
const expected = {
|
|
61
|
+
plugin1: { key1: 'value1' },
|
|
62
|
+
plugin2: { key2: 'value2' },
|
|
63
|
+
};
|
|
64
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should handle extra separators gracefully', () => {
|
|
68
|
+
const input = 'plugin1:key1=value1==value1.1;key2=value2;,plugin2:key3=value3';
|
|
69
|
+
const expected = {
|
|
70
|
+
plugin1: { key1: 'value1', key2: 'value2' },
|
|
71
|
+
plugin2: { key3: 'value3' },
|
|
72
|
+
};
|
|
73
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should ignore settings with empty keys or values', () => {
|
|
77
|
+
const input = 'plugin1:=value1;key2=,plugin2:key3=value3';
|
|
78
|
+
const expected = {
|
|
79
|
+
plugin2: { key3: 'value3' },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should ignore leading and trailing whitespace in keys and values', () => {
|
|
86
|
+
const input = ' plugin1 : key1 = value1 ; key2 = value2 , plugin2 : key3=value3 ';
|
|
87
|
+
const expected = {
|
|
88
|
+
plugin1: { key1: 'value1', key2: 'value2' },
|
|
89
|
+
plugin2: { key3: 'value3' },
|
|
90
|
+
};
|
|
91
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should handle special characters in keys and values', () => {
|
|
95
|
+
const input = 'plugin1:key1=value1+special;key2=value2#special,plugin2:key3=value3/special';
|
|
96
|
+
const expected = {
|
|
97
|
+
plugin1: { key1: 'value1+special', key2: 'value2#special' },
|
|
98
|
+
plugin2: { key3: 'value3/special' },
|
|
99
|
+
};
|
|
100
|
+
expect(parserPluginSettings(input)).toEqual(expected);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// ... (additional test cases as needed)
|
|
104
|
+
});
|
|
105
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const parserPluginSettings = (
|
|
2
|
+
settingsStr?: string,
|
|
3
|
+
): Record<string, Record<string, string>> => {
|
|
4
|
+
if (!settingsStr) return {};
|
|
5
|
+
|
|
6
|
+
const settings = new Map();
|
|
7
|
+
|
|
8
|
+
const array = settingsStr.split(/[,,]/).filter(Boolean);
|
|
9
|
+
|
|
10
|
+
for (const item of array) {
|
|
11
|
+
const [id, pluginSettingsStr] = item.split(':');
|
|
12
|
+
if (!id) continue;
|
|
13
|
+
|
|
14
|
+
const pluginSettingItems = pluginSettingsStr.split(';');
|
|
15
|
+
|
|
16
|
+
const cleanId = id.trim();
|
|
17
|
+
|
|
18
|
+
for (const item of pluginSettingItems) {
|
|
19
|
+
const [key, value] = item.split('=');
|
|
20
|
+
if (!key || !value) continue;
|
|
21
|
+
const cleanKey = key.trim();
|
|
22
|
+
const cleanValue = value.trim();
|
|
23
|
+
|
|
24
|
+
settings.set(cleanId, { ...settings.get(cleanId), [cleanKey]: cleanValue });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return Object.fromEntries(settings.entries());
|
|
29
|
+
};
|
|
@@ -8,7 +8,7 @@ import { useChatStore } from '@/store/chat';
|
|
|
8
8
|
const TopicSearchBar = memo<{ onClear?: () => void }>(({ onClear }) => {
|
|
9
9
|
const { t } = useTranslation('chat');
|
|
10
10
|
|
|
11
|
-
const [keywords, setKeywords] = useState
|
|
11
|
+
const [keywords, setKeywords] = useState('');
|
|
12
12
|
const { mobile } = useResponsive();
|
|
13
13
|
|
|
14
14
|
const useSearchTopics = useChatStore((s) => s.useSearchTopics);
|
package/src/config/server.ts
CHANGED
|
@@ -17,7 +17,9 @@ declare global {
|
|
|
17
17
|
IMGUR_CLIENT_ID?: string;
|
|
18
18
|
|
|
19
19
|
AGENTS_INDEX_URL?: string;
|
|
20
|
+
|
|
20
21
|
PLUGINS_INDEX_URL?: string;
|
|
22
|
+
PLUGIN_SETTINGS?: string;
|
|
21
23
|
}
|
|
22
24
|
}
|
|
23
25
|
}
|
|
@@ -62,5 +64,7 @@ export const getServerConfig = () => {
|
|
|
62
64
|
PLUGINS_INDEX_URL: !!process.env.PLUGINS_INDEX_URL
|
|
63
65
|
? process.env.PLUGINS_INDEX_URL
|
|
64
66
|
: 'https://chat-plugins.lobehub.com',
|
|
67
|
+
|
|
68
|
+
PLUGIN_SETTINGS: process.env.PLUGIN_SETTINGS,
|
|
65
69
|
};
|
|
66
70
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { TextAreaRef } from 'antd/es/input/TextArea';
|
|
2
2
|
import { useCallback, useRef, useState } from 'react';
|
|
3
3
|
|
|
4
|
+
import { useIsMobile } from '@/hooks/useIsMobile';
|
|
4
5
|
import { useChatStore } from '@/store/chat';
|
|
5
6
|
import { useGlobalStore } from '@/store/global';
|
|
6
7
|
import { useSessionStore } from '@/store/session';
|
|
@@ -23,12 +24,15 @@ export const useChatInput = () => {
|
|
|
23
24
|
s.updateInputMessage,
|
|
24
25
|
s.stopGenerateMessage,
|
|
25
26
|
]);
|
|
27
|
+
const mobile = useIsMobile();
|
|
26
28
|
|
|
27
29
|
const handleSend = useCallback(() => {
|
|
28
30
|
setExpand(false);
|
|
29
|
-
|
|
31
|
+
if (mobile) {
|
|
32
|
+
ref?.current?.blur();
|
|
33
|
+
}
|
|
30
34
|
onSend();
|
|
31
|
-
}, [onSend]);
|
|
35
|
+
}, [onSend, mobile]);
|
|
32
36
|
|
|
33
37
|
return {
|
|
34
38
|
canUpload,
|