@lobehub/chat 1.98.2 → 1.99.1

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 (458) hide show
  1. package/.cursor/rules/backend-architecture.mdc +93 -17
  2. package/.cursor/rules/cursor-ux.mdc +45 -35
  3. package/.cursor/rules/project-introduce.mdc +72 -6
  4. package/.cursor/rules/rules-attach.mdc +16 -7
  5. package/.eslintrc.js +10 -0
  6. package/CHANGELOG.md +44 -0
  7. package/apps/desktop/README.md +7 -0
  8. package/apps/desktop/electron-builder.js +5 -0
  9. package/apps/desktop/package.json +2 -1
  10. package/apps/desktop/src/main/appBrowsers.ts +1 -1
  11. package/apps/desktop/src/main/const/dir.ts +3 -0
  12. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +13 -8
  13. package/apps/desktop/src/main/core/App.ts +8 -0
  14. package/apps/desktop/src/main/core/BrowserManager.ts +5 -2
  15. package/apps/desktop/src/main/core/StaticFileServerManager.ts +221 -0
  16. package/apps/desktop/src/main/services/fileSrv.ts +231 -44
  17. package/apps/desktop/src/main/utils/next-electron-rsc.ts +36 -5
  18. package/changelog/v1.json +14 -0
  19. package/docs/development/database-schema.dbml +70 -0
  20. package/locales/ar/common.json +2 -0
  21. package/locales/ar/components.json +35 -0
  22. package/locales/ar/error.json +2 -0
  23. package/locales/ar/image.json +100 -0
  24. package/locales/ar/metadata.json +4 -0
  25. package/locales/ar/modelProvider.json +1 -0
  26. package/locales/ar/models.json +15 -0
  27. package/locales/ar/plugin.json +22 -0
  28. package/locales/ar/providers.json +3 -0
  29. package/locales/ar/setting.json +5 -0
  30. package/locales/bg-BG/common.json +2 -0
  31. package/locales/bg-BG/components.json +35 -0
  32. package/locales/bg-BG/error.json +2 -0
  33. package/locales/bg-BG/image.json +100 -0
  34. package/locales/bg-BG/metadata.json +4 -0
  35. package/locales/bg-BG/modelProvider.json +1 -0
  36. package/locales/bg-BG/models.json +15 -0
  37. package/locales/bg-BG/plugin.json +22 -0
  38. package/locales/bg-BG/providers.json +3 -0
  39. package/locales/bg-BG/setting.json +5 -0
  40. package/locales/de-DE/common.json +2 -0
  41. package/locales/de-DE/components.json +35 -0
  42. package/locales/de-DE/error.json +2 -0
  43. package/locales/de-DE/image.json +100 -0
  44. package/locales/de-DE/metadata.json +4 -0
  45. package/locales/de-DE/modelProvider.json +1 -0
  46. package/locales/de-DE/models.json +15 -0
  47. package/locales/de-DE/plugin.json +22 -0
  48. package/locales/de-DE/providers.json +3 -0
  49. package/locales/de-DE/setting.json +5 -0
  50. package/locales/en-US/common.json +2 -0
  51. package/locales/en-US/components.json +35 -0
  52. package/locales/en-US/error.json +2 -0
  53. package/locales/en-US/image.json +100 -0
  54. package/locales/en-US/metadata.json +4 -0
  55. package/locales/en-US/modelProvider.json +1 -0
  56. package/locales/en-US/models.json +15 -0
  57. package/locales/en-US/plugin.json +22 -0
  58. package/locales/en-US/providers.json +3 -0
  59. package/locales/en-US/setting.json +5 -0
  60. package/locales/es-ES/common.json +2 -0
  61. package/locales/es-ES/components.json +35 -0
  62. package/locales/es-ES/error.json +2 -0
  63. package/locales/es-ES/image.json +100 -0
  64. package/locales/es-ES/metadata.json +4 -0
  65. package/locales/es-ES/modelProvider.json +1 -0
  66. package/locales/es-ES/models.json +15 -0
  67. package/locales/es-ES/plugin.json +22 -0
  68. package/locales/es-ES/providers.json +3 -0
  69. package/locales/es-ES/setting.json +5 -0
  70. package/locales/fa-IR/common.json +2 -0
  71. package/locales/fa-IR/components.json +35 -0
  72. package/locales/fa-IR/error.json +2 -0
  73. package/locales/fa-IR/image.json +100 -0
  74. package/locales/fa-IR/metadata.json +4 -0
  75. package/locales/fa-IR/modelProvider.json +1 -0
  76. package/locales/fa-IR/models.json +15 -0
  77. package/locales/fa-IR/plugin.json +22 -0
  78. package/locales/fa-IR/providers.json +3 -0
  79. package/locales/fa-IR/setting.json +5 -0
  80. package/locales/fr-FR/common.json +2 -0
  81. package/locales/fr-FR/components.json +35 -0
  82. package/locales/fr-FR/error.json +2 -0
  83. package/locales/fr-FR/image.json +100 -0
  84. package/locales/fr-FR/metadata.json +4 -0
  85. package/locales/fr-FR/modelProvider.json +1 -0
  86. package/locales/fr-FR/models.json +15 -0
  87. package/locales/fr-FR/plugin.json +22 -0
  88. package/locales/fr-FR/providers.json +3 -0
  89. package/locales/fr-FR/setting.json +5 -0
  90. package/locales/it-IT/common.json +2 -0
  91. package/locales/it-IT/components.json +35 -0
  92. package/locales/it-IT/error.json +2 -0
  93. package/locales/it-IT/image.json +100 -0
  94. package/locales/it-IT/metadata.json +4 -0
  95. package/locales/it-IT/modelProvider.json +1 -0
  96. package/locales/it-IT/models.json +15 -0
  97. package/locales/it-IT/plugin.json +22 -0
  98. package/locales/it-IT/providers.json +3 -0
  99. package/locales/it-IT/setting.json +5 -0
  100. package/locales/ja-JP/common.json +2 -0
  101. package/locales/ja-JP/components.json +35 -0
  102. package/locales/ja-JP/error.json +2 -0
  103. package/locales/ja-JP/image.json +100 -0
  104. package/locales/ja-JP/metadata.json +4 -0
  105. package/locales/ja-JP/modelProvider.json +1 -0
  106. package/locales/ja-JP/models.json +15 -0
  107. package/locales/ja-JP/plugin.json +22 -0
  108. package/locales/ja-JP/providers.json +3 -0
  109. package/locales/ja-JP/setting.json +5 -0
  110. package/locales/ko-KR/common.json +2 -0
  111. package/locales/ko-KR/components.json +35 -0
  112. package/locales/ko-KR/error.json +2 -0
  113. package/locales/ko-KR/image.json +100 -0
  114. package/locales/ko-KR/metadata.json +4 -0
  115. package/locales/ko-KR/modelProvider.json +1 -0
  116. package/locales/ko-KR/models.json +15 -0
  117. package/locales/ko-KR/plugin.json +22 -0
  118. package/locales/ko-KR/providers.json +3 -0
  119. package/locales/ko-KR/setting.json +5 -0
  120. package/locales/nl-NL/common.json +2 -0
  121. package/locales/nl-NL/components.json +35 -0
  122. package/locales/nl-NL/error.json +2 -0
  123. package/locales/nl-NL/image.json +100 -0
  124. package/locales/nl-NL/metadata.json +4 -0
  125. package/locales/nl-NL/modelProvider.json +1 -0
  126. package/locales/nl-NL/models.json +15 -0
  127. package/locales/nl-NL/plugin.json +22 -0
  128. package/locales/nl-NL/providers.json +3 -0
  129. package/locales/nl-NL/setting.json +5 -0
  130. package/locales/pl-PL/common.json +2 -0
  131. package/locales/pl-PL/components.json +35 -0
  132. package/locales/pl-PL/error.json +2 -0
  133. package/locales/pl-PL/image.json +100 -0
  134. package/locales/pl-PL/metadata.json +4 -0
  135. package/locales/pl-PL/modelProvider.json +1 -0
  136. package/locales/pl-PL/models.json +15 -0
  137. package/locales/pl-PL/plugin.json +22 -0
  138. package/locales/pl-PL/providers.json +3 -0
  139. package/locales/pl-PL/setting.json +5 -0
  140. package/locales/pt-BR/common.json +2 -0
  141. package/locales/pt-BR/components.json +35 -0
  142. package/locales/pt-BR/error.json +2 -0
  143. package/locales/pt-BR/image.json +100 -0
  144. package/locales/pt-BR/metadata.json +4 -0
  145. package/locales/pt-BR/modelProvider.json +1 -0
  146. package/locales/pt-BR/models.json +15 -0
  147. package/locales/pt-BR/plugin.json +22 -0
  148. package/locales/pt-BR/providers.json +3 -0
  149. package/locales/pt-BR/setting.json +5 -0
  150. package/locales/ru-RU/common.json +2 -0
  151. package/locales/ru-RU/components.json +35 -0
  152. package/locales/ru-RU/error.json +2 -0
  153. package/locales/ru-RU/image.json +100 -0
  154. package/locales/ru-RU/metadata.json +4 -0
  155. package/locales/ru-RU/modelProvider.json +1 -0
  156. package/locales/ru-RU/models.json +15 -0
  157. package/locales/ru-RU/plugin.json +22 -0
  158. package/locales/ru-RU/providers.json +3 -0
  159. package/locales/ru-RU/setting.json +5 -0
  160. package/locales/tr-TR/common.json +2 -0
  161. package/locales/tr-TR/components.json +35 -0
  162. package/locales/tr-TR/error.json +2 -0
  163. package/locales/tr-TR/image.json +100 -0
  164. package/locales/tr-TR/metadata.json +4 -0
  165. package/locales/tr-TR/modelProvider.json +1 -0
  166. package/locales/tr-TR/models.json +15 -0
  167. package/locales/tr-TR/plugin.json +22 -0
  168. package/locales/tr-TR/providers.json +3 -0
  169. package/locales/tr-TR/setting.json +5 -0
  170. package/locales/vi-VN/common.json +2 -0
  171. package/locales/vi-VN/components.json +35 -0
  172. package/locales/vi-VN/error.json +2 -0
  173. package/locales/vi-VN/image.json +100 -0
  174. package/locales/vi-VN/metadata.json +4 -0
  175. package/locales/vi-VN/modelProvider.json +1 -0
  176. package/locales/vi-VN/models.json +15 -0
  177. package/locales/vi-VN/plugin.json +22 -0
  178. package/locales/vi-VN/providers.json +3 -0
  179. package/locales/vi-VN/setting.json +5 -0
  180. package/locales/zh-CN/common.json +2 -0
  181. package/locales/zh-CN/components.json +35 -0
  182. package/locales/zh-CN/error.json +2 -0
  183. package/locales/zh-CN/image.json +100 -0
  184. package/locales/zh-CN/metadata.json +4 -0
  185. package/locales/zh-CN/modelProvider.json +1 -0
  186. package/locales/zh-CN/models.json +15 -0
  187. package/locales/zh-CN/plugin.json +22 -0
  188. package/locales/zh-CN/providers.json +3 -0
  189. package/locales/zh-CN/setting.json +5 -0
  190. package/locales/zh-TW/common.json +2 -0
  191. package/locales/zh-TW/components.json +35 -0
  192. package/locales/zh-TW/error.json +2 -0
  193. package/locales/zh-TW/image.json +100 -0
  194. package/locales/zh-TW/metadata.json +4 -0
  195. package/locales/zh-TW/modelProvider.json +1 -0
  196. package/locales/zh-TW/models.json +15 -0
  197. package/locales/zh-TW/plugin.json +22 -0
  198. package/locales/zh-TW/providers.json +3 -0
  199. package/locales/zh-TW/setting.json +5 -0
  200. package/package.json +11 -4
  201. package/packages/electron-server-ipc/src/events/file.ts +3 -1
  202. package/packages/electron-server-ipc/src/types/file.ts +15 -0
  203. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +11 -1
  204. package/src/app/[variants]/(main)/image/@menu/components/AspectRatioSelect/index.tsx +73 -0
  205. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +39 -0
  206. package/src/app/[variants]/(main)/image/@menu/components/SizeSelect/index.tsx +89 -0
  207. package/src/app/[variants]/(main)/image/@menu/default.tsx +11 -0
  208. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/AspectRatioSelect.tsx +24 -0
  209. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/DimensionControlGroup.tsx +107 -0
  210. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageNum.tsx +290 -0
  211. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUpload.tsx +504 -0
  212. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrl.tsx +18 -0
  213. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrlsUpload.tsx +19 -0
  214. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect.tsx +155 -0
  215. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +415 -0
  216. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/index.tsx +732 -0
  217. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SeedNumberInput.tsx +24 -0
  218. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSelect.tsx +17 -0
  219. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSliderInput.tsx +15 -0
  220. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/StepsSliderInput.tsx +11 -0
  221. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/constants.ts +1 -0
  222. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +93 -0
  223. package/src/app/[variants]/(main)/image/@topic/default.tsx +17 -0
  224. package/src/app/[variants]/(main)/image/@topic/features/Topics/NewTopicButton.tsx +64 -0
  225. package/src/app/[variants]/(main)/image/@topic/features/Topics/SkeletonList.tsx +34 -0
  226. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItem.tsx +136 -0
  227. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItemContainer.tsx +91 -0
  228. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicList.tsx +57 -0
  229. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +37 -0
  230. package/src/app/[variants]/(main)/image/@topic/features/Topics/index.tsx +19 -0
  231. package/src/app/[variants]/(main)/image/NotSupportClient.tsx +153 -0
  232. package/src/app/[variants]/(main)/image/_layout/Desktop/Container.tsx +35 -0
  233. package/src/app/[variants]/(main)/image/_layout/Desktop/RegisterHotkeys.tsx +10 -0
  234. package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +30 -0
  235. package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +14 -0
  236. package/src/app/[variants]/(main)/image/_layout/type.ts +7 -0
  237. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +196 -0
  238. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ActionButtons.tsx +60 -0
  239. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ElapsedTime.tsx +90 -0
  240. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +65 -0
  241. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +44 -0
  242. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/SuccessState.tsx +49 -0
  243. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +154 -0
  244. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/styles.ts +51 -0
  245. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/types.ts +39 -0
  246. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.ts +11 -0
  247. package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +97 -0
  248. package/src/app/[variants]/(main)/image/features/ImageWorkspace/Content.tsx +48 -0
  249. package/src/app/[variants]/(main)/image/features/ImageWorkspace/EmptyState.tsx +37 -0
  250. package/src/app/[variants]/(main)/image/features/ImageWorkspace/SkeletonList.tsx +50 -0
  251. package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +23 -0
  252. package/src/app/[variants]/(main)/image/features/PromptInput/Title.tsx +38 -0
  253. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +114 -0
  254. package/src/app/[variants]/(main)/image/layout.tsx +19 -0
  255. package/src/app/[variants]/(main)/image/loading.tsx +3 -0
  256. package/src/app/[variants]/(main)/image/page.tsx +47 -0
  257. package/src/app/[variants]/(main)/settings/system-agent/index.tsx +2 -1
  258. package/src/chains/summaryGenerationTitle.ts +25 -0
  259. package/src/components/ImageItem/index.tsx +9 -6
  260. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/Bedrock.tsx +3 -4
  261. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/ProviderApiKeyForm.tsx +5 -4
  262. package/src/components/InvalidAPIKey/APIKeyForm/index.tsx +108 -0
  263. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/useApiKey.ts +2 -1
  264. package/src/components/InvalidAPIKey/index.tsx +30 -0
  265. package/src/components/KeyValueEditor/index.tsx +203 -0
  266. package/src/components/KeyValueEditor/utils.ts +42 -0
  267. package/src/config/aiModels/fal.ts +52 -0
  268. package/src/config/aiModels/index.ts +3 -0
  269. package/src/config/aiModels/openai.ts +20 -6
  270. package/src/config/llm.ts +6 -0
  271. package/src/config/modelProviders/fal.ts +21 -0
  272. package/src/config/modelProviders/index.ts +3 -0
  273. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +8 -0
  274. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +11 -0
  275. package/src/config/paramsSchemas/fal/flux-schnell.ts +9 -0
  276. package/src/config/paramsSchemas/fal/imagen4.ts +10 -0
  277. package/src/config/paramsSchemas/openai/gpt-image-1.ts +10 -0
  278. package/src/const/hotkeys.ts +2 -2
  279. package/src/const/image.ts +6 -0
  280. package/src/const/settings/systemAgent.ts +1 -0
  281. package/src/database/client/migrations.json +27 -0
  282. package/src/database/migrations/0026_add_autovacuum_tuning.sql +2 -0
  283. package/src/database/migrations/0027_ai_image.sql +47 -0
  284. package/src/database/migrations/meta/0027_snapshot.json +6003 -0
  285. package/src/database/migrations/meta/_journal.json +7 -0
  286. package/src/database/models/__tests__/asyncTask.test.ts +7 -5
  287. package/src/database/models/__tests__/file.test.ts +287 -0
  288. package/src/database/models/__tests__/generation.test.ts +786 -0
  289. package/src/database/models/__tests__/generationBatch.test.ts +614 -0
  290. package/src/database/models/__tests__/generationTopic.test.ts +411 -0
  291. package/src/database/models/aiModel.ts +2 -0
  292. package/src/database/models/asyncTask.ts +1 -1
  293. package/src/database/models/file.ts +28 -20
  294. package/src/database/models/generation.ts +197 -0
  295. package/src/database/models/generationBatch.ts +212 -0
  296. package/src/database/models/generationTopic.ts +131 -0
  297. package/src/database/repositories/aiInfra/index.test.ts +157 -1
  298. package/src/database/repositories/aiInfra/index.ts +37 -19
  299. package/src/database/repositories/tableViewer/index.test.ts +1 -1
  300. package/src/database/schemas/file.ts +8 -0
  301. package/src/database/schemas/generation.ts +127 -0
  302. package/src/database/schemas/index.ts +1 -0
  303. package/src/database/schemas/relations.ts +45 -1
  304. package/src/database/type.ts +2 -0
  305. package/src/database/utils/idGenerator.ts +3 -0
  306. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +39 -0
  307. package/src/features/Conversation/Error/InvalidAccessCode.tsx +2 -2
  308. package/src/features/Conversation/Error/index.tsx +3 -3
  309. package/src/features/ImageSidePanel/index.tsx +83 -0
  310. package/src/features/ImageTopicPanel/index.tsx +79 -0
  311. package/src/features/PluginDevModal/MCPManifestForm/CollapsibleSection.tsx +62 -0
  312. package/src/features/PluginDevModal/MCPManifestForm/QuickImportSection.tsx +158 -0
  313. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +99 -155
  314. package/src/features/PluginStore/McpList/Detail/Settings/index.tsx +5 -2
  315. package/src/hooks/useDownloadImage.ts +31 -0
  316. package/src/hooks/useFetchGenerationTopics.ts +13 -0
  317. package/src/hooks/useHotkeys/imageScope.ts +48 -0
  318. package/src/libs/mcp/client.ts +55 -22
  319. package/src/libs/mcp/types.ts +42 -6
  320. package/src/libs/model-runtime/BaseAI.ts +3 -1
  321. package/src/libs/model-runtime/ModelRuntime.test.ts +80 -0
  322. package/src/libs/model-runtime/ModelRuntime.ts +15 -1
  323. package/src/libs/model-runtime/UniformRuntime/index.ts +4 -1
  324. package/src/libs/model-runtime/fal/index.test.ts +442 -0
  325. package/src/libs/model-runtime/fal/index.ts +88 -0
  326. package/src/libs/model-runtime/openai/index.test.ts +396 -2
  327. package/src/libs/model-runtime/openai/index.ts +129 -3
  328. package/src/libs/model-runtime/runtimeMap.ts +2 -0
  329. package/src/libs/model-runtime/types/image.ts +25 -0
  330. package/src/libs/model-runtime/types/type.ts +1 -0
  331. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +10 -0
  332. package/src/libs/standard-parameters/index.ts +1 -0
  333. package/src/libs/standard-parameters/meta-schema.test.ts +214 -0
  334. package/src/libs/standard-parameters/meta-schema.ts +147 -0
  335. package/src/libs/swr/index.ts +1 -0
  336. package/src/libs/trpc/async/asyncAuth.ts +29 -8
  337. package/src/libs/trpc/async/context.ts +42 -4
  338. package/src/libs/trpc/async/index.ts +17 -4
  339. package/src/libs/trpc/async/init.ts +8 -0
  340. package/src/libs/trpc/client/lambda.ts +19 -2
  341. package/src/locales/default/common.ts +2 -0
  342. package/src/locales/default/components.ts +35 -0
  343. package/src/locales/default/error.ts +2 -0
  344. package/src/locales/default/image.ts +100 -0
  345. package/src/locales/default/index.ts +2 -0
  346. package/src/locales/default/metadata.ts +4 -0
  347. package/src/locales/default/modelProvider.ts +2 -0
  348. package/src/locales/default/plugin.ts +22 -0
  349. package/src/locales/default/setting.ts +5 -0
  350. package/src/middleware.ts +1 -0
  351. package/src/server/modules/ElectronIPCClient/index.ts +9 -1
  352. package/src/server/modules/S3/index.ts +15 -0
  353. package/src/server/routers/async/caller.ts +9 -1
  354. package/src/server/routers/async/image.ts +253 -0
  355. package/src/server/routers/async/index.ts +2 -0
  356. package/src/server/routers/lambda/aiProvider.test.ts +2 -0
  357. package/src/server/routers/lambda/generation.test.ts +267 -0
  358. package/src/server/routers/lambda/generation.ts +86 -0
  359. package/src/server/routers/lambda/generationBatch.test.ts +376 -0
  360. package/src/server/routers/lambda/generationBatch.ts +56 -0
  361. package/src/server/routers/lambda/generationTopic.test.ts +508 -0
  362. package/src/server/routers/lambda/generationTopic.ts +93 -0
  363. package/src/server/routers/lambda/image.ts +248 -0
  364. package/src/server/routers/lambda/index.ts +8 -0
  365. package/src/server/routers/tools/mcp.ts +15 -0
  366. package/src/server/services/file/__tests__/index.test.ts +135 -0
  367. package/src/server/services/file/impls/local.test.ts +153 -52
  368. package/src/server/services/file/impls/local.ts +70 -46
  369. package/src/server/services/file/impls/s3.test.ts +114 -0
  370. package/src/server/services/file/impls/s3.ts +40 -0
  371. package/src/server/services/file/impls/type.ts +10 -0
  372. package/src/server/services/file/index.ts +14 -0
  373. package/src/server/services/generation/index.ts +239 -0
  374. package/src/server/services/mcp/index.ts +20 -2
  375. package/src/services/__tests__/generation.test.ts +40 -0
  376. package/src/services/__tests__/generationBatch.test.ts +36 -0
  377. package/src/services/__tests__/generationTopic.test.ts +72 -0
  378. package/src/services/electron/file.ts +3 -1
  379. package/src/services/generation.ts +16 -0
  380. package/src/services/generationBatch.ts +25 -0
  381. package/src/services/generationTopic.ts +28 -0
  382. package/src/services/image.ts +33 -0
  383. package/src/services/mcp.ts +12 -7
  384. package/src/services/upload.ts +43 -9
  385. package/src/store/aiInfra/slices/aiProvider/action.ts +31 -6
  386. package/src/store/aiInfra/slices/aiProvider/initialState.ts +1 -0
  387. package/src/store/aiInfra/slices/aiProvider/selectors.ts +3 -0
  388. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
  389. package/src/store/chat/slices/message/action.ts +2 -2
  390. package/src/store/chat/slices/translate/action.ts +1 -1
  391. package/src/store/global/initialState.ts +9 -0
  392. package/src/store/global/selectors/systemStatus.ts +8 -0
  393. package/src/store/image/index.ts +2 -0
  394. package/src/store/image/initialState.ts +25 -0
  395. package/src/store/image/selectors.ts +4 -0
  396. package/src/store/image/slices/createImage/action.test.ts +330 -0
  397. package/src/store/image/slices/createImage/action.ts +134 -0
  398. package/src/store/image/slices/createImage/initialState.ts +9 -0
  399. package/src/store/image/slices/createImage/selectors.test.ts +114 -0
  400. package/src/store/image/slices/createImage/selectors.ts +9 -0
  401. package/src/store/image/slices/generationBatch/action.test.ts +495 -0
  402. package/src/store/image/slices/generationBatch/action.ts +303 -0
  403. package/src/store/image/slices/generationBatch/initialState.ts +13 -0
  404. package/src/store/image/slices/generationBatch/reducer.test.ts +568 -0
  405. package/src/store/image/slices/generationBatch/reducer.ts +101 -0
  406. package/src/store/image/slices/generationBatch/selectors.test.ts +307 -0
  407. package/src/store/image/slices/generationBatch/selectors.ts +36 -0
  408. package/src/store/image/slices/generationConfig/action.test.ts +351 -0
  409. package/src/store/image/slices/generationConfig/action.ts +295 -0
  410. package/src/store/image/slices/generationConfig/hooks.test.ts +304 -0
  411. package/src/store/image/slices/generationConfig/hooks.ts +118 -0
  412. package/src/store/image/slices/generationConfig/index.ts +1 -0
  413. package/src/store/image/slices/generationConfig/initialState.ts +37 -0
  414. package/src/store/image/slices/generationConfig/selectors.test.ts +204 -0
  415. package/src/store/image/slices/generationConfig/selectors.ts +25 -0
  416. package/src/store/image/slices/generationTopic/action.test.ts +687 -0
  417. package/src/store/image/slices/generationTopic/action.ts +319 -0
  418. package/src/store/image/slices/generationTopic/index.ts +2 -0
  419. package/src/store/image/slices/generationTopic/initialState.ts +14 -0
  420. package/src/store/image/slices/generationTopic/reducer.test.ts +198 -0
  421. package/src/store/image/slices/generationTopic/reducer.ts +66 -0
  422. package/src/store/image/slices/generationTopic/selectors.test.ts +103 -0
  423. package/src/store/image/slices/generationTopic/selectors.ts +15 -0
  424. package/src/store/image/store.ts +42 -0
  425. package/src/store/image/utils/size.ts +51 -0
  426. package/src/store/tool/slices/customPlugin/action.ts +10 -1
  427. package/src/store/tool/slices/mcpStore/action.ts +6 -4
  428. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +4 -0
  429. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  430. package/src/types/aiModel.ts +8 -3
  431. package/src/types/aiProvider.ts +2 -0
  432. package/src/types/asyncTask.ts +2 -0
  433. package/src/types/files/index.ts +5 -0
  434. package/src/types/generation/index.ts +80 -0
  435. package/src/types/hotkey.ts +2 -0
  436. package/src/types/plugins/mcp.ts +2 -6
  437. package/src/types/tool/plugin.ts +8 -0
  438. package/src/types/user/settings/keyVaults.ts +5 -0
  439. package/src/types/user/settings/systemAgent.ts +1 -0
  440. package/src/utils/client/downloadFile.ts +33 -4
  441. package/src/utils/number.test.ts +105 -0
  442. package/src/utils/number.ts +25 -0
  443. package/src/utils/server/__tests__/geo.test.ts +6 -3
  444. package/src/utils/storeDebug.test.ts +152 -0
  445. package/src/utils/storeDebug.ts +16 -7
  446. package/src/utils/time.test.ts +259 -0
  447. package/src/utils/time.ts +18 -0
  448. package/src/utils/units.ts +61 -0
  449. package/src/utils/url.test.ts +358 -9
  450. package/src/utils/url.ts +105 -3
  451. package/{vitest.server.config.ts → vitest.config.server.ts} +3 -0
  452. package/.cursor/rules/i18n/i18n-auto-attached.mdc +0 -6
  453. package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -105
  454. package/src/features/Conversation/Error/InvalidAPIKey.tsx +0 -16
  455. package/src/features/PluginDevModal/MCPManifestForm/EnvEditor.tsx +0 -227
  456. /package/.cursor/rules/{i18n/i18n.mdc → i18n.mdc} +0 -0
  457. /package/src/app/[variants]/(main)/settings/system-agent/features/{createForm.tsx → SystemAgentForm.tsx} +0 -0
  458. /package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/LoadingContext.ts +0 -0
