@lobehub/lobehub 2.0.0-next.1 → 2.0.0-next.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 (242) hide show
  1. package/.github/workflows/desktop-pr-build.yml +12 -10
  2. package/.github/workflows/docker.yml +25 -20
  3. package/.github/workflows/e2e.yml +3 -3
  4. package/.github/workflows/release-desktop-beta.yml +8 -8
  5. package/.github/workflows/release.yml +1 -1
  6. package/.github/workflows/test.yml +4 -4
  7. package/CHANGELOG.md +261 -0
  8. package/apps/desktop/src/main/utils/next-electron-rsc.ts +7 -5
  9. package/changelog/v1.json +82 -0
  10. package/docs/development/database-schema.dbml +11 -1
  11. package/docs/self-hosting/advanced/auth/next-auth/auth0.mdx +2 -2
  12. package/docs/self-hosting/advanced/auth/next-auth/auth0.zh-CN.mdx +2 -2
  13. package/docs/self-hosting/advanced/auth/next-auth/authelia.mdx +2 -2
  14. package/docs/self-hosting/advanced/auth/next-auth/authelia.zh-CN.mdx +2 -2
  15. package/docs/self-hosting/advanced/auth/next-auth/authentik.mdx +2 -2
  16. package/docs/self-hosting/advanced/auth/next-auth/authentik.zh-CN.mdx +2 -2
  17. package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +2 -2
  18. package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +2 -2
  19. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.mdx +2 -2
  20. package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.zh-CN.mdx +2 -2
  21. package/docs/self-hosting/advanced/auth/next-auth/github.mdx +2 -2
  22. package/docs/self-hosting/advanced/auth/next-auth/github.zh-CN.mdx +2 -2
  23. package/docs/self-hosting/advanced/auth/next-auth/google.mdx +32 -29
  24. package/docs/self-hosting/advanced/auth/next-auth/keycloak.mdx +2 -2
  25. package/docs/self-hosting/advanced/auth/next-auth/keycloak.zh-CN.mdx +2 -2
  26. package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +5 -3
  27. package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +5 -3
  28. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.mdx +2 -2
  29. package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.zh-CN.mdx +2 -2
  30. package/docs/self-hosting/advanced/auth/next-auth/okta.mdx +2 -2
  31. package/docs/self-hosting/advanced/auth/next-auth/okta.zh-CN.mdx +2 -2
  32. package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +2 -2
  33. package/docs/self-hosting/advanced/auth/next-auth/wechat.zh-CN.mdx +2 -2
  34. package/docs/self-hosting/advanced/auth/next-auth/zitadel.mdx +2 -2
  35. package/docs/self-hosting/advanced/auth/next-auth/zitadel.zh-CN.mdx +2 -2
  36. package/docs/self-hosting/advanced/auth.mdx +32 -21
  37. package/docs/self-hosting/advanced/auth.zh-CN.mdx +30 -19
  38. package/docs/self-hosting/advanced/online-search.mdx +30 -25
  39. package/docs/self-hosting/advanced/online-search.zh-CN.mdx +25 -23
  40. package/locales/ar/models.json +15 -6
  41. package/locales/bg-BG/models.json +15 -6
  42. package/locales/de-DE/models.json +15 -6
  43. package/locales/en-US/models.json +15 -6
  44. package/locales/es-ES/models.json +15 -6
  45. package/locales/fa-IR/models.json +15 -6
  46. package/locales/fr-FR/models.json +15 -6
  47. package/locales/it-IT/models.json +15 -6
  48. package/locales/ja-JP/models.json +15 -6
  49. package/locales/ko-KR/models.json +21 -12
  50. package/locales/nl-NL/models.json +15 -6
  51. package/locales/pl-PL/models.json +15 -6
  52. package/locales/pt-BR/models.json +15 -6
  53. package/locales/ru-RU/models.json +15 -6
  54. package/locales/tr-TR/models.json +15 -6
  55. package/locales/vi-VN/models.json +15 -6
  56. package/locales/zh-CN/models.json +15 -6
  57. package/locales/zh-TW/models.json +15 -6
  58. package/next.config.ts +2 -3
  59. package/package.json +17 -23
  60. package/packages/const/src/index.ts +0 -1
  61. package/packages/const/src/models.ts +13 -0
  62. package/packages/const/src/url.ts +1 -4
  63. package/packages/context-engine/src/index.ts +1 -6
  64. package/packages/context-engine/src/processors/GroupMessageFlatten.ts +12 -2
  65. package/packages/context-engine/src/processors/__tests__/GroupMessageFlatten.test.ts +73 -9
  66. package/packages/context-engine/src/providers/index.ts +0 -2
  67. package/packages/database/migrations/0041_improve_index.sql +10 -0
  68. package/packages/database/migrations/meta/0041_snapshot.json +7784 -0
  69. package/packages/database/migrations/meta/_journal.json +7 -0
  70. package/packages/database/package.json +1 -1
  71. package/packages/database/src/core/migrations.json +17 -0
  72. package/packages/database/src/models/__tests__/message.grouping.test.ts +812 -0
  73. package/packages/database/src/models/__tests__/message.test.ts +322 -170
  74. package/packages/database/src/models/message.ts +62 -24
  75. package/packages/database/src/models/session.ts +60 -19
  76. package/packages/database/src/schemas/agent.ts +10 -11
  77. package/packages/database/src/schemas/message.ts +5 -1
  78. package/packages/database/src/schemas/relations.ts +6 -4
  79. package/packages/database/src/schemas/session.ts +2 -0
  80. package/packages/database/src/schemas/topic.ts +6 -1
  81. package/packages/database/src/utils/__tests__/groupMessages.test.ts +145 -2
  82. package/packages/database/src/utils/groupMessages.ts +7 -5
  83. package/packages/electron-client-ipc/package.json +4 -1
  84. package/packages/file-loaders/package.json +1 -0
  85. package/packages/model-bank/src/aiModels/anthropic.ts +0 -63
  86. package/packages/model-bank/src/aiModels/azure.ts +155 -0
  87. package/packages/model-bank/src/aiModels/bedrock.ts +44 -0
  88. package/packages/model-bank/src/aiModels/higress.ts +0 -55
  89. package/packages/model-bank/src/aiModels/infiniai.ts +21 -0
  90. package/packages/model-bank/src/aiModels/ollamacloud.ts +13 -0
  91. package/packages/model-bank/src/aiModels/siliconcloud.ts +19 -0
  92. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  93. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +33 -3
  94. package/packages/model-runtime/src/core/parameterResolver.ts +3 -0
  95. package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +0 -38
  96. package/packages/model-runtime/src/providers/azureOpenai/index.ts +2 -1
  97. package/packages/model-runtime/src/providers/minimax/index.ts +5 -5
  98. package/packages/model-runtime/src/providers/search1api/index.test.ts +2 -2
  99. package/packages/types/src/message/common/base.ts +13 -0
  100. package/packages/types/src/message/common/image.ts +8 -0
  101. package/packages/types/src/message/common/metadata.ts +39 -0
  102. package/packages/types/src/message/common/tools.ts +10 -0
  103. package/packages/types/src/message/db/params.ts +47 -1
  104. package/packages/types/src/message/ui/chat.ts +4 -1
  105. package/packages/types/src/search.ts +16 -0
  106. package/packages/web-crawler/src/crawImpl/firecrawl.ts +39 -12
  107. package/scripts/migrateServerDB/index.ts +2 -1
  108. package/src/app/(backend)/oidc/consent/route.ts +0 -1
  109. package/src/app/(backend)/webapi/revalidate/route.ts +1 -1
  110. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/index.tsx +2 -2
  111. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/V1Mobile/useSend.ts +6 -4
  112. package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatInput/useSend.ts +15 -10
  113. package/src/app/[variants]/(main)/chat/@session/features/SessionListContent/List/Item/index.tsx +4 -2
  114. package/src/app/[variants]/(main)/settings/_layout/SettingsContent.tsx +0 -3
  115. package/src/app/[variants]/layout.tsx +1 -0
  116. package/src/app/sitemap.tsx +7 -1
  117. package/src/components/Thinking/index.tsx +4 -3
  118. package/src/config/modelProviders/anthropic.ts +0 -23
  119. package/src/config/modelProviders/higress.ts +0 -23
  120. package/src/config/modelProviders/minimax.ts +1 -1
  121. package/src/config/modelProviders/qiniu.ts +1 -1
  122. package/src/envs/auth.ts +0 -179
  123. package/src/features/AgentSetting/AgentPlugin/index.tsx +21 -13
  124. package/src/features/ChatInput/ActionBar/STT/browser.tsx +2 -2
  125. package/src/features/ChatInput/ActionBar/STT/openai.tsx +2 -2
  126. package/src/features/ChatInput/ActionBar/Tools/useControls.tsx +1 -3
  127. package/src/features/Conversation/Error/ErrorJsonViewer.tsx +4 -3
  128. package/src/features/Conversation/Error/OllamaBizError/index.tsx +7 -2
  129. package/src/features/Conversation/Error/index.tsx +15 -5
  130. package/src/features/Conversation/MarkdownElements/LobeArtifact/Render/index.tsx +2 -2
  131. package/src/features/Conversation/Messages/Assistant/Extra/index.tsx +2 -2
  132. package/src/features/Conversation/Messages/Assistant/MessageContent.tsx +5 -3
  133. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/BuiltinPluginTitle.tsx +2 -2
  134. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +4 -2
  135. package/src/features/Conversation/Messages/Assistant/Tool/Render/CustomRender.tsx +2 -2
  136. package/src/features/Conversation/Messages/Assistant/Tool/Render/index.tsx +2 -2
  137. package/src/features/Conversation/Messages/Assistant/Tool/index.tsx +2 -2
  138. package/src/features/Conversation/Messages/Assistant/index.tsx +4 -4
  139. package/src/features/Conversation/Messages/Default.tsx +2 -2
  140. package/src/features/Conversation/Messages/User/Extra.tsx +2 -2
  141. package/src/features/Conversation/Messages/User/index.tsx +4 -4
  142. package/src/features/Conversation/Messages/index.tsx +3 -3
  143. package/src/features/Conversation/components/AutoScroll.tsx +2 -2
  144. package/src/features/Conversation/components/Extras/Usage/UsageDetail/index.tsx +9 -6
  145. package/src/features/PluginTag/index.tsx +1 -3
  146. package/src/features/PluginsUI/Render/BuiltinType/index.test.tsx +37 -28
  147. package/src/features/Portal/Artifacts/Body/index.tsx +2 -2
  148. package/src/libs/next-auth/auth.config.ts +1 -1
  149. package/src/libs/next-auth/sso-providers/auth0.ts +0 -7
  150. package/src/libs/next-auth/sso-providers/authelia.ts +3 -5
  151. package/src/libs/next-auth/sso-providers/authentik.ts +0 -7
  152. package/src/libs/next-auth/sso-providers/cloudflare-zero-trust.ts +3 -6
  153. package/src/libs/next-auth/sso-providers/cognito.ts +1 -5
  154. package/src/libs/next-auth/sso-providers/generic-oidc.ts +3 -5
  155. package/src/libs/next-auth/sso-providers/github.ts +0 -6
  156. package/src/libs/next-auth/sso-providers/google.ts +0 -2
  157. package/src/libs/next-auth/sso-providers/index.ts +0 -2
  158. package/src/libs/next-auth/sso-providers/keycloak.ts +0 -3
  159. package/src/libs/next-auth/sso-providers/logto.ts +3 -5
  160. package/src/libs/next-auth/sso-providers/okta.ts +0 -4
  161. package/src/libs/next-auth/sso-providers/zitadel.ts +0 -7
  162. package/src/libs/oidc-provider/provider.ts +1 -1
  163. package/src/server/modules/AssistantStore/index.ts +1 -1
  164. package/src/server/modules/ModelRuntime/trace.ts +11 -4
  165. package/src/server/routers/lambda/message.ts +14 -3
  166. package/src/server/routers/lambda/session.ts +8 -5
  167. package/src/server/services/search/impls/firecrawl/index.ts +51 -11
  168. package/src/server/services/search/impls/firecrawl/type.ts +60 -9
  169. package/src/services/chat/chat.test.ts +1 -40
  170. package/src/services/chat/contextEngineering.test.ts +0 -30
  171. package/src/services/chat/contextEngineering.ts +1 -12
  172. package/src/services/chat/index.ts +2 -7
  173. package/src/services/chat/types.ts +1 -1
  174. package/src/services/message/_deprecated.ts +1 -1
  175. package/src/services/message/client.ts +8 -2
  176. package/src/services/message/server.ts +7 -2
  177. package/src/services/message/type.ts +6 -1
  178. package/src/services/user/client.test.ts +4 -1
  179. package/src/store/chat/helpers.test.ts +99 -0
  180. package/src/store/chat/helpers.ts +21 -2
  181. package/src/store/chat/selectors.ts +1 -1
  182. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +3 -3
  183. package/src/store/chat/slices/builtinTool/actions/index.ts +1 -4
  184. package/src/store/chat/slices/message/action.test.ts +5 -1
  185. package/src/store/chat/slices/message/action.ts +102 -14
  186. package/src/store/chat/slices/message/reducer.test.ts +363 -5
  187. package/src/store/chat/slices/message/reducer.ts +87 -3
  188. package/src/store/chat/slices/message/{selectors.test.ts → selectors/chat.test.ts} +266 -30
  189. package/src/store/chat/slices/message/{selectors.ts → selectors/chat.ts} +29 -79
  190. package/src/store/chat/slices/message/selectors/index.ts +2 -0
  191. package/src/store/chat/slices/message/selectors/messageState.test.ts +36 -0
  192. package/src/store/chat/slices/message/selectors/messageState.ts +80 -0
  193. package/src/store/chat/slices/plugin/action.test.ts +34 -132
  194. package/src/store/chat/slices/plugin/action.ts +1 -44
  195. package/src/store/tool/selectors/tool.test.ts +1 -1
  196. package/src/store/tool/selectors/tool.ts +6 -8
  197. package/src/store/tool/slices/builtin/action.test.ts +83 -35
  198. package/src/store/tool/slices/builtin/action.ts +0 -9
  199. package/src/store/tool/slices/builtin/selectors.test.ts +4 -30
  200. package/src/store/tool/slices/builtin/selectors.ts +15 -21
  201. package/src/tools/index.ts +0 -6
  202. package/src/tools/renders.ts +0 -3
  203. package/src/tools/web-browsing/Portal/Search/Footer.tsx +2 -2
  204. package/tsconfig.json +9 -2
  205. package/packages/const/src/guide.ts +0 -89
  206. package/packages/context-engine/src/providers/InboxGuide.ts +0 -102
  207. package/packages/context-engine/src/providers/__tests__/InboxGuideProvider.test.ts +0 -121
  208. package/src/app/[variants]/(main)/settings/llm/ProviderList/Azure/index.tsx +0 -93
  209. package/src/app/[variants]/(main)/settings/llm/ProviderList/Bedrock/index.tsx +0 -70
  210. package/src/app/[variants]/(main)/settings/llm/ProviderList/Cloudflare/index.tsx +0 -39
  211. package/src/app/[variants]/(main)/settings/llm/ProviderList/Github/index.tsx +0 -52
  212. package/src/app/[variants]/(main)/settings/llm/ProviderList/HuggingFace/index.tsx +0 -52
  213. package/src/app/[variants]/(main)/settings/llm/ProviderList/Ollama/index.tsx +0 -20
  214. package/src/app/[variants]/(main)/settings/llm/ProviderList/OpenAI/index.tsx +0 -17
  215. package/src/app/[variants]/(main)/settings/llm/ProviderList/providers.tsx +0 -132
  216. package/src/app/[variants]/(main)/settings/llm/components/Checker.tsx +0 -118
  217. package/src/app/[variants]/(main)/settings/llm/components/ProviderConfig/index.tsx +0 -303
  218. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/CustomModelOption.tsx +0 -98
  219. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/Form.tsx +0 -104
  220. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelConfigModal/index.tsx +0 -77
  221. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/ModelFetcher.tsx +0 -105
  222. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/Option.tsx +0 -68
  223. package/src/app/[variants]/(main)/settings/llm/components/ProviderModelList/index.tsx +0 -146
  224. package/src/app/[variants]/(main)/settings/llm/const.ts +0 -20
  225. package/src/app/[variants]/(main)/settings/llm/features/Footer.tsx +0 -35
  226. package/src/app/[variants]/(main)/settings/llm/index.tsx +0 -30
  227. package/src/app/[variants]/(main)/settings/llm/type.ts +0 -5
  228. package/src/envs/__tests__/auth.test.ts +0 -200
  229. package/src/libs/next-auth/sso-providers/azure-ad.ts +0 -33
  230. package/src/services/chat/__snapshots__/chat.test.ts.snap +0 -110
  231. package/src/store/chat/slices/builtinTool/actions/__tests__/dalle.test.ts +0 -121
  232. package/src/store/chat/slices/builtinTool/actions/dalle.ts +0 -124
  233. package/src/tools/dalle/Render/GalleyGrid.tsx +0 -60
  234. package/src/tools/dalle/Render/Item/EditMode.tsx +0 -66
  235. package/src/tools/dalle/Render/Item/Error.tsx +0 -49
  236. package/src/tools/dalle/Render/Item/Image.tsx +0 -44
  237. package/src/tools/dalle/Render/Item/ImageFileItem.tsx +0 -57
  238. package/src/tools/dalle/Render/Item/index.tsx +0 -88
  239. package/src/tools/dalle/Render/ToolBar.tsx +0 -56
  240. package/src/tools/dalle/Render/index.tsx +0 -52
  241. package/src/tools/dalle/index.ts +0 -92
  242. /package/src/{middleware.ts → proxy.ts} +0 -0
