@lobehub/chat 1.98.2 → 1.99.0

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 (456) 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 +27 -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/const/dir.ts +3 -0
  11. package/apps/desktop/src/main/controllers/UploadFileCtr.ts +13 -8
  12. package/apps/desktop/src/main/core/App.ts +8 -0
  13. package/apps/desktop/src/main/core/StaticFileServerManager.ts +221 -0
  14. package/apps/desktop/src/main/services/fileSrv.ts +231 -44
  15. package/apps/desktop/src/main/utils/next-electron-rsc.ts +36 -5
  16. package/changelog/v1.json +9 -0
  17. package/docs/development/database-schema.dbml +70 -0
  18. package/locales/ar/common.json +2 -0
  19. package/locales/ar/components.json +35 -0
  20. package/locales/ar/error.json +2 -0
  21. package/locales/ar/image.json +100 -0
  22. package/locales/ar/metadata.json +4 -0
  23. package/locales/ar/modelProvider.json +1 -0
  24. package/locales/ar/models.json +15 -0
  25. package/locales/ar/plugin.json +22 -0
  26. package/locales/ar/providers.json +3 -0
  27. package/locales/ar/setting.json +5 -0
  28. package/locales/bg-BG/common.json +2 -0
  29. package/locales/bg-BG/components.json +35 -0
  30. package/locales/bg-BG/error.json +2 -0
  31. package/locales/bg-BG/image.json +100 -0
  32. package/locales/bg-BG/metadata.json +4 -0
  33. package/locales/bg-BG/modelProvider.json +1 -0
  34. package/locales/bg-BG/models.json +15 -0
  35. package/locales/bg-BG/plugin.json +22 -0
  36. package/locales/bg-BG/providers.json +3 -0
  37. package/locales/bg-BG/setting.json +5 -0
  38. package/locales/de-DE/common.json +2 -0
  39. package/locales/de-DE/components.json +35 -0
  40. package/locales/de-DE/error.json +2 -0
  41. package/locales/de-DE/image.json +100 -0
  42. package/locales/de-DE/metadata.json +4 -0
  43. package/locales/de-DE/modelProvider.json +1 -0
  44. package/locales/de-DE/models.json +15 -0
  45. package/locales/de-DE/plugin.json +22 -0
  46. package/locales/de-DE/providers.json +3 -0
  47. package/locales/de-DE/setting.json +5 -0
  48. package/locales/en-US/common.json +2 -0
  49. package/locales/en-US/components.json +35 -0
  50. package/locales/en-US/error.json +2 -0
  51. package/locales/en-US/image.json +100 -0
  52. package/locales/en-US/metadata.json +4 -0
  53. package/locales/en-US/modelProvider.json +1 -0
  54. package/locales/en-US/models.json +15 -0
  55. package/locales/en-US/plugin.json +22 -0
  56. package/locales/en-US/providers.json +3 -0
  57. package/locales/en-US/setting.json +5 -0
  58. package/locales/es-ES/common.json +2 -0
  59. package/locales/es-ES/components.json +35 -0
  60. package/locales/es-ES/error.json +2 -0
  61. package/locales/es-ES/image.json +100 -0
  62. package/locales/es-ES/metadata.json +4 -0
  63. package/locales/es-ES/modelProvider.json +1 -0
  64. package/locales/es-ES/models.json +15 -0
  65. package/locales/es-ES/plugin.json +22 -0
  66. package/locales/es-ES/providers.json +3 -0
  67. package/locales/es-ES/setting.json +5 -0
  68. package/locales/fa-IR/common.json +2 -0
  69. package/locales/fa-IR/components.json +35 -0
  70. package/locales/fa-IR/error.json +2 -0
  71. package/locales/fa-IR/image.json +100 -0
  72. package/locales/fa-IR/metadata.json +4 -0
  73. package/locales/fa-IR/modelProvider.json +1 -0
  74. package/locales/fa-IR/models.json +15 -0
  75. package/locales/fa-IR/plugin.json +22 -0
  76. package/locales/fa-IR/providers.json +3 -0
  77. package/locales/fa-IR/setting.json +5 -0
  78. package/locales/fr-FR/common.json +2 -0
  79. package/locales/fr-FR/components.json +35 -0
  80. package/locales/fr-FR/error.json +2 -0
  81. package/locales/fr-FR/image.json +100 -0
  82. package/locales/fr-FR/metadata.json +4 -0
  83. package/locales/fr-FR/modelProvider.json +1 -0
  84. package/locales/fr-FR/models.json +15 -0
  85. package/locales/fr-FR/plugin.json +22 -0
  86. package/locales/fr-FR/providers.json +3 -0
  87. package/locales/fr-FR/setting.json +5 -0
  88. package/locales/it-IT/common.json +2 -0
  89. package/locales/it-IT/components.json +35 -0
  90. package/locales/it-IT/error.json +2 -0
  91. package/locales/it-IT/image.json +100 -0
  92. package/locales/it-IT/metadata.json +4 -0
  93. package/locales/it-IT/modelProvider.json +1 -0
  94. package/locales/it-IT/models.json +15 -0
  95. package/locales/it-IT/plugin.json +22 -0
  96. package/locales/it-IT/providers.json +3 -0
  97. package/locales/it-IT/setting.json +5 -0
  98. package/locales/ja-JP/common.json +2 -0
  99. package/locales/ja-JP/components.json +35 -0
  100. package/locales/ja-JP/error.json +2 -0
  101. package/locales/ja-JP/image.json +100 -0
  102. package/locales/ja-JP/metadata.json +4 -0
  103. package/locales/ja-JP/modelProvider.json +1 -0
  104. package/locales/ja-JP/models.json +15 -0
  105. package/locales/ja-JP/plugin.json +22 -0
  106. package/locales/ja-JP/providers.json +3 -0
  107. package/locales/ja-JP/setting.json +5 -0
  108. package/locales/ko-KR/common.json +2 -0
  109. package/locales/ko-KR/components.json +35 -0
  110. package/locales/ko-KR/error.json +2 -0
  111. package/locales/ko-KR/image.json +100 -0
  112. package/locales/ko-KR/metadata.json +4 -0
  113. package/locales/ko-KR/modelProvider.json +1 -0
  114. package/locales/ko-KR/models.json +15 -0
  115. package/locales/ko-KR/plugin.json +22 -0
  116. package/locales/ko-KR/providers.json +3 -0
  117. package/locales/ko-KR/setting.json +5 -0
  118. package/locales/nl-NL/common.json +2 -0
  119. package/locales/nl-NL/components.json +35 -0
  120. package/locales/nl-NL/error.json +2 -0
  121. package/locales/nl-NL/image.json +100 -0
  122. package/locales/nl-NL/metadata.json +4 -0
  123. package/locales/nl-NL/modelProvider.json +1 -0
  124. package/locales/nl-NL/models.json +15 -0
  125. package/locales/nl-NL/plugin.json +22 -0
  126. package/locales/nl-NL/providers.json +3 -0
  127. package/locales/nl-NL/setting.json +5 -0
  128. package/locales/pl-PL/common.json +2 -0
  129. package/locales/pl-PL/components.json +35 -0
  130. package/locales/pl-PL/error.json +2 -0
  131. package/locales/pl-PL/image.json +100 -0
  132. package/locales/pl-PL/metadata.json +4 -0
  133. package/locales/pl-PL/modelProvider.json +1 -0
  134. package/locales/pl-PL/models.json +15 -0
  135. package/locales/pl-PL/plugin.json +22 -0
  136. package/locales/pl-PL/providers.json +3 -0
  137. package/locales/pl-PL/setting.json +5 -0
  138. package/locales/pt-BR/common.json +2 -0
  139. package/locales/pt-BR/components.json +35 -0
  140. package/locales/pt-BR/error.json +2 -0
  141. package/locales/pt-BR/image.json +100 -0
  142. package/locales/pt-BR/metadata.json +4 -0
  143. package/locales/pt-BR/modelProvider.json +1 -0
  144. package/locales/pt-BR/models.json +15 -0
  145. package/locales/pt-BR/plugin.json +22 -0
  146. package/locales/pt-BR/providers.json +3 -0
  147. package/locales/pt-BR/setting.json +5 -0
  148. package/locales/ru-RU/common.json +2 -0
  149. package/locales/ru-RU/components.json +35 -0
  150. package/locales/ru-RU/error.json +2 -0
  151. package/locales/ru-RU/image.json +100 -0
  152. package/locales/ru-RU/metadata.json +4 -0
  153. package/locales/ru-RU/modelProvider.json +1 -0
  154. package/locales/ru-RU/models.json +15 -0
  155. package/locales/ru-RU/plugin.json +22 -0
  156. package/locales/ru-RU/providers.json +3 -0
  157. package/locales/ru-RU/setting.json +5 -0
  158. package/locales/tr-TR/common.json +2 -0
  159. package/locales/tr-TR/components.json +35 -0
  160. package/locales/tr-TR/error.json +2 -0
  161. package/locales/tr-TR/image.json +100 -0
  162. package/locales/tr-TR/metadata.json +4 -0
  163. package/locales/tr-TR/modelProvider.json +1 -0
  164. package/locales/tr-TR/models.json +15 -0
  165. package/locales/tr-TR/plugin.json +22 -0
  166. package/locales/tr-TR/providers.json +3 -0
  167. package/locales/tr-TR/setting.json +5 -0
  168. package/locales/vi-VN/common.json +2 -0
  169. package/locales/vi-VN/components.json +35 -0
  170. package/locales/vi-VN/error.json +2 -0
  171. package/locales/vi-VN/image.json +100 -0
  172. package/locales/vi-VN/metadata.json +4 -0
  173. package/locales/vi-VN/modelProvider.json +1 -0
  174. package/locales/vi-VN/models.json +15 -0
  175. package/locales/vi-VN/plugin.json +22 -0
  176. package/locales/vi-VN/providers.json +3 -0
  177. package/locales/vi-VN/setting.json +5 -0
  178. package/locales/zh-CN/common.json +2 -0
  179. package/locales/zh-CN/components.json +35 -0
  180. package/locales/zh-CN/error.json +2 -0
  181. package/locales/zh-CN/image.json +100 -0
  182. package/locales/zh-CN/metadata.json +4 -0
  183. package/locales/zh-CN/modelProvider.json +1 -0
  184. package/locales/zh-CN/models.json +15 -0
  185. package/locales/zh-CN/plugin.json +22 -0
  186. package/locales/zh-CN/providers.json +3 -0
  187. package/locales/zh-CN/setting.json +5 -0
  188. package/locales/zh-TW/common.json +2 -0
  189. package/locales/zh-TW/components.json +35 -0
  190. package/locales/zh-TW/error.json +2 -0
  191. package/locales/zh-TW/image.json +100 -0
  192. package/locales/zh-TW/metadata.json +4 -0
  193. package/locales/zh-TW/modelProvider.json +1 -0
  194. package/locales/zh-TW/models.json +15 -0
  195. package/locales/zh-TW/plugin.json +22 -0
  196. package/locales/zh-TW/providers.json +3 -0
  197. package/locales/zh-TW/setting.json +5 -0
  198. package/package.json +11 -4
  199. package/packages/electron-server-ipc/src/events/file.ts +3 -1
  200. package/packages/electron-server-ipc/src/types/file.ts +15 -0
  201. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/TopActions.tsx +11 -1
  202. package/src/app/[variants]/(main)/image/@menu/components/AspectRatioSelect/index.tsx +73 -0
  203. package/src/app/[variants]/(main)/image/@menu/components/SeedNumberInput/index.tsx +39 -0
  204. package/src/app/[variants]/(main)/image/@menu/components/SizeSelect/index.tsx +89 -0
  205. package/src/app/[variants]/(main)/image/@menu/default.tsx +11 -0
  206. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/AspectRatioSelect.tsx +24 -0
  207. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/DimensionControlGroup.tsx +107 -0
  208. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageNum.tsx +290 -0
  209. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUpload.tsx +504 -0
  210. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrl.tsx +18 -0
  211. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ImageUrlsUpload.tsx +19 -0
  212. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/ModelSelect.tsx +155 -0
  213. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/ImageManageModal.tsx +415 -0
  214. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/MultiImagesUpload/index.tsx +732 -0
  215. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SeedNumberInput.tsx +24 -0
  216. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSelect.tsx +17 -0
  217. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/SizeSliderInput.tsx +15 -0
  218. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/components/StepsSliderInput.tsx +11 -0
  219. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/constants.ts +1 -0
  220. package/src/app/[variants]/(main)/image/@menu/features/ConfigPanel/index.tsx +93 -0
  221. package/src/app/[variants]/(main)/image/@topic/default.tsx +17 -0
  222. package/src/app/[variants]/(main)/image/@topic/features/Topics/NewTopicButton.tsx +64 -0
  223. package/src/app/[variants]/(main)/image/@topic/features/Topics/SkeletonList.tsx +34 -0
  224. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItem.tsx +136 -0
  225. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicItemContainer.tsx +91 -0
  226. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicList.tsx +57 -0
  227. package/src/app/[variants]/(main)/image/@topic/features/Topics/TopicUrlSync.tsx +37 -0
  228. package/src/app/[variants]/(main)/image/@topic/features/Topics/index.tsx +19 -0
  229. package/src/app/[variants]/(main)/image/NotSupportClient.tsx +153 -0
  230. package/src/app/[variants]/(main)/image/_layout/Desktop/Container.tsx +35 -0
  231. package/src/app/[variants]/(main)/image/_layout/Desktop/RegisterHotkeys.tsx +10 -0
  232. package/src/app/[variants]/(main)/image/_layout/Desktop/index.tsx +30 -0
  233. package/src/app/[variants]/(main)/image/_layout/Mobile/index.tsx +14 -0
  234. package/src/app/[variants]/(main)/image/_layout/type.ts +7 -0
  235. package/src/app/[variants]/(main)/image/features/GenerationFeed/BatchItem.tsx +196 -0
  236. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ActionButtons.tsx +60 -0
  237. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ElapsedTime.tsx +90 -0
  238. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/ErrorState.tsx +65 -0
  239. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/LoadingState.tsx +43 -0
  240. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/SuccessState.tsx +49 -0
  241. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/index.tsx +156 -0
  242. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/styles.ts +51 -0
  243. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/types.ts +39 -0
  244. package/src/app/[variants]/(main)/image/features/GenerationFeed/GenerationItem/utils.ts +11 -0
  245. package/src/app/[variants]/(main)/image/features/GenerationFeed/index.tsx +97 -0
  246. package/src/app/[variants]/(main)/image/features/ImageWorkspace/Content.tsx +48 -0
  247. package/src/app/[variants]/(main)/image/features/ImageWorkspace/EmptyState.tsx +37 -0
  248. package/src/app/[variants]/(main)/image/features/ImageWorkspace/SkeletonList.tsx +50 -0
  249. package/src/app/[variants]/(main)/image/features/ImageWorkspace/index.tsx +23 -0
  250. package/src/app/[variants]/(main)/image/features/PromptInput/Title.tsx +38 -0
  251. package/src/app/[variants]/(main)/image/features/PromptInput/index.tsx +114 -0
  252. package/src/app/[variants]/(main)/image/layout.tsx +19 -0
  253. package/src/app/[variants]/(main)/image/loading.tsx +3 -0
  254. package/src/app/[variants]/(main)/image/page.tsx +47 -0
  255. package/src/app/[variants]/(main)/settings/system-agent/index.tsx +2 -1
  256. package/src/chains/summaryGenerationTitle.ts +25 -0
  257. package/src/components/ImageItem/index.tsx +9 -6
  258. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/Bedrock.tsx +3 -4
  259. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/ProviderApiKeyForm.tsx +5 -4
  260. package/src/components/InvalidAPIKey/APIKeyForm/index.tsx +108 -0
  261. package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/useApiKey.ts +2 -1
  262. package/src/components/InvalidAPIKey/index.tsx +30 -0
  263. package/src/components/KeyValueEditor/index.tsx +203 -0
  264. package/src/components/KeyValueEditor/utils.ts +42 -0
  265. package/src/config/aiModels/fal.ts +52 -0
  266. package/src/config/aiModels/index.ts +3 -0
  267. package/src/config/aiModels/openai.ts +20 -6
  268. package/src/config/llm.ts +6 -0
  269. package/src/config/modelProviders/fal.ts +21 -0
  270. package/src/config/modelProviders/index.ts +3 -0
  271. package/src/config/paramsSchemas/fal/flux-kontext-dev.ts +8 -0
  272. package/src/config/paramsSchemas/fal/flux-pro-kontext.ts +11 -0
  273. package/src/config/paramsSchemas/fal/flux-schnell.ts +9 -0
  274. package/src/config/paramsSchemas/fal/imagen4.ts +10 -0
  275. package/src/config/paramsSchemas/openai/gpt-image-1.ts +10 -0
  276. package/src/const/hotkeys.ts +2 -2
  277. package/src/const/image.ts +6 -0
  278. package/src/const/settings/systemAgent.ts +1 -0
  279. package/src/database/client/migrations.json +27 -0
  280. package/src/database/migrations/0026_add_autovacuum_tuning.sql +2 -0
  281. package/src/database/migrations/0027_ai_image.sql +47 -0
  282. package/src/database/migrations/meta/0027_snapshot.json +6003 -0
  283. package/src/database/migrations/meta/_journal.json +7 -0
  284. package/src/database/models/__tests__/asyncTask.test.ts +7 -5
  285. package/src/database/models/__tests__/file.test.ts +287 -0
  286. package/src/database/models/__tests__/generation.test.ts +786 -0
  287. package/src/database/models/__tests__/generationBatch.test.ts +614 -0
  288. package/src/database/models/__tests__/generationTopic.test.ts +411 -0
  289. package/src/database/models/aiModel.ts +2 -0
  290. package/src/database/models/asyncTask.ts +1 -1
  291. package/src/database/models/file.ts +28 -20
  292. package/src/database/models/generation.ts +197 -0
  293. package/src/database/models/generationBatch.ts +212 -0
  294. package/src/database/models/generationTopic.ts +131 -0
  295. package/src/database/repositories/aiInfra/index.test.ts +151 -1
  296. package/src/database/repositories/aiInfra/index.ts +28 -19
  297. package/src/database/repositories/tableViewer/index.test.ts +1 -1
  298. package/src/database/schemas/file.ts +8 -0
  299. package/src/database/schemas/generation.ts +127 -0
  300. package/src/database/schemas/index.ts +1 -0
  301. package/src/database/schemas/relations.ts +45 -1
  302. package/src/database/type.ts +2 -0
  303. package/src/database/utils/idGenerator.ts +3 -0
  304. package/src/features/Conversation/Error/ChatInvalidApiKey.tsx +39 -0
  305. package/src/features/Conversation/Error/InvalidAccessCode.tsx +2 -2
  306. package/src/features/Conversation/Error/index.tsx +3 -3
  307. package/src/features/ImageSidePanel/index.tsx +83 -0
  308. package/src/features/ImageTopicPanel/index.tsx +79 -0
  309. package/src/features/PluginDevModal/MCPManifestForm/CollapsibleSection.tsx +62 -0
  310. package/src/features/PluginDevModal/MCPManifestForm/QuickImportSection.tsx +158 -0
  311. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +99 -155
  312. package/src/features/PluginStore/McpList/Detail/Settings/index.tsx +5 -2
  313. package/src/hooks/useDownloadImage.ts +31 -0
  314. package/src/hooks/useFetchGenerationTopics.ts +13 -0
  315. package/src/hooks/useHotkeys/imageScope.ts +48 -0
  316. package/src/libs/mcp/client.ts +55 -22
  317. package/src/libs/mcp/types.ts +42 -6
  318. package/src/libs/model-runtime/BaseAI.ts +3 -1
  319. package/src/libs/model-runtime/ModelRuntime.test.ts +80 -0
  320. package/src/libs/model-runtime/ModelRuntime.ts +15 -1
  321. package/src/libs/model-runtime/UniformRuntime/index.ts +4 -1
  322. package/src/libs/model-runtime/fal/index.test.ts +442 -0
  323. package/src/libs/model-runtime/fal/index.ts +88 -0
  324. package/src/libs/model-runtime/openai/index.test.ts +396 -2
  325. package/src/libs/model-runtime/openai/index.ts +129 -3
  326. package/src/libs/model-runtime/runtimeMap.ts +2 -0
  327. package/src/libs/model-runtime/types/image.ts +25 -0
  328. package/src/libs/model-runtime/types/type.ts +1 -0
  329. package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +10 -0
  330. package/src/libs/standard-parameters/index.ts +1 -0
  331. package/src/libs/standard-parameters/meta-schema.test.ts +214 -0
  332. package/src/libs/standard-parameters/meta-schema.ts +147 -0
  333. package/src/libs/swr/index.ts +1 -0
  334. package/src/libs/trpc/async/asyncAuth.ts +29 -8
  335. package/src/libs/trpc/async/context.ts +42 -4
  336. package/src/libs/trpc/async/index.ts +17 -4
  337. package/src/libs/trpc/async/init.ts +8 -0
  338. package/src/libs/trpc/client/lambda.ts +19 -2
  339. package/src/locales/default/common.ts +2 -0
  340. package/src/locales/default/components.ts +35 -0
  341. package/src/locales/default/error.ts +2 -0
  342. package/src/locales/default/image.ts +100 -0
  343. package/src/locales/default/index.ts +2 -0
  344. package/src/locales/default/metadata.ts +4 -0
  345. package/src/locales/default/modelProvider.ts +2 -0
  346. package/src/locales/default/plugin.ts +22 -0
  347. package/src/locales/default/setting.ts +5 -0
  348. package/src/middleware.ts +1 -0
  349. package/src/server/modules/ElectronIPCClient/index.ts +9 -1
  350. package/src/server/modules/S3/index.ts +15 -0
  351. package/src/server/routers/async/caller.ts +9 -1
  352. package/src/server/routers/async/image.ts +253 -0
  353. package/src/server/routers/async/index.ts +2 -0
  354. package/src/server/routers/lambda/aiProvider.test.ts +1 -0
  355. package/src/server/routers/lambda/generation.test.ts +267 -0
  356. package/src/server/routers/lambda/generation.ts +86 -0
  357. package/src/server/routers/lambda/generationBatch.test.ts +376 -0
  358. package/src/server/routers/lambda/generationBatch.ts +56 -0
  359. package/src/server/routers/lambda/generationTopic.test.ts +508 -0
  360. package/src/server/routers/lambda/generationTopic.ts +93 -0
  361. package/src/server/routers/lambda/image.ts +248 -0
  362. package/src/server/routers/lambda/index.ts +8 -0
  363. package/src/server/routers/tools/mcp.ts +15 -0
  364. package/src/server/services/file/__tests__/index.test.ts +135 -0
  365. package/src/server/services/file/impls/local.test.ts +153 -52
  366. package/src/server/services/file/impls/local.ts +70 -46
  367. package/src/server/services/file/impls/s3.test.ts +114 -0
  368. package/src/server/services/file/impls/s3.ts +40 -0
  369. package/src/server/services/file/impls/type.ts +10 -0
  370. package/src/server/services/file/index.ts +14 -0
  371. package/src/server/services/generation/index.ts +239 -0
  372. package/src/server/services/mcp/index.ts +20 -2
  373. package/src/services/__tests__/generation.test.ts +40 -0
  374. package/src/services/__tests__/generationBatch.test.ts +36 -0
  375. package/src/services/__tests__/generationTopic.test.ts +72 -0
  376. package/src/services/electron/file.ts +3 -1
  377. package/src/services/generation.ts +16 -0
  378. package/src/services/generationBatch.ts +25 -0
  379. package/src/services/generationTopic.ts +28 -0
  380. package/src/services/image.ts +33 -0
  381. package/src/services/mcp.ts +12 -7
  382. package/src/services/upload.ts +43 -9
  383. package/src/store/aiInfra/slices/aiProvider/action.ts +25 -5
  384. package/src/store/aiInfra/slices/aiProvider/initialState.ts +1 -0
  385. package/src/store/aiInfra/slices/aiProvider/selectors.ts +3 -0
  386. package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +5 -5
  387. package/src/store/chat/slices/message/action.ts +2 -2
  388. package/src/store/chat/slices/translate/action.ts +1 -1
  389. package/src/store/global/initialState.ts +9 -0
  390. package/src/store/global/selectors/systemStatus.ts +8 -0
  391. package/src/store/image/index.ts +2 -0
  392. package/src/store/image/initialState.ts +25 -0
  393. package/src/store/image/selectors.ts +4 -0
  394. package/src/store/image/slices/createImage/action.test.ts +330 -0
  395. package/src/store/image/slices/createImage/action.ts +134 -0
  396. package/src/store/image/slices/createImage/initialState.ts +9 -0
  397. package/src/store/image/slices/createImage/selectors.test.ts +114 -0
  398. package/src/store/image/slices/createImage/selectors.ts +9 -0
  399. package/src/store/image/slices/generationBatch/action.test.ts +495 -0
  400. package/src/store/image/slices/generationBatch/action.ts +303 -0
  401. package/src/store/image/slices/generationBatch/initialState.ts +13 -0
  402. package/src/store/image/slices/generationBatch/reducer.test.ts +568 -0
  403. package/src/store/image/slices/generationBatch/reducer.ts +101 -0
  404. package/src/store/image/slices/generationBatch/selectors.test.ts +307 -0
  405. package/src/store/image/slices/generationBatch/selectors.ts +36 -0
  406. package/src/store/image/slices/generationConfig/action.test.ts +351 -0
  407. package/src/store/image/slices/generationConfig/action.ts +295 -0
  408. package/src/store/image/slices/generationConfig/hooks.test.ts +304 -0
  409. package/src/store/image/slices/generationConfig/hooks.ts +118 -0
  410. package/src/store/image/slices/generationConfig/index.ts +1 -0
  411. package/src/store/image/slices/generationConfig/initialState.ts +37 -0
  412. package/src/store/image/slices/generationConfig/selectors.test.ts +204 -0
  413. package/src/store/image/slices/generationConfig/selectors.ts +25 -0
  414. package/src/store/image/slices/generationTopic/action.test.ts +687 -0
  415. package/src/store/image/slices/generationTopic/action.ts +319 -0
  416. package/src/store/image/slices/generationTopic/index.ts +2 -0
  417. package/src/store/image/slices/generationTopic/initialState.ts +14 -0
  418. package/src/store/image/slices/generationTopic/reducer.test.ts +198 -0
  419. package/src/store/image/slices/generationTopic/reducer.ts +66 -0
  420. package/src/store/image/slices/generationTopic/selectors.test.ts +103 -0
  421. package/src/store/image/slices/generationTopic/selectors.ts +15 -0
  422. package/src/store/image/store.ts +42 -0
  423. package/src/store/image/utils/size.ts +51 -0
  424. package/src/store/tool/slices/customPlugin/action.ts +10 -1
  425. package/src/store/tool/slices/mcpStore/action.ts +6 -4
  426. package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +4 -0
  427. package/src/store/user/slices/settings/selectors/systemAgent.ts +2 -0
  428. package/src/types/aiModel.ts +8 -3
  429. package/src/types/aiProvider.ts +1 -0
  430. package/src/types/asyncTask.ts +2 -0
  431. package/src/types/files/index.ts +5 -0
  432. package/src/types/generation/index.ts +80 -0
  433. package/src/types/hotkey.ts +2 -0
  434. package/src/types/plugins/mcp.ts +2 -6
  435. package/src/types/tool/plugin.ts +8 -0
  436. package/src/types/user/settings/keyVaults.ts +5 -0
  437. package/src/types/user/settings/systemAgent.ts +1 -0
  438. package/src/utils/client/downloadFile.ts +33 -4
  439. package/src/utils/number.test.ts +105 -0
  440. package/src/utils/number.ts +25 -0
  441. package/src/utils/server/__tests__/geo.test.ts +6 -3
  442. package/src/utils/storeDebug.test.ts +152 -0
  443. package/src/utils/storeDebug.ts +16 -7
  444. package/src/utils/time.test.ts +259 -0
  445. package/src/utils/time.ts +18 -0
  446. package/src/utils/units.ts +61 -0
  447. package/src/utils/url.test.ts +358 -9
  448. package/src/utils/url.ts +105 -3
  449. package/{vitest.server.config.ts → vitest.config.server.ts} +3 -0
  450. package/.cursor/rules/i18n/i18n-auto-attached.mdc +0 -6
  451. package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -105
  452. package/src/features/Conversation/Error/InvalidAPIKey.tsx +0 -16
  453. package/src/features/PluginDevModal/MCPManifestForm/EnvEditor.tsx +0 -227
  454. /package/.cursor/rules/{i18n/i18n.mdc → i18n.mdc} +0 -0
  455. /package/src/app/[variants]/(main)/settings/system-agent/features/{createForm.tsx → SystemAgentForm.tsx} +0 -0
  456. /package/src/{features/Conversation/Error → components/InvalidAPIKey}/APIKeyForm/LoadingContext.ts +0 -0
