@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/docs/Deploy-with-Azure-OpenAI.md +3 -3
  3. package/locales/en_US/chat.json +10 -10
  4. package/locales/en_US/setting.json +7 -0
  5. package/locales/ja_JP/setting.json +7 -0
  6. package/locales/ko_KR/setting.json +7 -0
  7. package/locales/ru_RU/common.json +2 -1
  8. package/locales/ru_RU/setting.json +7 -0
  9. package/locales/zh_CN/setting.json +7 -0
  10. package/locales/zh_TW/setting.json +7 -0
  11. package/package.json +5 -4
  12. package/scripts/i18nWorkflow/utils.ts +4 -1
  13. package/src/app/chat/features/TopicListContent/SystemRole/index.tsx +4 -1
  14. package/src/app/chat/settings/(desktop)/{Header/Desktop.tsx → features/Header.tsx} +4 -3
  15. package/src/app/chat/settings/(desktop)/layout.desktop.tsx +1 -1
  16. package/src/app/chat/settings/{components/Header/Mobile.tsx → (mobile)/features/Header.tsx} +4 -3
  17. package/src/app/chat/settings/(mobile)/layout.mobile.tsx +1 -1
  18. package/src/app/chat/settings/{components/Header/index.tsx → features/HeaderContent.tsx} +11 -3
  19. package/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx +86 -0
  20. package/src/app/chat/settings/features/SubmitAgentButton/index.tsx +37 -0
  21. package/src/app/chat/settings/features/SubmitAgentButton/style.ts +46 -0
  22. package/src/app/market/features/AgentSearchBar/index.tsx +6 -3
  23. package/src/chains/pickEmoji.ts +9 -12
  24. package/src/chains/summaryAgentName.ts +7 -12
  25. package/src/chains/summaryDescription.ts +14 -3
  26. package/src/chains/summaryTags.ts +27 -0
  27. package/src/components/HotKeys/index.tsx +2 -2
  28. package/src/components/ResponsiveIndex/index.tsx +4 -2
  29. package/src/const/url.ts +1 -0
  30. package/src/features/AgentInfo/index.tsx +69 -0
  31. package/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx +48 -0
  32. package/src/features/AgentSetting/AgentMeta/index.tsx +16 -5
  33. package/src/features/AgentSetting/store/action.ts +62 -10
  34. package/src/locales/default/chat.ts +0 -1
  35. package/src/locales/default/setting.ts +8 -0
  36. package/src/locales/resources/index.ts +0 -1
  37. package/src/store/session/slices/session/reducers/session.ts +1 -1
  38. package/src/app/chat/settings/(desktop)/Header/index.tsx +0 -13
  39. 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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-verion-url].
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-verion-url] |
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-verion-url]: https://learn.microsoft.com/zh-cn/azure/ai-services/openai/reference#chat-completions
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
@@ -1,23 +1,23 @@
1
1
  {
2
- "agentDefaultMessage": "Hello, I'm **{{name}}**. You can start chatting with me right away or go to [Assistant Settings](/chat/settings#session={{id}}) to improve my information.",
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 assistant. Once deleted, it cannot be recovered. Please confirm your operation.",
8
- "defaultAgent": "Custom Assistant",
9
- "defaultSession": "Custom Assistant",
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 assistant. You can ask me any questions, and I will do my best to answer you. If you need a more professional or customized assistant, you can click on `+` to create a custom assistant.",
13
- "desc": "Activate brain clusters and spark thinking. Your intelligent assistant is here to communicate with you about everything.",
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 Assistant",
16
+ "newAgent": "Create New Agent",
17
17
  "noDescription": "No description available",
18
18
  "regenerate": "Regenerate",
19
19
  "roleAndArchive": "Roles and Archives",
20
- "searchAgentPlaceholder": "Search assistants and conversations...",
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 Assistant Role Setting"
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 Assistant Information",
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}}**. Вы можете начать общаться со мной прямо сейчас или перейти на [Assistant Settings](/chat/settings#session={{id}}) для моей настройки.",
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.93.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": "^1.17.8",
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.3.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.0.0",
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 { ReactNode, memo } from 'react';
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<{ children: ReactNode }>(({ children }) => {
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={children}
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 { type ReactNode, memo } from 'react';
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<{ children: ReactNode }>(({ children }) => {
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={children}
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
- <Dropdown arrow={false} menu={{ items }} trigger={['click']}>
45
- <ActionIcon icon={HardDriveDownload} size={size} title={t('export', { ns: 'common' })} />
46
- </Dropdown>
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) => useMarketStore.setState({ searchKeywords: e.target.value })}
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={keywords}
24
+ value={value}
22
25
  />
23
26
  );
24
27
  });
