@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.
Files changed (55) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/database/src/repositories/aiInfra/index.test.ts +234 -0
  5. package/packages/database/src/repositories/aiInfra/index.ts +106 -8
  6. package/packages/model-bank/src/types/aiModel.ts +2 -0
  7. package/packages/model-runtime/src/providers/google/index.ts +1 -1
  8. package/packages/model-runtime/src/providers/novita/__snapshots__/index.test.ts.snap +54 -0
  9. package/packages/model-runtime/src/providers/openai/__snapshots__/index.test.ts.snap +84 -0
  10. package/packages/model-runtime/src/utils/modelParse.test.ts +103 -0
  11. package/packages/model-runtime/src/utils/modelParse.ts +98 -23
  12. package/packages/model-runtime/src/utils/postProcessModelList.test.ts +0 -3
  13. package/packages/model-runtime/src/utils/postProcessModelList.ts +17 -4
  14. package/packages/prompts/src/prompts/index.ts +1 -0
  15. package/packages/prompts/src/prompts/search/crawlResults.test.ts +172 -0
  16. package/packages/prompts/src/prompts/search/crawlResults.ts +82 -0
  17. package/packages/prompts/src/prompts/search/index.ts +2 -0
  18. package/packages/prompts/src/prompts/search/searchResults.test.ts +138 -0
  19. package/packages/prompts/src/prompts/search/searchResults.ts +58 -0
  20. package/packages/prompts/src/prompts/search/xmlEscape.ts +21 -0
  21. package/packages/types/src/llm.ts +16 -0
  22. package/packages/types/src/tool/builtin.ts +8 -0
  23. package/packages/types/src/tool/index.ts +2 -0
  24. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +25 -0
  25. package/src/features/ChatInput/ActionBar/Search/Controls.tsx +47 -20
  26. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +17 -13
  27. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +1 -8
  28. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments/index.tsx +12 -16
  29. package/src/features/Conversation/Messages/Assistant/Tool/Render/LoadingPlaceholder/index.tsx +29 -0
  30. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +26 -7
  31. package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -0
  32. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +5 -5
  33. package/src/features/PluginsUI/Render/BuiltinType/index.tsx +1 -7
  34. package/src/helpers/getSearchConfig.test.ts +41 -0
  35. package/src/helpers/getSearchConfig.ts +5 -1
  36. package/src/locales/default/modelProvider.ts +15 -0
  37. package/src/store/aiInfra/slices/aiModel/action.ts +3 -0
  38. package/src/store/aiInfra/slices/aiModel/selectors.ts +7 -0
  39. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -1
  40. package/src/store/chat/slices/aiChat/actions/generateAIChatV2.ts +5 -1
  41. package/src/store/chat/slices/builtinTool/actions/search.test.ts +4 -3
  42. package/src/store/chat/slices/builtinTool/actions/search.ts +23 -15
  43. package/src/store/chat/slices/message/selectors.ts +10 -0
  44. package/src/styles/text.ts +10 -7
  45. package/src/tools/placeholders.ts +8 -0
  46. package/src/tools/web-browsing/Placeholder/PageContent.tsx +27 -0
  47. package/src/tools/web-browsing/Placeholder/Search.tsx +65 -0
  48. package/src/tools/web-browsing/Placeholder/index.tsx +40 -0
  49. package/src/tools/web-browsing/Render/PageContent/Loading.tsx +14 -5
  50. package/src/tools/web-browsing/Render/PageContent/Result.tsx +14 -13
  51. package/src/tools/web-browsing/Render/PageContent/index.tsx +15 -6
  52. package/src/tools/web-browsing/Render/Search/SearchQuery/SearchView.tsx +13 -19
  53. package/src/tools/web-browsing/Render/Search/SearchResult/index.tsx +21 -26
  54. package/src/tools/web-browsing/components/EngineAvatar.tsx +8 -2
  55. 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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#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.9",
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
- return {
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
- return [
142
- ...builtinModelList.flat(),
143
- ...allModels.filter((item) =>
233
+ // 用户数据库模型,检查搜索设置
234
+ const appendedUserModels = allModels
235
+ .filter((item) =>
144
236
  filterEnabled ? enabledProviderIds.has(item.providerId) && item.enabled : true,
145
- ),
146
- ].sort((a, b) => (a?.sort || -1) - (b?.sort || -1)) as EnabledAiModel[];
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 mergeArrayById(defaultModels, aiModels) as AiProviderModelListItem[];
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;