@@ -0,0 +1,214 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import {
4
+ ModelParamsMetaSchema,
5
+ ModelParamsSchema,
6
+ type RuntimeImageGenParams,
7
+ extractDefaultValues,
8
+ validateModelParamsSchema,
9
+ } from './meta-schema';
10
+
11
+ describe('meta-schema', () => {
12
+ describe('ModelParamsMetaSchema', () => {
13
+ it('should validate a complete parameter schema', () => {
14
+ const validSchema: ModelParamsSchema = {
15
+ prompt: { default: 'test prompt' },
16
+ width: { default: 1024, min: 512, max: 2048, step: 64 },
17
+ height: { default: 1024, min: 512, max: 2048, step: 64 },
18
+ steps: { default: 20, min: 1, max: 50 },
19
+ seed: { default: null, min: 0 },
20
+ cfg: { default: 7.5, min: 1, max: 20, step: 0.5 },
21
+ aspectRatio: { default: '1:1', enum: ['1:1', '16:9', '4:3'] },
22
+ size: { default: '1024x1024', enum: ['512x512', '1024x1024', '1536x1536'] },
23
+ imageUrl: { default: null },
24
+ imageUrls: { default: [] },
25
+ };
26
+
27
+ expect(() => ModelParamsMetaSchema.parse(validSchema)).not.toThrow();
28
+ });
29
+
30
+ it('should validate minimal parameter schema with only prompt', () => {
31
+ const minimalSchema: ModelParamsSchema = {
32
+ prompt: { default: '' },
33
+ };
34
+
35
+ expect(() => ModelParamsMetaSchema.parse(minimalSchema)).not.toThrow();
36
+ });
37
+
38
+ it('should apply default values for optional properties', () => {
39
+ const schema: ModelParamsSchema = {
40
+ prompt: {},
41
+ width: { default: 1024, min: 512, max: 2048 },
42
+ seed: {},
43
+ };
44
+
45
+ const result = ModelParamsMetaSchema.parse(schema);
46
+
47
+ expect(result.prompt.default).toBe('');
48
+ expect(result.width?.step).toBe(1);
49
+ expect(result.seed?.default).toBeNull();
50
+ expect(result.seed?.min).toBe(0);
51
+ });
52
+
53
+ it('should reject invalid parameter schemas', () => {
54
+ const invalidSchema = {
55
+ prompt: { default: 123 }, // Should be string
56
+ width: { min: 'invalid' }, // Should be number
57
+ };
58
+
59
+ expect(() => ModelParamsMetaSchema.parse(invalidSchema)).toThrow();
60
+ });
61
+
62
+ it('should handle optional parameters correctly', () => {
63
+ const partialSchema: ModelParamsSchema = {
64
+ prompt: { default: 'test' },
65
+ width: { default: 512, min: 256, max: 1024 },
66
+ };
67
+
68
+ const result = ModelParamsMetaSchema.parse(partialSchema);
69
+ expect(result.prompt.default).toBe('test');
70
+ expect(result.width?.default).toBe(512);
71
+ expect(result.height).toBeUndefined();
72
+ });
73
+ });
74
+
75
+ describe('validateModelParamsSchema', () => {
76
+ it('should validate correct parameter schema', () => {
77
+ const schema = {
78
+ prompt: { default: 'test' },
79
+ width: { default: 1024, min: 512, max: 2048 },
80
+ };
81
+
82
+ expect(() => validateModelParamsSchema(schema)).not.toThrow();
83
+ });
84
+
85
+ it('should throw error for invalid parameter schema', () => {
86
+ const invalidSchema = {
87
+ prompt: { default: 123 }, // Invalid type
88
+ };
89
+
90
+ expect(() => validateModelParamsSchema(invalidSchema)).toThrow();
91
+ });
92
+
93
+ it('should handle unknown properties gracefully', () => {
94
+ const schemaWithExtra = {
95
+ prompt: { default: 'test' },
96
+ unknownParam: { default: 'value' },
97
+ };
98
+
99
+ // Should not throw since schema uses passthrough
100
+ expect(() => validateModelParamsSchema(schemaWithExtra)).not.toThrow();
101
+ });
102
+ });
103
+
104
+ describe('extractDefaultValues', () => {
105
+ it('should extract default values from parameter schema', () => {
106
+ const schema: ModelParamsSchema = {
107
+ prompt: { default: 'test prompt' },
108
+ width: { default: 1024, min: 512, max: 2048 },
109
+ height: { default: 768, min: 512, max: 2048 },
110
+ steps: { default: 20, min: 1, max: 50 },
111
+ seed: { default: null },
112
+ };
113
+
114
+ const result = extractDefaultValues(schema);
115
+
116
+ expect(result).toEqual({
117
+ prompt: 'test prompt',
118
+ width: 1024,
119
+ height: 768,
120
+ steps: 20,
121
+ seed: null,
122
+ });
123
+ });
124
+
125
+ it('should apply schema defaults for missing properties', () => {
126
+ const schema: ModelParamsSchema = {
127
+ prompt: {},
128
+ width: { default: 1024, min: 512, max: 2048 },
129
+ seed: {},
130
+ };
131
+
132
+ const result = extractDefaultValues(schema);
133
+
134
+ expect(result.prompt).toBe(''); // Schema default
135
+ expect(result.width).toBe(1024);
136
+ expect(result.seed).toBeNull(); // Schema default
137
+ });
138
+
139
+ it('should handle empty schema gracefully', () => {
140
+ const schema: ModelParamsSchema = {
141
+ prompt: { default: '' },
142
+ };
143
+
144
+ const result = extractDefaultValues(schema);
145
+
146
+ expect(result).toEqual({
147
+ prompt: '',
148
+ });
149
+ });
150
+
151
+ it('should preserve all parameter types correctly', () => {
152
+ const schema: ModelParamsSchema = {
153
+ prompt: { default: 'test' },
154
+ width: { default: 1024, min: 512, max: 2048 },
155
+ seed: { default: 12345 },
156
+ cfg: { default: 7.5, min: 1, max: 20, step: 0.5 },
157
+ aspectRatio: { default: '16:9', enum: ['1:1', '16:9', '4:3'] },
158
+ imageUrls: { default: ['test.jpg'] },
159
+ imageUrl: { default: 'test.jpg' },
160
+ };
161
+
162
+ const result = extractDefaultValues(schema);
163
+
164
+ expect(typeof result.prompt).toBe('string');
165
+ expect(typeof result.width).toBe('number');
166
+ expect(typeof result.seed).toBe('number');
167
+ expect(typeof result.cfg).toBe('number');
168
+ expect(typeof result.aspectRatio).toBe('string');
169
+ expect(Array.isArray(result.imageUrls)).toBe(true);
170
+ expect(typeof result.imageUrl).toBe('string');
171
+ });
172
+
173
+ it('should handle null values properly', () => {
174
+ const schema: ModelParamsSchema = {
175
+ prompt: { default: 'test' },
176
+ seed: { default: null },
177
+ imageUrl: { default: null },
178
+ };
179
+
180
+ const result = extractDefaultValues(schema);
181
+
182
+ expect(result.seed).toBeNull();
183
+ expect(result.imageUrl).toBeNull();
184
+ });
185
+ });
186
+
187
+ describe('Type inference', () => {
188
+ it('should infer correct RuntimeImageGenParams type', () => {
189
+ // This is a compile-time test to ensure types are correctly inferred
190
+ const params: RuntimeImageGenParams = {
191
+ prompt: 'test',
192
+ width: 1024,
193
+ height: 768,
194
+ steps: 20,
195
+ seed: null,
196
+ cfg: 7.5,
197
+ };
198
+
199
+ expect(params.prompt).toBe('test');
200
+ expect(params.width).toBe(1024);
201
+ expect(params.seed).toBeNull();
202
+ });
203
+
204
+ it('should require prompt but make other parameters optional', () => {
205
+ // This test ensures prompt is required while others are optional
206
+ const minimalParams: RuntimeImageGenParams = {
207
+ prompt: 'required prompt',
208
+ };
209
+
210
+ expect(minimalParams.prompt).toBe('required prompt');
211
+ expect(minimalParams.width).toBeUndefined();
212
+ });
213
+ });
214
+ });
@@ -0,0 +1,147 @@
1
+ import type { Simplify } from 'type-fest';
2
+ import { z } from 'zod';
3
+
4
+ export const MAX_SEED = 2 ** 31 - 1;
5
+
6
+ // 定义顶层的元规范 - 平铺结构
7
+ export const ModelParamsMetaSchema = z.object({
8
+ aspectRatio: z
9
+ .object({
10
+ default: z.string(),
11
+ description: z.string().optional(),
12
+ enum: z.array(z.string()),
13
+ type: z.literal('string').optional(),
14
+ })
15
+ .optional(),
16
+
17
+ cfg: z
18
+ .object({
19
+ default: z.number(),
20
+ description: z.string().optional(),
21
+ max: z.number(),
22
+ min: z.number(),
23
+ step: z.number(),
24
+ type: z.literal('number').optional(),
25
+ })
26
+ .optional(),
27
+
28
+ height: z
29
+ .object({
30
+ default: z.number(),
31
+ description: z.string().optional(),
32
+ max: z.number(),
33
+ min: z.number(),
34
+ step: z.number().optional().default(1),
35
+ type: z.literal('number').optional(),
36
+ })
37
+ .optional(),
38
+
39
+ imageUrl: z
40
+ .object({
41
+ default: z.string().nullable().optional(),
42
+ description: z.string().optional(),
43
+ type: z.tuple([z.literal('string'), z.literal('null')]).optional(),
44
+ })
45
+ .optional(),
46
+
47
+ imageUrls: z
48
+ .object({
49
+ default: z.array(z.string()),
50
+ description: z.string().optional(),
51
+ type: z.literal('array').optional(),
52
+ })
53
+ .optional(),
54
+ /**
55
+ * Prompt 是唯一一个每个模型都有的参数
56
+ */
57
+ prompt: z.object({
58
+ default: z.string().optional().default(''),
59
+ description: z.string().optional(),
60
+ type: z.literal('string').optional(),
61
+ }),
62
+ seed: z
63
+ .object({
64
+ default: z.number().nullable().default(null),
65
+ description: z.string().optional(),
66
+ max: z.number().optional().default(MAX_SEED),
67
+ min: z.number().optional().default(0),
68
+ type: z.tuple([z.literal('number'), z.literal('null')]).optional(),
69
+ })
70
+ .optional(),
71
+ size: z
72
+ .object({
73
+ default: z.string(),
74
+ description: z.string().optional(),
75
+ enum: z.array(z.string()),
76
+ type: z.literal('string').optional(),
77
+ })
78
+ .optional(),
79
+ steps: z
80
+ .object({
81
+ default: z.number(),
82
+ description: z.string().optional(),
83
+ max: z.number(),
84
+ min: z.number(),
85
+ step: z.number().optional().default(1),
86
+ type: z.literal('number').optional(),
87
+ })
88
+ .optional(),
89
+ width: z
90
+ .object({
91
+ default: z.number(),
92
+ description: z.string().optional(),
93
+ max: z.number(),
94
+ min: z.number(),
95
+ step: z.number().optional().default(1),
96
+ type: z.literal('number').optional(),
97
+ })
98
+ .optional(),
99
+ });
100
+ // 导出推断出的类型,供定义对象使用
101
+ export type ModelParamsSchema = z.input<typeof ModelParamsMetaSchema>;
102
+ export type ModelParamsOutputSchema = z.output<typeof ModelParamsMetaSchema>;
103
+ export type ModelParamsKeys = Simplify<keyof ModelParamsOutputSchema>;
104
+
105
+ type TypeMapping<T> = T extends 'string'
106
+ ? string
107
+ : T extends 'number'
108
+ ? number
109
+ : T extends ['number', 'null']
110
+ ? number | null
111
+ : T extends ['string', 'null']
112
+ ? string | null
113
+ : T extends 'string'
114
+ ? string
115
+ : T extends 'boolean'
116
+ ? boolean
117
+ : never;
118
+ type TypeType<K extends ModelParamsKeys> = NonNullable<ModelParamsOutputSchema[K]>['type'];
119
+ type DefaultType<K extends ModelParamsKeys> = NonNullable<ModelParamsOutputSchema[K]>['default'];
120
+ type _StandardImageGenerationParameters<P extends ModelParamsKeys = ModelParamsKeys> = {
121
+ [key in P]: NonNullable<TypeType<key>> extends 'array'
122
+ ? DefaultType<key>
123
+ : TypeMapping<TypeType<key>>;
124
+ };
125
+
126
+ export type RuntimeImageGenParams = Pick<_StandardImageGenerationParameters, 'prompt'> &
127
+ Partial<Omit<_StandardImageGenerationParameters, 'prompt'>>;
128
+ export type RuntimeImageGenParamsKeys = keyof RuntimeImageGenParams;
129
+ export type RuntimeImageGenParamsValue = RuntimeImageGenParams[RuntimeImageGenParamsKeys];
130
+
131
+ // 验证函数
132
+ export function validateModelParamsSchema(paramsSchema: unknown): ModelParamsOutputSchema {
133
+ return ModelParamsMetaSchema.parse(paramsSchema);
134
+ }
135
+
136
+ /**
137
+ * 从参数定义对象提取默认值
138
+ */
139
+ export function extractDefaultValues(paramsSchema: ModelParamsSchema) {
140
+ // 部分默认值从 ModelParamsMetaSchema 中获取
141
+ const schemaWithDefault = ModelParamsMetaSchema.parse(paramsSchema);
142
+ return Object.fromEntries(
143
+ Object.entries(schemaWithDefault).map(([key, value]) => {
144
+ return [key, value.default];
145
+ }),
146
+ ) as RuntimeImageGenParams;
147
+ }
@@ -28,6 +28,7 @@ export const useClientDataSWR: SWRHook = (key, fetch, config) =>
28
28
  // we need to set it to 0.
