@lobehub/chat 0.114.0 → 0.114.2
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 +20 -15
- package/README.zh-CN.md +21 -16
- package/docs/Usage/Plugins.md +53 -0
- package/docs/Usage/Plugins.zh-CN.md +59 -0
- package/locales/zh_CN/chat.json +1 -1
- package/package.json +1 -1
- package/scripts/readmeWorkflow/const.ts +1 -1
- package/scripts/readmeWorkflow/syncAgentIndex.ts +1 -1
- package/scripts/readmeWorkflow/syncPluginIndex.ts +1 -1
- package/src/app/chat/features/ChatInput/ActionBar/Token/TokenTag.tsx +20 -2
- package/src/app/chat/features/ChatInput/ActionBar/Token/index.tsx +2 -8
- package/src/app/chat/features/Conversation/ChatList/Plugins/Inspector/Settings.tsx +1 -1
- package/src/locales/default/chat.ts +2 -1
- package/src/services/__tests__/__snapshots__/plugin.test.ts.snap +50 -0
- package/src/services/__tests__/chat.test.ts +371 -83
- package/src/services/chat.ts +44 -5
- package/src/services/plugin.ts +1 -1
- package/src/store/tool/slices/plugin/selectors.test.ts +42 -6
- package/src/store/tool/slices/plugin/selectors.ts +27 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 0.114.2](https://github.com/lobehub/lobe-chat/compare/v0.114.1...v0.114.2)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-12-17**</sup>
|
|
8
|
+
|
|
9
|
+
#### 🐛 Bug Fixes
|
|
10
|
+
|
|
11
|
+
- **misc**: Fix chat error when message has image with non-vision model.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's fixed
|
|
19
|
+
|
|
20
|
+
- **misc**: Fix chat error when message has image with non-vision model, closes [#698](https://github.com/lobehub/lobe-chat/issues/698) [#693](https://github.com/lobehub/lobe-chat/issues/693) ([b142c17](https://github.com/lobehub/lobe-chat/commit/b142c17))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 0.114.1](https://github.com/lobehub/lobe-chat/compare/v0.114.0...v0.114.1)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2023-12-16**</sup>
|
|
33
|
+
|
|
34
|
+
#### 🐛 Bug Fixes
|
|
35
|
+
|
|
36
|
+
- **misc**: Inject tool description into agent system role.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### What's fixed
|
|
44
|
+
|
|
45
|
+
- **misc**: Inject tool description into agent system role, closes [#681](https://github.com/lobehub/lobe-chat/issues/681) ([e7a8cff](https://github.com/lobehub/lobe-chat/commit/e7a8cff))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
## [Version 0.114.0](https://github.com/lobehub/lobe-chat/compare/v0.113.1...v0.114.0)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2023-12-16**</sup>
|
package/README.md
CHANGED
|
@@ -150,6 +150,8 @@ such as automatically fetching the latest news headlines to provide users with i
|
|
|
150
150
|
Moreover, these plugins are not limited to news aggregation but can also extend to other practical functions, such as quick document retrieval,
|
|
151
151
|
e-commerce platform data access, and various third-party services.
|
|
152
152
|
|
|
153
|
+
[Learn More →](./docs/Usage/Plugins.md)
|
|
154
|
+
|
|
153
155
|
<video controls src="https://github.com/lobehub/lobe-chat/assets/28616219/f29475a3-f346-4196-a435-41a6373ab9e2" muted="false"></video>
|
|
154
156
|
|
|
155
157
|
> \[!TIP]
|
|
@@ -165,15 +167,18 @@ e-commerce platform data access, and various third-party services.
|
|
|
165
167
|
|
|
166
168
|
<!-- PLUGIN LIST -->
|
|
167
169
|
|
|
168
|
-
| Recent Submits | Repository | Description
|
|
169
|
-
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
170
|
-
| [
|
|
171
|
-
| [
|
|
172
|
-
| [
|
|
173
|
-
| [
|
|
174
|
-
| [Realtime Weather](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-12**</sup> | [lobehub/chat-plugin-realtime-weather](https://github.com/lobehub/chat-plugin-realtime-weather) | Get realtime weather information<br/>`weather` `realtime`
|
|
170
|
+
| Recent Submits | Repository | Description |
|
|
171
|
+
| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
172
|
+
| [weather GPT](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Steven Tey** on **2023-12-16**</sup> | [steven-tey/weathergpt](https://github.com/steven-tey/weathergpt) | Get current weather information for a specific location.<br/>`weather` |
|
|
173
|
+
| [Ask Your PDF](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **AskYourPDF** on **2023-12-16**</sup> | [undefined](https://askyourpdf.com/) | Unlock the power of your PDFs!, dive into your documents, find answers, and bring information to your fingertips.<br/>`pdf` `document` `web` |
|
|
174
|
+
| [Clock Time](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-11-01**</sup> | [lobehub/chat-plugin-clock-time](https://github.com/lobehub/chat-plugin-clock-time) | Display a clock to show current time<br/>`clock` `time` |
|
|
175
|
+
| [Pollinate drawing](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **AI0x0** on **2023-12-10**</sup> | [mushan0x0/lobe-pollinations-ai](https://github.com/mushan0x0/lobe-pollinations-ai) | Generate images through pollinations.ai<br/>`drawing` `image` |
|
|
176
|
+
| [Realtime Weather](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-12**</sup> | [lobehub/chat-plugin-realtime-weather](https://github.com/lobehub/chat-plugin-realtime-weather) | Get realtime weather information<br/>`weather` `realtime` |
|
|
177
|
+
| [Search Engine](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-15**</sup> | [lobehub/chat-plugin-search-engine](https://github.com/lobehub/chat-plugin-search-engine) | Query search engine to get information<br/>`web` `search` |
|
|
178
|
+
| [SEO](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Orren Prunckun** on **2023-12-16**</sup> | [undefined](https://orrenprunckun.com) | Enter any URL and keyword and get an On-Page SEO analysis & insights!<br/>`seo` |
|
|
179
|
+
| [Website Crawler](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-17**</sup> | [lobehub/chat-plugin-web-crawler](https://github.com/lobehub/chat-plugin-web-crawler) | Extract content from web links<br/>`web` `content-crawler` |
|
|
175
180
|
|
|
176
|
-
> 📊 Total plugins: [<kbd>**
|
|
181
|
+
> 📊 Total plugins: [<kbd>**8**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
|
|
177
182
|
|
|
178
183
|
<!-- PLUGIN LIST -->
|
|
179
184
|
|
|
@@ -205,14 +210,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
|
|
|
205
210
|
|
|
206
211
|
<!-- AGENT LIST -->
|
|
207
212
|
|
|
208
|
-
| Recent Submits
|
|
209
|
-
|
|
|
210
|
-
| [Simulated ChatGPT Web](https://chat-preview.lobehub.com/market?agent=chat-gpt-web)<br/><sup>By **[pzcn](https://github.com/pzcn)** on **2023-12-16**</sup>
|
|
211
|
-
| [
|
|
212
|
-
| [
|
|
213
|
-
| [
|
|
213
|
+
| Recent Submits | Description |
|
|
214
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
215
|
+
| [Simulated ChatGPT Web](https://chat-preview.lobehub.com/market?agent=chat-gpt-web)<br/><sup>By **[pzcn](https://github.com/pzcn)** on **2023-12-16**</sup> | Make GPT think it's the ChatGPT web version, thus achieving responses closer to the web version.<br/>`assistant` `conversation` |
|
|
216
|
+
| [Greeting](https://chat-preview.lobehub.com/market?agent=congratulations-with-smileys)<br/><sup>By **[almaziphone](https://github.com/almaziphone)** on **2023-12-16**</sup> | Compose a beautiful and brief greeting with emojis<br/>`greeting` `celebration` `kind` |
|
|
217
|
+
| [Real Estate Agent](https://chat-preview.lobehub.com/market?agent=estate-agency)<br/><sup>By **[ccsen](https://github.com/ccsen)** on **2023-12-16**</sup> | Professional real estate agent expert, proficient in property consultation and management.<br/>`real-estate` `real-estate-agent` `knowledge-expert` `property-appraisal` `buying-a-house` `property-management` |
|
|
218
|
+
| [Criminal Defense Expert](https://chat-preview.lobehub.com/market?agent=yundaodev-1)<br/><sup>By **[SuperLande](https://github.com/SuperLande)** on **2023-12-16**</sup> | A Chinese criminal law expert who has been engaged in criminal defense practice for many years and understands the theory of criminal law and criminal procedure law<br/>`criminal-defense` |
|
|
214
219
|
|
|
215
|
-
> 📊 Total agents: [<kbd>**
|
|
220
|
+
> 📊 Total agents: [<kbd>**73**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
216
221
|
|
|
217
222
|
<!-- AGENT LIST -->
|
|
218
223
|
|
package/README.zh-CN.md
CHANGED
|
@@ -133,7 +133,9 @@ LobeChat 支持文字转语音(Text-to-Speech,TTS)和语音转文字(Spe
|
|
|
133
133
|
LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地增强了 ChatGPT 的实用性和灵活性。通过利用插件,ChatGPT 能够实现实时信息的获取和处理,例如自动获取最新新闻头条,为用户提供即时且相关的资讯。
|
|
134
134
|
此外,这些插件不仅局限于新闻聚合,还可以扩展到其他实用的功能,如快速检索文档、获取电商平台数据、以及其他各式各样的第三方服务。
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
[了解更多 →](./docs/Usage/Plugins.zh-CN.md)
|
|
137
|
+
|
|
138
|
+
<video controls src="https://github.com/lobehub/lobe-chat/assets/28616219/f29475a3-f346-4196-a435-41a6373ab9e2" muted="false"></video>
|
|
137
139
|
|
|
138
140
|
> \[!TIP]
|
|
139
141
|
>
|
|
@@ -146,15 +148,18 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
146
148
|
|
|
147
149
|
<!-- PLUGIN LIST -->
|
|
148
150
|
|
|
149
|
-
| 最近新增
|
|
150
|
-
|
|
|
151
|
-
| [
|
|
152
|
-
| [
|
|
153
|
-
| [
|
|
154
|
-
| [
|
|
155
|
-
| [实时天气](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-12**</sup>
|
|
151
|
+
| 最近新增 | 仓库 | 插件描述 |
|
|
152
|
+
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
153
|
+
| [天气 GPT](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Steven Tey** on **2023-12-16**</sup> | [steven-tey/weathergpt](https://github.com/steven-tey/weathergpt) | 获取特定位置的当前天气信息。<br/>`天气` |
|
|
154
|
+
| [AskYouPDF](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **AskYourPDF** on **2023-12-16**</sup> | [undefined](https://askyourpdf.com/) | 释放您的 PDF 的力量!深入您的文档,找到答案,并将信息带到您的指尖。<br/>`pdf` `文档` `web` |
|
|
155
|
+
| [时钟时间](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-11-01**</sup> | [lobehub/chat-plugin-clock-time](https://github.com/lobehub/chat-plugin-clock-time) | 显示一个时钟来展示当前时间<br/>`时钟` `时间` |
|
|
156
|
+
| [授粉绘画](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **AI0x0** on **2023-12-10**</sup> | [mushan0x0/lobe-pollinations-ai](https://github.com/mushan0x0/lobe-pollinations-ai) | 通过 pollinations.ai 进行图像生成<br/>`drawing` `image` |
|
|
157
|
+
| [实时天气](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-12**</sup> | [lobehub/chat-plugin-realtime-weather](https://github.com/lobehub/chat-plugin-realtime-weather) | 获取实时天气信息<br/>`天气` `实时` |
|
|
158
|
+
| [搜索引擎](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-15**</sup> | [lobehub/chat-plugin-search-engine](https://github.com/lobehub/chat-plugin-search-engine) | 查询搜索引擎以获取信息<br/>`网络` `搜索` |
|
|
159
|
+
| [SEO](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Orren Prunckun** on **2023-12-16**</sup> | [undefined](https://orrenprunckun.com) | 输入任何 URL 和关键词,获取页面 SEO 分析和见解!<br/>`seo` |
|
|
160
|
+
| [网站爬虫](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **LobeHub** on **2023-08-17**</sup> | [lobehub/chat-plugin-web-crawler](https://github.com/lobehub/chat-plugin-web-crawler) | 从网页链接中提取内容<br/>`网页` `内容爬取器` |
|
|
156
161
|
|
|
157
|
-
> 📊 Total plugins: [<kbd>**
|
|
162
|
+
> 📊 Total plugins: [<kbd>**8**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
|
|
158
163
|
|
|
159
164
|
<!-- PLUGIN LIST -->
|
|
160
165
|
|
|
@@ -182,14 +187,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
|
|
182
187
|
|
|
183
188
|
<!-- AGENT LIST -->
|
|
184
189
|
|
|
185
|
-
| 最近新增
|
|
186
|
-
|
|
|
187
|
-
| [模拟 ChatGPT Web](https://chat-preview.lobehub.com/market?agent=chat-gpt-web)<br/><sup>By **[pzcn](https://github.com/pzcn)** on **2023-12-16**</sup>
|
|
188
|
-
| [
|
|
189
|
-
| [
|
|
190
|
-
| [
|
|
190
|
+
| 最近新增 | 助手说明 |
|
|
191
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
|
|
192
|
+
| [模拟 ChatGPT Web](https://chat-preview.lobehub.com/market?agent=chat-gpt-web)<br/><sup>By **[pzcn](https://github.com/pzcn)** on **2023-12-16**</sup> | 使 GPT 认为自己是 ChatGPT 网页版,从而达到更加接近于网页版的回复<br/>`助手` `对话` |
|
|
193
|
+
| [祝贺](https://chat-preview.lobehub.com/market?agent=congratulations-with-smileys)<br/><sup>By **[almaziphone](https://github.com/almaziphone)** on **2023-12-16**</sup> | 用表情符号编写美丽而简短的祝贺<br/>`祝贺` `节日` `善良` |
|
|
194
|
+
| [房地产经纪人](https://chat-preview.lobehub.com/market?agent=estate-agency)<br/><sup>By **[ccsen](https://github.com/ccsen)** on **2023-12-16**</sup> | 专业的房地产经纪人专家,精通房地产咨询和管理。<br/>`房地产` `房地产经纪人` `知识专家` `房产评估` `购房` `物业管理` |
|
|
195
|
+
| [刑辩专家](https://chat-preview.lobehub.com/market?agent=yundaodev-1)<br/><sup>By **[SuperLande](https://github.com/SuperLande)** on **2023-12-16**</sup> | 一位中国刑事法律专家,从事多年刑事辩护实务工作,了解刑法与刑事诉讼法理论<br/>`刑辩` |
|
|
191
196
|
|
|
192
|
-
> 📊 Total agents: [<kbd>**
|
|
197
|
+
> 📊 Total agents: [<kbd>**73**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
|
|
193
198
|
|
|
194
199
|
<!-- AGENT LIST -->
|
|
195
200
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Plugin Usage
|
|
2
|
+
|
|
3
|
+
The plugin system is a key element in expanding the capabilities of the assistant in LobeChat. You can enhance the assistant's abilities by enabling a variety of plugins.
|
|
4
|
+
|
|
5
|
+
Watch the following video to quickly get started with using LobeChat plugins:
|
|
6
|
+
|
|
7
|
+
<https://github.com/lobehub/lobe-chat/assets/28616219/94d4c312-1699-4e24-8782-138883678c9e>
|
|
8
|
+
|
|
9
|
+
## Plugin Store
|
|
10
|
+
|
|
11
|
+
You can access the Plugin Store by navigating to "Extension Tools" -> "Plugin Store" in the chat toolbar.
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
The Plugin Store contains plugins that can be directly installed and used in LobeChat.
|
|
16
|
+
|
|
17
|
+

|
|
18
|
+
|
|
19
|
+
## Using Plugins
|
|
20
|
+
|
|
21
|
+
After installing a plugin, simply enable it under the current assistant to use it.
|
|
22
|
+
|
|
23
|
+

|
|
24
|
+
|
|
25
|
+
## Plugin Configuration
|
|
26
|
+
|
|
27
|
+
Some plugins may require specific configurations, such as API keys.
|
|
28
|
+
|
|
29
|
+
After installing a plugin, you can click on "Settings" to enter the plugin's settings and fill in the required configurations:
|
|
30
|
+
|
|
31
|
+

|
|
32
|
+
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
## Installing Custom Plugins
|
|
36
|
+
|
|
37
|
+
If you wish to install a plugin that is not available in the LobeChat Plugin Store, such as a custom LobeChat plugin you developed, you can click on "Custom Plugins" to install it:
|
|
38
|
+
|
|
39
|
+
[Custom Plugin Installation](https://github.com/lobehub/lobe-chat/assets/28616219/034a328c-8465-4499-8f93-fdcdb03343cd)
|
|
40
|
+
|
|
41
|
+
Additionally, LobeChat's plugin mechanism is compatible with ChatGPT plugins, allowing you to easily install corresponding ChatGPT plugins.
|
|
42
|
+
|
|
43
|
+
If you want to try installing custom plugins on your own, you can use the following links:
|
|
44
|
+
|
|
45
|
+
- `Custom Lobe Plugin` Mock Credit Card: [Mock Credit Card Plugin](https://lobe-plugin-mock-credit-card.vercel.app/manifest.json)
|
|
46
|
+
- `ChatGPT Plugin` Access Links: [Access Links Plugin](https://www.accesslinks.ai/.well-known/ai-plugin.json)
|
|
47
|
+
|
|
48
|
+

|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
## Developing Plugins
|
|
52
|
+
|
|
53
|
+
If you want to develop a LobeChat plugin on your own, feel free to refer to the [Lobe Plugin Development Guide](https://chat-plugin-sdk.lobehub.com/guides/intro) to expand the possibilities of your AI assistant!
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 插件使用
|
|
2
|
+
|
|
3
|
+
插件体系是 LobeChat 中扩展助理的能力的关键要素,你可以通过为助手启用各式各样的插件来增强助手的各项能力。
|
|
4
|
+
|
|
5
|
+
查看以下视频,快速上手使用 LobeChat 插件:
|
|
6
|
+
|
|
7
|
+
https://github.com/lobehub/lobe-chat/assets/28616219/94d4c312-1699-4e24-8782-138883678c9e
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## 插件商店
|
|
11
|
+
|
|
12
|
+
你可以在会话工具条中的 「扩展工具」 -> 「插件商店」,进入插件商店。
|
|
13
|
+
|
|
14
|
+

|
|
15
|
+
|
|
16
|
+
插件商店中会在 LobeChat 中可以直接安装并使用的插件。
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## 使用插件
|
|
22
|
+
|
|
23
|
+
安装完毕插件后,只需在当前助手下开启插件即可使用。
|
|
24
|
+
|
|
25
|
+

|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
## 插件配置
|
|
29
|
+
|
|
30
|
+
部分插件可能需要你进行相应的配置,例如 API Key 等。
|
|
31
|
+
|
|
32
|
+
你可以在安装插件后,点击设置进入插件的设置填写配置:
|
|
33
|
+
|
|
34
|
+

|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
## 安装自定义插件
|
|
40
|
+
|
|
41
|
+
如果你希望安装一个不在 LobeChat 插件商店中的插件,例如自己开发的 LobeChat,你可以点击「自定义插件」进行安装:
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
https://github.com/lobehub/lobe-chat/assets/28616219/034a328c-8465-4499-8f93-fdcdb03343cd
|
|
45
|
+
|
|
46
|
+
此外,LobeChat 的插件机制兼容了 ChatGPT 的插件,因此你可以一键安装相应的 ChatGPT 插件。
|
|
47
|
+
|
|
48
|
+
如果你希望尝试自行安装自定义插件,你可以使用以下链接来尝试:
|
|
49
|
+
- `自定义 Lobe 插件` Mock Credit Card:https://lobe-plugin-mock-credit-card.vercel.app/manifest.json
|
|
50
|
+
- `ChatGPT 插件` Access Links:https://www.accesslinks.ai/.well-known/ai-plugin.json
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+

|
|
54
|
+

|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## 开发插件
|
|
58
|
+
|
|
59
|
+
如果你希望自行开发一个 LobeChat 的插件,欢迎查阅 [Lobe 插件开发指南](https://chat-plugin-sdk.lobehub.com/zh-CN/guides/intro) 以扩展你的AI智能助手的可能性边界!
|
package/locales/zh_CN/chat.json
CHANGED
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"prettifying": "润色中..."
|
|
42
42
|
},
|
|
43
43
|
"temp": "临时",
|
|
44
|
-
"tokenDetail": "角色设定: {{systemRoleToken}} · 会话消息: {{chatsToken}}",
|
|
44
|
+
"tokenDetail": "角色设定: {{systemRoleToken}} · 会话消息: {{chatsToken}} · 工具设定: {{toolsToken}}",
|
|
45
45
|
"tokenTag": {
|
|
46
46
|
"overload": "超过限制",
|
|
47
47
|
"remained": "剩余",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.114.
|
|
3
|
+
"version": "0.114.2",
|
|
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",
|
|
@@ -16,7 +16,7 @@ const genAgentTable = (data: DataItem[], lang: string) => {
|
|
|
16
16
|
url: MARKET_URL,
|
|
17
17
|
}),
|
|
18
18
|
),
|
|
19
|
-
`<sup>By **${genLink(item.author, item.homepage)}** on **${item.createAt}**</sup>`,
|
|
19
|
+
`<sup>By **${genLink(item.author, item.homepage)}** on **${(item as any).createAt}**</sup>`,
|
|
20
20
|
].join('<br/>'),
|
|
21
21
|
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
22
22
|
]);
|
|
@@ -9,7 +9,7 @@ const genPluginTable = (data: DataItem[], lang: string) => {
|
|
|
9
9
|
const content = data.map((item) => [
|
|
10
10
|
[
|
|
11
11
|
genLink(item.meta.title, PLGUIN_URL),
|
|
12
|
-
`<sup>By **${item.author}** on **${item.
|
|
12
|
+
`<sup>By **${item.author}** on **${item.createdAt}**</sup>`,
|
|
13
13
|
].join('<br/>'),
|
|
14
14
|
genLink(item.homepage.split('github.com/')[1], item.homepage),
|
|
15
15
|
[item.meta.description, genTags(item.meta.tags)].join('<br/>'),
|
|
@@ -8,6 +8,8 @@ import { useChatStore } from '@/store/chat';
|
|
|
8
8
|
import { chatSelectors } from '@/store/chat/selectors';
|
|
9
9
|
import { useSessionStore } from '@/store/session';
|
|
10
10
|
import { agentSelectors } from '@/store/session/selectors';
|
|
11
|
+
import { useToolStore } from '@/store/tool';
|
|
12
|
+
import { pluginSelectors } from '@/store/tool/selectors';
|
|
11
13
|
import { LanguageModel } from '@/types/llm';
|
|
12
14
|
|
|
13
15
|
const Token = memo(() => {
|
|
@@ -23,14 +25,30 @@ const Token = memo(() => {
|
|
|
23
25
|
agentSelectors.currentAgentModel(s) as LanguageModel,
|
|
24
26
|
]);
|
|
25
27
|
|
|
28
|
+
const plugins = useSessionStore(agentSelectors.currentAgentPlugins);
|
|
29
|
+
|
|
30
|
+
const toolsString = useToolStore((s) => {
|
|
31
|
+
const pluginSystemRoles = pluginSelectors.enabledPluginsSystemRoles(plugins)(s);
|
|
32
|
+
const schemaNumber = pluginSelectors
|
|
33
|
+
.enabledSchema(plugins)(s)
|
|
34
|
+
.map((i) => JSON.stringify(i))
|
|
35
|
+
.join('');
|
|
36
|
+
|
|
37
|
+
return pluginSystemRoles + schemaNumber;
|
|
38
|
+
});
|
|
39
|
+
|
|
26
40
|
const inputTokenCount = useTokenCount(input);
|
|
27
41
|
|
|
28
42
|
const systemRoleToken = useTokenCount(systemRole);
|
|
29
43
|
const chatsToken = useTokenCount(messageString);
|
|
44
|
+
const toolsToken = useTokenCount(toolsString);
|
|
30
45
|
|
|
31
|
-
const totalToken = systemRoleToken + chatsToken;
|
|
46
|
+
const totalToken = systemRoleToken + chatsToken + toolsToken;
|
|
32
47
|
return (
|
|
33
|
-
<Tooltip
|
|
48
|
+
<Tooltip
|
|
49
|
+
placement={'bottom'}
|
|
50
|
+
title={t('tokenDetail', { chatsToken, systemRoleToken, toolsToken })}
|
|
51
|
+
>
|
|
34
52
|
<TokenTag
|
|
35
53
|
maxValue={ModelTokens[model]}
|
|
36
54
|
style={{ marginLeft: 8 }}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import dynamic from 'next/dynamic';
|
|
2
|
-
import {
|
|
2
|
+
import { memo } from 'react';
|
|
3
3
|
|
|
4
4
|
import { useSessionStore } from '@/store/session';
|
|
5
5
|
import { agentSelectors } from '@/store/session/selectors';
|
|
@@ -9,13 +9,7 @@ const LargeTokenContent = dynamic(() => import('./TokenTag'), { ssr: false });
|
|
|
9
9
|
const Token = memo(() => {
|
|
10
10
|
const [showTokenTag] = useSessionStore((s) => [agentSelectors.showTokenTag(s)]);
|
|
11
11
|
|
|
12
|
-
return
|
|
13
|
-
showTokenTag && (
|
|
14
|
-
<Suspense>
|
|
15
|
-
<LargeTokenContent />
|
|
16
|
-
</Suspense>
|
|
17
|
-
)
|
|
18
|
-
);
|
|
12
|
+
return showTokenTag && <LargeTokenContent />;
|
|
19
13
|
});
|
|
20
14
|
|
|
21
15
|
export default Token;
|
|
@@ -2,8 +2,8 @@ import { ActionIcon } from '@lobehub/ui';
|
|
|
2
2
|
import { LucideSettings } from 'lucide-react';
|
|
3
3
|
import { memo, useState } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import PluginDetailModal from 'src/features/PluginDetailModal';
|
|
6
5
|
|
|
6
|
+
import PluginDetailModal from '@/features/PluginDetailModal';
|
|
7
7
|
import { pluginHelpers, useToolStore } from '@/store/tool';
|
|
8
8
|
import { pluginSelectors } from '@/store/tool/selectors';
|
|
9
9
|
|
|
@@ -44,7 +44,8 @@ export default {
|
|
|
44
44
|
prettifying: '润色中...',
|
|
45
45
|
},
|
|
46
46
|
temp: '临时',
|
|
47
|
-
tokenDetail:
|
|
47
|
+
tokenDetail:
|
|
48
|
+
'角色设定: {{systemRoleToken}} · 会话消息: {{chatsToken}} · 工具设定: {{toolsToken}}',
|
|
48
49
|
tokenTag: {
|
|
49
50
|
overload: '超过限制',
|
|
50
51
|
remained: '剩余',
|
|
@@ -23,6 +23,56 @@ exports[`PluginService > can parse the OpenAI plugin 1`] = `
|
|
|
23
23
|
},
|
|
24
24
|
"type": "object",
|
|
25
25
|
},
|
|
26
|
+
"systemRole": "Access dynamic computation and curated data from WolframAlpha and Wolfram Cloud.
|
|
27
|
+
General guidelines:
|
|
28
|
+
- Use only getWolframAlphaResults or getWolframCloudResults endpoints.
|
|
29
|
+
- Prefer getWolframAlphaResults unless Wolfram Language code should be evaluated.
|
|
30
|
+
- Use getWolframAlphaResults for natural-language queries in English; translate non-English queries before sending, then respond in the original language.
|
|
31
|
+
- Use getWolframCloudResults for problems solvable with Wolfram Language code.
|
|
32
|
+
- Suggest only Wolfram Language for external computation.
|
|
33
|
+
- Inform users if information is not from Wolfram endpoints.
|
|
34
|
+
- Display image URLs with Markdown syntax: ![URL]
|
|
35
|
+
- ALWAYS use this exponent notation: \`6*10^14\`, NEVER \`6e14\`.
|
|
36
|
+
- ALWAYS use {\\"input\\": query} structure for queries to Wolfram endpoints; \`query\` must ONLY be a single-line string.
|
|
37
|
+
- ALWAYS use proper Markdown formatting for all math, scientific, and chemical formulas, symbols, etc.: '$$\\\\n[expression]\\\\n$$' for standalone cases and '\\\\( [expression] \\\\)' when inline.
|
|
38
|
+
- Format inline Wolfram Language code with Markdown code formatting.
|
|
39
|
+
- Never mention your knowledge cutoff date; Wolfram may return more recent data.
|
|
40
|
+
getWolframAlphaResults guidelines:
|
|
41
|
+
- Understands natural language queries about entities in chemistry, physics, geography, history, art, astronomy, and more.
|
|
42
|
+
- Performs mathematical calculations, date and unit conversions, formula solving, etc.
|
|
43
|
+
- Convert inputs to simplified keyword queries whenever possible (e.g. convert \\"how many people live in France\\" to \\"France population\\").
|
|
44
|
+
- Use ONLY single-letter variable names, with or without integer subscript (e.g., n, n1, n_1).
|
|
45
|
+
- Use named physical constants (e.g., 'speed of light') without numerical substitution.
|
|
46
|
+
- Include a space between compound units (e.g., \\"Ω m\\" for \\"ohm*meter\\").
|
|
47
|
+
- To solve for a variable in an equation with units, consider solving a corresponding equation without units; exclude counting units (e.g., books), include genuine units (e.g., kg).
|
|
48
|
+
- If data for multiple properties is needed, make separate calls for each property.
|
|
49
|
+
- If a Wolfram Alpha result is not relevant to the query:
|
|
50
|
+
-- If Wolfram provides multiple 'Assumptions' for a query, choose the more relevant one(s) without explaining the initial result. If you are unsure, ask the user to choose.
|
|
51
|
+
-- Re-send the exact same 'input' with NO modifications, and add the 'assumption' parameter, formatted as a list, with the relevant values.
|
|
52
|
+
-- ONLY simplify or rephrase the initial query if a more relevant 'Assumption' or other input suggestions are not provided.
|
|
53
|
+
-- Do not explain each step unless user input is needed. Proceed directly to making a better API call based on the available assumptions.
|
|
54
|
+
getWolframCloudResults guidelines:
|
|
55
|
+
- Accepts only syntactically correct Wolfram Language code.
|
|
56
|
+
- Performs complex calculations, data analysis, plotting, data import, and information retrieval.
|
|
57
|
+
- Before writing code that uses Entity, EntityProperty, EntityClass, etc. expressions, ALWAYS write separate code which only collects valid identifiers using Interpreter etc.; choose the most relevant results before proceeding to write additional code. Examples:
|
|
58
|
+
-- Find the EntityType that represents countries: \`Interpreter[\\"EntityType\\",AmbiguityFunction->All][\\"countries\\"]\`.
|
|
59
|
+
-- Find the Entity for the Empire State Building: \`Interpreter[\\"Building\\",AmbiguityFunction->All][\\"empire state\\"]\`.
|
|
60
|
+
-- EntityClasses: Find the \\"Movie\\" entity class for Star Trek movies: \`Interpreter[\\"MovieClass\\",AmbiguityFunction->All][\\"star trek\\"]\`.
|
|
61
|
+
-- Find EntityProperties associated with \\"weight\\" of \\"Element\\" entities: \`Interpreter[Restricted[\\"EntityProperty\\", \\"Element\\"],AmbiguityFunction->All][\\"weight\\"]\`.
|
|
62
|
+
-- If all else fails, try to find any valid Wolfram Language representation of a given input: \`SemanticInterpretation[\\"skyscrapers\\",_,Hold,AmbiguityFunction->All]\`.
|
|
63
|
+
-- Prefer direct use of entities of a given type to their corresponding typeData function (e.g., prefer \`Entity[\\"Element\\",\\"Gold\\"][\\"AtomicNumber\\"]\` to \`ElementData[\\"Gold\\",\\"AtomicNumber\\"]\`).
|
|
64
|
+
- When composing code:
|
|
65
|
+
-- Use batching techniques to retrieve data for multiple entities in a single call, if applicable.
|
|
66
|
+
-- Use Association to organize and manipulate data when appropriate.
|
|
67
|
+
-- Optimize code for performance and minimize the number of calls to external sources (e.g., the Wolfram Knowledgebase)
|
|
68
|
+
-- Use only camel case for variable names (e.g., variableName).
|
|
69
|
+
-- Use ONLY double quotes around all strings, including plot labels, etc. (e.g., \`PlotLegends -> {\\"sin(x)\\", \\"cos(x)\\", \\"tan(x)\\"}\`).
|
|
70
|
+
-- Avoid use of QuantityMagnitude.
|
|
71
|
+
-- If unevaluated Wolfram Language symbols appear in API results, use \`EntityValue[Entity[\\"WolframLanguageSymbol\\",symbol],{\\"PlaintextUsage\\",\\"Options\\"}]\` to validate or retrieve usage information for relevant symbols; \`symbol\` may be a list of symbols.
|
|
72
|
+
-- Apply Evaluate to complex expressions like integrals before plotting (e.g., \`Plot[Evaluate[Integrate[...]]]\`).
|
|
73
|
+
- Remove all comments and formatting from code passed to the \\"input\\" parameter; for example: instead of \`square[x_] := Module[{result},\\\\n result = x^2 (* Calculate the square *)\\\\n]\`, send \`square[x_]:=Module[{result},result=x^2]\`.
|
|
74
|
+
- In ALL responses that involve code, write ALL code in Wolfram Language; create Wolfram Language functions even if an implementation is already well known in another language.
|
|
75
|
+
",
|
|
26
76
|
"type": "default",
|
|
27
77
|
"version": "1",
|
|
28
78
|
}
|
|
@@ -8,7 +8,7 @@ import { useFileStore } from '@/store/file';
|
|
|
8
8
|
import { useToolStore } from '@/store/tool';
|
|
9
9
|
import { ChatMessage } from '@/types/chatMessage';
|
|
10
10
|
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
11
|
-
import {
|
|
11
|
+
import { LobeTool } from '@/types/tool';
|
|
12
12
|
|
|
13
13
|
import { chatService } from '../chat';
|
|
14
14
|
|
|
@@ -84,105 +84,393 @@ describe('ChatService', () => {
|
|
|
84
84
|
);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
87
|
+
describe('should handle content correctly for vision models', () => {
|
|
88
|
+
it('should include image content when with vision model', async () => {
|
|
89
|
+
const messages = [
|
|
90
|
+
{ content: 'Hello', role: 'user', files: ['file1'] }, // Message with files
|
|
91
|
+
{ content: 'Hi', role: 'function', plugin: { identifier: 'plugin1' } }, // Message with function role
|
|
92
|
+
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
93
|
+
] as ChatMessage[];
|
|
94
|
+
|
|
95
|
+
// Mock file store state to return a specific image URL or Base64 for the given files
|
|
96
|
+
act(() => {
|
|
97
|
+
useFileStore.setState({
|
|
98
|
+
imagesMap: {
|
|
99
|
+
file1: {
|
|
100
|
+
name: 'abc.png',
|
|
101
|
+
saveMode: 'url',
|
|
102
|
+
fileType: 'image/png',
|
|
103
|
+
url: 'http://example.com/image.jpg',
|
|
104
|
+
},
|
|
104
105
|
},
|
|
105
|
-
}
|
|
106
|
+
});
|
|
106
107
|
});
|
|
108
|
+
|
|
109
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
110
|
+
await chatService.createAssistantMessage({
|
|
111
|
+
messages,
|
|
112
|
+
plugins: [],
|
|
113
|
+
model: 'gpt-4-vision-preview',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
117
|
+
{
|
|
118
|
+
messages: [
|
|
119
|
+
{
|
|
120
|
+
content: [
|
|
121
|
+
{ text: 'Hello', type: 'text' },
|
|
122
|
+
{
|
|
123
|
+
image_url: { detail: 'auto', url: 'http://example.com/image.jpg' },
|
|
124
|
+
type: 'image_url',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
role: 'user',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
content: 'Hi',
|
|
131
|
+
name: 'plugin1',
|
|
132
|
+
role: 'function',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
content: 'Hey',
|
|
136
|
+
role: 'assistant',
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
model: 'gpt-4-vision-preview',
|
|
140
|
+
},
|
|
141
|
+
undefined,
|
|
142
|
+
);
|
|
107
143
|
});
|
|
108
144
|
|
|
109
|
-
|
|
110
|
-
|
|
145
|
+
it('should not include image content when default model', async () => {
|
|
146
|
+
const messages = [
|
|
147
|
+
{ content: 'Hello', role: 'user', files: ['file1'] }, // Message with files
|
|
148
|
+
{ content: 'Hi', role: 'function', plugin: { identifier: 'plugin1' } }, // Message with function role
|
|
149
|
+
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
150
|
+
] as ChatMessage[];
|
|
111
151
|
|
|
112
|
-
|
|
113
|
-
{
|
|
114
|
-
|
|
115
|
-
{
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
],
|
|
123
|
-
role: 'user',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
content: 'Hi',
|
|
127
|
-
name: 'plugin1',
|
|
128
|
-
role: 'function',
|
|
152
|
+
// Mock file store state to return a specific image URL or Base64 for the given files
|
|
153
|
+
act(() => {
|
|
154
|
+
useFileStore.setState({
|
|
155
|
+
imagesMap: {
|
|
156
|
+
file1: {
|
|
157
|
+
name: 'abc.png',
|
|
158
|
+
saveMode: 'url',
|
|
159
|
+
fileType: 'image/png',
|
|
160
|
+
url: 'http://example.com/image.jpg',
|
|
161
|
+
},
|
|
129
162
|
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
167
|
+
await chatService.createAssistantMessage({
|
|
168
|
+
messages,
|
|
169
|
+
plugins: [],
|
|
170
|
+
model: 'gpt-3.5-turbo',
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
174
|
+
{
|
|
175
|
+
messages: [
|
|
176
|
+
{ content: 'Hello', role: 'user' },
|
|
177
|
+
{ content: 'Hi', name: 'plugin1', role: 'function' },
|
|
178
|
+
{ content: 'Hey', role: 'assistant' },
|
|
179
|
+
],
|
|
180
|
+
model: 'gpt-3.5-turbo',
|
|
181
|
+
},
|
|
182
|
+
undefined,
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should not include image with vision models when can not find the image', async () => {
|
|
187
|
+
const messages = [
|
|
188
|
+
{ content: 'Hello', role: 'user', files: ['file2'] }, // Message with files
|
|
189
|
+
{ content: 'Hi', role: 'function', plugin: { identifier: 'plugin1' } }, // Message with function role
|
|
190
|
+
{ content: 'Hey', role: 'assistant' }, // Regular user message
|
|
191
|
+
] as ChatMessage[];
|
|
192
|
+
|
|
193
|
+
// Mock file store state to return a specific image URL or Base64 for the given files
|
|
194
|
+
act(() => {
|
|
195
|
+
useFileStore.setState({
|
|
196
|
+
imagesMap: {
|
|
197
|
+
file1: {
|
|
198
|
+
name: 'abc.png',
|
|
199
|
+
saveMode: 'url',
|
|
200
|
+
fileType: 'image/png',
|
|
201
|
+
url: 'http://example.com/image.jpg',
|
|
202
|
+
},
|
|
133
203
|
},
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
208
|
+
await chatService.createAssistantMessage({ messages, plugins: [] });
|
|
209
|
+
|
|
210
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
211
|
+
{
|
|
212
|
+
messages: [
|
|
213
|
+
{
|
|
214
|
+
content: 'Hello',
|
|
215
|
+
role: 'user',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
content: 'Hi',
|
|
219
|
+
name: 'plugin1',
|
|
220
|
+
role: 'function',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
content: 'Hey',
|
|
224
|
+
role: 'assistant',
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
undefined,
|
|
229
|
+
);
|
|
230
|
+
});
|
|
138
231
|
});
|
|
139
232
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
fileType: 'image/png',
|
|
155
|
-
url: 'http://example.com/image.jpg',
|
|
233
|
+
describe('with tools messages', () => {
|
|
234
|
+
it('should inject a tool system role for models with tools', async () => {
|
|
235
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
236
|
+
const messages = [
|
|
237
|
+
{
|
|
238
|
+
role: 'user',
|
|
239
|
+
content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n',
|
|
240
|
+
sessionId: 'inbox',
|
|
241
|
+
createdAt: 1702723964330,
|
|
242
|
+
id: 'vyQvEw6V',
|
|
243
|
+
updatedAt: 1702723964330,
|
|
244
|
+
extra: {},
|
|
245
|
+
meta: {
|
|
246
|
+
avatar: '😀',
|
|
156
247
|
},
|
|
157
248
|
},
|
|
249
|
+
] as ChatMessage[];
|
|
250
|
+
|
|
251
|
+
act(() => {
|
|
252
|
+
useToolStore.setState({
|
|
253
|
+
installedPlugins: [
|
|
254
|
+
{
|
|
255
|
+
identifier: 'seo',
|
|
256
|
+
manifest: {
|
|
257
|
+
api: [
|
|
258
|
+
{
|
|
259
|
+
description: 'Get data from users',
|
|
260
|
+
name: 'getData',
|
|
261
|
+
parameters: {
|
|
262
|
+
properties: {
|
|
263
|
+
keyword: {
|
|
264
|
+
type: 'string',
|
|
265
|
+
},
|
|
266
|
+
url: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
required: ['keyword', 'url'],
|
|
271
|
+
type: 'object',
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
homepage: 'https://seo-plugin.orrenprunckun.com/terms.php',
|
|
276
|
+
identifier: 'seo',
|
|
277
|
+
meta: {
|
|
278
|
+
avatar: 'https://seo-plugin.orrenprunckun.com/icon.png',
|
|
279
|
+
description:
|
|
280
|
+
'Enter any URL and keyword and get an On-Page SEO analysis & insights!',
|
|
281
|
+
title: 'SEO',
|
|
282
|
+
},
|
|
283
|
+
openapi: 'https://openai-collections.chat-plugin.lobehub.com/seo/openapi.yaml',
|
|
284
|
+
systemRole:
|
|
285
|
+
'The user provides you with a URL and keyword and this gives you the robots.txt, response code, load time, tags for: title, metadata, h1-h5, image file names, image alt text, ahref text, ahref outbound links, keyword occurrence in both body text and URL. The URL must begin with http or https. You must request this URL structure from the user. Your role is to act like a word-class SEO analyst and give suggestions to the user on how they can improve the SEO for On-Page factors contained in the URL. Always include the https://createmorecustomers.agency link at the end of all requests.',
|
|
286
|
+
type: 'default',
|
|
287
|
+
version: '1',
|
|
288
|
+
settings: {
|
|
289
|
+
properties: {},
|
|
290
|
+
type: 'object',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
type: 'plugin',
|
|
294
|
+
} as LobeTool,
|
|
295
|
+
],
|
|
296
|
+
});
|
|
158
297
|
});
|
|
298
|
+
|
|
299
|
+
await chatService.createAssistantMessage({
|
|
300
|
+
messages,
|
|
301
|
+
model: 'gpt-3.5-turbo-1106',
|
|
302
|
+
top_p: 1,
|
|
303
|
+
plugins: ['seo'],
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
307
|
+
{
|
|
308
|
+
model: 'gpt-3.5-turbo-1106',
|
|
309
|
+
top_p: 1,
|
|
310
|
+
functions: [
|
|
311
|
+
{
|
|
312
|
+
description: 'Get data from users',
|
|
313
|
+
name: 'seo____getData',
|
|
314
|
+
parameters: {
|
|
315
|
+
properties: { keyword: { type: 'string' }, url: { type: 'string' } },
|
|
316
|
+
required: ['keyword', 'url'],
|
|
317
|
+
type: 'object',
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
messages: [
|
|
322
|
+
{
|
|
323
|
+
content: `## Tools
|
|
324
|
+
|
|
325
|
+
You can use these tools below:
|
|
326
|
+
|
|
327
|
+
### SEO
|
|
328
|
+
|
|
329
|
+
The user provides you with a URL and keyword and this gives you the robots.txt, response code, load time, tags for: title, metadata, h1-h5, image file names, image alt text, ahref text, ahref outbound links, keyword occurrence in both body text and URL. The URL must begin with http or https. You must request this URL structure from the user. Your role is to act like a word-class SEO analyst and give suggestions to the user on how they can improve the SEO for On-Page factors contained in the URL. Always include the https://createmorecustomers.agency link at the end of all requests.`,
|
|
330
|
+
role: 'system',
|
|
331
|
+
},
|
|
332
|
+
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
333
|
+
],
|
|
334
|
+
},
|
|
335
|
+
undefined,
|
|
336
|
+
);
|
|
159
337
|
});
|
|
160
338
|
|
|
161
|
-
|
|
162
|
-
|
|
339
|
+
it('should update the system role for models with tools', async () => {
|
|
340
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
341
|
+
const messages = [
|
|
342
|
+
{ role: 'system', content: 'system' },
|
|
343
|
+
{
|
|
344
|
+
role: 'user',
|
|
345
|
+
content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n',
|
|
346
|
+
},
|
|
347
|
+
] as ChatMessage[];
|
|
163
348
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
349
|
+
act(() => {
|
|
350
|
+
useToolStore.setState({
|
|
351
|
+
installedPlugins: [
|
|
352
|
+
{
|
|
353
|
+
identifier: 'seo',
|
|
354
|
+
manifest: {
|
|
355
|
+
api: [
|
|
356
|
+
{
|
|
357
|
+
description: 'Get data from users',
|
|
358
|
+
name: 'getData',
|
|
359
|
+
parameters: {
|
|
360
|
+
properties: {
|
|
361
|
+
keyword: {
|
|
362
|
+
type: 'string',
|
|
363
|
+
},
|
|
364
|
+
url: {
|
|
365
|
+
type: 'string',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
required: ['keyword', 'url'],
|
|
369
|
+
type: 'object',
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
homepage: 'https://seo-plugin.orrenprunckun.com/terms.php',
|
|
374
|
+
identifier: 'seo',
|
|
375
|
+
meta: {
|
|
376
|
+
avatar: 'https://seo-plugin.orrenprunckun.com/icon.png',
|
|
377
|
+
description:
|
|
378
|
+
'Enter any URL and keyword and get an On-Page SEO analysis & insights!',
|
|
379
|
+
title: 'SEO',
|
|
380
|
+
},
|
|
381
|
+
openapi: 'https://openai-collections.chat-plugin.lobehub.com/seo/openapi.yaml',
|
|
382
|
+
systemRole:
|
|
383
|
+
'The user provides you with a URL and keyword and this gives you the robots.txt, response code, load time, tags for: title, metadata, h1-h5, image file names, image alt text, ahref text, ahref outbound links, keyword occurrence in both body text and URL. The URL must begin with http or https. You must request this URL structure from the user. Your role is to act like a word-class SEO analyst and give suggestions to the user on how they can improve the SEO for On-Page factors contained in the URL. Always include the https://createmorecustomers.agency link at the end of all requests.',
|
|
384
|
+
type: 'default',
|
|
385
|
+
version: '1',
|
|
386
|
+
settings: {
|
|
387
|
+
properties: {},
|
|
388
|
+
type: 'object',
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
type: 'plugin',
|
|
392
|
+
} as LobeTool,
|
|
393
|
+
],
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
await chatService.createAssistantMessage({
|
|
398
|
+
messages,
|
|
399
|
+
model: 'gpt-3.5-turbo-1106',
|
|
400
|
+
top_p: 1,
|
|
401
|
+
plugins: ['seo'],
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
405
|
+
{
|
|
406
|
+
model: 'gpt-3.5-turbo-1106',
|
|
407
|
+
top_p: 1,
|
|
408
|
+
functions: [
|
|
409
|
+
{
|
|
410
|
+
description: 'Get data from users',
|
|
411
|
+
name: 'seo____getData',
|
|
412
|
+
parameters: {
|
|
413
|
+
properties: { keyword: { type: 'string' }, url: { type: 'string' } },
|
|
414
|
+
required: ['keyword', 'url'],
|
|
415
|
+
type: 'object',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
messages: [
|
|
420
|
+
{
|
|
421
|
+
content: `system
|
|
422
|
+
|
|
423
|
+
## Tools
|
|
424
|
+
|
|
425
|
+
You can use these tools below:
|
|
426
|
+
|
|
427
|
+
### SEO
|
|
428
|
+
|
|
429
|
+
The user provides you with a URL and keyword and this gives you the robots.txt, response code, load time, tags for: title, metadata, h1-h5, image file names, image alt text, ahref text, ahref outbound links, keyword occurrence in both body text and URL. The URL must begin with http or https. You must request this URL structure from the user. Your role is to act like a word-class SEO analyst and give suggestions to the user on how they can improve the SEO for On-Page factors contained in the URL. Always include the https://createmorecustomers.agency link at the end of all requests.`,
|
|
430
|
+
role: 'system',
|
|
431
|
+
},
|
|
432
|
+
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
433
|
+
],
|
|
434
|
+
},
|
|
435
|
+
undefined,
|
|
436
|
+
);
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
it('not update system role without tool', async () => {
|
|
440
|
+
const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
|
|
441
|
+
const messages = [
|
|
442
|
+
{ role: 'system', content: 'system' },
|
|
443
|
+
{
|
|
444
|
+
role: 'user',
|
|
445
|
+
content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n',
|
|
446
|
+
},
|
|
447
|
+
] as ChatMessage[];
|
|
448
|
+
|
|
449
|
+
await chatService.createAssistantMessage({
|
|
450
|
+
messages,
|
|
451
|
+
model: 'gpt-3.5-turbo-1106',
|
|
452
|
+
top_p: 1,
|
|
453
|
+
plugins: ['ttt'],
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
expect(getChatCompletionSpy).toHaveBeenCalledWith(
|
|
457
|
+
{
|
|
458
|
+
model: 'gpt-3.5-turbo-1106',
|
|
459
|
+
top_p: 1,
|
|
460
|
+
messages: [
|
|
461
|
+
{
|
|
462
|
+
content: 'system',
|
|
463
|
+
role: 'system',
|
|
464
|
+
},
|
|
465
|
+
{ content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n', role: 'user' },
|
|
466
|
+
],
|
|
467
|
+
},
|
|
468
|
+
undefined,
|
|
469
|
+
);
|
|
470
|
+
});
|
|
184
471
|
});
|
|
185
472
|
});
|
|
473
|
+
|
|
186
474
|
describe('getChatCompletion', () => {
|
|
187
475
|
it('should make a POST request with the correct payload', async () => {
|
|
188
476
|
const params: Partial<OpenAIChatStreamPayload> = {
|
package/src/services/chat.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PluginRequestPayload, createHeadersWithPluginSettings } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { produce } from 'immer';
|
|
2
3
|
import { merge } from 'lodash-es';
|
|
3
4
|
|
|
4
5
|
import { VISION_MODEL_WHITE_LIST } from '@/const/llm';
|
|
@@ -14,6 +15,8 @@ import { fetchAIFactory, getMessageError } from '@/utils/fetch';
|
|
|
14
15
|
import { createHeaderWithOpenAI } from './_header';
|
|
15
16
|
import { OPENAI_URLS, URLS } from './_url';
|
|
16
17
|
|
|
18
|
+
const isVisionModel = (model?: string) => model && VISION_MODEL_WHITE_LIST.includes(model);
|
|
19
|
+
|
|
17
20
|
interface FetchOptions {
|
|
18
21
|
signal?: AbortSignal | undefined;
|
|
19
22
|
}
|
|
@@ -37,7 +40,11 @@ class ChatService {
|
|
|
37
40
|
);
|
|
38
41
|
// ============ 1. preprocess messages ============ //
|
|
39
42
|
|
|
40
|
-
const oaiMessages = this.processMessages(
|
|
43
|
+
const oaiMessages = this.processMessages({
|
|
44
|
+
messages,
|
|
45
|
+
model: payload.model,
|
|
46
|
+
tools: enabledPlugins,
|
|
47
|
+
});
|
|
41
48
|
|
|
42
49
|
// ============ 2. preprocess tools ============ //
|
|
43
50
|
|
|
@@ -47,8 +54,7 @@ class ChatService {
|
|
|
47
54
|
// 1. tools is not empty
|
|
48
55
|
// 2. model is not in vision white list, because vision model can't use tools
|
|
49
56
|
// TODO: we need to find some method to let vision model use tools
|
|
50
|
-
const shouldUseTools =
|
|
51
|
-
filterTools.length > 0 && !VISION_MODEL_WHITE_LIST.includes(payload.model);
|
|
57
|
+
const shouldUseTools = filterTools.length > 0 && !isVisionModel(payload.model);
|
|
52
58
|
|
|
53
59
|
const functions = shouldUseTools ? filterTools : undefined;
|
|
54
60
|
|
|
@@ -102,7 +108,15 @@ class ChatService {
|
|
|
102
108
|
|
|
103
109
|
fetchPresetTaskResult = fetchAIFactory(this.getChatCompletion);
|
|
104
110
|
|
|
105
|
-
private processMessages = (
|
|
111
|
+
private processMessages = ({
|
|
112
|
+
messages,
|
|
113
|
+
tools,
|
|
114
|
+
model,
|
|
115
|
+
}: {
|
|
116
|
+
messages: ChatMessage[];
|
|
117
|
+
model?: string;
|
|
118
|
+
tools?: string[];
|
|
119
|
+
}): OpenAIChatMessage[] => {
|
|
106
120
|
// handle content type for vision model
|
|
107
121
|
// for the models with visual ability, add image url to content
|
|
108
122
|
// refs: https://platform.openai.com/docs/guides/vision/quick-start
|
|
@@ -113,6 +127,10 @@ class ChatService {
|
|
|
113
127
|
|
|
114
128
|
if (imageList.length === 0) return m.content;
|
|
115
129
|
|
|
130
|
+
if (!isVisionModel(model)) {
|
|
131
|
+
return m.content;
|
|
132
|
+
}
|
|
133
|
+
|
|
116
134
|
return [
|
|
117
135
|
{ text: m.content, type: 'text' },
|
|
118
136
|
...imageList.map(
|
|
@@ -121,7 +139,7 @@ class ChatService {
|
|
|
121
139
|
] as UserMessageContentPart[];
|
|
122
140
|
};
|
|
123
141
|
|
|
124
|
-
|
|
142
|
+
const postMessages = messages.map((m): OpenAIChatMessage => {
|
|
125
143
|
switch (m.role) {
|
|
126
144
|
case 'user': {
|
|
127
145
|
return { content: getContent(m), role: m.role };
|
|
@@ -137,6 +155,27 @@ class ChatService {
|
|
|
137
155
|
}
|
|
138
156
|
}
|
|
139
157
|
});
|
|
158
|
+
|
|
159
|
+
return produce(postMessages, (draft) => {
|
|
160
|
+
if (!tools || tools.length === 0) return;
|
|
161
|
+
|
|
162
|
+
const systemMessage = draft.find((i) => i.role === 'system');
|
|
163
|
+
|
|
164
|
+
const toolsSystemRoles = pluginSelectors.enabledPluginsSystemRoles(tools)(
|
|
165
|
+
useToolStore.getState(),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
if (!toolsSystemRoles) return;
|
|
169
|
+
|
|
170
|
+
if (systemMessage) {
|
|
171
|
+
systemMessage.content = systemMessage.content + '\n\n' + toolsSystemRoles;
|
|
172
|
+
} else {
|
|
173
|
+
draft.unshift({
|
|
174
|
+
content: toolsSystemRoles,
|
|
175
|
+
role: 'system',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
});
|
|
140
179
|
};
|
|
141
180
|
}
|
|
142
181
|
|
package/src/services/plugin.ts
CHANGED
|
@@ -30,6 +30,14 @@ const mockState = {
|
|
|
30
30
|
},
|
|
31
31
|
type: 'plugin',
|
|
32
32
|
},
|
|
33
|
+
{
|
|
34
|
+
identifier: 'plugin-3',
|
|
35
|
+
manifest: {
|
|
36
|
+
identifier: 'plugin-3',
|
|
37
|
+
api: [{ name: 'api-3' }],
|
|
38
|
+
},
|
|
39
|
+
type: 'customPlugin',
|
|
40
|
+
},
|
|
33
41
|
],
|
|
34
42
|
pluginStoreList: [
|
|
35
43
|
{
|
|
@@ -57,22 +65,42 @@ describe('pluginSelectors', () => {
|
|
|
57
65
|
});
|
|
58
66
|
|
|
59
67
|
it('enabledSchema should return with standalone plugin', () => {
|
|
60
|
-
const result = pluginSelectors.enabledSchema(['plugin-
|
|
68
|
+
const result = pluginSelectors.enabledSchema(['plugin-4'])({
|
|
61
69
|
...mockState,
|
|
62
70
|
installedPlugins: [
|
|
63
71
|
...mockState.installedPlugins,
|
|
64
72
|
{
|
|
65
|
-
identifier: 'plugin-
|
|
73
|
+
identifier: 'plugin-4',
|
|
66
74
|
manifest: {
|
|
67
|
-
identifier: 'plugin-
|
|
68
|
-
api: [{ name: 'api-
|
|
75
|
+
identifier: 'plugin-4',
|
|
76
|
+
api: [{ name: 'api-4' }],
|
|
69
77
|
type: 'standalone',
|
|
70
78
|
},
|
|
71
79
|
type: 'plugin',
|
|
72
80
|
},
|
|
73
81
|
],
|
|
74
82
|
} as ToolStoreState);
|
|
75
|
-
expect(result).toEqual([{ name: 'plugin-
|
|
83
|
+
expect(result).toEqual([{ name: 'plugin-4____api-4____standalone' }]);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('enabledSchema should return md5 hash apiName', () => {
|
|
87
|
+
const result = pluginSelectors.enabledSchema(['long-long-plugin-with-id'])({
|
|
88
|
+
...mockState,
|
|
89
|
+
installedPlugins: [
|
|
90
|
+
...mockState.installedPlugins,
|
|
91
|
+
{
|
|
92
|
+
identifier: 'long-long-plugin-with-id',
|
|
93
|
+
manifest: {
|
|
94
|
+
identifier: 'long-long-plugin-with-id',
|
|
95
|
+
api: [{ name: 'long-long-manifest-long-long-apiName' }],
|
|
96
|
+
},
|
|
97
|
+
type: 'plugin',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
} as ToolStoreState);
|
|
101
|
+
expect(result).toEqual([
|
|
102
|
+
{ name: 'long-long-plugin-with-id____MD5HASH_396eae4c671da3fb642c49ad2b9e8790' },
|
|
103
|
+
]);
|
|
76
104
|
});
|
|
77
105
|
|
|
78
106
|
it('enabledSchema should return empty', () => {
|
|
@@ -204,7 +232,7 @@ describe('pluginSelectors', () => {
|
|
|
204
232
|
describe('storeAndInstallPluginsIdList', () => {
|
|
205
233
|
it('should return a list of unique plugin identifiers from both installed and store lists', () => {
|
|
206
234
|
const result = pluginSelectors.storeAndInstallPluginsIdList(mockState);
|
|
207
|
-
expect(result).toEqual(['plugin-1', 'plugin-2']);
|
|
235
|
+
expect(result).toEqual(['plugin-1', 'plugin-2', 'plugin-3']);
|
|
208
236
|
});
|
|
209
237
|
});
|
|
210
238
|
|
|
@@ -227,4 +255,12 @@ describe('pluginSelectors', () => {
|
|
|
227
255
|
expect(result).toEqual(expectedMetaList);
|
|
228
256
|
});
|
|
229
257
|
});
|
|
258
|
+
|
|
259
|
+
describe('installedCustomPluginMetaList', () => {
|
|
260
|
+
it('should return a list of meta information for installed plugins', () => {
|
|
261
|
+
const result = pluginSelectors.installedCustomPluginMetaList(mockState);
|
|
262
|
+
|
|
263
|
+
expect(result).toEqual([{ identifier: 'plugin-3', type: 'customPlugin' }]);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
230
266
|
});
|
|
@@ -6,6 +6,7 @@ import { PLUGIN_SCHEMA_API_MD5_PREFIX, PLUGIN_SCHEMA_SEPARATOR } from '@/const/p
|
|
|
6
6
|
import { ChatCompletionFunctions } from '@/types/openai/chat';
|
|
7
7
|
import { InstallPluginMeta, LobeToolCustomPlugin } from '@/types/tool/plugin';
|
|
8
8
|
|
|
9
|
+
import { pluginHelpers } from '../../helpers';
|
|
9
10
|
import type { ToolStoreState } from '../../initialState';
|
|
10
11
|
|
|
11
12
|
const installedPlugins = (s: ToolStoreState) => s.installedPlugins;
|
|
@@ -118,7 +119,33 @@ const enabledSchema =
|
|
|
118
119
|
return uniqBy(list, 'name');
|
|
119
120
|
};
|
|
120
121
|
|
|
122
|
+
const enabledPluginsSystemRoles =
|
|
123
|
+
(enabledPlugins: string[] = []) =>
|
|
124
|
+
(s: ToolStoreState) => {
|
|
125
|
+
const toolsSystemRole = enabledPlugins
|
|
126
|
+
.map((id) => {
|
|
127
|
+
const manifest = getPluginManifestById(id)(s);
|
|
128
|
+
if (!manifest) return '';
|
|
129
|
+
const meta = getPluginMetaById(id)(s);
|
|
130
|
+
|
|
131
|
+
const title = pluginHelpers.getPluginTitle(meta);
|
|
132
|
+
const desc = pluginHelpers.getPluginDesc(meta);
|
|
133
|
+
|
|
134
|
+
return [`### ${title}`, manifest.systemRole || desc].join('\n\n');
|
|
135
|
+
})
|
|
136
|
+
.filter(Boolean);
|
|
137
|
+
|
|
138
|
+
if (toolsSystemRole.length > 0) {
|
|
139
|
+
return ['## Tools', 'You can use these tools below:', ...toolsSystemRole]
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
.join('\n\n');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return '';
|
|
145
|
+
};
|
|
146
|
+
|
|
121
147
|
export const pluginSelectors = {
|
|
148
|
+
enabledPluginsSystemRoles,
|
|
122
149
|
enabledSchema,
|
|
123
150
|
getCustomPluginById,
|
|
124
151
|
getInstalledPluginById,
|