@lobehub/chat 0.92.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 (48) hide show
  1. package/CHANGELOG.md +58 -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/Conversation/ChatList/Actions/customAction.ts +6 -4
  14. package/src/app/chat/features/Conversation/ChatList/index.tsx +21 -14
  15. package/src/app/chat/features/TopicListContent/SystemRole/index.tsx +4 -1
  16. package/src/app/chat/settings/(desktop)/{Header/Desktop.tsx → features/Header.tsx} +4 -3
  17. package/src/app/chat/settings/(desktop)/layout.desktop.tsx +1 -1
  18. package/src/app/chat/settings/{components/Header/Mobile.tsx → (mobile)/features/Header.tsx} +4 -3
  19. package/src/app/chat/settings/(mobile)/layout.mobile.tsx +1 -1
  20. package/src/app/chat/settings/{components/Header/index.tsx → features/HeaderContent.tsx} +11 -3
  21. package/src/app/chat/settings/features/SubmitAgentButton/Inner.tsx +86 -0
  22. package/src/app/chat/settings/features/SubmitAgentButton/index.tsx +37 -0
  23. package/src/app/chat/settings/features/SubmitAgentButton/style.ts +46 -0
  24. package/src/app/market/features/AgentSearchBar/index.tsx +6 -3
  25. package/src/chains/langDetect.ts +31 -0
  26. package/src/chains/pickEmoji.ts +31 -0
  27. package/src/chains/summaryAgentName.ts +30 -0
  28. package/src/chains/summaryDescription.ts +26 -0
  29. package/src/chains/summaryTags.ts +27 -0
  30. package/src/chains/summaryTitle.ts +32 -0
  31. package/src/chains/translate.ts +17 -0
  32. package/src/components/HotKeys/index.tsx +2 -2
  33. package/src/components/ResponsiveIndex/index.tsx +4 -2
  34. package/src/const/url.ts +1 -0
  35. package/src/features/AgentInfo/index.tsx +69 -0
  36. package/src/features/AgentSetting/AgentMeta/AutoGenerateSelect.tsx +48 -0
  37. package/src/features/AgentSetting/AgentMeta/index.tsx +16 -5
  38. package/src/features/AgentSetting/store/action.ts +66 -12
  39. package/src/locales/default/chat.ts +0 -1
  40. package/src/locales/default/setting.ts +8 -0
  41. package/src/locales/resources/index.ts +0 -1
  42. package/src/store/session/slices/chat/actions/topic.ts +1 -1
  43. package/src/store/session/slices/chat/actions/translate.ts +2 -1
  44. package/src/store/session/slices/session/reducers/session.ts +1 -1
  45. package/src/app/chat/settings/(desktop)/Header/index.tsx +0 -13
  46. package/src/app/chat/settings/(mobile)/Header.tsx +0 -12
  47. package/src/chains/agent.ts +0 -76
  48. package/src/chains/chat.ts +0 -77
@@ -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
  });
