@lobehub/chat 0.159.11 → 0.160.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 (73) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/README.zh-CN.md +8 -8
  4. package/package.json +4 -2
  5. package/src/app/(main)/chat/@session/features/SessionHydration.tsx +2 -0
  6. package/src/app/trpc/{[trpc] → edge/[trpc]}/route.ts +3 -3
  7. package/src/config/__tests__/server.test.ts +0 -11
  8. package/src/config/file.ts +34 -0
  9. package/src/config/server/app.ts +0 -8
  10. package/src/config/server/provider.ts +3 -3
  11. package/src/database/client/models/file.ts +2 -1
  12. package/src/database/client/schemas/files.ts +2 -2
  13. package/src/features/Conversation/Error/index.tsx +13 -4
  14. package/src/features/Conversation/components/VirtualizedList/index.tsx +27 -21
  15. package/src/libs/agent-runtime/google/index.test.ts +20 -1
  16. package/src/libs/agent-runtime/google/index.ts +22 -9
  17. package/src/libs/agent-runtime/utils/uriParser.test.ts +29 -0
  18. package/src/libs/agent-runtime/utils/uriParser.ts +17 -9
  19. package/src/libs/trpc/client.ts +5 -3
  20. package/src/libs/trpc/index.ts +10 -34
  21. package/src/libs/trpc/init.ts +26 -0
  22. package/src/libs/trpc/middleware/password.test.ts +87 -0
  23. package/src/libs/trpc/middleware/password.ts +26 -0
  24. package/src/libs/trpc/middleware/userAuth.test.ts +44 -0
  25. package/src/libs/trpc/middleware/userAuth.ts +18 -0
  26. package/src/server/context.ts +28 -3
  27. package/src/server/files/s3.ts +58 -0
  28. package/src/server/globalConfig/index.ts +2 -0
  29. package/src/server/mock.ts +2 -2
  30. package/src/server/routers/{config → edge/config}/index.test.ts +1 -0
  31. package/src/server/routers/edge/upload.ts +16 -0
  32. package/src/server/routers/index.ts +5 -3
  33. package/src/services/__tests__/global.test.ts +4 -5
  34. package/src/services/__tests__/sync.test.ts +56 -0
  35. package/src/services/__tests__/upload.test.ts +72 -0
  36. package/src/services/_url.ts +2 -0
  37. package/src/services/file/client.test.ts +102 -34
  38. package/src/services/file/client.ts +24 -49
  39. package/src/services/file/type.ts +1 -2
  40. package/src/services/global.ts +3 -18
  41. package/src/services/message/type.ts +6 -3
  42. package/src/services/sync.ts +19 -0
  43. package/src/services/upload.ts +99 -0
  44. package/src/store/chat/slices/builtinTool/action.test.ts +4 -2
  45. package/src/store/chat/slices/builtinTool/action.ts +6 -3
  46. package/src/store/chat/slices/enchance/action.test.ts +16 -12
  47. package/src/store/chat/slices/message/action.test.ts +495 -24
  48. package/src/store/chat/slices/message/action.ts +143 -32
  49. package/src/store/chat/slices/message/initialState.ts +2 -2
  50. package/src/store/chat/slices/message/selectors.test.ts +39 -9
  51. package/src/store/chat/slices/message/selectors.ts +13 -3
  52. package/src/store/chat/slices/message/utils.ts +7 -0
  53. package/src/store/chat/slices/plugin/action.test.ts +7 -2
  54. package/src/store/chat/slices/share/action.test.ts +19 -3
  55. package/src/store/chat/slices/topic/action.test.ts +13 -2
  56. package/src/store/chat/slices/topic/action.ts +27 -6
  57. package/src/store/chat/slices/topic/initialState.ts +2 -0
  58. package/src/store/file/slices/images/action.test.ts +10 -17
  59. package/src/store/file/slices/images/action.ts +4 -1
  60. package/src/store/file/slices/tts/action.test.ts +8 -14
  61. package/src/store/file/slices/tts/action.ts +4 -1
  62. package/src/store/serverConfig/selectors.ts +1 -0
  63. package/src/store/serverConfig/store.ts +10 -0
  64. package/src/store/user/slices/common/action.ts +26 -14
  65. package/src/store/user/slices/sync/action.test.ts +6 -6
  66. package/src/store/user/slices/sync/action.ts +3 -3
  67. package/src/types/serverConfig.ts +1 -0
  68. package/src/utils/fetch.test.ts +17 -11
  69. package/src/utils/fetch.ts +20 -22
  70. package/src/app/api/files/image/imgur.ts +0 -72
  71. package/src/app/api/files/image/route.ts +0 -42
  72. /package/src/server/routers/{config → edge/config}/__snapshots__/index.test.ts.snap +0 -0
  73. /package/src/server/routers/{config → edge/config}/index.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 0.160.0](https://github.com/lobehub/lobe-chat/compare/v0.159.12...v0.160.0)
6
+
7
+ <sup>Released on **2024-05-18**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Bump version and add enable ollama env.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Bump version and add enable ollama env, closes [#2554](https://github.com/lobehub/lobe-chat/issues/2554) ([f5ce7c9](https://github.com/lobehub/lobe-chat/commit/f5ce7c9))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 0.159.12](https://github.com/lobehub/lobe-chat/compare/v0.159.11...v0.159.12)
31
+
32
+ <sup>Released on **2024-05-15**</sup>
33
+
34
+ #### ♻ Code Refactoring
35
+
36
+ - **misc**: Refactor the create message flow to fix some bugs.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Code refactoring
44
+
45
+ - **misc**: Refactor the create message flow to fix some bugs, closes [#2521](https://github.com/lobehub/lobe-chat/issues/2521) ([7263a33](https://github.com/lobehub/lobe-chat/commit/7263a33))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 0.159.11](https://github.com/lobehub/lobe-chat/compare/v0.159.10...v0.159.11)
6
56
 
7
57
  <sup>Released on **2024-05-15**</sup>
package/README.md CHANGED
@@ -230,7 +230,7 @@ In addition, these plugins are not limited to news aggregation, but can also ext
230
230
  | [Social Search](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **say-apps** on **2024-05-02**</sup> | The Social Search provides access to tweets, users, followers, images, media and more.<br/>`social` `twitter` `x` `search` |
231
231
  | [Search Google via Serper](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Barry** on **2024-04-30**</sup> | Google search engine via Serper.dev free API (2500x🆓/month)<br/>`web` `search` |
232
232
 
233
- > 📊 Total plugins: [<kbd>**58**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
233
+ > 📊 Total plugins: [<kbd>**56**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
234
234
 
235
235
  <!-- PLUGIN LIST -->
236
236
 
@@ -262,14 +262,14 @@ Our marketplace is not just a showcase platform but also a collaborative space.
262
262
 
263
263
  <!-- AGENT LIST -->
264
264
 
265
- | Recent Submits | Description |
266
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
267
- | [SF Symbols Finder](https://chat-preview.lobehub.com/market?agent=sf-symbols-finder)<br/><sup>By **[inquiry-paring0a](https://github.com/inquiry-paring0a)** on **2024-05-08**</sup> | Master Apple SF Symbols, select symbols that match the description<br/>`sf-symbols` `expert` `icon` `symbol` `plugin` |
268
- | [GhostWriter Pro](https://chat-preview.lobehub.com/market?agent=ghostwriter-pro-ai)<br/><sup>By **[EarlofSandwhich](https://github.com/EarlofSandwhich)** on **2024-05-07**</sup> | A sophisticated AI-powered ghostwriting agent designed to craft high-quality content across a diverse range of genres and formats. Equipped with advanced language models, GhostWriter Pro excels in creating personalized, engaging, and research-backed writing that meets professional standards.<br/>`author` `writing` |
269
- | [Video to Blog Post Assistant](https://chat-preview.lobehub.com/market?agent=video-2-blog-assistant)<br/><sup>By **[yayoinoyume](https://github.com/yayoinoyume)** on **2024-05-06**</sup> | Helps you quickly organize messy subtitles into beautiful blog posts<br/>`subtitle-organization` `blog-post-formatting` `video-to-blog` |
270
- | [Art Evaluation Tutor](https://chat-preview.lobehub.com/market?agent=wanwusheng-art)<br/><sup>By **[dingyufei615](https://github.com/dingyufei615)** on **2024-05-06**</sup> | Specializes in teaching children's art, meticulously evaluates works, pays attention to details, and adapts to students of different age groups.<br/>`art-education` `evaluation` `creativity` `teaching` `painting` |
265
+ | Recent Submits | Description |
266
+ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
267
+ | [AWS Guru](https://chat-preview.lobehub.com/market?agent=aws-guru)<br/><sup>By **[wilbeibi](https://github.com/wilbeibi)** on **2024-05-15**</sup> | Agent to answer AWS questions<br/>`programming` |
268
+ | [Linux Buddy](https://chat-preview.lobehub.com/market?agent=linux-buddy)<br/><sup>By **[Firpo7](https://github.com/Firpo7)** on **2024-05-15**</sup> | Your Linux expert friend<br/>`linux` `technical-support` `buddy` |
269
+ | [Photography Critic](https://chat-preview.lobehub.com/market?agent=photography-critic)<br/><sup>By **[Justin3go](https://github.com/Justin3go)** on **2024-05-15**</sup> | Specializes in detailed analysis of photographic works, including themes, composition, technical quality, use of light, creativity, and originality.<br/>`photography` `evaluation` `analysis` `composition` `technical-quality` |
270
+ | [Python Buddy](https://chat-preview.lobehub.com/market?agent=python-buddy)<br/><sup>By **[Firpo7](https://github.com/Firpo7)** on **2024-05-15**</sup> | Your Python expert friend<br/>`python` `software-development` `coding` `code` `buddy` |
271
271
 
272
- > 📊 Total agents: [<kbd>**249**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
272
+ > 📊 Total agents: [<kbd>**258**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
273
273
 
274
274
  <!-- AGENT LIST -->
275
275
 
package/README.zh-CN.md CHANGED
@@ -222,7 +222,7 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
222
222
  | [社交搜索](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **say-apps** on **2024-05-02**</sup> | 社交搜索提供访问推文、用户、关注者、图片、媒体等功能。<br/>`社交` `推特` `x` `搜索` |
223
223
  | [通过 Serper 搜索 Google](https://chat-preview.lobehub.com/settings/agent)<br/><sup>By **Barry** on **2024-04-30**</sup> | 通过 Serper.dev 免费 API 进行 Google 搜索引擎(每月 2500 次🆓)<br/>`网络` `搜索` |
224
224
 
225
- > 📊 Total plugins: [<kbd>**58**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
225
+ > 📊 Total plugins: [<kbd>**56**</kbd>](https://github.com/lobehub/lobe-chat-plugins)
226
226
 
227
227
  <!-- PLUGIN LIST -->
228
228
 
@@ -250,14 +250,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
250
250
 
251
251
  <!-- AGENT LIST -->
252
252
 
253
- | 最近新增 | 助手说明 |
254
- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
255
- | [SF Symbols 查找器](https://chat-preview.lobehub.com/market?agent=sf-symbols-finder)<br/><sup>By **[inquiry-paring0a](https://github.com/inquiry-paring0a)** on **2024-05-08**</sup> | 精通苹果 SF Symbols,可根据描述选择符合的 Symbols<br/>`sf-symbols` `专家` `图标` `符号` `插件` |
256
- | [GhostWriter Pro](https://chat-preview.lobehub.com/market?agent=ghostwriter-pro-ai)<br/><sup>By **[EarlofSandwhich](https://github.com/EarlofSandwhich)** on **2024-05-07**</sup> | 一款复杂的 AI 驱动的写手代理,旨在跨多种流派和格式创作高质量内容。GhostWriter Pro 配备先进的语言模型,擅长创作个性化、引人入胜且有研究支持的写作,符合专业标准。<br/>`作者` `写作` |
257
- | [视频转博客文章助手](https://chat-preview.lobehub.com/market?agent=video-2-blog-assistant)<br/><sup>By **[yayoinoyume](https://github.com/yayoinoyume)** on **2024-05-06**</sup> | 帮你快速整理缭乱的字幕,变成精美的博客文章<br/>`字幕整理` `博文格式` `视频变博客` |
258
- | [美术评价导师](https://chat-preview.lobehub.com/market?agent=wanwusheng-art)<br/><sup>By **[dingyufei615](https://github.com/dingyufei615)** on **2024-05-06**</sup> | 擅长少儿美术教学,细致评价作品,关注细节,适应不同年龄段学生。<br/>`美术教育` `评价` `创意` `教学` `绘画` |
253
+ | 最近新增 | 助手说明 |
254
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
255
+ | [AWS Guru](https://chat-preview.lobehub.com/market?agent=aws-guru)<br/><sup>By **[wilbeibi](https://github.com/wilbeibi)** on **2024-05-15**</sup> | Agent to answer AWS questions<br/>`programming` |
256
+ | [Linux Buddy](https://chat-preview.lobehub.com/market?agent=linux-buddy)<br/><sup>By **[Firpo7](https://github.com/Firpo7)** on **2024-05-15**</sup> | 您的 Linux 专家朋友<br/>`linux` `technical-support` `buddy` |
257
+ | [摄影评论家](https://chat-preview.lobehub.com/market?agent=photography-critic)<br/><sup>By **[Justin3go](https://github.com/Justin3go)** on **2024-05-15**</sup> | 擅长摄影作品细致分析,包括主题、构图、技术质量、光线使用、创意与原创性等。<br/>`摄影` `评价` `分析` `构图` `技术质量` |
258
+ | [Python Buddy](https://chat-preview.lobehub.com/market?agent=python-buddy)<br/><sup>By **[Firpo7](https://github.com/Firpo7)** on **2024-05-15**</sup> | 您的 Python 专家朋友<br/>`python` `软件开发` `编程` `代码` `伙伴` |
259
259
 
260
- > 📊 Total agents: [<kbd>**249**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
260
+ > 📊 Total agents: [<kbd>**258**</kbd> ](https://github.com/lobehub/lobe-chat-agents)
261
261
 
262
262
  <!-- AGENT LIST -->
263
263
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "0.159.11",
3
+ "version": "0.160.0",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -85,6 +85,8 @@
85
85
  "@anthropic-ai/sdk": "^0.20.9",
86
86
  "@auth/core": "0.28.0",
87
87
  "@aws-sdk/client-bedrock-runtime": "^3.574.0",
88
+ "@aws-sdk/client-s3": "^3.577.0",
89
+ "@aws-sdk/s3-request-presigner": "^3.577.0",
88
90
  "@azure/openai": "1.0.0-beta.12",
89
91
  "@cfworker/json-schema": "^1.12.8",
90
92
  "@clerk/localizations": "2.0.0",
@@ -108,7 +110,7 @@
108
110
  "@vercel/speed-insights": "^1.0.10",
109
111
  "ahooks": "^3.7.11",
110
112
  "ai": "3.0.19",
111
- "antd": "5.17.0",
113
+ "antd": "^5.17.2",
112
114
  "antd-style": "^3.6.2",
113
115
  "brotli-wasm": "^3.0.0",
114
116
  "chroma-js": "^2.4.2",
@@ -13,6 +13,7 @@ import { useSessionStore } from '@/store/session';
13
13
  const SessionHydration = memo(() => {
14
14
  const useStoreUpdater = createStoreUpdater(useSessionStore);
15
15
  const useAgentStoreUpdater = createStoreUpdater(useAgentStore);
16
+ const useChatStoreUpdater = createStoreUpdater(useChatStore);
16
17
  const [switchTopic] = useChatStore((s) => [s.switchTopic]);
17
18
 
18
19
  // two-way bindings the url and session store
@@ -22,6 +23,7 @@ const SessionHydration = memo(() => {
22
23
  );
23
24
  useStoreUpdater('activeId', session);
24
25
  useAgentStoreUpdater('activeId', session);
26
+ useChatStoreUpdater('activeId', session);
25
27
 
26
28
  useEffect(() => {
27
29
  const unsubscribe = useSessionStore.subscribe(
@@ -3,7 +3,7 @@ import type { NextRequest } from 'next/server';
3
3
 
4
4
  import { pino } from '@/libs/logger';
5
5
  import { createContext } from '@/server/context';
6
- import { appRouter } from '@/server/routers';
6
+ import { edgeRouter } from '@/server/routers';
7
7
 
8
8
  export const runtime = 'edge';
9
9
 
@@ -14,7 +14,7 @@ const handler = (req: NextRequest) =>
14
14
  */
15
15
  createContext: () => createContext(req),
16
16
 
17
- endpoint: '/trpc',
17
+ endpoint: '/trpc/edge',
18
18
 
19
19
  onError: ({ error, path }) => {
20
20
  pino.info(`Error in tRPC handler (edge) on path: ${path}`);
@@ -22,7 +22,7 @@ const handler = (req: NextRequest) =>
22
22
  },
23
23
 
24
24
  req,
25
- router: appRouter,
25
+ router: edgeRouter,
26
26
  });
27
27
 
28
28
  export { handler as GET, handler as POST };
@@ -33,17 +33,6 @@ describe('getServerConfig', () => {
33
33
  expect(config.OPENAI_FUNCTION_REGIONS).toStrictEqual(['iad1', 'sfo1']);
34
34
  });
35
35
 
36
- it('returns default IMGUR_CLIENT_ID when no environment variable is set', () => {
37
- const config = getServerConfig();
38
- expect(config.IMGUR_CLIENT_ID).toBe('e415f320d6e24f9');
39
- });
40
-
41
- it('returns custom IMGUR_CLIENT_ID when environment variable is set', () => {
42
- process.env.IMGUR_CLIENT_ID = 'custom-client-id';
43
- const config = getServerConfig();
44
- expect(config.IMGUR_CLIENT_ID).toBe('custom-client-id');
45
- });
46
-
47
36
  describe('index url', () => {
48
37
  it('should return default URLs when no environment variables are set', () => {
49
38
  const config = getServerConfig();
@@ -0,0 +1,34 @@
1
+ import { createEnv } from '@t3-oss/env-nextjs';
2
+ import { z } from 'zod';
3
+
4
+ const DEFAULT_S3_FILE_PATH = 'files';
5
+
6
+ export const getFileConfig = () => {
7
+ return createEnv({
8
+ client: {
9
+ NEXT_PUBLIC_S3_DOMAIN: z.string().optional(),
10
+ NEXT_PUBLIC_S3_FILE_PATH: z.string().optional(),
11
+ },
12
+ runtimeEnv: {
13
+ NEXT_PUBLIC_S3_DOMAIN: process.env.NEXT_PUBLIC_S3_DOMAIN,
14
+ NEXT_PUBLIC_S3_FILE_PATH: process.env.NEXT_PUBLIC_S3_FILE_PATH || DEFAULT_S3_FILE_PATH,
15
+
16
+ S3_ACCESS_KEY_ID: process.env.S3_ACCESS_KEY_ID,
17
+ S3_BUCKET: process.env.S3_BUCKET,
18
+ S3_ENDPOINT: process.env.S3_ENDPOINT,
19
+ S3_REGION: process.env.S3_REGION,
20
+ S3_SECRET_ACCESS_KEY: process.env.S3_SECRET_ACCESS_KEY,
21
+ },
22
+ server: {
23
+ // S3
24
+ S3_ACCESS_KEY_ID: z.string().optional(),
25
+ S3_BUCKET: z.string().optional(),
26
+ S3_ENDPOINT: z.string().optional(),
27
+
28
+ S3_REGION: z.string().optional(),
29
+ S3_SECRET_ACCESS_KEY: z.string().optional(),
30
+ },
31
+ });
32
+ };
33
+
34
+ export const fileEnv = getFileConfig();
@@ -6,8 +6,6 @@ declare global {
6
6
  interface ProcessEnv {
7
7
  ACCESS_CODE?: string;
8
8
 
9
- IMGUR_CLIENT_ID?: string;
10
-
11
9
  SITE_URL?: string;
12
10
 
13
11
  AGENTS_INDEX_URL?: string;
@@ -25,10 +23,6 @@ declare global {
25
23
  }
26
24
  }
27
25
 
28
- // we apply a free imgur app to get a client id
29
- // refs: https://apidocs.imgur.com/
30
- const DEFAULT_IMAGUR_CLIENT_ID = 'e415f320d6e24f9';
31
-
32
26
  export const getAppConfig = () => {
33
27
  if (typeof process === 'undefined') {
34
28
  throw new Error('[Server Config] you are importing a server-only module outside of server');
@@ -45,8 +39,6 @@ export const getAppConfig = () => {
45
39
 
46
40
  SITE_URL: process.env.SITE_URL,
47
41
 
48
- IMGUR_CLIENT_ID: process.env.IMGUR_CLIENT_ID || DEFAULT_IMAGUR_CLIENT_ID,
49
-
50
42
  AGENTS_INDEX_URL: !!process.env.AGENTS_INDEX_URL
51
43
  ? process.env.AGENTS_INDEX_URL
52
44
  : 'https://chat-agents.lobehub.com',
@@ -22,7 +22,7 @@ declare global {
22
22
  // DeepSeek Provider
23
23
  ENABLED_DEEPSEEK?: string;
24
24
  DEEPSEEK_API_KEY?: string;
25
-
25
+
26
26
  // ZhiPu Provider
27
27
  ENABLED_ZHIPU?: string;
28
28
  ZHIPU_API_KEY?: string;
@@ -113,7 +113,7 @@ export const getProviderConfig = () => {
113
113
  const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID || '';
114
114
 
115
115
  const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || '';
116
-
116
+
117
117
  const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY || '';
118
118
 
119
119
  const MOONSHOT_API_KEY = process.env.MOONSHOT_API_KEY || '';
@@ -221,7 +221,7 @@ export const getProviderConfig = () => {
221
221
  AWS_ACCESS_KEY_ID: AWS_ACCESS_KEY_ID,
222
222
  AWS_SECRET_ACCESS_KEY: process.env.AWS_SECRET_ACCESS_KEY || '',
223
223
 
224
- ENABLE_OLLAMA: Boolean(process.env.ENABLE_OLLAMA),
224
+ ENABLE_OLLAMA: process.env.ENABLE_OLLAMA === '0',
225
225
  OLLAMA_PROXY_URL: process.env.OLLAMA_PROXY_URL || '',
226
226
  OLLAMA_MODEL_LIST: process.env.OLLAMA_MODEL_LIST || process.env.OLLAMA_CUSTOM_MODELS,
227
227
  };
@@ -1,3 +1,4 @@
1
+ import { DBModel } from '@/database/client/core/types/db';
1
2
  import { DB_File, DB_FileSchema } from '@/database/client/schemas/files';
2
3
  import { nanoid } from '@/utils/uuid';
3
4
 
@@ -14,7 +15,7 @@ class _FileModel extends BaseModel<'files'> {
14
15
  return this._addWithSync(file, `file-${id}`);
15
16
  }
16
17
 
17
- async findById(id: string) {
18
+ async findById(id: string): Promise<DBModel<DB_File>> {
18
19
  return this.table.get(id);
19
20
  }
20
21
 
@@ -8,7 +8,7 @@ export const DB_FileSchema = z.object({
8
8
  /**
9
9
  * file data array buffer
10
10
  */
11
- data: z.instanceof(ArrayBuffer),
11
+ data: z.instanceof(ArrayBuffer).optional(),
12
12
  /**
13
13
  * file type
14
14
  * @example 'image/png'
@@ -33,7 +33,7 @@ export const DB_FileSchema = z.object({
33
33
  /**
34
34
  * file url if saveMode is url
35
35
  */
36
- url: z.string().url().optional(),
36
+ url: z.string().optional(),
37
37
  });
38
38
 
39
39
  export type DB_File = z.infer<typeof DB_FileSchema>;
@@ -1,6 +1,8 @@
1
1
  import { IPluginErrorType, PluginErrorType } from '@lobehub/chat-plugin-sdk';
2
2
  import type { AlertProps } from '@lobehub/ui';
3
- import { memo } from 'react';
3
+ import { Skeleton } from 'antd';
4
+ import dynamic from 'next/dynamic';
5
+ import { Suspense, memo } from 'react';
4
6
 
5
7
  import { AgentRuntimeErrorType, ILobeAgentRuntimeErrorType } from '@/libs/agent-runtime';
6
8
  import { ChatErrorType, ErrorType } from '@/types/fetch';
@@ -10,9 +12,12 @@ import ClerkLogin from './ClerkLogin';
10
12
  import ErrorJsonViewer from './ErrorJsonViewer';
11
13
  import InvalidAPIKey from './InvalidAPIKey';
12
14
  import InvalidAccessCode from './InvalidAccessCode';
13
- import OllamaBizError from './OllamaBizError';
14
15
  import OpenAiBizError from './OpenAiBizError';
15
- import PluginSettings from './PluginSettings';
16
+
17
+ const loading = () => <Skeleton active />;
18
+
19
+ const OllamaBizError = dynamic(() => import('./OllamaBizError'), { loading, ssr: false });
20
+ const PluginSettings = dynamic(() => import('./PluginSettings'), { loading, ssr: false });
16
21
 
17
22
  // Config for the errorMessage display
18
23
  export const getErrorAlertConfig = (
@@ -95,4 +100,8 @@ const ErrorMessageExtra = memo<{ data: ChatMessage }>(({ data }) => {
95
100
  }
96
101
  });
97
102
 
98
- export default ErrorMessageExtra;
103
+ export default memo<{ data: ChatMessage }>(({ data }) => (
104
+ <Suspense fallback={<Skeleton active style={{ width: '100%' }} />}>
105
+ <ErrorMessageExtra data={data} />
106
+ </Suspense>
107
+ ));
@@ -29,14 +29,17 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
29
29
 
30
30
  const [id] = useChatStore((s) => [chatSelectors.currentChatKey(s)]);
31
31
 
32
- const [activeTopicId, useFetchMessages, isFirstLoading] = useChatStore((s) => [
33
- s.activeTopicId,
34
- s.useFetchMessages,
35
- chatSelectors.currentChatLoadingState(s),
36
- ]);
32
+ const [activeTopicId, useFetchMessages, isFirstLoading, isCurrentChatLoaded] = useChatStore(
33
+ (s) => [
34
+ s.activeTopicId,
35
+ s.useFetchMessages,
36
+ chatSelectors.currentChatLoadingState(s),
37
+ chatSelectors.isCurrentChatLoaded(s),
38
+ ],
39
+ );
37
40
 
38
41
  const [sessionId] = useSessionStore((s) => [s.activeId]);
39
- const { isLoading } = useFetchMessages(sessionId, activeTopicId);
42
+ useFetchMessages(sessionId, activeTopicId);
40
43
 
41
44
  const data = useChatStore((s) => {
42
45
  const showInboxWelcome = chatSelectors.showInboxWelcome(s);
@@ -77,23 +80,26 @@ const VirtualizedList = memo<VirtualizedListProps>(({ mobile }) => {
77
80
  [mobile],
78
81
  );
79
82
 
80
- // first time loading
83
+ // first time loading or not loaded
81
84
  if (isFirstLoading) return <SkeletonList mobile={mobile} />;
82
85
 
83
- // in server mode and switch page
84
- if (isServerMode && isLoading) return <SkeletonList mobile={mobile} />;
85
-
86
- // in client mode using the center loading for more
87
- return isLoading ? (
88
- <Center height={'100%'} width={'100%'}>
89
- <Icon
90
- icon={Loader2Icon}
91
- size={{ fontSize: 32 }}
92
- spin
93
- style={{ color: theme.colorTextTertiary }}
94
- />
95
- </Center>
96
- ) : (
86
+ if (!isCurrentChatLoaded)
87
+ // use skeleton list when not loaded in server mode due to the loading duration is much longer than client mode
88
+ return isServerMode ? (
89
+ <SkeletonList mobile={mobile} />
90
+ ) : (
91
+ // in client mode and switch page, using the center loading for smooth transition
92
+ <Center height={'100%'} width={'100%'}>
93
+ <Icon
94
+ icon={Loader2Icon}
95
+ size={{ fontSize: 32 }}
96
+ spin
97
+ style={{ color: theme.colorTextTertiary }}
98
+ />
99
+ </Center>
100
+ );
101
+
102
+ return (
97
103
  <Flexbox height={'100%'}>
98
104
  <Virtuoso
99
105
  atBottomStateChange={setAtBottom}
@@ -560,7 +560,7 @@ describe('LobeGoogleAI', () => {
560
560
  });
561
561
  });
562
562
 
563
- it('should correctly convert message with content parts', () => {
563
+ it('should correctly convert message with inline base64 image parts', () => {
564
564
  const message: OpenAIChatMessage = {
565
565
  role: 'user',
566
566
  content: [
@@ -571,6 +571,25 @@ describe('LobeGoogleAI', () => {
571
571
 
572
572
  const converted = instance['convertOAIMessagesToGoogleMessage'](message);
573
573
 
574
+ expect(converted).toEqual({
575
+ role: 'user',
576
+ parts: [
577
+ { text: 'Check this image:' },
578
+ { inlineData: { data: '...', mimeType: 'image/png' } },
579
+ ],
580
+ });
581
+ });
582
+ it.skip('should correctly convert message with image url parts', () => {
583
+ const message: OpenAIChatMessage = {
584
+ role: 'user',
585
+ content: [
586
+ { type: 'text', text: 'Check this image:' },
587
+ { type: 'image_url', image_url: { url: 'https://image-file.com' } },
588
+ ],
589
+ };
590
+
591
+ const converted = instance['convertOAIMessagesToGoogleMessage'](message);
592
+
574
593
  expect(converted).toEqual({
575
594
  role: 'user',
576
595
  parts: [
@@ -115,18 +115,31 @@ export class LobeGoogleAI implements LobeRuntimeAI {
115
115
  return { text: content.text };
116
116
  }
117
117
  case 'image_url': {
118
- const { mimeType, base64 } = parseDataUri(content.image_url.url);
118
+ const { mimeType, base64, type } = parseDataUri(content.image_url.url);
119
119
 
120
- if (!base64) {
121
- throw new TypeError("Image URL doesn't contain base64 data");
120
+ if (type === 'base64') {
121
+ if (!base64) {
122
+ throw new TypeError("Image URL doesn't contain base64 data");
123
+ }
124
+
125
+ return {
126
+ inlineData: {
127
+ data: base64,
128
+ mimeType: mimeType || 'image/png',
129
+ },
130
+ };
122
131
  }
123
132
 
124
- return {
125
- inlineData: {
126
- data: base64,
127
- mimeType: mimeType || 'image/png',
128
- },
129
- };
133
+ // if (type === 'url') {
134
+ // return {
135
+ // fileData: {
136
+ // fileUri: content.image_url.url,
137
+ // mimeType: mimeType || 'image/png',
138
+ // },
139
+ // };
140
+ // }
141
+
142
+ throw new TypeError(`currently we don't support image url: ${content.image_url.url}`);
130
143
  }
131
144
  }
132
145
  };
@@ -0,0 +1,29 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { parseDataUri } from './uriParser';
4
+
5
+ describe('parseDataUri', () => {
6
+ it('should parse a valid data URI', () => {
7
+ const dataUri = 'data:image/png;base64,abc';
8
+ const result = parseDataUri(dataUri);
9
+ expect(result).toEqual({ base64: 'abc', mimeType: 'image/png', type: 'base64' });
10
+ });
11
+
12
+ it('should parse a valid URL', () => {
13
+ const url = 'https://example.com/image.jpg';
14
+ const result = parseDataUri(url);
15
+ expect(result).toEqual({ base64: null, mimeType: null, type: 'url' });
16
+ });
17
+
18
+ it('should return null for an invalid input', () => {
19
+ const invalidInput = 'invalid-data';
20
+ const result = parseDataUri(invalidInput);
21
+ expect(result).toEqual({ base64: null, mimeType: null, type: null });
22
+ });
23
+
24
+ it('should handle an empty input', () => {
25
+ const emptyInput = '';
26
+ const result = parseDataUri(emptyInput);
27
+ expect(result).toEqual({ base64: null, mimeType: null, type: null });
28
+ });
29
+ });
@@ -1,16 +1,24 @@
1
- export const parseDataUri = (
2
- dataUri: string,
3
- ): { base64: string | null; mimeType: string | null } => {
1
+ interface UriParserResult {
2
+ base64: string | null;
3
+ mimeType: string | null;
4
+ type: 'url' | 'base64' | null;
5
+ }
6
+
7
+ export const parseDataUri = (dataUri: string): UriParserResult => {
4
8
  // 正则表达式匹配整个 Data URI 结构
5
9
  const dataUriMatch = dataUri.match(/^data:([^;]+);base64,(.+)$/);
6
10
 
7
- // 如果匹配成功,则返回 mimeType 和 base64,否则返回 null
8
11
  if (dataUriMatch) {
9
- return {
10
- base64: dataUriMatch[2],
11
- mimeType: dataUriMatch[1],
12
- };
12
+ // 如果是合法的 Data URI
13
+ return { base64: dataUriMatch[2], mimeType: dataUriMatch[1], type: 'base64' };
13
14
  }
14
15
 
15
- return { base64: null, mimeType: null };
16
+ try {
17
+ new URL(dataUri);
18
+ // 如果是合法的 URL
19
+ return { base64: null, mimeType: null, type: 'url' };
20
+ } catch {
21
+ // 既不是 Data URI 也不是合法 URL
22
+ return { base64: null, mimeType: null, type: null };
23
+ }
16
24
  };
@@ -1,13 +1,15 @@
1
1
  import { createTRPCClient, httpBatchLink } from '@trpc/client';
2
2
  import superjson from 'superjson';
3
3
 
4
- import type { AppRouter } from '@/server/routers';
4
+ import type { EdgeRouter } from '@/server/routers';
5
+ import { createHeaderWithAuth } from '@/services/_auth';
5
6
 
6
- export const trpcClient = createTRPCClient<AppRouter>({
7
+ export const edgeClient = createTRPCClient<EdgeRouter>({
7
8
  links: [
8
9
  httpBatchLink({
10
+ headers: async () => createHeaderWithAuth(),
9
11
  transformer: superjson,
10
- url: '/trpc',
12
+ url: '/trpc/edge',
11
13
  }),
12
14
  ],
13
15
  });