@lobehub/chat 1.23.1 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/Dockerfile +2 -0
  3. package/Dockerfile.database +2 -0
  4. package/locales/ar/modelProvider.json +16 -0
  5. package/locales/ar/models.json +27 -0
  6. package/locales/ar/providers.json +1 -0
  7. package/locales/bg-BG/modelProvider.json +16 -0
  8. package/locales/bg-BG/models.json +27 -0
  9. package/locales/bg-BG/providers.json +1 -0
  10. package/locales/de-DE/modelProvider.json +16 -0
  11. package/locales/de-DE/models.json +27 -0
  12. package/locales/de-DE/providers.json +1 -0
  13. package/locales/en-US/modelProvider.json +16 -0
  14. package/locales/en-US/models.json +27 -0
  15. package/locales/en-US/providers.json +1 -0
  16. package/locales/es-ES/modelProvider.json +16 -0
  17. package/locales/es-ES/models.json +27 -0
  18. package/locales/es-ES/providers.json +1 -0
  19. package/locales/fr-FR/modelProvider.json +16 -0
  20. package/locales/fr-FR/models.json +27 -0
  21. package/locales/fr-FR/providers.json +1 -0
  22. package/locales/it-IT/modelProvider.json +16 -0
  23. package/locales/it-IT/models.json +27 -0
  24. package/locales/it-IT/providers.json +1 -0
  25. package/locales/ja-JP/modelProvider.json +16 -0
  26. package/locales/ja-JP/models.json +27 -0
  27. package/locales/ja-JP/providers.json +1 -0
  28. package/locales/ko-KR/modelProvider.json +16 -0
  29. package/locales/ko-KR/models.json +27 -0
  30. package/locales/ko-KR/providers.json +1 -0
  31. package/locales/nl-NL/modelProvider.json +16 -0
  32. package/locales/nl-NL/models.json +27 -0
  33. package/locales/nl-NL/providers.json +1 -0
  34. package/locales/pl-PL/modelProvider.json +16 -0
  35. package/locales/pl-PL/models.json +27 -0
  36. package/locales/pl-PL/providers.json +1 -0
  37. package/locales/pt-BR/modelProvider.json +16 -0
  38. package/locales/pt-BR/models.json +27 -0
  39. package/locales/pt-BR/providers.json +1 -0
  40. package/locales/ru-RU/modelProvider.json +16 -0
  41. package/locales/ru-RU/models.json +27 -0
  42. package/locales/ru-RU/providers.json +1 -0
  43. package/locales/tr-TR/modelProvider.json +16 -0
  44. package/locales/tr-TR/models.json +27 -0
  45. package/locales/tr-TR/providers.json +1 -0
  46. package/locales/vi-VN/modelProvider.json +16 -0
  47. package/locales/vi-VN/models.json +27 -0
  48. package/locales/vi-VN/providers.json +1 -0
  49. package/locales/zh-CN/modelProvider.json +16 -0
  50. package/locales/zh-CN/models.json +27 -0
  51. package/locales/zh-CN/providers.json +1 -0
  52. package/locales/zh-TW/modelProvider.json +16 -0
  53. package/locales/zh-TW/models.json +27 -0
  54. package/locales/zh-TW/providers.json +1 -0
  55. package/package.json +4 -4
  56. package/src/app/(main)/chat/(workspace)/_layout/Desktop/Portal.tsx +8 -8
  57. package/src/app/(main)/settings/llm/ProviderList/SenseNova/index.tsx +44 -0
  58. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +4 -0
  59. package/src/config/llm.ts +10 -0
  60. package/src/config/modelProviders/index.ts +4 -0
  61. package/src/config/modelProviders/sensenova.ts +124 -0
  62. package/src/const/auth.ts +3 -0
  63. package/src/const/layoutTokens.ts +3 -2
  64. package/src/const/settings/llm.ts +5 -0
  65. package/src/features/Conversation/Error/APIKeyForm/SenseNova.tsx +49 -0
  66. package/src/features/Conversation/Error/APIKeyForm/index.tsx +3 -0
  67. package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
  68. package/src/libs/agent-runtime/google/index.test.ts +4 -91
  69. package/src/libs/agent-runtime/google/index.ts +5 -49
  70. package/src/libs/agent-runtime/index.ts +1 -0
  71. package/src/libs/agent-runtime/sensenova/authToken.test.ts +18 -0
  72. package/src/libs/agent-runtime/sensenova/authToken.ts +27 -0
  73. package/src/libs/agent-runtime/sensenova/index.test.ts +321 -0
  74. package/src/libs/agent-runtime/sensenova/index.ts +98 -0
  75. package/src/libs/agent-runtime/types/type.ts +1 -0
  76. package/src/locales/default/modelProvider.ts +17 -0
  77. package/src/server/globalConfig/index.ts +12 -0
  78. package/src/server/modules/AgentRuntime/index.ts +10 -0
  79. package/src/services/_auth.ts +14 -0
  80. package/src/store/user/slices/modelList/selectors/keyVaults.ts +2 -0
  81. package/src/store/user/slices/modelList/selectors/modelConfig.ts +2 -0
  82. package/src/types/user/settings/keyVaults.ts +6 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.23.1",
