@lobehub/chat 1.19.30 → 1.19.32

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 (34) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/locales/ar/models.json +12 -0
  3. package/locales/bg-BG/models.json +12 -0
  4. package/locales/de-DE/models.json +12 -0
  5. package/locales/en-US/models.json +12 -0
  6. package/locales/es-ES/models.json +12 -0
  7. package/locales/fr-FR/models.json +12 -0
  8. package/locales/it-IT/models.json +12 -0
  9. package/locales/ja-JP/models.json +12 -0
  10. package/locales/ko-KR/models.json +12 -0
  11. package/locales/nl-NL/models.json +12 -0
  12. package/locales/pl-PL/models.json +12 -0
  13. package/locales/pt-BR/models.json +12 -0
  14. package/locales/ru-RU/models.json +12 -0
  15. package/locales/tr-TR/models.json +12 -0
  16. package/locales/vi-VN/models.json +12 -0
  17. package/locales/zh-CN/models.json +13 -1
  18. package/locales/zh-TW/models.json +12 -0
  19. package/next.config.mjs +5 -7
  20. package/package.json +3 -2
  21. package/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/TextArea.test.tsx +1 -0
  22. package/src/app/sw.ts +26 -0
  23. package/src/config/modelProviders/google.ts +54 -2
  24. package/src/config/modelProviders/taichu.ts +5 -2
  25. package/src/features/FileViewer/Renderer/Image/index.tsx +1 -0
  26. package/src/features/PWAInstall/Install.tsx +80 -0
  27. package/src/features/PWAInstall/index.tsx +6 -61
  28. package/src/libs/agent-runtime/google/index.test.ts +99 -5
  29. package/src/libs/agent-runtime/google/index.ts +58 -30
  30. package/src/server/services/discover/index.ts +1 -1
  31. package/src/services/message/server.ts +1 -1
  32. package/src/services/session/server.ts +2 -2
  33. package/src/store/chat/slices/plugin/action.ts +3 -1
  34. package/tsconfig.json +3 -3
@@ -332,9 +332,15 @@
332
332
  "gemini-1.5-flash-001": {
333
333
  "description": "Gemini 1.5 Flash 001 是一款高效的多模态模型,支持广泛应用的扩展。"
334
334
  },
335
+ "gemini-1.5-flash-002": {
336
+ "description": "Gemini 1.5 Flash 002 是一款高效的多模态模型,支持广泛应用的扩展。"
337
+ },
335
338
  "gemini-1.5-flash-8b-exp-0827": {
336
339
  "description": "Gemini 1.5 Flash 8B 0827 专为处理大规模任务场景设计,提供无与伦比的处理速度。"
337
340
  },
341
+ "gemini-1.5-flash-8b-exp-0924": {
342
+ "description": "Gemini 1.5 Flash 8B 0924 是最新的实验性模型,在文本和多模态用例中都有显著的性能提升。"
343
+ },
338
344
  "gemini-1.5-flash-exp-0827": {
339
345
  "description": "Gemini 1.5 Flash 0827 提供了优化后的多模态处理能力,适用多种复杂任务场景。"
340
346
  },
@@ -344,6 +350,9 @@
344
350
  "gemini-1.5-pro-001": {
345
351
  "description": "Gemini 1.5 Pro 001 是可扩展的多模态AI解决方案,支持广泛的复杂任务。"
346
352
  },
353
+ "gemini-1.5-pro-002": {
354
+ "description": "Gemini 1.5 Pro 002 是最新的生产就绪模型,提供更高质量的输出,特别在数学、长上下文和视觉任务方面有显著提升。"
355
+ },
347
356
  "gemini-1.5-pro-exp-0801": {
348
357
  "description": "Gemini 1.5 Pro 0801 提供出色的多模态处理能力,为应用开发带来更大灵活性。"
349
358
  },
@@ -872,7 +881,10 @@
872
881
  "description": "支持大规模上下文交互,适合复杂对话场景。"
873
882
  },
