@lobehub/chat 0.63.0 → 0.63.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/package.json +1 -1
  3. package/src/features/AgentSetting/AgentConfig/index.tsx +6 -8
  4. package/src/features/AgentSetting/AgentMeta/index.tsx +35 -50
  5. package/src/features/AgentSetting/AgentPlugin/index.tsx +9 -15
  6. package/src/features/AgentSetting/AgentPrompt/index.tsx +10 -11
  7. package/src/features/AgentSetting/StoreUpdater.tsx +22 -0
  8. package/src/features/AgentSetting/index.tsx +25 -0
  9. package/src/features/AgentSetting/store/action.ts +212 -0
  10. package/src/features/AgentSetting/store/index.ts +12 -0
  11. package/src/features/AgentSetting/store/initialState.ts +28 -0
  12. package/src/features/AgentSetting/store/reducers/config.ts +43 -0
  13. package/src/features/AgentSetting/store/reducers/meta.ts +21 -0
  14. package/src/helpers/export.ts +2 -1
  15. package/src/pages/chat/SessionList/List/Item/Actions.tsx +2 -1
  16. package/src/pages/chat/SessionList/List/Item/index.tsx +3 -2
  17. package/src/pages/chat/SessionList/List/index.tsx +2 -1
  18. package/src/pages/chat/[id]/index.page.tsx +2 -1
  19. package/src/pages/chat/[id]/setting/index.page.tsx +10 -16
  20. package/src/pages/chat/features/Conversation/ChatList/MessageExtra.tsx +2 -1
  21. package/src/pages/chat/features/Conversation/ChatList/index.tsx +2 -6
  22. package/src/pages/chat/features/Conversation/Input/Action.tsx +2 -1
  23. package/src/pages/chat/features/Conversation/Input/Token.tsx +2 -1
  24. package/src/pages/chat/features/Header/Mobile.tsx +2 -1
  25. package/src/pages/chat/features/Header/PC.tsx +2 -1
  26. package/src/pages/chat/features/Sidebar/SystemRole.tsx +2 -1
  27. package/src/pages/chat/features/Sidebar/Topic/index.tsx +2 -1
  28. package/src/pages/index.page.tsx +2 -1
  29. package/src/pages/settings/Settings/Agent.tsx +13 -12
  30. package/src/store/global/slices/agent.ts +6 -57
  31. package/src/store/session/index.ts +0 -1
  32. package/src/store/session/initialState.ts +1 -3
  33. package/src/store/session/slices/agentConfig/action.ts +0 -193
  34. package/src/store/session/slices/agentConfig/initialState.ts +0 -18
  35. package/src/store/session/slices/agentConfig/selectors.ts +0 -9
  36. package/src/store/session/slices/chat/actions/message.ts +6 -6
  37. package/src/types/session.ts +8 -1
  38. package/src/features/AgentSetting/index.ts +0 -4
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 0.63.2](https://github.com/lobehub/lobe-chat/compare/v0.63.1...v0.63.2)
6
+
7
+ <sup>Released on **2023-08-27**</sup>
8
+
9
+ #### ♻ Code Refactoring
10
+
11
+ - **misc**: Refactor AgentSettings.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Code refactoring
19
+
20
+ - **misc**: Refactor AgentSettings, closes [#121](https://github.com/lobehub/lobe-chat/issues/121) ([1f29199](https://github.com/lobehub/lobe-chat/commit/1f29199))
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
+
30
+ ### [Version 0.63.1](https://github.com/lobehub/lobe-chat/compare/v0.63.0...v0.63.1)
31
+
32
+ <sup>Released on **2023-08-27**</sup>
33
+
34
+ #### ♻ Code Refactoring
35
+
36
+ - **misc**: Refactor the selectors import.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Code refactoring
44
+
45
+ - **misc**: Refactor the selectors import, closes [#120](https://github.com/lobehub/lobe-chat/issues/120) ([6646502](https://github.com/lobehub/lobe-chat/commit/6646502))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ## [Version 0.63.0](https://github.com/lobehub/lobe-chat/compare/v0.62.1...v0.63.0)
6
56
 
7
57
  <sup>Released on **2023-08-27**</sup>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.63.0",
3
+ "version": "0.63.2",
4
4
  "description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
5
5
  "keywords": [
6
6
  "chatbot",
@@ -1,6 +1,7 @@
1
1
  import { Form, ItemGroup, SliderWithInput } from '@lobehub/ui';
2
2
  import { ConfigProvider, Input, Select, Switch } from 'antd';
3
3
  import { useTheme, useThemeMode } from 'antd-style';
4
+ import isEqual from 'fast-deep-equal';
4
5
  import { debounce } from 'lodash-es';
5
6
  import { BrainCog, LayoutList, MessageSquare, MessagesSquare } from 'lucide-react';
6
7
  import { memo, useMemo } from 'react';
@@ -8,24 +9,21 @@ import { useTranslation } from 'react-i18next';
8
9
 
9
10
  import SelectWithImg from '@/components/SelectWithImg';
10
11
  import { FORM_STYLE } from '@/const/layoutTokens';
11
- import { AgentAction } from '@/store/session/slices/agentConfig';
12
12
  import { LanguageModel } from '@/types/llm';
13
- import type { LobeAgentConfig } from '@/types/session';
14
13
  import { LobeAgentConfigKeys } from '@/types/session';
15
14
 
15
+ import { useStore } from '../store';
16
+
16
17
  type SettingItemGroup = ItemGroup & {
17
18
  children: {
18
19
  name?: LobeAgentConfigKeys | string | string[];
19
20
  }[];
20
21
  };
21
22
 
22
- export interface AgentConfigProps {
23
- config: LobeAgentConfig;
24
- updateConfig: AgentAction['updateAgentConfig'];
25
- }
26
-
27
- const AgentConfig = memo<AgentConfigProps>(({ config, updateConfig }) => {
23
+ const AgentConfig = memo(() => {
28
24
  const { t } = useTranslation('setting');
25
+ const updateConfig = useStore((s) => s.setAgentConfig);
26
+ const config = useStore((s) => s.config, isEqual);
29
27
  const theme = useTheme();
30
28
  const { isDarkMode } = useThemeMode();
31
29
 
@@ -1,38 +1,28 @@
1
1
  import { Form, type FormItemProps, Icon, type ItemGroup, Tooltip } from '@lobehub/ui';
2
2
  import { Button } from 'antd';
3
+ import isEqual from 'fast-deep-equal';
3
4
  import { UserCircle, Wand2 } from 'lucide-react';
4
- import { ReactNode, memo, useMemo } from 'react';
5
+ import { memo, useMemo } from 'react';
5
6
  import { useTranslation } from 'react-i18next';
6
7
 
7
8
  import EmojiPicker from '@/components/EmojiPicker';
8
9
  import { FORM_STYLE } from '@/const/layoutTokens';
9
- import { AgentAction, SessionLoadingState } from '@/store/session/slices/agentConfig';
10
- import { MetaData } from '@/types/meta';
11
- import { LobeAgentConfig } from '@/types/session';
12
10
 
11
+ import { useStore } from '../store';
12
+ import { SessionLoadingState } from '../store/initialState';
13
13
  import AutoGenerateInput from './AutoGenerateInput';
14
14
  import BackgroundSwatches from './BackgroundSwatches';
15
15
 
16
- export interface Autocomplete {
17
- autocompleteMeta: AgentAction['autocompleteMeta'];
18
- autocompleteSessionAgentMeta: AgentAction['autocompleteSessionAgentMeta'];
19
- id: string | null;
20
- loading: SessionLoadingState;
21
- }
22
-
23
- export interface AgentMetaProps {
24
- autocomplete?: Autocomplete;
25
- config: LobeAgentConfig;
26
- meta: MetaData;
27
- updateMeta: AgentAction['updateAgentMeta'];
28
- }
29
-
30
- const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete }) => {
16
+ const AgentMeta = memo(() => {
31
17
  const { t } = useTranslation('setting');
32
-
33
- const hasSystemRole = useMemo(() => !!config.systemRole, [config]);
34
-
35
- let extra: ReactNode | undefined;
18
+ const [hasSystemRole, updateMeta, autocompleteMeta, autocompleteAllMeta] = useStore((s) => [
19
+ !!s.config.systemRole,
20
+ s.setAgentMeta,
21
+ s.autocompleteMeta,
22
+ s.autocompleteAllMeta,
23
+ ]);
24
+ const loading = useStore((s) => s.autocompleteLoading);
25
+ const meta = useStore((s) => s.meta, isEqual);
36
26
 
37
27
  const basic = [
38
28
  {
@@ -49,15 +39,16 @@ const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete
49
39
  ];
50
40
 
51
41
  const autocompleteItems: FormItemProps[] = basic.map((item) => {
52
- const handleGenerate = () => autocomplete?.autocompleteMeta(item.key as keyof typeof meta);
53
42
  return {
54
43
  children: (
55
44
  <AutoGenerateInput
56
- loading={autocomplete?.loading[item.key as keyof SessionLoadingState]}
45
+ loading={loading[item.key as keyof SessionLoadingState]}
57
46
  onChange={(e) => {
58
47
  updateMeta({ [item.key]: e.target.value });
59
48
  }}
60
- onGenerate={autocomplete ? handleGenerate : undefined}
49
+ onGenerate={() => {
50
+ autocompleteMeta(item.key as keyof typeof meta);
51
+ }}
61
52
  placeholder={item.placeholder}
62
53
  value={meta[item.key as keyof typeof meta]}
63
54
  />
@@ -66,28 +57,6 @@ const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete
66
57
  };
67
58
  });
68
59
 
69
- if (autocomplete) {
70
- const { autocompleteSessionAgentMeta, loading, id } = autocomplete;
71
-
72
- extra = (
73
- <Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
74
- <Button
75
- disabled={!hasSystemRole}
76
- icon={<Icon icon={Wand2} />}
77
- loading={Object.values(loading).some((i) => !!i)}
78
- onClick={(e: any) => {
79
- e.stopPropagation();
80
- if (!id) return;
81
- autocompleteSessionAgentMeta(id, true);
82
- }}
83
- size={'small'}
84
- >
85
- {t('autoGenerate', { ns: 'common' })}
86
- </Button>
87
- </Tooltip>
88
- );
89
- }
90
-
91
60
  const metaData: ItemGroup = useMemo(
92
61
  () => ({
93
62
  children: [
@@ -115,11 +84,27 @@ const AgentMeta = memo<AgentMetaProps>(({ config, meta, updateMeta, autocomplete
115
84
  },
116
85
  ...autocompleteItems,
117
86
  ],
118
- extra: extra,
87
+ extra: (
88
+ <Tooltip title={t('autoGenerateTooltip', { ns: 'common' })}>
89
+ <Button
90
+ disabled={!hasSystemRole}
91
+ icon={<Icon icon={Wand2} />}
92
+ loading={Object.values(loading).some((i) => !!i)}
93
+ onClick={(e: any) => {
94
+ e.stopPropagation();
95
+
96
+ autocompleteAllMeta(true);
97
+ }}
98
+ size={'small'}
99
+ >
100
+ {t('autoGenerate', { ns: 'common' })}
101
+ </Button>
102
+ </Tooltip>
103
+ ),
119
104
  icon: UserCircle,
120
105
  title: t('settingAgent.title'),
121
106
  }),
122
- [autocompleteItems, extra, meta],
107
+ [autocompleteItems, meta],
123
108
  );
124
109
 
125
110
  return <Form items={[metaData]} {...FORM_STYLE} />;
@@ -10,9 +10,8 @@ import { FORM_STYLE } from '@/const/layoutTokens';
10
10
  import { transformPluginSettings } from '@/features/PluginSettings';
11
11
  import PluginSettingRender from '@/features/PluginSettings/PluginSettingRender';
12
12
  import { pluginHelpers, usePluginStore } from '@/store/plugin';
13
- import { useSessionStore } from '@/store/session';
14
- import { AgentAction, agentSelectors } from '@/store/session/slices/agentConfig';
15
- import { LobeAgentConfig } from '@/types/session';
13
+
14
+ import { useStore } from '../store';
16
15
 
17
16
  const useStyles = createStyles(({ css, token }) => ({
18
17
  avatar: css`
@@ -36,14 +35,13 @@ const useStyles = createStyles(({ css, token }) => ({
36
35
  `,
37
36
  }));
38
37
 
39
- export interface AgentPluginProps {
40
- config: LobeAgentConfig;
41
- updateConfig: AgentAction['toggleAgentPlugin'];
42
- }
43
-
44
- const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
38
+ const AgentPlugin = memo(() => {
45
39
  const { t } = useTranslation('setting');
46
40
  const { styles } = useStyles();
41
+
42
+ const updateConfig = useStore((s) => s.toggleAgentPlugin);
43
+ const [plugins, hasPlugin] = useStore((s) => [s.config.plugins || [], !!s.config.plugins]);
44
+
47
45
  const [useFetchPluginList, fetchPluginManifest, updatePluginSettings] = usePluginStore((s) => [
48
46
  s.useFetchPluginList,
49
47
  s.fetchPluginManifest,
@@ -54,8 +52,6 @@ const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
54
52
  const pluginsSettings = usePluginStore((s) => s.pluginsSettings, isEqual);
55
53
  const { data } = useFetchPluginList();
56
54
 
57
- const plugins = useSessionStore(agentSelectors.currentAgentPlugins);
58
-
59
55
  const loadingItem = {
60
56
  avatar: (
61
57
  <Skeleton
@@ -85,13 +81,11 @@ const AgentPlugin = memo<AgentPluginProps>(({ config, updateConfig }) => {
85
81
  <Switch
86
82
  checked={
87
83
  // 如果在加载中,说明激活了
88
- pluginManifestLoading[identifier] || !config.plugins
89
- ? false
90
- : config.plugins.includes(identifier)
84
+ pluginManifestLoading[identifier] || !hasPlugin ? false : plugins.includes(identifier)
91
85
  }
92
86
  loading={pluginManifestLoading[identifier]}
93
87
  onChange={(checked) => {
94
- updateConfig(identifier, checked);
88
+ updateConfig(identifier);
95
89
  if (checked) {
96
90
  fetchPluginManifest(identifier);
97
91
  }
@@ -3,14 +3,14 @@ import { Button } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import { encode } from 'gpt-tokenizer';
5
5
  import { Bot } from 'lucide-react';
6
- import { memo, useMemo, useState } from 'react';
6
+ import { memo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { Flexbox } from 'react-layout-kit';
9
9
 
10
10
  import { FORM_STYLE } from '@/const/layoutTokens';
11
11
  import { ModelTokens } from '@/const/modelTokens';
12
- import { AgentAction } from '@/store/session/slices/agentConfig';
13
- import { LobeAgentConfig } from '@/types/session';
12
+
13
+ import { useStore } from '../store';
14
14
 
15
15
  export const useStyles = createStyles(({ css, token }) => ({
16
16
  markdown: css`
@@ -21,17 +21,16 @@ export const useStyles = createStyles(({ css, token }) => ({
21
21
  `,
22
22
  }));
23
23
 
24
- export interface AgentPromptProps {
25
- config: LobeAgentConfig;
26
- updateConfig: AgentAction['updateAgentConfig'];
27
- }
28
-
29
- const AgentPrompt = memo<AgentPromptProps>(({ config, updateConfig }) => {
24
+ const AgentPrompt = memo(() => {
30
25
  const { t } = useTranslation('setting');
31
26
  const { styles } = useStyles();
32
27
  const [editing, setEditing] = useState(false);
33
- const { systemRole, model } = config;
34
- const systemTokenCount = useMemo(() => encode(systemRole || '').length, [systemRole]);
28
+ const updateConfig = useStore((s) => s.setAgentConfig);
29
+ const [systemRole, model, systemTokenCount] = useStore((s) => [
30
+ s.config.systemRole,
31
+ s.config.model,
32
+ encode(s.config.systemRole || '').length,
33
+ ]);
35
34
 
36
35
  return (
37
36
  <FormGroup
@@ -0,0 +1,22 @@
1
+ import { memo } from 'react';
2
+ import { createStoreUpdater } from 'zustand-utils';
3
+
4
+ import { State, useStoreApi } from './store';
5
+
6
+ export type StoreUpdaterProps = Partial<
7
+ Pick<State, 'onMetaChange' | 'onConfigChange' | 'meta' | 'config'>
8
+ >;
9
+
10
+ const StoreUpdater = memo<StoreUpdaterProps>(({ onConfigChange, onMetaChange, meta, config }) => {
11
+ const storeApi = useStoreApi();
12
+ const useStoreUpdater = createStoreUpdater(storeApi);
13
+
14
+ useStoreUpdater('meta', meta);
15
+ useStoreUpdater('config', config);
16
+ useStoreUpdater('onConfigChange', onConfigChange);
17
+ useStoreUpdater('onMetaChange', onMetaChange);
18
+
19
+ return null;
20
+ });
21
+
22
+ export default StoreUpdater;
@@ -0,0 +1,25 @@
1
+ import { memo } from 'react';
2
+
3
+ import AgentConfig from './AgentConfig';
4
+ import AgentMeta from './AgentMeta';
5
+ import AgentPlugin from './AgentPlugin';
6
+ import AgentPrompt from './AgentPrompt';
7
+ import StoreUpdater, { StoreUpdaterProps } from './StoreUpdater';
8
+ import { Provider, createStore } from './store';
9
+
10
+ type AgentSettingsProps = StoreUpdaterProps;
11
+
12
+ const AgentSettings = memo<AgentSettingsProps>((props) => {
13
+ return (
14
+ <Provider createStore={createStore}>
15
+ <StoreUpdater {...props} />
16
+
17
+ <AgentPrompt />
18
+ <AgentMeta />
19
+ <AgentConfig />
20
+ <AgentPlugin />
21
+ </Provider>
22
+ );
23
+ });
24
+
25
+ export default AgentSettings;
@@ -0,0 +1,212 @@
1
+ import { StateCreator } from 'zustand/vanilla';
2
+
3
+ import { promptPickEmoji, promptSummaryAgentName, promptSummaryDescription } from '@/prompts/agent';
4
+ import { MetaData } from '@/types/meta';
5
+ import { LobeAgentConfig } from '@/types/session';
6
+ import { fetchPresetTaskResult } from '@/utils/fetch';
7
+ import { setNamespace } from '@/utils/storeDebug';
8
+
9
+ import { SessionLoadingState } from '../store/initialState';
10
+ import { State, initialState } from './initialState';
11
+ import { ConfigDispatch, configReducer } from './reducers/config';
12
+ import { MetaDataDispatch, metaDataReducer } from './reducers/meta';
13
+
14
+ /**
15
+ * 设置操作
16
+ */
17
+ export interface Action {
18
+ /**
19
+ * 自动选择表情
20
+ * @param id - 表情的 ID
21
+ */
22
+ autoPickEmoji: () => void;
23
+ /**
24
+ * 自动完成代理描述
25
+ * @param id - 代理的 ID
26
+ * @returns 一个 Promise,用于异步操作完成后的处理
27
+ */
28
+ autocompleteAgentDescription: () => Promise<void>;
29
+ /**
30
+ * 自动完成代理标题
31
+ * @param id - 代理的 ID
32
+ * @returns 一个 Promise,用于异步操作完成后的处理
33
+ */
34
+ autocompleteAgentTitle: () => Promise<void>;
35
+ /**
36
+ * 自动完成助理元数据
37
+ */
38
+ autocompleteAllMeta: (replace?: boolean) => void;
39
+ autocompleteMeta: (key: keyof MetaData) => void;
40
+ dispatchConfig: (payload: ConfigDispatch) => void;
41
+ dispatchMeta: (payload: MetaDataDispatch) => void;
42
+
43
+ resetAgentConfig: () => void;
44
+
45
+ resetAgentMeta: () => void;
46
+
47
+ setAgentConfig: (config: Partial<LobeAgentConfig>) => void;
48
+
49
+ setAgentMeta: (meta: Partial<MetaData>) => void;
50
+ streamUpdateMeta: (key: keyof MetaData) => any;
51
+ toggleAgentPlugin: (pluginId: string) => void;
52
+ /**
53
+ * 更新加载状态
54
+ * @param key - SessionLoadingState 的键
55
+ * @param value - 加载状态的值
56
+ */
57
+ updateLoadingState: (key: keyof SessionLoadingState, value: boolean) => void;
58
+ }
59
+
60
+ export type Store = Action & State;
61
+
62
+ const t = setNamespace('AgentSettings');
63
+
64
+ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, get) => ({
65
+ ...initialState,
66
+
67
+ autoPickEmoji: async () => {
68
+ const { config, dispatchMeta } = get();
69
+
70
+ const systemRole = config.systemRole;
71
+
72
+ const emoji = await fetchPresetTaskResult({
73
+ onLoadingChange: (loading) => {
74
+ get().updateLoadingState('avatar', loading);
75
+ },
76
+ params: promptPickEmoji(systemRole),
77
+ });
78
+
79
+ if (emoji) {
80
+ dispatchMeta({ type: 'update', value: { avatar: emoji } });
81
+ }
82
+ },
83
+ autocompleteAgentDescription: async () => {
84
+ const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
85
+
86
+ const systemRole = config.systemRole;
87
+
88
+ if (!systemRole) return;
89
+
90
+ const preValue = meta.description;
91
+
92
+ // 替换为 ...
93
+ dispatchMeta({ type: 'update', value: { description: '...' } });
94
+
95
+ fetchPresetTaskResult({
96
+ onError: () => {
97
+ dispatchMeta({ type: 'update', value: { description: preValue } });
98
+ },
99
+ onLoadingChange: (loading) => {
100
+ updateLoadingState('description', loading);
101
+ },
102
+ onMessageHandle: streamUpdateMeta('description'),
103
+ params: promptSummaryDescription(systemRole),
104
+ });
105
+ },
106
+ autocompleteAgentTitle: async () => {
107
+ const { dispatchMeta, config, meta, updateLoadingState, streamUpdateMeta } = get();
108
+
109
+ const systemRole = config.systemRole;
110
+
111
+ if (!systemRole) return;
112
+
113
+ const previousTitle = meta.title;
114
+
115
+ // 替换为 ...
116
+ dispatchMeta({ type: 'update', value: { title: '...' } });
117
+
118
+ fetchPresetTaskResult({
119
+ onError: () => {
120
+ dispatchMeta({ type: 'update', value: { title: previousTitle } });
121
+ },
122
+ onLoadingChange: (loading) => {
123
+ updateLoadingState('title', loading);
124
+ },
125
+ onMessageHandle: streamUpdateMeta('title'),
126
+ params: promptSummaryAgentName(systemRole),
127
+ });
128
+ },
129
+ autocompleteAllMeta: (replace) => {
130
+ const { meta } = get();
131
+
132
+ if (!meta.title || replace) {
133
+ get().autocompleteAgentTitle();
134
+ }
135
+
136
+ if (!meta.description || replace) {
137
+ get().autocompleteAgentDescription();
138
+ }
139
+
140
+ if (!meta.avatar || replace) {
141
+ get().autoPickEmoji();
142
+ }
143
+ },
144
+ autocompleteMeta: (key) => {
145
+ const { autoPickEmoji, autocompleteAgentTitle, autocompleteAgentDescription } = get();
146
+
147
+ switch (key) {
148
+ case 'avatar': {
149
+ autoPickEmoji();
150
+ return;
151
+ }
152
+
153
+ case 'description': {
154
+ autocompleteAgentDescription();
155
+ return;
156
+ }
157
+
158
+ case 'title': {
159
+ autocompleteAgentTitle();
160
+ }
161
+ }
162
+ },
163
+
164
+ dispatchConfig: (payload) => {
165
+ const nextConfig = configReducer(get().config, payload);
166
+
167
+ set({ config: nextConfig }, false, payload);
168
+
169
+ get().onConfigChange?.(nextConfig);
170
+ },
171
+ dispatchMeta: (payload) => {
172
+ const nextValue = metaDataReducer(get().meta, payload);
173
+
174
+ set({ meta: nextValue }, false, payload);
175
+
176
+ get().onMetaChange?.(nextValue);
177
+ },
178
+
179
+ resetAgentConfig: () => {
180
+ get().dispatchConfig({ type: 'reset' });
181
+ },
182
+ resetAgentMeta: () => {
183
+ get().dispatchMeta({ type: 'reset' });
184
+ },
185
+
186
+ setAgentConfig: (config) => {
187
+ get().dispatchConfig({ config, type: 'update' });
188
+ },
189
+ setAgentMeta: (meta) => {
190
+ get().dispatchMeta({ type: 'update', value: meta });
191
+ },
192
+
193
+ streamUpdateMeta: (key: keyof MetaData) => {
194
+ let value = '';
195
+ return (text: string) => {
196
+ value += text;
197
+ get().dispatchMeta({ type: 'update', value: { [key]: value } });
198
+ };
199
+ },
200
+
201
+ toggleAgentPlugin: (id) => {
202
+ get().dispatchConfig({ pluginId: id, type: 'togglePlugin' });
203
+ },
204
+
205
+ updateLoadingState: (key, value) => {
206
+ set(
207
+ { autocompleteLoading: { ...get().autocompleteLoading, [key]: value } },
208
+ false,
209
+ t('updateLoadingState', { key, value }),
210
+ );
211
+ },
212
+ });
@@ -0,0 +1,12 @@
1
+ import { StoreApi } from 'zustand';
2
+ import { createContext } from 'zustand-utils';
3
+ import { shallow } from 'zustand/shallow';
4
+ import { createWithEqualityFn } from 'zustand/traditional';
5
+
6
+ import { Store, store } from './action';
7
+
8
+ export type { State } from './initialState';
9
+
10
+ export const createStore = () => createWithEqualityFn(store, shallow);
11
+
12
+ export const { useStore, useStoreApi, Provider } = createContext<StoreApi<Store>>();
@@ -0,0 +1,28 @@
1
+ import { DEFAULT_AGENT_META } from '@/const/meta';
2
+ import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
3
+ import { MetaData } from '@/types/meta';
4
+ import { LobeAgentConfig } from '@/types/session';
5
+
6
+ export interface State {
7
+ autocompleteLoading: SessionLoadingState;
8
+ config: LobeAgentConfig;
9
+ meta: MetaData;
10
+
11
+ onConfigChange?: (config: LobeAgentConfig) => void;
12
+ onMetaChange?: (meta: MetaData) => void;
13
+ }
14
+ export type SessionLoadingState = Record<Partial<keyof MetaData>, boolean>;
15
+
16
+ export const initialState: State = {
17
+ // loading 中间态
18
+ autocompleteLoading: {
19
+ avatar: false,
20
+ backgroundColor: false,
21
+ description: false,
22
+ tag: false,
23
+ title: false,
24
+ },
25
+
26
+ config: DEFAULT_AGENT_CONFIG,
27
+ meta: DEFAULT_AGENT_META,
28
+ };
@@ -0,0 +1,43 @@
1
+ import { produce } from 'immer';
2
+ import { merge } from 'lodash-es';
3
+
4
+ import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
5
+ import { LobeAgentConfig } from '@/types/session';
6
+
7
+ export type ConfigDispatch =
8
+ | { config: Partial<any>; type: 'update' }
9
+ | { pluginId: string; type: 'togglePlugin' }
10
+ | { type: 'reset' };
11
+
12
+ export const configReducer = (state: LobeAgentConfig, payload: ConfigDispatch): LobeAgentConfig => {
13
+ switch (payload.type) {
14
+ case 'update': {
15
+ return produce(state, (draftState) => {
16
+ return merge({}, draftState, payload.config);
17
+ });
18
+ }
19
+
20
+ case 'togglePlugin': {
21
+ return produce(state, (config) => {
22
+ const { pluginId: id } = payload;
23
+ if (config.plugins === undefined) {
24
+ config.plugins = [id];
25
+ } else {
26
+ if (config.plugins.includes(id)) {
27
+ config.plugins.splice(config.plugins.indexOf(id), 1);
28
+ } else {
29
+ config.plugins.push(id);
30
+ }
31
+ }
32
+ });
33
+ }
34
+
35
+ case 'reset': {
36
+ return DEFAULT_AGENT_CONFIG;
37
+ }
38
+
39
+ default: {
40
+ return state;
41
+ }
42
+ }
43
+ };