@@ -242,6 +242,62 @@ describe('AiInfraRepos', () => {
242
242
  settings: mockSettings,
243
243
  });
244
244
  });
245
+
246
+ it('should return all models including disabled ones when filterEnabled is false', async () => {
247
+ const mockProviders = [
248
+ { enabled: true, id: 'openai', name: 'OpenAI', source: 'builtin' },
249
+ { enabled: false, id: 'anthropic', name: 'Anthropic', source: 'builtin' },
250
+ ] as AiProviderListItem[];
251
+
252
+ const mockAllModels = [
253
+ {
254
+ abilities: {},
255
+ enabled: false,
256
+ id: 'claude-3',
257
+ providerId: 'anthropic',
258
+ type: 'chat' as const,
259
+ },
260
+ ] as EnabledAiModel[];
261
+
262
+ vi.spyOn(repo, 'getAiProviderList').mockResolvedValue(mockProviders);
263
+ vi.spyOn(repo.aiModelModel, 'getAllModels').mockResolvedValue(mockAllModels);
264
+ vi.spyOn(repo as any, 'fetchBuiltinModels').mockResolvedValue([
265
+ {
266
+ enabled: true,
267
+ id: 'gpt-4',
268
+ type: 'chat' as const,
269
+ },
270
+ {
271
+ enabled: false,
272
+ id: 'claude-3',
273
+ type: 'chat' as const,
274
+ },
275
+ ]);
276
+
277
+ const result = await repo.getEnabledModels(false);
278
+
279
+ // Should include both enabled and disabled models
280
+ expect(result).toEqual(
281
+ expect.arrayContaining([
282
+ expect.objectContaining({
283
+ id: 'gpt-4',
284
+ enabled: true,
285
+ providerId: 'openai',
286
+ }),
287
+ expect.objectContaining({
288
+ id: 'claude-3',
289
+ enabled: false,
290
+ providerId: 'anthropic',
291
+ }),
292
+ ]),
293
+ );
294
+ // Verify we have at least the expected models (may have more from builtin models)
295
+ expect(result.length).toBeGreaterThanOrEqual(2);
296
+
297
+ // Verify disabled models are included
298
+ const disabledModels = result.filter((model) => !model.enabled);
299
+ expect(disabledModels.length).toBeGreaterThan(0);
300
+ });
245
301
  });
