@lobehub/lobehub 2.0.0-next.46 → 2.0.0-next.48

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 (254) hide show
  1. package/.env.example +11 -0
  2. package/CHANGELOG.md +42 -0
  3. package/apps/desktop/src/main/controllers/AuthCtr.ts +27 -2
  4. package/apps/desktop/src/main/core/infrastructure/ProtocolManager.ts +9 -4
  5. package/changelog/v1.json +14 -0
  6. package/docs/development/database-schema.dbml +2 -0
  7. package/docs/self-hosting/environment-variables/basic.mdx +49 -3
  8. package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +49 -4
  9. package/locales/ar/discover.json +45 -0
  10. package/locales/ar/marketAuth.json +42 -0
  11. package/locales/ar/setting.json +94 -1
  12. package/locales/bg-BG/discover.json +45 -0
  13. package/locales/bg-BG/marketAuth.json +42 -0
  14. package/locales/bg-BG/setting.json +94 -1
  15. package/locales/de-DE/discover.json +45 -0
  16. package/locales/de-DE/marketAuth.json +42 -0
  17. package/locales/de-DE/setting.json +94 -1
  18. package/locales/en-US/discover.json +45 -0
  19. package/locales/en-US/marketAuth.json +42 -0
  20. package/locales/en-US/setting.json +94 -1
  21. package/locales/es-ES/discover.json +45 -0
  22. package/locales/es-ES/marketAuth.json +42 -0
  23. package/locales/es-ES/setting.json +94 -1
  24. package/locales/fa-IR/discover.json +45 -0
  25. package/locales/fa-IR/marketAuth.json +42 -0
  26. package/locales/fa-IR/setting.json +94 -1
  27. package/locales/fr-FR/discover.json +45 -0
  28. package/locales/fr-FR/marketAuth.json +42 -0
  29. package/locales/fr-FR/setting.json +94 -1
  30. package/locales/it-IT/discover.json +45 -0
  31. package/locales/it-IT/marketAuth.json +42 -0
  32. package/locales/it-IT/setting.json +94 -1
  33. package/locales/ja-JP/discover.json +45 -0
  34. package/locales/ja-JP/marketAuth.json +42 -0
  35. package/locales/ja-JP/setting.json +94 -1
  36. package/locales/ko-KR/discover.json +45 -0
  37. package/locales/ko-KR/marketAuth.json +42 -0
  38. package/locales/ko-KR/setting.json +94 -1
  39. package/locales/nl-NL/discover.json +45 -0
  40. package/locales/nl-NL/marketAuth.json +42 -0
  41. package/locales/nl-NL/setting.json +94 -1
  42. package/locales/pl-PL/discover.json +45 -0
  43. package/locales/pl-PL/marketAuth.json +42 -0
  44. package/locales/pl-PL/setting.json +94 -1
  45. package/locales/pt-BR/discover.json +45 -0
  46. package/locales/pt-BR/marketAuth.json +42 -0
  47. package/locales/pt-BR/setting.json +94 -1
  48. package/locales/ru-RU/discover.json +45 -0
  49. package/locales/ru-RU/marketAuth.json +42 -0
  50. package/locales/ru-RU/setting.json +94 -1
  51. package/locales/tr-TR/discover.json +45 -0
  52. package/locales/tr-TR/marketAuth.json +42 -0
  53. package/locales/tr-TR/setting.json +94 -1
  54. package/locales/vi-VN/discover.json +45 -0
  55. package/locales/vi-VN/marketAuth.json +42 -0
  56. package/locales/vi-VN/setting.json +94 -1
  57. package/locales/zh-CN/discover.json +45 -0
  58. package/locales/zh-CN/marketAuth.json +42 -0
  59. package/locales/zh-CN/setting.json +94 -1
  60. package/locales/zh-TW/discover.json +45 -0
  61. package/locales/zh-TW/marketAuth.json +42 -0
  62. package/locales/zh-TW/setting.json +94 -1
  63. package/package.json +27 -26
  64. package/packages/const/src/url.ts +1 -0
  65. package/packages/database/migrations/0044_add_tool_intervention.sql +1 -0
  66. package/packages/database/migrations/0044_high_toxin.sql +1 -0
  67. package/packages/database/migrations/0045_add_tool_intervention.sql +1 -0
  68. package/packages/database/migrations/meta/0039_snapshot.json +1 -1
  69. package/packages/database/migrations/meta/0044_snapshot.json +7813 -0
  70. package/packages/database/migrations/meta/0045_snapshot.json +8431 -0
  71. package/packages/database/migrations/meta/_journal.json +14 -0
  72. package/packages/database/src/core/migrations.json +36 -7
  73. package/packages/database/src/models/file.ts +15 -1
  74. package/packages/database/src/models/message.ts +1 -1
  75. package/packages/database/src/models/session.ts +42 -1
  76. package/packages/database/src/repositories/aiInfra/index.test.ts +1 -1
  77. package/packages/database/src/repositories/dataExporter/index.test.ts +1 -1
  78. package/packages/database/src/repositories/tableViewer/index.test.ts +1 -1
  79. package/packages/database/src/schemas/agent.ts +1 -0
  80. package/packages/database/src/schemas/message.ts +5 -8
  81. package/packages/electron-client-ipc/src/events/index.ts +6 -1
  82. package/packages/electron-client-ipc/src/events/remoteServer.ts +8 -0
  83. package/packages/fetch-sse/package.json +29 -0
  84. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/fetchSSE.test.ts +4 -4
  85. package/packages/{utils/src/fetch → fetch-sse/src}/__tests__/parseError.test.ts +7 -4
  86. package/packages/{utils/src/fetch → fetch-sse/src}/fetchSSE.ts +2 -2
  87. package/packages/{utils/src/fetch → fetch-sse/src}/parseError.ts +3 -3
  88. package/packages/model-bank/src/aiModels/mistral.ts +2 -1
  89. package/packages/model-runtime/src/core/contextBuilders/anthropic.test.ts +17 -11
  90. package/packages/model-runtime/src/core/contextBuilders/anthropic.ts +1 -1
  91. package/packages/model-runtime/src/core/contextBuilders/google.test.ts +1 -1
  92. package/packages/model-runtime/src/core/contextBuilders/google.ts +3 -6
  93. package/packages/model-runtime/src/core/contextBuilders/openai.test.ts +4 -2
  94. package/packages/model-runtime/src/core/contextBuilders/openai.ts +1 -1
  95. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.test.ts +1 -1
  96. package/packages/model-runtime/src/core/openaiCompatibleFactory/createImage.ts +1 -1
  97. package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +3 -6
  98. package/packages/model-runtime/src/core/streams/openai/responsesStream.test.ts +1 -1
  99. package/packages/model-runtime/src/helpers/mergeChatMethodOptions.ts +2 -1
  100. package/packages/model-runtime/src/providers/aihubmix/index.test.ts +1 -1
  101. package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +1 -1
  102. package/packages/model-runtime/src/providers/anthropic/index.test.ts +1 -1
  103. package/packages/model-runtime/src/providers/baichuan/index.test.ts +1 -1
  104. package/packages/model-runtime/src/providers/bedrock/index.test.ts +1 -1
  105. package/packages/model-runtime/src/providers/bfl/createImage.test.ts +4 -4
  106. package/packages/model-runtime/src/providers/bfl/createImage.ts +1 -1
  107. package/packages/model-runtime/src/providers/cloudflare/index.test.ts +1 -1
  108. package/packages/model-runtime/src/providers/cohere/index.test.ts +1 -1
  109. package/packages/model-runtime/src/providers/google/createImage.test.ts +2 -2
  110. package/packages/model-runtime/src/providers/google/createImage.ts +1 -1
  111. package/packages/model-runtime/src/providers/google/generateObject.test.ts +1 -1
  112. package/packages/model-runtime/src/providers/google/index.test.ts +1 -4
  113. package/packages/model-runtime/src/providers/groq/index.test.ts +1 -1
  114. package/packages/model-runtime/src/providers/hunyuan/index.test.ts +1 -1
  115. package/packages/model-runtime/src/providers/minimax/createImage.test.ts +1 -1
  116. package/packages/model-runtime/src/providers/mistral/index.test.ts +1 -1
  117. package/packages/model-runtime/src/providers/moonshot/index.test.ts +1 -1
  118. package/packages/model-runtime/src/providers/novita/index.test.ts +1 -1
  119. package/packages/model-runtime/src/providers/ollama/index.test.ts +43 -32
  120. package/packages/model-runtime/src/providers/ollama/index.ts +31 -7
  121. package/packages/model-runtime/src/providers/openrouter/index.test.ts +1 -1
  122. package/packages/model-runtime/src/providers/perplexity/index.test.ts +1 -1
  123. package/packages/model-runtime/src/providers/ppio/index.test.ts +1 -1
  124. package/packages/model-runtime/src/providers/qwen/createImage.test.ts +1 -1
  125. package/packages/model-runtime/src/providers/search1api/index.test.ts +1 -1
  126. package/packages/model-runtime/src/providers/siliconcloud/createImage.ts +1 -1
  127. package/packages/model-runtime/src/providers/taichu/index.test.ts +1 -1
  128. package/packages/model-runtime/src/providers/wenxin/index.test.ts +1 -1
  129. package/packages/model-runtime/src/providers/zhipu/index.test.ts +1 -1
  130. package/packages/model-runtime/src/utils/errorResponse.test.ts +1 -1
  131. package/packages/ssrf-safe-fetch/index.browser.ts +14 -0
  132. package/packages/ssrf-safe-fetch/package.json +8 -1
  133. package/packages/types/src/aiProvider.ts +1 -1
  134. package/packages/types/src/discover/assistants.ts +16 -0
  135. package/packages/types/src/document/index.ts +38 -38
  136. package/packages/types/src/exportConfig.ts +15 -15
  137. package/packages/types/src/generation/index.ts +5 -5
  138. package/packages/types/src/index.ts +1 -0
  139. package/packages/types/src/message/common/tools.ts +10 -0
  140. package/packages/types/src/message/db/item.ts +15 -1
  141. package/packages/types/src/message/ui/params.ts +15 -1
  142. package/packages/types/src/meta.ts +4 -0
  143. package/packages/types/src/openai/chat.ts +15 -15
  144. package/packages/types/src/plugins/mcp.ts +29 -29
  145. package/packages/types/src/plugins/protocol.ts +43 -43
  146. package/packages/types/src/search.ts +4 -4
  147. package/packages/types/src/session/agentSession.ts +2 -0
  148. package/packages/types/src/tool/plugin.ts +3 -3
  149. package/packages/utils/src/imageToBase64.ts +17 -10
  150. package/packages/utils/src/index.ts +1 -1
  151. package/src/app/(backend)/f/[id]/route.ts +55 -0
  152. package/src/app/(backend)/market/agent/[[...segments]]/route.ts +153 -0
  153. package/src/app/(backend)/market/oidc/[[...segments]]/route.ts +207 -0
  154. package/src/app/[variants]/(main)/(mobile)/me/settings/features/useCategory.tsx +1 -0
  155. package/src/app/[variants]/(main)/_layout/Desktop/SideBar/PinList/index.tsx +4 -2
  156. package/src/app/[variants]/(main)/chat/settings/features/AgentInfoDescription/index.tsx +349 -0
  157. package/src/app/[variants]/(main)/chat/settings/features/HeaderContent.tsx +2 -2
  158. package/src/app/[variants]/(main)/chat/settings/features/PublishResultModal/index.tsx +64 -0
  159. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishButton.tsx +196 -0
  160. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/MarketPublishModal.tsx +358 -0
  161. package/src/app/[variants]/(main)/chat/settings/features/SmartAgentActionButton/index.tsx +75 -0
  162. package/src/app/[variants]/(main)/discover/(detail)/assistant/AssistantDetailPage.tsx +11 -2
  163. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/Client.tsx +12 -1
  164. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Nav.tsx +19 -12
  165. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/TagList.tsx +14 -5
  166. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Overview/index.tsx +2 -0
  167. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Related/index.tsx +14 -5
  168. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/TagList.tsx +14 -5
  169. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/SystemRole/index.tsx +43 -29
  170. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/Versions/index.tsx +137 -0
  171. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Details/index.tsx +2 -0
  172. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Header.tsx +9 -10
  173. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/ActionButton/AddAgent.tsx +105 -14
  174. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/Sidebar/Related/index.tsx +20 -6
  175. package/src/app/[variants]/(main)/discover/(detail)/assistant/[...slugs]/features/StatusPage/index.tsx +113 -0
  176. package/src/app/[variants]/(main)/discover/(detail)/features/Breadcrumb.tsx +4 -3
  177. package/src/app/[variants]/(main)/discover/(list)/_layout/Desktop/Nav.tsx +3 -1
  178. package/src/app/[variants]/(main)/discover/(list)/assistant/AssistantPage.tsx +4 -1
  179. package/src/app/[variants]/(main)/discover/(list)/assistant/Client.tsx +6 -2
  180. package/src/app/[variants]/(main)/discover/(list)/assistant/features/Category/index.tsx +7 -3
  181. package/src/app/[variants]/(main)/discover/(list)/assistant/features/List/Item.tsx +13 -2
  182. package/src/app/[variants]/(main)/discover/(list)/assistant/features/MarketSourceSwitch.tsx +64 -0
  183. package/src/app/[variants]/(main)/discover/(list)/features/SortButton/index.tsx +26 -7
  184. package/src/app/[variants]/(main)/profile/_layout/Desktop/index.tsx +10 -10
  185. package/src/app/[variants]/(main)/settings/_layout/type.ts +1 -1
  186. package/src/app/[variants]/(main)/settings/agent/index.tsx +11 -10
  187. package/src/app/[variants]/(main)/settings/common/index.tsx +1 -1
  188. package/src/app/[variants]/(main)/settings/page.tsx +13 -10
  189. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/Item.tsx +35 -36
  190. package/src/app/[variants]/(main)/settings/provider/ProviderMenu/SearchResult.tsx +5 -5
  191. package/src/app/[variants]/(main)/settings/provider/_layout/Desktop/Container.tsx +10 -4
  192. package/src/app/market-auth-callback/layout.tsx +15 -0
  193. package/src/app/market-auth-callback/page.tsx +196 -0
  194. package/src/envs/app.ts +4 -3
  195. package/src/features/AgentSetting/AgentPrompt/TokenTag.tsx +3 -2
  196. package/src/features/AgentSetting/AgentTTS/SelectWithTTSPreview.tsx +1 -1
  197. package/src/features/AgentSetting/store/action.ts +1 -1
  198. package/src/features/ChatInput/ActionBar/STT/browser.tsx +1 -1
  199. package/src/features/ChatInput/ActionBar/STT/openai.tsx +1 -1
  200. package/src/features/Conversation/components/Extras/TTS/InitPlayer.tsx +1 -1
  201. package/src/features/Conversation/components/VirtualizedList/index.tsx +2 -1
  202. package/src/features/PluginTag/PluginStatus.tsx +1 -1
  203. package/src/features/PluginsUI/Render/MCPType/index.tsx +26 -6
  204. package/src/hooks/useAgentOwnershipCheck.ts +143 -0
  205. package/src/instrumentation.node.ts +3 -2
  206. package/src/layout/AuthProvider/MarketAuth/MarketAuthProvider.tsx +364 -0
  207. package/src/layout/AuthProvider/MarketAuth/errors.ts +75 -0
  208. package/src/layout/AuthProvider/MarketAuth/index.ts +2 -0
  209. package/src/layout/AuthProvider/MarketAuth/oidc.ts +382 -0
  210. package/src/layout/AuthProvider/MarketAuth/types.ts +64 -0
  211. package/src/layout/AuthProvider/index.tsx +17 -4
  212. package/src/locales/default/discover.ts +46 -0
  213. package/src/locales/default/index.ts +2 -0
  214. package/src/locales/default/marketAuth.ts +42 -0
  215. package/src/locales/default/setting.ts +94 -1
  216. package/src/server/globalConfig/genServerAiProviderConfig.test.ts +5 -5
  217. package/src/server/globalConfig/genServerAiProviderConfig.ts +1 -1
  218. package/src/server/routers/desktop/mcp.ts +23 -8
  219. package/src/server/routers/lambda/market/index.ts +36 -14
  220. package/src/server/routers/lambda/message.ts +2 -2
  221. package/src/server/routers/tools/mcp.ts +24 -4
  222. package/src/server/services/discover/index.test.ts +153 -11
  223. package/src/server/services/discover/index.ts +339 -40
  224. package/src/server/services/file/impls/local.ts +4 -1
  225. package/src/server/services/file/index.ts +96 -1
  226. package/src/server/services/mcp/contentProcessor.ts +101 -0
  227. package/src/server/services/mcp/index.test.ts +52 -10
  228. package/src/server/services/mcp/index.ts +29 -26
  229. package/src/server/sitemap.ts +49 -35
  230. package/src/services/_url.ts +15 -1
  231. package/src/services/chat/chat.test.ts +5 -5
  232. package/src/services/chat/clientModelRuntime.test.ts +1 -1
  233. package/src/services/chat/index.ts +6 -6
  234. package/src/services/chat/types.ts +1 -2
  235. package/src/services/discover.ts +16 -5
  236. package/src/services/electron/remoteServer.ts +8 -1
  237. package/src/services/marketApi.ts +124 -0
  238. package/src/services/models.ts +2 -1
  239. package/src/services/session/index.ts +0 -14
  240. package/src/store/discover/slices/assistant/action.ts +20 -7
  241. package/{packages/utils/src → src/utils}/electron/desktopRemoteRPCFetch.ts +1 -1
  242. package/{packages/utils/src → src/utils/server}/parseModels.ts +1 -2
  243. package/src/utils/server/routeVariants.test.ts +340 -0
  244. package/vitest.config.mts +2 -0
  245. package/packages/model-runtime/src/utils/imageToBase64.test.ts +0 -91
  246. package/packages/model-runtime/src/utils/imageToBase64.ts +0 -62
  247. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/SubmitAgentModal.tsx +0 -98
  248. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/index.tsx +0 -35
  249. package/src/app/[variants]/(main)/chat/settings/features/SubmitAgentButton/style.ts +0 -47
  250. /package/packages/{utils/src/fetch → fetch-sse/src}/headers.ts +0 -0
  251. /package/packages/{utils/src/fetch → fetch-sse/src}/index.ts +0 -0
  252. /package/packages/{utils/src/fetch → fetch-sse/src}/request.ts +0 -0
  253. /package/{packages/utils/src → src/utils/server}/__snapshots__/parseModels.test.ts.snap +0 -0
  254. /package/{packages/utils/src → src/utils/server}/parseModels.test.ts +0 -0