@@ -26,7 +26,7 @@ export class FirecrawlImpl implements SearchServiceImpl {
26
26
 
27
27
  private get baseUrl(): string {
28
28
  // Assuming the base URL is consistent with the crawl endpoint
29
- return process.env.FIRECRAWL_URL || 'https://api.firecrawl.dev/v1';
29
+ return process.env.FIRECRAWL_URL || 'https://api.firecrawl.dev/v2';
30
30
  }
31
31
 
32
32
  async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
@@ -34,13 +34,14 @@ export class FirecrawlImpl implements SearchServiceImpl {
34
34
  const endpoint = urlJoin(this.baseUrl, '/search');
35
35
 
36
36
  const defaultQueryParams: FirecrawlSearchParameters = {
37
- limit: 15,
37
+ limit: 20,
38
38
  query,
39
39
  /*
40
40
  scrapeOptions: {
41
41
  formats: ["markdown"]
42
42
  },
43
43
  */
44
+ sources: [{ type: 'web' }, { type: 'news' }],
44
45
  };
45
46
 
46
47
  let body: FirecrawlSearchParameters = {
@@ -95,25 +96,64 @@ export class FirecrawlImpl implements SearchServiceImpl {
95
96
 
96
97
  log('Parsed Firecrawl response: %o', firecrawlResponse);
97
98
 
98
- const mappedResults = (firecrawlResponse.data || []).map(
99
+ // V2 API returns data as object with web/images/news arrays
100
+ const webResults = firecrawlResponse.data.web || [];
101
+ const imageResults = firecrawlResponse.data.images || [];
102
+ const newsResults = firecrawlResponse.data.news || [];
103
+
104
+ // Map web results
105
+ const mappedWebResults = webResults.map(
106
+ (result): UniformSearchResult => ({
107
+ category: 'general',
108
+ content: result.description || result.markdown || '',
109
+ engines: ['firecrawl'],
110
+ parsedUrl: result.url ? new URL(result.url).hostname : '',
111
+ score: 1,
112
+ title: result.title || '',
113
+ url: result.url,
114
+ }),
115
+ );
116
+
117
+ // Map news results
118
+ const mappedNewsResults = newsResults.map(
99
119
  (result): UniformSearchResult => ({
100
- category: 'general', // Default category
101
- content: result.description || '', // Prioritize content, fallback to snippet
102
- engines: ['firecrawl'], // Use 'firecrawl' as the engine name
103
- parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
104
- score: 1, // Default score to 1
120
+ category: 'news',
121
+ content: result.snippet || result.markdown || '',
122
+ engines: ['firecrawl'],
123
+ parsedUrl: result.url ? new URL(result.url).hostname : '',
124
+ score: 1,
105
125
  title: result.title || '',
106
126
  url: result.url,
107
127
  }),
108
128
  );
109
129
 
110
- log('Mapped %d results to SearchResult format', mappedResults.length);
130
+ // Map image results
131
+ const mappedImageResults = imageResults.map(
132
+ (result): UniformSearchResult => ({
133
+ category: 'images',
134
+ content: result.title || '',
135
+ engines: ['firecrawl'],
136
+ parsedUrl: result.url ? new URL(result.url).hostname : '',
137
+ score: 1,
138
+ title: result.title || '',
139
+ url: result.imageUrl, // Use imageUrl for images
140
+ }),
141
+ );
142
+
143
+ // Combine all results
144
+ const allResults = [...mappedWebResults, ...mappedNewsResults, ...mappedImageResults];
145
+
146
+ log('Mapped %d results to SearchResult format', allResults.length);
147
+
148
+ if (firecrawlResponse.warning) {
149
+ log.extend('warn')('Firecrawl warning: %s', firecrawlResponse.warning);
150
+ }
111
151
 
112
152
  return {
113
153
  costTime,
114
154
  query: query,
115
- resultNumbers: mappedResults.length,
116
- results: mappedResults,
155
+ resultNumbers: allResults.length,
156
+ results: allResults,
117
157
  };
118
158
  } catch (error) {
119
159
  log.extend('error')('Error parsing Firecrawl response: %o', error);
@@ -1,35 +1,86 @@
1
+ // V2 API Types
1
2
  interface FirecrawlScrapeOptions {
2
- formats: string[];
3
+ blockAds?: boolean;
4
+ formats?: string[];
5
+ maxAge?: number;
6
+ onlyMainContent?: boolean;
7
+ removeBase64Images?: boolean;
3
8
  }
4
9
 
10
+ type FirecrawlSource =
11
+ | { location?: string; tbs?: string; type: 'web' }
12
+ | { type: 'images' }
13
+ | { type: 'news' };
14
+
15
+ type FirecrawlCategory = { type: 'github' } | { type: 'research' } | { type: 'pdf' };
16
+
5
17
  export interface FirecrawlSearchParameters {
18
+ categories?: FirecrawlCategory[];
6
19
  country?: string;
7
- lang?: string;
20
+ ignoreInvalidURLs?: boolean;
8
21
  limit?: number;
22
+ location?: string;
9
23
  query: string;
10
24
  scrapeOptions?: FirecrawlScrapeOptions;
25
+ sources?: FirecrawlSource[];
11
26
  tbs?: string;
12
27
  timeout?: number;
13
28
  }
14
29
 
15
30
  interface FirecrawlMetadata {
16
31
  description?: string;
32
+ error?: string | null;
17
33
  sourceURL?: string;
18
34
  statusCode?: number;
35
+ title?: string;
36
+ }
37
+
38
+ // Web search result
39
+ interface FirecrawlWebResult {
40
+ description: string;
41
+ html?: string | null;
42
+ links?: string[];
43
+ markdown?: string | null;
44
+ metadata?: FirecrawlMetadata;
45
+ rawHtml?: string | null;
46
+ screenshot?: string | null;
19
47
  title: string;
48
+ url: string;
20
49
  }
21
50
 
22
- interface FirecrawlData {
23
- description?: string;
24
- html?: string;
51
+ // Image search result
52
+ interface FirecrawlImageResult {
53
+ imageHeight: number;
54
+ imageUrl: string;
55
+ imageWidth: number;
56
+ position: number;
57
+ title: string;
58
+ url: string;
59
+ }
60
+
61
+ // News search result
62
+ interface FirecrawlNewsResult {
63
+ date: string;
64
+ html?: string | null;
65
+ imageUrl?: string;
25
66
  links?: string[];
26
- markdown?: string;
67
+ markdown?: string | null;
27
68
  metadata?: FirecrawlMetadata;
28
- title?: string;
69
+ position: number;
70
+ rawHtml?: string | null;
71
+ screenshot?: string | null;
72
+ snippet: string;
73
+ title: string;
29
74
  url: string;
30
75
  }
31
76
 
77
+ // V2 Response structure
32
78
  export interface FirecrawlResponse {
33
- data: FirecrawlData[];
34
- success?: boolean;
79
+ data: {
80
+ images?: FirecrawlImageResult[];
81
+ news?: FirecrawlNewsResult[];
82
+ web?: FirecrawlWebResult[];
83
+ };
84
+ success: boolean;
85
+ warning?: string | null;
35
86
  }
@@ -8,18 +8,12 @@ import { type Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vite
8
8
 
9
9
  import { DEFAULT_USER_AVATAR } from '@/const/meta';
10
10
  import { DEFAULT_AGENT_CONFIG } from '@/const/settings';
11
- import * as isCanUseFCModule from '@/helpers/isCanUseFC';
12
11
  import * as toolEngineeringModule from '@/helpers/toolEngineering';
13
- import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/selectors';
12
+ import { agentChatConfigSelectors } from '@/store/agent/selectors';
14
13
  import { aiModelSelectors } from '@/store/aiInfra';
15
14
  import { useToolStore } from '@/store/tool';
16
- import { toolSelectors } from '@/store/tool/selectors';
17
- import { modelProviderSelectors } from '@/store/user/selectors';
18
- import { DalleManifest } from '@/tools/dalle';
19
15
  import { WebBrowsingManifest } from '@/tools/web-browsing';
20
16
 
21
- import { API_ENDPOINTS } from '../_url';
22
- import * as helpers from './helper';
23
17
  import { chatService } from './index';
24
18
 
25
19
  // Mocking external dependencies
@@ -831,39 +825,6 @@ describe('ChatService', () => {
831
825
  undefined,
832
826
  );
833
827
  });
834
-
835
- it('work with dalle3', async () => {
836
- const getChatCompletionSpy = vi.spyOn(chatService, 'getChatCompletion');
837
- const messages = [
838
- {
839
- role: 'user',
840
- content: 'https://vercel.com/ 请分析 chatGPT 关键词\n\n',
841
- sessionId: 'inbox',
842
- createdAt: 1702723964330,
843
- id: 'vyQvEw6V',
844
- updatedAt: 1702723964330,
845
- extra: {},
846
- meta: {
847
- avatar: DEFAULT_USER_AVATAR,
848
- },
849
- },
850
- ] as UIChatMessage[];
851
-
852
- await chatService.createAssistantMessage({
853
- messages,
854
- model: 'gpt-3.5-turbo-1106',
855
- top_p: 1,
856
- plugins: [DalleManifest.identifier],
857
- });
858
-
859
- // Assert that getChatCompletionSpy was called with the expected arguments
860
- expect(getChatCompletionSpy).toHaveBeenCalled();
861
-
862
- const calls = getChatCompletionSpy.mock.lastCall;
863
- // Take a snapshot of the first call's first argument
864
- expect(calls![0]).toMatchSnapshot();
865
- expect(calls![1]).toBeUndefined();
866
- });
867
828
  });
868
829
 
869
830
  describe('search functionality', () => {
@@ -248,36 +248,6 @@ describe('contextEngineering', () => {
248
248
  ]);
249
249
  });
250
250
 
251
- it('should inject INBOX_GUIDE_SYSTEM_ROLE for welcome questions in inbox session', async () => {
252
- // Don't mock INBOX_GUIDE_SYSTEMROLE, use the real one
253
- const messages: UIChatMessage[] = [
254
- {
255
- role: 'user',
256
- content: 'Hello, this is my first question',
257
- createdAt: Date.now(),
258
- id: 'test-welcome',
259
- meta: {},
260
- updatedAt: Date.now(),
261
- },
262
- ];
263
-
264
- const result = await contextEngineering({
265
- messages,
266
- model: 'gpt-4',
267
- provider: 'openai',
268
- isWelcomeQuestion: true,
269
- sessionId: 'inbox',
270
- });
271
-
272
- // Should have system message with inbox guide content
273
- const systemMessage = result.find((msg) => msg.role === 'system');
274
- expect(systemMessage).toBeDefined();
275
- // Check for characteristic content of the actual INBOX_GUIDE_SYSTEMROLE
276
- expect(systemMessage!.content).toContain('LobeChat Support Assistant');
277
- expect(systemMessage!.content).toContain('LobeHub');
278
- expect(Object.keys(systemMessage!).length).toEqual(2);
279
- });
280
-
281
251
  it('should inject historySummary into system message when provided', async () => {
282
252
  const historySummary = 'Previous conversation summary: User discussed AI topics.';
283
253
 
@@ -1,9 +1,8 @@
1
- import { INBOX_GUIDE_SYSTEMROLE, INBOX_SESSION_ID, isDesktop, isServerMode } from '@lobechat/const';
1
+ import { isDesktop, isServerMode } from '@lobechat/const';
2
2
  import {
3
3
  ContextEngine,
4
4
  HistorySummaryProvider,
5
5
  HistoryTruncateProcessor,
6
- InboxGuideProvider,
7
6
  InputTemplateProcessor,
8
7
  MessageCleanupProcessor,
9
8
  MessageContentProcessor,
@@ -48,8 +47,6 @@ export const contextEngineering = async ({
48
47
  enableHistoryCount,
49
48
  historyCount,
50
49
  historySummary,
51
- sessionId,
52
- isWelcomeQuestion,
53
50
  }: ContextEngineeringContext): Promise<OpenAIChatMessage[]> => {
54
51
  const toolNameResolver = new ToolNameResolver();
55
52
 
@@ -63,14 +60,6 @@ export const contextEngineering = async ({
63
60
  // 2. System role injection (agent's system role)
64
61
  new SystemRoleInjector({ systemRole }),
65
62
 
66
- // 3. Inbox guide system role injection
67
- new InboxGuideProvider({
68
- inboxGuideSystemRole: INBOX_GUIDE_SYSTEMROLE,
69
- inboxSessionId: INBOX_SESSION_ID,
70
- isWelcomeQuestion: isWelcomeQuestion,
71
- sessionId: sessionId,
72
- }),
73
-
74
63
  // 4. Tool system role injection
75
64
  new ToolSystemRoleProvider({
76
65
  getToolSystemRoles: (tools) => toolSelectors.enabledSystemRoles(tools)(getToolStoreState()),
@@ -66,7 +66,6 @@ interface FetchAITaskResultParams extends FetchSSEOptions {
66
66
  interface CreateAssistantMessageStream extends FetchSSEOptions {
67
67
  abortController?: AbortController;
68
68
  historySummary?: string;
69
- isWelcomeQuestion?: boolean;
70
69
  params: GetChatCompletionPayload;
71
70
  trace?: TracePayload;
72
71
  }
@@ -113,9 +112,7 @@ class ChatService {
113
112
  enableHistoryCount: agentChatConfigSelectors.enableHistoryCount(agentStoreState),
114
113
  // include user messages
115
114
  historyCount: agentChatConfigSelectors.historyCount(agentStoreState) + 2,
116
- historySummary: options?.historySummary,
117
115
  inputTemplate: chatConfig.inputTemplate,
118
- isWelcomeQuestion: options?.isWelcomeQuestion,
119
116
  messages,
120
117
  model: payload.model,
121
118
  provider: payload.provider!,
@@ -217,12 +214,10 @@ class ChatService {
217
214
  onErrorHandle,
218
215
  onFinish,
219
216
  trace,
220
- isWelcomeQuestion,
221
217
  historySummary,
222
218
  }: CreateAssistantMessageStream) => {
223
219
  await this.createAssistantMessage(params, {
224
220
  historySummary,
225
- isWelcomeQuestion,
226
221
  onAbort,
227
222
  onErrorHandle,
228
223
  onFinish,
@@ -404,7 +399,7 @@ class ChatService {
404
399
  onLoadingChange?.(true);
405
400
 
406
401
  try {
407
- const oaiMessages = await contextEngineering({
402
+ const llmMessages = await contextEngineering({
408
403
  messages: params.messages as any,
409
404
  model: params.model!,
410
405
  provider: params.provider!,
@@ -421,7 +416,7 @@ class ChatService {
421
416
  // remove plugins
422
417
  delete params.plugins;
423
418
  await this.getChatCompletion(
424
- { ...params, messages: oaiMessages, tools },
419
+ { ...params, messages: llmMessages, tools },
425
420
  {
426
421
  onErrorHandle: (error) => {
427
422
  errorHandle(new Error(error.message), error);
@@ -1,9 +1,9 @@
1
1
  import { TracePayload } from '@lobechat/types';
2
+
2
3
  import { FetchSSEOptions } from '@/utils/fetch';
3
4
 
4
5
  export interface FetchOptions extends FetchSSEOptions {
5
6
  historySummary?: string;
6
- isWelcomeQuestion?: boolean;
7
7
  signal?: AbortSignal | undefined;
8
8
  trace?: TracePayload;
9
9
  }
@@ -102,7 +102,7 @@ export class ClientService implements IMessageService {
102
102
  }
103
103
 
104
104
  // @ts-ignore
105
- async updateMessage(id: string, message: Partial<DB_Message>) {
105
+ async updateMessage(id: string, message: Partial<DB_Message>): Promise<any> {
106
106
  return MessageModel.update(id, message);
107
107
  }
108
108
 
@@ -45,6 +45,7 @@ export class ClientService extends BaseClientService implements IMessageService
45
45
  topicId,
46
46
  },
47
47
  {
48
+ groupAssistantMessages: false,
48
49
  postProcessUrl: this.postProcessUrl,
49
50
  },
50
51
  );
@@ -60,6 +61,7 @@ export class ClientService extends BaseClientService implements IMessageService
60
61
  topicId,
61
62
  },
62
63
  {
64
+ groupAssistantMessages: false,
63
65
  postProcessUrl: this.postProcessUrl,
64
66
  },
65
67
  );
@@ -99,8 +101,12 @@ export class ClientService extends BaseClientService implements IMessageService
99
101
  return this.messageModel.update(id, { error });
100
102
  };
101
103
 
102
- updateMessage: IMessageService['updateMessage'] = async (id, message) => {
103
- return this.messageModel.update(id, message);
104
+ updateMessage: IMessageService['updateMessage'] = async (id, message, options) => {
105
+ return this.messageModel.update(id, message, {
106
+ postProcessUrl: this.postProcessUrl,
107
+ sessionId: options?.sessionId,
108
+ topicId: options?.topicId,
109
+ });
104
110
  };
105
111
 
106
112
  updateMessageTTS: IMessageService['updateMessageTTS'] = async (id, tts) => {
@@ -81,8 +81,13 @@ export class ServerService implements IMessageService {
81
81
  return lambdaClient.message.updateMessagePlugin.mutate({ id, value: { arguments: args } });
82
82
  };
83
83
 
84
- updateMessage: IMessageService['updateMessage'] = async (id, value) => {
85
- return lambdaClient.message.update.mutate({ id, value });
84
+ updateMessage: IMessageService['updateMessage'] = async (id, value, options) => {
85
+ return lambdaClient.message.update.mutate({
86
+ id,
87
+ sessionId: options?.sessionId,
88
+ topicId: options?.topicId,
89
+ value,
90
+ });
86
91
  };
87
92
 
88
93
  updateMessageTranslate: IMessageService['updateMessageTranslate'] = async (id, translate) => {
@@ -10,6 +10,7 @@ import {
10
10
  UIChatMessage,
11
11
  UpdateMessageParams,
12
12
  UpdateMessageRAGParams,
13
+ UpdateMessageResult,
13
14
  } from '@lobechat/types';
14
15
  import type { HeatmapsProps } from '@lobehub/charts';
15
16
 
@@ -37,7 +38,11 @@ export interface IMessageService {
37
38
  rankModels(): Promise<ModelRankItem[]>;
38
39
  getHeatmaps(): Promise<HeatmapsProps['data']>;
39
40
  updateMessageError(id: string, error: ChatMessageError): Promise<any>;
40
- updateMessage(id: string, message: Partial<UpdateMessageParams>): Promise<any>;
41
+ updateMessage(
42
+ id: string,
43
+ message: Partial<UpdateMessageParams>,
44
+ options?: { sessionId?: string | null; topicId?: string | null },
45
+ ): Promise<UpdateMessageResult>;
41
46
  updateMessageTTS(id: string, tts: Partial<ChatTTS> | false): Promise<any>;
42
47
  updateMessageTranslate(id: string, translate: Partial<ChatTranslate> | false): Promise<any>;
43
48
  updateMessagePluginState(id: string, value: Record<string, any>): Promise<any>;
@@ -26,7 +26,10 @@ beforeEach(async () => {
26
26
  await initializeDB();
27
27
  await clientDB.delete(users);
28
28
 
29
- await clientDB.insert(users).values({ id: mockUser.uuid, avatar: 'avatar.png' });
29
+ await clientDB
30
+ .insert(users)
31
+ .values({ id: mockUser.uuid, avatar: 'avatar.png' })
32
+ .onConflictDoNothing();
30
33
  await clientDB
31
34
  .insert(userSettings)
32
35
  .values({ id: mockUser.uuid, general: { themeMode: 'light' } });
@@ -64,6 +64,105 @@ describe('chatHelpers', () => {
64
64
  const message = chatHelpers.getMessageById([], '1');
65
65
  expect(message).toBeUndefined();
66
66
  });
67
+
68
+ it('finds a block within a group message', () => {
69
+ const messagesWithGroup = [
70
+ { id: '1', content: 'Hello' },
71
+ {
72
+ id: 'group1',
73
+ role: 'group',
74
+ content: '',
75
+ children: [
76
+ { id: 'block1', content: 'First block' },
77
+ { id: 'block2', content: 'Second block' },
78
+ ],
79
+ },
80
+ ] as UIChatMessage[];
81
+
82
+ const block = chatHelpers.getMessageById(messagesWithGroup, 'block1');
83
+ expect(block).toBeDefined();
84
+ expect(block?.id).toBe('block1');
85
+ expect(block?.content).toBe('First block');
86
+ });
87
+
88
+ it('returns block with parentId set to group message id', () => {
89
+ const messagesWithGroup = [
90
+ {
91
+ id: 'group1',
92
+ role: 'group',
93
+ content: '',
94
+ children: [{ id: 'block1', content: 'Block content' }],
95
+ },
96
+ ] as UIChatMessage[];
97
+
98
+ const block = chatHelpers.getMessageById(messagesWithGroup, 'block1');
99
+ expect(block).toBeDefined();
100
+ expect(block?.parentId).toBe('group1');
101
+ });
102
+
103
+ it('searches across multiple group messages', () => {
104
+ const messagesWithGroups = [
105
+ {
106
+ id: 'group1',
107
+ role: 'group',
108
+ content: '',
109
+ children: [{ id: 'block1', content: 'First group block' }],
110
+ },
111
+ {
112
+ id: 'group2',
113
+ role: 'group',
114
+ content: '',
115
+ children: [{ id: 'block2', content: 'Second group block' }],
116
+ },
117
+ ] as UIChatMessage[];
118
+
119
+ const block = chatHelpers.getMessageById(messagesWithGroups, 'block2');
120
+ expect(block).toBeDefined();
121
+ expect(block?.id).toBe('block2');
122
+ expect(block?.parentId).toBe('group2');
123
+ expect(block?.content).toBe('Second group block');
124
+ });
125
+
126
+ it('prioritizes top-level message over block with same id', () => {
127
+ const messagesWithConflict = [
128
+ { id: 'duplicate', content: 'Top-level message', role: 'user' },
129
+ {
130
+ id: 'group1',
131
+ role: 'group',
132
+ content: '',
133
+ children: [{ id: 'duplicate', content: 'Block message' }],
134
+ },
135
+ ] as UIChatMessage[];
136
+
137
+ const message = chatHelpers.getMessageById(messagesWithConflict, 'duplicate');
138
+ expect(message).toBeDefined();
139
+ expect(message?.content).toBe('Top-level message');
140
+ expect(message?.role).toBe('user');
141
+ expect(message?.parentId).toBeUndefined();
142
+ });
143
+
144
+ it('returns undefined when block is not found in any group', () => {
145
+ const messagesWithGroup = [
146
+ {
147
+ id: 'group1',
148
+ role: 'group',
149
+ content: '',
150
+ children: [{ id: 'block1', content: 'Block content' }],
151
+ },
152
+ ] as UIChatMessage[];
153
+
154
+ const block = chatHelpers.getMessageById(messagesWithGroup, 'nonexistent');
155
+ expect(block).toBeUndefined();
156
+ });
157
+
158
+ it('handles group message without children', () => {
159
+ const messagesWithEmptyGroup = [
160
+ { id: 'group1', role: 'group', content: '' },
161
+ ] as UIChatMessage[];
162
+
163
+ const block = chatHelpers.getMessageById(messagesWithEmptyGroup, 'block1');
164
+ expect(block).toBeUndefined();
165
+ });
67
166
  });
68
167
 
69
168
  describe('getSlicedMessages', () => {
@@ -5,8 +5,27 @@ import { encodeAsync } from '@/utils/tokenizer';
5
5
  export const getMessagesTokenCount = async (messages: OpenAIChatMessage[]) =>
6
6
  encodeAsync(messages.map((m) => m.content).join(''));
7
7
 
8
- export const getMessageById = (messages: UIChatMessage[], id: string) =>
9
- messages.find((m) => m.id === id);
8
+ export const getMessageById = (
9
+ messages: UIChatMessage[],
10
+ id: string,
11
+ ): UIChatMessage | undefined => {
12
+ // First try to find in top-level messages
13
+ const directMatch = messages.find((m) => m.id === id);
14
+ if (directMatch) return directMatch;
15
+
16
+ // If not found, search in group message children (blocks)
17
+ for (const message of messages) {
18
+ if (message.role === 'group' && message.children) {
19
+ const blockMatch = message.children.find((block) => block.id === id);
20
+ if (blockMatch) {
21
+ // Return the block with parentId set to group message ID
22
+ return { ...blockMatch, parentId: message.id } as UIChatMessage;
23
+ }
24
+ }
25
+ }
26
+
27
+ return undefined;
28
+ };
10
29
 
11
30
  const getSlicedMessages = (
12
31
  messages: UIChatMessage[],
@@ -1,6 +1,6 @@
1
1
  export { aiChatSelectors } from './slices/aiChat/selectors';
2
2
  export { chatToolSelectors } from './slices/builtinTool/selectors';
3
- export { chatSelectors } from './slices/message/selectors';
3
+ export * from './slices/message/selectors';
4
4
  export * from './slices/portal/selectors';
5
5
  export { threadSelectors } from './slices/thread/selectors';
6
6
  export { topicSelectors } from './slices/topic/selectors';
@@ -29,7 +29,7 @@ import { useSessionStore } from '@/store/session';
29
29
  import { WebBrowsingManifest } from '@/tools/web-browsing';
30
30
  import { Action, setNamespace } from '@/utils/storeDebug';
31
31
 
32
- import { chatSelectors, topicSelectors } from '../../../selectors';
32
+ import { chatSelectors, messageStateSelectors, topicSelectors } from '../../../selectors';
33
33
 
34
34
  const n = setNamespace('ai');
35
35
 
@@ -598,7 +598,6 @@ export const generateAIChat: StateCreator<
598
598
  topicId: get().activeTopicId,
599
599
  traceName: TraceNameMap.Conversation,
600
600
  },
601
- isWelcomeQuestion: params?.isWelcomeQuestion,
602
601
  onErrorHandle: async (error) => {
603
602
  await messageService.updateMessageError(messageId, error);
604
603
  await refreshMessages();
@@ -707,7 +706,8 @@ export const generateAIChat: StateCreator<
707
706
  if (!duration) {
708
707
  duration = Date.now() - thinkingStartAt;
709
708
 
710
- const isInChatReasoning = chatSelectors.isMessageInChatReasoning(messageId)(get());
709
+ const isInChatReasoning =
710
+ messageStateSelectors.isMessageInChatReasoning(messageId)(get());
711
711
  if (isInChatReasoning) {
712
712
  internal_toggleChatReasoning(
713
713
  false,
@@ -2,14 +2,12 @@ import { StateCreator } from 'zustand/vanilla';
2
2
 
3
3
  import { ChatStore } from '@/store/chat/store';
4
4
 
5
- import { ChatDallEAction, dalleSlice } from './dalle';
6
5
  import { ChatCodeInterpreterAction, codeInterpreterSlice } from './interpreter';
7
6
  import { LocalFileAction, localSystemSlice } from './localSystem';
8
7
  import { SearchAction, searchSlice } from './search';
9
8
 
10
9
  export interface ChatBuiltinToolAction
11
- extends ChatDallEAction,
12
- SearchAction,
10
+ extends SearchAction,
13
11
  LocalFileAction,
14
12
  ChatCodeInterpreterAction {}
15
13
 
@@ -19,7 +17,6 @@ export const chatToolSlice: StateCreator<
19
17
  [],
20
18
  ChatBuiltinToolAction
21
19
  > = (...params) => ({
22
- ...dalleSlice(...params),
23
20
  ...searchSlice(...params),
24
21
  ...localSystemSlice(...params),
25
22
  ...codeInterpreterSlice(...params),