@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
|
@@ -601,5 +601,178 @@ describe('GeneralChatAgent', () => {
|
|
|
601
601
|
},
|
|
602
602
|
]);
|
|
603
603
|
});
|
|
604
|
+
|
|
605
|
+
it('should execute all tools when user approvalMode is auto-run', async () => {
|
|
606
|
+
const agent = new GeneralChatAgent({
|
|
607
|
+
agentConfig: { maxSteps: 100 },
|
|
608
|
+
sessionId: 'test-session',
|
|
609
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
const toolCall: ChatToolPayload = {
|
|
613
|
+
id: 'call-1',
|
|
614
|
+
identifier: 'dangerous-tool',
|
|
615
|
+
apiName: 'delete',
|
|
616
|
+
arguments: '{}',
|
|
617
|
+
type: 'default',
|
|
618
|
+
};
|
|
619
|
+
|
|
620
|
+
const state = createMockState({
|
|
621
|
+
toolManifestMap: {
|
|
622
|
+
'dangerous-tool': {
|
|
623
|
+
identifier: 'dangerous-tool',
|
|
624
|
+
humanIntervention: 'required', // Tool requires approval
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
userInterventionConfig: {
|
|
628
|
+
approvalMode: 'auto-run', // But user config overrides
|
|
629
|
+
allowList: [],
|
|
630
|
+
},
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const context = createMockContext('llm_result', {
|
|
634
|
+
hasToolsCalling: true,
|
|
635
|
+
toolsCalling: [toolCall],
|
|
636
|
+
parentMessageId: 'msg-1',
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const result = await agent.runner(context, state);
|
|
640
|
+
|
|
641
|
+
// Should execute directly despite tool requiring approval
|
|
642
|
+
expect(result).toEqual([
|
|
643
|
+
{
|
|
644
|
+
type: 'call_tool',
|
|
645
|
+
payload: {
|
|
646
|
+
parentMessageId: 'msg-1',
|
|
647
|
+
toolCalling: toolCall,
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
]);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it('should respect allowList when approvalMode is allow-list', async () => {
|
|
654
|
+
const agent = new GeneralChatAgent({
|
|
655
|
+
agentConfig: { maxSteps: 100 },
|
|
656
|
+
sessionId: 'test-session',
|
|
657
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
const allowedTool: ChatToolPayload = {
|
|
661
|
+
id: 'call-1',
|
|
662
|
+
identifier: 'bash',
|
|
663
|
+
apiName: 'bash',
|
|
664
|
+
arguments: '{"command":"ls"}',
|
|
665
|
+
type: 'builtin',
|
|
666
|
+
};
|
|
667
|
+
|
|
668
|
+
const blockedTool: ChatToolPayload = {
|
|
669
|
+
id: 'call-2',
|
|
670
|
+
identifier: 'bash',
|
|
671
|
+
apiName: 'dangerous-command',
|
|
672
|
+
arguments: '{"command":"rm -rf"}',
|
|
673
|
+
type: 'builtin',
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
const state = createMockState({
|
|
677
|
+
toolManifestMap: {
|
|
678
|
+
bash: {
|
|
679
|
+
identifier: 'bash',
|
|
680
|
+
humanIntervention: 'never', // Tool doesn't require approval by default
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
userInterventionConfig: {
|
|
684
|
+
approvalMode: 'allow-list',
|
|
685
|
+
allowList: ['bash/bash'], // Only bash/bash is allowed
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
const context = createMockContext('llm_result', {
|
|
690
|
+
hasToolsCalling: true,
|
|
691
|
+
toolsCalling: [allowedTool, blockedTool],
|
|
692
|
+
parentMessageId: 'msg-1',
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
const result = await agent.runner(context, state);
|
|
696
|
+
|
|
697
|
+
// Should execute allowed tool first, then request approval for blocked tool
|
|
698
|
+
expect(result).toEqual([
|
|
699
|
+
{
|
|
700
|
+
type: 'call_tool',
|
|
701
|
+
payload: {
|
|
702
|
+
parentMessageId: 'msg-1',
|
|
703
|
+
toolCalling: allowedTool,
|
|
704
|
+
},
|
|
705
|
+
},
|
|
706
|
+
{
|
|
707
|
+
type: 'request_human_approve',
|
|
708
|
+
pendingToolsCalling: [blockedTool],
|
|
709
|
+
reason: 'human_intervention_required',
|
|
710
|
+
},
|
|
711
|
+
]);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('should use tool config when approvalMode is manual', async () => {
|
|
715
|
+
const agent = new GeneralChatAgent({
|
|
716
|
+
agentConfig: { maxSteps: 100 },
|
|
717
|
+
sessionId: 'test-session',
|
|
718
|
+
modelRuntimeConfig: mockModelRuntimeConfig,
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
const safeTool: ChatToolPayload = {
|
|
722
|
+
id: 'call-1',
|
|
723
|
+
identifier: 'web-search',
|
|
724
|
+
apiName: 'search',
|
|
725
|
+
arguments: '{}',
|
|
726
|
+
type: 'default',
|
|
727
|
+
};
|
|
728
|
+
|
|
729
|
+
const dangerousTool: ChatToolPayload = {
|
|
730
|
+
id: 'call-2',
|
|
731
|
+
identifier: 'bash',
|
|
732
|
+
apiName: 'bash',
|
|
733
|
+
arguments: '{}',
|
|
734
|
+
type: 'builtin',
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
const state = createMockState({
|
|
738
|
+
toolManifestMap: {
|
|
739
|
+
'web-search': {
|
|
740
|
+
identifier: 'web-search',
|
|
741
|
+
humanIntervention: 'never', // Safe tool
|
|
742
|
+
},
|
|
743
|
+
'bash': {
|
|
744
|
+
identifier: 'bash',
|
|
745
|
+
humanIntervention: 'required', // Dangerous tool
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
userInterventionConfig: {
|
|
749
|
+
approvalMode: 'manual', // Use tool's own config
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
const context = createMockContext('llm_result', {
|
|
754
|
+
hasToolsCalling: true,
|
|
755
|
+
toolsCalling: [safeTool, dangerousTool],
|
|
756
|
+
parentMessageId: 'msg-1',
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
const result = await agent.runner(context, state);
|
|
760
|
+
|
|
761
|
+
// Should execute safe tool, request approval for dangerous tool
|
|
762
|
+
expect(result).toEqual([
|
|
763
|
+
{
|
|
764
|
+
type: 'call_tool',
|
|
765
|
+
payload: {
|
|
766
|
+
parentMessageId: 'msg-1',
|
|
767
|
+
toolCalling: safeTool,
|
|
768
|
+
},
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
type: 'request_human_approve',
|
|
772
|
+
pendingToolsCalling: [dangerousTool],
|
|
773
|
+
reason: 'human_intervention_required',
|
|
774
|
+
},
|
|
775
|
+
]);
|
|
776
|
+
});
|
|
604
777
|
});
|
|
605
778
|
});
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix, typescript-sort-keys/interface */
|
|
2
2
|
// Disable the auto sort key eslint rule to make the code more logic and readable
|
|
3
|
-
import {
|
|
3
|
+
import { type AgentRuntimeContext } from '@lobechat/agent-runtime';
|
|
4
4
|
import { MESSAGE_CANCEL_FLAT } from '@lobechat/const';
|
|
5
5
|
import { produce } from 'immer';
|
|
6
6
|
import { StateCreator } from 'zustand/vanilla';
|
|
7
7
|
|
|
8
|
-
import { getAgentStoreState } from '@/store/agent';
|
|
9
|
-
import { agentSelectors } from '@/store/agent/slices/chat';
|
|
10
|
-
import { createAgentToolsEngine } from '@/store/chat/agents/createToolEngine';
|
|
11
8
|
import { ChatStore } from '@/store/chat/store';
|
|
12
9
|
import { setNamespace } from '@/utils/storeDebug';
|
|
13
10
|
|
|
@@ -146,7 +143,7 @@ export const conversationControl: StateCreator<
|
|
|
146
143
|
}
|
|
147
144
|
},
|
|
148
145
|
approveToolCalling: async (toolMessageId) => {
|
|
149
|
-
const {
|
|
146
|
+
const { activeThreadId, internal_execAgentRuntime } = get();
|
|
150
147
|
|
|
151
148
|
// 1. Get tool message and verify it exists
|
|
152
149
|
const toolMessage = dbMessageSelectors.getDbMessageById(toolMessageId)(get());
|
|
@@ -158,51 +155,22 @@ export const conversationControl: StateCreator<
|
|
|
158
155
|
// 3. Get current messages for state construction
|
|
159
156
|
const currentMessages = displayMessageSelectors.mainAIChats(get());
|
|
160
157
|
|
|
161
|
-
// 4.
|
|
162
|
-
const
|
|
163
|
-
const agentConfigData = agentSelectors.currentAgentConfig(agentStoreState);
|
|
164
|
-
|
|
165
|
-
const toolsEngine = createAgentToolsEngine({
|
|
166
|
-
model: agentConfigData.model,
|
|
167
|
-
provider: agentConfigData.provider!,
|
|
168
|
-
});
|
|
169
|
-
const { enabledToolIds } = toolsEngine.generateToolsDetailed({
|
|
170
|
-
model: agentConfigData.model,
|
|
171
|
-
provider: agentConfigData.provider!,
|
|
172
|
-
toolIds: agentConfigData.plugins,
|
|
173
|
-
});
|
|
174
|
-
const toolManifestMap = Object.fromEntries(
|
|
175
|
-
toolsEngine.getEnabledPluginManifests(enabledToolIds).entries(),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
// 5. Construct AgentState
|
|
179
|
-
const state = AgentRuntime.createInitialState({
|
|
180
|
-
sessionId: activeId,
|
|
158
|
+
// 4. Create agent state and context with user intervention config
|
|
159
|
+
const { state, context: initialContext } = get().internal_createAgentState({
|
|
181
160
|
messages: currentMessages,
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
sessionId: activeId,
|
|
185
|
-
topicId: activeTopicId,
|
|
186
|
-
threadId: activeThreadId,
|
|
187
|
-
},
|
|
188
|
-
toolManifestMap,
|
|
161
|
+
parentMessageId: toolMessageId,
|
|
162
|
+
threadId: activeThreadId,
|
|
189
163
|
});
|
|
190
164
|
|
|
191
|
-
|
|
192
|
-
// 6. Construct AgentRuntimeContext with 'human_approved_tool' phase
|
|
165
|
+
// 5. Override context with 'human_approved_tool' phase
|
|
193
166
|
const context: AgentRuntimeContext = {
|
|
167
|
+
...initialContext,
|
|
194
168
|
phase: 'human_approved_tool',
|
|
195
169
|
payload: {
|
|
196
170
|
approvedToolCall: toolMessage.plugin,
|
|
197
171
|
parentMessageId: toolMessageId,
|
|
198
172
|
skipCreateToolMessage: true,
|
|
199
173
|
},
|
|
200
|
-
session: {
|
|
201
|
-
sessionId: activeId,
|
|
202
|
-
messageCount: currentMessages.length,
|
|
203
|
-
status: 'running',
|
|
204
|
-
stepCount: 0,
|
|
205
|
-
},
|
|
206
174
|
};
|
|
207
175
|
|
|
208
176
|
// 7. Execute agent runtime from tool message position
|
|
@@ -26,6 +26,8 @@ import { createAgentExecutors } from '@/store/chat/agents/createAgentExecutors';
|
|
|
26
26
|
import { createAgentToolsEngine } from '@/store/chat/agents/createToolEngine';
|
|
27
27
|
import { ChatStore } from '@/store/chat/store';
|
|
28
28
|
import { getFileStoreState } from '@/store/file/store';
|
|
29
|
+
import { toolInterventionSelectors } from '@/store/user/selectors';
|
|
30
|
+
import { getUserStoreState } from '@/store/user/store';
|
|
29
31
|
import { setNamespace } from '@/utils/storeDebug';
|
|
30
32
|
|
|
31
33
|
import { topicSelectors } from '../../../selectors';
|
|
@@ -54,6 +56,19 @@ interface ProcessMessageParams {
|
|
|
54
56
|
* Core streaming execution actions for AI chat
|
|
55
57
|
*/
|
|
56
58
|
export interface StreamingExecutorAction {
|
|
59
|
+
/**
|
|
60
|
+
* Creates initial agent state and context with user intervention config
|
|
61
|
+
*/
|
|
62
|
+
internal_createAgentState: (params: {
|
|
63
|
+
messages: UIChatMessage[];
|
|
64
|
+
parentMessageId: string;
|
|
65
|
+
threadId?: string;
|
|
66
|
+
initialState?: AgentState;
|
|
67
|
+
initialContext?: AgentRuntimeContext;
|
|
68
|
+
}) => {
|
|
69
|
+
state: AgentState;
|
|
70
|
+
context: AgentRuntimeContext;
|
|
71
|
+
};
|
|
57
72
|
/**
|
|
58
73
|
* Retrieves an AI-generated chat message from the backend service with streaming
|
|
59
74
|
*/
|
|
@@ -106,6 +121,73 @@ export const streamingExecutor: StateCreator<
|
|
|
106
121
|
[],
|
|
107
122
|
StreamingExecutorAction
|
|
108
123
|
> = (set, get) => ({
|
|
124
|
+
internal_createAgentState: ({
|
|
125
|
+
messages,
|
|
126
|
+
parentMessageId,
|
|
127
|
+
threadId,
|
|
128
|
+
initialState,
|
|
129
|
+
initialContext,
|
|
130
|
+
}) => {
|
|
131
|
+
const { activeId, activeTopicId } = get();
|
|
132
|
+
const agentStoreState = getAgentStoreState();
|
|
133
|
+
const agentConfigData = agentSelectors.currentAgentConfig(agentStoreState);
|
|
134
|
+
|
|
135
|
+
// Get tools manifest map
|
|
136
|
+
const toolsEngine = createAgentToolsEngine({
|
|
137
|
+
model: agentConfigData.model,
|
|
138
|
+
provider: agentConfigData.provider!,
|
|
139
|
+
});
|
|
140
|
+
const { enabledToolIds } = toolsEngine.generateToolsDetailed({
|
|
141
|
+
model: agentConfigData.model,
|
|
142
|
+
provider: agentConfigData.provider!,
|
|
143
|
+
toolIds: agentConfigData.plugins,
|
|
144
|
+
});
|
|
145
|
+
const toolManifestMap = Object.fromEntries(
|
|
146
|
+
toolsEngine.getEnabledPluginManifests(enabledToolIds).entries(),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
// Get user intervention config
|
|
150
|
+
const userStore = getUserStoreState();
|
|
151
|
+
const userInterventionConfig = {
|
|
152
|
+
approvalMode: toolInterventionSelectors.approvalMode(userStore),
|
|
153
|
+
allowList: toolInterventionSelectors.allowList(userStore),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Create initial state or use provided state
|
|
157
|
+
const state =
|
|
158
|
+
initialState ||
|
|
159
|
+
AgentRuntime.createInitialState({
|
|
160
|
+
sessionId: activeId,
|
|
161
|
+
messages,
|
|
162
|
+
maxSteps: 400,
|
|
163
|
+
metadata: {
|
|
164
|
+
sessionId: activeId,
|
|
165
|
+
topicId: activeTopicId,
|
|
166
|
+
threadId,
|
|
167
|
+
},
|
|
168
|
+
toolManifestMap,
|
|
169
|
+
userInterventionConfig,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Create initial context or use provided context
|
|
173
|
+
const context: AgentRuntimeContext = initialContext || {
|
|
174
|
+
phase: 'init',
|
|
175
|
+
payload: {
|
|
176
|
+
model: agentConfigData.model,
|
|
177
|
+
provider: agentConfigData.provider,
|
|
178
|
+
parentMessageId,
|
|
179
|
+
},
|
|
180
|
+
session: {
|
|
181
|
+
sessionId: activeId,
|
|
182
|
+
messageCount: messages.length,
|
|
183
|
+
status: state.status,
|
|
184
|
+
stepCount: 0,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
return { state, context };
|
|
189
|
+
},
|
|
190
|
+
|
|
109
191
|
internal_fetchAIChatMessage: async ({ messages, messageId, params, provider, model }) => {
|
|
110
192
|
const {
|
|
111
193
|
internal_toggleChatLoading,
|
|
@@ -469,16 +551,6 @@ export const streamingExecutor: StateCreator<
|
|
|
469
551
|
},
|
|
470
552
|
});
|
|
471
553
|
|
|
472
|
-
const toolsEngine = createAgentToolsEngine({ model: model, provider: provider! });
|
|
473
|
-
const { enabledToolIds } = toolsEngine.generateToolsDetailed({
|
|
474
|
-
model: model,
|
|
475
|
-
provider: provider!,
|
|
476
|
-
toolIds: agentConfigData.plugins,
|
|
477
|
-
});
|
|
478
|
-
const toolManifestMap = Object.fromEntries(
|
|
479
|
-
toolsEngine.getEnabledPluginManifests(enabledToolIds).entries(),
|
|
480
|
-
);
|
|
481
|
-
|
|
482
554
|
const runtime = new AgentRuntime(agent, {
|
|
483
555
|
executors: createAgentExecutors({
|
|
484
556
|
get,
|
|
@@ -489,33 +561,18 @@ export const streamingExecutor: StateCreator<
|
|
|
489
561
|
}),
|
|
490
562
|
});
|
|
491
563
|
|
|
492
|
-
// Create
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
AgentRuntime.createInitialState({
|
|
496
|
-
sessionId: activeId,
|
|
564
|
+
// Create agent state and context with user intervention config
|
|
565
|
+
const { state: initialAgentState, context: initialAgentContext } =
|
|
566
|
+
get().internal_createAgentState({
|
|
497
567
|
messages,
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
topicId: activeTopicId,
|
|
503
|
-
threadId: params.threadId,
|
|
504
|
-
},
|
|
505
|
-
toolManifestMap,
|
|
568
|
+
parentMessageId: params.parentMessageId,
|
|
569
|
+
threadId: params.threadId,
|
|
570
|
+
initialState: params.initialState,
|
|
571
|
+
initialContext: params.initialContext,
|
|
506
572
|
});
|
|
507
573
|
|
|
508
|
-
|
|
509
|
-
let nextContext
|
|
510
|
-
phase: 'init',
|
|
511
|
-
payload: { model, provider, parentMessageId: params.parentMessageId },
|
|
512
|
-
session: {
|
|
513
|
-
sessionId: activeId,
|
|
514
|
-
messageCount: messages.length,
|
|
515
|
-
status: state.status,
|
|
516
|
-
stepCount: 0,
|
|
517
|
-
},
|
|
518
|
-
};
|
|
574
|
+
let state = initialAgentState;
|
|
575
|
+
let nextContext = initialAgentContext;
|
|
519
576
|
|
|
520
577
|
log(
|
|
521
578
|
'[internal_execAgentRuntime] Agent runtime loop start, initial phase: %s',
|
|
@@ -26,6 +26,10 @@ export interface UserSettingsAction {
|
|
|
26
26
|
setSettings: (settings: PartialDeep<UserSettings>) => Promise<void>;
|
|
27
27
|
updateDefaultAgent: (agent: PartialDeep<LobeAgentSettings>) => Promise<void>;
|
|
28
28
|
updateGeneralConfig: (settings: Partial<UserGeneralConfig>) => Promise<void>;
|
|
29
|
+
updateHumanIntervention: (config: {
|
|
30
|
+
allowList?: string[];
|
|
31
|
+
approvalMode?: 'auto-run' | 'allow-list' | 'manual';
|
|
32
|
+
}) => Promise<void>;
|
|
29
33
|
updateKeyVaults: (settings: Partial<UserKeyVaults>) => Promise<void>;
|
|
30
34
|
|
|
31
35
|
updateSystemAgent: (
|
|
@@ -111,6 +115,14 @@ export const createSettingsSlice: StateCreator<
|
|
|
111
115
|
updateGeneralConfig: async (general) => {
|
|
112
116
|
await get().setSettings({ general });
|
|
113
117
|
},
|
|
118
|
+
updateHumanIntervention: async (config) => {
|
|
119
|
+
const current = get().settings.tool?.humanIntervention || {};
|
|
120
|
+
await get().setSettings({
|
|
121
|
+
tool: {
|
|
122
|
+
humanIntervention: { ...current, ...config },
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
},
|
|
114
126
|
updateKeyVaults: async (keyVaults) => {
|
|
115
127
|
await get().setSettings({ keyVaults });
|
|
116
128
|
},
|
|
@@ -95,13 +95,6 @@ exports[`settingsSelectors > currentTTS > should merge DEFAULT_TTS_CONFIG and s.
|
|
|
95
95
|
}
|
|
96
96
|
`;
|
|
97
97
|
|
|
98
|
-
exports[`settingsSelectors > dalleConfig > should return the dalle configuration 1`] = `
|
|
99
|
-
{
|
|
100
|
-
"apiKey": "dalle-api-key",
|
|
101
|
-
"autoGenerate": true,
|
|
102
|
-
}
|
|
103
|
-
`;
|
|
104
|
-
|
|
105
98
|
exports[`settingsSelectors > defaultAgent > should merge DEFAULT_AGENT and s.settings.defaultAgent correctly 1`] = `
|
|
106
99
|
{
|
|
107
100
|
"config": {
|
|
@@ -2,3 +2,4 @@ export { userGeneralSettingsSelectors } from './general';
|
|
|
2
2
|
export { keyVaultsConfigSelectors } from './keyVaults';
|
|
3
3
|
export { settingsSelectors } from './settings';
|
|
4
4
|
export { systemAgentSelectors } from './systemAgent';
|
|
5
|
+
export { toolInterventionSelectors } from './toolIntervention';
|
|
@@ -120,43 +120,6 @@ describe('settingsSelectors', () => {
|
|
|
120
120
|
});
|
|
121
121
|
});
|
|
122
122
|
|
|
123
|
-
describe('dalleConfig', () => {
|
|
124
|
-
it('should return the dalle configuration', () => {
|
|
125
|
-
const s = {
|
|
126
|
-
settings: {
|
|
127
|
-
tool: {
|
|
128
|
-
dalle: {
|
|
129
|
-
apiKey: 'dalle-api-key',
|
|
130
|
-
autoGenerate: true,
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
},
|
|
134
|
-
} as unknown as UserStore;
|
|
135
|
-
|
|
136
|
-
const result = settingsSelectors.dalleConfig(s);
|
|
137
|
-
|
|
138
|
-
expect(result).toMatchSnapshot();
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('isDalleAutoGenerating', () => {
|
|
143
|
-
it('should return the autoGenerate flag from dalle configuration', () => {
|
|
144
|
-
const s = {
|
|
145
|
-
settings: {
|
|
146
|
-
tool: {
|
|
147
|
-
dalle: {
|
|
148
|
-
autoGenerate: true,
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
} as unknown as UserStore;
|
|
153
|
-
|
|
154
|
-
const result = settingsSelectors.isDalleAutoGenerating(s);
|
|
155
|
-
|
|
156
|
-
expect(result).toBe(true);
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
123
|
describe('getProviderConfigById', () => {
|
|
161
124
|
it('should return the provider config for a given provider id', () => {
|
|
162
125
|
const providerConfig = {
|
|
@@ -36,9 +36,6 @@ const defaultAgentMeta = (s: UserStore) => merge(DEFAULT_AGENT_META, defaultAgen
|
|
|
36
36
|
|
|
37
37
|
const exportSettings = currentSettings;
|
|
38
38
|
|
|
39
|
-
const dalleConfig = (s: UserStore) => currentSettings(s).tool?.dalle || {};
|
|
40
|
-
const isDalleAutoGenerating = (s: UserStore) => currentSettings(s).tool?.dalle?.autoGenerate;
|
|
41
|
-
|
|
42
39
|
const currentSystemAgent = (s: UserStore) =>
|
|
43
40
|
merge(DEFAULT_SYSTEM_AGENT_CONFIG, currentSettings(s).systemAgent);
|
|
44
41
|
|
|
@@ -50,12 +47,10 @@ export const settingsSelectors = {
|
|
|
50
47
|
currentSettings,
|
|
51
48
|
currentSystemAgent,
|
|
52
49
|
currentTTS,
|
|
53
|
-
dalleConfig,
|
|
54
50
|
defaultAgent,
|
|
55
51
|
defaultAgentConfig,
|
|
56
52
|
defaultAgentMeta,
|
|
57
53
|
exportSettings,
|
|
58
54
|
getHotkeyById,
|
|
59
|
-
isDalleAutoGenerating,
|
|
60
55
|
providerConfig: getProviderConfigById,
|
|
61
56
|
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { UserStore } from '@/store/user';
|
|
2
|
+
|
|
3
|
+
import { currentSettings } from './settings';
|
|
4
|
+
|
|
5
|
+
const humanInterventionConfig = (s: UserStore) => currentSettings(s).tool?.humanIntervention || {};
|
|
6
|
+
|
|
7
|
+
const interventionApprovalMode = (s: UserStore) =>
|
|
8
|
+
currentSettings(s).tool?.humanIntervention?.approvalMode || 'manual';
|
|
9
|
+
|
|
10
|
+
const interventionAllowList = (s: UserStore) =>
|
|
11
|
+
currentSettings(s).tool?.humanIntervention?.allowList || [];
|
|
12
|
+
|
|
13
|
+
export const toolInterventionSelectors = {
|
|
14
|
+
allowList: interventionAllowList,
|
|
15
|
+
approvalMode: interventionApprovalMode,
|
|
16
|
+
config: humanInterventionConfig,
|
|
17
|
+
};
|
|
@@ -17,7 +17,7 @@ import { chatToolSelectors } from '@/store/chat/slices/builtinTool/selectors';
|
|
|
17
17
|
import ResultFileGallery from './components/ResultFileGallery';
|
|
18
18
|
|
|
19
19
|
const CodeInterpreter = memo<
|
|
20
|
-
BuiltinRenderProps<
|
|
20
|
+
BuiltinRenderProps<CodeInterpreterParams, CodeInterpreterState, CodeInterpreterResponse>
|
|
21
21
|
>(({ content, args, pluginState, messageId, apiName }) => {
|
|
22
22
|
const { t } = useTranslation('tool');
|
|
23
23
|
const theme = useTheme();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BuiltinIntervention } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { LocalSystemApiName, LocalSystemManifest } from './local-system';
|
|
4
|
+
import RunCommand from './local-system/Intervention/RunCommand';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Builtin tools interventions registry
|
|
8
|
+
* Organized by toolset (identifier) -> API name
|
|
9
|
+
* Only register APIs that have custom intervention UI
|
|
10
|
+
*/
|
|
11
|
+
export const BuiltinToolInterventions: Record<string, Record<string, any>> = {
|
|
12
|
+
[LocalSystemManifest.identifier]: {
|
|
13
|
+
[LocalSystemApiName.runCommand]: RunCommand,
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get builtin intervention component for a specific API
|
|
19
|
+
* @param identifier - Tool identifier (e.g., 'lobe-local-system')
|
|
20
|
+
* @param apiName - API name (e.g., 'runCommand')
|
|
21
|
+
*/
|
|
22
|
+
export const getBuiltinIntervention = (
|
|
23
|
+
identifier?: string,
|
|
24
|
+
apiName?: string,
|
|
25
|
+
): BuiltinIntervention | undefined => {
|
|
26
|
+
if (!identifier || !apiName) return undefined;
|
|
27
|
+
|
|
28
|
+
const toolset = BuiltinToolInterventions[identifier];
|
|
29
|
+
if (!toolset) return undefined;
|
|
30
|
+
|
|
31
|
+
return toolset[apiName];
|
|
32
|
+
};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { RunCommandParams } from '@lobechat/electron-client-ipc';
|
|
2
|
+
import { Highlighter, Text } from '@lobehub/ui';
|
|
3
|
+
import { memo } from 'react';
|
|
4
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
|
+
|
|
6
|
+
const formatTimeout = (ms?: number) => {
|
|
7
|
+
if (!ms) return null;
|
|
8
|
+
|
|
9
|
+
const seconds = ms / 1000;
|
|
10
|
+
|
|
11
|
+
// >= 60s 显示分钟
|
|
12
|
+
if (seconds >= 60) {
|
|
13
|
+
const minutes = seconds / 60;
|
|
14
|
+
return `${minutes.toFixed(1)}min`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// >= 1s 显示秒
|
|
18
|
+
if (seconds >= 1) {
|
|
19
|
+
return `${seconds.toFixed(1)}s`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// < 1s 显示毫秒
|
|
23
|
+
return `${ms}ms`;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
interface RunCommandProps extends RunCommandParams {
|
|
27
|
+
messageId: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const RunCommand = memo<RunCommandProps>(({ description, command, timeout }) => {
|
|
31
|
+
return (
|
|
32
|
+
<Flexbox gap={8}>
|
|
33
|
+
<Flexbox horizontal justify={'space-between'}>
|
|
34
|
+
{description && <Text>{description}</Text>}
|
|
35
|
+
{timeout && (
|
|
36
|
+
<Text style={{ fontSize: 12 }} type={'secondary'}>
|
|
37
|
+
timeout: {formatTimeout(timeout)}
|
|
38
|
+
</Text>
|
|
39
|
+
)}
|
|
40
|
+
</Flexbox>
|
|
41
|
+
{command && (
|
|
42
|
+
<Highlighter
|
|
43
|
+
language={'sh'}
|
|
44
|
+
showLanguage={false}
|
|
45
|
+
style={{ padding: '4px 8px' }}
|
|
46
|
+
variant={'outlined'}
|
|
47
|
+
wrap
|
|
48
|
+
>
|
|
49
|
+
{command}
|
|
50
|
+
</Highlighter>
|
|
51
|
+
)}
|
|
52
|
+
</Flexbox>
|
|
53
|
+
);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export default RunCommand;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
|
|
2
|
+
import { BuiltinPlaceholderProps } from '@lobechat/types';
|
|
2
3
|
import { Skeleton } from 'antd';
|
|
3
4
|
import React, { memo } from 'react';
|
|
4
5
|
import { Center, Flexbox } from 'react-layout-kit';
|
|
5
6
|
|
|
6
7
|
import { LocalFolder } from '@/features/LocalFile';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
args: ListLocalFileParams;
|
|
10
|
-
}
|
|
11
|
-
export const ListFiles = memo<ListFilesProps>(({ args }) => {
|
|
9
|
+
export const ListFiles = memo<BuiltinPlaceholderProps<ListLocalFileParams>>(({ args }) => {
|
|
12
10
|
return (
|
|
13
11
|
<Flexbox gap={12}>
|
|
14
|
-
<LocalFolder path={args.path} />
|
|
12
|
+
{args?.path && <LocalFolder path={args.path} />}
|
|
15
13
|
<Center height={140}>
|
|
16
14
|
<Flexbox gap={4} width={'90%'}>
|
|
17
15
|
<Skeleton.Button active block style={{ height: 16 }} />
|