@lobehub/chat 0.93.0 → 0.94.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/CHANGELOG.md +25 -0
- package/docs/Deploy-with-Azure-OpenAI.md +3 -3
- package/locales/en_US/chat.json +10 -10
- package/locales/en_US/setting.json +7 -0
- package/locales/ja_JP/setting.json +7 -0
- package/locales/ko_KR/setting.json +7 -0
- package/locales/ru_RU/common.json +2 -1
- package/locales/ru_RU/setting.json +7 -0
- package/locales/zh_CN/setting.json +7 -0
- package/locales/zh_TW/setting.json +7 -0
- package/package.json +5 -4
- package/scripts/i18nWorkflow/utils.ts +4 -1
- package/src/app/chat/features/TopicListContent/SystemRole/index.tsx +4 -1
- package/src/app/chat/settings/(desktop)/{Header/Desktop.tsx → features/Header.tsx} +4 -3
- package/src/app/chat/settings/(desktop)/layout.desktop.tsx +1 -1
- package/src/app/chat/settings/{components/Header/Mobile.tsx → (mobile)/features/Header.tsx} +4 -3
- package/src/app/chat/settings/(mobile)/layout.mobile.tsx +1 -1
- package/src/app/chat/settings/{components/Header/index.tsx → features/HeaderContent.tsx} +11 -3
- package/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx +86 -0
- package/src/app/chat/settings/features/SubmitAgentButton/index.tsx +37 -0
- package/src/app/chat/settings/features/SubmitAgentButton/style.ts +46 -0
- package/src/app/market/features/AgentSearchBar/index.tsx +6 -3
- package/src/chains/pickEmoji.ts +9 -12
- package/src/chains/summaryAgentName.ts +7 -12
- package/src/chains/summaryDescription.ts +14 -3
- package/src/chains/summaryTags.ts +27 -0
- package/src/components/HotKeys/index.tsx +2 -2
- package/src/components/ResponsiveIndex/index.tsx +4 -2
- package/src/const/url.ts +1 -0
- package/src/features/AgentInfo/index.tsx +69 -0
- package/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx +48 -0
- package/src/features/AgentSetting/AgentMeta/index.tsx +16 -5
- package/src/features/AgentSetting/store/action.ts +62 -10
- package/src/locales/default/chat.ts +0 -1
- package/src/locales/default/setting.ts +8 -0
- package/src/locales/resources/index.ts +0 -1
- package/src/store/session/slices/session/reducers/session.ts +1 -1
- package/src/app/chat/settings/(desktop)/Header/index.tsx +0 -13
- package/src/app/chat/settings/(mobile)/Header.tsx +0 -12
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## [Version 0.94.0](https://github.com/lobehub/lobe-chat/compare/v0.93.0...v0.94.0)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2023-10-18**</sup>
|
|
8
|
+
|
|
9
|
+
#### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **misc**: Add agent share.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### What's improved
|
|
19
|
+
|
|
20
|
+
- **misc**: Add agent share ([953d7c7](https://github.com/lobehub/lobe-chat/commit/953d7c7))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
5
30
|
## [Version 0.93.0](https://github.com/lobehub/lobe-chat/compare/v0.92.0...v0.93.0)
|
|
6
31
|
|
|
7
32
|
<sup>Released on **2023-10-18**</sup>
|
|
@@ -27,7 +27,7 @@ You can fill in the corresponding configuration items as needed:
|
|
|
27
27
|
|
|
28
28
|
- **APIKey**: The API key you applied for on the Azure OpenAI account page, which can be found in the "Keys and Endpoints" section.
|
|
29
29
|
- **API Address**: Azure API address, which can be found in the "Keys and Endpoints" section when checking resources from the Azure portal.
|
|
30
|
-
- **Azure Api Version**: The API version of Azure, which follows the YYYY-MM-DD format, refer to the [latest version][azure-api-
|
|
30
|
+
- **Azure Api Version**: The API version of Azure, which follows the YYYY-MM-DD format, refer to the [latest version][azure-api-version-url].
|
|
31
31
|
|
|
32
32
|
After completing the above field configuration, click on "Check". If the prompt says "Check Passed", it means the configuration was successful.
|
|
33
33
|
|
|
@@ -42,12 +42,12 @@ If you want the deployed version to be directly configured with Azure OpenAI for
|
|
|
42
42
|
| `USE_AZURE_OPENAI` | Yes | Set this value to `1` to enable Azure OpenAI configuration | - | `1` |
|
|
43
43
|
| `AZURE_API_KEY` | Yes | This is the API key you applied for on the Azure OpenAI account page | - | `c55168be3874490ef0565d9779ecd5a6` |
|
|
44
44
|
| `OPENAI_PROXY_URL` | Yes | Azure API address, can be found in the "Keys and Endpoints" section | - | `https://docs-test-001.openai.azure.com` |
|
|
45
|
-
| `AZURE_API_VERSION` | No | Azure's API version, follows the YYYY-MM-DD format | 2023-08-01-preview | `2023-05-15`, refer to [latest version][azure-api-
|
|
45
|
+
| `AZURE_API_VERSION` | No | Azure's API version, follows the YYYY-MM-DD format | 2023-08-01-preview | `2023-05-15`, refer to [latest version][azure-api-version-url] |
|
|
46
46
|
| `ACCESS_CODE` | No | Add a password to access this service, the password should be 6 digits or letters | - | `awCT74` or `e3@09!` |
|
|
47
47
|
|
|
48
48
|
> **Note**\
|
|
49
49
|
> When you enable `USE_AZURE_OPENAI` on the server side, users will not be able to modify and use the OpenAI key in the front-end configuration.
|
|
50
50
|
|
|
51
|
-
[azure-api-
|
|
51
|
+
[azure-api-version-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions
|
|
52
52
|
[azure-openai-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/concepts/models
|
|
53
53
|
[rfc]: https://github.com/lobehub/lobe-chat/discussions/178
|
package/locales/en_US/chat.json
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
{
|
|
2
|
-
"agentDefaultMessage": "Hello, I'm **{{name}}**. You can start chatting with me right away or go to [
|
|
2
|
+
"agentDefaultMessage": "Hello, I'm **{{name}}**. You can start chatting with me right away or go to [Agent Settings](/chat/settings#session={{id}}) to improve my information.",
|
|
3
3
|
"agentDefaultMessageWithSystemRole": "Hello, I'm **{{name}}**, {{systemRole}}. Let's start the conversation!",
|
|
4
4
|
"backToBottom": "Go to Latest Messages",
|
|
5
5
|
"clearCurrentMessages": "Clear Current Session Messages",
|
|
6
6
|
"confirmClearCurrentMessages": "You are about to clear the current session messages. Once cleared, they cannot be recovered. Please confirm your operation.",
|
|
7
|
-
"confirmRemoveSessionItemAlert": "You are about to delete this
|
|
8
|
-
"defaultAgent": "Custom
|
|
9
|
-
"defaultSession": "Custom
|
|
7
|
+
"confirmRemoveSessionItemAlert": "You are about to delete this agent. Once deleted, it cannot be recovered. Please confirm your operation.",
|
|
8
|
+
"defaultAgent": "Custom Agent",
|
|
9
|
+
"defaultSession": "Custom Agent",
|
|
10
10
|
"historyRange": "History Range",
|
|
11
11
|
"inbox": {
|
|
12
|
-
"defaultMessage": "Hello, I'm your intelligent
|
|
13
|
-
"desc": "Activate brain clusters and spark thinking. Your intelligent
|
|
12
|
+
"defaultMessage": "Hello, I'm your intelligent agent. You can ask me any questions, and I will do my best to answer you. If you need a more professional or customized agent, you can click on `+` to create a custom agent.",
|
|
13
|
+
"desc": "Activate brain clusters and spark thinking. Your intelligent agent is here to communicate with you about everything.",
|
|
14
14
|
"title": "Chat Randomly"
|
|
15
15
|
},
|
|
16
|
-
"newAgent": "Create New
|
|
16
|
+
"newAgent": "Create New Agent",
|
|
17
17
|
"noDescription": "No description available",
|
|
18
18
|
"regenerate": "Regenerate",
|
|
19
19
|
"roleAndArchive": "Roles and Archives",
|
|
20
|
-
"searchAgentPlaceholder": "Search
|
|
20
|
+
"searchAgentPlaceholder": "Search agents and conversations...",
|
|
21
21
|
"send": "Send",
|
|
22
22
|
"sendPlaceholder": "Enter chat content...",
|
|
23
23
|
"shareModal": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"withBackground": "Include Background Image",
|
|
30
30
|
"withFooter": "Include Footer",
|
|
31
31
|
"withPluginInfo": "Include Plugin Information",
|
|
32
|
-
"withSystemRole": "Include
|
|
32
|
+
"withSystemRole": "Include Agent Role Setting"
|
|
33
33
|
},
|
|
34
34
|
"stop": "Stop",
|
|
35
35
|
"temp": "Temporary",
|
|
@@ -50,6 +50,6 @@
|
|
|
50
50
|
"clear": "Clear Translation"
|
|
51
51
|
},
|
|
52
52
|
"translateTo": "Translate",
|
|
53
|
-
"updateAgent": "Update
|
|
53
|
+
"updateAgent": "Update Agent Information",
|
|
54
54
|
"warp": "Line Break"
|
|
55
55
|
}
|
|
@@ -235,6 +235,13 @@
|
|
|
235
235
|
},
|
|
236
236
|
"title": "Theme Settings"
|
|
237
237
|
},
|
|
238
|
+
"submitAgentModal": {
|
|
239
|
+
"tooltips": "Share to Assistant Market",
|
|
240
|
+
"button": "Submit Assistant",
|
|
241
|
+
"identifier": "Identifier",
|
|
242
|
+
"metaMiss": "Please complete the assistant information before submitting. It should include name, description, and tags.",
|
|
243
|
+
"placeholder": "Please enter a unique identifier for the assistant, such as web-development."
|
|
244
|
+
},
|
|
238
245
|
"tab": {
|
|
239
246
|
"agent": "Default Agent",
|
|
240
247
|
"common": "Common Settings",
|
|
@@ -222,6 +222,13 @@
|
|
|
222
222
|
},
|
|
223
223
|
"title": "テーマ設定"
|
|
224
224
|
},
|
|
225
|
+
"submitAgentModal": {
|
|
226
|
+
"tooltips": "アシスタントマーケットに共有する",
|
|
227
|
+
"button": "助手を提出する",
|
|
228
|
+
"identifier": "識別子 エージェントの識別子",
|
|
229
|
+
"metaMiss": "エージェント情報を入力してから提出してください。名前、説明、およびタグが必要です",
|
|
230
|
+
"placeholder": "エージェントの識別子を入力してください。一意である必要があります。例:web-development"
|
|
231
|
+
},
|
|
225
232
|
"tab": {
|
|
226
233
|
"agent": "デフォルトのアシスタント",
|
|
227
234
|
"common": "一般設定",
|
|
@@ -222,6 +222,13 @@
|
|
|
222
222
|
},
|
|
223
223
|
"title": "테마 설정"
|
|
224
224
|
},
|
|
225
|
+
"submitAgentModal": {
|
|
226
|
+
"tooltips": "도우미 마켓에 공유",
|
|
227
|
+
"button": "도우미 제출",
|
|
228
|
+
"identifier": "식별자 도우미 식별자",
|
|
229
|
+
"metaMiss": "도우미 정보를 입력한 후 제출하세요. 이름, 설명 및 태그를 포함해야 합니다.",
|
|
230
|
+
"placeholder": "도우미의 식별자를 입력하세요. 고유해야 하며, 예를 들어 web-development과 같은 형식이어야 합니다."
|
|
231
|
+
},
|
|
225
232
|
"tab": {
|
|
226
233
|
"agent": "기본 도우미",
|
|
227
234
|
"common": "일반 설정",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"about": "Наш Github",
|
|
3
3
|
"advanceSettings": "Дополнительные настройки",
|
|
4
|
-
"agentDefaultMessage": "Здравствуйте, я **{{name}}**. Вы можете начать общаться со мной прямо сейчас или перейти на [
|
|
4
|
+
"agentDefaultMessage": "Здравствуйте, я **{{name}}**. Вы можете начать общаться со мной прямо сейчас или перейти на [Agent Settings](/chat/settings#session={{id}}) для моей настройки.",
|
|
5
|
+
"agentMaxToken": "Максимальная длина сессии",
|
|
5
6
|
"agentModel": "Модель",
|
|
6
7
|
"agentProfile": "Информация о помощнике",
|
|
7
8
|
"appInitializing": "LobeChat запускается, пожалуйста, подождите...",
|
|
@@ -235,6 +235,13 @@
|
|
|
235
235
|
},
|
|
236
236
|
"title": "Настройки темы"
|
|
237
237
|
},
|
|
238
|
+
"submitAgentModal": {
|
|
239
|
+
"tooltips": "Поделиться на рынке помощников",
|
|
240
|
+
"button": "Отправить агента",
|
|
241
|
+
"identifier": "Идентификатор агента",
|
|
242
|
+
"metaMiss": "Пожалуйста, заполните информацию об агенте перед отправкой. Необходимо указать имя, описание и метки",
|
|
243
|
+
"placeholder": "Введите идентификатор агента, который должен быть уникальным, например, web-development"
|
|
244
|
+
},
|
|
238
245
|
"tab": {
|
|
239
246
|
"agent": "Помощник по умолчанию",
|
|
240
247
|
"common": "Общие настройки",
|
|
@@ -222,6 +222,13 @@
|
|
|
222
222
|
},
|
|
223
223
|
"title": "主题设置"
|
|
224
224
|
},
|
|
225
|
+
"submitAgentModal": {
|
|
226
|
+
"button": "提交助手",
|
|
227
|
+
"identifier": "identifier 助手标识符",
|
|
228
|
+
"metaMiss": "请补全助手信息后提交,需要包含名称、描述和标签",
|
|
229
|
+
"placeholder": "请输入助手的标识符,需要是唯一的,比如 web-development",
|
|
230
|
+
"tooltips": "分享到助手市场"
|
|
231
|
+
},
|
|
225
232
|
"tab": {
|
|
226
233
|
"agent": "默认助手",
|
|
227
234
|
"common": "通用设置",
|
|
@@ -235,6 +235,13 @@
|
|
|
235
235
|
},
|
|
236
236
|
"title": "主題設定"
|
|
237
237
|
},
|
|
238
|
+
"submitAgentModal": {
|
|
239
|
+
"tooltips": "分享到助手市場",
|
|
240
|
+
"button": "提交助手",
|
|
241
|
+
"identifier": "助手識別符",
|
|
242
|
+
"metaMiss": "請補全助手資訊後提交,需要包含名稱、描述和標籤",
|
|
243
|
+
"placeholder": "請輸入助手的識別符,需要是唯一的,例如 web-development"
|
|
244
|
+
},
|
|
238
245
|
"tab": {
|
|
239
246
|
"agent": "預設助理",
|
|
240
247
|
"common": "通用設定",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.94.0",
|
|
4
4
|
"description": "An open-source, extensible (Function Calling), high-performance chatbot framework. It supports one-click free deployment of your private ChatGPT/LLM web application",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@emoji-mart/data": "^1",
|
|
70
70
|
"@emoji-mart/react": "^1",
|
|
71
71
|
"@icons-pack/react-simple-icons": "^9",
|
|
72
|
-
"@lobehub/chat-plugin-sdk": "
|
|
72
|
+
"@lobehub/chat-plugin-sdk": "latest",
|
|
73
73
|
"@lobehub/chat-plugins-gateway": "latest",
|
|
74
74
|
"@lobehub/ui": "latest",
|
|
75
75
|
"@vercel/analytics": "^1",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"antd-style": "^3.5",
|
|
80
80
|
"brotli-wasm": "^1",
|
|
81
81
|
"chroma-js": "^2",
|
|
82
|
-
"copy-to-clipboard": "^3
|
|
82
|
+
"copy-to-clipboard": "^3",
|
|
83
83
|
"dayjs": "^1",
|
|
84
84
|
"emoji-mart": "^5",
|
|
85
85
|
"fast-deep-equal": "^3",
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"openai": "^4.10.0",
|
|
99
99
|
"polished": "^4",
|
|
100
100
|
"posthog-js": "^1",
|
|
101
|
+
"query-string": "^8",
|
|
101
102
|
"react": "^18",
|
|
102
103
|
"react-dom": "^18",
|
|
103
104
|
"react-hotkeys-hook": "^4",
|
|
@@ -146,7 +147,7 @@
|
|
|
146
147
|
"eslint": "^8",
|
|
147
148
|
"husky": "^8",
|
|
148
149
|
"jsdom": "^22",
|
|
149
|
-
"lint-staged": "^15
|
|
150
|
+
"lint-staged": "^15",
|
|
150
151
|
"lodash": "^4",
|
|
151
152
|
"next-pwa": "^5",
|
|
152
153
|
"node-fetch": "^3",
|
|
@@ -13,9 +13,12 @@ export const readJSON = (filePath: string) => {
|
|
|
13
13
|
return JSON.parse(data);
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
export const replaceAssistantToAgent = (text: string) =>
|
|
17
|
+
text.replaceAll('assistant', 'agent').replaceAll('Assistant', 'Agent');
|
|
18
|
+
|
|
16
19
|
export const writeJSON = (filePath: string, data: any) => {
|
|
17
20
|
const jsonStr = JSON.stringify(data, null, 2);
|
|
18
|
-
writeFileSync(filePath, jsonStr, 'utf8');
|
|
21
|
+
writeFileSync(filePath, replaceAssistantToAgent(jsonStr), 'utf8');
|
|
19
22
|
};
|
|
20
23
|
|
|
21
24
|
export const genResourcesContent = (locales: string[]) => {
|
|
@@ -5,6 +5,7 @@ import { memo, useState } from 'react';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { Flexbox } from 'react-layout-kit';
|
|
7
7
|
|
|
8
|
+
import AgentInfo from '@/features/AgentInfo';
|
|
8
9
|
import { useSessionChatInit, useSessionStore } from '@/store/session';
|
|
9
10
|
import { agentSelectors } from '@/store/session/selectors';
|
|
10
11
|
|
|
@@ -15,8 +16,9 @@ const SystemRole = memo(() => {
|
|
|
15
16
|
const [openModal, setOpenModal] = useState(false);
|
|
16
17
|
const [editing, setEditing] = useState(false);
|
|
17
18
|
const { styles } = useStyles();
|
|
18
|
-
const [systemRole, updateAgentConfig] = useSessionStore((s) => [
|
|
19
|
+
const [systemRole, meta, updateAgentConfig] = useSessionStore((s) => [
|
|
19
20
|
agentSelectors.currentAgentSystemRole(s),
|
|
21
|
+
agentSelectors.currentAgentMeta(s),
|
|
20
22
|
s.updateAgentConfig,
|
|
21
23
|
]);
|
|
22
24
|
|
|
@@ -57,6 +59,7 @@ const SystemRole = memo(() => {
|
|
|
57
59
|
<EditableMessage
|
|
58
60
|
classNames={{ markdown: styles.prompt }}
|
|
59
61
|
editing={editing}
|
|
62
|
+
model={{ extra: <AgentInfo meta={meta} style={{ marginBottom: 16 }} /> }}
|
|
60
63
|
onChange={(e) => {
|
|
61
64
|
updateAgentConfig({ systemRole: e });
|
|
62
65
|
}}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { ChatHeader, ChatHeaderTitle } from '@lobehub/ui';
|
|
2
2
|
import { useRouter } from 'next/navigation';
|
|
3
|
-
import {
|
|
3
|
+
import { memo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
|
+
import HeaderContent from '@/app/chat/settings/features/HeaderContent';
|
|
6
7
|
import { pathString } from '@/utils/url';
|
|
7
8
|
|
|
8
|
-
const Header = memo
|
|
9
|
+
const Header = memo(() => {
|
|
9
10
|
const { t } = useTranslation('setting');
|
|
10
11
|
const router = useRouter();
|
|
11
12
|
|
|
@@ -13,7 +14,7 @@ const Header = memo<{ children: ReactNode }>(({ children }) => {
|
|
|
13
14
|
<ChatHeader
|
|
14
15
|
left={<ChatHeaderTitle title={t('header.session')} />}
|
|
15
16
|
onBackClick={() => router.push(pathString('/chat', { hash: location.hash }))}
|
|
16
|
-
right={
|
|
17
|
+
right={<HeaderContent />}
|
|
17
18
|
showBackButton
|
|
18
19
|
/>
|
|
19
20
|
);
|
|
@@ -5,7 +5,7 @@ import SafeSpacing from '@/components/SafeSpacing';
|
|
|
5
5
|
import { HEADER_HEIGHT } from '@/const/layoutTokens';
|
|
6
6
|
|
|
7
7
|
import Layout from '../../(desktop)/layout.desktop';
|
|
8
|
-
import Header from './Header';
|
|
8
|
+
import Header from './features/Header';
|
|
9
9
|
|
|
10
10
|
export default memo(({ children }: PropsWithChildren) => (
|
|
11
11
|
<Layout>
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { MobileNavBar, MobileNavBarTitle } from '@lobehub/ui';
|
|
2
2
|
import { useRouter } from 'next/navigation';
|
|
3
|
-
import {
|
|
3
|
+
import { memo } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
|
+
import HeaderContent from '@/app/chat/settings/features/HeaderContent';
|
|
6
7
|
import { pathString } from '@/utils/url';
|
|
7
8
|
|
|
8
|
-
const Header = memo
|
|
9
|
+
const Header = memo(() => {
|
|
9
10
|
const { t } = useTranslation('setting');
|
|
10
11
|
const router = useRouter();
|
|
11
12
|
|
|
@@ -13,7 +14,7 @@ const Header = memo<{ children: ReactNode }>(({ children }) => {
|
|
|
13
14
|
<MobileNavBar
|
|
14
15
|
center={<MobileNavBarTitle title={t('header.session')} />}
|
|
15
16
|
onBackClick={() => router.push(pathString('/chat/mobile', { hash: location.hash }))}
|
|
16
|
-
right={
|
|
17
|
+
right={<HeaderContent />}
|
|
17
18
|
showBackButton
|
|
18
19
|
/>
|
|
19
20
|
);
|
|
@@ -2,7 +2,7 @@ import { PropsWithChildren, memo } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import AppLayoutMobile from '@/layout/AppLayout.mobile';
|
|
4
4
|
|
|
5
|
-
import Header from './Header';
|
|
5
|
+
import Header from './features/Header';
|
|
6
6
|
|
|
7
7
|
export default memo(({ children }: PropsWithChildren) => (
|
|
8
8
|
<AppLayoutMobile navBar={<Header />}>{children}</AppLayoutMobile>
|
|
@@ -9,9 +9,12 @@ import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
|
9
9
|
import { exportSingleAgent, exportSingleSession } from '@/helpers/export';
|
|
10
10
|
import { useSessionStore } from '@/store/session';
|
|
11
11
|
|
|
12
|
+
import SubmitAgentButton from './SubmitAgentButton';
|
|
13
|
+
|
|
12
14
|
export const HeaderContent = memo<{ mobile?: boolean }>(() => {
|
|
13
15
|
const { t } = useTranslation('setting');
|
|
14
16
|
const id = useSessionStore((s) => s.activeId);
|
|
17
|
+
|
|
15
18
|
const { mobile } = useResponsive();
|
|
16
19
|
|
|
17
20
|
const items = useMemo<MenuProps['items']>(
|
|
@@ -41,8 +44,13 @@ export const HeaderContent = memo<{ mobile?: boolean }>(() => {
|
|
|
41
44
|
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
|
42
45
|
|
|
43
46
|
return (
|
|
44
|
-
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
+
<>
|
|
48
|
+
<SubmitAgentButton />
|
|
49
|
+
<Dropdown arrow={false} menu={{ items }} trigger={['click']}>
|
|
50
|
+
<ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
|
|
51
|
+
</Dropdown>
|
|
52
|
+
</>
|
|
47
53
|
);
|
|
48
54
|
});
|
|
55
|
+
|
|
56
|
+
export default HeaderContent;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Alert, Button, Divider, Input } from 'antd';
|
|
2
|
+
import { useTheme } from 'antd-style';
|
|
3
|
+
import isEqual from 'fast-deep-equal';
|
|
4
|
+
import { kebabCase } from 'lodash-es';
|
|
5
|
+
import qs from 'query-string';
|
|
6
|
+
import { memo, useState } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { Flexbox } from 'react-layout-kit';
|
|
9
|
+
|
|
10
|
+
import MobilePadding from '@/components/MobilePadding';
|
|
11
|
+
import { AGENTS_INDEX_GITHUB_ISSUE } from '@/const/url';
|
|
12
|
+
import AgentInfo from '@/features/AgentInfo';
|
|
13
|
+
import { useGlobalStore } from '@/store/global';
|
|
14
|
+
import { useSessionStore } from '@/store/session';
|
|
15
|
+
import { agentSelectors } from '@/store/session/slices/agentConfig';
|
|
16
|
+
|
|
17
|
+
const Inner = memo(() => {
|
|
18
|
+
const { t } = useTranslation('setting');
|
|
19
|
+
const [identifier, setIdentifier] = useState('');
|
|
20
|
+
const systemRole = useSessionStore(agentSelectors.currentAgentSystemRole);
|
|
21
|
+
const theme = useTheme();
|
|
22
|
+
const meta = useSessionStore(agentSelectors.currentAgentMeta, isEqual);
|
|
23
|
+
const language = useGlobalStore((s) => s.settings.language);
|
|
24
|
+
|
|
25
|
+
const isMetaPass = Boolean(
|
|
26
|
+
meta && meta.title && meta.description && (meta.tags as string[])?.length > 0 && meta.avatar,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const handleSubmit = () => {
|
|
30
|
+
const body = [
|
|
31
|
+
'### systemRole',
|
|
32
|
+
systemRole,
|
|
33
|
+
'### identifier',
|
|
34
|
+
identifier,
|
|
35
|
+
'### avatar',
|
|
36
|
+
meta.avatar,
|
|
37
|
+
'### title',
|
|
38
|
+
meta.title,
|
|
39
|
+
'### description',
|
|
40
|
+
meta.description,
|
|
41
|
+
'### tags',
|
|
42
|
+
(meta.tags as string[]).join(', '),
|
|
43
|
+
'### locale',
|
|
44
|
+
language === 'auto' ? navigator.language : language,
|
|
45
|
+
].join('\n\n');
|
|
46
|
+
|
|
47
|
+
const url = qs.stringifyUrl({
|
|
48
|
+
query: { body, labels: '🤖 Agent PR', title: `[Agent] ${meta.title}` },
|
|
49
|
+
url: AGENTS_INDEX_GITHUB_ISSUE,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
window.open(url, '_blank');
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Flexbox gap={16}>
|
|
57
|
+
<MobilePadding>
|
|
58
|
+
{!isMetaPass && (
|
|
59
|
+
<Alert message={t('submitAgentModal.metaMiss')} showIcon type={'warning'} />
|
|
60
|
+
)}
|
|
61
|
+
<AgentInfo meta={meta} systemRole={systemRole} />
|
|
62
|
+
<Divider style={{ margin: '8px 0' }} />
|
|
63
|
+
<strong>
|
|
64
|
+
<span style={{ color: theme.colorError, marginRight: 4 }}>*</span>
|
|
65
|
+
{t('submitAgentModal.identifier')}
|
|
66
|
+
</strong>
|
|
67
|
+
<Input
|
|
68
|
+
onChange={(e) => setIdentifier(kebabCase(e.target.value))}
|
|
69
|
+
placeholder={t('submitAgentModal.placeholder')}
|
|
70
|
+
value={identifier}
|
|
71
|
+
/>
|
|
72
|
+
<Button
|
|
73
|
+
block
|
|
74
|
+
disabled={!isMetaPass || !identifier}
|
|
75
|
+
onClick={handleSubmit}
|
|
76
|
+
size={'large'}
|
|
77
|
+
type={'primary'}
|
|
78
|
+
>
|
|
79
|
+
{t('submitAgentModal.button')}
|
|
80
|
+
</Button>
|
|
81
|
+
</MobilePadding>
|
|
82
|
+
</Flexbox>
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export default Inner;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ActionIcon, Modal } from '@lobehub/ui';
|
|
2
|
+
import { useResponsive } from 'antd-style';
|
|
3
|
+
import { Share2 } from 'lucide-react';
|
|
4
|
+
import { memo, useState } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
import { MOBILE_HEADER_ICON_SIZE } from '@/const/layoutTokens';
|
|
8
|
+
|
|
9
|
+
import Inner from './Inner';
|
|
10
|
+
|
|
11
|
+
const SubmitAgentButton = memo(() => {
|
|
12
|
+
const { t } = useTranslation('setting');
|
|
13
|
+
const { mobile } = useResponsive();
|
|
14
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
15
|
+
const size = mobile ? MOBILE_HEADER_ICON_SIZE : { fontSize: 24 };
|
|
16
|
+
return (
|
|
17
|
+
<>
|
|
18
|
+
<ActionIcon
|
|
19
|
+
icon={Share2}
|
|
20
|
+
onClick={() => setIsModalOpen(true)}
|
|
21
|
+
size={size}
|
|
22
|
+
title={t('submitAgentModal.tooltips')}
|
|
23
|
+
/>
|
|
24
|
+
<Modal
|
|
25
|
+
centered={false}
|
|
26
|
+
footer={null}
|
|
27
|
+
onCancel={() => setIsModalOpen(false)}
|
|
28
|
+
open={isModalOpen}
|
|
29
|
+
title={t('submitAgentModal.tooltips')}
|
|
30
|
+
>
|
|
31
|
+
<Inner />
|
|
32
|
+
</Modal>
|
|
33
|
+
</>
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export default SubmitAgentButton;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { createStyles } from 'antd-style';
|
|
2
|
+
|
|
3
|
+
export const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
|
4
|
+
author: css`
|
|
5
|
+
font-size: 12px;
|
|
6
|
+
`,
|
|
7
|
+
|
|
8
|
+
avatar: css`
|
|
9
|
+
flex: none;
|
|
10
|
+
`,
|
|
11
|
+
container: css`
|
|
12
|
+
position: relative;
|
|
13
|
+
padding: 16px 16px 24px;
|
|
14
|
+
border-bottom: 1px solid ${token.colorBorderSecondary};
|
|
15
|
+
`,
|
|
16
|
+
date: css`
|
|
17
|
+
font-size: 12px;
|
|
18
|
+
color: ${token.colorTextDescription};
|
|
19
|
+
`,
|
|
20
|
+
desc: css`
|
|
21
|
+
color: ${token.colorTextDescription};
|
|
22
|
+
text-align: center;
|
|
23
|
+
`,
|
|
24
|
+
loading: css`
|
|
25
|
+
.${prefixCls}-skeleton-content {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: column;
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
nav: css`
|
|
31
|
+
padding-top: 4px;
|
|
32
|
+
|
|
33
|
+
.${prefixCls}-tabs-tab {
|
|
34
|
+
margin: 4px !important;
|
|
35
|
+
|
|
36
|
+
+ .${prefixCls}-tabs-tab {
|
|
37
|
+
margin: 4px !important;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
`,
|
|
41
|
+
title: css`
|
|
42
|
+
font-size: 20px;
|
|
43
|
+
font-weight: 600;
|
|
44
|
+
text-align: center;
|
|
45
|
+
`,
|
|
46
|
+
}));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SearchBar } from '@lobehub/ui';
|
|
2
2
|
import { useResponsive } from 'antd-style';
|
|
3
|
-
import { memo } from 'react';
|
|
3
|
+
import { memo, useState } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
6
|
import { useMarketStore } from '@/store/market';
|
|
@@ -8,17 +8,20 @@ import { useMarketStore } from '@/store/market';
|
|
|
8
8
|
const AgentSearchBar = memo(() => {
|
|
9
9
|
const { t } = useTranslation('market');
|
|
10
10
|
const keywords = useMarketStore((s) => s.searchKeywords);
|
|
11
|
+
const [value, setValue] = useState(keywords);
|
|
11
12
|
const { mobile } = useResponsive();
|
|
12
13
|
return (
|
|
13
14
|
<SearchBar
|
|
14
15
|
allowClear
|
|
15
16
|
enableShortKey={!mobile}
|
|
16
|
-
onChange={(e) =>
|
|
17
|
+
onChange={(e) => setValue(e.target.value)}
|
|
18
|
+
onPressEnter={() => useMarketStore.setState({ searchKeywords: value })}
|
|
19
|
+
onSubmit={() => useMarketStore.setState({ searchKeywords: value })}
|
|
17
20
|
placeholder={t('search.placeholder')}
|
|
18
21
|
shortKey={'k'}
|
|
19
22
|
spotlight={!mobile}
|
|
20
23
|
type={mobile ? 'block' : 'ghost'}
|
|
21
|
-
value={
|
|
24
|
+
value={value}
|
|
22
25
|
/>
|
|
23
26
|
);
|
|
24
27
|
});
|
package/src/chains/pickEmoji.ts
CHANGED
|
@@ -7,28 +7,25 @@ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
|
7
7
|
export const chainPickEmoji = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
|
8
8
|
messages: [
|
|
9
9
|
{
|
|
10
|
-
content:
|
|
10
|
+
content:
|
|
11
|
+
'你是一名擅长进行概念抽象的设计师与 Emoji 专家,你需要根据角色能力的描述抽象出一个表达物理实体的概念 Emoji 作为角色头像, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {一个Emoji}',
|
|
11
12
|
role: 'system',
|
|
12
13
|
},
|
|
13
14
|
{
|
|
14
|
-
content:
|
|
15
|
+
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
|
15
16
|
role: 'user',
|
|
16
17
|
},
|
|
18
|
+
{ content: '✒️', role: 'assistant' },
|
|
17
19
|
{
|
|
18
|
-
content:
|
|
19
|
-
role: 'assistant',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
content: `输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。`,
|
|
20
|
+
content: `输入: {你是一名代码巫师,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
|
23
21
|
role: 'user',
|
|
24
22
|
},
|
|
23
|
+
{ content: '🧙♂️', role: 'assistant' },
|
|
25
24
|
{
|
|
26
|
-
content:
|
|
27
|
-
role: 'assistant',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
content: `输入:${content}`,
|
|
25
|
+
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
|
31
26
|
role: 'user',
|
|
32
27
|
},
|
|
28
|
+
{ content: '🚀', role: 'assistant' },
|
|
29
|
+
{ content: `输入: {${content}}`, role: 'user' },
|
|
33
30
|
],
|
|
34
31
|
});
|
|
@@ -7,29 +7,24 @@ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
|
7
7
|
export const chainSummaryAgentName = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
|
8
8
|
messages: [
|
|
9
9
|
{
|
|
10
|
-
content:
|
|
11
|
-
输入: {文本作为JSON引用字符串}
|
|
12
|
-
输出: {角色名}
|
|
13
|
-
`,
|
|
10
|
+
content: `你是一名擅长起名的起名大师,名字需要有文学内涵,注重精炼和赋子意境,你需要将用户的描述总结为 20 个字以内的角色, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}`,
|
|
14
11
|
role: 'system',
|
|
15
12
|
},
|
|
16
13
|
{
|
|
17
|
-
content: `输入: {
|
|
14
|
+
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
|
18
15
|
role: 'user',
|
|
19
16
|
},
|
|
20
|
-
{ content: '
|
|
17
|
+
{ content: '创意命名师', role: 'assistant' },
|
|
21
18
|
{
|
|
22
|
-
content: `输入: {
|
|
19
|
+
content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
|
23
20
|
role: 'user',
|
|
24
21
|
},
|
|
25
|
-
{ content: '
|
|
22
|
+
{ content: 'ts转换魔术师', role: 'assistant' },
|
|
26
23
|
{
|
|
27
|
-
content: `输入:{
|
|
28
|
-
输入:页面加载中
|
|
29
|
-
输出:页面似乎在思考,一会儿才能准备好}`,
|
|
24
|
+
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
|
30
25
|
role: 'user',
|
|
31
26
|
},
|
|
32
|
-
{ content: '
|
|
27
|
+
{ content: '创业咨询专家', role: 'assistant' },
|
|
33
28
|
{ content: `输入: {${content}}`, role: 'user' },
|
|
34
29
|
],
|
|
35
30
|
});
|
|
@@ -3,13 +3,24 @@ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
|
3
3
|
export const chainSummaryDescription = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
|
4
4
|
messages: [
|
|
5
5
|
{
|
|
6
|
-
content:
|
|
7
|
-
'你是一名擅长会话的助理,你需要将用户的输入的内容总结为一个专家的简介,不超过 20 个字',
|
|
6
|
+
content: `你是一名擅长技能总结的助理,你需要将用户的输入的内容总结为一个角色技能简介,确保信息清晰、逻辑清晰,并有效地传达角色的技能和经验,不超过 20 个字, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色技能简介}`,
|
|
8
7
|
role: 'system',
|
|
9
8
|
},
|
|
10
9
|
{
|
|
11
|
-
content:
|
|
10
|
+
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
|
12
11
|
role: 'user',
|
|
13
12
|
},
|
|
13
|
+
{ content: '擅长文创艺术作品起名', role: 'assistant' },
|
|
14
|
+
{
|
|
15
|
+
content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
|
16
|
+
role: 'user',
|
|
17
|
+
},
|
|
18
|
+
{ content: '擅长 ts 转换和补充类型声明', role: 'assistant' },
|
|
19
|
+
{
|
|
20
|
+
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
|
21
|
+
role: 'user',
|
|
22
|
+
},
|
|
23
|
+
{ content: '擅长创业计划撰写与咨询', role: 'assistant' },
|
|
24
|
+
{ content: `输入: {${content}}`, role: 'user' },
|
|
14
25
|
],
|
|
15
26
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { OpenAIChatStreamPayload } from '@/types/openai/chat';
|
|
2
|
+
|
|
3
|
+
export const chainSummaryTags = (content: string): Partial<OpenAIChatStreamPayload> => ({
|
|
4
|
+
messages: [
|
|
5
|
+
{
|
|
6
|
+
content:
|
|
7
|
+
'你是一名擅长会话标签总结的助理,你需要将用户的输入的内容提炼出分类标签,使用`,`分隔,不超过 5 个标签, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}',
|
|
8
|
+
role: 'system',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
|
|
12
|
+
role: 'user',
|
|
13
|
+
},
|
|
14
|
+
{ content: '起名,写作,创意', role: 'assistant' },
|
|
15
|
+
{
|
|
16
|
+
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
|
17
|
+
role: 'user',
|
|
18
|
+
},
|
|
19
|
+
{ content: '代码,软件开发,效率', role: 'assistant' },
|
|
20
|
+
{
|
|
21
|
+
content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
|
|
22
|
+
role: 'user',
|
|
23
|
+
},
|
|
24
|
+
{ content: '创业,计划,咨询', role: 'assistant' },
|
|
25
|
+
{ content: `输入: {${content}}`, role: 'user' },
|
|
26
|
+
],
|
|
27
|
+
});
|
|
@@ -47,9 +47,9 @@ const HotKeys = memo<HotKeysProps>(({ keys, desc }) => {
|
|
|
47
47
|
|
|
48
48
|
if (!desc) return content;
|
|
49
49
|
return (
|
|
50
|
-
<Flexbox align={'center'}
|
|
51
|
-
{content}
|
|
50
|
+
<Flexbox align={'center'} style={{ textAlign: 'center' }}>
|
|
52
51
|
{desc}
|
|
52
|
+
{content}
|
|
53
53
|
</Flexbox>
|
|
54
54
|
);
|
|
55
55
|
});
|
|
@@ -14,10 +14,12 @@ const ResponsiveIndex = ({ children, Mobile }: ResponsiveIndexProps) => {
|
|
|
14
14
|
const { t } = useTranslation();
|
|
15
15
|
const mobile = useIsMobile();
|
|
16
16
|
|
|
17
|
-
return (
|
|
17
|
+
return mobile ? (
|
|
18
18
|
<Suspense fallback={<FullscreenLoading title={t('layoutInitializing', { ns: 'common' })} />}>
|
|
19
|
-
|
|
19
|
+
<Mobile />
|
|
20
20
|
</Suspense>
|
|
21
|
+
) : (
|
|
22
|
+
children
|
|
21
23
|
);
|
|
22
24
|
};
|
|
23
25
|
|
package/src/const/url.ts
CHANGED
|
@@ -43,6 +43,7 @@ export const getAgentJSON = (
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
export const AGENTS_INDEX_GITHUB = 'https://github.com/lobehub/lobe-chat-agents';
|
|
46
|
+
export const AGENTS_INDEX_GITHUB_ISSUE = urlJoin(AGENTS_INDEX_GITHUB, 'issues/new');
|
|
46
47
|
|
|
47
48
|
export const SESSION_CHAT_URL = (id: string = INBOX_SESSION_ID, mobile?: boolean) =>
|
|
48
49
|
mobile ? `/chat/mobile#session=${id}` : `/chat#session=${id}`;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Avatar, Markdown, Tag } from '@lobehub/ui';
|
|
2
|
+
import { Divider } from 'antd';
|
|
3
|
+
import { createStyles } from 'antd-style';
|
|
4
|
+
import { startCase } from 'lodash-es';
|
|
5
|
+
import { CSSProperties, memo } from 'react';
|
|
6
|
+
import { Center } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import { MetaData } from '@/types/meta';
|
|
9
|
+
|
|
10
|
+
const useStyles = createStyles(({ css, token }) => ({
|
|
11
|
+
avatar: css`
|
|
12
|
+
flex: none;
|
|
13
|
+
`,
|
|
14
|
+
desc: css`
|
|
15
|
+
color: ${token.colorTextDescription};
|
|
16
|
+
text-align: center;
|
|
17
|
+
`,
|
|
18
|
+
|
|
19
|
+
title: css`
|
|
20
|
+
font-size: 20px;
|
|
21
|
+
font-weight: 600;
|
|
22
|
+
text-align: center;
|
|
23
|
+
`,
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
export interface AgentInfoProps {
|
|
27
|
+
meta?: MetaData;
|
|
28
|
+
style?: CSSProperties;
|
|
29
|
+
systemRole?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const AgentInfo = memo<AgentInfoProps>(({ systemRole, style, meta }) => {
|
|
33
|
+
const { styles, theme } = useStyles();
|
|
34
|
+
|
|
35
|
+
if (!meta) return;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Center gap={16} style={style}>
|
|
39
|
+
{meta.avatar && (
|
|
40
|
+
<Avatar
|
|
41
|
+
animation
|
|
42
|
+
avatar={meta.avatar}
|
|
43
|
+
background={meta.backgroundColor || theme.colorFillTertiary}
|
|
44
|
+
className={styles.avatar}
|
|
45
|
+
size={100}
|
|
46
|
+
/>
|
|
47
|
+
)}
|
|
48
|
+
{meta.title && <div className={styles.title}>{meta.title}</div>}
|
|
49
|
+
{(meta?.tags as string[])?.length > 0 && (
|
|
50
|
+
<Center gap={6} horizontal style={{ flexWrap: 'wrap' }}>
|
|
51
|
+
{(meta.tags as string[]).map((tag: string, index) => (
|
|
52
|
+
<Tag key={index} style={{ margin: 0 }}>
|
|
53
|
+
{startCase(tag).trim()}
|
|
54
|
+
</Tag>
|
|
55
|
+
))}
|
|
56
|
+
</Center>
|
|
57
|
+
)}
|
|
58
|
+
{meta.description && <div className={styles.desc}>{meta.description}</div>}
|
|
59
|
+
{systemRole && (
|
|
60
|
+
<>
|
|
61
|
+
<Divider style={{ margin: '8px 0' }} />
|
|
62
|
+
<Markdown>{systemRole}</Markdown>
|
|
63
|
+
</>
|
|
64
|
+
)}
|
|
65
|
+
</Center>
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default AgentInfo;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
2
|
+
import { Select, SelectProps } from 'antd';
|
|
3
|
+
import { useTheme } from 'antd-style';
|
|
4
|
+
import { isString } from 'lodash-es';
|
|
5
|
+
import { Wand2 } from 'lucide-react';
|
|
6
|
+
import { memo } from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
export interface AutoGenerateInputProps extends SelectProps {
|
|
10
|
+
loading?: boolean;
|
|
11
|
+
onGenerate?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const AutoGenerateSelect = memo<AutoGenerateInputProps>(
|
|
15
|
+
({ loading, onGenerate, value, ...props }) => {
|
|
16
|
+
const { t } = useTranslation('common');
|
|
17
|
+
const theme = useTheme();
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Select
|
|
21
|
+
mode="tags"
|
|
22
|
+
open={false}
|
|
23
|
+
style={{ width: '100%' }}
|
|
24
|
+
suffixIcon={
|
|
25
|
+
onGenerate && (
|
|
26
|
+
<ActionIcon
|
|
27
|
+
active
|
|
28
|
+
icon={Wand2}
|
|
29
|
+
loading={loading}
|
|
30
|
+
onClick={onGenerate}
|
|
31
|
+
size={'small'}
|
|
32
|
+
style={{
|
|
33
|
+
color: theme.colorInfo,
|
|
34
|
+
marginRight: -4,
|
|
35
|
+
}}
|
|
36
|
+
title={t('autoGenerate')}
|
|
37
|
+
/>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
tokenSeparators={[',', ',', ' ']}
|
|
41
|
+
value={isString(value) ? value.split(',') : value}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
export default AutoGenerateSelect;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Form, type FormItemProps, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
|
|
2
2
|
import { Button } from 'antd';
|
|
3
3
|
import isEqual from 'fast-deep-equal';
|
|
4
|
+
import { isString } from 'lodash-es';
|
|
4
5
|
import { UserCircle, Wand2 } from 'lucide-react';
|
|
5
6
|
import dynamic from 'next/dynamic';
|
|
6
7
|
import { memo, useMemo } from 'react';
|
|
@@ -11,6 +12,7 @@ import { FORM_STYLE } from '@/const/layoutTokens';
|
|
|
11
12
|
import { useStore } from '../store';
|
|
12
13
|
import { SessionLoadingState } from '../store/initialState';
|
|
13
14
|
import AutoGenerateInput from './AutoGenerateInput';
|
|
15
|
+
import AutoGenerateSelect from './AutoGenerateSelect';
|
|
14
16
|
import BackgroundSwatches from './BackgroundSwatches';
|
|
15
17
|
|
|
16
18
|
const EmojiPicker = dynamic(() => import('@/components/EmojiPicker'), { ssr: false });
|
|
@@ -28,26 +30,35 @@ const AgentMeta = memo(() => {
|
|
|
28
30
|
|
|
29
31
|
const basic = [
|
|
30
32
|
{
|
|
33
|
+
Render: AutoGenerateInput,
|
|
31
34
|
key: 'title',
|
|
32
35
|
label: t('settingAgent.name.title'),
|
|
36
|
+
onChange: (e: any) => updateMeta({ title: e.target.value }),
|
|
33
37
|
placeholder: t('settingAgent.name.placeholder'),
|
|
34
38
|
},
|
|
35
39
|
{
|
|
40
|
+
Render: AutoGenerateInput,
|
|
36
41
|
key: 'description',
|
|
37
42
|
label: t('settingAgent.description.title'),
|
|
43
|
+
onChange: (e: any) => updateMeta({ description: e.target.value }),
|
|
38
44
|
placeholder: t('settingAgent.description.placeholder'),
|
|
39
45
|
},
|
|
40
|
-
|
|
46
|
+
{
|
|
47
|
+
Render: AutoGenerateSelect,
|
|
48
|
+
key: 'tags',
|
|
49
|
+
label: t('settingAgent.tag.title'),
|
|
50
|
+
onChange: (e: any) => updateMeta({ tags: isString(e) ? e.split(',') : e }),
|
|
51
|
+
placeholder: t('settingAgent.tag.placeholder'),
|
|
52
|
+
},
|
|
41
53
|
];
|
|
42
54
|
|
|
43
55
|
const autocompleteItems: FormItemProps[] = basic.map((item) => {
|
|
56
|
+
const AutoGenerate = item.Render;
|
|
44
57
|
return {
|
|
45
58
|
children: (
|
|
46
|
-
<
|
|
59
|
+
<AutoGenerate
|
|
47
60
|
loading={loading[item.key as keyof SessionLoadingState]}
|
|
48
|
-
onChange={
|
|
49
|
-
updateMeta({ [item.key]: e.target.value });
|
|
50
|
-
}}
|
|
61
|
+
onChange={item.onChange}
|
|
51
62
|
onGenerate={() => {
|
|
52
63
|
autocompleteMeta(item.key as keyof typeof meta);
|
|
53
64
|
}}
|
|
@@ -3,6 +3,7 @@ import { StateCreator } from 'zustand/vanilla';
|
|
|
3
3
|
import { chainPickEmoji } from '@/chains/pickEmoji';
|
|
4
4
|
import { chainSummaryAgentName } from '@/chains/summaryAgentName';
|
|
5
5
|
import { chainSummaryDescription } from '@/chains/summaryDescription';
|
|
6
|
+
import { chainSummaryTags } from '@/chains/summaryTags';
|
|
6
7
|
import { MetaData } from '@/types/meta';
|
|
7
8
|
import { LobeAgentConfig } from '@/types/session';
|
|
8
9
|
import { fetchPresetTaskResult } from '@/utils/fetch';
|
|
@@ -28,6 +29,7 @@ export interface Action {
|
|
|
28
29
|
* @returns 一个 Promise,用于异步操作完成后的处理
|
|
29
30
|
*/
|
|
30
31
|
autocompleteAgentDescription: () => Promise<void>;
|
|
32
|
+
autocompleteAgentTags: () => Promise<void>;
|
|
31
33
|
/**
|
|
32
34
|
* 自动完成代理标题
|
|
33
35
|
* @param id - 代理的 ID
|
|
@@ -49,7 +51,8 @@ export interface Action {
|
|
|
49
51
|
setAgentConfig: (config: Partial<LobeAgentConfig>) => void;
|
|
50
52
|
|
|
51
53
|
setAgentMeta: (meta: Partial<MetaData>) => void;
|
|
52
|
-
|
|
54
|
+
streamUpdateMetaArray: (key: keyof MetaData) => any;
|
|
55
|
+
streamUpdateMetaString: (key: keyof MetaData) => any;
|
|
53
56
|
toggleAgentPlugin: (pluginId: string, state?: boolean) => void;
|
|
54
57
|
/**
|
|
55
58
|
* 更新加载状态
|
|
@@ -67,7 +70,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
67
70
|
...initialState,
|
|
68
71
|
|
|
69
72
|
autoPickEmoji: async () => {
|
|
70
|
-
const { config, dispatchMeta } = get();
|
|
73
|
+
const { config, meta, dispatchMeta } = get();
|
|
71
74
|
|
|
72
75
|
const systemRole = config.systemRole;
|
|
73
76
|
|
|
@@ -75,7 +78,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
75
78
|
onLoadingChange: (loading) => {
|
|
76
79
|
get().updateLoadingState('avatar', loading);
|
|
77
80
|
},
|
|
78
|
-
params: chainPickEmoji(systemRole),
|
|
81
|
+
params: chainPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
|
|
79
82
|
});
|
|
80
83
|
|
|
81
84
|
if (emoji) {
|
|
@@ -83,7 +86,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
83
86
|
}
|
|
84
87
|
},
|
|
85
88
|
autocompleteAgentDescription: async () => {
|
|
86
|
-
const { dispatchMeta, config, meta, updateLoadingState,
|
|
89
|
+
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
|
|
87
90
|
|
|
88
91
|
const systemRole = config.systemRole;
|
|
89
92
|
|
|
@@ -101,12 +104,37 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
101
104
|
onLoadingChange: (loading) => {
|
|
102
105
|
updateLoadingState('description', loading);
|
|
103
106
|
},
|
|
104
|
-
onMessageHandle:
|
|
107
|
+
onMessageHandle: streamUpdateMetaString('description'),
|
|
105
108
|
params: chainSummaryDescription(systemRole),
|
|
106
109
|
});
|
|
107
110
|
},
|
|
111
|
+
autocompleteAgentTags: async () => {
|
|
112
|
+
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaArray } = get();
|
|
113
|
+
|
|
114
|
+
const systemRole = config.systemRole;
|
|
115
|
+
|
|
116
|
+
if (!systemRole) return;
|
|
117
|
+
|
|
118
|
+
const preValue = meta.tags;
|
|
119
|
+
|
|
120
|
+
// 替换为 ...
|
|
121
|
+
dispatchMeta({ type: 'update', value: { tags: ['...'] } });
|
|
122
|
+
|
|
123
|
+
fetchPresetTaskResult({
|
|
124
|
+
onError: () => {
|
|
125
|
+
dispatchMeta({ type: 'update', value: { tags: preValue } });
|
|
126
|
+
},
|
|
127
|
+
onLoadingChange: (loading) => {
|
|
128
|
+
updateLoadingState('tags', loading);
|
|
129
|
+
},
|
|
130
|
+
onMessageHandle: streamUpdateMetaArray('tags'),
|
|
131
|
+
params: chainSummaryTags(
|
|
132
|
+
[meta.title, meta.description, systemRole].filter(Boolean).join(','),
|
|
133
|
+
),
|
|
134
|
+
});
|
|
135
|
+
},
|
|
108
136
|
autocompleteAgentTitle: async () => {
|
|
109
|
-
const { dispatchMeta, config, meta, updateLoadingState,
|
|
137
|
+
const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
|
|
110
138
|
|
|
111
139
|
const systemRole = config.systemRole;
|
|
112
140
|
|
|
@@ -124,8 +152,8 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
124
152
|
onLoadingChange: (loading) => {
|
|
125
153
|
updateLoadingState('title', loading);
|
|
126
154
|
},
|
|
127
|
-
onMessageHandle:
|
|
128
|
-
params: chainSummaryAgentName(systemRole),
|
|
155
|
+
onMessageHandle: streamUpdateMetaString('title'),
|
|
156
|
+
params: chainSummaryAgentName([meta.description, systemRole].filter(Boolean).join(',')),
|
|
129
157
|
});
|
|
130
158
|
},
|
|
131
159
|
autocompleteAllMeta: (replace) => {
|
|
@@ -142,9 +170,18 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
142
170
|
if (!meta.avatar || replace) {
|
|
143
171
|
get().autoPickEmoji();
|
|
144
172
|
}
|
|
173
|
+
|
|
174
|
+
if (!meta.tags || replace) {
|
|
175
|
+
get().autocompleteAgentTags();
|
|
176
|
+
}
|
|
145
177
|
},
|
|
146
178
|
autocompleteMeta: (key) => {
|
|
147
|
-
const {
|
|
179
|
+
const {
|
|
180
|
+
autoPickEmoji,
|
|
181
|
+
autocompleteAgentTitle,
|
|
182
|
+
autocompleteAgentDescription,
|
|
183
|
+
autocompleteAgentTags,
|
|
184
|
+
} = get();
|
|
148
185
|
|
|
149
186
|
switch (key) {
|
|
150
187
|
case 'avatar': {
|
|
@@ -159,6 +196,12 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
159
196
|
|
|
160
197
|
case 'title': {
|
|
161
198
|
autocompleteAgentTitle();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
case 'tags': {
|
|
203
|
+
autocompleteAgentTags();
|
|
204
|
+
return;
|
|
162
205
|
}
|
|
163
206
|
}
|
|
164
207
|
},
|
|
@@ -189,10 +232,19 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
|
|
189
232
|
get().dispatchConfig({ config, type: 'update' });
|
|
190
233
|
},
|
|
191
234
|
setAgentMeta: (meta) => {
|
|
235
|
+
console.log(meta);
|
|
192
236
|
get().dispatchMeta({ type: 'update', value: meta });
|
|
193
237
|
},
|
|
194
238
|
|
|
195
|
-
|
|
239
|
+
streamUpdateMetaArray: (key: keyof MetaData) => {
|
|
240
|
+
let value = '';
|
|
241
|
+
return (text: string) => {
|
|
242
|
+
value += text;
|
|
243
|
+
get().dispatchMeta({ type: 'update', value: { [key]: value.split(',') } });
|
|
244
|
+
};
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
streamUpdateMetaString: (key: keyof MetaData) => {
|
|
196
248
|
let value = '';
|
|
197
249
|
return (text: string) => {
|
|
198
250
|
value += text;
|
|
@@ -109,6 +109,7 @@ export default {
|
|
|
109
109
|
},
|
|
110
110
|
title: '助手信息',
|
|
111
111
|
},
|
|
112
|
+
|
|
112
113
|
settingChat: {
|
|
113
114
|
chatStyleType: {
|
|
114
115
|
title: '聊天窗口样式',
|
|
@@ -222,6 +223,13 @@ export default {
|
|
|
222
223
|
},
|
|
223
224
|
title: '主题设置',
|
|
224
225
|
},
|
|
226
|
+
submitAgentModal: {
|
|
227
|
+
button: '提交助手',
|
|
228
|
+
identifier: 'identifier 助手标识符',
|
|
229
|
+
metaMiss: '请补全助手信息后提交,需要包含名称、描述和标签',
|
|
230
|
+
placeholder: '请输入助手的标识符,需要是唯一的,比如 web-development',
|
|
231
|
+
tooltips: '分享到助手市场',
|
|
232
|
+
},
|
|
225
233
|
tab: {
|
|
226
234
|
agent: '默认助手',
|
|
227
235
|
common: '通用设置',
|
|
@@ -112,7 +112,7 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
|
|
|
112
112
|
|
|
113
113
|
const { key, value } = payload;
|
|
114
114
|
|
|
115
|
-
const validKeys = ['avatar', 'backgroundColor', 'description', '
|
|
115
|
+
const validKeys = ['avatar', 'backgroundColor', 'description', 'tags', 'title'];
|
|
116
116
|
|
|
117
117
|
if (validKeys.includes(key)) chat.meta[key] = value;
|
|
118
118
|
});
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
2
|
-
|
|
3
|
-
import { HeaderContent } from '@/app/chat/settings/components/Header';
|
|
4
|
-
import Layout from '@/app/chat/settings/components/Header/Mobile';
|
|
5
|
-
|
|
6
|
-
const Header = memo(() => (
|
|
7
|
-
<Layout>
|
|
8
|
-
<HeaderContent />
|
|
9
|
-
</Layout>
|
|
10
|
-
));
|
|
11
|
-
|
|
12
|
-
export default Header;
|