@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
|
@@ -150,6 +150,64 @@ describe('convertOpenAIMessages', () => {
|
|
|
150
150
|
|
|
151
151
|
expect(Promise.all).toHaveBeenCalledTimes(2); // 一次用于消息数组,一次用于内容数组
|
|
152
152
|
});
|
|
153
|
+
|
|
154
|
+
it('should filter out reasoning field from messages', async () => {
|
|
155
|
+
const messages = [
|
|
156
|
+
{
|
|
157
|
+
role: 'assistant',
|
|
158
|
+
content: 'Hello',
|
|
159
|
+
reasoning: { content: 'some reasoning', duration: 100 },
|
|
160
|
+
},
|
|
161
|
+
{ role: 'user', content: 'Hi' },
|
|
162
|
+
] as any;
|
|
163
|
+
|
|
164
|
+
const result = await convertOpenAIMessages(messages);
|
|
165
|
+
|
|
166
|
+
expect(result).toEqual([
|
|
167
|
+
{ role: 'assistant', content: 'Hello' },
|
|
168
|
+
{ role: 'user', content: 'Hi' },
|
|
169
|
+
]);
|
|
170
|
+
// Ensure reasoning field is removed
|
|
171
|
+
expect((result[0] as any).reasoning).toBeUndefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should filter out reasoning_content field from messages', async () => {
|
|
175
|
+
const messages = [
|
|
176
|
+
{
|
|
177
|
+
role: 'assistant',
|
|
178
|
+
content: 'Hello',
|
|
179
|
+
reasoning_content: 'some reasoning content',
|
|
180
|
+
},
|
|
181
|
+
{ role: 'user', content: 'Hi' },
|
|
182
|
+
] as any;
|
|
183
|
+
|
|
184
|
+
const result = await convertOpenAIMessages(messages);
|
|
185
|
+
|
|
186
|
+
expect(result).toEqual([
|
|
187
|
+
{ role: 'assistant', content: 'Hello' },
|
|
188
|
+
{ role: 'user', content: 'Hi' },
|
|
189
|
+
]);
|
|
190
|
+
// Ensure reasoning_content field is removed
|
|
191
|
+
expect((result[0] as any).reasoning_content).toBeUndefined();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should filter out both reasoning and reasoning_content fields from messages', async () => {
|
|
195
|
+
const messages = [
|
|
196
|
+
{
|
|
197
|
+
role: 'assistant',
|
|
198
|
+
content: 'Hello',
|
|
199
|
+
reasoning: { content: 'some reasoning', duration: 100 },
|
|
200
|
+
reasoning_content: 'some reasoning content',
|
|
201
|
+
},
|
|
202
|
+
] as any;
|
|
203
|
+
|
|
204
|
+
const result = await convertOpenAIMessages(messages);
|
|
205
|
+
|
|
206
|
+
expect(result).toEqual([{ role: 'assistant', content: 'Hello' }]);
|
|
207
|
+
// Ensure both fields are removed
|
|
208
|
+
expect((result[0] as any).reasoning).toBeUndefined();
|
|
209
|
+
expect((result[0] as any).reasoning_content).toBeUndefined();
|
|
210
|
+
});
|
|
153
211
|
});
|
|
154
212
|
|
|
155
213
|
describe('convertOpenAIResponseInputs', () => {
|
|
@@ -26,17 +26,31 @@ export const convertMessageContent = async (
|
|
|
26
26
|
|
|
27
27
|
export const convertOpenAIMessages = async (messages: OpenAI.ChatCompletionMessageParam[]) => {
|
|
28
28
|
return (await Promise.all(
|
|
29
|
-
messages.map(async (message) =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
messages.map(async (message) => {
|
|
30
|
+
const msg = message as any;
|
|
31
|
+
|
|
32
|
+
// Explicitly map only valid ChatCompletionMessageParam fields
|
|
33
|
+
// Exclude reasoning and reasoning_content fields as they should not be sent in requests
|
|
34
|
+
const result: any = {
|
|
35
|
+
content:
|
|
36
|
+
typeof message.content === 'string'
|
|
37
|
+
? message.content
|
|
38
|
+
: await Promise.all(
|
|
39
|
+
(message.content || []).map((c) =>
|
|
40
|
+
convertMessageContent(c as OpenAI.ChatCompletionContentPart),
|
|
41
|
+
),
|
|
37
42
|
),
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
role: msg.role,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Add optional fields if they exist
|
|
47
|
+
if (msg.name !== undefined) result.name = msg.name;
|
|
48
|
+
if (msg.tool_calls !== undefined) result.tool_calls = msg.tool_calls;
|
|
49
|
+
if (msg.tool_call_id !== undefined) result.tool_call_id = msg.tool_call_id;
|
|
50
|
+
if (msg.function_call !== undefined) result.function_call = msg.function_call;
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}),
|
|
40
54
|
)) as OpenAI.ChatCompletionMessageParam[];
|
|
41
55
|
};
|
|
42
56
|
|
|
@@ -766,12 +766,12 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
766
766
|
|
|
767
767
|
const inputStartAt = Date.now();
|
|
768
768
|
|
|
769
|
-
const { messages, reasoning_effort, tools, reasoning, responseMode, ...res } =
|
|
769
|
+
const { messages, reasoning_effort, tools, reasoning, responseMode, max_tokens, ...res } =
|
|
770
770
|
responses?.handlePayload
|
|
771
771
|
? (responses?.handlePayload(payload, this._options) as ChatStreamPayload)
|
|
772
772
|
: payload;
|
|
773
773
|
|
|
774
|
-
// remove penalty params
|
|
774
|
+
// remove penalty params and chat completion specific params
|
|
775
775
|
delete res.apiMode;
|
|
776
776
|
delete res.frequency_penalty;
|
|
777
777
|
delete res.presence_penalty;
|
|
@@ -797,6 +797,7 @@ export const createOpenAICompatibleRuntime = <T extends Record<string, any> = an
|
|
|
797
797
|
}
|
|
798
798
|
: {}),
|
|
799
799
|
input,
|
|
800
|
+
...(max_tokens && { max_output_tokens: max_tokens }),
|
|
800
801
|
store: false,
|
|
801
802
|
stream: !isStreaming ? undefined : isStreaming,
|
|
802
803
|
tools: tools?.map((tool) => this.convertChatCompletionToolToResponseTool(tool)),
|
|
@@ -409,6 +409,50 @@ describe('LobeOpenAI', () => {
|
|
|
409
409
|
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
410
410
|
expect(createCall.reasoning).toEqual({ effort: 'high', summary: 'auto' });
|
|
411
411
|
});
|
|
412
|
+
|
|
413
|
+
it('should convert max_tokens to max_output_tokens for responses API', async () => {
|
|
414
|
+
const payload = {
|
|
415
|
+
max_tokens: 2048,
|
|
416
|
+
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
417
|
+
model: 'o1-pro',
|
|
418
|
+
temperature: 0.7,
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
await instance.chat(payload);
|
|
422
|
+
|
|
423
|
+
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
424
|
+
expect(createCall.max_output_tokens).toBe(2048);
|
|
425
|
+
expect(createCall.max_tokens).toBeUndefined();
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('should not include max_output_tokens when max_tokens is undefined', async () => {
|
|
429
|
+
const payload = {
|
|
430
|
+
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
431
|
+
model: 'o1-pro',
|
|
432
|
+
temperature: 0.7,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
await instance.chat(payload);
|
|
436
|
+
|
|
437
|
+
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
438
|
+
expect(createCall.max_output_tokens).toBeUndefined();
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
it('should convert max_tokens to max_output_tokens for search-enabled models', async () => {
|
|
442
|
+
const payload = {
|
|
443
|
+
enabledSearch: true,
|
|
444
|
+
max_tokens: 4096,
|
|
445
|
+
messages: [{ content: 'Hello', role: 'user' as const }],
|
|
446
|
+
model: 'gpt-4o',
|
|
447
|
+
temperature: 0.7,
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
await instance.chat(payload);
|
|
451
|
+
|
|
452
|
+
const createCall = (instance['client'].responses.create as Mock).mock.calls[0][0];
|
|
453
|
+
expect(createCall.max_output_tokens).toBe(4096);
|
|
454
|
+
expect(createCall.max_tokens).toBeUndefined();
|
|
455
|
+
});
|
|
412
456
|
});
|
|
413
457
|
|
|
414
458
|
describe('supportsFlexTier', () => {
|
|
@@ -4,12 +4,17 @@ import {
|
|
|
4
4
|
OpenAICompatibleFactoryOptions,
|
|
5
5
|
createOpenAICompatibleRuntime,
|
|
6
6
|
} from '../../core/openaiCompatibleFactory';
|
|
7
|
+
import { processMultiProviderModelList } from '../../utils/modelParse';
|
|
8
|
+
|
|
9
|
+
export interface WenxinModelCard {
|
|
10
|
+
id: string;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export const params = {
|
|
9
14
|
baseURL: 'https://qianfan.baidubce.com/v2',
|
|
10
15
|
chatCompletion: {
|
|
11
16
|
handlePayload: (payload) => {
|
|
12
|
-
const { enabledSearch, ...rest } = payload;
|
|
17
|
+
const { enabledSearch, thinking, ...rest } = payload;
|
|
13
18
|
|
|
14
19
|
return {
|
|
15
20
|
...rest,
|
|
@@ -21,12 +26,28 @@ export const params = {
|
|
|
21
26
|
enable_trace: true,
|
|
22
27
|
},
|
|
23
28
|
}),
|
|
29
|
+
...(thinking && {
|
|
30
|
+
enable_thinking: { disabled: false, enabled: true }[thinking.type],
|
|
31
|
+
...(thinking?.budget_tokens !== 0 && {
|
|
32
|
+
thinking_budget: Math.min(Math.max(thinking?.budget_tokens, 100), 16_384),
|
|
33
|
+
}),
|
|
34
|
+
}),
|
|
24
35
|
} as any;
|
|
25
36
|
},
|
|
26
37
|
},
|
|
27
38
|
debug: {
|
|
28
39
|
chatCompletion: () => process.env.DEBUG_WENXIN_CHAT_COMPLETION === '1',
|
|
29
40
|
},
|
|
41
|
+
models: async ({ client }) => {
|
|
42
|
+
const modelsPage = (await client.models.list()) as any;
|
|
43
|
+
const modelList: WenxinModelCard[] = modelsPage.data;
|
|
44
|
+
|
|
45
|
+
const standardModelList = modelList.map((model) => ({
|
|
46
|
+
id: model.id,
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
return processMultiProviderModelList(standardModelList, 'wenxin');
|
|
50
|
+
},
|
|
30
51
|
provider: ModelProvider.Wenxin,
|
|
31
52
|
} satisfies OpenAICompatibleFactoryOptions;
|
|
32
53
|
|
|
@@ -98,6 +98,11 @@ export const MODEL_LIST_CONFIGS = {
|
|
|
98
98
|
reasoningKeywords: ['thinking', 'seed', 'ui-tars'],
|
|
99
99
|
visionKeywords: ['vision', '-m', 'seed', 'ui-tars'],
|
|
100
100
|
},
|
|
101
|
+
wenxin: {
|
|
102
|
+
functionCallKeywords: ['ernie-5', 'ernie-x1', 'pro', 'ernie-4.5-21b-a3b-thinking'],
|
|
103
|
+
reasoningKeywords: ['thinking', 'ernie-x', 'ernie-4.5-vl-28b-a3b'],
|
|
104
|
+
visionKeywords: ['-vl', 'ernie-5.0', 'picocr', 'qianfan-composition'],
|
|
105
|
+
},
|
|
101
106
|
xai: {
|
|
102
107
|
functionCallKeywords: ['grok'],
|
|
103
108
|
reasoningKeywords: ['mini', 'grok-4', 'grok-code-fast', '!non-reasoning'],
|
|
@@ -129,6 +134,7 @@ export const MODEL_OWNER_DETECTION_CONFIG = {
|
|
|
129
134
|
qwen: ['qwen', 'qwq', 'qvq'],
|
|
130
135
|
v0: ['v0'],
|
|
131
136
|
volcengine: ['doubao'],
|
|
137
|
+
wenxin: ['ernie', 'qianfan'],
|
|
132
138
|
xai: ['grok'],
|
|
133
139
|
zeroone: ['yi-'],
|
|
134
140
|
zhipu: ['glm'],
|
|
@@ -115,7 +115,7 @@ export const LobeBuiltinToolSchema = z.object({
|
|
|
115
115
|
type: z.literal('builtin'),
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
export interface BuiltinRenderProps<
|
|
118
|
+
export interface BuiltinRenderProps<Arguments = any, State = any, Content = any> {
|
|
119
119
|
apiName?: string;
|
|
120
120
|
args: Arguments;
|
|
121
121
|
content: Content;
|
|
@@ -125,7 +125,9 @@ export interface BuiltinRenderProps<Content = any, Arguments = any, State = any>
|
|
|
125
125
|
pluginState?: State;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
export type BuiltinRender = <
|
|
128
|
+
export type BuiltinRender = <A = any, S = any, C = any>(
|
|
129
|
+
props: BuiltinRenderProps<A, S, C>,
|
|
130
|
+
) => ReactNode;
|
|
129
131
|
|
|
130
132
|
export interface BuiltinPortalProps<Arguments = Record<string, any>, State = any> {
|
|
131
133
|
apiName?: string;
|
|
@@ -137,9 +139,9 @@ export interface BuiltinPortalProps<Arguments = Record<string, any>, State = any
|
|
|
137
139
|
|
|
138
140
|
export type BuiltinPortal = <T = any>(props: BuiltinPortalProps<T>) => ReactNode;
|
|
139
141
|
|
|
140
|
-
export interface BuiltinPlaceholderProps {
|
|
142
|
+
export interface BuiltinPlaceholderProps<T extends Record<string, any> = any> {
|
|
141
143
|
apiName: string;
|
|
142
|
-
args?:
|
|
144
|
+
args?: T;
|
|
143
145
|
identifier: string;
|
|
144
146
|
}
|
|
145
147
|
|
|
@@ -151,3 +153,12 @@ export interface BuiltinServerRuntimeOutput {
|
|
|
151
153
|
state?: any;
|
|
152
154
|
success: boolean;
|
|
153
155
|
}
|
|
156
|
+
|
|
157
|
+
export interface BuiltinInterventionProps<Arguments = any> {
|
|
158
|
+
apiName?: string;
|
|
159
|
+
args: Arguments;
|
|
160
|
+
identifier?: string;
|
|
161
|
+
messageId: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export type BuiltinIntervention = (props: BuiltinInterventionProps) => ReactNode;
|
|
@@ -5,9 +5,9 @@ import { z } from 'zod';
|
|
|
5
5
|
*/
|
|
6
6
|
export type HumanInterventionPolicy =
|
|
7
7
|
| 'never' // Never intervene, auto-execute
|
|
8
|
-
| '
|
|
8
|
+
| 'required'; // Always require intervention
|
|
9
9
|
|
|
10
|
-
export const HumanInterventionPolicySchema = z.enum(['never', '
|
|
10
|
+
export const HumanInterventionPolicySchema = z.enum(['never', 'required']);
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Argument Matcher for parameter-level filtering
|
|
@@ -116,6 +116,36 @@ export const HumanInterventionResponseSchema = z.object({
|
|
|
116
116
|
.optional(),
|
|
117
117
|
});
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* User's global intervention configuration
|
|
121
|
+
* Applied across all tools in the session
|
|
122
|
+
*/
|
|
123
|
+
export interface UserInterventionConfig {
|
|
124
|
+
/**
|
|
125
|
+
* Allow list of approved tools (used in 'allow-list' mode)
|
|
126
|
+
* Format: "identifier/apiName"
|
|
127
|
+
*
|
|
128
|
+
* Examples:
|
|
129
|
+
* - "bash/bash"
|
|
130
|
+
* - "web-browsing/crawlSinglePage"
|
|
131
|
+
* - "search/search"
|
|
132
|
+
*/
|
|
133
|
+
allowList?: string[];
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Tool approval mode
|
|
137
|
+
* - auto-run: Automatically approve all tools without user consent
|
|
138
|
+
* - allow-list: Only approve tools in the allow list
|
|
139
|
+
* - manual: Use tool's own humanIntervention config (default)
|
|
140
|
+
*/
|
|
141
|
+
approvalMode: 'auto-run' | 'allow-list' | 'manual';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const UserInterventionConfigSchema = z.object({
|
|
145
|
+
allowList: z.array(z.string()).optional(),
|
|
146
|
+
approvalMode: z.enum(['auto-run', 'allow-list', 'manual']),
|
|
147
|
+
});
|
|
148
|
+
|
|
119
149
|
/**
|
|
120
150
|
* Parameters for shouldIntervene method
|
|
121
151
|
*/
|
|
@@ -1,29 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* Tool approval mode
|
|
4
|
-
* - auto-run: Automatically approve all tools without user consent
|
|
5
|
-
* - allow-list: Only approve tools in the allow list
|
|
6
|
-
* - manual: Require manual approval for each tool execution
|
|
7
|
-
*/
|
|
8
|
-
approvalMode?: 'auto-run' | 'allow-list' | 'manual';
|
|
9
|
-
|
|
10
|
-
dalle: {
|
|
11
|
-
autoGenerate: boolean;
|
|
12
|
-
};
|
|
1
|
+
import { UserInterventionConfig } from '../../tool';
|
|
13
2
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
humanIntervention?: {
|
|
18
|
-
/**
|
|
19
|
-
* Allow list of approved tools (used in 'allow-list' mode)
|
|
20
|
-
* Format: "identifier/apiName"
|
|
21
|
-
*
|
|
22
|
-
* Examples:
|
|
23
|
-
* - "web-browsing/crawlSinglePage"
|
|
24
|
-
* - "bash/bash"
|
|
25
|
-
* - "search/search"
|
|
26
|
-
*/
|
|
27
|
-
allowList?: string[];
|
|
28
|
-
};
|
|
3
|
+
export interface UserToolConfig {
|
|
4
|
+
humanIntervention?: UserInterventionConfig;
|
|
29
5
|
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { ModelProviderCard } from '@/types/llm';
|
|
2
2
|
|
|
3
|
-
// ref https://cloud.baidu.com/doc/WENXINWORKSHOP/s/Nlks5zkzu
|
|
4
3
|
const BaiduWenxin: ModelProviderCard = {
|
|
5
4
|
chatModels: [],
|
|
6
|
-
checkModel: 'ernie-
|
|
5
|
+
checkModel: 'ernie-4.5-turbo-latest',
|
|
7
6
|
description:
|
|
8
7
|
'企业级一站式大模型与AI原生应用开发及服务平台,提供最全面易用的生成式人工智能模型开发、应用开发全流程工具链',
|
|
9
8
|
id: 'wenxin',
|
|
10
|
-
modelsUrl: 'https://
|
|
9
|
+
modelsUrl: 'https://console.bce.baidu.com/qianfan/modelcenter/model/buildIn/list',
|
|
11
10
|
name: 'Wenxin',
|
|
12
11
|
settings: {
|
|
13
12
|
proxyUrl: {
|
|
@@ -1,5 +1,138 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
+
exports[`createRemarkSelfClosingTagPlugin > should handle multiple tags in unordered list with directories and files 1`] = `
|
|
4
|
+
{
|
|
5
|
+
"children": [
|
|
6
|
+
{
|
|
7
|
+
"children": [
|
|
8
|
+
{
|
|
9
|
+
"position": {
|
|
10
|
+
"end": {
|
|
11
|
+
"column": 32,
|
|
12
|
+
"line": 1,
|
|
13
|
+
"offset": 31,
|
|
14
|
+
},
|
|
15
|
+
"start": {
|
|
16
|
+
"column": 1,
|
|
17
|
+
"line": 1,
|
|
18
|
+
"offset": 0,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
"type": "text",
|
|
22
|
+
"value": "我已查看了你桌面上 test 文件夹的目录,里面包含以下项目:",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
"position": {
|
|
26
|
+
"end": {
|
|
27
|
+
"column": 32,
|
|
28
|
+
"line": 1,
|
|
29
|
+
"offset": 31,
|
|
30
|
+
},
|
|
31
|
+
"start": {
|
|
32
|
+
"column": 1,
|
|
33
|
+
"line": 1,
|
|
34
|
+
"offset": 0,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
"type": "paragraph",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"children": [
|
|
41
|
+
{
|
|
42
|
+
"checked": null,
|
|
43
|
+
"children": [
|
|
44
|
+
{
|
|
45
|
+
"data": {
|
|
46
|
+
"hName": "localFile",
|
|
47
|
+
"hProperties": {
|
|
48
|
+
"isDirectory": true,
|
|
49
|
+
"name": ".config",
|
|
50
|
+
"path": "/Users/user/Desktop/test/.config",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
"type": "localFile",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
"position": {
|
|
57
|
+
"end": {
|
|
58
|
+
"column": 87,
|
|
59
|
+
"line": 3,
|
|
60
|
+
"offset": 119,
|
|
61
|
+
},
|
|
62
|
+
"start": {
|
|
63
|
+
"column": 1,
|
|
64
|
+
"line": 3,
|
|
65
|
+
"offset": 33,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
"spread": false,
|
|
69
|
+
"type": "listItem",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"checked": null,
|
|
73
|
+
"children": [
|
|
74
|
+
{
|
|
75
|
+
"data": {
|
|
76
|
+
"hName": "localFile",
|
|
77
|
+
"hProperties": {
|
|
78
|
+
"isDirectory": true,
|
|
79
|
+
"name": ".venv",
|
|
80
|
+
"path": "/Users/user/Desktop/test/.venv",
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
"type": "localFile",
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
"position": {
|
|
87
|
+
"end": {
|
|
88
|
+
"column": 79,
|
|
89
|
+
"line": 4,
|
|
90
|
+
"offset": 198,
|
|
91
|
+
},
|
|
92
|
+
"start": {
|
|
93
|
+
"column": 1,
|
|
94
|
+
"line": 4,
|
|
95
|
+
"offset": 120,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
"spread": false,
|
|
99
|
+
"type": "listItem",
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
"ordered": false,
|
|
103
|
+
"position": {
|
|
104
|
+
"end": {
|
|
105
|
+
"column": 79,
|
|
106
|
+
"line": 4,
|
|
107
|
+
"offset": 198,
|
|
108
|
+
},
|
|
109
|
+
"start": {
|
|
110
|
+
"column": 1,
|
|
111
|
+
"line": 3,
|
|
112
|
+
"offset": 33,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
"spread": false,
|
|
116
|
+
"start": null,
|
|
117
|
+
"type": "list",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
"position": {
|
|
121
|
+
"end": {
|
|
122
|
+
"column": 79,
|
|
123
|
+
"line": 4,
|
|
124
|
+
"offset": 198,
|
|
125
|
+
},
|
|
126
|
+
"start": {
|
|
127
|
+
"column": 1,
|
|
128
|
+
"line": 1,
|
|
129
|
+
"offset": 0,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
"type": "root",
|
|
133
|
+
}
|
|
134
|
+
`;
|
|
135
|
+
|
|
3
136
|
exports[`createRemarkSelfClosingTagPlugin > should handle tag within a list item and generate snapshot 1`] = `
|
|
4
137
|
{
|
|
5
138
|
"children": [
|
|
@@ -201,4 +201,52 @@ describe('createRemarkSelfClosingTagPlugin', () => {
|
|
|
201
201
|
const tree = processMarkdown(markdown, tagName);
|
|
202
202
|
expect(tree).toMatchSnapshot();
|
|
203
203
|
});
|
|
204
|
+
|
|
205
|
+
it('should handle multiple tags in unordered list with directories and files', () => {
|
|
206
|
+
const markdown = [
|
|
207
|
+
'我已查看了你桌面上 test 文件夹的目录,里面包含以下项目:',
|
|
208
|
+
'',
|
|
209
|
+
'- <localFile name=".config" path="/Users/user/Desktop/test/.config" isDirectory /> ', // 注意:行尾有 4 个空格
|
|
210
|
+
'- <localFile name=".venv" path="/Users/user/Desktop/test/.venv" isDirectory />',
|
|
211
|
+
].join('\n');
|
|
212
|
+
const tree = processMarkdown(markdown, tagName);
|
|
213
|
+
|
|
214
|
+
// Should have 2 children: a paragraph and a list
|
|
215
|
+
expect(tree.children).toHaveLength(2);
|
|
216
|
+
|
|
217
|
+
// First child should be the introductory paragraph
|
|
218
|
+
expect(tree.children[0].type).toBe('paragraph');
|
|
219
|
+
|
|
220
|
+
// Second child should be the unordered list
|
|
221
|
+
const listNode = tree.children[1];
|
|
222
|
+
expect(listNode.type).toBe('list');
|
|
223
|
+
expect(listNode.ordered).toBe(false);
|
|
224
|
+
|
|
225
|
+
// The list should have 2 items
|
|
226
|
+
expect(listNode.children).toHaveLength(2);
|
|
227
|
+
|
|
228
|
+
// Verify first item (.config directory) - this one has trailing spaces after />
|
|
229
|
+
// When a tag is standalone in a list item, remark doesn't wrap it in a paragraph
|
|
230
|
+
const firstItem = listNode.children[0];
|
|
231
|
+
expect(firstItem.type).toBe('listItem');
|
|
232
|
+
const firstTag = firstItem.children[0];
|
|
233
|
+
expect(firstTag.type).toBe(tagName);
|
|
234
|
+
expect(firstTag.data?.hProperties).toEqual({
|
|
235
|
+
name: '.config',
|
|
236
|
+
path: '/Users/user/Desktop/test/.config',
|
|
237
|
+
isDirectory: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Verify second item (.venv directory)
|
|
241
|
+
const secondItem = listNode.children[1];
|
|
242
|
+
const secondTag = secondItem.children[0];
|
|
243
|
+
expect(secondTag.type).toBe(tagName);
|
|
244
|
+
expect(secondTag.data?.hProperties).toEqual({
|
|
245
|
+
name: '.venv',
|
|
246
|
+
path: '/Users/user/Desktop/test/.venv',
|
|
247
|
+
isDirectory: true,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
expect(tree).toMatchSnapshot();
|
|
251
|
+
});
|
|
204
252
|
});
|
package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts
CHANGED
|
@@ -25,7 +25,8 @@ export const createRemarkSelfClosingTagPlugin =
|
|
|
25
25
|
(tagName: string): Plugin<[], any> =>
|
|
26
26
|
() => {
|
|
27
27
|
// Regex for the specific tag, ensure it matches the entire string for HTML check
|
|
28
|
-
|
|
28
|
+
// Allow trailing whitespace after /> to handle cases where markdown parsers include it
|
|
29
|
+
const exactTagRegex = new RegExp(`^<${tagName}(\\s+[^>]*?)?\\s*\\/>\\s*$`);
|
|
29
30
|
// Regex for finding tags within text
|
|
30
31
|
const textTagRegex = new RegExp(`<${tagName}(\\s+[^>]*?)?\\s*\\/>`, 'g');
|
|
31
32
|
|
package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx
CHANGED
|
@@ -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
|
);
|