@lobehub/chat 1.136.9 → 1.136.11
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/changelog/v1.json +18 -0
- package/package.json +1 -1
- package/packages/database/src/repositories/aiInfra/index.test.ts +234 -0
- package/packages/database/src/repositories/aiInfra/index.ts +106 -8
- package/packages/model-bank/src/types/aiModel.ts +2 -0
- package/packages/model-runtime/src/providers/google/index.ts +1 -1
- package/packages/model-runtime/src/providers/novita/__snapshots__/index.test.ts.snap +54 -0
- package/packages/model-runtime/src/providers/openai/__snapshots__/index.test.ts.snap +84 -0
- package/packages/model-runtime/src/utils/modelParse.test.ts +103 -0
- package/packages/model-runtime/src/utils/modelParse.ts +98 -23
- package/packages/model-runtime/src/utils/postProcessModelList.test.ts +0 -3
- package/packages/model-runtime/src/utils/postProcessModelList.ts +17 -4
- package/packages/prompts/src/prompts/index.ts +1 -0
- package/packages/prompts/src/prompts/search/crawlResults.test.ts +172 -0
- package/packages/prompts/src/prompts/search/crawlResults.ts +82 -0
- package/packages/prompts/src/prompts/search/index.ts +2 -0
- package/packages/prompts/src/prompts/search/searchResults.test.ts +138 -0
- package/packages/prompts/src/prompts/search/searchResults.ts +58 -0
- package/packages/prompts/src/prompts/search/xmlEscape.ts +21 -0
- package/packages/types/src/llm.ts +16 -0
- package/packages/types/src/tool/builtin.ts +8 -0
- package/packages/types/src/tool/index.ts +2 -0
- package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +25 -0
- package/src/features/ChatInput/ActionBar/Search/Controls.tsx +47 -20
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +17 -13
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +1 -8
- package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +12 -16
- package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +26 -7
- package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -0
- package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +5 -5
- package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -7
- package/src/helpers/getSearchConfig.test.ts +41 -0
- package/src/helpers/getSearchConfig.ts +5 -1
- package/src/locales/default/modelProvider.ts +15 -0
- package/src/store/aiInfra/slices/aiModel/action.ts +3 -0
- package/src/store/aiInfra/slices/aiModel/selectors.ts +7 -0
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -1
- package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +5 -1
- package/src/store/chat/slices/builtinTool/actions/search.test.ts +4 -3
- package/src/store/chat/slices/builtinTool/actions/search.ts +23 -15
- package/src/store/chat/slices/message/selectors.ts +10 -0
- package/src/styles/text.ts +10 -7
- package/src/tools/placeholders.ts +8 -0
- package/src/tools/web-browsing/Placeholder/PageContent.tsx +27 -0
- package/src/tools/web-browsing/Placeholder/Search.tsx +65 -0
- package/src/tools/web-browsing/Placeholder/index.tsx +40 -0
- package/src/tools/web-browsing/Render/PageContent/Loading.tsx +14 -5
- package/src/tools/web-browsing/Render/PageContent/Result.tsx +14 -13
- package/src/tools/web-browsing/Render/PageContent/index.tsx +15 -6
- package/src/tools/web-browsing/Render/Search/SearchQuery/SearchView.tsx +13 -19
- package/src/tools/web-browsing/Render/Search/SearchResult/index.tsx +21 -26
- package/src/tools/web-browsing/components/EngineAvatar.tsx +8 -2
- package/src/tools/web-browsing/const.ts +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
### [Version 1.136.11](https://github.com/lobehub/lobe-chat/compare/v1.136.10...v1.136.11)
|
|
6
|
+
|
|
7
|
+
<sup>Released on **2025-10-11**</sup>
|
|
8
|
+
|
|
9
|
+
#### 💄 Styles
|
|
10
|
+
|
|
11
|
+
- **misc**: Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities.
|
|
12
|
+
|
|
13
|
+
<br/>
|
|
14
|
+
|
|
15
|
+
<details>
|
|
16
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
17
|
+
|
|
18
|
+
#### Styles
|
|
19
|
+
|
|
20
|
+
- **misc**: Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities, closes [#9022](https://github.com/lobehub/lobe-chat/issues/9022) ([4e44569](https://github.com/lobehub/lobe-chat/commit/4e44569))
|
|
21
|
+
|
|
22
|
+
</details>
|
|
23
|
+
|
|
24
|
+
<div align="right">
|
|
25
|
+
|
|
26
|
+
[](#readme-top)
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
### [Version 1.136.10](https://github.com/lobehub/lobe-chat/compare/v1.136.9...v1.136.10)
|
|
31
|
+
|
|
32
|
+
<sup>Released on **2025-10-11**</sup>
|
|
33
|
+
|
|
34
|
+
#### 💄 Styles
|
|
35
|
+
|
|
36
|
+
- **misc**: Improve search experience.
|
|
37
|
+
|
|
38
|
+
<br/>
|
|
39
|
+
|
|
40
|
+
<details>
|
|
41
|
+
<summary><kbd>Improvements and Fixes</kbd></summary>
|
|
42
|
+
|
|
43
|
+
#### Styles
|
|
44
|
+
|
|
45
|
+
- **misc**: Improve search experience, closes [#9661](https://github.com/lobehub/lobe-chat/issues/9661) ([8624f84](https://github.com/lobehub/lobe-chat/commit/8624f84))
|
|
46
|
+
|
|
47
|
+
</details>
|
|
48
|
+
|
|
49
|
+
<div align="right">
|
|
50
|
+
|
|
51
|
+
[](#readme-top)
|
|
52
|
+
|
|
53
|
+
</div>
|
|
54
|
+
|
|
5
55
|
### [Version 1.136.9](https://github.com/lobehub/lobe-chat/compare/v1.136.8...v1.136.9)
|
|
6
56
|
|
|
7
57
|
<sup>Released on **2025-10-11**</sup>
|
package/changelog/v1.json
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"children": {
|
|
4
|
+
"improvements": [
|
|
5
|
+
"Add capability inference for web search, image output and video recognition in model parsing and update UI form items to support search, imageOutput and video abilities."
|
|
6
|
+
]
|
|
7
|
+
},
|
|
8
|
+
"date": "2025-10-11",
|
|
9
|
+
"version": "1.136.11"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"children": {
|
|
13
|
+
"improvements": [
|
|
14
|
+
"Improve search experience."
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"date": "2025-10-11",
|
|
18
|
+
"version": "1.136.10"
|
|
19
|
+
},
|
|
2
20
|
{
|
|
3
21
|
"children": {
|
|
4
22
|
"improvements": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "1.136.
|
|
3
|
+
"version": "1.136.11",
|
|
4
4
|
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework",
|
|
@@ -298,6 +298,127 @@ describe('AiInfraRepos', () => {
|
|
|
298
298
|
const disabledModels = result.filter((model) => !model.enabled);
|
|
299
299
|
expect(disabledModels.length).toBeGreaterThan(0);
|
|
300
300
|
});
|
|
301
|
+
|
|
302
|
+
it('should allow search=true and add searchImpl=params when user enables it without providing settings (builtin has no search and no settings)', async () => {
|
|
303
|
+
const mockProviders = [
|
|
304
|
+
{ enabled: true, id: 'openai', name: 'OpenAI', source: 'builtin' as const },
|
|
305
|
+
] as AiProviderListItem[];
|
|
306
|
+
|
|
307
|
+
// User explicitly enables search but provides no settings field
|
|
308
|
+
const userModel: EnabledAiModel = {
|
|
309
|
+
id: 'gpt-4',
|
|
310
|
+
providerId: 'openai',
|
|
311
|
+
enabled: true,
|
|
312
|
+
type: 'chat',
|
|
313
|
+
abilities: { search: true },
|
|
314
|
+
// no settings on user model
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// Builtin does NOT have search capability and thus no settings
|
|
318
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue([
|
|
319
|
+
{
|
|
320
|
+
id: 'gpt-4',
|
|
321
|
+
enabled: true,
|
|
322
|
+
type: 'chat' as const,
|
|
323
|
+
abilities: { search: false },
|
|
324
|
+
// no settings since builtin search is false
|
|
325
|
+
},
|
|
326
|
+
]);
|
|
327
|
+
|
|
328
|
+
vi.spyOn(repo, 'getAiProviderList').mockResolvedValue(mockProviders);
|
|
329
|
+
vi.spyOn(repo.aiModelModel, 'getAllModels').mockResolvedValue([userModel]);
|
|
330
|
+
|
|
331
|
+
const result = await repo.getEnabledModels();
|
|
332
|
+
|
|
333
|
+
const merged = result.find(
|
|
334
|
+
(m) => m.id === 'gpt-4' && m.providerId === 'openai' && m.type === 'chat',
|
|
335
|
+
);
|
|
336
|
+
expect(merged).toBeDefined();
|
|
337
|
+
expect(merged?.abilities).toMatchObject({ search: true });
|
|
338
|
+
// settings should remain undefined because builtin had none and user never has settings
|
|
339
|
+
expect(merged?.settings).toEqual({ searchImpl: 'params' });
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it('should remove builtin rearch settings and disable search when user turns search off', async () => {
|
|
343
|
+
const mockProviders = [
|
|
344
|
+
{ enabled: true, id: 'openai', name: 'OpenAI', source: 'builtin' as const },
|
|
345
|
+
] as AiProviderListItem[];
|
|
346
|
+
|
|
347
|
+
// User explicitly disables search and provides no settings field
|
|
348
|
+
const userModel: EnabledAiModel = {
|
|
349
|
+
id: 'gpt-4',
|
|
350
|
+
providerId: 'openai',
|
|
351
|
+
enabled: true,
|
|
352
|
+
type: 'chat',
|
|
353
|
+
abilities: { search: false },
|
|
354
|
+
// no settings on user model
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const builtinSettings = { searchImpl: 'tool' as const };
|
|
358
|
+
|
|
359
|
+
// Builtin has search capability and corresponding settings
|
|
360
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue([
|
|
361
|
+
{
|
|
362
|
+
id: 'gpt-4',
|
|
363
|
+
enabled: true,
|
|
364
|
+
type: 'chat' as const,
|
|
365
|
+
abilities: { search: true },
|
|
366
|
+
settings: builtinSettings,
|
|
367
|
+
},
|
|
368
|
+
]);
|
|
369
|
+
|
|
370
|
+
vi.spyOn(repo, 'getAiProviderList').mockResolvedValue(mockProviders);
|
|
371
|
+
vi.spyOn(repo.aiModelModel, 'getAllModels').mockResolvedValue([userModel]);
|
|
372
|
+
|
|
373
|
+
const result = await repo.getEnabledModels();
|
|
374
|
+
|
|
375
|
+
const merged = result.find(
|
|
376
|
+
(m) => m.id === 'gpt-4' && m.providerId === 'openai' && m.type === 'chat',
|
|
377
|
+
);
|
|
378
|
+
expect(merged).toBeDefined();
|
|
379
|
+
// User's choice takes precedence
|
|
380
|
+
expect(merged?.abilities).toMatchObject({ search: false });
|
|
381
|
+
// Builtin settings are preserved on the merged model
|
|
382
|
+
expect(merged?.settings).toBeUndefined();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should set search=true and settings=params for custom provider when user enables search and builtin has no search/settings', async () => {
|
|
386
|
+
const mockProviders = [
|
|
387
|
+
{ enabled: true, id: 'custom', name: 'Custom Provider', source: 'custom' as const },
|
|
388
|
+
] as AiProviderListItem[];
|
|
389
|
+
|
|
390
|
+
// Builtin (preset) has the model but without search and without settings
|
|
391
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue([
|
|
392
|
+
{
|
|
393
|
+
id: 'my-model',
|
|
394
|
+
enabled: true,
|
|
395
|
+
type: 'chat' as const,
|
|
396
|
+
abilities: { search: false },
|
|
397
|
+
// no settings
|
|
398
|
+
} as any,
|
|
399
|
+
]);
|
|
400
|
+
|
|
401
|
+
// User explicitly enables search; user models do not carry settings
|
|
402
|
+
vi.spyOn(repo.aiModelModel, 'getAllModels').mockResolvedValue([
|
|
403
|
+
{
|
|
404
|
+
id: 'my-model',
|
|
405
|
+
providerId: 'custom',
|
|
406
|
+
enabled: true,
|
|
407
|
+
type: 'chat',
|
|
408
|
+
abilities: { search: true },
|
|
409
|
+
} as EnabledAiModel,
|
|
410
|
+
]);
|
|
411
|
+
|
|
412
|
+
vi.spyOn(repo, 'getAiProviderList').mockResolvedValue(mockProviders);
|
|
413
|
+
|
|
414
|
+
const result = await repo.getEnabledModels();
|
|
415
|
+
|
|
416
|
+
const merged = result.find((m) => m.id === 'my-model' && m.providerId === 'custom');
|
|
417
|
+
expect(merged).toBeDefined();
|
|
418
|
+
expect(merged?.abilities).toMatchObject({ search: true });
|
|
419
|
+
// For custom provider, when user enables search with no builtin settings, default to 'params'
|
|
420
|
+
expect(merged?.settings).toEqual({ searchImpl: 'params' });
|
|
421
|
+
});
|
|
301
422
|
});
|
|
302
423
|
|
|
303
424
|
describe('getAiProviderModelList', () => {
|
|
@@ -380,6 +501,119 @@ describe('AiInfraRepos', () => {
|
|
|
380
501
|
|
|
381
502
|
expect(result).toHaveLength(0);
|
|
382
503
|
});
|
|
504
|
+
|
|
505
|
+
// New tests for getAiProviderModelList per the corrected behavior
|
|
506
|
+
it('should allow search=true and add searchImpl=params when user enables it without providing settings (builtin has no search and no settings)', async () => {
|
|
507
|
+
const providerId = 'openai';
|
|
508
|
+
|
|
509
|
+
// User explicitly enables search in custom model, but provides no settings
|
|
510
|
+
const userModels: AiProviderModelListItem[] = [
|
|
511
|
+
{
|
|
512
|
+
id: 'gpt-4',
|
|
513
|
+
type: 'chat',
|
|
514
|
+
enabled: true,
|
|
515
|
+
abilities: { search: true },
|
|
516
|
+
// user never has settings
|
|
517
|
+
} as any,
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
// Builtin has no search and no settings
|
|
521
|
+
const builtinModels: AiProviderModelListItem[] = [
|
|
522
|
+
{
|
|
523
|
+
id: 'gpt-4',
|
|
524
|
+
type: 'chat',
|
|
525
|
+
enabled: true,
|
|
526
|
+
abilities: { search: false },
|
|
527
|
+
// no settings
|
|
528
|
+
} as any,
|
|
529
|
+
];
|
|
530
|
+
|
|
531
|
+
vi.spyOn(repo.aiModelModel, 'getModelListByProviderId').mockResolvedValue(userModels);
|
|
532
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue(builtinModels);
|
|
533
|
+
|
|
534
|
+
const result = await repo.getAiProviderModelList(providerId);
|
|
535
|
+
|
|
536
|
+
const merged = result.find((m) => m.id === 'gpt-4');
|
|
537
|
+
expect(merged).toBeDefined();
|
|
538
|
+
expect(merged.abilities).toMatchObject({ search: true });
|
|
539
|
+
// when user enables search with no settings, default searchImpl should be 'params'
|
|
540
|
+
expect(merged.settings).toEqual({ searchImpl: 'params' });
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
it('should remove builtin search settings and disable search when user turns search off', async () => {
|
|
544
|
+
const providerId = 'openai';
|
|
545
|
+
|
|
546
|
+
// User explicitly disables search in custom model, and provides no settings
|
|
547
|
+
const userModels: AiProviderModelListItem[] = [
|
|
548
|
+
{
|
|
549
|
+
id: 'gpt-4',
|
|
550
|
+
type: 'chat',
|
|
551
|
+
enabled: true,
|
|
552
|
+
abilities: { search: false },
|
|
553
|
+
// user never has settings
|
|
554
|
+
} as any,
|
|
555
|
+
];
|
|
556
|
+
|
|
557
|
+
// Builtin has search with settings
|
|
558
|
+
const builtinModels: AiProviderModelListItem[] = [
|
|
559
|
+
{
|
|
560
|
+
id: 'gpt-4',
|
|
561
|
+
type: 'chat',
|
|
562
|
+
enabled: true,
|
|
563
|
+
abilities: { search: true },
|
|
564
|
+
settings: { searchImpl: 'tool' },
|
|
565
|
+
} as any,
|
|
566
|
+
];
|
|
567
|
+
|
|
568
|
+
vi.spyOn(repo.aiModelModel, 'getModelListByProviderId').mockResolvedValue(userModels);
|
|
569
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue(builtinModels);
|
|
570
|
+
|
|
571
|
+
const result = await repo.getAiProviderModelList(providerId);
|
|
572
|
+
|
|
573
|
+
const merged = result.find((m) => m.id === 'gpt-4');
|
|
574
|
+
expect(merged).toBeDefined();
|
|
575
|
+
// User's choice takes precedence
|
|
576
|
+
expect(merged.abilities).toMatchObject({ search: false });
|
|
577
|
+
// Builtin search settings should be removed since user turned search off
|
|
578
|
+
expect(merged.settings).toBeUndefined();
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
it('should set search=true and settings=params for custom provider when user enables search and builtin has no search/settings', async () => {
|
|
582
|
+
const providerId = 'custom';
|
|
583
|
+
|
|
584
|
+
// User list: model with search enabled, but no settings
|
|
585
|
+
const userModels: AiProviderModelListItem[] = [
|
|
586
|
+
{
|
|
587
|
+
id: 'my-model',
|
|
588
|
+
type: 'chat',
|
|
589
|
+
enabled: true,
|
|
590
|
+
abilities: { search: true },
|
|
591
|
+
// user never has settings
|
|
592
|
+
} as any,
|
|
593
|
+
];
|
|
594
|
+
|
|
595
|
+
// Default list: same model without search and without settings
|
|
596
|
+
const defaultModels: AiProviderModelListItem[] = [
|
|
597
|
+
{
|
|
598
|
+
id: 'my-model',
|
|
599
|
+
type: 'chat',
|
|
600
|
+
enabled: true,
|
|
601
|
+
abilities: { search: false },
|
|
602
|
+
// no settings
|
|
603
|
+
} as any,
|
|
604
|
+
];
|
|
605
|
+
|
|
606
|
+
vi.spyOn(repo.aiModelModel, 'getModelListByProviderId').mockResolvedValue(userModels);
|
|
607
|
+
vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue(defaultModels);
|
|
608
|
+
|
|
609
|
+
const result = await repo.getAiProviderModelList(providerId);
|
|
610
|
+
|
|
611
|
+
const merged = result.find((m) => m.id === 'my-model') as any;
|
|
612
|
+
expect(merged).toBeDefined();
|
|
613
|
+
expect(merged.abilities).toMatchObject({ search: true });
|
|
614
|
+
// For custom provider, when user enables search with no builtin settings, default to 'params'
|
|
615
|
+
expect(merged.settings).toEqual({ searchImpl: 'params' });
|
|
616
|
+
});
|
|
383
617
|
});
|
|
384
618
|
|
|
385
619
|
describe('getAiProviderRuntimeState', () => {
|
|
@@ -23,6 +23,97 @@ import { LobeChatDatabase } from '../../type';
|
|
|
23
23
|
|
|
24
24
|
type DecryptUserKeyVaults = (encryptKeyVaultsStr: string | null) => Promise<any>;
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Provider 级默认表(只在本地内置模型没给出 settings.searchImpl 和 settings.searchProvider 时使用)
|
|
28
|
+
* 注意:不在 DB 存储,纯读取时注入
|
|
29
|
+
*/
|
|
30
|
+
const PROVIDER_SEARCH_DEFAULTS: Record<
|
|
31
|
+
string,
|
|
32
|
+
{ searchImpl?: 'tool' | 'params' | 'internal'; searchProvider?: string }
|
|
33
|
+
> = {
|
|
34
|
+
ai360: { searchImpl: 'params' },
|
|
35
|
+
aihubmix: { searchImpl: 'params' },
|
|
36
|
+
anthropic: { searchImpl: 'params' },
|
|
37
|
+
baichuan: { searchImpl: 'params' },
|
|
38
|
+
default: { searchImpl: 'params' },
|
|
39
|
+
google: { searchImpl: 'params', searchProvider: 'google' },
|
|
40
|
+
hunyuan: { searchImpl: 'params' },
|
|
41
|
+
jina: { searchImpl: 'internal' },
|
|
42
|
+
minimax: { searchImpl: 'params' },
|
|
43
|
+
// openai: 默认 params,但对 -search- 型号做 internal 特判
|
|
44
|
+
openai: { searchImpl: 'params' },
|
|
45
|
+
// perplexity: 默认 internal
|
|
46
|
+
perplexity: { searchImpl: 'internal' },
|
|
47
|
+
qwen: { searchImpl: 'params' },
|
|
48
|
+
spark: { searchImpl: 'params' }, // 某些模型(如 max-32k)若内置标了 internal,会优先使用内置
|
|
49
|
+
stepfun: { searchImpl: 'params' },
|
|
50
|
+
vertexai: { searchImpl: 'params', searchProvider: 'google' },
|
|
51
|
+
wenxin: { searchImpl: 'params' },
|
|
52
|
+
xai: { searchImpl: 'params' },
|
|
53
|
+
zhipu: { searchImpl: 'params' },
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// 特殊模型配置 - 模型级别的特殊设置会覆盖服务商默认配置
|
|
57
|
+
const MODEL_SEARCH_DEFAULTS: Record<
|
|
58
|
+
string,
|
|
59
|
+
Record<string, { searchImpl?: 'tool' | 'params' | 'internal'; searchProvider?: string }>
|
|
60
|
+
> = {
|
|
61
|
+
openai: {
|
|
62
|
+
'gpt-4o-mini-search-preview': { searchImpl: 'internal' },
|
|
63
|
+
'gpt-4o-search-preview': { searchImpl: 'internal' },
|
|
64
|
+
// 可在此处添加其他特殊模型配置
|
|
65
|
+
},
|
|
66
|
+
spark: {
|
|
67
|
+
'max-32k': { searchImpl: 'internal' },
|
|
68
|
+
},
|
|
69
|
+
// 可在此处添加其他服务商的特殊模型配置
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// 根据 providerId + modelId 推断默认 settings
|
|
73
|
+
const inferProviderSearchDefaults = (
|
|
74
|
+
providerId: string | undefined,
|
|
75
|
+
modelId: string,
|
|
76
|
+
): { searchImpl?: 'tool' | 'params' | 'internal'; searchProvider?: string } => {
|
|
77
|
+
const modelSpecificConfig = providerId ? MODEL_SEARCH_DEFAULTS[providerId]?.[modelId] : undefined;
|
|
78
|
+
if (modelSpecificConfig) {
|
|
79
|
+
return modelSpecificConfig;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return (providerId && PROVIDER_SEARCH_DEFAULTS[providerId]) || PROVIDER_SEARCH_DEFAULTS.default;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// 仅在读取时注入 settings; 根据 abilities.search 来添加或删去settings 中的 search 相关字段
|
|
86
|
+
const injectSearchSettings = (providerId: string, item: any) => {
|
|
87
|
+
const abilities = item?.abilities || {};
|
|
88
|
+
|
|
89
|
+
// 模型未开启搜索能力:移除 settings 中的 search 相关字段,确保 UI 不显示启用模型内置搜索
|
|
90
|
+
if (abilities.search !== true) {
|
|
91
|
+
if (item?.settings?.searchImpl || item?.settings?.searchProvider) {
|
|
92
|
+
const next = { ...item } as any;
|
|
93
|
+
if (next.settings) {
|
|
94
|
+
const { searchImpl, searchProvider, ...restSettings } = next.settings;
|
|
95
|
+
next.settings = Object.keys(restSettings).length > 0 ? restSettings : undefined;
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
98
|
+
}
|
|
99
|
+
return item;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 内置(本地)模型如果已经带了任一字段,直接保留,不覆盖
|
|
103
|
+
if (item?.settings?.searchImpl || item?.settings?.searchProvider) return item;
|
|
104
|
+
|
|
105
|
+
// 否则按 providerId + modelId
|
|
106
|
+
const searchSettings = inferProviderSearchDefaults(providerId, item.id);
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...item,
|
|
110
|
+
settings: {
|
|
111
|
+
...item.settings,
|
|
112
|
+
...searchSettings,
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
|
|
26
117
|
export class AiInfraRepos {
|
|
27
118
|
private userId: string;
|
|
28
119
|
private db: LobeChatDatabase;
|
|
@@ -107,6 +198,7 @@ export class AiInfraRepos {
|
|
|
107
198
|
.map<EnabledAiModel & { enabled?: boolean | null }>((item) => {
|
|
108
199
|
const user = allModels.find((m) => m.id === item.id && m.providerId === provider.id);
|
|
109
200
|
|
|
201
|
+
// 用户未修改本地模型
|
|
110
202
|
if (!user)
|
|
111
203
|
return {
|
|
112
204
|
...item,
|
|
@@ -114,7 +206,7 @@ export class AiInfraRepos {
|
|
|
114
206
|
providerId: provider.id,
|
|
115
207
|
};
|
|
116
208
|
|
|
117
|
-
|
|
209
|
+
const mergedModel = {
|
|
118
210
|
...item,
|
|
119
211
|
abilities: !isEmpty(user.abilities) ? user.abilities : item.abilities || {},
|
|
120
212
|
config: !isEmpty(user.config) ? user.config : item.config,
|
|
@@ -130,6 +222,7 @@ export class AiInfraRepos {
|
|
|
130
222
|
sort: user.sort || undefined,
|
|
131
223
|
type: user.type || item.type,
|
|
132
224
|
};
|
|
225
|
+
return injectSearchSettings(provider.id, mergedModel); // 用户修改本地模型,检查搜索设置
|
|
133
226
|
})
|
|
134
227
|
.filter((item) => (filterEnabled ? item.enabled : true));
|
|
135
228
|
},
|
|
@@ -137,13 +230,16 @@ export class AiInfraRepos {
|
|
|
137
230
|
);
|
|
138
231
|
|
|
139
232
|
const enabledProviderIds = new Set(enabledProviders.map((item) => item.id));
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
...allModels.filter((item) =>
|
|
233
|
+
// 用户数据库模型,检查搜索设置
|
|
234
|
+
const appendedUserModels = allModels
|
|
235
|
+
.filter((item) =>
|
|
144
236
|
filterEnabled ? enabledProviderIds.has(item.providerId) && item.enabled : true,
|
|
145
|
-
)
|
|
146
|
-
|
|
237
|
+
)
|
|
238
|
+
.map((item) => injectSearchSettings(item.providerId, item));
|
|
239
|
+
|
|
240
|
+
return [...builtinModelList.flat(), ...appendedUserModels].sort(
|
|
241
|
+
(a, b) => (a?.sort || -1) - (b?.sort || -1),
|
|
242
|
+
) as EnabledAiModel[];
|
|
147
243
|
};
|
|
148
244
|
|
|
149
245
|
getAiProviderRuntimeState = async (
|
|
@@ -181,8 +277,10 @@ export class AiInfraRepos {
|
|
|
181
277
|
|
|
182
278
|
const defaultModels: AiProviderModelListItem[] =
|
|
183
279
|
(await this.fetchBuiltinModels(providerId)) || [];
|
|
280
|
+
// 这里不修改搜索设置不影响使用,但是为了get数据统一
|
|
281
|
+
const mergedModel = mergeArrayById(defaultModels, aiModels) as AiProviderModelListItem[];
|
|
184
282
|
|
|
185
|
-
return
|
|
283
|
+
return mergedModel.map((m) => injectSearchSettings(providerId, m));
|
|
186
284
|
};
|
|
187
285
|
|
|
188
286
|
/**
|
|
@@ -63,8 +63,10 @@ export interface ModelAbilities {
|
|
|
63
63
|
const AiModelAbilitiesSchema = z.object({
|
|
64
64
|
// files: z.boolean().optional(),
|
|
65
65
|
functionCall: z.boolean().optional(),
|
|
66
|
+
imageOutput: z.boolean().optional(),
|
|
66
67
|
reasoning: z.boolean().optional(),
|
|
67
68
|
search: z.boolean().optional(),
|
|
69
|
+
video: z.boolean().optional(),
|
|
68
70
|
vision: z.boolean().optional(),
|
|
69
71
|
});
|
|
70
72
|
|
|
@@ -471,7 +471,7 @@ export class LobeGoogleAI implements LobeRuntimeAI {
|
|
|
471
471
|
|
|
472
472
|
const { MODEL_LIST_CONFIGS, processModelList } = await import('../../utils/modelParse');
|
|
473
473
|
|
|
474
|
-
return processModelList(processedModels, MODEL_LIST_CONFIGS.google);
|
|
474
|
+
return processModelList(processedModels, MODEL_LIST_CONFIGS.google, 'google');
|
|
475
475
|
} catch (error) {
|
|
476
476
|
log('Failed to fetch Google models: %O', error);
|
|
477
477
|
throw error;
|