@lobehub/chat 1.23.1 → 1.24.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.
- package/CHANGELOG.md +25 -0
- package/Dockerfile +2 -0
- package/Dockerfile.database +2 -0
- package/locales/ar/modelProvider.json +16 -0
- package/locales/ar/models.json +27 -0
- package/locales/ar/providers.json +1 -0
- package/locales/bg-BG/modelProvider.json +16 -0
- package/locales/bg-BG/models.json +27 -0
- package/locales/bg-BG/providers.json +1 -0
- package/locales/de-DE/modelProvider.json +16 -0
- package/locales/de-DE/models.json +27 -0
- package/locales/de-DE/providers.json +1 -0
- package/locales/en-US/modelProvider.json +16 -0
- package/locales/en-US/models.json +27 -0
- package/locales/en-US/providers.json +1 -0
- package/locales/es-ES/modelProvider.json +16 -0
- package/locales/es-ES/models.json +27 -0
- package/locales/es-ES/providers.json +1 -0
- package/locales/fr-FR/modelProvider.json +16 -0
- package/locales/fr-FR/models.json +27 -0
- package/locales/fr-FR/providers.json +1 -0
- package/locales/it-IT/modelProvider.json +16 -0
- package/locales/it-IT/models.json +27 -0
- package/locales/it-IT/providers.json +1 -0
- package/locales/ja-JP/modelProvider.json +16 -0
- package/locales/ja-JP/models.json +27 -0
- package/locales/ja-JP/providers.json +1 -0
- package/locales/ko-KR/modelProvider.json +16 -0
- package/locales/ko-KR/models.json +27 -0
- package/locales/ko-KR/providers.json +1 -0
- package/locales/nl-NL/modelProvider.json +16 -0
- package/locales/nl-NL/models.json +27 -0
- package/locales/nl-NL/providers.json +1 -0
- package/locales/pl-PL/modelProvider.json +16 -0
- package/locales/pl-PL/models.json +27 -0
- package/locales/pl-PL/providers.json +1 -0
- package/locales/pt-BR/modelProvider.json +16 -0
- package/locales/pt-BR/models.json +27 -0
- package/locales/pt-BR/providers.json +1 -0
- package/locales/ru-RU/modelProvider.json +16 -0
- package/locales/ru-RU/models.json +27 -0
- package/locales/ru-RU/providers.json +1 -0
- package/locales/tr-TR/modelProvider.json +16 -0
- package/locales/tr-TR/models.json +27 -0
- package/locales/tr-TR/providers.json +1 -0
- package/locales/vi-VN/modelProvider.json +16 -0
- package/locales/vi-VN/models.json +27 -0
- package/locales/vi-VN/providers.json +1 -0
- package/locales/zh-CN/modelProvider.json +16 -0
- package/locales/zh-CN/models.json +27 -0
- package/locales/zh-CN/providers.json +1 -0
- package/locales/zh-TW/modelProvider.json +16 -0
- package/locales/zh-TW/models.json +27 -0
- package/locales/zh-TW/providers.json +1 -0
- package/package.json +3 -3
- package/src/app/(main)/settings/llm/ProviderList/SenseNova/index.tsx +44 -0
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +4 -0
- package/src/config/llm.ts +10 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/config/modelProviders/sensenova.ts +124 -0
- package/src/const/auth.ts +3 -0
- package/src/const/settings/llm.ts +5 -0
- package/src/features/Conversation/Error/APIKeyForm/SenseNova.tsx +49 -0
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +3 -0
- package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
- package/src/libs/agent-runtime/index.ts +1 -0
- package/src/libs/agent-runtime/sensenova/authToken.test.ts +18 -0
- package/src/libs/agent-runtime/sensenova/authToken.ts +27 -0
- package/src/libs/agent-runtime/sensenova/index.test.ts +321 -0
- package/src/libs/agent-runtime/sensenova/index.ts +98 -0
- package/src/libs/agent-runtime/types/type.ts +1 -0
- package/src/locales/default/modelProvider.ts +17 -0
- package/src/server/globalConfig/index.ts +12 -0
- package/src/server/modules/AgentRuntime/index.ts +10 -0
- package/src/services/_auth.ts +14 -0
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +2 -0
- package/src/store/user/slices/modelList/selectors/modelConfig.ts +2 -0
- package/src/types/user/settings/keyVaults.ts +6 -0
@@ -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
@@ -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);
|
@@ -15,6 +15,7 @@ export { LobeOpenAI } from './openai';
|
|
15
15
|
export { LobeOpenRouterAI } from './openrouter';
|
16
16
|
export { LobePerplexityAI } from './perplexity';
|
17
17
|
export { LobeQwenAI } from './qwen';
|
18
|
+
export { LobeSenseNovaAI } from './sensenova';
|
18
19
|
export { LobeTogetherAI } from './togetherai';
|
19
20
|
export * from './types';
|
20
21
|
export { AgentRuntimeError } from './utils/createError';
|
@@ -0,0 +1,18 @@
|
|
1
|
+
// @vitest-environment node
|
2
|
+
import { generateApiToken } from './authToken';
|
3
|
+
|
4
|
+
describe('generateApiToken', () => {
|
5
|
+
it('should throw an error if no apiKey is provided', async () => {
|
6
|
+
await expect(generateApiToken()).rejects.toThrow('Invalid apiKey');
|
7
|
+
});
|
8
|
+
|
9
|
+
it('should throw an error if apiKey is invalid', async () => {
|
10
|
+
await expect(generateApiToken('invalid')).rejects.toThrow('Invalid apiKey');
|
11
|
+
});
|
12
|
+
|
13
|
+
it('should return a token if a valid apiKey is provided', async () => {
|
14
|
+
const apiKey = 'id:secret';
|
15
|
+
const token = await generateApiToken(apiKey);
|
16
|
+
expect(token).toBeDefined();
|
17
|
+
});
|
18
|
+
});
|
@@ -0,0 +1,27 @@
|
|
1
|
+
import { SignJWT } from 'jose';
|
2
|
+
|
3
|
+
// https://console.sensecore.cn/help/docs/model-as-a-service/nova/overview/Authorization
|
4
|
+
export const generateApiToken = async (apiKey?: string): Promise<string> => {
|
5
|
+
if (!apiKey) {
|
6
|
+
throw new Error('Invalid apiKey');
|
7
|
+
}
|
8
|
+
|
9
|
+
const [id, secret] = apiKey.split(':');
|
10
|
+
if (!id || !secret) {
|
11
|
+
throw new Error('Invalid apiKey');
|
12
|
+
}
|
13
|
+
|
14
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
15
|
+
|
16
|
+
const payload = {
|
17
|
+
exp: currentTime + 1800,
|
18
|
+
iss: id,
|
19
|
+
nbf: currentTime - 5,
|
20
|
+
};
|
21
|
+
|
22
|
+
const jwt = await new SignJWT(payload)
|
23
|
+
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
|
24
|
+
.sign(new TextEncoder().encode(secret));
|
25
|
+
|
26
|
+
return jwt;
|
27
|
+
};
|