@@ -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: '你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。',
10
+ content:
11
+ '你是一名擅长进行概念抽象的设计师与 Emoji 专家,你需要根据角色能力的描述抽象出一个表达物理实体的概念 Emoji 作为角色头像, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {一个Emoji}',
11
12
  role: 'system',
12
13
  },
13
14
  {
14
- content: `输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。`,
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: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
11
- 输入: {文本作为JSON引用字符串}
12
- 输出: {角色名}
13
- `,
10
+ content: `你是一名擅长起名的起名大师,名字需要有文学内涵,注重精炼和赋子意境,你需要将用户的描述总结为 20 个字以内的角色, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}`,
14
11
  role: 'system',
15
12
  },
16
13
  {
17
- content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
14
+ content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
18
15
  role: 'user',
19
16
  },
20
- { content: '前端 vitest 测试专家', role: 'assistant' },
17
+ { content: '创意命名师', role: 'assistant' },
21
18
  {
22
- content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
19
+ content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
23
20
  role: 'user',
24
21
  },
25
- { content: 'js 转 ts 专家', role: 'assistant' },
22
+ { content: 'ts转换魔术师', role: 'assistant' },
26
23
  {
27
- content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
28
- 输入:页面加载中
29
- 输出:页面似乎在思考,一会儿才能准备好}`,
24
+ content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
30
25
  role: 'user',
31
26
  },
32
- { content: '文案比喻优化专家', role: 'assistant' },
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: `${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'} gap={8} horizontal>
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
- {mobile ? <Mobile /> : children}
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
- // { key: 'tag', label: t('agentTag'), placeholder: t('agentTagPlaceholder') },
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
- <AutoGenerateInput
59
+ <AutoGenerate
47
60
  loading={loading[item.key as keyof SessionLoadingState]}
48
- onChange={(e) => {
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
- streamUpdateMeta: (key: keyof MetaData) => any;
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, streamUpdateMeta } = get();
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: streamUpdateMeta('description'),
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, streamUpdateMeta } = get();
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: streamUpdateMeta('title'),
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 { autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
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
- streamUpdateMeta: (key: keyof MetaData) => {
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;
@@ -36,7 +36,6 @@ export default {
36
36
  withSystemRole: '包含助手角色设定',
37
37
  },
38
38
  stop: '停止',
39
-
40
39
  temp: '临时',
41
40
  tokenDetail: '角色设定: {{systemRoleToken}} · 历史消息: {{chatsToken}}',
42
41
  tokenTag: {
@@ -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: '通用设置',
@@ -13,7 +13,6 @@ const resources = {
13
13
  'zh-CN': zh_CN,
14
14
  'zh-TW': zh_TW,
15
15
  } as const;
16
-
17
16
  export default resources;
18
17
  export const defaultResources = zh_CN;
19
18
  export type Resources = typeof resources;
@@ -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', 'tag', 'title'];
115
+ const validKeys = ['avatar', 'backgroundColor', 'description', 'tags', 'title'];
116
116
 
117
117
  if (validKeys.includes(key)) chat.meta[key] = value;
118
118
  });
@@ -1,13 +0,0 @@
1
- import { memo } from 'react';
2
-
3
- import { HeaderContent } from '@/app/chat/settings/components/Header';
4
-
5
- import Layout from './Desktop';
6
-
7
- const Header = memo(() => (
8
- <Layout>
9
- <HeaderContent />
10
- </Layout>
11
- ));
12
-
13
- export default Header;
@@ -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;