Skip to content

Commit

Permalink
♻️ refactor: refactor with new plugin implement with dexie db (lobehu…
Browse files Browse the repository at this point in the history
…b#596)

* ♻️ refactor: refactor with plugin dev

* ✅ test: add test for chain

* 🐛 fix: fix plugin display in tool

* ✅ test: add test for plugin service

* ✅ test: add test for plugin service

* 💚 ci: fix ci
  • Loading branch information
arvinxx authored Dec 8, 2023
1 parent 36e48bf commit f3b5e7b
Show file tree
Hide file tree
Showing 66 changed files with 1,743 additions and 1,020 deletions.
9 changes: 4 additions & 5 deletions src/app/chat/features/ChatHeader/PluginTag/PluginStatus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ interface PluginStatusProps {
}
const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
const { t } = useTranslation('common');
const [status, isCustom, fetchPluginManifest] = useToolStore((s) => [
const [status, isCustom, reinstallCustomPlugin] = useToolStore((s) => [
pluginSelectors.getPluginManifestLoadingStatus(id)(s),
customPluginSelectors.isCustomPlugin(id)(s),
s.installPlugin,
s.reinstallCustomPlugin,
]);

const manifest = useToolStore(pluginSelectors.getPluginManifestById(id));
Expand All @@ -37,7 +37,7 @@ const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
<ActionIcon
icon={LucideRotateCw}
onClick={() => {
fetchPluginManifest(id);
reinstallCustomPlugin(id);
}}
size={'small'}
title={t('retry')}
Expand Down Expand Up @@ -87,8 +87,7 @@ const PluginStatus = memo<PluginStatusProps>(({ title, id, deprecated }) => {
icon={RotateCwIcon}
onClick={(e) => {
e.stopPropagation();
fetchPluginManifest(id);
// form.validateFields(['manifest']);
reinstallCustomPlugin(id);
}}
size={'small'}
title={t('dev.meta.manifest.refresh', { ns: 'plugin' })}
Expand Down
16 changes: 11 additions & 5 deletions src/app/chat/features/ChatHeader/PluginTag/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,26 @@ export interface PluginTagProps {
}

const PluginTag = memo<PluginTagProps>(({ plugins }) => {
const list = useToolStore(pluginSelectors.displayPluginList);
const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
const displayPlugin = useToolStore(pluginSelectors.getPluginMetaById(plugins[0]), isEqual);

if (plugins.length === 0) return null;

const items: MenuProps['items'] = plugins.map((id) => {
const item = list.find((i) => i.identifier === id);
const isDeprecated = !item?.title;
const avatar = isDeprecated ? '♻️' : item?.avatar || '🧩';
const isDeprecated = !pluginHelpers.getPluginTitle(item?.meta);
const avatar = isDeprecated ? '♻️' : pluginHelpers.getPluginAvatar(item?.meta);

return {
icon: <Avatar avatar={avatar} size={24} style={{ marginLeft: -6, marginRight: 2 }} />,
key: id,
label: <PluginStatus deprecated={isDeprecated} id={id} title={item?.title} />,
label: (
<PluginStatus
deprecated={isDeprecated}
id={id}
title={pluginHelpers.getPluginTitle(item?.meta)}
/>
),
};
});

Expand All @@ -39,7 +45,7 @@ const PluginTag = memo<PluginTagProps>(({ plugins }) => {
<div>
<Tag>
{<Icon icon={LucideToyBrick} />}
{pluginHelpers.getPluginTitle(displayPlugin?.meta) || plugins[0]}
{pluginHelpers.getPluginTitle(displayPlugin) || plugins[0]}
{count > 1 && <div>({plugins.length - 1}+)</div>}
</Tag>
</div>
Expand Down
17 changes: 15 additions & 2 deletions src/app/chat/features/ChatInput/ActionBar/Tools/ToolItem.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { Checkbox } from 'antd';
import { Checkbox, Tag } from 'antd';
import { memo } from 'react';
import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import { useSessionStore } from '@/store/session';
import { agentSelectors } from '@/store/session/selectors';
import { useToolStore } from '@/store/tool';
import { customPluginSelectors } from '@/store/tool/selectors';

const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, label }) => {
const { t } = useTranslation('plugin');
const [checked, togglePlugin] = useSessionStore((s) => [
agentSelectors.currentAgentPlugins(s).includes(identifier),
s.togglePlugin,
]);

const isCustom = useToolStore((s) => customPluginSelectors.isCustomPlugin(identifier)(s));

return (
<Flexbox
gap={40}
Expand All @@ -22,7 +28,14 @@ const ToolItem = memo<{ identifier: string; label: string }>(({ identifier, labe
}}
padding={'8px 12px'}
>
{label}
<Flexbox align={'center'} gap={8} horizontal>
{label}
{isCustom && (
<Tag bordered={false} color={'gold'}>
{t('list.item.local.title')}
</Tag>
)}
</Flexbox>
<Checkbox
checked={checked}
onClick={(e) => {
Expand Down
8 changes: 4 additions & 4 deletions src/app/chat/features/ChatInput/ActionBar/Tools/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Flexbox } from 'react-layout-kit';
import PluginStore from '@/features/PluginStore';
import { useSessionStore } from '@/store/session';
import { agentSelectors } from '@/store/session/selectors';
import { useToolStore } from '@/store/tool';
import { pluginHelpers, useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/selectors';

import ToolItem from './ToolItem';
Expand All @@ -30,7 +30,7 @@ const useStyles = createStyles(({ css, prefixCls }) => ({

const Tools = memo(() => {
const { t } = useTranslation('setting');
const list = useToolStore(pluginSelectors.installedPlugins, isEqual);
const list = useToolStore(pluginSelectors.installedPluginMetaList, isEqual);
const enablePluginCount = useSessionStore((s) => agentSelectors.currentAgentPlugins(s).length);
const [open, setOpen] = useState(false);
const { styles } = useStyles();
Expand Down Expand Up @@ -58,15 +58,15 @@ const Tools = memo(() => {
children: [
...list.map((item) => ({
icon: item.meta?.avatar ? (
<Avatar avatar={item.meta?.avatar} size={24} />
<Avatar avatar={pluginHelpers.getPluginAvatar(item.meta)} size={24} />
) : (
<Icon icon={ToyBrick} size={{ fontSize: 16 }} style={{ padding: 4 }} />
),
key: item.identifier,
label: (
<ToolItem
identifier={item.identifier}
label={item.meta?.title || item.identifier}
label={pluginHelpers.getPluginTitle(item?.meta) || item.identifier}
/>
),
})),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,35 +23,37 @@ const PluginSettings: RenderErrorMessage['Render'] = memo(({ id, plugin }) => {
const manifest = useToolStore(pluginSelectors.getPluginManifestById(pluginIdentifier), isEqual);

return (
<ErrorActionContainer>
<Center gap={16} style={{ maxWidth: 400 }}>
<Avatar
avatar={pluginHelpers.getPluginAvatar(pluginMeta?.meta) || '⚙️'}
background={theme.colorFillContent}
gap={12}
size={80}
/>
<Flexbox style={{ fontSize: 20 }}>
{t('pluginSettings.title', { name: pluginHelpers.getPluginTitle(pluginMeta?.meta) })}
</Flexbox>
<Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
<Divider style={{ margin: '0 16px' }} />
{manifest.settings && (
<PluginSettingsConfig id={manifest.identifier} schema={manifest.settings} />
)}
<Button
block
onClick={() => {
resend(id);
deleteMessage(id);
}}
style={{ marginTop: 8 }}
type={'primary'}
>
{t('unlock.confirm')}
</Button>
</Center>
</ErrorActionContainer>
!!manifest && (
<ErrorActionContainer>
<Center gap={16} style={{ maxWidth: 400 }}>
<Avatar
avatar={pluginHelpers.getPluginAvatar(pluginMeta) || '⚙️'}
background={theme.colorFillContent}
gap={12}
size={80}
/>
<Flexbox style={{ fontSize: 20 }}>
{t('pluginSettings.title', { name: pluginHelpers.getPluginTitle(pluginMeta) })}
</Flexbox>
<Flexbox className={styles.desc}>{t('pluginSettings.desc')}</Flexbox>
<Divider style={{ margin: '0 16px' }} />
{manifest.settings && (
<PluginSettingsConfig id={manifest.identifier} schema={manifest.settings} />
)}
<Button
block
onClick={() => {
resend(id);
deleteMessage(id);
}}
style={{ marginTop: 8 }}
type={'primary'}
>
{t('unlock.confirm')}
</Button>
</Center>
</ErrorActionContainer>
)
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ const Inspector = memo<InspectorProps>(
const { styles } = useStyles();
const [open, setOpen] = useState(false);

const item = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
const showRightAction = useToolStore(pluginSelectors.hasPluginUI(id));
const pluginAvatar = pluginHelpers.getPluginAvatar(item?.meta);
const pluginTitle = pluginHelpers.getPluginTitle(item?.meta);
const pluginMeta = useToolStore(pluginSelectors.getPluginMetaById(id), isEqual);
const showRightAction = useToolStore(pluginSelectors.isPluginHasUI(id));
const pluginAvatar = pluginHelpers.getPluginAvatar(pluginMeta);
const pluginTitle = pluginHelpers.getPluginTitle(pluginMeta) ?? t('plugins.unknown');

const avatar = pluginAvatar ? (
<Avatar avatar={pluginAvatar} size={32} />
Expand Down Expand Up @@ -80,7 +80,7 @@ const Inspector = memo<InspectorProps>(
) : (
avatar
)}
{pluginTitle ?? t('plugins.unknown')}
{loading ? t('plugins.loading') : pluginTitle}
{showRightAction && <Icon icon={showRender ? LucideChevronUp : LucideChevronDown} />}
</Flexbox>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
import { Flexbox } from 'react-layout-kit';

import { useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/selectors';

import IFrameRender from './IFrameRender';

Expand Down Expand Up @@ -72,7 +73,7 @@ export interface PluginDefaultTypeProps {
}

const PluginDefaultType = memo<PluginDefaultTypeProps>(({ content, name }) => {
const manifest = useToolStore((s) => s.pluginManifestMap[name || '']);
const manifest = useToolStore(pluginSelectors.getPluginManifestById(name || ''));
let isJSON = true;
try {
JSON.parse(content);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PluginRequestPayload } from '@lobehub/chat-plugin-sdk';
import { memo } from 'react';

import { useToolStore } from '@/store/tool';
import { pluginSelectors } from '@/store/tool/slices/plugin/selectors';

import IFrameRender from './Iframe';

Expand All @@ -12,7 +13,7 @@ export interface PluginStandaloneTypeProps {
}

const PluginDefaultType = memo<PluginStandaloneTypeProps>(({ payload, id, name = 'unknown' }) => {
const manifest = useToolStore((s) => s.pluginManifestMap[name]);
const manifest = useToolStore(pluginSelectors.getPluginManifestById(name));

if (!manifest?.ui) return;

Expand Down
8 changes: 4 additions & 4 deletions src/app/chat/features/Conversation/useInitConversation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ export const useInitConversation = () => {
useFetchMessages(sessionId, activeTopicId);
useFetchTopics(sessionId);

const [useFetchPluginStore, checkPluginsIsInstalled] = useToolStore((s) => [
s.useFetchPluginStore,
s.useCheckPluginsIsInstalled,
]);
const [useFetchPluginStore, useFetchInstalledPlugins, checkPluginsIsInstalled] = useToolStore(
(s) => [s.useFetchPluginStore, s.useFetchInstalledPlugins, s.useCheckPluginsIsInstalled],
);

useFetchPluginStore();
useFetchInstalledPlugins();
checkPluginsIsInstalled(plugins);

useEffect(() => {
Expand Down
14 changes: 8 additions & 6 deletions src/app/settings/common/Common.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
s.clearAllMessages,
]);
const [removeAllFiles] = useFileStore((s) => [s.removeAllFiles]);
const resetPluginSettings = useToolStore((s) => s.resetPluginSettings);
const removeAllPlugins = useToolStore((s) => s.removeAllPlugins);

const settings = useGlobalStore(settingsSelectors.currentSettings, isEqual);
const [setThemeMode, setSettings, resetSettings] = useGlobalStore((s) => [
Expand Down Expand Up @@ -69,11 +69,13 @@ const Common = memo<SettingsCommonProps>(({ showAccessCodeConfig }) => {
},
okText: t('ok', { ns: 'common' }),
onOk: async () => {
await clearSessions();
resetPluginSettings();
await clearTopics();
await removeAllFiles();
await clearAllMessages();
await Promise.all([
clearSessions,
removeAllPlugins,
clearTopics,
removeAllFiles,
clearAllMessages,
]);

message.success(t('danger.clear.success'));
},
Expand Down
51 changes: 51 additions & 0 deletions src/chains/__tests__/langDetect.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, expect, it } from 'vitest';

import { OpenAIChatStreamPayload } from '@/types/openai/chat';

import { chainLangDetect } from '../langDetect';

// 描述测试块
describe('chainLangDetect', () => {
// 测试用例:验证函数返回的结构
it('should return a payload with the correct structure and embedded user content', () => {
// 用户输入的内容
const userContent = 'Hola';

// 预期的返回值结构
const expectedPayload: Partial<OpenAIChatStreamPayload> = {
messages: [
{
content:
'你是一名精通全世界语言的语言专家,你需要识别用户输入的内容,以国际标准 locale 进行输出',
role: 'system',
},
{
content: '{你好}',
role: 'user',
},
{
content: 'zh-CN',
role: 'assistant',
},
{
content: '{hello}',
role: 'user',
},
{
content: 'en-US',
role: 'assistant',
},
{
content: `{${userContent}}`,
role: 'user',
},
],
};

// 执行函数并获取结果
const result = chainLangDetect(userContent);

// 断言结果是否符合预期
expect(result).toEqual(expectedPayload);
});
});
Loading

0 comments on commit f3b5e7b

Please sign in to comment.