3
+ "version": "1.24.1",
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",
@@ -43,7 +43,7 @@
43
43
  "docs:seo": "lobe-seo && npm run lint:mdx",
44
44
  "i18n": "npm run workflow:i18n && lobe-i18n",
45
45
  "lint": "npm run lint:ts && npm run lint:style && npm run type-check && npm run lint:circular",
46
- "lint:circular": "dpdm src/**/*.ts --warning false --tree false --exit-code circular:1 -T true --skip-dynamic-imports circular",
46
+ "lint:circular": "dpdm src/**/*.ts --no-warning --no-tree --exit-code circular:1 --no-progress -T true --skip-dynamic-imports circular",
47
47
  "lint:md": "remark . --quiet --frail --output",
48
48
  "lint:mdx": "npm run workflow:mdx-with-lint && prettier -c --write \"{src,docs}/**/*.mdx\" && npm run workflow:mdx-with-lint",
49
49
  "lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
@@ -116,7 +116,7 @@
116
116
  "@clerk/themes": "^2.1.37",
117
117
  "@codesandbox/sandpack-react": "^2.19.9",
118
118
  "@cyntler/react-doc-viewer": "^1.17.0",
119
- "@google/generative-ai": "^0.16.1",
119
+ "@google/generative-ai": "^0.21.0",
120
120
  "@huggingface/inference": "^2.8.1",
121
121
  "@icons-pack/react-simple-icons": "9.6.0",
122
122
  "@khmyznikov/pwa-install": "^0.3.9",
@@ -270,7 +270,7 @@
270
270
  "commitlint": "^19.5.0",
271
271
  "consola": "^3.2.3",
272
272
  "dotenv": "^16.4.5",
273
- "dpdm": "^3.14.0",
273
+ "dpdm-fast": "^1.0.4",
274
274
  "drizzle-kit": "^0.26.0",
275
275
  "eslint": "^8.57.1",
276
276
  "eslint-plugin-mdx": "^2.3.4",
@@ -6,7 +6,11 @@ import { rgba } from 'polished';
6
6
  import { PropsWithChildren, memo } from 'react';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
- import { CHAT_DOCK_TOOL_UI_WIDTH, CHAT_DOCK_WIDTH, MAX_WIDTH } from '@/const/layoutTokens';
9
+ import {
10
+ CHAT_PORTAL_MAX_WIDTH,
11
+ CHAT_PORTAL_TOOL_UI_WIDTH,
12
+ CHAT_PORTAL_WIDTH,
13
+ } from '@/const/layoutTokens';
10
14
  import { useChatStore } from '@/store/chat';
11
15
  import { chatPortalSelectors } from '@/store/chat/slices/portal/selectors';
12
16
 
