@lobehub/chat 0.55.1 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -0
- package/package.json +3 -3
- package/src/const/fetch.ts +2 -0
- package/src/const/plugin.ts +1 -0
- package/src/features/AgentSetting/AgentPlugin/index.tsx +107 -60
- package/src/features/PluginSettings/PluginSettingRender.tsx +65 -0
- package/src/features/PluginSettings/index.tsx +65 -0
- package/src/hooks/useImportConfig.ts +7 -0
- package/src/locales/default/error.ts +30 -2
- package/src/locales/default/plugin.ts +1 -1
- package/src/locales/default/setting.ts +1 -0
- package/src/pages/api/auth.ts +2 -2
- package/src/pages/api/error.ts +3 -3
- package/src/pages/api/openai.ts +3 -3
- package/src/pages/chat/SessionList/List/Item/index.tsx +2 -2
- package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginError.tsx +28 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/Plugin/PluginSettings.tsx +61 -0
- package/src/pages/chat/features/Conversation/ChatList/Error/index.tsx +42 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/FunctionCall.tsx +62 -58
- package/src/pages/chat/features/Conversation/ChatList/index.tsx +15 -25
- package/src/pages/chat/features/Header/PluginTag.tsx +1 -1
- package/src/services/plugin.ts +11 -14
- package/src/services/pluginMarket.ts +10 -0
- package/src/store/global/selectors.ts +2 -2
- package/src/store/global/store.ts +7 -0
- package/src/store/plugin/action.ts +18 -7
- package/src/store/plugin/initialState.ts +3 -0
- package/src/store/plugin/reducers/manifest.ts +2 -2
- package/src/store/plugin/selectors.ts +17 -5
- package/src/store/plugin/store.ts +3 -1
- package/src/store/session/slices/chat/actions/message.ts +30 -21
- package/src/store/session/slices/chat/selectors/utils.test.ts +4 -2
- package/src/store/session/slices/chat/selectors/utils.ts +2 -2
- package/src/types/chatMessage.ts +9 -0
- package/src/types/fetch.ts +18 -14
- package/src/types/plugin.ts +2 -2
- package/src/utils/fetch.ts +24 -18
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Avatar } from '@lobehub/ui';
|
|
2
|
+
import { Button, Divider } from 'antd';
|
|
3
|
+
import { useTheme } from 'antd-style';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Center, Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import PluginSettingsConfig from '@/features/PluginSettings';
|
|
9
|
+
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
10
|
+
import { useSessionStore } from '@/store/session';
|
|
11
|
+
|
|
12
|
+
import { ErrorActionContainer, useStyles } from '../style';
|
|
13
|
+
|
|
14
|
+
interface PluginSettingsProps {
|
|
15
|
+
id: string;
|
|
16
|
+
pluginIdentifier: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const PluginSettings = memo<PluginSettingsProps>(({ id, pluginIdentifier }) => {
|
|
20
|
+
const { styles } = useStyles();
|
|
21
|
+
|
|
22
|
+
const { t } = useTranslation('error');
|
|
23
|
+
const theme = useTheme();
|
|
24
|
+
const [resend, deleteMessage] = useSessionStore((s) => [s.resendMessage, s.deleteMessage]);
|
|
25
|
+
const plugin = usePluginStore(pluginSelectors.getPluginMetaById(pluginIdentifier));
|
|
26
|
+
const manifest = usePluginStore(pluginSelectors.getPluginManifestSettingsById(pluginIdentifier));
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<ErrorActionContainer>
|
|
30
|
+
<Center gap={16} style={{ maxWidth: 400 }}>
|
|
31
|
+
<Avatar
|
|
32
|
+
avatar={plugin?.meta.avatar || '⚙️'}
|
|
33
|
+
background={theme.colorFillContent}
|
|
34
|
+
gap={12}
|
|
35
|
+
size={80}
|
|
36
|
+
/>
|
|
37
|
+
<Flexbox style={{ fontSize: 20 }}>
|
|
38
|
+
{t('pluginSettings.title', { name: plugin?.meta.title })}
|
|
39
|
+
</Flexbox>
|
|
40
|
+
<Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
|
|
41
|
+
<Divider style={{ margin: '0 16px' }} />
|
|
42
|
+
{manifest.settings && (
|
|
43
|
+
<PluginSettingsConfig id={manifest.identifier} settings={manifest.settings} />
|
|
44
|
+
)}
|
|
45
|
+
<Button
|
|
46
|
+
block
|
|
47
|
+
onClick={() => {
|
|
48
|
+
resend(id);
|
|
49
|
+
deleteMessage(id);
|
|
50
|
+
}}
|
|
51
|
+
style={{ marginTop: 8 }}
|
|
52
|
+
type={'primary'}
|
|
53
|
+
>
|
|
54
|
+
{t('unlock.confirm')}
|
|
55
|
+
</Button>
|
|
56
|
+
</Center>
|
|
57
|
+
</ErrorActionContainer>
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
export default PluginSettings;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { IPluginErrorType, PluginErrorType } from '@lobehub/chat-plugin-sdk';
|
|
2
|
+
import { RenderErrorMessage } from '@lobehub/ui';
|
|
3
|
+
|
|
4
|
+
import { ChatMessage } from '@/types/chatMessage';
|
|
5
|
+
import { ChatErrorType, ErrorType } from '@/types/fetch';
|
|
6
|
+
|
|
7
|
+
import InvalidAccess from './InvalidAccess';
|
|
8
|
+
import OpenAiBizError from './OpenAiBizError';
|
|
9
|
+
import PluginError from './Plugin/PluginError';
|
|
10
|
+
import PluginSettings from './Plugin/PluginSettings';
|
|
11
|
+
|
|
12
|
+
export const renderErrorMessage: RenderErrorMessage = (error, message: ChatMessage) => {
|
|
13
|
+
switch (error.type as IPluginErrorType) {
|
|
14
|
+
case PluginErrorType.PluginMarketIndexNotFound:
|
|
15
|
+
case PluginErrorType.PluginMarketIndexInvalid:
|
|
16
|
+
case PluginErrorType.PluginMetaInvalid:
|
|
17
|
+
case PluginErrorType.PluginMetaNotFound:
|
|
18
|
+
case PluginErrorType.PluginManifestInvalid:
|
|
19
|
+
case PluginErrorType.PluginManifestNotFound:
|
|
20
|
+
case PluginErrorType.PluginApiNotFound:
|
|
21
|
+
case PluginErrorType.PluginApiParamsError: {
|
|
22
|
+
return <PluginError content={(error as any).body} id={message.id} />;
|
|
23
|
+
}
|
|
24
|
+
case PluginErrorType.PluginSettingsInvalid: {
|
|
25
|
+
return (
|
|
26
|
+
message.plugin?.identifier && (
|
|
27
|
+
<PluginSettings id={message.id} pluginIdentifier={message.plugin?.identifier} />
|
|
28
|
+
)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
switch (error.type as ErrorType) {
|
|
34
|
+
case ChatErrorType.InvalidAccessCode: {
|
|
35
|
+
return <InvalidAccess id={message.id} />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
case ChatErrorType.OpenAIBizError: {
|
|
39
|
+
return <OpenAiBizError content={(error as any).body} id={message.id} />;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -7,77 +7,81 @@ import { useTranslation } from 'react-i18next';
|
|
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
|
8
8
|
|
|
9
9
|
import { pluginSelectors, usePluginStore } from '@/store/plugin';
|
|
10
|
-
import { OpenAIFunctionCall } from '@/types/chatMessage';
|
|
11
10
|
|
|
12
11
|
import PluginResult from './PluginResultRender';
|
|
13
12
|
import { useStyles } from './style';
|
|
14
13
|
|
|
15
14
|
export interface FunctionCallProps {
|
|
15
|
+
arguments?: string;
|
|
16
|
+
command?: any;
|
|
16
17
|
content: string;
|
|
17
|
-
|
|
18
|
+
id?: string;
|
|
18
19
|
loading?: boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const FunctionCall = memo<FunctionCallProps>(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const FunctionCall = memo<FunctionCallProps>(
|
|
23
|
+
({ arguments: requestArgs = '{}', command, loading, content, id = 'unknown' }) => {
|
|
24
|
+
const { t } = useTranslation('plugin');
|
|
25
|
+
const { styles } = useStyles();
|
|
26
|
+
const [open, setOpen] = useState(false);
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
<Avatar avatar={item?.meta.avatar} size={32} />
|
|
29
|
-
) : (
|
|
30
|
-
<Icon icon={LucideToyBrick} />
|
|
31
|
-
);
|
|
28
|
+
const item = usePluginStore(pluginSelectors.getPluginMetaById(id));
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
const avatar = item?.meta.avatar ? (
|
|
31
|
+
<Avatar avatar={item?.meta.avatar} size={32} />
|
|
32
|
+
) : (
|
|
33
|
+
<Icon icon={LucideToyBrick} />
|
|
34
|
+
);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
36
|
+
const args = JSON.stringify(command, null, 2);
|
|
37
|
+
const params = JSON.stringify(JSON.parse(requestArgs), null, 2);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<Flexbox gap={8}>
|
|
41
|
+
<Flexbox
|
|
42
|
+
align={'center'}
|
|
43
|
+
className={styles.container}
|
|
44
|
+
gap={8}
|
|
45
|
+
horizontal
|
|
46
|
+
onClick={() => {
|
|
47
|
+
setOpen(!open);
|
|
48
|
+
}}
|
|
49
|
+
>
|
|
50
|
+
{loading ? (
|
|
51
|
+
<div>
|
|
52
|
+
<LoadingOutlined />
|
|
53
|
+
</div>
|
|
54
|
+
) : (
|
|
55
|
+
avatar
|
|
56
|
+
)}
|
|
57
|
+
{t(`plugins.${id}` as any, { ns: 'plugin' })}
|
|
58
|
+
<Icon icon={open ? LucideChevronUp : LucideChevronDown} />
|
|
59
|
+
</Flexbox>
|
|
60
|
+
{open && (
|
|
61
|
+
<Tabs
|
|
62
|
+
items={[
|
|
63
|
+
{
|
|
64
|
+
children: <Highlighter language={'json'}>{args}</Highlighter>,
|
|
65
|
+
key: 'function_call',
|
|
66
|
+
label: t('debug.function_call'),
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
children: <Highlighter language={'json'}>{params}</Highlighter>,
|
|
70
|
+
key: 'arguments',
|
|
71
|
+
label: t('debug.arguments'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
children: <PluginResult content={content} />,
|
|
75
|
+
key: 'response',
|
|
76
|
+
label: t('debug.response'),
|
|
77
|
+
},
|
|
78
|
+
]}
|
|
79
|
+
style={{ maxWidth: 800 }}
|
|
80
|
+
/>
|
|
53
81
|
)}
|
|
54
|
-
{t(`plugins.${function_call?.name}` as any, { ns: 'plugin' })}
|
|
55
|
-
<Icon icon={open ? LucideChevronUp : LucideChevronDown} />
|
|
56
82
|
</Flexbox>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
{
|
|
61
|
-
children: <Highlighter language={'json'}>{args}</Highlighter>,
|
|
62
|
-
key: 'function_call',
|
|
63
|
-
label: t('debug.function_call'),
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
children: <Highlighter language={'json'}>{params}</Highlighter>,
|
|
67
|
-
key: 'arguments',
|
|
68
|
-
label: t('debug.arguments'),
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
children: <PluginResult content={content} />,
|
|
72
|
-
key: 'response',
|
|
73
|
-
label: t('debug.response'),
|
|
74
|
-
},
|
|
75
|
-
]}
|
|
76
|
-
style={{ maxWidth: 800 }}
|
|
77
|
-
/>
|
|
78
|
-
)}
|
|
79
|
-
</Flexbox>
|
|
80
|
-
);
|
|
81
|
-
});
|
|
83
|
+
);
|
|
84
|
+
},
|
|
85
|
+
);
|
|
82
86
|
|
|
83
87
|
export default FunctionCall;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatList,
|
|
1
|
+
import { ChatList, RenderMessage } from '@lobehub/ui';
|
|
2
2
|
import isEqual from 'fast-deep-equal';
|
|
3
3
|
import { memo, useCallback } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -11,27 +11,14 @@ import {
|
|
|
11
11
|
useSessionStore,
|
|
12
12
|
} from '@/store/session';
|
|
13
13
|
import { ChatMessage } from '@/types/chatMessage';
|
|
14
|
-
import { ErrorType } from '@/types/fetch';
|
|
15
14
|
import { isFunctionMessage } from '@/utils/message';
|
|
16
15
|
|
|
17
|
-
import
|
|
18
|
-
import OpenAiBizError from './Error/OpenAiBizError';
|
|
16
|
+
import { renderErrorMessage } from './Error';
|
|
19
17
|
import MessageExtra from './MessageExtra';
|
|
20
18
|
import FunctionCall from './Plugins/FunctionCall';
|
|
21
19
|
import PluginMessage from './Plugins/PluginMessage';
|
|
22
20
|
import SkeletonList from './SkeletonList';
|
|
23
21
|
|
|
24
|
-
const renderErrorMessage: RenderErrorMessage = (error, message) => {
|
|
25
|
-
switch (error.type as ErrorType) {
|
|
26
|
-
case 'InvalidAccessCode': {
|
|
27
|
-
return <InvalidAccess id={message.id} />;
|
|
28
|
-
}
|
|
29
|
-
case 'OpenAIBizError': {
|
|
30
|
-
return <OpenAiBizError content={(error as any).body} id={message.id} />;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
22
|
const List = () => {
|
|
36
23
|
const init = useSessionChatInit();
|
|
37
24
|
const { t } = useTranslation('common');
|
|
@@ -49,25 +36,28 @@ const List = () => {
|
|
|
49
36
|
|
|
50
37
|
const renderMessage: RenderMessage = useCallback(
|
|
51
38
|
(content, message: ChatMessage) => {
|
|
39
|
+
const id = message.plugin?.identifier || message.function_call?.name;
|
|
40
|
+
const command = message.plugin ?? message.function_call;
|
|
41
|
+
const args = command?.arguments;
|
|
42
|
+
const fcProps = {
|
|
43
|
+
arguments: args,
|
|
44
|
+
command,
|
|
45
|
+
content: message.content,
|
|
46
|
+
id,
|
|
47
|
+
loading: message.id === chatLoadingId,
|
|
48
|
+
};
|
|
49
|
+
|
|
52
50
|
if (message.role === 'function')
|
|
53
51
|
return (
|
|
54
52
|
<Flexbox gap={12}>
|
|
55
|
-
<FunctionCall
|
|
56
|
-
content={message.content}
|
|
57
|
-
function_call={message.function_call}
|
|
58
|
-
loading={message.id === chatLoadingId}
|
|
59
|
-
/>
|
|
53
|
+
<FunctionCall {...fcProps} />
|
|
60
54
|
<PluginMessage loading={message.id === chatLoadingId} {...message} />
|
|
61
55
|
</Flexbox>
|
|
62
56
|
);
|
|
63
57
|
|
|
64
58
|
if (message.role === 'assistant') {
|
|
65
59
|
return isFunctionMessage(message.content) || !!message.function_call ? (
|
|
66
|
-
<FunctionCall
|
|
67
|
-
content={message.content}
|
|
68
|
-
function_call={message.function_call}
|
|
69
|
-
loading={message.id === chatLoadingId}
|
|
70
|
-
/>
|
|
60
|
+
<FunctionCall {...fcProps} />
|
|
71
61
|
) : (
|
|
72
62
|
content
|
|
73
63
|
);
|
|
@@ -19,7 +19,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
|
|
|
19
19
|
if (plugins.length === 0) return null;
|
|
20
20
|
|
|
21
21
|
const items: MenuProps['items'] = plugins.map((id) => {
|
|
22
|
-
const item = list?.find((p) => p.
|
|
22
|
+
const item = list?.find((p) => p.identifier === id);
|
|
23
23
|
return {
|
|
24
24
|
icon: (
|
|
25
25
|
<Avatar avatar={item?.meta.avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />
|
package/src/services/plugin.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { LOBE_PLUGIN_SETTINGS } from '@/const/fetch';
|
|
4
|
+
import { usePluginStore } from '@/store/plugin';
|
|
5
|
+
import { getMessageError } from '@/utils/fetch';
|
|
6
6
|
|
|
7
7
|
import { URLS } from './url';
|
|
8
8
|
|
|
@@ -12,30 +12,27 @@ interface FetchChatModelOptions {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* 请求插件结果´
|
|
16
16
|
*/
|
|
17
17
|
export const fetchPlugin = async (
|
|
18
18
|
params: PluginRequestPayload,
|
|
19
19
|
options?: FetchChatModelOptions,
|
|
20
20
|
) => {
|
|
21
|
+
const settings = usePluginStore.getState().pluginsSettings?.[params.identifier];
|
|
22
|
+
|
|
21
23
|
const res = await fetch(URLS.plugins, {
|
|
22
24
|
body: JSON.stringify(params),
|
|
23
25
|
headers: {
|
|
24
26
|
'Content-Type': 'application/json',
|
|
25
|
-
[
|
|
27
|
+
[LOBE_PLUGIN_SETTINGS]: JSON.stringify(settings) || '',
|
|
26
28
|
},
|
|
27
29
|
method: 'POST',
|
|
28
30
|
signal: options?.signal,
|
|
29
31
|
});
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
throw await getMessageError(res);
|
|
35
|
+
}
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
* 请求插件列表
|
|
36
|
-
*/
|
|
37
|
-
export const getPluginList = async () => {
|
|
38
|
-
const res = await fetch(PLUGINS_INDEX_URL);
|
|
39
|
-
|
|
40
|
-
return res.json();
|
|
37
|
+
return await res.text();
|
|
41
38
|
};
|
|
@@ -11,10 +11,10 @@ const currentSettings = (s: GlobalStore) => merge({}, DEFAULT_SETTINGS, s.settin
|
|
|
11
11
|
const currentDefaultAgent = (s: GlobalStore) => merge({}, DEFAULT_AGENT, s.settings.defaultAgent);
|
|
12
12
|
|
|
13
13
|
const currentAgentConfig = (s: GlobalStore) =>
|
|
14
|
-
merge({}, DEFAULT_AGENT_CONFIG, s.
|
|
14
|
+
merge({}, DEFAULT_AGENT_CONFIG, currentDefaultAgent(s).config);
|
|
15
15
|
|
|
16
16
|
const currentAgentMeta = (s: GlobalStore) =>
|
|
17
|
-
merge({}, DEFAULT_AGENT_META, s.
|
|
17
|
+
merge({}, DEFAULT_AGENT_META, currentDefaultAgent(s).meta);
|
|
18
18
|
|
|
19
19
|
export const exportSettings = (s: GlobalStore) => {
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -3,6 +3,7 @@ import { shallow } from 'zustand/shallow';
|
|
|
3
3
|
import { createWithEqualityFn } from 'zustand/traditional';
|
|
4
4
|
import { StateCreator } from 'zustand/vanilla';
|
|
5
5
|
|
|
6
|
+
import { DEFAULT_AGENT } from '@/const/settings';
|
|
6
7
|
import { isDev } from '@/utils/env';
|
|
7
8
|
|
|
8
9
|
import { type GlobalState, initialState } from './initialState';
|
|
@@ -25,6 +26,12 @@ const createStore: StateCreator<GlobalStore, [['zustand/devtools', never]]> = (.
|
|
|
25
26
|
type GlobalPersist = Pick<GlobalStore, 'preference' | 'settings'>;
|
|
26
27
|
|
|
27
28
|
const persistOptions: PersistOptions<GlobalStore, GlobalPersist> = {
|
|
29
|
+
merge: (persistedState, currentState) => {
|
|
30
|
+
if (!(persistedState as GlobalPersist).settings.defaultAgent) {
|
|
31
|
+
currentState.settings.defaultAgent = DEFAULT_AGENT;
|
|
32
|
+
}
|
|
33
|
+
return currentState;
|
|
34
|
+
},
|
|
28
35
|
name: 'LOBE_SETTINGS',
|
|
29
36
|
partialize: (s) => ({
|
|
30
37
|
preference: s.preference,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
LobeChatPluginManifest,
|
|
3
3
|
LobeChatPluginsMarketIndex,
|
|
4
4
|
pluginManifestSchema,
|
|
5
5
|
} from '@lobehub/chat-plugin-sdk';
|
|
6
6
|
import { message } from 'antd';
|
|
7
7
|
import { produce } from 'immer';
|
|
8
|
-
import { uniq } from 'lodash-es';
|
|
8
|
+
import { merge, uniq } from 'lodash-es';
|
|
9
9
|
import useSWR, { SWRResponse } from 'swr';
|
|
10
10
|
import { StateCreator } from 'zustand/vanilla';
|
|
11
11
|
|
|
12
|
-
import { getPluginList } from '@/services/
|
|
12
|
+
import { getPluginList } from '@/services/pluginMarket';
|
|
13
|
+
import { pluginSelectors } from '@/store/plugin/selectors';
|
|
13
14
|
import { LobeSessions } from '@/types/session';
|
|
14
15
|
import { setNamespace } from '@/utils/storeDebug';
|
|
15
16
|
|
|
@@ -26,6 +27,7 @@ export interface PluginAction {
|
|
|
26
27
|
dispatchPluginManifest: (payload: PluginDispatch) => void;
|
|
27
28
|
fetchPluginManifest: (name: string) => Promise<void>;
|
|
28
29
|
updateManifestLoadingState: (key: string, value: boolean | undefined) => void;
|
|
30
|
+
updatePluginSettings: <T>(id: string, settings: Partial<T>) => void;
|
|
29
31
|
useFetchPluginList: () => SWRResponse<LobeChatPluginsMarketIndex>;
|
|
30
32
|
}
|
|
31
33
|
|
|
@@ -51,7 +53,6 @@ export const createPluginSlice: StateCreator<
|
|
|
51
53
|
|
|
52
54
|
await Promise.all(plugins.map((name) => fetchPluginManifest(name)));
|
|
53
55
|
|
|
54
|
-
console.log('fetched');
|
|
55
56
|
set({ manifestPrepared: true }, false, t('checkLocalEnabledPlugins'));
|
|
56
57
|
},
|
|
57
58
|
dispatchPluginManifest: (payload) => {
|
|
@@ -62,7 +63,7 @@ export const createPluginSlice: StateCreator<
|
|
|
62
63
|
},
|
|
63
64
|
|
|
64
65
|
fetchPluginManifest: async (name) => {
|
|
65
|
-
const plugin =
|
|
66
|
+
const plugin = pluginSelectors.getPluginMetaById(name)(get());
|
|
66
67
|
// 1. 校验文件
|
|
67
68
|
|
|
68
69
|
if (!plugin) return;
|
|
@@ -74,7 +75,7 @@ export const createPluginSlice: StateCreator<
|
|
|
74
75
|
|
|
75
76
|
// 2. 发送请求
|
|
76
77
|
get().updateManifestLoadingState(name, true);
|
|
77
|
-
let data:
|
|
78
|
+
let data: LobeChatPluginManifest | null;
|
|
78
79
|
|
|
79
80
|
try {
|
|
80
81
|
const res = await fetch(plugin.manifest);
|
|
@@ -100,7 +101,7 @@ export const createPluginSlice: StateCreator<
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
// 4. 存储 manifest 信息
|
|
103
|
-
get().dispatchPluginManifest({ id: plugin.
|
|
104
|
+
get().dispatchPluginManifest({ id: plugin.identifier, plugin: data, type: 'addManifest' });
|
|
104
105
|
},
|
|
105
106
|
|
|
106
107
|
updateManifestLoadingState: (key, value) => {
|
|
@@ -112,6 +113,16 @@ export const createPluginSlice: StateCreator<
|
|
|
112
113
|
t('updateManifestLoadingState'),
|
|
113
114
|
);
|
|
114
115
|
},
|
|
116
|
+
|
|
117
|
+
updatePluginSettings: (id, settings) => {
|
|
118
|
+
set(
|
|
119
|
+
produce((draft) => {
|
|
120
|
+
draft.pluginsSettings[id] = merge({}, draft.pluginsSettings[id], settings);
|
|
121
|
+
}),
|
|
122
|
+
false,
|
|
123
|
+
t('updatePluginSettings'),
|
|
124
|
+
);
|
|
125
|
+
},
|
|
115
126
|
useFetchPluginList: () =>
|
|
116
127
|
useSWR<LobeChatPluginsMarketIndex>('fetchPluginList', getPluginList, {
|
|
117
128
|
onSuccess: (pluginMarketIndex) => {
|
|
@@ -3,12 +3,14 @@ import { LobeChatPluginMeta } from '@lobehub/chat-plugin-sdk';
|
|
|
3
3
|
import { PluginManifestMap } from '@/types/plugin';
|
|
4
4
|
|
|
5
5
|
export type PluginManifestLoadingState = Record<string, boolean>;
|
|
6
|
+
export type PluginsSettings = Record<string, any>;
|
|
6
7
|
|
|
7
8
|
export interface PluginStoreState {
|
|
8
9
|
manifestPrepared: boolean;
|
|
9
10
|
pluginList: LobeChatPluginMeta[];
|
|
10
11
|
pluginManifestLoading: PluginManifestLoadingState;
|
|
11
12
|
pluginManifestMap: PluginManifestMap;
|
|
13
|
+
pluginsSettings: PluginsSettings;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const initialState: PluginStoreState = {
|
|
@@ -16,4 +18,5 @@ export const initialState: PluginStoreState = {
|
|
|
16
18
|
pluginList: [],
|
|
17
19
|
pluginManifestLoading: {},
|
|
18
20
|
pluginManifestMap: {},
|
|
21
|
+
pluginsSettings: {},
|
|
19
22
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
|
2
2
|
import { produce } from 'immer';
|
|
3
3
|
|
|
4
4
|
import { PluginManifestMap } from '@/types/plugin';
|
|
5
5
|
|
|
6
|
-
type AddManifestDispatch = { id: string; plugin:
|
|
6
|
+
type AddManifestDispatch = { id: string; plugin: LobeChatPluginManifest; type: 'addManifest' };
|
|
7
7
|
type DeleteManifestDispatch = { id: string; type: 'deleteManifest' };
|
|
8
8
|
// type UpdateManifestDispatch = {
|
|
9
9
|
// id: string;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
|
|
2
|
+
|
|
1
3
|
import { PluginStoreState } from './initialState';
|
|
2
4
|
|
|
3
5
|
const enabledSchema =
|
|
@@ -9,15 +11,25 @@ const enabledSchema =
|
|
|
9
11
|
if (!enabledPlugins) return false;
|
|
10
12
|
|
|
11
13
|
// 如果存在 enabledPlugins,那么只启用 enabledPlugins 中的插件
|
|
12
|
-
return enabledPlugins.includes(p.
|
|
14
|
+
return enabledPlugins.includes(p.identifier);
|
|
13
15
|
})
|
|
14
|
-
.
|
|
16
|
+
.flatMap((manifest) =>
|
|
17
|
+
manifest.api.map((m) => ({
|
|
18
|
+
...m,
|
|
19
|
+
// 将插件的 identifier 作为前缀,避免重复
|
|
20
|
+
name: manifest.identifier + PLUGIN_SCHEMA_SEPARATOR + m.name,
|
|
21
|
+
})),
|
|
22
|
+
);
|
|
15
23
|
};
|
|
16
24
|
|
|
17
|
-
const
|
|
18
|
-
s.pluginList?.find((p) => p.
|
|
25
|
+
const getPluginMetaById = (id: string) => (s: PluginStoreState) =>
|
|
26
|
+
s.pluginList?.find((p) => p.identifier === id);
|
|
27
|
+
|
|
28
|
+
const getPluginManifestSettingsById = (id: string) => (s: PluginStoreState) =>
|
|
29
|
+
s.pluginManifestMap[id];
|
|
19
30
|
|
|
20
31
|
export const pluginSelectors = {
|
|
21
32
|
enabledSchema,
|
|
22
|
-
|
|
33
|
+
getPluginManifestSettingsById,
|
|
34
|
+
getPluginMetaById,
|
|
23
35
|
};
|
|
@@ -19,7 +19,7 @@ const createStore: StateCreator<PluginStore, [['zustand/devtools', never]]> = (.
|
|
|
19
19
|
|
|
20
20
|
// =============== persist 本地缓存中间件配置 ============ //
|
|
21
21
|
|
|
22
|
-
type SessionPersist = Pick<PluginStore, 'pluginList'>;
|
|
22
|
+
type SessionPersist = Pick<PluginStore, 'pluginList' | 'pluginManifestMap' | 'pluginsSettings'>;
|
|
23
23
|
|
|
24
24
|
const storeName = 'LOBE_PLUGIN';
|
|
25
25
|
|
|
@@ -28,6 +28,8 @@ const persistOptions: PersistOptions<PluginStore, SessionPersist> = {
|
|
|
28
28
|
|
|
29
29
|
partialize: (s) => ({
|
|
30
30
|
pluginList: s.pluginList,
|
|
31
|
+
pluginManifestMap: s.pluginManifestMap,
|
|
32
|
+
pluginsSettings: s.pluginsSettings,
|
|
31
33
|
}),
|
|
32
34
|
|
|
33
35
|
// 手动控制 Hydration ,避免 ssr 报错
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
|
|
1
2
|
import { template } from 'lodash-es';
|
|
2
3
|
import { StateCreator } from 'zustand/vanilla';
|
|
3
4
|
|
|
4
5
|
import { LOADING_FLAT } from '@/const/message';
|
|
6
|
+
import { PLUGIN_SCHEMA_SEPARATOR } from '@/const/plugin';
|
|
5
7
|
import { fetchChatModel } from '@/services/chatModel';
|
|
6
8
|
import { fetchPlugin } from '@/services/plugin';
|
|
7
9
|
import { SessionStore } from '@/store/session';
|
|
@@ -319,36 +321,43 @@ export const chatMessage: StateCreator<
|
|
|
319
321
|
const message = session.chats[id];
|
|
320
322
|
if (!message) return;
|
|
321
323
|
|
|
322
|
-
let payload:
|
|
324
|
+
let payload: PluginRequestPayload = { apiName: '', identifier: '' };
|
|
325
|
+
// 识别到内容是 function_call 的情况下
|
|
326
|
+
// 将 function_call 转换为 plugin request payload
|
|
323
327
|
if (message.content) {
|
|
324
|
-
const { function_call } = JSON.parse(message.content)
|
|
325
|
-
|
|
328
|
+
const { function_call } = JSON.parse(message.content) as {
|
|
329
|
+
function_call: OpenAIFunctionCall;
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const [identifier, apiName] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
|
|
333
|
+
payload = { apiName, arguments: function_call.arguments, identifier };
|
|
334
|
+
|
|
335
|
+
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
326
336
|
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: '' });
|
|
327
|
-
payload = function_call;
|
|
328
337
|
} else {
|
|
329
|
-
if (message.
|
|
330
|
-
payload = message.
|
|
338
|
+
if (message.plugin) {
|
|
339
|
+
payload = message.plugin;
|
|
331
340
|
}
|
|
332
341
|
}
|
|
333
342
|
|
|
334
|
-
if (!payload.
|
|
343
|
+
if (!payload.apiName) return;
|
|
335
344
|
|
|
336
|
-
// const fid = nanoid();
|
|
337
345
|
dispatchMessage({ id, key: 'role', type: 'updateMessage', value: 'function' });
|
|
338
|
-
dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.
|
|
339
|
-
dispatchMessage({ id, key: '
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
const data = await fetchPlugin(payload, { signal: abortController?.signal });
|
|
346
|
+
dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.identifier });
|
|
347
|
+
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
|
|
348
|
+
|
|
349
|
+
let data: string;
|
|
350
|
+
try {
|
|
351
|
+
const abortController = toggleChatLoading(true, id);
|
|
352
|
+
data = await fetchPlugin(payload, { signal: abortController?.signal });
|
|
353
|
+
} catch (error) {
|
|
354
|
+
dispatchMessage({ id, key: 'error', type: 'updateMessage', value: error });
|
|
355
|
+
|
|
356
|
+
data = '';
|
|
357
|
+
}
|
|
351
358
|
toggleChatLoading(false);
|
|
359
|
+
// 如果报错则结束了
|
|
360
|
+
if (!data) return;
|
|
352
361
|
|
|
353
362
|
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
|
|
354
363
|
|