874
883
  "taichu_llm": {
875
- "description": "紫东太初语言大模型具备超强语言理解能力以及文本创作、知识问答、代码编程、数学计算、逻辑推理、情感分析、文本摘要等能力。创新性地将大数据预训练与多源丰富知识相结合,通过持续打磨算法技术,并不断吸收海量文本数据中词汇、结构、语法、语义等方面的新知识,实现模型效果不断进化。为用户提供更加便捷的信息和服务以及更为智能化的体验。"
884
+ "description": "Taichu 2.0 基于海量高质数据训练,具有更强的文本理解、内容创作、对话问答等能力"
885
+ },
886
+ "taichu_vqa": {
887
+ "description": "Taichu 2.0V 融合了图像理解、知识迁移、逻辑归因等能力,在图文问答领域表现突出"
876
888
  },
877
889
  "togethercomputer/StripedHyena-Nous-7B": {
878
890
  "description": "StripedHyena Nous (7B) 通过高效的策略和模型架构,提供增强的计算能力。"
@@ -332,9 +332,15 @@
332
332
  "gemini-1.5-flash-001": {
333
333
  "description": "Gemini 1.5 Flash 001 是一款高效的多模態模型,支持廣泛應用的擴展。"
334
334
  },
335
+ "gemini-1.5-flash-002": {
336
+ "description": "Gemini 1.5 Flash 002 是一款高效的多模態模型,支持廣泛應用的擴展。"
337
+ },
335
338
  "gemini-1.5-flash-8b-exp-0827": {
336
339
  "description": "Gemini 1.5 Flash 8B 0827 專為處理大規模任務場景設計,提供無與倫比的處理速度。"
337
340
  },
341
+ "gemini-1.5-flash-8b-exp-0924": {
342
+ "description": "Gemini 1.5 Flash 8B 0924 是最新的實驗性模型,在文本和多模態用例中都有顯著的性能提升。"
343
+ },
338
344
  "gemini-1.5-flash-exp-0827": {
339
345
  "description": "Gemini 1.5 Flash 0827 提供了優化後的多模態處理能力,適用多種複雜任務場景。"
340
346
  },
@@ -344,6 +350,9 @@
344
350
  "gemini-1.5-pro-001": {
345
351
  "description": "Gemini 1.5 Pro 001 是可擴展的多模態 AI 解決方案,支持廣泛的複雜任務。"
346
352
  },
353
+ "gemini-1.5-pro-002": {
354
+ "description": "Gemini 1.5 Pro 002 是最新的生產就緒模型,提供更高品質的輸出,特別在數學、長上下文和視覺任務方面有顯著提升。"
355
+ },
347
356
  "gemini-1.5-pro-exp-0801": {
348
357
  "description": "Gemini 1.5 Pro 0801 提供出色的多模態處理能力,為應用開發帶來更大靈活性。"
349
358
  },
@@ -874,6 +883,9 @@
874
883
  "taichu_llm": {
875
884
  "description": "紫東太初語言大模型具備超強語言理解能力以及文本創作、知識問答、代碼編程、數學計算、邏輯推理、情感分析、文本摘要等能力。創新性地將大數據預訓練與多源豐富知識相結合,通過持續打磨算法技術,並不斷吸收海量文本數據中詞彙、結構、語法、語義等方面的新知識,實現模型效果不斷進化。為用戶提供更加便捷的信息和服務以及更為智能化的體驗。"
876
885
  },
886
+ "taichu_vqa": {
887
+ "description": "Taichu 2.0V 融合了圖像理解、知識遷移、邏輯歸因等能力,在圖文問答領域表現突出。"
888
+ },
877
889
  "togethercomputer/StripedHyena-Nous-7B": {
878
890
  "description": "StripedHyena Nous (7B) 通過高效的策略和模型架構,提供增強的計算能力。"
879
891
  },
package/next.config.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import nextPWA from '@ducanh2912/next-pwa';
2
1
  import analyzer from '@next/bundle-analyzer';
3
2
  import { withSentryConfig } from '@sentry/nextjs';
3
+ import withSerwistInit from '@serwist/next';
4
4
 
5
5
  const isProd = process.env.NODE_ENV === 'production';
6
6
  const buildWithDocker = process.env.DOCKER === 'true';
@@ -192,12 +192,10 @@ const noWrapper = (config) => config;
192
192
  const withBundleAnalyzer = process.env.ANALYZE === 'true' ? analyzer() : noWrapper;
193
193
 
194
194
  const withPWA = isProd
195
- ? nextPWA({
196
- dest: 'public',
197
- register: true,
198
- workboxOptions: {
199
- skipWaiting: true,
200
- },
195
+ ? withSerwistInit({
196
+ register: false,
197
+ swDest: 'public/sw.js',
198
+ swSrc: 'src/app/sw.ts',
201
199
  })
202
200
  : noWrapper;
203
201
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.19.30",
3
+ "version": "1.19.32",
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",
@@ -127,6 +127,7 @@
127
127
  "@next/third-parties": "^14.2.6",
128
128
  "@react-spring/web": "^9.7.3",
129
129
  "@sentry/nextjs": "^7.119.0",
130
+ "@serwist/next": "^9.0.8",
130
131
  "@t3-oss/env-nextjs": "^0.11.0",
131
132
  "@tanstack/react-query": "^5.52.1",
132
133
  "@trpc/client": "next",
@@ -232,7 +233,6 @@
232
233
  },
233
234
  "devDependencies": {
234
235
  "@commitlint/cli": "^19.4.0",
235
- "@ducanh2912/next-pwa": "^10.2.8",
236
236
  "@edge-runtime/vm": "^4.0.2",
237
237
  "@lobehub/i18n-cli": "^1.19.1",
238
238
  "@lobehub/lint": "^1.24.4",
@@ -288,6 +288,7 @@
288
288
  "remark-cli": "^11.0.0",
289
289
  "remark-parse": "^10.0.2",
290
290
  "semantic-release": "^21.1.2",
291
+ "serwist": "^9.0.8",
291
292
  "stylelint": "^15.11.0",
292
293
  "supports-color": "8",
293
294
  "tsx": "^4.17.0",
@@ -150,6 +150,7 @@ describe('<InputArea />', () => {
150
150
  const beforeUnloadHandler = vi.fn();
151
151
 
152
152
  addEventListenerSpy.mockImplementation((event, handler) => {
153
+ // @ts-ignore
153
154
  if (event === 'beforeunload') {
154
155
  beforeUnloadHandler.mockImplementation(handler as any);
155
156
  }
package/src/app/sw.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { defaultCache } from '@serwist/next/worker';
2
+ import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist';
3
+ import { Serwist } from 'serwist';
4
+
5
+ // This declares the value of `injectionPoint` to TypeScript.
6
+ // `injectionPoint` is the string that will be replaced by the
7
+ // actual precache manifest. By default, this string is set to
8
+ // `"self.__SW_MANIFEST"`.
9
+ declare global {
10
+ interface WorkerGlobalScope extends SerwistGlobalConfig {
11
+ __SW_MANIFEST: (PrecacheEntry | string)[] | undefined;
12
+ }
13
+ }
14
+
15
+ // eslint-disable-next-line no-undef
16
+ declare const self: ServiceWorkerGlobalScope;
17
+
18
+ const serwist = new Serwist({
19
+ clientsClaim: true,
20
+ navigationPreload: true,
21
+ precacheEntries: self.__SW_MANIFEST,
22
+ runtimeCaching: defaultCache,
23
+ skipWaiting: true,
24
+ });
25
+
26
+ serwist.addEventListeners();
@@ -22,7 +22,6 @@ const Google: ModelProviderCard = {
22
22
  {
23
23
  description: 'Gemini 1.5 Flash 0827 提供了优化后的多模态处理能力,适用多种复杂任务场景。',
24
24
  displayName: 'Gemini 1.5 Flash 0827',
25
- enabled: true,
26
25
  functionCall: true,
27
26
  id: 'gemini-1.5-flash-exp-0827',
28
27
  maxOutput: 8192,
@@ -45,12 +44,49 @@ const Google: ModelProviderCard = {
45
44
  tokens: 1_048_576 + 8192,
46
45
  vision: true,
47
46
  },
47
+ {
48
+ description:
49
+ 'Gemini 1.5 Flash 8B 0924 是最新的实验性模型,在文本和多模态用例中都有显著的性能提升。',
50
+ displayName: 'Gemini 1.5 Flash 8B 0924',
51
+ enabled: true,
52
+ functionCall: true,
53
+ id: 'gemini-1.5-flash-8b-exp-0924',
54
+ maxOutput: 8192,
55
+ pricing: {
56
+ cachedInput: 0.018_75,
57
+ input: 0.075,
58
+ output: 0.3,
59
+ },
60
+ releasedAt: '2024-09-24',
61
+ tokens: 1_048_576 + 8192,
62
+ vision: true,
63
+ },
48
64
  {
49
65
  description: 'Gemini 1.5 Flash 001 是一款高效的多模态模型,支持广泛应用的扩展。',
50
66
  displayName: 'Gemini 1.5 Flash 001',
51
67
  functionCall: true,
52
68
  id: 'gemini-1.5-flash-001',
53
69
  maxOutput: 8192,
70
+ pricing: {
71
+ cachedInput: 0.018_75,
72
+ input: 0.075,
73
+ output: 0.3,
74
+ },
75
+ tokens: 1_048_576 + 8192,
76
+ vision: true,
77
+ },
78
+ {
79
+ description: 'Gemini 1.5 Flash 002 是一款高效的多模态模型,支持广泛应用的扩展。',
80
+ displayName: 'Gemini 1.5 Flash 002',
81
+ functionCall: true,
82
+ id: 'gemini-1.5-flash-002',
83
+ maxOutput: 8192,
84
+ pricing: {
85
+ cachedInput: 0.018_75,
86
+ input: 0.075,
87
+ output: 0.3,
88
+ },
89
+ releasedAt: '2024-09-25',
54
90
  tokens: 1_048_576 + 8192,
55
91
  vision: true,
56
92
  },
@@ -74,7 +110,6 @@ const Google: ModelProviderCard = {
74
110
  {
75
111
  description: 'Gemini 1.5 Pro 0827 结合最新优化技术,带来更高效的多模态数据处理能力。',
76
112
  displayName: 'Gemini 1.5 Pro 0827',
77
- enabled: true,
78
113
  functionCall: true,
79
114
  id: 'gemini-1.5-pro-exp-0827',
80
115
  maxOutput: 8192,
@@ -117,6 +152,23 @@ const Google: ModelProviderCard = {
117
152
  tokens: 2_097_152 + 8192,
118
153
  vision: true,
119
154
  },
155
+ {
156
+ description:
157
+ 'Gemini 1.5 Pro 002 是最新的生产就绪模型,提供更高质量的输出,特别在数学、长上下文和视觉任务方面有显著提升。',
158
+ displayName: 'Gemini 1.5 Pro 002',
159
+ enabled: true,
160
+ functionCall: true,
161
+ id: 'gemini-1.5-pro-002',
162
+ maxOutput: 8192,
163
+ pricing: {
164
+ cachedInput: 0.315,
165
+ input: 1.25,
166
+ output: 2.5,
167
+ },
168
+ releasedAt: '2024-09-24',
169
+ tokens: 2_097_152 + 8192,
170
+ vision: true,
171
+ },
120
172
  {
121
173
  description: 'Gemini 1.0 Pro 是Google的高性能AI模型,专为广泛任务扩展而设计。',
122
174
  displayName: 'Gemini 1.0 Pro',
@@ -8,11 +8,13 @@ const Taichu: ModelProviderCard = {
8
8
  'Taichu 2.0 基于海量高质数据训练,具有更强的文本理解、内容创作、对话问答等能力',
9
9
  displayName: 'Taichu 2.0',
10
10
  enabled: true,
11
- functionCall: false,
11
+ functionCall: true,
12
12
  id: 'taichu_llm',
13
13
  tokens: 32_768,
14
14
  },
15
- {
15
+ /*
16
+ // TODO: Not support for now
17
+ {
16
18
  description:
17
19
  'Taichu 2.0V 融合了图像理解、知识迁移、逻辑归因等能力,在图文问答领域表现突出',
18
20
  displayName: 'Taichu 2.0V',
@@ -20,6 +22,7 @@ const Taichu: ModelProviderCard = {
20
22
  tokens: 4096,
21
23
  vision: true,
22
24
  },
25
+ */
23
26
  ],
24
27
  checkModel: 'taichu_llm',
25
28
  description:
@@ -6,6 +6,7 @@ const ImageRenderer: DocRenderer = ({ mainState: { currentDocument } }) => {
6
6
 
7
7
  return (
8
8
  <Center height={'100%'} width={'100%'}>
9
+ {/* eslint-disable-next-line @next/next/no-img-element */}
9
10
  <img
10
11
  alt={fileName}
11
12
  height={'100%'}
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+ import { memo, useEffect, useLayoutEffect } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ import { BRANDING_NAME } from '@/const/branding';
8
+ import { PWA_INSTALL_ID } from '@/const/layoutTokens';
9
+ import { usePWAInstall } from '@/hooks/usePWAInstall';
10
+ import { useGlobalStore } from '@/store/global';
11
+ import { systemStatusSelectors } from '@/store/global/selectors';
12
+ import { useUserStore } from '@/store/user';
13
+
14
+ // @ts-ignore
15
+ const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.react.js'), {
16
+ ssr: false,
17
+ });
18
+
19
+ const PWAInstall = memo(() => {
20
+ const { t } = useTranslation('metadata');
21
+
22
+ const { install, canInstall } = usePWAInstall();
23
+
24
+ const isShowPWAGuide = useUserStore((s) => s.isShowPWAGuide);
25
+ const [hidePWAInstaller, updateSystemStatus] = useGlobalStore((s) => [
26
+ systemStatusSelectors.hidePWAInstaller(s),
27
+ s.updateSystemStatus,
28
+ ]);
29
+
30
+ // we need to make the pwa installer hidden by default
31
+ useLayoutEffect(() => {
32
+ sessionStorage.setItem('pwa-hide-install', 'true');
33
+ }, []);
34
+
35
+ const pwaInstall =
36
+ // eslint-disable-next-line unicorn/prefer-query-selector
37
+ typeof window === 'undefined' ? undefined : document.getElementById(PWA_INSTALL_ID);
38
+
39
+ // add an event listener to control the user close installer action
40
+ useEffect(() => {
41
+ if (!pwaInstall) return;
42
+
43
+ const handler = (e: Event) => {
44
+ const event = e as CustomEvent;
45
+
46
+ // it means user hide installer
47
+ if (event.detail.message === 'dismissed') {
48
+ updateSystemStatus({ hidePWAInstaller: true });
49
+ }
50
+ };
51
+
52
+ pwaInstall.addEventListener('pwa-user-choice-result-event', handler);
53
+ return () => {
54
+ pwaInstall.removeEventListener('pwa-user-choice-result-event', handler);
55
+ };
56
+ }, [pwaInstall]);
57
+
58
+ // trigger the PWA guide on demand
59
+ useEffect(() => {
60
+ if (!canInstall || hidePWAInstaller) return;
61
+
62
+ // trigger the pwa installer and register the service worker
63
+ if (isShowPWAGuide) {
64
+ install();
65
+ if ('serviceWorker' in navigator && window.serwist !== undefined) {
66
+ window.serwist.register();
67
+ }
68
+ }
69
+ }, [canInstall, hidePWAInstaller, isShowPWAGuide]);
70
+
71
+ return (
72
+ <PWA
73
+ description={t('chat.description', { appName: BRANDING_NAME })}
74
+ id={PWA_INSTALL_ID}
75
+ manifest-url={'/manifest.webmanifest'}
76
+ />
77
+ );
78
+ });
79
+
80
+ export default PWAInstall;
@@ -1,79 +1,24 @@
1
1
  'use client';
2
2
 
3
3
  import dynamic from 'next/dynamic';
4
- import { memo, useEffect, useLayoutEffect } from 'react';
5
- import { useTranslation } from 'react-i18next';
4
+ import { memo } from 'react';
6
5
 
7
- import { BRANDING_NAME } from '@/const/branding';
8
- import { PWA_INSTALL_ID } from '@/const/layoutTokens';
9
- import { usePWAInstall } from '@/hooks/usePWAInstall';
10
6
  import { usePlatform } from '@/hooks/usePlatform';
11
- import { useGlobalStore } from '@/store/global';
12
- import { systemStatusSelectors } from '@/store/global/selectors';
13
7
  import { useUserStore } from '@/store/user';
14
8
 
15
- // @ts-ignore
16
- const PWA: any = dynamic(() => import('@khmyznikov/pwa-install/dist/pwa-install.react.js'), {
9
+ const Install: any = dynamic(() => import('./Install'), {
17
10
  ssr: false,
18
11
  });
19
12
 
20
13
  const PWAInstall = memo(() => {
21
- const { t } = useTranslation('metadata');
22
14
  const { isPWA } = usePlatform();
23
-
24
- const { install, canInstall } = usePWAInstall();
25
-
26
15
  const isShowPWAGuide = useUserStore((s) => s.isShowPWAGuide);
27
- const [hidePWAInstaller, updateSystemStatus] = useGlobalStore((s) => [
28
- systemStatusSelectors.hidePWAInstaller(s),
29
- s.updateSystemStatus,
30
- ]);
31
-
32
- // we need to make the pwa installer hidden by default
33
- useLayoutEffect(() => {
34
- sessionStorage.setItem('pwa-hide-install', 'true');
35
- }, []);
36
-
37
- const pwaInstall =
38
- // eslint-disable-next-line unicorn/prefer-query-selector
39
- typeof window === 'undefined' ? undefined : document.getElementById(PWA_INSTALL_ID);
40
-
41
- // add an event listener to control the user close installer action
42
- useEffect(() => {
43
- if (!pwaInstall) return;
44
-
45
- const handler = (e: Event) => {
46
- const event = e as CustomEvent;
47
-
48
- // it means user hide installer
49
- if (event.detail.message === 'dismissed') {
50
- updateSystemStatus({ hidePWAInstaller: true });
51
- }
52
- };
53
-
54
- pwaInstall.addEventListener('pwa-user-choice-result-event', handler);
55
- return () => {
56
- pwaInstall.removeEventListener('pwa-user-choice-result-event', handler);
57
- };
58
- }, [pwaInstall]);
59
-
60
- // trigger the PWA guide on demand
61
- useEffect(() => {
62
- if (!canInstall || hidePWAInstaller) return;
63
16
 
64
- if (isShowPWAGuide) {
65
- install();
66
- }
67
- }, [canInstall, hidePWAInstaller, isShowPWAGuide]);
17
+ if (isPWA || !isShowPWAGuide) return null;
68
18
 
69
- if (isPWA) return null;
70
- return (
71
- <PWA
72
- description={t('chat.description', { appName: BRANDING_NAME })}
73
- id={PWA_INSTALL_ID}
74
- manifest-url={'/manifest.webmanifest'}
75
- />
76
- );
19
+ // only when the user is suitable for the pwa install and not install the pwa
20
+ // then show the installation guide
21
+ return <Install />;
77
22
  });
78
23
 
79
24
  export default PWAInstall;
@@ -304,6 +304,30 @@ describe('LobeGoogleAI', () => {
304
304
 
305
305
  describe('private method', () => {
306
306
  describe('convertContentToGooglePart', () => {
307
+ it('should handle text type messages', async () => {
308
+ const result = await instance['convertContentToGooglePart']({
309
+ type: 'text',
310
+ text: 'Hello',
311
+ });
312
+ expect(result).toEqual({ text: 'Hello' });
313
+ });
314
+
315
+ it('should handle base64 type images', async () => {
316
+ const base64Image =
317
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
318
+ const result = await instance['convertContentToGooglePart']({
319
+ type: 'image_url',
320
+ image_url: { url: base64Image },
321
+ });
322
+
323
+ expect(result).toEqual({
324
+ inlineData: {
325
+ data: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
326
+ mimeType: 'image/png',
327
+ },
328
+ });
329
+ });
330
+
307
331
  it('should handle URL type images', async () => {
308
332
  const imageUrl = 'http://example.com/image.png';
309
333
  const mockBase64 = 'mockBase64Data';
@@ -357,7 +381,7 @@ describe('LobeGoogleAI', () => {
357
381
  { content: 'Hi', role: 'assistant' },
358
382
  ];
359
383
 
360
- const contents = await instance['buildGoogleMessages'](messages, 'gemini-pro');
384
+ const contents = await instance['buildGoogleMessages'](messages, 'gemini-1.0');
361
385
 
362
386
  expect(contents).toHaveLength(3);
363
387
  expect(contents).toEqual([
@@ -373,7 +397,7 @@ describe('LobeGoogleAI', () => {
373
397
  { content: 'Who are you', role: 'user' },
374
398
  ];
375
399
 
376
- const contents = await instance['buildGoogleMessages'](messages, 'gemini-pro');
400
+ const contents = await instance['buildGoogleMessages'](messages, 'gemini-1.0');
377
401
 
378
402
  expect(contents).toHaveLength(3);
379
403
  expect(contents).toEqual([
@@ -487,9 +511,6 @@ describe('LobeGoogleAI', () => {
487
511
  });
488
512
  });
489
513
 
490
- // 类似地添加 array/string/number/boolean 类型schema的测试用例
491
- // ...
492
-
493
514
  it('should correctly convert nested schema', () => {
494
515
  const schema: JSONSchema7 = {
495
516
  type: 'object',
@@ -523,6 +544,36 @@ describe('LobeGoogleAI', () => {
523
544
  },
524
545
  });
525
546
  });
547
+
548
+ it('should correctly convert array schema', () => {
549
+ const schema: JSONSchema7 = {
550
+ type: 'array',
551
+ items: { type: 'string' },
552
+ };
553
+ const converted = instance['convertSchemaObject'](schema);
554
+ expect(converted).toEqual({
555
+ type: FunctionDeclarationSchemaType.ARRAY,
556
+ items: { type: FunctionDeclarationSchemaType.STRING },
557
+ });
558
+ });
559
+
560
+ it('should correctly convert string schema', () => {
561
+ const schema: JSONSchema7 = { type: 'string' };
562
+ const converted = instance['convertSchemaObject'](schema);
563
+ expect(converted).toEqual({ type: FunctionDeclarationSchemaType.STRING });
564
+ });
565
+
566
+ it('should correctly convert number schema', () => {
567
+ const schema: JSONSchema7 = { type: 'number' };
568
+ const converted = instance['convertSchemaObject'](schema);
569
+ expect(converted).toEqual({ type: FunctionDeclarationSchemaType.NUMBER });
570
+ });
571
+
572
+ it('should correctly convert boolean schema', () => {
573
+ const schema: JSONSchema7 = { type: 'boolean' };
574
+ const converted = instance['convertSchemaObject'](schema);
575
+ expect(converted).toEqual({ type: FunctionDeclarationSchemaType.BOOLEAN });
576
+ });
526
577
  });
527
578
 
528
579
  describe('convertOAIMessagesToGoogleMessage', () => {
@@ -592,6 +643,49 @@ describe('LobeGoogleAI', () => {
592
643
  ],
593
644
  });
594
645
  });
646
+
647
+ it('should correctly convert function call message', async () => {
648
+ const message = {
649
+ role: 'assistant',
650
+ tool_calls: [
651
+ {
652
+ id: 'call_1',
653
+ function: {
654
+ name: 'get_current_weather',
655
+ arguments: JSON.stringify({ location: 'London', unit: 'celsius' }),
656
+ },
657
+ type: 'function',
658
+ },
659
+ ],
660
+ } as OpenAIChatMessage;
661
+
662
+ const converted = await instance['convertOAIMessagesToGoogleMessage'](message);
663
+ expect(converted).toEqual({
664
+ role: 'function',
665
+ parts: [
666
+ {
667
+ functionCall: {
668
+ name: 'get_current_weather',
669
+ args: { location: 'London', unit: 'celsius' },
670
+ },
671
+ },
672
+ ],
673
+ });
674
+ });
675
+
676
+ it('should correctly handle empty content', async () => {
677
+ const message: OpenAIChatMessage = {
678
+ role: 'user',
679
+ content: '' as any, // explicitly set as empty string
680
+ };
681
+
682
+ const converted = await instance['convertOAIMessagesToGoogleMessage'](message);
683
+
684
+ expect(converted).toEqual({
685
+ role: 'user',
686
+ parts: [{ text: '' }],
687
+ });
688
+ });
595
689
  });
596
690
  });
597
691
  });