@@ -0,0 +1,124 @@
1
+ import { AgentItemDetail } from '@lobehub/market-sdk';
2
+
3
+ import { MARKET_ENDPOINTS } from '@/services/_url';
4
+
5
+ export class MarketApiService {
6
+ private accessToken?: string;
7
+
8
+ // eslint-disable-next-line no-undef
9
+ private async request<T>(endpoint: string, init?: RequestInit): Promise<T> {
10
+ const headers = new Headers(init?.headers);
11
+
12
+ if (init?.body && !headers.has('content-type')) {
13
+ headers.set('content-type', 'application/json');
14
+ }
15
+
16
+ if (this.accessToken && !headers.has('authorization')) {
17
+ headers.set('authorization', `Bearer ${this.accessToken}`);
18
+ }
19
+
20
+ const response = await fetch(endpoint, {
21
+ ...init,
22
+ credentials: init?.credentials ?? 'same-origin',
23
+ headers,
24
+ });
25
+
26
+ if (!response.ok) {
27
+ let message = 'Unknown error';
28
+
29
+ try {
30
+ const errorBody = await response.json();
31
+ message = errorBody?.message ?? message;
32
+ } catch {
33
+ message = await response.text();
34
+ }
35
+
36
+ throw new Error(message || 'Market request failed');
37
+ }
38
+
39
+ if (response.status === 204) {
40
+ return undefined as T;
41
+ }
42
+
43
+ return (await response.json()) as T;
44
+ }
45
+
46
+ setAccessToken(token: string) {
47
+ this.accessToken = token;
48
+ }
49
+ // Create new agent
50
+ async createAgent(agentData: {
51
+ homepage?: string;
52
+ identifier: string;
53
+ isFeatured?: boolean;
54
+ name: string;
55
+ status?: 'published' | 'unpublished' | 'archived' | 'deprecated';
56
+ tokenUsage?: number;
57
+ visibility?: 'public' | 'private' | 'internal';
58
+ }): Promise<AgentItemDetail> {
59
+ return this.request(MARKET_ENDPOINTS.createAgent, {
60
+ body: JSON.stringify(agentData),
61
+ method: 'POST',
62
+ });
63
+ }
64
+
65
+ // Get agent detail by identifier
66
+ async getAgentDetail(identifier: string): Promise<AgentItemDetail> {
67
+ return this.request(MARKET_ENDPOINTS.getAgentDetail(identifier), {
68
+ method: 'GET',
69
+ });
70
+ }
71
+
72
+ // Check if agent exists (returns true if exists, false if not)
73
+ async checkAgentExists(identifier: string): Promise<boolean> {
74
+ try {
75
+ await this.getAgentDetail(identifier);
76
+ return true;
77
+ } catch {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ // Create agent version
83
+ async createAgentVersion(versionData: {
84
+ a2aProtocolVersion?: string;
85
+ avatar?: string;
86
+ category?: string;
87
+ changelog?: string;
88
+ config?: Record<string, any>;
89
+ defaultInputModes?: string[];
90
+ defaultOutputModes?: string[];
91
+ description?: string;
92
+ documentationUrl?: string;
93
+ extensions?: Record<string, any>[];
94
+ hasPushNotifications?: boolean;
95
+ hasStateTransitionHistory?: boolean;
96
+ hasStreaming?: boolean;
97
+ identifier: string;
98
+ interfaces?: Record<string, any>[];
99
+ name?: string;
100
+ preferredTransport?: string;
101
+ providerId?: number;
102
+ securityRequirements?: Record<string, any>[];
103
+ securitySchemes?: Record<string, any>;
104
+ setAsCurrent?: boolean;
105
+ summary?: string;
106
+ supportsAuthenticatedExtendedCard?: boolean;
107
+ tokenUsage?: number;
108
+ url?: string;
109
+ }): Promise<AgentItemDetail> {
110
+ const { identifier, ...rest } = versionData;
111
+ const targetIdentifier = identifier;
112
+ if (!targetIdentifier) throw new Error('Identifier is required');
113
+
114
+ return this.request(MARKET_ENDPOINTS.createAgentVersion, {
115
+ body: JSON.stringify({
116
+ identifier: targetIdentifier,
117
+ ...rest,
118
+ }),
119
+ method: 'POST',
120
+ });
121
+ }
122
+ }
123
+
124
+ export const marketApiService = new MarketApiService();
@@ -1,7 +1,8 @@
1
+ import { getMessageError } from '@lobechat/fetch-sse';
2
+
1
3
  import { createHeaderWithAuth } from '@/services/_auth';
2
4
  import { aiProviderSelectors, getAiInfraStoreState } from '@/store/aiInfra';
3
5
  import { ChatModelCard } from '@/types/llm';
4
- import { getMessageError } from '@/utils/fetch';
5
6
 
6
7
  import { API_ENDPOINTS } from './_url';
7
8
  import { initializeWithClientStore } from './chat/clientModelRuntime';
@@ -4,14 +4,12 @@ import type { PartialDeep } from 'type-fest';
4
4
  import { lambdaClient } from '@/libs/trpc/client';
5
5
  import { LobeAgentChatConfig, LobeAgentConfig } from '@/types/agent';
6
6
  import { MetaData } from '@/types/meta';
7
- import { BatchTaskResult } from '@/types/service';
8
7
  import {
9
8
  ChatSessionList,
10
9
  LobeAgentSession,
11
10
  LobeSessionType,
12
11
  LobeSessions,
13
12
  SessionGroupItem,
14
- SessionGroups,
15
13
  SessionRankItem,
16
14
  UpdateSessionParams,
17
15
  } from '@/types/session';
@@ -114,18 +112,6 @@ export class SessionService {
114
112
  return lambdaClient.sessionGroup.createSessionGroup.mutate({ name, sort });
115
113
  };
116
114
 
117
- getSessionGroups = (): Promise<SessionGroupItem[]> => {
118
- return lambdaClient.sessionGroup.getSessionGroup.query();
119
- };
120
-
121
- /**
122
- * 需要废弃
123
- * @deprecated
124
- */
125
- batchCreateSessionGroups = (groups: SessionGroups): Promise<BatchTaskResult> => {
126
- return Promise.resolve({ added: 0, ids: [], skips: [], success: true });
127
- };
128
-
129
115
  removeSessionGroup = (id: string, removeChildren?: boolean) => {
130
116
  return lambdaClient.sessionGroup.removeSessionGroup.mutate({ id, removeChildren });
131
117
  };
@@ -7,17 +7,24 @@ import { DiscoverStore } from '@/store/discover';
7
7
  import { globalHelpers } from '@/store/global/helpers';
8
8
  import {
9
9
  AssistantListResponse,
10
+ AssistantMarketSource,
10
11
  AssistantQueryParams,
11
12
  DiscoverAssistantDetail,
12
13
  IdentifiersResponse,
13
14
  } from '@/types/discover';
14
15
 
15
16
  export interface AssistantAction {
16
- useAssistantCategories: (params: CategoryListQuery) => SWRResponse<CategoryItem[]>;
17
+ useAssistantCategories: (
18
+ params: CategoryListQuery & { source?: AssistantMarketSource },
19
+ ) => SWRResponse<CategoryItem[]>;
17
20
  useAssistantDetail: (params: {
18
21
  identifier: string;
22
+ source?: AssistantMarketSource;
23
+ version?: string;
19
24
  }) => SWRResponse<DiscoverAssistantDetail | undefined>;
20
- useAssistantIdentifiers: () => SWRResponse<IdentifiersResponse>;
25
+ useAssistantIdentifiers: (params?: {
26
+ source?: AssistantMarketSource;
27
+ }) => SWRResponse<IdentifiersResponse>;
21
28
  useAssistantList: (params?: AssistantQueryParams) => SWRResponse<AssistantListResponse>;
22
29
  }
23
30
 
@@ -41,7 +48,9 @@ export const createAssistantSlice: StateCreator<
41
48
  useAssistantDetail: (params) => {
42
49
  const locale = globalHelpers.getCurrentLanguage();
43
50
  return useSWR(
44
- ['assistant-details', locale, params.identifier].filter(Boolean).join('-'),
51
+ ['assistant-details', locale, params.identifier, params.version, params.source]
52
+ .filter(Boolean)
53
+ .join('-'),
45
54
  async () => discoverService.getAssistantDetail(params),
46
55
  {
47
56
  revalidateOnFocus: false,
@@ -49,10 +58,14 @@ export const createAssistantSlice: StateCreator<
49
58
  );
50
59
  },
51
60
 
52
- useAssistantIdentifiers: () => {
53
- return useSWR('assistant-identifiers', async () => discoverService.getAssistantIdentifiers(), {
54
- revalidateOnFocus: false,
55
- });
61
+ useAssistantIdentifiers: (params) => {
62
+ return useSWR(
63
+ ['assistant-identifiers', params?.source].filter(Boolean).join('-') || 'assistant-identifiers',
64
+ async () => discoverService.getAssistantIdentifiers(params),
65
+ {
66
+ revalidateOnFocus: false,
67
+ },
68
+ );
56
69
  },
57
70
 
58
71
  useAssistantList: (params = {}) => {
@@ -1,10 +1,10 @@
1
1
  import { isDesktop } from '@lobechat/const';
2
2
  import { ProxyTRPCRequestParams, dispatch, streamInvoke } from '@lobechat/electron-client-ipc';
3
+ import { getRequestBody, headersToRecord } from '@lobechat/fetch-sse';
3
4
  import debug from 'debug';
4
5
 
5
6
  import { getElectronStoreState } from '@/store/electron';
6
7
  import { electronSyncSelectors } from '@/store/electron/selectors';
7
- import { getRequestBody, headersToRecord } from '@/utils/fetch';
8
8
 
9
9
  const log = debug('utils:desktopRemoteRPCFetch');
10
10
 
@@ -1,9 +1,8 @@
1
1
  import { getModelPropertyWithFallback } from '@lobechat/model-runtime';
2
+ import { merge } from '@lobechat/utils';
2
3
  import { produce } from 'immer';
3
4
  import { AiFullModelCard, AiModelType } from 'model-bank';
4
5
 
5
- import { merge } from './merge';
6
-
7
6
  /**
8
7
  * Parse model string to add or remove models.
9
8
  */
@@ -0,0 +1,340 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { DEFAULT_LANG } from '@/const/locale';
4
+ import { DynamicLayoutProps } from '@/types/next';
5
+
6
+ import { DEFAULT_VARIANTS, IRouteVariants, RouteVariants } from './routeVariants';
7
+
8
+ describe('RouteVariants', () => {
9
+ describe('DEFAULT_VARIANTS', () => {
10
+ it('should have correct default values', () => {
11
+ expect(DEFAULT_VARIANTS).toEqual({
12
+ isMobile: false,
13
+ locale: DEFAULT_LANG,
14
+ theme: 'light',
15
+ });
16
+ });
17
+ });
18
+
19
+ describe('serializeVariants', () => {
20
+ it('should serialize variants with default values', () => {
21
+ const variants: IRouteVariants = {
22
+ isMobile: false,
23
+ locale: 'en-US',
24
+ theme: 'light',
25
+ };
26
+ const result = RouteVariants.serializeVariants(variants);
27
+ expect(result).toBe('en-US__0__light');
28
+ });
29
+
30
+ it('should serialize variants with mobile enabled', () => {
31
+ const variants: IRouteVariants = {
32
+ isMobile: true,
33
+ locale: 'zh-CN',
34
+ theme: 'dark',
35
+ };
36
+ const result = RouteVariants.serializeVariants(variants);
37
+ expect(result).toBe('zh-CN__1__dark');
38
+ });
39
+
40
+ it('should serialize variants with different locales', () => {
41
+ const variants: IRouteVariants = {
42
+ isMobile: false,
43
+ locale: 'ja-JP',
44
+ theme: 'light',
45
+ };
46
+ const result = RouteVariants.serializeVariants(variants);
47
+ expect(result).toBe('ja-JP__0__light');
48
+ });
49
+
50
+ it('should serialize variants with custom colors', () => {
51
+ const variants: IRouteVariants = {
52
+ isMobile: true,
53
+ locale: 'en-US',
54
+ neutralColor: '#cccccc',
55
+ primaryColor: '#ff0000',
56
+ theme: 'dark',
57
+ };
58
+ const result = RouteVariants.serializeVariants(variants);
59
+ expect(result).toBe('en-US__1__dark');
60
+ });
61
+ });
62
+
63
+ describe('deserializeVariants', () => {
64
+ it('should deserialize valid serialized string', () => {
65
+ const serialized = 'en-US__0__light';
66
+ const result = RouteVariants.deserializeVariants(serialized);
67
+ expect(result).toEqual({
68
+ isMobile: false,
69
+ locale: 'en-US',
70
+ theme: 'light',
71
+ });
72
+ });
73
+
74
+ it('should deserialize mobile variants', () => {
75
+ const serialized = 'zh-CN__1__dark';
76
+ const result = RouteVariants.deserializeVariants(serialized);
77
+ expect(result).toEqual({
78
+ isMobile: true,
79
+ locale: 'zh-CN',
80
+ theme: 'dark',
81
+ });
82
+ });
83
+
84
+ it('should return default values for invalid serialized string', () => {
85
+ const serialized = 'invalid';
86
+ const result = RouteVariants.deserializeVariants(serialized);
87
+ expect(result).toEqual(DEFAULT_VARIANTS);
88
+ });
89
+
90
+ it('should return default values for empty string', () => {
91
+ const serialized = '';
92
+ const result = RouteVariants.deserializeVariants(serialized);
93
+ expect(result).toEqual(DEFAULT_VARIANTS);
94
+ });
95
+
96
+ it('should handle invalid locale by falling back to default', () => {
97
+ const serialized = 'invalid-locale__0__light';
98
+ const result = RouteVariants.deserializeVariants(serialized);
99
+ expect(result).toEqual({
100
+ isMobile: false,
101
+ locale: DEFAULT_LANG,
102
+ theme: 'light',
103
+ });
104
+ });
105
+
106
+ it('should handle invalid theme by falling back to default', () => {
107
+ const serialized = 'en-US__0__invalid-theme';
108
+ const result = RouteVariants.deserializeVariants(serialized);
109
+ expect(result).toEqual({
110
+ isMobile: false,
111
+ locale: 'en-US',
112
+ theme: 'light',
113
+ });
114
+ });
115
+
116
+ it('should handle malformed serialized string', () => {
117
+ const serialized = 'en-US';
118
+ const result = RouteVariants.deserializeVariants(serialized);
119
+ expect(result).toEqual(DEFAULT_VARIANTS);
120
+ });
121
+
122
+ it('should handle isMobile value correctly for "0"', () => {
123
+ const serialized = 'en-US__0__dark';
124
+ const result = RouteVariants.deserializeVariants(serialized);
125
+ expect(result.isMobile).toBe(false);
126
+ });
127
+
128
+ it('should handle isMobile value correctly for "1"', () => {
129
+ const serialized = 'en-US__1__dark';
130
+ const result = RouteVariants.deserializeVariants(serialized);
131
+ expect(result.isMobile).toBe(true);
132
+ });
133
+
134
+ it('should handle isMobile value correctly for other values', () => {
135
+ const serialized = 'en-US__2__dark';
136
+ const result = RouteVariants.deserializeVariants(serialized);
137
+ expect(result.isMobile).toBe(false);
138
+ });
139
+ });
140
+
141
+ describe('getVariantsFromProps', () => {
142
+ it('should extract and deserialize variants from props', async () => {
143
+ const props: DynamicLayoutProps = {
144
+ params: Promise.resolve({ variants: 'en-US__0__light' }),
145
+ };
146
+ const result = await RouteVariants.getVariantsFromProps(props);
147
+ expect(result).toEqual({
148
+ isMobile: false,
149
+ locale: 'en-US',
150
+ theme: 'light',
151
+ });
152
+ });
153
+
154
+ it('should handle mobile variants from props', async () => {
155
+ const props: DynamicLayoutProps = {
156
+ params: Promise.resolve({ variants: 'zh-CN__1__dark' }),
157
+ };
158
+ const result = await RouteVariants.getVariantsFromProps(props);
159
+ expect(result).toEqual({
160
+ isMobile: true,
161
+ locale: 'zh-CN',
162
+ theme: 'dark',
163
+ });
164
+ });
165
+
166
+ it('should handle invalid variants in props', async () => {
167
+ const props: DynamicLayoutProps = {
168
+ params: Promise.resolve({ variants: 'invalid' }),
169
+ };
170
+ const result = await RouteVariants.getVariantsFromProps(props);
171
+ expect(result).toEqual(DEFAULT_VARIANTS);
172
+ });
173
+ });
174
+
175
+ describe('getIsMobile', () => {
176
+ it('should extract isMobile as false from props', async () => {
177
+ const props: DynamicLayoutProps = {
178
+ params: Promise.resolve({ variants: 'en-US__0__light' }),
179
+ };
180
+ const result = await RouteVariants.getIsMobile(props);
181
+ expect(result).toBe(false);
182
+ });
183
+
184
+ it('should extract isMobile as true from props', async () => {
185
+ const props: DynamicLayoutProps = {
186
+ params: Promise.resolve({ variants: 'en-US__1__dark' }),
187
+ };
188
+ const result = await RouteVariants.getIsMobile(props);
189
+ expect(result).toBe(true);
190
+ });
191
+
192
+ it('should return default isMobile for invalid props', async () => {
193
+ const props: DynamicLayoutProps = {
194
+ params: Promise.resolve({ variants: 'invalid' }),
195
+ };
196
+ const result = await RouteVariants.getIsMobile(props);
197
+ expect(result).toBe(DEFAULT_VARIANTS.isMobile);
198
+ });
199
+ });
200
+
201
+ describe('getLocale', () => {
202
+ it('should extract locale from props', async () => {
203
+ const props: DynamicLayoutProps = {
204
+ params: Promise.resolve({ variants: 'zh-CN__0__light' }),
205
+ };
206
+ const result = await RouteVariants.getLocale(props);
207
+ expect(result).toBe('zh-CN');
208
+ });
209
+
210
+ it('should extract different locale from props', async () => {
211
+ const props: DynamicLayoutProps = {
212
+ params: Promise.resolve({ variants: 'ja-JP__1__dark' }),
213
+ };
214
+ const result = await RouteVariants.getLocale(props);
215
+ expect(result).toBe('ja-JP');
216
+ });
217
+
218
+ it('should return default locale for invalid props', async () => {
219
+ const props: DynamicLayoutProps = {
220
+ params: Promise.resolve({ variants: 'invalid' }),
221
+ };
222
+ const result = await RouteVariants.getLocale(props);
223
+ expect(result).toBe(DEFAULT_LANG);
224
+ });
225
+
226
+ it('should return default locale for invalid locale in props', async () => {
227
+ const props: DynamicLayoutProps = {
228
+ params: Promise.resolve({ variants: 'invalid-locale__0__light' }),
229
+ };
230
+ const result = await RouteVariants.getLocale(props);
231
+ expect(result).toBe(DEFAULT_LANG);
232
+ });
233
+ });
234
+
235
+ describe('createVariants', () => {
236
+ it('should create variants with default values when no options provided', () => {
237
+ const result = RouteVariants.createVariants();
238
+ expect(result).toEqual(DEFAULT_VARIANTS);
239
+ });
240
+
241
+ it('should create variants with custom isMobile', () => {
242
+ const result = RouteVariants.createVariants({ isMobile: true });
243
+ expect(result).toEqual({
244
+ ...DEFAULT_VARIANTS,
245
+ isMobile: true,
246
+ });
247
+ });
248
+
249
+ it('should create variants with custom locale', () => {
250
+ const result = RouteVariants.createVariants({ locale: 'zh-CN' });
251
+ expect(result).toEqual({
252
+ ...DEFAULT_VARIANTS,
253
+ locale: 'zh-CN',
254
+ });
255
+ });
256
+
257
+ it('should create variants with custom theme', () => {
258
+ const result = RouteVariants.createVariants({ theme: 'dark' });
259
+ expect(result).toEqual({
260
+ ...DEFAULT_VARIANTS,
261
+ theme: 'dark',
262
+ });
263
+ });
264
+
265
+ it('should create variants with multiple custom options', () => {
266
+ const result = RouteVariants.createVariants({
267
+ isMobile: true,
268
+ locale: 'ja-JP',
269
+ theme: 'dark',
270
+ });
271
+ expect(result).toEqual({
272
+ isMobile: true,
273
+ locale: 'ja-JP',
274
+ theme: 'dark',
275
+ });
276
+ });
277
+
278
+ it('should create variants with custom colors', () => {
279
+ const result = RouteVariants.createVariants({
280
+ neutralColor: '#cccccc',
281
+ primaryColor: '#ff0000',
282
+ });
283
+ expect(result).toEqual({
284
+ ...DEFAULT_VARIANTS,
285
+ neutralColor: '#cccccc',
286
+ primaryColor: '#ff0000',
287
+ });
288
+ });
289
+
290
+ it('should create variants with all custom options', () => {
291
+ const result = RouteVariants.createVariants({
292
+ isMobile: true,
293
+ locale: 'zh-CN',
294
+ neutralColor: '#aaaaaa',
295
+ primaryColor: '#00ff00',
296
+ theme: 'dark',
297
+ });
298
+ expect(result).toEqual({
299
+ isMobile: true,
300
+ locale: 'zh-CN',
301
+ neutralColor: '#aaaaaa',
302
+ primaryColor: '#00ff00',
303
+ theme: 'dark',
304
+ });
305
+ });
306
+ });
307
+
308
+ describe('roundtrip serialization', () => {
309
+ it('should maintain data integrity through serialize and deserialize', () => {
310
+ const original: IRouteVariants = {
311
+ isMobile: true,
312
+ locale: 'zh-CN',
313
+ theme: 'dark',
314
+ };
315
+ const serialized = RouteVariants.serializeVariants(original);
316
+ const deserialized = RouteVariants.deserializeVariants(serialized);
317
+ expect(deserialized).toEqual(original);
318
+ });
319
+
320
+ it('should maintain data integrity for default variants', () => {
321
+ const serialized = RouteVariants.serializeVariants(DEFAULT_VARIANTS);
322
+ const deserialized = RouteVariants.deserializeVariants(serialized);
323
+ expect(deserialized).toEqual(DEFAULT_VARIANTS);
324
+ });
325
+
326
+ it('should maintain data integrity for various locales', () => {
327
+ const locales = ['en-US', 'zh-CN', 'ja-JP', 'ko-KR', 'fr-FR'];
328
+ locales.forEach((locale) => {
329
+ const original: IRouteVariants = {
330
+ isMobile: false,
331
+ locale: locale as any,
332
+ theme: 'light',
333
+ };
334
+ const serialized = RouteVariants.serializeVariants(original);
335
+ const deserialized = RouteVariants.deserializeVariants(serialized);
336
+ expect(deserialized).toEqual(original);
337
+ });
338
+ });
339
+ });
340
+ });
package/vitest.config.mts CHANGED
@@ -16,6 +16,8 @@ export default defineConfig({
16
16
  // TODO: after refactor the errorResponse, we can remove it
17
17
  '@/utils/errorResponse': resolve(__dirname, './src/utils/errorResponse'),
18
18
  '@/utils/unzipFile': resolve(__dirname, './src/utils/unzipFile'),
19
+ '@/utils/server': resolve(__dirname, './src/utils/server'),
20
+ '@/utils/electron': resolve(__dirname, './src/utils/electron'),
19
21
  '@/utils': resolve(__dirname, './packages/utils/src'),
20
22
  '@/types': resolve(__dirname, './packages/types/src'),
21
23
  '@/const': resolve(__dirname, './packages/const/src'),
@@ -1,91 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2
-
3
- import { imageToBase64, imageUrlToBase64 } from './imageToBase64';
4
-
5
- describe('imageToBase64', () => {
6
- let mockImage: HTMLImageElement;
7
- let mockCanvas: HTMLCanvasElement;
8
- let mockContext: CanvasRenderingContext2D;
9
-
10
- beforeEach(() => {
11
- mockImage = {
12
- width: 200,
13
- height: 100,
14
- } as HTMLImageElement;
15
-
16
- mockContext = {
17
- drawImage: vi.fn(),
18
- } as unknown as CanvasRenderingContext2D;
19
-
20
- mockCanvas = {
21
- width: 0,
22
- height: 0,
23
- getContext: vi.fn().mockReturnValue(mockContext),
24
- toDataURL: vi.fn().mockReturnValue('data:image/webp;base64,mockBase64Data'),
25
- } as unknown as HTMLCanvasElement;
26
-
27
- vi.spyOn(document, 'createElement').mockReturnValue(mockCanvas);
28
- });
29
-
30
- afterEach(() => {
31
- vi.restoreAllMocks();
32
- });
33
-
34
- it('should convert image to base64 with correct size and type', () => {
35
- const result = imageToBase64({ img: mockImage, size: 100, type: 'image/jpeg' });
36
-
37
- expect(document.createElement).toHaveBeenCalledWith('canvas');
38
- expect(mockCanvas.width).toBe(100);
39
- expect(mockCanvas.height).toBe(100);
40
- expect(mockCanvas.getContext).toHaveBeenCalledWith('2d');
41
- expect(mockContext.drawImage).toHaveBeenCalledWith(mockImage, 50, 0, 100, 100, 0, 0, 100, 100);
42
- expect(mockCanvas.toDataURL).toHaveBeenCalledWith('image/jpeg');
43
- expect(result).toBe('data:image/webp;base64,mockBase64Data');
44
- });
45
-
46
- it('should use default type when not specified', () => {
47
- imageToBase64({ img: mockImage, size: 100 });
48
- expect(mockCanvas.toDataURL).toHaveBeenCalledWith('image/webp');
49
- });
50
-
51
- it('should handle taller images correctly', () => {
52
- mockImage.width = 100;
53
- mockImage.height = 200;
54
- imageToBase64({ img: mockImage, size: 100 });
55
- expect(mockContext.drawImage).toHaveBeenCalledWith(mockImage, 0, 50, 100, 100, 0, 0, 100, 100);
56
- });
57
- });
58
-
59
- describe('imageUrlToBase64', () => {
60
- const mockFetch = vi.fn();
61
- const mockArrayBuffer = new ArrayBuffer(8);
62
-
63
- beforeEach(() => {
64
- global.fetch = mockFetch;
65
- global.btoa = vi.fn().mockReturnValue('mockBase64String');
66
- });
67
-
68
- afterEach(() => {
69
- vi.restoreAllMocks();
70
- });
71
-
72
- it('should convert image URL to base64 string', async () => {
73
- mockFetch.mockResolvedValue({
74
- arrayBuffer: () => Promise.resolve(mockArrayBuffer),
75
- blob: () => Promise.resolve(new Blob([mockArrayBuffer], { type: 'image/jpg' })),
76
- });
77
-
78
- const result = await imageUrlToBase64('https://example.com/image.jpg');
79
-
80
- expect(mockFetch).toHaveBeenCalledWith('https://example.com/image.jpg');
81
- expect(global.btoa).toHaveBeenCalled();
82
- expect(result).toEqual({ base64: 'mockBase64String', mimeType: 'image/jpg' });
83
- });
84
-
85
- it('should throw an error when fetch fails', async () => {
86
- const mockError = new Error('Fetch failed');
87
- mockFetch.mockRejectedValue(mockError);
88
-
89
- await expect(imageUrlToBase64('https://example.com/image.jpg')).rejects.toThrow('Fetch failed');
90
- });
91
- });