29
29
  dedupingInterval: 0,
30
30
  focusThrottleInterval:
31
+ // FIXME: desktop 云同步模式也是走 edge 请求,也应该增大延迟
31
32
  // desktop 1.5s
32
33
  isDesktop
33
34
  ? 1500
@@ -1,25 +1,46 @@
1
1
  import { TRPCError } from '@trpc/server';
2
+ import debug from 'debug';
2
3
 
3
4
  import { serverDBEnv } from '@/config/db';
4
5
  import { UserModel } from '@/database/models/user';
5
- import { serverDB } from '@/database/server';
6
+ import { LobeChatDatabase } from '@/database/type';
6
7
 
7
8
  import { asyncTrpc } from './init';
8
9
 
10
+ const log = debug('lobe-async:auth');
11
+
9
12
  export const asyncAuth = asyncTrpc.middleware(async (opts) => {
10
13
  const { ctx } = opts;
11
14
 
15
+ log('Async auth middleware called for userId: %s', ctx.userId);
16
+ log('Secret validation: %s', ctx.secret === serverDBEnv.KEY_VAULTS_SECRET);
17
+
12
18
  if (ctx.secret !== serverDBEnv.KEY_VAULTS_SECRET || !ctx.userId) {
19
+ log('Async auth failed - invalid secret or missing userId');
20
+ log('Has secret: %s, Has userId: %s', !!ctx.secret, !!ctx.userId);
13
21
  throw new TRPCError({ code: 'UNAUTHORIZED' });
14
22
  }
15
23
 
16
- const result = await UserModel.findById(serverDB, ctx.userId);
24
+ try {
25
+ log('Looking up user in database: %s', ctx.userId);
26
+ const result = await UserModel.findById(ctx.serverDB as LobeChatDatabase, ctx.userId);
17
27
 
18
- if (!result) {
19
- throw new TRPCError({ code: 'UNAUTHORIZED', message: 'user is invalid' });
20
- }
28
+ if (!result) {
29
+ log('User not found in database: %s', ctx.userId);
30
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'user is invalid' });
31
+ }
21
32
 
22
- return opts.next({
23
- ctx: { userId: ctx.userId },
24
- });
33
+ log('User authentication successful: %s', ctx.userId);
34
+ log('Passing jwtPayload keys: %O', Object.keys(ctx.jwtPayload || {}));
35
+
36
+ return opts.next({
37
+ ctx: {
38
+ jwtPayload: ctx.jwtPayload,
39
+ userId: ctx.userId,
40
+ },
41
+ });
42
+ } catch (error) {
43
+ log('Database error during user lookup: %O', error);
44
+ throw error;
45
+ }
25
46
  });