@@ -25,12 +29,8 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({
25
29
  `,
26
30
  panel: css`
27
31
  overflow: hidden;
28
-
29
32
  height: 100%;
30
- margin: 4px;
31
-
32
33
  background: ${isDarkMode ? rgba(token.colorBgElevated, 0.8) : token.colorBgElevated};
33
- border-radius: 8px;
34
34
  `,
35
35
  }));
36
36
 
@@ -53,8 +53,8 @@ const PortalPanel = memo(({ children }: PropsWithChildren) => {
53
53
  }}
54
54
  expand
55
55
  hanlderStyle={{ display: 'none' }}
56
- maxWidth={MAX_WIDTH}
57
- minWidth={showArtifactUI || showToolUI ? CHAT_DOCK_TOOL_UI_WIDTH : CHAT_DOCK_WIDTH}
56
+ maxWidth={CHAT_PORTAL_MAX_WIDTH}
57
+ minWidth={showArtifactUI || showToolUI ? CHAT_PORTAL_TOOL_UI_WIDTH : CHAT_PORTAL_WIDTH}
58
58
  mode={md ? 'fixed' : 'float'}
59
59
  placement={'right'}
60
60
  showHandlerWhenUnexpand={false}
@@ -65,7 +65,7 @@ const PortalPanel = memo(({ children }: PropsWithChildren) => {
65
65
  flex: 'none',
66
66
  height: '100%',
67
67
  maxHeight: '100vh',
68
- minWidth: CHAT_DOCK_WIDTH,
68
+ minWidth: CHAT_PORTAL_WIDTH,
69
69
  }}
70
70
  >
71
71
  <Flexbox className={styles.panel}>{children}</Flexbox>
@@ -0,0 +1,44 @@
1
+ 'use client';
2
+
3
+ import { Input } from 'antd';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { SenseNovaProviderCard } from '@/config/modelProviders';
7
+ import { GlobalLLMProviderKey } from '@/types/user/settings';
8
+
9
+ import { KeyVaultsConfigKey } from '../../const';
10
+ import { ProviderItem } from '../../type';
11
+
12
+ const providerKey: GlobalLLMProviderKey = 'sensenova';
13
+
14
+ export const useSenseNovaProvider = (): ProviderItem => {
15
+ const { t } = useTranslation('modelProvider');
16
+
17
+ return {
18
+ ...SenseNovaProviderCard,
19
+ apiKeyItems: [
20
+ {
21
+ children: (
22
+ <Input.Password
23
+ autoComplete={'new-password'}
24
+ placeholder={t(`${providerKey}.sensenovaAccessKeyID.placeholder`)}
25
+ />
26
+ ),
27
+ desc: t(`${providerKey}.sensenovaAccessKeyID.desc`),
28
+ label: t(`${providerKey}.sensenovaAccessKeyID.title`),
29
+ name: [KeyVaultsConfigKey, providerKey, 'sensenovaAccessKeyID'],
30
+ },
31
+ {
32
+ children: (
33
+ <Input.Password
34
+ autoComplete={'new-password'}
35
+ placeholder={t(`${providerKey}.sensenovaAccessKeySecret.placeholder`)}
36
+ />
37
+ ),
38
+ desc: t(`${providerKey}.sensenovaAccessKeySecret.desc`),
39
+ label: t(`${providerKey}.sensenovaAccessKeySecret.title`),
40
+ name: [KeyVaultsConfigKey, providerKey, 'sensenovaAccessKeySecret'],
41
+ },
42
+ ],
43
+ };
44
+ };
@@ -35,6 +35,7 @@ import { useHuggingFaceProvider } from './HuggingFace';
35
35
  import { useOllamaProvider } from './Ollama';
36
36
  import { useOpenAIProvider } from './OpenAI';
37
37
  import { useWenxinProvider } from './Wenxin';
38
+ import { useSenseNovaProvider } from './SenseNova';
38
39
 
39
40
  export const useProviderList = (): ProviderItem[] => {
40
41
  const AzureProvider = useAzureProvider();
@@ -44,6 +45,7 @@ export const useProviderList = (): ProviderItem[] => {
44
45
  const GithubProvider = useGithubProvider();
45
46
  const HuggingFaceProvider = useHuggingFaceProvider();
46
47
  const WenxinProvider = useWenxinProvider();
48
+ const SenseNovaProvider = useSenseNovaProvider();
47
49
 
48
50
  return useMemo(
49
51
  () => [
@@ -71,6 +73,7 @@ export const useProviderList = (): ProviderItem[] => {
71
73
  SparkProviderCard,
72
74
  ZhiPuProviderCard,
73
75
  ZeroOneProviderCard,
76
+ SenseNovaProvider,
74
77
  StepfunProviderCard,
75
78
  MoonshotProviderCard,
76
79
  BaichuanProviderCard,
@@ -87,6 +90,7 @@ export const useProviderList = (): ProviderItem[] => {
87
90
  GithubProvider,
88
91
  WenxinProvider,
89
92
  HuggingFaceProvider,
93
+ SenseNovaProvider,
90
94
  ],
91
95
  );
92
96
  };
package/src/config/llm.ts CHANGED
@@ -144,6 +144,11 @@ export const getLLMConfig = () => {
144
144
  HUGGINGFACE_API_KEY: z.string().optional(),
145
145
  HUGGINGFACE_PROXY_URL: z.string().optional(),
146
146
  HUGGINGFACE_MODEL_LIST: z.string().optional(),
147
+
148
+ ENABLED_SENSENOVA: z.boolean(),
149
+ SENSENOVA_ACCESS_KEY_ID: z.string().optional(),
150
+ SENSENOVA_ACCESS_KEY_SECRET: z.string().optional(),
151
+ SENSENOVA_MODEL_LIST: z.string().optional(),
147
152
  },
148
153
  runtimeEnv: {
149
154
  API_KEY_SELECT_MODE: process.env.API_KEY_SELECT_MODE,
@@ -285,6 +290,11 @@ export const getLLMConfig = () => {
285
290
  HUGGINGFACE_API_KEY: process.env.HUGGINGFACE_API_KEY,
286
291
  HUGGINGFACE_PROXY_URL: process.env.HUGGINGFACE_PROXY_URL,
287
292
  HUGGINGFACE_MODEL_LIST: process.env.HUGGINGFACE_MODEL_LIST,
293
+
294
+ ENABLED_SENSENOVA: !!process.env.SENSENOVA_ACCESS_KEY_ID && !!process.env.SENSENOVA_ACCESS_KEY_SECRET,
295
+ SENSENOVA_ACCESS_KEY_ID: process.env.SENSENOVA_ACCESS_KEY_ID,
296
+ SENSENOVA_ACCESS_KEY_SECRET: process.env.SENSENOVA_ACCESS_KEY_SECRET,
297
+ SENSENOVA_MODEL_LIST: process.env.SENSENOVA_MODEL_LIST,
288
298
  },
289
299
  });
290
300
  };
@@ -22,6 +22,7 @@ import OpenAIProvider from './openai';
22
22
  import OpenRouterProvider from './openrouter';
23
23
  import PerplexityProvider from './perplexity';
24
24
  import QwenProvider from './qwen';
25
+ import SenseNovaProvider from './sensenova';
25
26
  import SiliconCloudProvider from './siliconcloud';
26
27
  import SparkProvider from './spark';
27
28
  import StepfunProvider from './stepfun';
@@ -63,6 +64,7 @@ export const LOBE_DEFAULT_MODEL_LIST: ChatModelCard[] = [
63
64
  Ai21Provider.chatModels,
64
65
  HunyuanProvider.chatModels,
65
66
  WenxinProvider.chatModels,
67
+ SenseNovaProvider.chatModels,
66
68
  ].flat();
67
69
 
68
70
  export const DEFAULT_MODEL_PROVIDER_LIST = [
@@ -90,6 +92,7 @@ export const DEFAULT_MODEL_PROVIDER_LIST = [
90
92
  SparkProvider,
91
93
  ZhiPuProvider,
92
94
  ZeroOneProvider,
95
+ SenseNovaProvider,
93
96
  StepfunProvider,
94
97
  MoonshotProvider,
95
98
  BaichuanProvider,
@@ -130,6 +133,7 @@ export { default as OpenAIProviderCard } from './openai';
130
133
  export { default as OpenRouterProviderCard } from './openrouter';
131
134
  export { default as PerplexityProviderCard } from './perplexity';
132
135
  export { default as QwenProviderCard } from './qwen';
136
+ export { default as SenseNovaProviderCard } from './sensenova';
133
137
  export { default as SiliconCloudProviderCard } from './siliconcloud';
134
138
  export { default as SparkProviderCard } from './spark';
135
139
  export { default as StepfunProviderCard } from './stepfun';
@@ -0,0 +1,124 @@
1
+ import { ModelProviderCard } from '@/types/llm';
2
+
3
+ // ref https://platform.sensenova.cn/pricing
4
+ // ref https://platform.sensenova.cn/release?path=/release-202409.md
5
+ const SenseNova: ModelProviderCard = {
6
+ chatModels: [
7
+ {
8
+ description: '最新版本模型 (V5.5),128K上下文长度,在数学推理、英文对话、指令跟随以及长文本理解等领域能力显著提升,比肩GPT-4o',
9
+ displayName: 'SenseChat 5.5',
10
+ enabled: true,
11
+ functionCall: true,
12
+ id: 'SenseChat-5',
13
+ pricing: {
14
+ currency: 'CNY',
15
+ input: 40,
16
+ output: 100,
17
+ },
18
+ tokens: 131_072,
19
+ },
20
+ {
21
+ description: '最新版本模型 (V5.5),16K上下文长度,支持多图的输入,全面实现模型基础能力优化,在对象属性识别、空间关系、动作事件识别、场景理解、情感识别、逻辑常识推理和文本理解生成上都实现了较大提升。',
22
+ displayName: 'SenseChat 5.5 Vision',
23
+ enabled: true,
24
+ id: 'SenseChat-Vision',
25
+ pricing: {
26
+ currency: 'CNY',
27
+ input: 100,
28
+ output: 100,
29
+ },
30
+ tokens: 16_384,
31
+ vision: true,
32
+ },
33
+ {
34
+ description: '适用于快速问答、模型微调场景',
35
+ displayName: 'SenseChat 5.0 Turbo',
36
+ enabled: true,
37
+ id: 'SenseChat-Turbo',
38
+ pricing: {
39
+ currency: 'CNY',
40
+ input: 2,
41
+ output: 5,
42
+ },
43
+ tokens: 32_768,
44
+ },
45
+ {
46
+ description: '32K上下文长度,在粤语的对话理解上超越了GPT-4,在知识、推理、数学及代码编写等多个领域均能与GPT-4 Turbo相媲美',
47
+ displayName: 'SenseChat 5.0 Cantonese',
48
+ id: 'SenseChat-5-Cantonese',
49
+ pricing: {
50
+ currency: 'CNY',
51
+ input: 27,
52
+ output: 27,
53
+ },
54
+ tokens: 32_768,
55
+ },
56
+ {
57
+ description: '基础版本模型 (V4),128K上下文长度,在长文本理解及生成等任务中表现出色',
58
+ displayName: 'SenseChat 4.0 128K',
59
+ enabled: true,
60
+ id: 'SenseChat-128K',
61
+ pricing: {
62
+ currency: 'CNY',
63
+ input: 60,
64
+ output: 60,
65
+ },
66
+ tokens: 131_072,
67
+ },
68
+ {
69
+ description: '基础版本模型 (V4),32K上下文长度,灵活应用于各类场景',
70
+ displayName: 'SenseChat 4.0 32K',
71
+ enabled: true,
72
+ id: 'SenseChat-32K',
73
+ pricing: {
74
+ currency: 'CNY',
75
+ input: 36,
76
+ output: 36,
77
+ },
78
+ tokens: 32_768,
79
+ },
80
+ {
81
+ description: '基础版本模型 (V4),4K上下文长度,通用能力强大',
82
+ displayName: 'SenseChat 4.0 4K',
83
+ enabled: true,
84
+ id: 'SenseChat',
85
+ pricing: {
86
+ currency: 'CNY',
87
+ input: 12,
88
+ output: 12,
89
+ },
90
+ tokens: 4096,
91
+ },
92
+ {
93
+ description: '标准版模型,8K上下文长度,高响应速度',
94
+ displayName: 'SenseChat Character',
95
+ id: 'SenseChat-Character',
96
+ pricing: {
97
+ currency: 'CNY',
98
+ input: 12,
99
+ output: 12,
100
+ },
101
+ tokens: 8192,
102
+ },
103
+ {
104
+ description: '高级版模型,32K上下文长度,能力全面提升,支持中/英文对话',
105
+ displayName: 'SenseChat Character Pro',
106
+ id: 'SenseChat-Character-Pro',
107
+ pricing: {
108
+ currency: 'CNY',
109
+ input: 15,
110
+ output: 15,
111
+ },
112
+ tokens: 32_768,
113
+ },
114
+ ],
115
+ checkModel: 'SenseChat-Turbo',
116
+ disableBrowserRequest: true,
117
+ id: 'sensenova',
118
+ modelList: { showModelFetcher: true },
119
+ modelsUrl: 'https://platform.sensenova.cn/pricing',
120
+ name: 'SenseNova',
121
+ url: 'https://platform.sensenova.cn/home',
122
+ };
123
+
124
+ export default SenseNova;
package/src/const/auth.ts CHANGED
@@ -40,6 +40,9 @@ export interface JWTPayload {
40
40
  wenxinAccessKey?: string;
41
41
  wenxinSecretKey?: string;
42
42
 
43
+ sensenovaAccessKeyID?: string;
44
+ sensenovaAccessKeySecret?: string;
45
+
43
46
  /**
44
47
  * user id
45
48
  * in client db mode it's a uuid
@@ -8,8 +8,9 @@ export const CHAT_TEXTAREA_HEIGHT = 160;
8
8
  export const CHAT_TEXTAREA_HEIGHT_MOBILE = 108;
9
9
  export const CHAT_SIDEBAR_WIDTH = 280;
10
10
 
11
- export const CHAT_DOCK_WIDTH = 400;
12
- export const CHAT_DOCK_TOOL_UI_WIDTH = 600;
11
+ export const CHAT_PORTAL_WIDTH = 400;
12
+ export const CHAT_PORTAL_MAX_WIDTH = 1280;
13
+ export const CHAT_PORTAL_TOOL_UI_WIDTH = 600;
13
14
 
14
15
  export const MARKET_SIDEBAR_WIDTH = 400;
15
16
  export const FOLDER_WIDTH = 270;
@@ -20,6 +20,7 @@ import {
20
20
  OpenRouterProviderCard,
21
21
  PerplexityProviderCard,
22
22
  QwenProviderCard,
23
+ SenseNovaProviderCard,
23
24
  SiliconCloudProviderCard,
24
25
  SparkProviderCard,
25
26
  StepfunProviderCard,
@@ -123,6 +124,10 @@ export const DEFAULT_LLM_CONFIG: UserModelProviderConfig = {
123
124
  enabled: false,
124
125
  enabledModels: filterEnabledModels(QwenProviderCard),
125
126
  },
127
+ sensenova: {
128
+ enabled: false,
129
+ enabledModels: filterEnabledModels(SenseNovaProviderCard),
130
+ },
126
131
  siliconcloud: {
127
132
  enabled: false,
128
133
  enabledModels: filterEnabledModels(SiliconCloudProviderCard),
@@ -0,0 +1,49 @@
1
+ import { SenseNova } from '@lobehub/icons';
2
+ import { Input } from 'antd';
3
+ import { memo } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { ModelProvider } from '@/libs/agent-runtime';
7
+ import { useUserStore } from '@/store/user';
8
+ import { keyVaultsConfigSelectors } from '@/store/user/selectors';
9
+
10
+ import { FormAction } from '../style';
11
+
12
+ const SenseNovaForm = memo(() => {
13
+ const { t } = useTranslation('modelProvider');
14
+
15
+ const [sensenovaAccessKeyID, sensenovaAccessKeySecret, setConfig] = useUserStore((s) => [
16
+ keyVaultsConfigSelectors.sensenovaConfig(s).sensenovaAccessKeyID,
17
+ keyVaultsConfigSelectors.sensenovaConfig(s).sensenovaAccessKeySecret,
18
+ s.updateKeyVaultConfig,
19
+ ]);
20
+
21
+ return (
22
+ <FormAction
23
+ avatar={<SenseNova color={SenseNova.colorPrimary} size={56} />}
24
+ description={t('sensenova.unlock.description')}
25
+ title={t('sensenova.unlock.title')}
26
+ >
27
+ <Input.Password
28
+ autoComplete={'new-password'}
29
+ onChange={(e) => {
30
+ setConfig(ModelProvider.SenseNova, { sensenovaAccessKeyID: e.target.value });
31
+ }}
32
+ placeholder={t('sensenova.sensenovaAccessKeyID.placeholder')}
33
+ type={'block'}
34
+ value={sensenovaAccessKeyID}
35
+ />
36
+ <Input.Password
37
+ autoComplete={'new-password'}
38
+ onChange={(e) => {
39
+ setConfig(ModelProvider.SenseNova, { sensenovaAccessKeySecret: e.target.value });
40
+ }}
41
+ placeholder={t('sensenova.sensenovaAccessKeySecret.placeholder')}
42
+ type={'block'}
43
+ value={sensenovaAccessKeySecret}
44
+ />
45
+ </FormAction>
46
+ );
47
+ });
48
+
49
+ export default SenseNovaForm;
@@ -10,6 +10,7 @@ import { GlobalLLMProviderKey } from '@/types/user/settings';
10
10
 
11
11
  import BedrockForm from './Bedrock';
12
12
  import ProviderApiKeyForm from './ProviderApiKeyForm';
13
+ import SenseNovaForm from './SenseNova';
13
14
  import WenxinForm from './Wenxin';
14
15
 
15
16
  interface APIKeyFormProps {
@@ -66,6 +67,8 @@ const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
66
67
  <Center gap={16} style={{ maxWidth: 300 }}>
67
68
  {provider === ModelProvider.Bedrock ? (
68
69
  <BedrockForm />
70
+ ) : provider === ModelProvider.SenseNova ? (
71
+ <SenseNovaForm />
69
72
  ) : provider === ModelProvider.Wenxin ? (
70
73
  <WenxinForm />
71
74
  ) : (
@@ -25,6 +25,7 @@ import { LobeOpenAI } from './openai';
25
25
  import { LobeOpenRouterAI } from './openrouter';
26
26
  import { LobePerplexityAI } from './perplexity';
27
27
  import { LobeQwenAI } from './qwen';
28
+ import { LobeSenseNovaAI } from './sensenova';
28
29
  import { LobeSiliconCloudAI } from './siliconcloud';
29
30
  import { LobeSparkAI } from './spark';
30
31
  import { LobeStepfunAI } from './stepfun';
@@ -146,6 +147,7 @@ class AgentRuntime {
146
147
  openrouter: Partial<ClientOptions>;
147
148
  perplexity: Partial<ClientOptions>;
148
149
  qwen: Partial<ClientOptions>;
150
+ sensenova: Partial<ClientOptions>;
149
151
  siliconcloud: Partial<ClientOptions>;
150
152
  spark: Partial<ClientOptions>;
151
153
  stepfun: Partial<ClientOptions>;
@@ -314,6 +316,11 @@ class AgentRuntime {
314
316
  runtimeModel = new LobeHunyuanAI(params.hunyuan);
315
317
  break;
316
318
  }
319
+
320
+ case ModelProvider.SenseNova: {
321
+ runtimeModel = await LobeSenseNovaAI.fromAPIKey(params.sensenova);
322
+ break;
323
+ }
317
324
  }
318
325
 
319
326
  return new AgentRuntime(runtimeModel);
@@ -1,6 +1,5 @@
1
1
  // @vitest-environment edge-runtime
2
- import { FunctionDeclarationSchemaType, FunctionDeclarationsTool } from '@google/generative-ai';
3
- import { JSONSchema7 } from 'json-schema';
2
+ import { FunctionDeclarationsTool } from '@google/generative-ai';
4
3
  import OpenAI from 'openai';
5
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
5
 
@@ -479,10 +478,10 @@ describe('LobeGoogleAI', () => {
479
478
  name: 'testTool',
480
479
  description: 'A test tool',
481
480
  parameters: {
482
- type: FunctionDeclarationSchemaType.OBJECT,
481
+ type: 'object',
483
482
  properties: {
484
- param1: { type: FunctionDeclarationSchemaType.STRING },
485
- param2: { type: FunctionDeclarationSchemaType.NUMBER },
483
+ param1: { type: 'string' },
484
+ param2: { type: 'number' },
486
485
  },
487
486
  required: ['param1'],
488
487
  },
@@ -490,92 +489,6 @@ describe('LobeGoogleAI', () => {
490
489
  });
491
490
  });
492
491
 
493
- describe('convertSchemaObject', () => {
494
- it('should correctly convert object schema', () => {
495
- const schema: JSONSchema7 = {
496
- type: 'object',
497
- properties: {
498
- prop1: { type: 'string' },
499
- prop2: { type: 'number' },
500
- },
501
- };
502
-
503
- const converted = instance['convertSchemaObject'](schema);
504
-
505
- expect(converted).toEqual({
506
- type: FunctionDeclarationSchemaType.OBJECT,
507
- properties: {
508
- prop1: { type: FunctionDeclarationSchemaType.STRING },
509
- prop2: { type: FunctionDeclarationSchemaType.NUMBER },
510
- },
511
- });
512
- });
513
-
514
- it('should correctly convert nested schema', () => {
515
- const schema: JSONSchema7 = {
516
- type: 'object',
517
- properties: {
518
- nested: {
519
- type: 'array',
520
- items: {
521
- type: 'object',
522
- properties: {
523
- prop: { type: 'string' },
524
- },
525
- },
526
- },
527
- },
528
- };
529
-
530
- const converted = instance['convertSchemaObject'](schema);
531
-
532
- expect(converted).toEqual({
533
- type: FunctionDeclarationSchemaType.OBJECT,
534
- properties: {
535
- nested: {
536
- type: FunctionDeclarationSchemaType.ARRAY,
537
- items: {
538
- type: FunctionDeclarationSchemaType.OBJECT,
539
- properties: {
540
- prop: { type: FunctionDeclarationSchemaType.STRING },
541
- },
542
- },
543
- },
544
- },
545
- });
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
- });
577
- });
578
-
579
492
  describe('convertOAIMessagesToGoogleMessage', () => {
580
493
  it('should correctly convert assistant message', async () => {
581
494
  const message: OpenAIChatMessage = {
@@ -2,14 +2,11 @@ import {
2
2
  Content,
3
3
  FunctionCallPart,
4
4
  FunctionDeclaration,
5
- FunctionDeclarationSchemaProperty,
6
- FunctionDeclarationSchemaType,
7
5
  Tool as GoogleFunctionCallTool,
8
6
  GoogleGenerativeAI,
9
7
  Part,
8
+ SchemaType,
10
9
  } from '@google/generative-ai';
11
- import { JSONSchema7 } from 'json-schema';
12
- import { transform } from 'lodash-es';
13
10
 
14
11
  import { imageUrlToBase64 } from '@/utils/imageToBase64';
15
12
  import { safeParseJSON } from '@/utils/safeParseJSON';
@@ -190,13 +187,12 @@ export class LobeGoogleAI implements LobeRuntimeAI {
190
187
  };
191
188
  };
192
189
 
193
- // convert messages from the Vercel AI SDK Format to the format
194
- // that is expected by the Google GenAI SDK
190
+ // convert messages from the OpenAI format to Google GenAI SDK
195
191
  private buildGoogleMessages = async (
196
192
  messages: OpenAIChatMessage[],
197
193
  model: string,
198
194
  ): Promise<Content[]> => {
199
- // if the model is gemini-1.0 we don't need to pair messages
195
+ // if the model is gemini-1.0 we need to pair messages
200
196
  if (model.startsWith('gemini-1.0')) {
201
197
  const contents: Content[] = [];
202
198
  let lastRole = 'model';
@@ -298,52 +294,12 @@ export class LobeGoogleAI implements LobeRuntimeAI {
298
294
  name: functionDeclaration.name,
299
295
  parameters: {
300
296
  description: parameters?.description,
301
- properties: transform(parameters?.properties, (result, value, key: string) => {
302
- result[key] = this.convertSchemaObject(value as JSONSchema7);
303
- }),
297
+ properties: parameters?.properties,
304
298
  required: parameters?.required,
305
- type: FunctionDeclarationSchemaType.OBJECT,
299
+ type: SchemaType.OBJECT,
306
300
  },
307
301
  };
308
302
  };
309
-
310
- private convertSchemaObject(schema: JSONSchema7): FunctionDeclarationSchemaProperty {
311
- switch (schema.type) {
312
- default:
313
- case 'object': {
314
- return {
315
- ...schema,
316
- properties: Object.fromEntries(
317
- Object.entries(schema.properties || {}).map(([key, value]) => [
318
- key,
319
- this.convertSchemaObject(value as JSONSchema7),
320
- ]),
321
- ),
322
- type: FunctionDeclarationSchemaType.OBJECT,
323
- } as any;
324
- }
325
-
326
- case 'array': {
327
- return {
328
- ...schema,
329
- items: this.convertSchemaObject(schema.items as JSONSchema7),
330
- type: FunctionDeclarationSchemaType.ARRAY,
331
- } as any;
332
- }
333
-
334
- case 'string': {
335
- return { ...schema, type: FunctionDeclarationSchemaType.STRING } as any;
336
- }
337
-
338
- case 'number': {
339
- return { ...schema, type: FunctionDeclarationSchemaType.NUMBER } as any;
340
- }
341
-
342
- case 'boolean': {
343
- return { ...schema, type: FunctionDeclarationSchemaType.BOOLEAN } as any;
344
- }
345
- }
346
- }
347
303
  }
348
304
 
349
305
  export default LobeGoogleAI;