246
302
 
247
303
  describe('getAiProviderModelList', () => {
@@ -332,7 +388,9 @@ describe('AiInfraRepos', () => {
332
388
  openai: { apiKey: 'test-key' },
333
389
  } as unknown as Record<string, AiProviderRuntimeConfig>;
334
390
  const mockEnabledProviders = [{ id: 'openai', name: 'OpenAI' }] as EnabledProvider[];
335
- const mockEnabledModels = [{ id: 'gpt-4', providerId: 'openai' }] as EnabledAiModel[];
391
+ const mockEnabledModels = [
392
+ { id: 'gpt-4', providerId: 'openai', enabled: true },
393
+ ] as EnabledAiModel[];
336
394
 
337
395
  vi.spyOn(repo.aiProviderModel, 'getAiProviderRuntimeConfig').mockResolvedValue(
338
396
  mockRuntimeConfig,
@@ -385,6 +443,10 @@ describe('AiInfraRepos', () => {
385
443
  }),
386
444
  ],
387
445
  enabledAiProviders: [{ id: 'openai', logo: 'logo1', name: 'OpenAI', source: 'builtin' }],
446
+ enabledChatAiProviders: [
447
+ { id: 'openai', logo: 'logo1', name: 'OpenAI', source: 'builtin' },
448
+ ],
449
+ enabledImageAiProviders: [],
388
450
  runtimeConfig: {
389
451
  openai: {
390
452
  apiKey: 'test-key',
@@ -393,6 +455,100 @@ describe('AiInfraRepos', () => {
393
455
  },
394
456
  });
395
457
  });
458
+
459
+ it('should return provider runtime state with enabledImageAiProviders', async () => {
460
+ const mockRuntimeConfig = {
461
+ fal: {
462
+ apiKey: 'test-fal-key',
463
+ },
464
+ openai: {
465
+ apiKey: 'test-openai-key',
466
+ },
467
+ } as unknown as Record<string, AiProviderRuntimeConfig>;
468
+
469
+ vi.spyOn(repo.aiProviderModel, 'getAiProviderRuntimeConfig').mockResolvedValue(
470
+ mockRuntimeConfig,
471
+ );
472
+
473
+ // Mock providers including fal for image generation
474
+ vi.spyOn(repo, 'getUserEnabledProviderList').mockResolvedValue([
475
+ { id: 'openai', logo: 'openai-logo', name: 'OpenAI', source: 'builtin' },
476
+ { id: 'fal', logo: 'fal-logo', name: 'Fal', source: 'builtin' },
477
+ ]);
478
+
479
+ // Mock models including image models from fal
480
+ vi.spyOn(repo, 'getEnabledModels').mockResolvedValue([
481
+ {
482
+ abilities: {},
483
+ enabled: true,
484
+ id: 'gpt-4',
485
+ providerId: 'openai',
486
+ type: 'chat',
487
+ },
488
+ {
489
+ abilities: {},
490
+ enabled: true,
491
+ id: 'flux/schnell',
492
+ providerId: 'fal',
493
+ type: 'image',
494
+ },
495
+ {
496
+ abilities: {},
497
+ enabled: true,
498
+ id: 'flux-kontext/dev',
499
+ providerId: 'fal',
500
+ type: 'image',
501
+ },
502
+ ]);
503
+
504
+ const result = await repo.getAiProviderRuntimeState();
505
+
506
+ expect(result).toEqual({
507
+ enabledAiModels: [
508
+ expect.objectContaining({
509
+ enabled: true,
510
+ id: 'gpt-4',
511
+ providerId: 'openai',
512
+ type: 'chat',
513
+ }),
514
+ expect.objectContaining({
515
+ enabled: true,
516
+ id: 'flux/schnell',
517
+ providerId: 'fal',
518
+ type: 'image',
519
+ }),
520
+ expect.objectContaining({
521
+ enabled: true,
522
+ id: 'flux-kontext/dev',
523
+ providerId: 'fal',
524
+ type: 'image',
525
+ }),
526
+ ],
527
+ enabledAiProviders: [
528
+ { id: 'openai', logo: 'openai-logo', name: 'OpenAI', source: 'builtin' },
529
+ { id: 'fal', logo: 'fal-logo', name: 'Fal', source: 'builtin' },
530
+ ],
531
+ enabledChatAiProviders: [
532
+ { id: 'openai', logo: 'openai-logo', name: 'OpenAI', source: 'builtin' },
533
+ ],
534
+ enabledImageAiProviders: [
535
+ expect.objectContaining({
536
+ id: 'fal',
537
+ name: 'Fal',
538
+ }),
539
+ ],
540
+ runtimeConfig: {
541
+ fal: {
542
+ apiKey: 'test-fal-key',
543
+ enabled: undefined,
544
+ },
545
+ openai: {
546
+ apiKey: 'test-openai-key',
547
+ enabled: true,
548
+ },
549
+ },
550
+ });
551
+ });
396
552
  });
