@lobehub/lobehub 2.0.0-next.261 → 2.0.0-next.263

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 (163) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/changelog/v1.json +18 -0
  5. package/locales/zh-CN/chat.json +1 -0
  6. package/locales/zh-CN/modelProvider.json +20 -0
  7. package/package.json +1 -1
  8. package/packages/database/src/models/aiModel.ts +2 -0
  9. package/packages/database/src/repositories/aiInfra/index.test.ts +41 -1
  10. package/packages/database/src/repositories/aiInfra/index.ts +3 -1
  11. package/packages/model-runtime/src/providers/openrouter/index.test.ts +9 -55
  12. package/packages/model-runtime/src/providers/openrouter/index.ts +47 -27
  13. package/packages/model-runtime/src/providers/openrouter/type.ts +16 -28
  14. package/packages/model-runtime/src/providers/vercelaigateway/index.test.ts +6 -6
  15. package/packages/model-runtime/src/providers/vercelaigateway/index.ts +54 -11
  16. package/packages/model-runtime/src/utils/modelParse.test.ts +185 -3
  17. package/packages/model-runtime/src/utils/modelParse.ts +108 -1
  18. package/packages/types/src/agent/chatConfig.ts +1 -1
  19. package/packages/types/src/agent/item.ts +10 -10
  20. package/packages/types/src/agentRuntime.ts +1 -1
  21. package/packages/types/src/clientDB.ts +2 -2
  22. package/packages/types/src/llm.ts +3 -1
  23. package/packages/types/src/topic/topic.ts +2 -2
  24. package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Body.tsx +1 -1
  25. package/src/app/[variants]/(main)/{chat → agent}/_layout/index.tsx +1 -1
  26. package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/AgentWelcome/OpeningQuestions.tsx +1 -1
  27. package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/Tags/index.tsx +1 -4
  28. package/src/app/[variants]/(main)/{chat → agent}/features/Portal/_layout/Desktop.tsx +1 -3
  29. package/src/app/[variants]/(main)/{chat → agent}/features/Portal/features/PortalPanel.tsx +3 -2
  30. package/src/app/[variants]/(main)/{chat → agent}/features/Portal/index.tsx +3 -2
  31. package/src/app/[variants]/(main)/group/features/Conversation/AgentWelcome/ToolAuthAlert.tsx +1 -1
  32. package/src/app/[variants]/(main)/group/features/Portal/index.tsx +2 -2
  33. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/ExtendParamsSelect.tsx +398 -0
  34. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/Form.tsx +11 -2
  35. package/src/app/[variants]/(main)/settings/provider/features/ModelList/CreateNewModelModal/__tests__/ExtendParamsSelect.test.tsx +59 -0
  36. package/src/app/[variants]/(mobile)/chat/_layout/index.tsx +1 -1
  37. package/src/app/[variants]/(mobile)/chat/features/ChatHeader/index.tsx +1 -1
  38. package/src/app/[variants]/(mobile)/chat/features/Topic/index.tsx +1 -1
  39. package/src/app/[variants]/(mobile)/chat/index.tsx +4 -4
  40. package/src/app/[variants]/(mobile)/chat/settings/features/SettingButton.tsx +1 -1
  41. package/src/app/[variants]/router/desktopRouter.config.tsx +4 -4
  42. package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +1 -1
  43. package/src/features/ChatInput/ActionBar/Model/GPT51ReasoningEffortSlider.tsx +9 -54
  44. package/src/features/ChatInput/ActionBar/Model/GPT52ProReasoningEffortSlider.tsx +9 -53
  45. package/src/features/ChatInput/ActionBar/Model/GPT52ReasoningEffortSlider.tsx +9 -55
  46. package/src/features/ChatInput/ActionBar/Model/GPT5ReasoningEffortSlider.tsx +9 -54
  47. package/src/features/ChatInput/ActionBar/Model/ImageAspectRatioSelect.tsx +50 -16
  48. package/src/features/ChatInput/ActionBar/Model/ImageResolutionSlider.tsx +7 -53
  49. package/src/features/ChatInput/ActionBar/Model/LevelSlider.tsx +92 -0
  50. package/src/features/ChatInput/ActionBar/Model/ReasoningEffortSlider.tsx +9 -53
  51. package/src/features/ChatInput/ActionBar/Model/TextVerbositySlider.tsx +9 -53
  52. package/src/features/ChatInput/ActionBar/Model/ThinkingLevel2Slider.tsx +9 -52
  53. package/src/features/ChatInput/ActionBar/Model/ThinkingLevelSlider.tsx +9 -54
  54. package/src/features/ChatInput/ActionBar/Model/ThinkingSlider.tsx +20 -56
  55. package/src/features/ChatInput/ActionBar/Model/__tests__/createLevelSlider.test.tsx +126 -0
  56. package/src/features/ChatInput/ActionBar/Model/createLevelSlider.tsx +105 -0
  57. package/src/locales/default/chat.ts +1 -0
  58. package/src/locales/default/modelProvider.ts +38 -0
  59. package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +0 -1
  60. package/src/store/chat/slices/builtinTool/initialState.ts +0 -10
  61. package/src/store/chat/slices/builtinTool/selectors.test.ts +0 -63
  62. package/src/store/chat/slices/builtinTool/selectors.ts +0 -7
  63. package/src/app/[variants]/(main)/chat/features/Conversation/Header/Tags/HistoryLimitTags.tsx +0 -25
  64. package/src/app/[variants]/(main)/group/features/Conversation/Header/Tags/HistoryLimitTags.tsx +0 -25
  65. /package/src/app/[variants]/(main)/{chat → agent}/_layout/AgentIdSync.tsx +0 -0
  66. /package/src/app/[variants]/(main)/{chat → agent}/_layout/RegisterHotkeys.tsx +0 -0
  67. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Header/AddTopicButon.tsx +0 -0
  68. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Header/Agent/SwitchPanel.tsx +0 -0
  69. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Header/Agent/index.tsx +0 -0
  70. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Header/Nav.tsx +0 -0
  71. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Header/index.tsx +0 -0
  72. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/Actions.tsx +0 -0
  73. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/AllTopicsDrawer/Content.tsx +0 -0
  74. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/AllTopicsDrawer/index.tsx +0 -0
  75. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/CronTopicList/CronTopicGroup.tsx +0 -0
  76. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/CronTopicList/CronTopicItem.tsx +0 -0
  77. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/CronTopicList/index.tsx +0 -0
  78. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/List/Item/Actions.tsx +0 -0
  79. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/List/Item/Editing.tsx +0 -0
  80. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/List/Item/index.tsx +0 -0
  81. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/List/Item/useDropdownMenu.tsx +0 -0
  82. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/List/index.tsx +0 -0
  83. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ByTimeMode/GroupItem.tsx +0 -0
  84. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ByTimeMode/index.tsx +0 -0
  85. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/FlatMode/index.tsx +0 -0
  86. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/SearchResult/index.tsx +0 -0
  87. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Actions.tsx +0 -0
  88. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/Editing.tsx +0 -0
  89. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/index.tsx +0 -0
  90. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ThreadList/ThreadItem/useDropdownMenu.tsx +0 -0
  91. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/ThreadList/index.tsx +0 -0
  92. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicListContent/index.tsx +0 -0
  93. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/TopicSearchBar/index.tsx +0 -0
  94. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/hooks/useThreadNavigation.ts +0 -0
  95. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/hooks/useTopicNavigation.ts +0 -0
  96. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/index.tsx +0 -0
  97. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/Topic/useDropdownMenu.tsx +0 -0
  98. /package/src/app/[variants]/(main)/{chat → agent}/_layout/Sidebar/index.tsx +0 -0
  99. /package/src/app/[variants]/(main)/{chat → agent}/_layout/style.ts +0 -0
  100. /package/src/app/[variants]/(main)/{chat → agent}/cron/[cronId]/index.tsx +0 -0
  101. /package/src/app/[variants]/(main)/{chat → agent}/features/ChangelogModal.tsx +0 -0
  102. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/AgentWelcome/AddButton.tsx +0 -0
  103. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/AgentWelcome/ToolAuthAlert.tsx +0 -0
  104. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/AgentWelcome/index.tsx +0 -0
  105. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/ChatHydration/index.tsx +0 -0
  106. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/ConversationArea.tsx +0 -0
  107. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/HeaderActions/index.tsx +0 -0
  108. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/HeaderActions/useMenu.tsx +0 -0
  109. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/NotebookButton/index.tsx +0 -0
  110. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/ShareButton/index.tsx +0 -0
  111. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/Tags/KnowledgeTag.tsx +0 -0
  112. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/Tags/MemberCountTag.tsx +0 -0
  113. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/Tags/SearchTags.tsx +0 -0
  114. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/WorkingDirectory/WorkingDirectoryContent.tsx +0 -0
  115. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/WorkingDirectory/index.tsx +0 -0
  116. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/Header/index.tsx +0 -0
  117. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/MainChatInput/MessageFromUrl.tsx +0 -0
  118. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/MainChatInput/index.tsx +0 -0
  119. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/MainChatInput/useSendMenuItems.tsx +0 -0
  120. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/ThreadHydration.tsx +0 -0
  121. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/index.tsx +0 -0
  122. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/useActionsBarConfig.ts +0 -0
  123. /package/src/app/[variants]/(main)/{chat → agent}/features/Conversation/useAgentContext.ts +0 -0
  124. /package/src/app/[variants]/(main)/{chat → agent}/features/PageTitle/index.tsx +0 -0
  125. /package/src/app/[variants]/(main)/{chat → agent}/features/Portal/_layout/Mobile.tsx +0 -0
  126. /package/src/app/[variants]/(main)/{chat → agent}/features/Portal/features/Body.tsx +0 -0
  127. /package/src/app/[variants]/(main)/{chat → agent}/features/Portal/features/Portal.tsx +0 -0
  128. /package/src/app/[variants]/(main)/{chat → agent}/features/TelemetryNotification.tsx +0 -0
  129. /package/src/app/[variants]/(main)/{chat → agent}/index.tsx +0 -0
  130. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/AgentCronJobs/CronJobCards.tsx +0 -0
  131. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/AgentCronJobs/CronJobForm.tsx +0 -0
  132. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/AgentCronJobs/CronJobList.tsx +0 -0
  133. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/AgentCronJobs/hooks/useAgentCronJobs.ts +0 -0
  134. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/AgentCronJobs/index.tsx +0 -0
  135. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/EditorCanvas/TypoBar.tsx +0 -0
  136. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/EditorCanvas/index.tsx +0 -0
  137. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/EditorCanvas/useSlashItems.tsx +0 -0
  138. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/ForkConfirmModal.tsx +0 -0
  139. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/PublishButton.tsx +0 -0
  140. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/PublishResultModal.tsx +0 -0
  141. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/index.tsx +0 -0
  142. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/types.ts +0 -0
  143. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/useMarketPublish.ts +0 -0
  144. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AgentPublishButton/utils.ts +0 -0
  145. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/AutoSaveHint.tsx +0 -0
  146. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/Header/index.tsx +0 -0
  147. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/AgentHeader.tsx +0 -0
  148. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/AgentTool.tsx +0 -0
  149. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/MentionList/MentionDropdown.tsx +0 -0
  150. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/MentionList/index.tsx +0 -0
  151. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/MentionList/types.ts +0 -0
  152. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/MentionList/useMentionItems.tsx +0 -0
  153. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/PluginTag.tsx +0 -0
  154. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileEditor/index.tsx +0 -0
  155. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileHydration.tsx +0 -0
  156. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/ProfileProvider.tsx +0 -0
  157. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/StoreUpdater.tsx +0 -0
  158. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/constants.ts +0 -0
  159. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/store/action.ts +0 -0
  160. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/store/index.ts +0 -0
  161. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/store/initialState.ts +0 -0
  162. /package/src/app/[variants]/(main)/{chat → agent}/profile/features/store/selectors.ts +0 -0
  163. /package/src/app/[variants]/(main)/{chat → agent}/profile/index.tsx +0 -0