@@ -1,11 +1,16 @@
1
+ import debug from 'debug';
1
2
  import { NextRequest } from 'next/server';
2
3
 
3
4
  import { JWTPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
5
+ import { LobeChatDatabase } from '@/database/type';
4
6
  import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
5
7
 
8
+ const log = debug('lobe-async:context');
9
+
6
10
  export interface AsyncAuthContext {
7
11
  jwtPayload: JWTPayload;
8
12
  secret: string;
13
+ serverDB?: LobeChatDatabase;
9
14
  userId?: string | null;
10
15
  }
11
16
 
@@ -28,13 +33,46 @@ export type AsyncContext = Awaited<ReturnType<typeof createAsyncContextInner>>;
28
33
  export const createAsyncRouteContext = async (request: NextRequest): Promise<AsyncContext> => {
29
34
  // for API-response caching see https://trpc.io/docs/v11/caching
30
35
 
36
+ log('Creating async route context');
37
+
31
38
  const authorization = request.headers.get('Authorization');
32
39
  const lobeChatAuthorization = request.headers.get(LOBE_CHAT_AUTH_HEADER);
33
40
 
41
+ log('Authorization header present: %s', !!authorization);
42
+ log('LobeChat auth header present: %s', !!lobeChatAuthorization);
43
+
44
+ if (!authorization) {
45
+ log('No authorization header found');
46
+ throw new Error('No authorization header found');
47
+ }
48
+
49
+ if (!lobeChatAuthorization) {
50
+ log('No LobeChat authorization header found');
51
+ throw new Error('No LobeChat authorization header found');
52
+ }
53
+
34
54
  const secret = authorization?.split(' ')[1];
35
- const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
36
- const { plaintext } = await gateKeeper.decrypt(lobeChatAuthorization || '');
55
+ log('Secret extracted from authorization header: %s', !!secret);
56
+
57
+ try {
58
+ log('Initializing KeyVaultsGateKeeper');
59
+ const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
60
+
61
+ log('Decrypting LobeChat authorization');
62
+ const { plaintext } = await gateKeeper.decrypt(lobeChatAuthorization);
63
+
64
+ log('Parsing decrypted authorization data');
65
+ const { userId, payload } = JSON.parse(plaintext);
66
+
67
+ log(
68
+ 'Successfully parsed authorization data - userId: %s, payload keys: %O',
69
+ userId,
70
+ Object.keys(payload || {}),
71
+ );
37
72
 
38
- const { userId, payload } = JSON.parse(plaintext);
39
- return createAsyncContextInner({ jwtPayload: payload, secret, userId });
73
+ return createAsyncContextInner({ jwtPayload: payload, secret, userId });
74
+ } catch (error) {
75
+ log('Error creating async route context: %O', error);
76
+ throw error;
77
+ }
40
78
  };
@@ -1,20 +1,33 @@
1
+ import debug from 'debug';
2
+
1
3
  import { getServerDB } from '@/database/core/db-adaptor';
2
4
 
3
5
  import { asyncAuth } from './asyncAuth';
4
6
  import { asyncTrpc } from './init';
5
7
 
8
+ const log = debug('lobe-async:middleware');
9
+
6
10
  export const publicProcedure = asyncTrpc.procedure;
7
11
 
8
12
  export const asyncRouter = asyncTrpc.router;
9
13
 
10
- export const asyncAuthedProcedure = asyncTrpc.procedure.use(asyncAuth).use(
11
- asyncTrpc.middleware(async (opts) => {
14
+ const dbMiddleware = asyncTrpc.middleware(async (opts) => {
15
+ log('Database middleware called');
16
+
17
+ try {
18
+ log('Getting server database connection');
12
19
  const serverDB = await getServerDB();
20
+ log('Database connection established successfully');
13
21
 
14
22
  return opts.next({
15
23
  ctx: { serverDB },
16
24
  });
17
- }),
18
- );
25
+ } catch (error) {
26
+ log('Failed to establish database connection: %O', error);
27
+ throw error;
28
+ }
29
+ });
30
+
31
+ export const asyncAuthedProcedure = asyncTrpc.procedure.use(dbMiddleware).use(asyncAuth);
19
32
 
20
33
  export const createAsyncCallerFactory = asyncTrpc.createCallerFactory;
@@ -1,11 +1,19 @@
1
1
  import { initTRPC } from '@trpc/server';
2
+ import debug from 'debug';
2
3
  import superjson from 'superjson';
3
4
 
4
5
  import { AsyncContext } from './context';
5
6
 
7
+ const log = debug('lobe-async:init');
8
+
9
+ log('Initializing async tRPC with context and superjson transformer');
10
+
6
11
  export const asyncTrpc = initTRPC.context<AsyncContext>().create({
7
12
  errorFormatter({ shape }) {
13
+ log('tRPC error formatter called: %O', shape);
8
14
  return shape;
9
15
  },
10
16
  transformer: superjson,
11
17
  });
18
+
19
+ log('Async tRPC initialized successfully');
@@ -1,5 +1,6 @@
1
1
  import { createTRPCClient, httpBatchLink } from '@trpc/client';
2
2
  import { createTRPCReact } from '@trpc/react-query';
3
+ import debug from 'debug';
3
4
  import superjson from 'superjson';
4
5
 
5
6
  import { isDesktop } from '@/const/version';
@@ -8,6 +9,8 @@ import type { LambdaRouter } from '@/server/routers/lambda';
8
9
 
9
10
  import { ErrorResponse } from './types';
10
11
 
12
+ const log = debug('lobe-image:lambda-client');
13
+
11
14
  const links = [
12
15
  httpBatchLink({
13
16
  fetch: async (input, init) => {
@@ -49,8 +52,22 @@ const links = [
49
52
  // dynamic import to avoid circular dependency
50
53
  const { createHeaderWithAuth } = await import('@/services/_auth');
51
54
 
52
- // TODO: we need to support provider select
53
- return createHeaderWithAuth({ provider: ModelProvider.OpenAI });
55
+ let provider: ModelProvider = ModelProvider.OpenAI;
56
+ // for image page, we need to get the provider from the store
57
+ log('Getting provider from store for image page: %s', location.pathname);
58
+ if (location.pathname === '/image') {
59
+ const { getImageStoreState } = await import('@/store/image');
60
+ const { imageGenerationConfigSelectors } = await import(
61
+ '@/store/image/slices/generationConfig/selectors'
62
+ );
63
+ provider = imageGenerationConfigSelectors.provider(getImageStoreState()) as ModelProvider;
64
+ log('Getting provider from store for image page: %s', provider);
65
+ }
66
+
67
+ // TODO: we need to support provider select for chat page
68
+ const headers = await createHeaderWithAuth({ provider });
69
+ log('Headers: %O', headers);
70
+ return headers;
54
71
  },
55
72
  maxURLLength: 2083,
56
73
  transformer: superjson,
@@ -186,6 +186,7 @@ export default {
186
186
  },
187
187
  fullscreen: '全屏模式',
188
188
  historyRange: '历史范围',
189
+ import: '导入',
189
190
  importData: '导入数据',
190
191
  importModal: {
191
192
  error: {
@@ -319,6 +320,7 @@ export default {
319
320
  unconnected: { tip: '信令服务器连接失败,将无法建立点对点通信频道,请检查网络后重试' },
320
321
  },
321
322
  tab: {
323
+ aiImage: 'AI 绘画',
322
324
  chat: '会话',
323
325
  discover: '发现',
324
326
  files: '文件',
@@ -70,6 +70,22 @@ export default {
70
70
  GoBack: {
71
71
  back: '返回',
72
72
  },
73
+ ImageUpload: {
74
+ actions: {
75
+ changeImage: '点击更换图片',
76
+ },
77
+ placeholder: {
78
+ primary: '添加图片',
79
+ secondary: '点击上传',
80
+ },
81
+ },
82
+ KeyValueEditor: {
83
+ addButton: '新增一行',
84
+ deleteTooltip: '删除',
85
+ duplicateKeyError: '键名必须唯一',
86
+ keyPlaceholder: '键',
87
+ valuePlaceholder: '值',
88
+ },
73
89
  MaxTokenSlider: {
74
90
  unlimited: '无限制',
75
91
  },
@@ -93,6 +109,25 @@ export default {
93
109
  provider: '服务商',
94
110
  title: '模型',
95
111
  },
112
+ MultiImagesUpload: {
113
+ actions: {
114
+ uploadMore: '点击上传更多',
115
+ },
116
+ modal: {
117
+ complete: '完成',
118
+ newFileIndicator: '新增',
119
+ selectImageToPreview: '请选择要预览的图片',
120
+ title: '管理图片 ({{count}})',
121
+ upload: '上传图片',
122
+ },
123
+ placeholder: {
124
+ primary: '点击上传图片',
125
+ secondary: '支持多张图片选择',
126
+ },
127
+ progress: {
128
+ uploadingWithCount: '{{completed}}/{{total}} 已上传',
129
+ },
130
+ },
96
131
  OllamaSetupGuide: {
97
132
  action: {
98
133
  close: '关闭提示',
@@ -151,6 +151,7 @@ export default {
151
151
  stt: {
152
152
  responseError: '服务请求失败,请检查配置或重试',
153
153
  },
154
+ testConnectionFailed: '测试连接失败:{{error}}',
154
155
  tts: {
155
156
  responseError: '服务请求失败,请检查配置或重试',
156
157
  },
@@ -158,6 +159,7 @@ export default {
158
159
  addProxyUrl: '添加 OpenAI 代理地址(可选)',
159
160
  apiKey: {
160
161
  description: '输入你的 {{name}} API Key 即可开始会话',
162
+ imageGenerationDescription: '输入你的 {{name}} API Key 即可开始生成',
161
163
  title: '使用自定义 {{name}} API Key',
162
164
  },
163
165
  closeMessage: '关闭提示',