397
553
 
398
554
  describe('getAiProviderDetail', () => {
@@ -91,14 +91,14 @@ export class AiInfraRepos {
91
91
  /**
92
92
  * used in the chat page. to show the enabled models
93
93
  */
94
- getEnabledModels = async () => {
95
- const providers = await this.getAiProviderList();
96
- const enabledProviders = providers.filter((item) => item.enabled);
97
-
98
- const allModels = await this.aiModelModel.getAllModels();
99
- const userEnabledModels = allModels.filter((item) => item.enabled);
100
-
101
- const modelList = await pMap(
94
+ getEnabledModels = async (filterEnabled: boolean = true) => {
95
+ const [providers, allModels] = await Promise.all([
96
+ this.getAiProviderList(),
97
+ this.aiModelModel.getAllModels(),
98
+ ]);
99
+ const enabledProviders = providers.filter((item) => (filterEnabled ? item.enabled : true));
100
+
101
+ const builtinModelList = await pMap(
102
102
  enabledProviders,
103
103
  async (provider) => {
104
104
  const aiModels = await this.fetchBuiltinModels(provider.id);
@@ -114,6 +114,7 @@ export class AiInfraRepos {
114
114
  };
115
115
 
116
116
  return {
117
+ ...item,
117
118
  abilities: !isEmpty(user.abilities) ? user.abilities : item.abilities || {},
118
119
  config: !isEmpty(user.config) ? user.config : item.config,
119
120
  contextWindowTokens:
@@ -129,32 +130,49 @@ export class AiInfraRepos {
129
130
  type: item.type,
130
131
  };
131
132
  })
132
- .filter((i) => i.enabled);
133
+ .filter((item) => (filterEnabled ? item.enabled : true));
133
134
  },
134
135
  { concurrency: 10 },
135
136
  );
136
137
 
137
- return [...modelList.flat(), ...userEnabledModels].sort(
138
- (a, b) => (a?.sort || -1) - (b?.sort || -1),
139
- ) as EnabledAiModel[];
138
+ const enabledProviderIds = new Set(enabledProviders.map((item) => item.id));
139
+
140
+ return [
141
+ ...builtinModelList.flat(),
142
+ ...allModels.filter((item) =>
143
+ filterEnabled ? enabledProviderIds.has(item.providerId) && item.enabled : true,
144
+ ),
145
+ ].sort((a, b) => (a?.sort || -1) - (b?.sort || -1)) as EnabledAiModel[];
140
146
  };
141
147
 
142
148
  getAiProviderRuntimeState = async (
143
149
  decryptor?: DecryptUserKeyVaults,
144
150
  ): Promise<AiProviderRuntimeState> => {
145
- const result = await this.aiProviderModel.getAiProviderRuntimeConfig(decryptor);
151
+ const [result, enabledAiProviders, allModels] = await Promise.all([
152
+ this.aiProviderModel.getAiProviderRuntimeConfig(decryptor),
153
+ this.getUserEnabledProviderList(),
154
+ this.getEnabledModels(false),
155
+ ]);
146
156
 
147
157
  const runtimeConfig = result;
148
-
149
158
  Object.entries(result).forEach(([key, value]) => {
150
159
  runtimeConfig[key] = merge(this.providerConfigs[key] || {}, value);
151
160
  });
161
+ const enabledAiModels = allModels.filter((model) => model.enabled);
162
+ const enabledChatAiProviders = enabledAiProviders.filter((provider) => {
163
+ return allModels.some((model) => model.providerId === provider.id && model.type === 'chat');
164
+ });
165
+ const enabledImageAiProviders = enabledAiProviders.filter((provider) => {
166
+ return allModels.some((model) => model.providerId === provider.id && model.type === 'image');
167
+ });
152
168
 
153
- const enabledAiProviders = await this.getUserEnabledProviderList();
154
-
155
- const enabledAiModels = await this.getEnabledModels();
156
-
157
- return { enabledAiModels, enabledAiProviders, runtimeConfig };
169
+ return {
170
+ enabledAiModels,
171
+ enabledAiProviders,
172
+ enabledChatAiProviders,
173
+ enabledImageAiProviders,
174
+ runtimeConfig,
175
+ };
158
176
  };
159
177
 
160
178
  getAiProviderModelList = async (providerId: string) => {
@@ -23,7 +23,7 @@ describe('TableViewerRepo', () => {
23
23
  it('should return all tables with counts', async () => {
24
24
  const result = await repo.getAllTables();
25
25
 
26
- expect(result.length).toEqual(55);
26
+ expect(result.length).toEqual(58);
27
27
  expect(result[0]).toEqual({ name: 'agents', count: 0, type: 'BASE TABLE' });
28
28
  });
29
29
 
@@ -14,6 +14,7 @@ import {
14
14
  import { createInsertSchema } from 'drizzle-zod';
15
15
 
16
16
  import { idGenerator } from '@/database/utils/idGenerator';
17
+ import { FileSource } from '@/types/files';
17
18
 
18
19
  import { accessedAt, createdAt, timestamps } from './_helpers';
19
20
  import { asyncTasks } from './asyncTask';
@@ -45,13 +46,20 @@ export const files = pgTable(
45
46
  userId: text('user_id')
46
47
  .references(() => users.id, { onDelete: 'cascade' })
47
48
  .notNull(),
49
+ /**
50
+ * mime
51
+ */
48
52
  fileType: varchar('file_type', { length: 255 }).notNull(),
53
+ /**
54
+ * sha256
55
+ */
49
56
  fileHash: varchar('file_hash', { length: 64 }).references(() => globalFiles.hashId, {
50
57
  onDelete: 'no action',
51
58
  }),
52
59
  name: text('name').notNull(),
53
60
  size: integer('size').notNull(),
54
61
  url: text('url').notNull(),
62
+ source: text('source').$type<FileSource>(),
55
63
 
56
64
  clientId: text('client_id'),
57
65
  metadata: jsonb('metadata'),
@@ -0,0 +1,127 @@
1
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
2
+ import { integer, jsonb, pgTable, text, uuid, varchar } from 'drizzle-orm/pg-core';
3
+ import { createInsertSchema } from 'drizzle-zod';
4
+
5
+ import { idGenerator } from '@/database/utils/idGenerator';
6
+ import { ImageGenerationAsset } from '@/types/generation';
7
+
8
+ import { timestamps } from './_helpers';
9
+ import { AsyncTaskSelectItem, asyncTasks } from './asyncTask';
10
+ import { files } from './file';
11
+ import { users } from './user';
12
+
13
+ /**
14
+ * 生成主题表 - 用于组织和管理 AI 生成内容的主题
15
+ */
16
+ export const generationTopics = pgTable('generation_topics', {
17
+ id: text('id')
18
+ .$defaultFn(() => idGenerator('generationTopics'))
19
+ .notNull()
20
+ .primaryKey(),
21
+
22
+ userId: text('user_id')
23
+ .references(() => users.id, { onDelete: 'cascade' })
24
+ .notNull(),
25
+
26
+ /** 简要描述主题内容,由 LLM 生成 */
27
+ title: text('title'),
28
+
29
+ /** 主题封面图片 URL */
30
+ coverUrl: text('cover_url'),
31
+
32
+ ...timestamps,
33
+ });
34
+
35
+ export const insertGenerationTopicSchema = createInsertSchema(generationTopics);
36
+
37
+ export type NewGenerationTopic = typeof generationTopics.$inferInsert;
38
+ export type GenerationTopicItem = typeof generationTopics.$inferSelect;
39
+
40
+ /**
41
+ * 生成批次表 - 存储一次生成请求的配置信息
42
+ */
43
+ export const generationBatches = pgTable('generation_batches', {
44
+ id: text('id')
45
+ .$defaultFn(() => idGenerator('generationBatches'))
46
+ .notNull()
47
+ .primaryKey(),
48
+
49
+ userId: text('user_id')
50
+ .references(() => users.id, { onDelete: 'cascade' })
51
+ .notNull(),
52
+
53
+ generationTopicId: text('generation_topic_id')
54
+ .notNull()
55
+ .references(() => generationTopics.id, { onDelete: 'cascade' }),
56
+
57
+ /** 服务商名称 */
58
+ provider: text('provider').notNull(),
59
+
60
+ /** 模型名称 */
61
+ model: text('model').notNull(),
62
+
63
+ /** 生成提示词 */
64
+ prompt: text('prompt').notNull(),
65
+
66
+ /** 图片宽度 */
67
+ width: integer('width'),
68
+
69
+ /** 图片高度 */
70
+ height: integer('height'),
71
+
72
+ /** 图片比例 */
73
+ ratio: varchar('ratio', { length: 64 }),
74
+
75
+ /** 存储生成批次的配置,存放不需要建立索引的公共配置 */
76
+ config: jsonb('config'),
77
+
78
+ ...timestamps,
79
+ });
80
+
81
+ export const insertGenerationBatchSchema = createInsertSchema(generationBatches);
82
+
83
+ export type NewGenerationBatch = typeof generationBatches.$inferInsert;
84
+ export type GenerationBatchItem = typeof generationBatches.$inferSelect;
85
+ export type GenerationBatchWithGenerations = GenerationBatchItem & {
86
+ generations: GenerationWithAsyncTask[];
87
+ };
88
+
89
+ /**
90
+ * 存储单个 AI 生成信息
91
+ */
92
+ export const generations = pgTable('generations', {
93
+ id: text('id')
94
+ .$defaultFn(() => idGenerator('generations'))
95
+ .notNull()
96
+ .primaryKey(),
97
+
98
+ userId: text('user_id')
99
+ .references(() => users.id, { onDelete: 'cascade' })
100
+ .notNull(),
101
+
102
+ generationBatchId: varchar('generation_batch_id', { length: 64 })
103
+ .notNull()
104
+ .references(() => generationBatches.id, { onDelete: 'cascade' }),
105
+
106
+ /** 关联的异步任务 ID */
107
+ asyncTaskId: uuid('async_task_id').references(() => asyncTasks.id, {
108
+ onDelete: 'set null',
109
+ }),
110
+
111
+ /** 关联的生成文件 ID,删除文件时连带删除生成记录 */
112
+ fileId: text('file_id').references(() => files.id, { onDelete: 'cascade' }),
113
+
114
+ /** 生成种子值 */
115
+ seed: integer('seed'),
116
+
117
+ /** 生成的资源信息,包含存储在 s3 上的 key, 图片实际宽高,缩略图 key 等 */
118
+ asset: jsonb('asset').$type<ImageGenerationAsset>(),
119
+
120
+ ...timestamps,
121
+ });
122
+
123
+ export const insertGenerationSchema = createInsertSchema(generations);
124
+
125
+ export type NewGeneration = typeof generations.$inferInsert;
126
+ export type GenerationItem = typeof generations.$inferSelect;
127
+ export type GenerationWithAsyncTask = GenerationItem & { asyncTask?: AsyncTaskSelectItem };
@@ -3,6 +3,7 @@ export * from './aiInfra';
3
3
  export * from './asyncTask';
4
4
  export * from './document';
5
5
  export * from './file';
6
+ export * from './generation';
6
7
  export * from './message';
7
8
  export * from './nextauth';
8
9
  export * from './oidc';
@@ -8,6 +8,7 @@ import { agents, agentsFiles, agentsKnowledgeBases } from './agent';
8
8
  import { asyncTasks } from './asyncTask';
9
9
  import { documentChunks, documents } from './document';
10
10
  import { files, knowledgeBases } from './file';
11
+ import { generationBatches, generationTopics, generations } from './generation';
11
12
  import { messages, messagesFiles } from './message';
12
13
  import { chunks, unstructuredChunks } from './rag';
13
14
  import { sessionGroups, sessions } from './session';
@@ -198,7 +199,10 @@ export const filesRelations = relations(files, ({ many, one }) => ({
198
199
  sessions: many(filesToSessions),
199
200
  agents: many(agentsFiles),
200
201
  documents: many(documents, { relationName: 'fileDocuments' }),
201
-
202
+ generation: one(generations, {
203
+ fields: [files.id],
204
+ references: [generations.fileId],
205
+ }),
202
206
  chunkingTask: one(asyncTasks, {
203
207
  fields: [files.chunkTaskId],
204
208
  references: [asyncTasks.id],
@@ -237,3 +241,43 @@ export const documentChunksRelations = relations(documentChunks, ({ one }) => ({
237
241
  references: [documents.id],
238
242
  }),
239
243
  }));
244
+
245
+ // Generation 相关关系定义
246
+ export const generationTopicsRelations = relations(generationTopics, ({ one, many }) => ({
247
+ user: one(users, {
248
+ fields: [generationTopics.userId],
249
+ references: [users.id],
250
+ }),
251
+ batches: many(generationBatches),
252
+ }));
253
+
254
+ export const generationBatchesRelations = relations(generationBatches, ({ one, many }) => ({
255
+ user: one(users, {
256
+ fields: [generationBatches.userId],
257
+ references: [users.id],
258
+ }),
259
+ topic: one(generationTopics, {
260
+ fields: [generationBatches.generationTopicId],
261
+ references: [generationTopics.id],
262
+ }),
263
+ generations: many(generations),
264
+ }));
265
+
266
+ export const generationsRelations = relations(generations, ({ one }) => ({
267
+ user: one(users, {
268
+ fields: [generations.userId],
269
+ references: [users.id],
270
+ }),
271
+ batch: one(generationBatches, {
272
+ fields: [generations.generationBatchId],
273
+ references: [generationBatches.id],
274
+ }),
275
+ asyncTask: one(asyncTasks, {
276
+ fields: [generations.asyncTaskId],
277
+ references: [asyncTasks.id],
278
+ }),
279
+ file: one(files, {
280
+ fields: [generations.fileId],
281
+ references: [files.id],
282
+ }),
283
+ }));
@@ -5,3 +5,5 @@ import * as schema from './schemas';
5
5
  export type LobeChatDatabaseSchema = typeof schema;
6
6
 
7
7
  export type LobeChatDatabase = NeonDatabase<LobeChatDatabaseSchema>;
8
+
9
+ export type Transaction = Parameters<Parameters<LobeChatDatabase['transaction']>[0]>[0];
@@ -6,6 +6,9 @@ const prefixes = {
6
6
  agents: 'agt',
7
7
  documents: 'docs',
8
8
  files: 'file',
9
+ generationBatches: 'gb',
10
+ generationTopics: 'gt',
11
+ generations: 'gen',
9
12
  knowledgeBases: 'kb',
10
13
  messages: 'msg',
11
14
  plugins: 'plg',
@@ -0,0 +1,39 @@
1
+ import { memo } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+
4
+ import InvalidAPIKey from '@/components/InvalidAPIKey';
5
+ import { useProviderName } from '@/hooks/useProviderName';
6
+ import { useChatStore } from '@/store/chat';
7
+ import { GlobalLLMProviderKey } from '@/types/user/settings/modelProvider';
8
+
9
+ interface ChatInvalidAPIKeyProps {
10
+ id: string;
11
+ provider?: string;
12
+ }
13
+ const ChatInvalidAPIKey = memo<ChatInvalidAPIKeyProps>(({ id, provider }) => {
14
+ const { t } = useTranslation('modelProvider');
15
+ const { t: modelProviderErrorT } = useTranslation(['modelProvider', 'error']);
16
+ const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
17
+ const providerName = useProviderName(provider as GlobalLLMProviderKey);
18
+
19
+ return (
20
+ <InvalidAPIKey
21
+ bedrockDescription={t('bedrock.unlock.description')}
22
+ description={modelProviderErrorT(`unlock.apiKey.description`, {
23
+ name: providerName,
24
+ ns: 'error',
25
+ })}
26
+ id={id}
27
+ onClose={() => {
28
+ deleteMessage(id);
29
+ }}
30
+ onRecreate={() => {
31
+ resend(id);
32
+ deleteMessage(id);
33
+ }}
34
+ provider={provider}
35
+ />
36
+ );
37
+ });
38
+
39
+ export default ChatInvalidAPIKey;
@@ -8,8 +8,8 @@ import { Flexbox } from 'react-layout-kit';
8
8
  import { useServerConfigStore } from '@/store/serverConfig';
9
9
  import { featureFlagsSelectors, serverConfigSelectors } from '@/store/serverConfig/selectors';
10
10
 
11
- import APIKeyForm from './APIKeyForm';
12
11
  import AccessCodeForm from './AccessCodeForm';
12
+ import ChatInvalidAPIKey from './ChatInvalidApiKey';
13
13
  import OAuthForm from './OAuthForm';
14
14
  import { ErrorActionContainer } from './style';
15
15
 
@@ -69,7 +69,7 @@ const InvalidAccessCode = memo<InvalidAccessCodeProps>(({ id, provider }) => {
69
69
 
70
70
  <Flexbox gap={24}>
71
71
  {mode === Tab.Password && <AccessCodeForm id={id} />}
72
- {showOpenAIApiKey && mode === Tab.Api && <APIKeyForm id={id} provider={provider} />}
72
+ {showOpenAIApiKey && mode === Tab.Api && <ChatInvalidAPIKey id={id} provider={provider} />}
73
73
  {isEnabledOAuth && mode === Tab.Oauth && <OAuthForm id={id} />}
74
74
  </Flexbox>
75
75
  </ErrorActionContainer>
@@ -10,9 +10,9 @@ import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '@/libs/model-
10
10
  import { ChatErrorType, ErrorType } from '@/types/fetch';
11
11
  import { ChatMessage, ChatMessageError } from '@/types/message';
12
12
 
13
+ import ChatInvalidAPIKey from './ChatInvalidApiKey';
13
14
  import ClerkLogin from './ClerkLogin';
14
15
  import ErrorJsonViewer from './ErrorJsonViewer';
15
- import InvalidAPIKey from './InvalidAPIKey';
16
16
  import InvalidAccessCode from './InvalidAccessCode';
17
17
  import { ErrorActionContainer } from './style';
18
18
 
@@ -113,13 +113,13 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
113
113
 
114
114
  case AgentRuntimeErrorType.NoOpenAIAPIKey: {
115
115
  {
116
- return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
116
+ return <ChatInvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
117
117
  }
118
118
  }
119
119
  }
120
120
 
121
121
  if (error.type.toString().includes('Invalid')) {
122
- return <InvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
122
+ return <ChatInvalidAPIKey id={data.id} provider={data.error?.body?.provider} />;
123
123
  }
124
124
 
125
125
  return <ErrorJsonViewer error={data.error} id={data.id} />;