@lobehub/lobehub 2.0.0-next.50 → 2.0.0-next.52
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 +50 -0
- package/apps/desktop/src/main/controllers/LocalFileCtr.ts +25 -5
- package/apps/desktop/src/main/controllers/ShellCommandCtr.ts +242 -0
- package/apps/desktop/src/main/controllers/__tests__/LocalFileCtr.test.ts +4 -1
- package/apps/desktop/src/main/controllers/__tests__/ShellCommandCtr.test.ts +499 -0
- package/apps/desktop/src/main/modules/fileSearch/__tests__/macOS.integration.test.ts +357 -0
- package/apps/desktop/src/main/modules/fileSearch/impl/macOS.ts +30 -22
- package/changelog/v1.json +18 -0
- package/locales/ar/chat.json +20 -0
- package/locales/ar/common.json +1 -0
- package/locales/ar/components.json +6 -0
- package/locales/ar/models.json +119 -126
- package/locales/ar/plugin.json +2 -1
- package/locales/bg-BG/chat.json +20 -0
- package/locales/bg-BG/common.json +1 -0
- package/locales/bg-BG/components.json +6 -0
- package/locales/bg-BG/models.json +104 -132
- package/locales/bg-BG/plugin.json +2 -1
- package/locales/de-DE/chat.json +20 -0
- package/locales/de-DE/common.json +1 -0
- package/locales/de-DE/components.json +6 -0
- package/locales/de-DE/models.json +119 -126
- package/locales/de-DE/plugin.json +2 -1
- package/locales/en-US/chat.json +20 -0
- package/locales/en-US/common.json +1 -0
- package/locales/en-US/components.json +6 -0
- package/locales/en-US/models.json +167 -126
- package/locales/en-US/plugin.json +2 -1
- package/locales/es-ES/chat.json +20 -0
- package/locales/es-ES/common.json +1 -0
- package/locales/es-ES/components.json +6 -0
- package/locales/es-ES/models.json +119 -126
- package/locales/es-ES/plugin.json +2 -1
- package/locales/fa-IR/chat.json +20 -0
- package/locales/fa-IR/common.json +1 -0
- package/locales/fa-IR/components.json +6 -0
- package/locales/fa-IR/models.json +119 -126
- package/locales/fa-IR/plugin.json +2 -1
- package/locales/fr-FR/chat.json +20 -0
- package/locales/fr-FR/common.json +1 -0
- package/locales/fr-FR/components.json +6 -0
- package/locales/fr-FR/models.json +119 -126
- package/locales/fr-FR/plugin.json +2 -1
- package/locales/it-IT/chat.json +20 -0
- package/locales/it-IT/common.json +1 -0
- package/locales/it-IT/components.json +6 -0
- package/locales/it-IT/models.json +119 -126
- package/locales/it-IT/plugin.json +2 -1
- package/locales/ja-JP/chat.json +20 -0
- package/locales/ja-JP/common.json +1 -0
- package/locales/ja-JP/components.json +6 -0
- package/locales/ja-JP/models.json +119 -126
- package/locales/ja-JP/plugin.json +2 -1
- package/locales/ko-KR/chat.json +20 -0
- package/locales/ko-KR/common.json +1 -0
- package/locales/ko-KR/components.json +6 -0
- package/locales/ko-KR/models.json +119 -126
- package/locales/ko-KR/plugin.json +2 -1
- package/locales/nl-NL/chat.json +20 -0
- package/locales/nl-NL/common.json +1 -0
- package/locales/nl-NL/components.json +6 -0
- package/locales/nl-NL/models.json +119 -126
- package/locales/nl-NL/plugin.json +2 -1
- package/locales/pl-PL/chat.json +20 -0
- package/locales/pl-PL/common.json +1 -0
- package/locales/pl-PL/components.json +6 -0
- package/locales/pl-PL/models.json +119 -126
- package/locales/pl-PL/plugin.json +2 -1
- package/locales/pt-BR/chat.json +20 -0
- package/locales/pt-BR/common.json +1 -0
- package/locales/pt-BR/components.json +6 -0
- package/locales/pt-BR/models.json +119 -126
- package/locales/pt-BR/plugin.json +2 -1
- package/locales/ru-RU/chat.json +20 -0
- package/locales/ru-RU/common.json +1 -0
- package/locales/ru-RU/components.json +6 -0
- package/locales/ru-RU/models.json +119 -126
- package/locales/ru-RU/plugin.json +2 -1
- package/locales/tr-TR/chat.json +20 -0
- package/locales/tr-TR/common.json +1 -0
- package/locales/tr-TR/components.json +6 -0
- package/locales/tr-TR/models.json +119 -126
- package/locales/tr-TR/plugin.json +2 -1
- package/locales/vi-VN/chat.json +20 -0
- package/locales/vi-VN/common.json +1 -0
- package/locales/vi-VN/components.json +6 -0
- package/locales/vi-VN/models.json +119 -126
- package/locales/vi-VN/plugin.json +2 -1
- package/locales/zh-CN/chat.json +20 -0
- package/locales/zh-CN/common.json +1 -0
- package/locales/zh-CN/components.json +6 -0
- package/locales/zh-CN/models.json +173 -80
- package/locales/zh-CN/plugin.json +2 -1
- package/locales/zh-TW/chat.json +20 -0
- package/locales/zh-TW/common.json +1 -0
- package/locales/zh-TW/components.json +6 -0
- package/locales/zh-TW/models.json +119 -126
- package/locales/zh-TW/plugin.json +2 -1
- package/package.json +1 -1
- package/packages/agent-runtime/src/core/InterventionChecker.ts +1 -1
- package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +23 -23
- package/packages/agent-runtime/src/types/state.ts +7 -1
- package/packages/const/src/settings/tool.ts +1 -5
- package/packages/electron-client-ipc/src/types/localSystem.ts +26 -2
- package/packages/file-loaders/src/loaders/docx/index.ts +1 -1
- package/packages/model-bank/src/aiModels/wenxin.ts +1348 -291
- package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +58 -0
- package/packages/model-runtime/src/core/contextBuilders/openai.ts +24 -10
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +3 -2
- package/packages/model-runtime/src/providers/openai/index.test.ts +44 -0
- package/packages/model-runtime/src/providers/wenxin/index.ts +22 -1
- package/packages/model-runtime/src/utils/modelParse.ts +6 -0
- package/packages/types/src/tool/builtin.ts +15 -4
- package/packages/types/src/tool/intervention.ts +32 -2
- package/packages/types/src/user/settings/tool.ts +3 -27
- package/src/config/modelProviders/wenxin.ts +2 -3
- package/src/features/Conversation/MarkdownElements/remarkPlugins/__snapshots__/createRemarkSelfClosingTagPlugin.test.ts.snap +133 -0
- package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +48 -0
- package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +2 -1
- package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/Fallback.tsx +98 -0
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/ModeSelector.tsx +5 -6
- package/src/features/Conversation/Messages/Group/Tool/Render/Intervention/index.tsx +40 -36
- package/src/features/Conversation/Messages/Group/Tool/Render/LoadingPlaceholder/index.tsx +3 -3
- package/src/features/Conversation/Messages/Group/Tool/Render/index.tsx +25 -18
- package/src/features/LocalFile/LocalFile.tsx +55 -5
- package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +10 -4
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +2 -2
- package/src/locales/default/components.ts +6 -0
- package/src/locales/default/plugin.ts +2 -1
- package/src/services/chat/chat.test.ts +1 -0
- package/src/services/electron/localFileService.ts +4 -0
- package/src/store/aiInfra/slices/aiProvider/__tests__/selectors.test.ts +62 -0
- package/src/store/aiInfra/slices/aiProvider/selectors.ts +1 -1
- package/src/store/chat/agents/GeneralChatAgent.ts +26 -1
- package/src/store/chat/agents/__tests__/GeneralChatAgent.test.ts +173 -0
- package/src/store/chat/slices/aiChat/actions/conversationControl.ts +8 -40
- package/src/store/chat/slices/aiChat/actions/streamingExecutor.ts +91 -34
- package/src/store/user/selectors.ts +1 -0
- package/src/store/user/slices/settings/action.ts +12 -0
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +0 -7
- package/src/store/user/slices/settings/selectors/index.ts +1 -0
- package/src/store/user/slices/settings/selectors/settings.test.ts +0 -37
- package/src/store/user/slices/settings/selectors/settings.ts +0 -5
- package/src/store/user/slices/settings/selectors/toolIntervention.ts +17 -0
- package/src/tools/code-interpreter/Render/index.tsx +1 -1
- package/src/tools/interventions.ts +32 -0
- package/src/tools/local-system/Intervention/RunCommand/index.tsx +56 -0
- package/src/tools/local-system/Placeholder/ListFiles.tsx +3 -5
- package/src/tools/local-system/Placeholder/SearchFiles.tsx +2 -5
- package/src/tools/local-system/Render/ListFiles/index.tsx +16 -21
- package/src/tools/local-system/Render/RenameLocalFile/index.tsx +15 -20
- package/src/tools/local-system/Render/RunCommand/index.tsx +103 -27
- package/src/tools/local-system/Render/SearchFiles/SearchQuery/index.tsx +0 -1
- package/src/tools/local-system/Render/SearchFiles/index.tsx +15 -20
- package/src/tools/local-system/Render/WriteFile/index.tsx +2 -8
- package/src/tools/local-system/index.ts +184 -4
- package/src/tools/local-system/systemRole.ts +62 -8
- package/src/tools/placeholders.ts +39 -8
- package/src/tools/renders.ts +56 -9
- package/src/tools/web-browsing/Placeholder/{PageContent.tsx → CrawlMultiPages.tsx} +4 -1
- package/src/tools/web-browsing/Placeholder/CrawlSinglePage.tsx +12 -0
- package/src/tools/web-browsing/Placeholder/Search.tsx +4 -4
- package/src/tools/web-browsing/Render/CrawlMultiPages.tsx +15 -0
- package/src/tools/web-browsing/Render/CrawlSinglePage.tsx +15 -0
- package/src/tools/web-browsing/Render/Search/index.tsx +39 -44
- package/packages/database/migrations/0044_add_tool_intervention.sql +0 -1
- package/src/tools/local-system/Placeholder/index.tsx +0 -25
- package/src/tools/local-system/Render/index.tsx +0 -40
- package/src/tools/web-browsing/Placeholder/index.tsx +0 -40
- package/src/tools/web-browsing/Render/index.tsx +0 -57
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { safeParseJSON } from '@lobechat/utils';
|
|
2
|
+
import { ActionIcon } from '@lobehub/ui';
|
|
3
|
+
import { Edit3Icon } from 'lucide-react';
|
|
4
|
+
import { Suspense, memo, useCallback, useState } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Flexbox } from 'react-layout-kit';
|
|
7
|
+
|
|
8
|
+
import { useChatStore } from '@/store/chat';
|
|
9
|
+
import { useUserStore } from '@/store/user';
|
|
10
|
+
import { toolInterventionSelectors } from '@/store/user/selectors';
|
|
11
|
+
|
|
12
|
+
import Arguments from '../Arguments';
|
|
13
|
+
import ApprovalActions from './ApprovalActions';
|
|
14
|
+
import KeyValueEditor from './KeyValueEditor';
|
|
15
|
+
import ModeSelector from './ModeSelector';
|
|
16
|
+
|
|
17
|
+
interface FallbackInterventionProps {
|
|
18
|
+
apiName: string;
|
|
19
|
+
id: string;
|
|
20
|
+
identifier: string;
|
|
21
|
+
requestArgs: string;
|
|
22
|
+
toolCallId: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const FallbackIntervention = memo<FallbackInterventionProps>(
|
|
26
|
+
({ requestArgs, id, identifier, apiName, toolCallId }) => {
|
|
27
|
+
const { t } = useTranslation('chat');
|
|
28
|
+
const approvalMode = useUserStore(toolInterventionSelectors.approvalMode);
|
|
29
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
30
|
+
const [optimisticUpdatePluginArguments] = useChatStore((s) => [
|
|
31
|
+
s.optimisticUpdatePluginArguments,
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const handleCancel = useCallback(() => {
|
|
35
|
+
setIsEditing(false);
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
const handleFinish = useCallback(
|
|
39
|
+
async (editedObject: Record<string, any>) => {
|
|
40
|
+
if (!id) return;
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const newArgsString = JSON.stringify(editedObject, null, 2);
|
|
44
|
+
|
|
45
|
+
if (newArgsString !== requestArgs) {
|
|
46
|
+
await optimisticUpdatePluginArguments(id, editedObject, true);
|
|
47
|
+
}
|
|
48
|
+
setIsEditing(false);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error stringifying arguments:', error);
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
[requestArgs, id],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (isEditing)
|
|
57
|
+
return (
|
|
58
|
+
<Suspense fallback={<Arguments arguments={requestArgs} />}>
|
|
59
|
+
<KeyValueEditor
|
|
60
|
+
initialValue={safeParseJSON(requestArgs || '')}
|
|
61
|
+
onCancel={handleCancel}
|
|
62
|
+
onFinish={handleFinish}
|
|
63
|
+
/>
|
|
64
|
+
</Suspense>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<Flexbox gap={12}>
|
|
69
|
+
<Arguments
|
|
70
|
+
actions={
|
|
71
|
+
<ActionIcon
|
|
72
|
+
icon={Edit3Icon}
|
|
73
|
+
onClick={() => {
|
|
74
|
+
setIsEditing(true);
|
|
75
|
+
}}
|
|
76
|
+
size={'small'}
|
|
77
|
+
title={t('edit', { ns: 'common' })}
|
|
78
|
+
/>
|
|
79
|
+
}
|
|
80
|
+
arguments={requestArgs}
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
<Flexbox horizontal justify={'space-between'}>
|
|
84
|
+
<ModeSelector />
|
|
85
|
+
<ApprovalActions
|
|
86
|
+
apiName={apiName}
|
|
87
|
+
approvalMode={approvalMode}
|
|
88
|
+
identifier={identifier}
|
|
89
|
+
messageId={id}
|
|
90
|
+
toolCallId={toolCallId}
|
|
91
|
+
/>
|
|
92
|
+
</Flexbox>
|
|
93
|
+
</Flexbox>
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
export default FallbackIntervention;
|
|
@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
6
6
|
import { Center } from 'react-layout-kit';
|
|
7
7
|
|
|
8
8
|
import { useUserStore } from '@/store/user';
|
|
9
|
+
import { toolInterventionSelectors } from '@/store/user/selectors';
|
|
9
10
|
|
|
10
11
|
import { ApprovalMode } from './index';
|
|
11
12
|
|
|
@@ -39,10 +40,8 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
39
40
|
const ModeSelector = memo(() => {
|
|
40
41
|
const { t } = useTranslation('chat');
|
|
41
42
|
const { styles } = useStyles();
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
s.setSettings,
|
|
45
|
-
]);
|
|
43
|
+
const approvalMode = useUserStore(toolInterventionSelectors.approvalMode);
|
|
44
|
+
const updateHumanIntervention = useUserStore((s) => s.updateHumanIntervention);
|
|
46
45
|
|
|
47
46
|
const modeLabels = useMemo(
|
|
48
47
|
() => ({
|
|
@@ -55,9 +54,9 @@ const ModeSelector = memo(() => {
|
|
|
55
54
|
|
|
56
55
|
const handleModeChange = useCallback(
|
|
57
56
|
async (mode: ApprovalMode) => {
|
|
58
|
-
await
|
|
57
|
+
await updateHumanIntervention({ approvalMode: mode });
|
|
59
58
|
},
|
|
60
|
-
[
|
|
59
|
+
[updateHumanIntervention],
|
|
61
60
|
);
|
|
62
61
|
|
|
63
62
|
const menuItems = useMemo<MenuProps['items']>(
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { safeParseJSON } from '@lobechat/utils';
|
|
2
|
-
import { ActionIcon } from '@lobehub/ui';
|
|
3
|
-
import { Edit3Icon } from 'lucide-react';
|
|
4
2
|
import { Suspense, memo, useCallback, useState } from 'react';
|
|
5
|
-
import { useTranslation } from 'react-i18next';
|
|
6
3
|
import { Flexbox } from 'react-layout-kit';
|
|
7
4
|
|
|
8
5
|
import { useChatStore } from '@/store/chat';
|
|
9
6
|
import { useUserStore } from '@/store/user';
|
|
7
|
+
import { toolInterventionSelectors } from '@/store/user/selectors';
|
|
8
|
+
import { getBuiltinIntervention } from '@/tools/interventions';
|
|
10
9
|
|
|
11
10
|
import Arguments from '../Arguments';
|
|
12
11
|
import ApprovalActions from './ApprovalActions';
|
|
12
|
+
import Fallback from './Fallback';
|
|
13
13
|
import KeyValueEditor from './KeyValueEditor';
|
|
14
14
|
import ModeSelector from './ModeSelector';
|
|
15
15
|
|
|
@@ -25,8 +25,7 @@ interface InterventionProps {
|
|
|
25
25
|
|
|
26
26
|
const Intervention = memo<InterventionProps>(
|
|
27
27
|
({ requestArgs, id, identifier, apiName, toolCallId }) => {
|
|
28
|
-
const
|
|
29
|
-
const approvalMode = useUserStore((s) => s.settings.tool?.approvalMode || 'manual');
|
|
28
|
+
const approvalMode = useUserStore(toolInterventionSelectors.approvalMode);
|
|
30
29
|
const [isEditing, setIsEditing] = useState(false);
|
|
31
30
|
const [optimisticUpdatePluginArguments] = useChatStore((s) => [
|
|
32
31
|
s.optimisticUpdatePluginArguments,
|
|
@@ -53,45 +52,50 @@ const Intervention = memo<InterventionProps>(
|
|
|
53
52
|
},
|
|
54
53
|
[requestArgs, id],
|
|
55
54
|
);
|
|
55
|
+
const BuiltinToolInterventionRender = getBuiltinIntervention(identifier, apiName);
|
|
56
56
|
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
</Suspense>
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<Flexbox gap={12}>
|
|
70
|
-
<Arguments
|
|
71
|
-
actions={
|
|
72
|
-
<ActionIcon
|
|
73
|
-
icon={Edit3Icon}
|
|
74
|
-
onClick={() => {
|
|
75
|
-
setIsEditing(true);
|
|
76
|
-
}}
|
|
77
|
-
size={'small'}
|
|
78
|
-
title={t('edit', { ns: 'common' })}
|
|
57
|
+
if (BuiltinToolInterventionRender) {
|
|
58
|
+
if (isEditing)
|
|
59
|
+
return (
|
|
60
|
+
<Suspense fallback={<Arguments arguments={requestArgs} />}>
|
|
61
|
+
<KeyValueEditor
|
|
62
|
+
initialValue={safeParseJSON(requestArgs || '')}
|
|
63
|
+
onCancel={handleCancel}
|
|
64
|
+
onFinish={handleFinish}
|
|
79
65
|
/>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/>
|
|
66
|
+
</Suspense>
|
|
67
|
+
);
|
|
83
68
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<
|
|
69
|
+
return (
|
|
70
|
+
<Flexbox gap={12}>
|
|
71
|
+
<BuiltinToolInterventionRender
|
|
87
72
|
apiName={apiName}
|
|
88
|
-
|
|
73
|
+
args={safeParseJSON(requestArgs || '')}
|
|
89
74
|
identifier={identifier}
|
|
90
75
|
messageId={id}
|
|
91
|
-
toolCallId={toolCallId}
|
|
92
76
|
/>
|
|
77
|
+
<Flexbox horizontal justify={'space-between'}>
|
|
78
|
+
<ModeSelector />
|
|
79
|
+
<ApprovalActions
|
|
80
|
+
apiName={apiName}
|
|
81
|
+
approvalMode={approvalMode}
|
|
82
|
+
identifier={identifier}
|
|
83
|
+
messageId={id}
|
|
84
|
+
toolCallId={toolCallId}
|
|
85
|
+
/>
|
|
86
|
+
</Flexbox>
|
|
93
87
|
</Flexbox>
|
|
94
|
-
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<Fallback
|
|
93
|
+
apiName={apiName}
|
|
94
|
+
id={id}
|
|
95
|
+
identifier={identifier}
|
|
96
|
+
requestArgs={requestArgs}
|
|
97
|
+
toolCallId={toolCallId}
|
|
98
|
+
/>
|
|
95
99
|
);
|
|
96
100
|
},
|
|
97
101
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { safeParseJSON } from '@lobechat/utils';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getBuiltinPlaceholder } from '@/tools/placeholders';
|
|
5
5
|
|
|
6
6
|
import Arguments from '../Arguments';
|
|
7
7
|
|
|
@@ -14,9 +14,9 @@ interface LoadingPlaceholderProps {
|
|
|
14
14
|
|
|
15
15
|
const LoadingPlaceholder = memo<LoadingPlaceholderProps>(
|
|
16
16
|
({ identifier, requestArgs, apiName, loading }) => {
|
|
17
|
-
const Render =
|
|
17
|
+
const Render = getBuiltinPlaceholder(identifier, apiName);
|
|
18
18
|
|
|
19
|
-
if (
|
|
19
|
+
if (Render) {
|
|
20
20
|
return (
|
|
21
21
|
<Render apiName={apiName} args={safeParseJSON(requestArgs) || {}} identifier={identifier} />
|
|
22
22
|
);
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { LOADING_FLAT } from '@lobechat/const';
|
|
2
2
|
import { ChatToolResult, ToolIntervention } from '@lobechat/types';
|
|
3
3
|
import { Suspense, memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
4
5
|
|
|
5
6
|
import CustomRender from './CustomRender';
|
|
6
7
|
import ErrorResponse from './ErrorResponse';
|
|
7
8
|
import Intervention from './Intervention';
|
|
9
|
+
import ModeSelector from './Intervention/ModeSelector';
|
|
8
10
|
import LoadingPlaceholder from './LoadingPlaceholder';
|
|
9
11
|
import RejectedResponse from './RejectedResponse';
|
|
10
12
|
|
|
@@ -99,24 +101,29 @@ const Render = memo<RenderProps>(
|
|
|
99
101
|
|
|
100
102
|
return (
|
|
101
103
|
<Suspense fallback={placeholder}>
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
104
|
+
<Flexbox gap={8}>
|
|
105
|
+
<CustomRender
|
|
106
|
+
content={result.content || ''}
|
|
107
|
+
id={toolCallId}
|
|
108
|
+
plugin={
|
|
109
|
+
type
|
|
110
|
+
? ({
|
|
111
|
+
apiName,
|
|
112
|
+
arguments: requestArgs || '',
|
|
113
|
+
identifier,
|
|
114
|
+
type,
|
|
115
|
+
} as any)
|
|
116
|
+
: undefined
|
|
117
|
+
}
|
|
118
|
+
pluginState={result.state}
|
|
119
|
+
requestArgs={requestArgs}
|
|
120
|
+
setShowPluginRender={setShowPluginRender}
|
|
121
|
+
showPluginRender={showPluginRender}
|
|
122
|
+
/>
|
|
123
|
+
<div>
|
|
124
|
+
<ModeSelector />
|
|
125
|
+
</div>
|
|
126
|
+
</Flexbox>
|
|
120
127
|
</Suspense>
|
|
121
128
|
);
|
|
122
129
|
},
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { Button } from '@lobehub/ui';
|
|
2
|
+
import { Popover, Space } from 'antd';
|
|
1
3
|
import { createStyles } from 'antd-style';
|
|
4
|
+
import { ExternalLink, FolderOpen } from 'lucide-react';
|
|
2
5
|
import React from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
3
7
|
import { Flexbox } from 'react-layout-kit';
|
|
4
8
|
|
|
5
9
|
import FileIcon from '@/components/FileIcon';
|
|
@@ -13,7 +17,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
|
13
17
|
padding-inline: 4px 8px;
|
|
14
18
|
border-radius: 4px;
|
|
15
19
|
|
|
16
|
-
color: ${token.
|
|
20
|
+
color: ${token.colorText};
|
|
17
21
|
|
|
18
22
|
:hover {
|
|
19
23
|
color: ${token.colorText};
|
|
@@ -39,19 +43,25 @@ interface LocalFileProps {
|
|
|
39
43
|
|
|
40
44
|
export const LocalFile = ({ name, path, isDirectory = false }: LocalFileProps) => {
|
|
41
45
|
const { styles } = useStyles();
|
|
42
|
-
const
|
|
43
|
-
if (!path) return;
|
|
46
|
+
const { t } = useTranslation('components');
|
|
44
47
|
|
|
48
|
+
const handleOpenFile = () => {
|
|
49
|
+
if (!path) return;
|
|
45
50
|
localFileService.openLocalFileOrFolder(path, isDirectory);
|
|
46
51
|
};
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
const handleOpenFolder = () => {
|
|
54
|
+
if (!path) return;
|
|
55
|
+
localFileService.openFileFolder(path);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const fileContent = (
|
|
49
59
|
<Flexbox
|
|
50
60
|
align={'center'}
|
|
51
61
|
className={styles.container}
|
|
52
62
|
gap={4}
|
|
53
63
|
horizontal
|
|
54
|
-
onClick={
|
|
64
|
+
onClick={isDirectory ? handleOpenFile : undefined}
|
|
55
65
|
style={{ display: 'inline-flex', verticalAlign: 'middle' }}
|
|
56
66
|
>
|
|
57
67
|
<FileIcon fileName={name} isDirectory={isDirectory} size={22} variant={'raw'} />
|
|
@@ -60,4 +70,44 @@ export const LocalFile = ({ name, path, isDirectory = false }: LocalFileProps) =
|
|
|
60
70
|
</Flexbox>
|
|
61
71
|
</Flexbox>
|
|
62
72
|
);
|
|
73
|
+
|
|
74
|
+
// Directory: no popover, just click to open
|
|
75
|
+
if (isDirectory) {
|
|
76
|
+
return fileContent;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// File: show popover with two actions
|
|
80
|
+
const popoverContent = (
|
|
81
|
+
<Space.Compact>
|
|
82
|
+
<Button
|
|
83
|
+
icon={ExternalLink}
|
|
84
|
+
onClick={handleOpenFile}
|
|
85
|
+
size="small"
|
|
86
|
+
title={t('LocalFile.action.open')}
|
|
87
|
+
>
|
|
88
|
+
{t('LocalFile.action.open')}
|
|
89
|
+
</Button>
|
|
90
|
+
<Button
|
|
91
|
+
icon={FolderOpen}
|
|
92
|
+
onClick={handleOpenFolder}
|
|
93
|
+
size="small"
|
|
94
|
+
title={t('LocalFile.action.showInFolder')}
|
|
95
|
+
>
|
|
96
|
+
{t('LocalFile.action.showInFolder')}
|
|
97
|
+
</Button>
|
|
98
|
+
</Space.Compact>
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<Popover
|
|
103
|
+
arrow={false}
|
|
104
|
+
content={popoverContent}
|
|
105
|
+
styles={{
|
|
106
|
+
body: { padding: 0 },
|
|
107
|
+
}}
|
|
108
|
+
trigger={['hover']}
|
|
109
|
+
>
|
|
110
|
+
{fileContent}
|
|
111
|
+
</Popover>
|
|
112
|
+
);
|
|
63
113
|
};
|
|
@@ -4,11 +4,17 @@ import { describe, expect, it, vi } from 'vitest';
|
|
|
4
4
|
import BuiltinType from './index';
|
|
5
5
|
|
|
6
6
|
// Mock renders module
|
|
7
|
+
const mockWebBrowsingRender = vi.fn(({ content }) => <div>WebBrowsingRender: {content}</div>);
|
|
8
|
+
const mockCodeInterpreterRender = vi.fn(({ content }) => (
|
|
9
|
+
<div>CodeInterpreterRender: {content}</div>
|
|
10
|
+
));
|
|
11
|
+
|
|
7
12
|
vi.mock('@/tools/renders', () => ({
|
|
8
|
-
|
|
9
|
-
'lobe-web-browsing'
|
|
10
|
-
'lobe-code-interpreter'
|
|
11
|
-
|
|
13
|
+
getBuiltinRender: vi.fn((identifier, apiName) => {
|
|
14
|
+
if (identifier === 'lobe-web-browsing') return mockWebBrowsingRender;
|
|
15
|
+
if (identifier === 'lobe-code-interpreter') return mockCodeInterpreterRender;
|
|
16
|
+
return undefined;
|
|
17
|
+
}),
|
|
12
18
|
}));
|
|
13
19
|
|
|
14
20
|
// Mock useParseContent hook
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { safeParseJSON } from '@lobechat/utils';
|
|
2
2
|
import { memo } from 'react';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import { getBuiltinRender } from '@/tools/renders';
|
|
5
5
|
|
|
6
6
|
import { useParseContent } from '../useParseContent';
|
|
7
7
|
|
|
@@ -28,7 +28,7 @@ const BuiltinType = memo<BuiltinTypeProps>(
|
|
|
28
28
|
}) => {
|
|
29
29
|
const { data } = useParseContent(content);
|
|
30
30
|
|
|
31
|
-
const Render =
|
|
31
|
+
const Render = getBuiltinRender(identifier, apiName);
|
|
32
32
|
|
|
33
33
|
if (!Render) return;
|
|
34
34
|
|
|
@@ -256,10 +256,11 @@ export default {
|
|
|
256
256
|
moveLocalFiles: '移动文件',
|
|
257
257
|
readLocalFile: '读取文件内容',
|
|
258
258
|
renameLocalFile: '重命名',
|
|
259
|
+
runCommand: '执行代码',
|
|
259
260
|
searchLocalFiles: '搜索文件',
|
|
260
261
|
writeLocalFile: '写入文件',
|
|
261
262
|
},
|
|
262
|
-
title: '
|
|
263
|
+
title: '本地系统',
|
|
263
264
|
},
|
|
264
265
|
mcpInstall: {
|
|
265
266
|
CHECKING_INSTALLATION: '检查安装环境...',
|
|
@@ -98,6 +98,10 @@ class LocalFileService {
|
|
|
98
98
|
return this.openLocalFile({ path });
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
async openFileFolder(path: string) {
|
|
103
|
+
return this.openLocalFolder({ isDirectory: false, path });
|
|
104
|
+
}
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
export const localFileService = new LocalFileService();
|
|
@@ -246,4 +246,66 @@ describe('aiProviderSelectors', () => {
|
|
|
246
246
|
);
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
|
+
|
|
250
|
+
describe('isProviderEnableResponseApi', () => {
|
|
251
|
+
it('should return true when config explicitly sets enableResponseApi to true', () => {
|
|
252
|
+
const state = {
|
|
253
|
+
...mockState,
|
|
254
|
+
aiProviderRuntimeConfig: {
|
|
255
|
+
test: {
|
|
256
|
+
config: { enableResponseApi: true },
|
|
257
|
+
keyVaults: {},
|
|
258
|
+
settings: {},
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
expect(aiProviderSelectors.isProviderEnableResponseApi('test')(state)).toBe(true);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should return false when config explicitly sets enableResponseApi to false', () => {
|
|
266
|
+
const state = {
|
|
267
|
+
...mockState,
|
|
268
|
+
aiProviderRuntimeConfig: {
|
|
269
|
+
test: {
|
|
270
|
+
config: { enableResponseApi: false },
|
|
271
|
+
keyVaults: {},
|
|
272
|
+
settings: {},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
expect(aiProviderSelectors.isProviderEnableResponseApi('test')(state)).toBe(false);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('should return true by default for openai provider', () => {
|
|
280
|
+
const state = {
|
|
281
|
+
...mockState,
|
|
282
|
+
aiProviderRuntimeConfig: {
|
|
283
|
+
openai: {
|
|
284
|
+
keyVaults: {},
|
|
285
|
+
settings: {},
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
expect(aiProviderSelectors.isProviderEnableResponseApi('openai')(state)).toBe(true);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should return false by default for non-openai provider', () => {
|
|
293
|
+
const state = {
|
|
294
|
+
...mockState,
|
|
295
|
+
aiProviderRuntimeConfig: {
|
|
296
|
+
anthropic: {
|
|
297
|
+
keyVaults: {},
|
|
298
|
+
settings: {},
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
expect(aiProviderSelectors.isProviderEnableResponseApi('anthropic')(state)).toBe(false);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('should return false for provider without config', () => {
|
|
306
|
+
expect(aiProviderSelectors.isProviderEnableResponseApi('non-existing')(mockState)).toBe(
|
|
307
|
+
false,
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
249
311
|
});
|
|
@@ -108,7 +108,7 @@ const isProviderEnableResponseApi = (id: string) => (s: AIProviderStoreState) =>
|
|
|
108
108
|
|
|
109
109
|
if (typeof enableResponseApi === 'boolean') return enableResponseApi;
|
|
110
110
|
|
|
111
|
-
return
|
|
111
|
+
return id === 'openai';
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
const isInitAiProviderRuntimeState = (s: AIProviderStoreState) => !!s.isInitAiProviderRuntimeState;
|
|
@@ -54,6 +54,7 @@ export class GeneralChatAgent implements Agent {
|
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
56
|
* Check if tool calls need human intervention
|
|
57
|
+
* Combines user's global config with tool's own config
|
|
57
58
|
* Returns [toolsNeedingIntervention, toolsToExecute]
|
|
58
59
|
*/
|
|
59
60
|
private checkInterventionNeeded(
|
|
@@ -63,7 +64,31 @@ export class GeneralChatAgent implements Agent {
|
|
|
63
64
|
const toolsNeedingIntervention: ChatToolPayload[] = [];
|
|
64
65
|
const toolsToExecute: ChatToolPayload[] = [];
|
|
65
66
|
|
|
67
|
+
// Get user config (default to 'manual' mode)
|
|
68
|
+
const userConfig = state.userInterventionConfig || { approvalMode: 'manual' };
|
|
69
|
+
const { approvalMode, allowList = [] } = userConfig;
|
|
70
|
+
|
|
66
71
|
for (const toolCalling of toolsCalling) {
|
|
72
|
+
const { identifier, apiName } = toolCalling;
|
|
73
|
+
const toolKey = `${identifier}/${apiName}`;
|
|
74
|
+
|
|
75
|
+
// Priority 1: User config is 'auto-run', all tools execute directly
|
|
76
|
+
if (approvalMode === 'auto-run') {
|
|
77
|
+
toolsToExecute.push(toolCalling);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Priority 2: User config is 'allow-list', check if tool is in whitelist
|
|
82
|
+
if (approvalMode === 'allow-list') {
|
|
83
|
+
if (allowList.includes(toolKey)) {
|
|
84
|
+
toolsToExecute.push(toolCalling);
|
|
85
|
+
} else {
|
|
86
|
+
toolsNeedingIntervention.push(toolCalling);
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Priority 3: User config is 'manual' (default), use tool's own config
|
|
67
92
|
const config = this.getToolInterventionConfig(toolCalling, state);
|
|
68
93
|
|
|
69
94
|
// Parse arguments for intervention checking
|
|
@@ -82,7 +107,7 @@ export class GeneralChatAgent implements Agent {
|
|
|
82
107
|
if (policy === 'never') {
|
|
83
108
|
toolsToExecute.push(toolCalling);
|
|
84
109
|
} else {
|
|
85
|
-
// '
|
|
110
|
+
// 'required' or undefined requires intervention
|
|
86
111
|
toolsNeedingIntervention.push(toolCalling);
|
|
87
112
|
}
|
|
88
113
|
}
|