♻️ refactor: 优化 plugin 文件夹命名以支持 standalone 类型的插件

This commit is contained in:
arvinxx
2023-10-22 22:22:34 +08:00
parent e195fdba09
commit 98860a8fb9
16 changed files with 153 additions and 67 deletions
@@ -5,22 +5,18 @@ import { useSessionStore } from '@/store/session';
import { chatSelectors } from '@/store/session/selectors';
import { isFunctionMessageAtStart } from '@/utils/message';
import FunctionCall from '../Plugins/FunctionCall';
import Inspector from '../Plugins/Inspector';
import { DefaultMessage } from './Default';
export const AssistantMessage: RenderMessage = memo(
({ id, plugin, function_call, content, ...props }) => {
const genFunctionCallProps = useSessionStore(chatSelectors.getFunctionMessageParams);
export const AssistantMessage: RenderMessage = memo(({ id, plugin, content, ...props }) => {
const fcProps = useSessionStore(chatSelectors.getFunctionMessageProps({ content, id, plugin }));
if (!isFunctionMessageAtStart(content))
return <DefaultMessage content={content} id={id} {...props} />;
if (!isFunctionMessageAtStart(content))
return <DefaultMessage content={content} id={id} {...props} />;
const fcProps = genFunctionCallProps({ content, function_call, id, plugin });
return (
<div id={id}>
<FunctionCall {...fcProps} />
</div>
);
},
);
return (
<div id={id}>
<Inspector {...fcProps} />
</div>
);
});
@@ -1,23 +1,24 @@
import { RenderMessage } from '@lobehub/ui';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { useSessionStore } from '@/store/session';
import { chatSelectors } from '@/store/session/selectors';
import FunctionCall from '../Plugins/FunctionCall';
import PluginMessage from '../Plugins/PluginMessage';
import Inspector from '../Plugins/Inspector';
import PluginRender from '../Plugins/Render';
export const FunctionMessage: RenderMessage = memo(
({ id, content, plugin, function_call, name }) => {
const genFunctionCallProps = useSessionStore(chatSelectors.getFunctionMessageParams);
const fcProps = genFunctionCallProps({ content, function_call, id, plugin });
export const FunctionMessage: RenderMessage = memo(({ id, content, plugin, name }) => {
const fcProps = useSessionStore(
chatSelectors.getFunctionMessageProps({ content, id, plugin }),
isEqual,
);
return (
<Flexbox gap={12} id={id}>
<FunctionCall {...fcProps} />
<PluginMessage content={content} loading={fcProps.loading} name={name} />
</Flexbox>
);
},
);
return (
<Flexbox gap={12} id={id}>
<Inspector {...fcProps} />
<PluginRender content={content} loading={fcProps.loading} name={name} type={fcProps.type} />
</Flexbox>
);
});
@@ -8,10 +8,10 @@ import { Flexbox } from 'react-layout-kit';
import { pluginHelpers, pluginSelectors, usePluginStore } from '@/store/plugin';
import PluginResult from './PluginResultRender';
import { useStyles } from './style';
import { useStyles } from '../style';
import PluginResult from './PluginResultJSON';
export interface FunctionCallProps {
export interface InspectorProps {
arguments?: string;
command?: any;
content: string;
@@ -19,7 +19,7 @@ export interface FunctionCallProps {
loading?: boolean;
}
const FunctionCall = memo<FunctionCallProps>(
const Inspector = memo<InspectorProps>(
({ arguments: requestArgs = '{}', command, loading, content, id = 'unknown' }) => {
const { t } = useTranslation('plugin');
const { styles } = useStyles();
@@ -87,4 +87,4 @@ const FunctionCall = memo<FunctionCallProps>(
},
);
export default FunctionCall;
export default Inspector;
@@ -11,13 +11,13 @@ import IFrameRender from './IFrameRender';
const SystemJsRender = dynamic(() => import('./SystemJsRender'), { ssr: false });
export interface FunctionMessageProps {
export interface PluginDefaultTypeProps {
content: string;
loading?: boolean;
name?: string;
}
const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
const { t } = useTranslation('plugin');
const manifest = usePluginStore((s) => s.pluginManifestMap[name || '']);
@@ -65,4 +65,4 @@ const PluginMessage = memo<FunctionMessageProps>(({ content, name }) => {
);
});
export default PluginMessage;
export default PluginDefaultType;
@@ -0,0 +1,39 @@
import { Skeleton } from 'antd';
import { memo, useRef, useState } from 'react';
interface IFrameRenderProps {
height?: number;
url: string;
width?: number;
}
const IFrameRender = memo<IFrameRenderProps>(({ url, width = 600, height = 300 }) => {
const [loading, setLoading] = useState(true);
const iframeRef = useRef<HTMLIFrameElement>(null);
return (
<>
{loading && <Skeleton active style={{ width }} />}
<iframe
// @ts-ignore
allowtransparency="true"
height={height}
hidden={loading}
onLoad={() => {
setLoading(false);
}}
ref={iframeRef}
src={url}
style={{
border: 0,
// iframe 在 color-scheme:dark 模式下无法透明
// refs: https://www.jianshu.com/p/bc5a37bb6a7b
colorScheme: 'light',
}}
width={width}
/>
</>
);
});
export default IFrameRender;
@@ -0,0 +1,23 @@
import { memo } from 'react';
import { usePluginStore } from '@/store/plugin';
import IFrameRender from './Iframe';
export interface PluginStandaloneTypeProps {
name?: string;
}
const PluginDefaultType = memo<PluginStandaloneTypeProps>(({ name = 'unknown' }) => {
const manifest = usePluginStore((s) => s.pluginManifestMap[name]);
if (!manifest?.ui) return;
const ui = manifest.ui;
if (!ui.url) return;
return <IFrameRender height={ui.height} url={ui.url} width={ui.width} />;
});
export default PluginDefaultType;
@@ -0,0 +1,26 @@
import { LobePluginType } from '@lobehub/chat-plugin-sdk';
import { memo } from 'react';
import DefaultType from './DefaultType';
import Standalone from './StandaloneType';
export interface PluginRenderProps {
content: string;
loading?: boolean;
name?: string;
type?: LobePluginType;
}
const PluginRender = memo<PluginRenderProps>(({ content, name, type }) => {
switch (type) {
case 'standalone': {
return <Standalone name={name} />;
}
default: {
return <DefaultType content={content} name={name} />;
}
}
});
export default PluginRender;
@@ -16,7 +16,8 @@ const t = setNamespace('chat/plugin');
* 插件方法
*/
export interface ChatPluginAction {
runPluginAutoMode: (id: string, payload: any) => Promise<void>;
runPluginDefaultType: (id: string, payload: any) => Promise<void>;
runPluginStandaloneType: (id: string, payload: any) => Promise<void>;
triggerFunctionCall: (id: string) => Promise<void>;
}
@@ -26,7 +27,7 @@ export const chatPlugin: StateCreator<
[],
ChatPluginAction
> = (set, get) => ({
runPluginAutoMode: async (id, payload) => {
runPluginDefaultType: async (id, payload) => {
const { dispatchMessage, coreProcessMessage, toggleChatLoading } = get();
let data: string;
try {
@@ -44,12 +45,13 @@ export const chatPlugin: StateCreator<
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: data });
const chats = chatSelectors.currentChats(get());
await coreProcessMessage(chats, id);
},
runPluginStandaloneType: async (id, payload) => {
console.log('触发standalone', id, payload);
},
triggerFunctionCall: async (id) => {
const { dispatchMessage, runPluginAutoMode } = get();
const { dispatchMessage, runPluginDefaultType, runPluginStandaloneType } = get();
const session = sessionSelectors.currentSession(get());
if (!session) return;
@@ -58,6 +60,7 @@ export const chatPlugin: StateCreator<
if (!message) return;
let payload: PluginRequestPayload = { apiName: '', identifier: '' };
// 识别到内容是 function_call 的情况下
// 将 function_call 转换为 plugin request payload
if (message.content) {
@@ -65,8 +68,14 @@ export const chatPlugin: StateCreator<
function_call: OpenAIFunctionCall;
};
const [identifier, apiName] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
payload = { apiName, arguments: function_call.arguments, identifier };
const [identifier, apiName, type] = function_call.name.split(PLUGIN_SCHEMA_SEPARATOR);
payload = {
apiName,
arguments: function_call.arguments,
identifier,
type: type ?? 'default',
};
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
dispatchMessage({ id, key: 'content', type: 'updateMessage', value: '' });
@@ -82,6 +91,7 @@ export const chatPlugin: StateCreator<
dispatchMessage({ id, key: 'name', type: 'updateMessage', value: payload.identifier });
dispatchMessage({ id, key: 'plugin', type: 'updateMessage', value: payload });
runPluginAutoMode(id, payload);
if (payload.type === 'standalone') runPluginStandaloneType(id, payload);
else runPluginDefaultType(id, payload);
},
});
+11 -20
View File
@@ -1,6 +1,6 @@
import { LobePluginType } from '@lobehub/chat-plugin-sdk';
import { t } from 'i18next';
import { FunctionCallProps } from '@/app/chat/features/Conversation/ChatList/Plugins/FunctionCall';
import { DEFAULT_INBOX_AVATAR, DEFAULT_USER_AVATAR } from '@/const/meta';
import { INBOX_SESSION_ID } from '@/const/session';
import { useGlobalStore } from '@/store/global';
@@ -92,22 +92,13 @@ export const chatsMessageString = (s: SessionStore): string => {
return chats.map((m) => m.content).join('');
};
export const getFunctionMessageParams =
(
s: SessionStore,
): ((
props: Pick<ChatMessage, 'plugin' | 'function_call' | 'content' | 'id'>,
) => FunctionCallProps) =>
({ plugin, function_call, content, id }) => {
const itemId = plugin?.identifier || function_call?.name;
const command = plugin ?? function_call;
const args = command?.arguments;
return {
arguments: args,
command,
content,
id: itemId,
loading: id === s.chatLoadingId,
};
};
export const getFunctionMessageProps =
({ plugin, content, id }: Pick<ChatMessage, 'plugin' | 'content' | 'id'>) =>
(s: SessionStore) => ({
arguments: plugin?.arguments,
command: plugin,
content,
id: plugin?.identifier,
loading: id === s.chatLoadingId,
type: plugin?.type as LobePluginType,
});
@@ -4,7 +4,7 @@ import {
currentChatsWithGuideMessage,
currentChatsWithHistoryConfig,
getChatsById,
getFunctionMessageParams,
getFunctionMessageProps,
} from './chat';
import { currentTopics, getTopicMessages } from './topic';
@@ -14,7 +14,7 @@ export const chatSelectors = {
currentChatsWithGuideMessage,
currentChatsWithHistoryConfig,
getChatsById,
getFunctionMessageParams,
getFunctionMessageProps,
};
export const topicSelectors = {