@@ -0,0 +1,31 @@
1
+ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
+
3
+ export const chainLangDetect = (content: string): Partial<OpenAIChatStreamPayload> => ({
4
+ messages: [
5
+ {
6
+ content:
7
+ '你是一名精通全世界语言的语言专家,你需要识别用户输入的内容,以国际标准 locale 进行输出',
8
+ role: 'system',
9
+ },
10
+ {
11
+ content: '{你好}',
12
+ role: 'user',
13
+ },
14
+ {
15
+ content: 'zh-CN',
16
+ role: 'assistant',
17
+ },
18
+ {
19
+ content: '{hello}',
20
+ role: 'user',
21
+ },
22
+ {
23
+ content: 'en-US',
24
+ role: 'assistant',
25
+ },
26
+ {
27
+ content: `{${content}}`,
28
+ role: 'user',
29
+ },
30
+ ],
31
+ });
@@ -0,0 +1,31 @@
1
+ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
+
3
+ /**
4
+ * pick emoji for user prompt
5
+ * @param content
6
+ */
7
+ export const chainPickEmoji = (content: string): Partial<OpenAIChatStreamPayload> => ({
8
+ messages: [
9
+ {
10
+ content:
11
+ '你是一名擅长进行概念抽象的设计师与 Emoji 专家,你需要根据角色能力的描述抽象出一个表达物理实体的概念 Emoji 作为角色头像, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {一个Emoji}',
12
+ role: 'system',
13
+ },
14
+ {
15
+ content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
16
+ role: 'user',
17
+ },
18
+ { content: '✒️', role: 'assistant' },
19
+ {
20
+ content: `输入: {你是一名代码巫师,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
21
+ role: 'user',
22
+ },
23
+ { content: '🧙‍♂️', role: 'assistant' },
24
+ {
25
+ content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
26
+ role: 'user',
27
+ },
28
+ { content: '🚀', role: 'assistant' },
29
+ { content: `输入: {${content}}`, role: 'user' },
30
+ ],
31
+ });
@@ -0,0 +1,30 @@
1
+ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
+
3
+ /**
4
+ * summary agent name for user prompt
5
+ * @param content
6
+ */
7
+ export const chainSummaryAgentName = (content: string): Partial<OpenAIChatStreamPayload> => ({
8
+ messages: [
9
+ {
10
+ content: `你是一名擅长起名的起名大师,名字需要有文学内涵,注重精炼和赋子意境,你需要将用户的描述总结为 20 个字以内的角色, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色名}`,
11
+ role: 'system',
12
+ },
13
+ {
14
+ content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
15
+ role: 'user',
16
+ },
17
+ { content: '创意命名师', role: 'assistant' },
18
+ {
19
+ content: `输入: {你是一名前端代码专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
20
+ role: 'user',
21
+ },
22
+ { content: 'ts转换魔术师', role: 'assistant' },
23
+ {
24
+ content: `输入: {你是一名创业计划撰写专家,可以提供包括创意名称、简短的标语、目标用户画像、用户痛点、主要价值主张、销售/营销渠道、收入流、成本结构等计划生成。}`,
25
+ role: 'user',
26
+ },
27
+ { content: '创业咨询专家', role: 'assistant' },
28
+ { content: `输入: {${content}}`, role: 'user' },
29
+ ],
30
+ });
@@ -0,0 +1,26 @@
1
+ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
+
3
+ export const chainSummaryDescription = (content: string): Partial<OpenAIChatStreamPayload> => ({
4
+ messages: [
5
+ {
6
+ content: `你是一名擅长技能总结的助理,你需要将用户的输入的内容总结为一个角色技能简介,确保信息清晰、逻辑清晰,并有效地传达角色的技能和经验,不超过 20 个字, 格式要求如下:\n输入: {文本作为JSON引用字符串}\n输出: {角色技能简介}`,
7
+ role: 'system',
8
+ },
9
+ {
10
+ content: `输入: {你是一名文案大师,帮我为一些设计 / 艺术作品起名,名字需要有文学内涵,注重精炼和赋子意境,表达作品的情景氛国,使名称既简洁又富有诗意。}`,
11
+ role: 'user',
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' },
25
+ ],
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
+ });
@@ -0,0 +1,32 @@
1
+ import { chatHelpers } from '@/store/session/slices/chat/helpers';
2
+ import { LanguageModel } from '@/types/llm';
3
+ import { OpenAIChatMessage, OpenAIChatStreamPayload } from '@/types/openai/chat';
4
+
5
+ export const chainSummaryTitle = async (
6
+ messages: OpenAIChatMessage[],
7
+ ): Promise<Partial<OpenAIChatStreamPayload>> => {
8
+ const finalMessages: OpenAIChatMessage[] = [
9
+ {
10
+ content:
11
+ '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
12
+ role: 'system',
13
+ },
14
+ {
15
+ content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
16
+
17
+ 请总结上述对话为10个字以内的标题,不需要包含标点符号`,
18
+ role: 'user',
19
+ },
20
+ ];
21
+ // 如果超过 4k,则使用 GPT3.5 16K 模型
22
+ const tokens = await chatHelpers.getMessagesTokenCount(finalMessages);
23
+ let model: LanguageModel | undefined = undefined;
24
+ if (tokens > 4000) {
25
+ model = LanguageModel.GPT3_5_16K;
26
+ }
27
+
28
+ return {
29
+ messages: finalMessages,
30
+ model,
31
+ };
32
+ };
@@ -0,0 +1,17 @@
1
+ import { OpenAIChatStreamPayload } from '@/types/openai/chat';
2
+
3
+ export const chainTranslate = (
4
+ content: string,
5
+ targetLang: string,
6
+ ): Partial<OpenAIChatStreamPayload> => ({
7
+ messages: [
8
+ {
9
+ content: '你是一名擅长翻译的助理,你需要将输入的语言翻译为目标语言',
10
+ role: 'system',
11
+ },
12
+ {
13
+ content: `请将以下内容 ${content},翻译为 ${targetLang} `,
14
+ role: 'user',
15
+ },
16
+ ],
17
+ });
@@ -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
  }}
@@ -1,6 +1,9 @@
1
1
  import { StateCreator } from 'zustand/vanilla';
2
2
 
3
- import { promptPickEmoji, promptSummaryAgentName, promptSummaryDescription } from '@/chains/agent';
3
+ import { chainPickEmoji } from '@/chains/pickEmoji';
4
+ import { chainSummaryAgentName } from '@/chains/summaryAgentName';
5
+ import { chainSummaryDescription } from '@/chains/summaryDescription';
6
+ import { chainSummaryTags } from '@/chains/summaryTags';
4
7
  import { MetaData } from '@/types/meta';
5
8
  import { LobeAgentConfig } from '@/types/session';
6
9
  import { fetchPresetTaskResult } from '@/utils/fetch';
@@ -26,6 +29,7 @@ export interface Action {
26
29
  * @returns 一个 Promise,用于异步操作完成后的处理
27
30
  */
28
31
  autocompleteAgentDescription: () => Promise<void>;
32
+ autocompleteAgentTags: () => Promise<void>;
29
33
  /**
30
34
  * 自动完成代理标题
31
35
  * @param id - 代理的 ID
@@ -47,7 +51,8 @@ export interface Action {
47
51
  setAgentConfig: (config: Partial<LobeAgentConfig>) => void;
48
52
 
49
53
  setAgentMeta: (meta: Partial<MetaData>) => void;
50
- streamUpdateMeta: (key: keyof MetaData) => any;
54
+ streamUpdateMetaArray: (key: keyof MetaData) => any;
55
+ streamUpdateMetaString: (key: keyof MetaData) => any;
51
56
  toggleAgentPlugin: (pluginId: string, state?: boolean) => void;
52
57
  /**
53
58
  * 更新加载状态
@@ -65,7 +70,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
65
70
  ...initialState,
66
71
 
67
72
  autoPickEmoji: async () => {
68
- const { config, dispatchMeta } = get();
73
+ const { config, meta, dispatchMeta } = get();
69
74
 
70
75
  const systemRole = config.systemRole;
71
76
 
@@ -73,7 +78,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
73
78
  onLoadingChange: (loading) => {
74
79
  get().updateLoadingState('avatar', loading);
75
80
  },
76
- params: promptPickEmoji(systemRole),
81
+ params: chainPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
77
82
  });
78
83
 
79
84
  if (emoji) {
@@ -81,7 +86,7 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
81
86
  }
82
87
  },
83
88
  autocompleteAgentDescription: async () => {
84
- const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
89
+ const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
85
90
 
86
91
  const systemRole = config.systemRole;
87
92
 
@@ -99,12 +104,37 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
99
104
  onLoadingChange: (loading) => {
100
105
  updateLoadingState('description', loading);
101
106
  },
102
- onMessageHandle: streamUpdateMeta('description'),
103
- params: promptSummaryDescription(systemRole),
107
+ onMessageHandle: streamUpdateMetaString('description'),
108
+ params: chainSummaryDescription(systemRole),
109
+ });
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
+ ),
104
134
  });
105
135
  },
106
136
  autocompleteAgentTitle: async () => {
107
- const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
137
+ const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMetaString } = get();
108
138
 
109
139
  const systemRole = config.systemRole;
110
140
 
@@ -122,8 +152,8 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
122
152
  onLoadingChange: (loading) => {
123
153
  updateLoadingState('title', loading);
124
154
  },
125
- onMessageHandle: streamUpdateMeta('title'),
126
- params: promptSummaryAgentName(systemRole),
155
+ onMessageHandle: streamUpdateMetaString('title'),
156
+ params: chainSummaryAgentName([meta.description, systemRole].filter(Boolean).join(',')),
127
157
  });
128
158
  },
129
159
  autocompleteAllMeta: (replace) => {
@@ -140,9 +170,18 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
140
170
  if (!meta.avatar || replace) {
141
171
  get().autoPickEmoji();
142
172
  }
173
+
174
+ if (!meta.tags || replace) {
175
+ get().autocompleteAgentTags();
176
+ }
143
177
  },
144
178
  autocompleteMeta: (key) => {
145
- const { autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
179
+ const {
180
+ autoPickEmoji,
181
+ autocompleteAgentTitle,
182
+ autocompleteAgentDescription,
183
+ autocompleteAgentTags,
184
+ } = get();
146
185
 
147
186
  switch (key) {
148
187
  case 'avatar': {
@@ -157,6 +196,12 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
157
196
 
158
197
  case 'title': {
159
198
  autocompleteAgentTitle();
199
+ return;
200
+ }
201
+
202
+ case 'tags': {
203
+ autocompleteAgentTags();
204
+ return;
160
205
  }
161
206
  }
162
207
  },
@@ -187,10 +232,19 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
187
232
  get().dispatchConfig({ config, type: 'update' });
188
233
  },
189
234
  setAgentMeta: (meta) => {
235
+ console.log(meta);
190
236
  get().dispatchMeta({ type: 'update', value: meta });
191
237
  },
192
238
 
193
- 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) => {
194
248
  let value = '';
195
249
  return (text: string) => {
196
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: {