@@ -23,6 +23,11 @@ export interface VercelAIGatewayModelCard {
23
23
  type?: string;
24
24
  }
25
25
 
26
+ export interface VercelAIGatewayReasoning {
27
+ enabled?: boolean;
28
+ max_tokens?: number;
29
+ }
30
+
26
31
  export const formatPrice = (price?: string | number) => {
27
32
  if (price === undefined || price === null) return undefined;
28
33
  const n = typeof price === 'number' ? price : Number(price);
@@ -35,23 +40,41 @@ export const params = {
35
40
  baseURL: 'https://ai-gateway.vercel.sh/v1',
36
41
  chatCompletion: {
37
42
  handlePayload: (payload) => {
38
- const { model, reasoning_effort, verbosity, ...rest } = payload;
43
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
44
+ const { reasoning_effort, thinking, reasoning: _reasoning, verbosity, ...rest } = payload;
39
45
 
40
- const providerOptions: any = {};
41
- if (reasoning_effort || verbosity) {
42
- providerOptions.openai = {};
43
- if (reasoning_effort) {
44
- providerOptions.openai.reasoningEffort = reasoning_effort;
45
- providerOptions.openai.reasoningSummary = 'auto';
46
- }
47
- if (verbosity) {
48
- providerOptions.openai.textVerbosity = verbosity;
46
+ let reasoning: VercelAIGatewayReasoning | undefined;
47
+
48
+ if (thinking?.type || thinking?.budget_tokens !== undefined || reasoning_effort) {
49
+ if (thinking?.type === 'disabled') {
50
+ reasoning = { enabled: false };
51
+ } else if (thinking?.budget_tokens !== undefined) {
52
+ reasoning = {
53
+ enabled: true,
54
+ max_tokens: thinking?.budget_tokens,
55
+ };
56
+ } else if (reasoning_effort) {
57
+ reasoning = { enabled: true };
49
58
  }
50
59
  }
51
60
 
61
+ const providerOptions: any = {};
62
+ if ((verbosity || reasoning) && payload.model.includes('openai')) {
63
+ providerOptions.openai = {
64
+ ...(reasoning_effort && {
65
+ reasoningEffort: reasoning_effort,
66
+ reasoningSummary: 'auto',
67
+ }),
68
+ ...(verbosity && {
69
+ textVerbosity: verbosity,
70
+ }),
71
+ };
72
+ }
73
+
52
74
  return {
53
75
  ...rest,
54
- model,
76
+ model: payload.model,
77
+ ...(reasoning && { reasoning }),
55
78
  providerOptions,
56
79
  } as any;
57
80
  },
@@ -99,6 +122,26 @@ export const params = {
99
122
  reasoning: tags.includes('reasoning') || false,
100
123
  type: m.type === 'embedding' ? 'embedding' : 'chat',
101
124
  vision: tags.includes('vision') || false,
125
+ // Merge all applicable extendParams for settings
126
+ ...(() => {
127
+ const extendParams: string[] = [];
128
+ if (tags.includes('reasoning') && m.id.includes('gpt-5')) {
129
+ extendParams.push('gpt5ReasoningEffort', 'textVerbosity');
130
+ }
131
+ if (tags.includes('reasoning') && m.id.includes('openai') && !m.id.includes('gpt-5')) {
132
+ extendParams.push('reasoningEffort', 'textVerbosity');
133
+ }
134
+ if (tags.includes('reasoning') && m.id.includes('claude')) {
135
+ extendParams.push('enableReasoning', 'reasoningBudgetToken');
136
+ }
137
+ if (m.id.includes('claude') && writeCacheInputPrice && writeCacheInputPrice !== 0) {
138
+ extendParams.push('disableContextCaching');
139
+ }
140
+ if (tags.includes('reasoning') && m.id.includes('gemini-2.5')) {
141
+ extendParams.push('reasoningBudgetToken');
142
+ }
143
+ return extendParams.length > 0 ? { settings: { extendParams } } : {};
144
+ })(),
102
145
  } as any;
103
146
  });
104
147
 
@@ -94,6 +94,16 @@ const mockDefaultModelList: (Partial<ChatModelCard> & { id: string })[] = [
94
94
  enabled: false,
95
95
  id: 'model-known-disabled',
96
96
  },
97
+ {
98
+ displayName: 'Known Model With Settings',
99
+ enabled: true,
100
+ id: 'model-known-settings',
101
+ settings: {
102
+ extendParams: ['enableReasoning'],
103
+ searchImpl: 'params',
104
+ searchProvider: 'builtin',
105
+ },
106
+ },
97
107
  ];
98
108
 
99
109
  // Mock the import
@@ -242,7 +252,11 @@ describe('modelParse', () => {
242
252
  describe('search and imageOutput (processModelList)', () => {
243
253
  it('openai: default search keywords should make "*-search" models support search', async () => {
244
254
  // openai config does not define searchKeywords, so DEFAULT_SEARCH_KEYWORDS ['-search'] applies
245
- const out = await processModelList([{ id: 'gpt-4o-search' }], MODEL_LIST_CONFIGS.openai, 'openai');
255
+ const out = await processModelList(
256
+ [{ id: 'gpt-4o-search' }],
257
+ MODEL_LIST_CONFIGS.openai,
258
+ 'openai',
259
+ );
246
260
  expect(out).toHaveLength(1);
247
261
  expect(out[0].search).toBe(true);
248
262
  });
@@ -276,7 +290,11 @@ describe('modelParse', () => {
276
290
  });
277
291
 
278
292
  it('google: gemini-* without "-image-" should not infer imageOutput and get search=true via known google model', async () => {
279
- const out = await processModelList([{ id: 'gemini-2.5-pro' }], MODEL_LIST_CONFIGS.google, 'google');
293
+ const out = await processModelList(
294
+ [{ id: 'gemini-2.5-pro' }],
295
+ MODEL_LIST_CONFIGS.google,
296
+ 'google',
297
+ );
280
298
  expect(out).toHaveLength(1);
281
299
  expect(out[0].displayName).toBe('Gemini 2.5 Pro');
282
300
  expect(out[0].search).toBe(true);
@@ -354,6 +372,36 @@ describe('modelParse', () => {
354
372
  expect(result.find((m) => m.id === 'model-known-disabled')!.enabled).toBe(false);
355
373
  expect(result.find((m) => m.id === 'unknown-model-for-enabled-test')!.enabled).toBe(false);
356
374
  });
375
+
376
+ it('should include settings from known model when remote data does not provide them', async () => {
377
+ const modelList = [{ id: 'model-known-settings' }];
378
+ const result = await processModelList(modelList, config);
379
+
380
+ const settings = result[0].settings;
381
+ expect(settings).toBeDefined();
382
+ expect(settings?.extendParams).toEqual(['enableReasoning']);
383
+ expect(settings?.searchImpl).toBe('params');
384
+ expect(settings?.searchProvider).toBe('builtin');
385
+ });
386
+
387
+ it('should merge extendParams from known and remote models while preserving uniqueness', async () => {
388
+ const modelList = [
389
+ {
390
+ id: 'model-known-settings',
391
+ settings: {
392
+ extendParams: ['reasoningBudgetToken', 'enableReasoning'],
393
+ searchImpl: 'tool',
394
+ },
395
+ },
396
+ ];
397
+
398
+ const result = await processModelList(modelList, config);
399
+ const settings = result[0].settings;
400
+
401
+ expect(settings?.extendParams).toEqual(['enableReasoning', 'reasoningBudgetToken']);
402
+ expect(settings?.searchImpl).toBe('tool');
403
+ expect(settings?.searchProvider).toBe('builtin');
404
+ });
357
405
  });
358
406
  });
359
407
 
@@ -497,7 +545,7 @@ describe('modelParse', () => {
497
545
  });
498
546
 
499
547
  it('default search keywords should make "*-search" models support search', async () => {
500
- const out = await processMultiProviderModelList([{ id: 'gpt-4o-search'}]);
548
+ const out = await processMultiProviderModelList([{ id: 'gpt-4o-search' }]);
501
549
  expect(out).toHaveLength(1);
502
550
  expect(out[0].search).toBe(true);
503
551
  });
@@ -674,6 +722,140 @@ describe('modelParse', () => {
674
722
  expect(glm.vision).toBe(true);
675
723
  });
676
724
 
725
+ it('should include known extendParams for OpenAI and Google providers regardless of providerid', async () => {
726
+ const mockModule = await import('model-bank');
727
+ mockModule.LOBE_DEFAULT_MODEL_LIST.push(
728
+ {
729
+ id: 'gpt-openai-extend-restricted',
730
+ displayName: 'OpenAI Extend Restricted',
731
+ settings: {
732
+ extendParams: ['openaiParam'],
733
+ },
734
+ } as any,
735
+ {
736
+ id: 'gemini-extend-restricted',
737
+ displayName: 'Gemini Extend Restricted',
738
+ settings: {
739
+ extendParams: ['thinkingBudget', 'urlContext'],
740
+ },
741
+ } as any,
742
+ );
743
+
744
+ const modelList = [
745
+ { id: 'gpt-openai-extend-restricted' },
746
+ { id: 'gemini-extend-restricted' },
747
+ ];
748
+
749
+ const result = await processMultiProviderModelList(modelList, 'vercelaigateway');
750
+
751
+ const openaiModel = result.find((m) => m.id === 'gpt-openai-extend-restricted');
752
+ const googleModel = result.find((m) => m.id === 'gemini-extend-restricted');
753
+
754
+ // Both OpenAI and Google providers always include known extendParams
755
+ expect(openaiModel?.settings?.extendParams).toEqual(['openaiParam']);
756
+ expect(googleModel?.settings?.extendParams).toEqual(['thinkingBudget', 'urlContext']);
757
+ });
758
+
759
+ it('should allow known extendParams for non-OpenAI providers when provider is aihubmix', async () => {
760
+ const mockModule = await import('model-bank');
761
+ mockModule.LOBE_DEFAULT_MODEL_LIST.push({
762
+ id: 'gemini-extend-aihubmix',
763
+ displayName: 'Gemini Extend Aihubmix',
764
+ settings: {
765
+ extendParams: ['thinkingBudget', 'urlContext'],
766
+ },
767
+ } as any);
768
+
769
+ const modelList = [{ id: 'gemini-extend-aihubmix' }];
770
+
771
+ const result = await processMultiProviderModelList(modelList, 'aihubmix');
772
+
773
+ const googleModel = result.find((m) => m.id === 'gemini-extend-aihubmix');
774
+
775
+ expect(googleModel?.settings?.extendParams).toEqual(['thinkingBudget', 'urlContext']);
776
+ });
777
+
778
+ it('should omit search settings when provider is neither aihubmix nor newapi', async () => {
779
+ const mockModule = await import('model-bank');
780
+ const initialLength = mockModule.LOBE_DEFAULT_MODEL_LIST.length;
781
+ mockModule.LOBE_DEFAULT_MODEL_LIST.push({
782
+ id: 'search-settings-model',
783
+ displayName: 'Search Settings Model',
784
+ settings: {
785
+ searchImpl: 'params',
786
+ searchProvider: 'builtin',
787
+ },
788
+ } as any);
789
+
790
+ try {
791
+ const result = await processMultiProviderModelList(
792
+ [{ id: 'search-settings-model' }],
793
+ 'vercelaigateway',
794
+ );
795
+
796
+ const model = result.find((m) => m.id === 'search-settings-model');
797
+
798
+ expect(model?.settings?.searchImpl).toBeUndefined();
799
+ expect(model?.settings?.searchProvider).toBeUndefined();
800
+ } finally {
801
+ mockModule.LOBE_DEFAULT_MODEL_LIST.splice(initialLength);
802
+ }
803
+ });
804
+
805
+ it('should include search settings when provider is aihubmix', async () => {
806
+ const mockModule = await import('model-bank');
807
+ const initialLength = mockModule.LOBE_DEFAULT_MODEL_LIST.length;
808
+ mockModule.LOBE_DEFAULT_MODEL_LIST.push({
809
+ id: 'search-settings-aihubmix',
810
+ displayName: 'Search Settings Aihubmix',
811
+ settings: {
812
+ searchImpl: 'params',
813
+ searchProvider: 'builtin',
814
+ },
815
+ } as any);
816
+
817
+ try {
818
+ const result = await processMultiProviderModelList(
819
+ [{ id: 'search-settings-aihubmix' }],
820
+ 'aihubmix',
821
+ );
822
+
823
+ const model = result.find((m) => m.id === 'search-settings-aihubmix');
824
+
825
+ expect(model?.settings?.searchImpl).toBe('params');
826
+ expect(model?.settings?.searchProvider).toBe('builtin');
827
+ } finally {
828
+ mockModule.LOBE_DEFAULT_MODEL_LIST.splice(initialLength);
829
+ }
830
+ });
831
+
832
+ it('should include search settings when provider is newapi', async () => {
833
+ const mockModule = await import('model-bank');
834
+ const initialLength = mockModule.LOBE_DEFAULT_MODEL_LIST.length;
835
+ mockModule.LOBE_DEFAULT_MODEL_LIST.push({
836
+ id: 'search-settings-newapi',
837
+ displayName: 'Search Settings NewAPI',
838
+ settings: {
839
+ searchImpl: 'params',
840
+ searchProvider: 'builtin',
841
+ },
842
+ } as any);
843
+
844
+ try {
845
+ const result = await processMultiProviderModelList(
846
+ [{ id: 'search-settings-newapi' }],
847
+ 'newapi',
848
+ );
849
+
850
+ const model = result.find((m) => m.id === 'search-settings-newapi');
851
+
852
+ expect(model?.settings?.searchImpl).toBe('params');
853
+ expect(model?.settings?.searchProvider).toBe('builtin');
854
+ } finally {
855
+ mockModule.LOBE_DEFAULT_MODEL_LIST.splice(initialLength);
856
+ }
857
+ });
858
+
677
859
  it('should correctly handle models with excluded keywords in different providers', async () => {
678
860
  // OpenAI excludes 'audio', other providers don't have excluded keywords
679
861
  const modelList = [
@@ -1,5 +1,6 @@
1
1
  import type { ChatModelCard } from '@lobechat/types';
2
2
  import { AIBaseModelCard } from 'model-bank';
3
+ import type { AiModelSettings, ExtendParamsType } from 'model-bank';
3
4
 
4
5
  import type { ModelProviderKey } from '../types';
5
6
 
@@ -344,6 +345,75 @@ const processDisplayName = (displayName: string): string => {
344
345
  return displayName;
345
346
  };
346
347
 
348
+ const mergeExtendParams = (
349
+ modelExtendParams?: ReadonlyArray<ExtendParamsType>,
350
+ knownExtendParams?: ReadonlyArray<ExtendParamsType>,
351
+ options?: { includeKnownExtendParams?: boolean },
352
+ ): ExtendParamsType[] | undefined => {
353
+ const includeKnown = options?.includeKnownExtendParams ?? true;
354
+
355
+ const combined = [
356
+ ...(includeKnown ? (knownExtendParams ?? []) : []),
357
+ ...(modelExtendParams ?? []),
358
+ ];
359
+
360
+ if (combined.length === 0) return undefined;
361
+
362
+ return Array.from(new Set(combined));
363
+ };
364
+
365
+ const mergeSettings = (
366
+ modelSettings?: AiModelSettings,
367
+ knownSettings?: AiModelSettings,
368
+ options?: { includeKnownExtendParams?: boolean; includeSearchSettings?: boolean },
369
+ ): AiModelSettings | undefined => {
370
+ if (!modelSettings && !knownSettings) return undefined;
371
+
372
+ const merged: AiModelSettings = {};
373
+
374
+ if (knownSettings) {
375
+ Object.assign(merged, knownSettings);
376
+ }
377
+
378
+ if (modelSettings) {
379
+ Object.assign(merged, modelSettings);
380
+ }
381
+
382
+ const extendParams = mergeExtendParams(
383
+ modelSettings?.extendParams,
384
+ knownSettings?.extendParams,
385
+ options,
386
+ );
387
+ if (extendParams) {
388
+ merged.extendParams = extendParams;
389
+ } else {
390
+ delete merged.extendParams;
391
+ }
392
+
393
+ const includeSearchSettings = options?.includeSearchSettings ?? true;
394
+
395
+ if (includeSearchSettings) {
396
+ const searchImpl = modelSettings?.searchImpl ?? knownSettings?.searchImpl;
397
+ if (searchImpl) {
398
+ merged.searchImpl = searchImpl;
399
+ } else {
400
+ delete merged.searchImpl;
401
+ }
402
+
403
+ const searchProvider = modelSettings?.searchProvider ?? knownSettings?.searchProvider;
404
+ if (searchProvider) {
405
+ merged.searchProvider = searchProvider;
406
+ } else {
407
+ delete merged.searchProvider;
408
+ }
409
+ } else {
410
+ delete merged.searchImpl;
411
+ delete merged.searchProvider;
412
+ }
413
+
414
+ return Object.keys(merged).length > 0 ? merged : undefined;
415
+ };
416
+
347
417
  /**
348
418
  * Get the local configuration of the model provider
349
419
  * @param provider Model provider
@@ -389,6 +459,7 @@ const processModelCard = (
389
459
  model: { [key: string]: any; id: string },
390
460
  config: ModelProcessorConfig,
391
461
  knownModel?: any,
462
+ options?: { includeKnownExtendParams?: boolean; includeSearchSettings?: boolean },
392
463
  ): ChatModelCard | undefined => {
393
464
  const {
394
465
  functionCallKeywords = [],
@@ -421,6 +492,8 @@ const processModelCard = (
421
492
  return undefined;
422
493
  }
423
494
 
495
+ const mergedSettings = mergeSettings(model.settings, knownModel?.settings, options);
496
+
424
497
  const formatPricing = (pricing?: {
425
498
  cachedInput?: number;
426
499
  input?: number;
@@ -510,6 +583,7 @@ const processModelCard = (
510
583
  ...(modelType === 'image' && {
511
584
  parameters: model.parameters ?? knownModel?.parameters,
512
585
  }),
586
+ ...(mergedSettings ? { settings: mergedSettings } : {}),
513
587
  video:
514
588
  model.video ??
515
589
  knownModel?.abilities?.video ??
@@ -606,13 +680,46 @@ export const processMultiProviderModelList = async (
606
680
  );
607
681
  }
608
682
 
683
+ const includeKnownExtendParams =
684
+ providerid === 'aihubmix' ||
685
+ providerid === 'newapi' ||
686
+ detectedProvider === 'openai' ||
687
+ detectedProvider === 'google';
688
+ const includeSearchSettings = providerid === 'aihubmix' || providerid === 'newapi';
689
+
609
690
  // If providerid is provided and has local configuration, try to get the model's enabled status from it
610
691
  const providerLocalModelConfig = getModelLocalEnableConfig(
611
692
  providerLocalConfig as any[],
612
693
  model,
613
694
  );
614
695
 
615
- const processedModel = processModelCard(model, config, knownModel);
696
+ const processedModel = processModelCard(model, config, knownModel, {
697
+ includeKnownExtendParams,
698
+ includeSearchSettings,
699
+ });
700
+
701
+ if (processedModel && includeSearchSettings && providerLocalModelConfig?.settings) {
702
+ const localSettings = providerLocalModelConfig.settings as AiModelSettings | undefined;
703
+ const searchImpl = localSettings?.searchImpl;
704
+ const searchProvider = localSettings?.searchProvider;
705
+
706
+ if (searchImpl || searchProvider) {
707
+ const updatedSettings: AiModelSettings = processedModel.settings
708
+ ? { ...processedModel.settings }
709
+ : ({} as AiModelSettings);
710
+
711
+ if (searchImpl) {
712
+ updatedSettings.searchImpl = searchImpl;
713
+ }
714
+
715
+ if (searchProvider) {
716
+ updatedSettings.searchProvider = searchProvider;
717
+ }
718
+
719
+ processedModel.settings =
720
+ Object.keys(updatedSettings).length > 0 ? updatedSettings : undefined;
721
+ }
722
+ }
616
723
 
617
724
  // If model is found in local configuration, use its enabled status
618
725
  if (
@@ -11,7 +11,7 @@ export interface WorkingModel {
11
11
 
12
12
  export interface LobeAgentChatConfig {
13
13
  /**
14
- * Local System 配置(桌面端专用)
14
+ * Local System configuration (desktop only)
15
15
  */
16
16
  localSystem?: LocalSystemConfig;
17
17
  enableAutoCreateTopic?: boolean;
@@ -12,8 +12,8 @@ export interface LobeAgentConfig {
12
12
  chatConfig: LobeAgentChatConfig;
13
13
 
14
14
  /**
15
- * 编辑器内容(JSON 格式)
16
- * 用于保存富文本编辑器的完整状态,包括 mention 等特殊节点
15
+ * Editor content (JSON format)
16
+ * Used to save the complete state of the rich text editor, including special nodes like mention
17
17
  */
18
18
  editorData?: any;
19
19
  enableAgentMode?: boolean;
@@ -26,41 +26,41 @@ export interface LobeAgentConfig {
26
26
  */
27
27
  knowledgeBases?: KnowledgeBaseItem[];
28
28
  /**
29
- * 角色所使用的语言模型
29
+ * Language model used by the agent
30
30
  * @default gpt-4o-mini
31
31
  */
32
32
  model: string;
33
33
 
34
34
  /**
35
- * 开场白
35
+ * Opening message
36
36
  */
37
37
  openingMessage?: string;
38
38
  /**
39
- * 开场问题
39
+ * Opening questions
40
40
  */
41
41
  openingQuestions?: string[];
42
42
 
43
43
  /**
44
- * 语言模型参数
44
+ * Language model parameters
45
45
  */
46
46
  params: LLMParams;
47
47
  /**
48
- * 启用的插件
48
+ * Enabled plugins
49
49
  */
50
50
  plugins?: string[];
51
51
 
52
52
  /**
53
- * 模型供应商
53
+ * Model provider
54
54
  */
55
55
  provider?: string;
56
56
 
57
57
  /**
58
- * 系统角色
58
+ * System role
59
59
  */
60
60
  systemRole: string;
61
61
 
62
62
  /**
63
- * 语音服务
63
+ * Text-to-speech service
64
64
  */
65
65
  tts: LobeAgentTTSConfig;
66
66
 
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable sort-keys-fix/sort-keys-fix */
2
2
  // ******* Runtime Biz Error ******* //
3
3
  export const AgentRuntimeErrorType = {
4
- AgentRuntimeError: 'AgentRuntimeError', // Agent Runtime 模块运行时错误
4
+ AgentRuntimeError: 'AgentRuntimeError', // Agent Runtime module runtime error
5
5
  LocationNotSupportError: 'LocationNotSupportError',
6
6
 
7
7
  QuotaLimitReached: 'QuotaLimitReached',
@@ -1,4 +1,4 @@
1
- // 定义加载状态类型
1
+ // Define loading state types
2
2
  export enum DatabaseLoadingState {
3
3
  Error = 'error',
4
4
  Finished = 'finished',
@@ -19,7 +19,7 @@ export const ClientDatabaseInitStages = [
19
19
  DatabaseLoadingState.Finished,
20
20
  ];
21
21
 
22
- // 定义进度回调接口
22
+ // Define progress callback interface
23
23
  export interface ClientDBLoadingProgress {
24
24
  costTime?: number;
25
25
  phase: 'wasm' | 'dependencies';
@@ -1,4 +1,4 @@
1
- import { AiModelType, ModelParamsSchema, Pricing } from 'model-bank';
1
+ import { AiModelSettings, AiModelType, ModelParamsSchema, Pricing } from 'model-bank';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  import { AiProviderSettings } from './aiProvider';
@@ -64,6 +64,8 @@ export interface ChatModelCard {
64
64
  */
65
65
  search?: boolean;
66
66
 
67
+ settings?: AiModelSettings;
68
+
67
69
  type?: AiModelType;
68
70
 
69
71
  /**
@@ -43,8 +43,8 @@ export interface ChatTopicMetadata {
43
43
  userMemoryExtractRunState?: TopicUserMemoryExtractRunState;
44
44
  userMemoryExtractStatus?: 'pending' | 'completed' | 'failed';
45
45
  /**
46
- * Local System 工作目录(桌面端专用)
47
- * 优先级高于 Agent 级别的设置
46
+ * Local System working directory (desktop only)
47
+ * Priority is higher than Agent-level settings
48
48
  */
49
49
  workingDirectory?: string;
50
50
  }
@@ -6,7 +6,7 @@ import CronTopicList from './Topic/CronTopicList';
6
6
 
7
7
  export enum ChatSidebarKey {
8
8
  CronTopics = 'cronTopics',
9
- Topic = 'topic'
9
+ Topic = 'topic',
10
10
  }
11
11
 
12
12
  const Body = memo(() => {
@@ -2,7 +2,7 @@ import { Flexbox } from '@lobehub/ui';
2
2
  import { type FC } from 'react';
3
3
  import { Outlet } from 'react-router-dom';
4
4
 
5
- import AgentIdSync from '@/app/[variants]/(main)/chat/_layout/AgentIdSync';
5
+ import AgentIdSync from '@/app/[variants]/(main)/agent/_layout/AgentIdSync';
6
6
  import { isDesktop } from '@/const/version';
7
7
  import ProtocolUrlHandler from '@/features/ProtocolUrlHandler';
8
8
  import { useInitAgentConfig } from '@/hooks/useInitAgentConfig';
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { Block, Flexbox } from '@lobehub/ui';
4
- import { createStaticStyles , responsive } from 'antd-style';
4
+ import { createStaticStyles, responsive } from 'antd-style';
5
5
  import { memo } from 'react';
6
6
  import { useTranslation } from 'react-i18next';
7
7
 
@@ -8,13 +8,12 @@ import PluginTag from '@/features/PluginTag';
8
8
  import { useAgentEnableSearch } from '@/hooks/useAgentEnableSearch';
9
9
  import { useModelSupportToolUse } from '@/hooks/useModelSupportToolUse';
10
10
  import { useAgentStore } from '@/store/agent';
11
- import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
11
+ import { agentSelectors } from '@/store/agent/selectors';
12
12
  import { useSessionStore } from '@/store/session';
13
13
  import { sessionSelectors } from '@/store/session/selectors';
14
14
  import { useUserStore } from '@/store/user';
15
15
  import { authSelectors } from '@/store/user/selectors';
16
16
 
17
- import HistoryLimitTags from './HistoryLimitTags';
18
17
  import KnowledgeTag from './KnowledgeTag';
19
18
  import MemberCountTag from './MemberCountTag';
20
19
  import SearchTags from './SearchTags';
@@ -29,7 +28,6 @@ const TitleTags = memo(() => {
29
28
 
30
29
  const plugins = useAgentStore(agentSelectors.displayableAgentPlugins, isEqual);
31
30
  const enabledKnowledge = useAgentStore(agentSelectors.currentEnabledKnowledge, isEqual);
32
- const enableHistoryCount = useAgentStore(agentChatConfigSelectors.enableHistoryCount);
33
31
 
34
32
  const showPlugin = useModelSupportToolUse(model, provider);
35
33
  const isLogin = useUserStore(authSelectors.isLogin);
@@ -55,7 +53,6 @@ const TitleTags = memo(() => {
55
53
  {isAgentEnableSearch && <SearchTags />}
56
54
  {showPlugin && plugins?.length > 0 && <PluginTag plugins={plugins} />}
57
55
  {hasKnowledge && <KnowledgeTag data={enabledKnowledge} />}
58
- {enableHistoryCount && <HistoryLimitTags />}
59
56
  </Flexbox>
60
57
  );
61
58
  });
@@ -3,9 +3,7 @@ import { PortalContent } from '@/features/Portal/router';
3
3
  import Body from '../features/Body';
4
4
 
5
5
  const Layout = () => {
6
- return (
7
- <PortalContent renderBody={(body) => <Body>{body}</Body>} />
8
- );
6
+ return <PortalContent renderBody={(body) => <Body>{body}</Body>} />;
9
7
  };
10
8
 
11
9
  export default Layout;