@lobehub/chat 1.106.8 → 1.107.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.
- package/.env.example +9 -0
- package/CHANGELOG.md +50 -0
- package/Dockerfile +2 -0
- package/Dockerfile.database +2 -0
- package/Dockerfile.pglite +2 -0
- package/changelog/v1.json +18 -0
- package/docs/usage/providers/aihubmix.zh-CN.mdx +101 -0
- package/locales/ar/modelProvider.json +1 -0
- package/locales/ar/models.json +21 -0
- package/locales/ar/providers.json +3 -0
- package/locales/bg-BG/modelProvider.json +1 -0
- package/locales/bg-BG/models.json +21 -0
- package/locales/bg-BG/providers.json +3 -0
- package/locales/de-DE/modelProvider.json +1 -0
- package/locales/de-DE/models.json +21 -0
- package/locales/de-DE/providers.json +3 -0
- package/locales/en-US/modelProvider.json +1 -0
- package/locales/en-US/models.json +21 -0
- package/locales/en-US/providers.json +3 -0
- package/locales/es-ES/modelProvider.json +1 -0
- package/locales/es-ES/models.json +21 -0
- package/locales/es-ES/providers.json +3 -0
- package/locales/fa-IR/modelProvider.json +1 -0
- package/locales/fa-IR/models.json +21 -0
- package/locales/fa-IR/providers.json +3 -0
- package/locales/fr-FR/modelProvider.json +1 -0
- package/locales/fr-FR/models.json +21 -0
- package/locales/fr-FR/providers.json +3 -0
- package/locales/it-IT/modelProvider.json +1 -0
- package/locales/it-IT/models.json +21 -0
- package/locales/it-IT/providers.json +3 -0
- package/locales/ja-JP/modelProvider.json +1 -0
- package/locales/ja-JP/models.json +21 -0
- package/locales/ja-JP/providers.json +3 -0
- package/locales/ko-KR/modelProvider.json +1 -0
- package/locales/ko-KR/models.json +21 -0
- package/locales/ko-KR/providers.json +3 -0
- package/locales/nl-NL/modelProvider.json +1 -0
- package/locales/nl-NL/models.json +21 -0
- package/locales/nl-NL/providers.json +3 -0
- package/locales/pl-PL/modelProvider.json +1 -0
- package/locales/pl-PL/models.json +21 -0
- package/locales/pl-PL/providers.json +3 -0
- package/locales/pt-BR/modelProvider.json +1 -0
- package/locales/pt-BR/models.json +21 -0
- package/locales/pt-BR/providers.json +3 -0
- package/locales/ru-RU/modelProvider.json +1 -0
- package/locales/ru-RU/models.json +21 -0
- package/locales/ru-RU/providers.json +3 -0
- package/locales/tr-TR/modelProvider.json +1 -0
- package/locales/tr-TR/models.json +21 -0
- package/locales/tr-TR/providers.json +3 -0
- package/locales/vi-VN/modelProvider.json +1 -0
- package/locales/vi-VN/models.json +21 -0
- package/locales/vi-VN/providers.json +3 -0
- package/locales/zh-CN/modelProvider.json +1 -0
- package/locales/zh-CN/models.json +21 -0
- package/locales/zh-CN/providers.json +3 -0
- package/locales/zh-TW/modelProvider.json +1 -0
- package/locales/zh-TW/models.json +21 -0
- package/locales/zh-TW/providers.json +3 -0
- package/package.json +2 -3
- package/scripts/cdnWorkflow/utils.ts +1 -1
- package/src/app/(backend)/middleware/auth/index.ts +2 -2
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +12 -12
- package/src/app/(backend)/webapi/chat/[provider]/route.ts +6 -6
- package/src/app/(backend)/webapi/chat/vertexai/route.ts +2 -2
- package/src/app/(backend)/webapi/models/[provider]/pull/route.ts +2 -2
- package/src/app/(backend)/webapi/models/[provider]/route.ts +2 -2
- package/src/app/(backend)/webapi/text-to-image/[provider]/route.ts +2 -2
- package/src/app/[variants]/(main)/settings/provider/(detail)/github/page.tsx +2 -2
- package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/index.tsx +17 -2
- package/src/config/aiModels/aihubmix.ts +164 -0
- package/src/config/aiModels/index.ts +3 -0
- package/src/config/llm.ts +6 -0
- package/src/config/modelProviders/aihubmix.ts +18 -0
- package/src/config/modelProviders/huggingface.ts +1 -0
- package/src/config/modelProviders/index.ts +4 -0
- package/src/libs/model-runtime/ModelRuntime.test.ts +9 -10
- package/src/libs/model-runtime/ModelRuntime.ts +2 -3
- package/src/libs/model-runtime/RouterRuntime/baseRuntimeMap.ts +15 -0
- package/src/libs/model-runtime/RouterRuntime/createRuntime.ts +193 -0
- package/src/libs/model-runtime/RouterRuntime/index.ts +9 -0
- package/src/libs/model-runtime/aihubmix/index.ts +118 -0
- package/src/libs/model-runtime/index.ts +1 -1
- package/src/libs/model-runtime/openrouter/index.ts +2 -2
- package/src/libs/model-runtime/runtimeMap.ts +2 -0
- package/src/libs/model-runtime/types/type.ts +1 -0
- package/src/libs/trpc/client/async.ts +2 -1
- package/src/libs/trpc/client/edge.ts +2 -1
- package/src/libs/trpc/client/lambda.ts +4 -2
- package/src/libs/trpc/client/tools.ts +2 -1
- package/src/locales/default/modelProvider.ts +1 -0
- package/src/server/modules/{AgentRuntime → ModelRuntime}/index.test.ts +64 -67
- package/src/server/modules/{AgentRuntime → ModelRuntime}/index.ts +3 -3
- package/src/server/routers/async/file.ts +2 -2
- package/src/server/routers/async/image.ts +2 -2
- package/src/server/routers/async/ragEval.ts +2 -2
- package/src/server/routers/lambda/chunk.ts +3 -3
- package/src/services/__tests__/chat.test.ts +21 -21
- package/src/services/chat.ts +2 -2
- package/src/types/aiProvider.ts +1 -0
- package/src/types/llm.ts +4 -0
- package/src/types/user/settings/keyVaults.ts +1 -0
- package/src/app/[variants]/(main)/settings/provider/(detail)/huggingface/page.tsx +0 -67
- /package/src/server/modules/{AgentRuntime → ModelRuntime}/apiKeyManager.test.ts +0 -0
- /package/src/server/modules/{AgentRuntime → ModelRuntime}/apiKeyManager.ts +0 -0
- /package/src/server/modules/{AgentRuntime → ModelRuntime}/trace.ts +0 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
import urlJoin from 'url-join';
|
2
|
+
|
3
|
+
import { LOBE_DEFAULT_MODEL_LIST } from '@/config/aiModels';
|
4
|
+
import AiHubMixModels from '@/config/aiModels/aihubmix';
|
5
|
+
import type { ChatModelCard } from '@/types/llm';
|
6
|
+
|
7
|
+
import { createRouterRuntime } from '../RouterRuntime';
|
8
|
+
import { ModelProvider } from '../types';
|
9
|
+
|
10
|
+
export interface AiHubMixModelCard {
|
11
|
+
created: number;
|
12
|
+
id: string;
|
13
|
+
object: string;
|
14
|
+
owned_by: string;
|
15
|
+
}
|
16
|
+
|
17
|
+
const baseURL = 'https://aihubmix.com';
|
18
|
+
|
19
|
+
export const LobeAiHubMixAI = createRouterRuntime({
|
20
|
+
constructorOptions: {
|
21
|
+
defaultHeaders: {
|
22
|
+
'APP-Code': 'LobeHub',
|
23
|
+
},
|
24
|
+
},
|
25
|
+
debug: {
|
26
|
+
chatCompletion: () => process.env.DEBUG_AIHUBMIX_CHAT_COMPLETION === '1',
|
27
|
+
},
|
28
|
+
id: ModelProvider.AiHubMix,
|
29
|
+
models: async ({ client }) => {
|
30
|
+
const functionCallKeywords = [
|
31
|
+
'gpt-4',
|
32
|
+
'gpt-3.5',
|
33
|
+
'claude',
|
34
|
+
'gemini',
|
35
|
+
'qwen',
|
36
|
+
'deepseek',
|
37
|
+
'llama',
|
38
|
+
];
|
39
|
+
|
40
|
+
const visionKeywords = [
|
41
|
+
'gpt-4o',
|
42
|
+
'gpt-4-vision',
|
43
|
+
'claude-3',
|
44
|
+
'claude-4',
|
45
|
+
'gemini-pro-vision',
|
46
|
+
'qwen-vl',
|
47
|
+
'llava',
|
48
|
+
];
|
49
|
+
|
50
|
+
const reasoningKeywords = [
|
51
|
+
'o1',
|
52
|
+
'deepseek-r1',
|
53
|
+
'qwq',
|
54
|
+
'claude-opus-4',
|
55
|
+
'claude-sonnet-4',
|
56
|
+
'claude-3-5-sonnet',
|
57
|
+
'claude-3-5-haiku',
|
58
|
+
];
|
59
|
+
|
60
|
+
try {
|
61
|
+
const modelsPage = (await client.models.list()) as any;
|
62
|
+
const modelList: AiHubMixModelCard[] = modelsPage.data || [];
|
63
|
+
|
64
|
+
return modelList
|
65
|
+
.map((model) => {
|
66
|
+
const knownModel = AiHubMixModels.find(
|
67
|
+
(m) => model.id.toLowerCase() === m.id.toLowerCase(),
|
68
|
+
);
|
69
|
+
|
70
|
+
const modelId = model.id.toLowerCase();
|
71
|
+
|
72
|
+
return {
|
73
|
+
contextWindowTokens: knownModel?.contextWindowTokens ?? undefined,
|
74
|
+
displayName: knownModel?.displayName ?? model.id,
|
75
|
+
enabled: knownModel?.enabled || false,
|
76
|
+
functionCall:
|
77
|
+
functionCallKeywords.some((keyword) => modelId.includes(keyword)) ||
|
78
|
+
knownModel?.abilities?.functionCall ||
|
79
|
+
false,
|
80
|
+
id: model.id,
|
81
|
+
reasoning:
|
82
|
+
reasoningKeywords.some((keyword) => modelId.includes(keyword)) ||
|
83
|
+
knownModel?.abilities?.reasoning ||
|
84
|
+
false,
|
85
|
+
vision:
|
86
|
+
visionKeywords.some((keyword) => modelId.includes(keyword)) ||
|
87
|
+
knownModel?.abilities?.vision ||
|
88
|
+
false,
|
89
|
+
};
|
90
|
+
})
|
91
|
+
.filter(Boolean) as ChatModelCard[];
|
92
|
+
} catch (error) {
|
93
|
+
console.warn(
|
94
|
+
'Failed to fetch AiHubMix models. Please ensure your AiHubMix API key is valid:',
|
95
|
+
error,
|
96
|
+
);
|
97
|
+
return [];
|
98
|
+
}
|
99
|
+
},
|
100
|
+
routers: [
|
101
|
+
{
|
102
|
+
apiType: 'anthropic',
|
103
|
+
models: LOBE_DEFAULT_MODEL_LIST.map((m) => m.id).filter(
|
104
|
+
(id) => id.startsWith('claude') || id.startsWith('kimi-k2'),
|
105
|
+
),
|
106
|
+
options: { baseURL },
|
107
|
+
},
|
108
|
+
{
|
109
|
+
apiType: 'google',
|
110
|
+
models: LOBE_DEFAULT_MODEL_LIST.map((m) => m.id).filter((id) => id.startsWith('gemini')),
|
111
|
+
options: { baseURL: urlJoin(baseURL, '/gemini') },
|
112
|
+
},
|
113
|
+
{
|
114
|
+
apiType: 'openai',
|
115
|
+
options: { baseURL: urlJoin(baseURL, '/v1') },
|
116
|
+
},
|
117
|
+
],
|
118
|
+
});
|
@@ -10,7 +10,7 @@ export { LobeGroq } from './groq';
|
|
10
10
|
export * from './helpers';
|
11
11
|
export { LobeMinimaxAI } from './minimax';
|
12
12
|
export { LobeMistralAI } from './mistral';
|
13
|
-
export {
|
13
|
+
export { ModelRuntime } from './ModelRuntime';
|
14
14
|
export { LobeMoonshotAI } from './moonshot';
|
15
15
|
export { LobeOllamaAI } from './ollama';
|
16
16
|
export { LobeOpenAI } from './openai';
|
@@ -49,8 +49,8 @@ export const LobeOpenRouterAI = createOpenAICompatibleRuntime({
|
|
49
49
|
},
|
50
50
|
constructorOptions: {
|
51
51
|
defaultHeaders: {
|
52
|
-
'HTTP-Referer': 'https://
|
53
|
-
'X-Title': '
|
52
|
+
'HTTP-Referer': 'https://lobehub.com',
|
53
|
+
'X-Title': 'LobeHub',
|
54
54
|
},
|
55
55
|
},
|
56
56
|
debug: {
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { LobeAi21AI } from './ai21';
|
2
2
|
import { LobeAi360AI } from './ai360';
|
3
|
+
import { LobeAiHubMixAI } from './aihubmix';
|
3
4
|
import { LobeAnthropicAI } from './anthropic';
|
4
5
|
import { LobeAzureOpenAI } from './azureOpenai';
|
5
6
|
import { LobeAzureAI } from './azureai';
|
@@ -56,6 +57,7 @@ import { LobeZhipuAI } from './zhipu';
|
|
56
57
|
export const providerRuntimeMap = {
|
57
58
|
ai21: LobeAi21AI,
|
58
59
|
ai360: LobeAi360AI,
|
60
|
+
aihubmix: LobeAiHubMixAI,
|
59
61
|
anthropic: LobeAnthropicAI,
|
60
62
|
azure: LobeAzureOpenAI,
|
61
63
|
azureai: LobeAzureAI,
|
@@ -9,7 +9,8 @@ export const asyncClient = createTRPCClient<AsyncRouter>({
|
|
9
9
|
links: [
|
10
10
|
httpBatchLink({
|
11
11
|
fetch: isDesktop
|
12
|
-
?
|
12
|
+
? // eslint-disable-next-line no-undef
|
13
|
+
(input, init) => fetchWithDesktopRemoteRPC(input as string, init as RequestInit)
|
13
14
|
: undefined,
|
14
15
|
maxURLLength: 2083,
|
15
16
|
transformer: superjson,
|
@@ -10,7 +10,8 @@ export const edgeClient = createTRPCClient<EdgeRouter>({
|
|
10
10
|
links: [
|
11
11
|
httpBatchLink({
|
12
12
|
fetch: isDesktop
|
13
|
-
?
|
13
|
+
? // eslint-disable-next-line no-undef
|
14
|
+
(input, init) => fetchWithDesktopRemoteRPC(input as string, init as RequestInit)
|
14
15
|
: undefined,
|
15
16
|
headers: async () => {
|
16
17
|
// dynamic import to avoid circular dependency
|
@@ -17,12 +17,14 @@ const links = [
|
|
17
17
|
if (isDesktop) {
|
18
18
|
const { desktopRemoteRPCFetch } = await import('@/utils/electron/desktopRemoteRPCFetch');
|
19
19
|
|
20
|
-
|
20
|
+
// eslint-disable-next-line no-undef
|
21
|
+
const res = await desktopRemoteRPCFetch(input as string, init as RequestInit);
|
21
22
|
|
22
23
|
if (res) return res;
|
23
24
|
}
|
24
25
|
|
25
|
-
|
26
|
+
// eslint-disable-next-line no-undef
|
27
|
+
const response = await fetch(input, init as RequestInit);
|
26
28
|
|
27
29
|
if (response.ok) return response;
|
28
30
|
|
@@ -9,7 +9,8 @@ export const toolsClient = createTRPCClient<ToolsRouter>({
|
|
9
9
|
links: [
|
10
10
|
httpBatchLink({
|
11
11
|
fetch: isDesktop
|
12
|
-
?
|
12
|
+
? // eslint-disable-next-line no-undef
|
13
|
+
(input, init) => fetchWithDesktopRemoteRPC(input as string, init as RequestInit)
|
13
14
|
: undefined,
|
14
15
|
headers: async () => {
|
15
16
|
// dynamic import to avoid circular dependency
|
@@ -23,10 +23,10 @@ import {
|
|
23
23
|
LobeZhipuAI,
|
24
24
|
ModelProvider,
|
25
25
|
} from '@/libs/model-runtime';
|
26
|
-
import {
|
26
|
+
import { ModelRuntime } from '@/libs/model-runtime';
|
27
27
|
import { LobeStepfunAI } from '@/libs/model-runtime/stepfun';
|
28
28
|
|
29
|
-
import {
|
29
|
+
import { initModelRuntimeWithUserPayload } from './index';
|
30
30
|
|
31
31
|
// 模拟依赖项
|
32
32
|
vi.mock('@/config/llm', () => ({
|
@@ -59,20 +59,17 @@ vi.mock('@/config/llm', () => ({
|
|
59
59
|
}));
|
60
60
|
|
61
61
|
/**
|
62
|
-
* Test cases for function
|
63
|
-
* this method will use
|
62
|
+
* Test cases for function initModelRuntimeWithUserPayload
|
63
|
+
* this method will use ModelRuntime from `@/libs/model-runtime`
|
64
64
|
* and method `getLlmOptionsFromPayload` to initialize runtime
|
65
65
|
* with user payload. Test case below will test both the methods
|
66
66
|
*/
|
67
|
-
describe('
|
67
|
+
describe('initModelRuntimeWithUserPayload method', () => {
|
68
68
|
describe('should initialize with options correctly', () => {
|
69
69
|
it('OpenAI provider: with apikey and endpoint', async () => {
|
70
|
-
const jwtPayload: ClientSecretPayload = {
|
71
|
-
|
72
|
-
|
73
|
-
};
|
74
|
-
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
75
|
-
expect(runtime).toBeInstanceOf(AgentRuntime);
|
70
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-openai-key', baseURL: 'user-endpoint' };
|
71
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
72
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
76
73
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
77
74
|
expect(runtime['_runtime'].baseURL).toBe(jwtPayload.baseURL);
|
78
75
|
});
|
@@ -83,37 +80,37 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
83
80
|
baseURL: 'user-azure-endpoint',
|
84
81
|
azureApiVersion: '2024-06-01',
|
85
82
|
};
|
86
|
-
const runtime = await
|
87
|
-
expect(runtime).toBeInstanceOf(
|
83
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Azure, jwtPayload);
|
84
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
88
85
|
expect(runtime['_runtime']).toBeInstanceOf(LobeAzureOpenAI);
|
89
86
|
expect(runtime['_runtime'].baseURL).toBe(jwtPayload.baseURL);
|
90
87
|
});
|
91
88
|
|
92
89
|
it('ZhiPu AI provider: with apikey', async () => {
|
93
90
|
const jwtPayload: ClientSecretPayload = { apiKey: 'zhipu.user-key' };
|
94
|
-
const runtime = await
|
95
|
-
expect(runtime).toBeInstanceOf(
|
91
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.ZhiPu, jwtPayload);
|
92
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
96
93
|
expect(runtime['_runtime']).toBeInstanceOf(LobeZhipuAI);
|
97
94
|
});
|
98
95
|
|
99
96
|
it('Google provider: with apikey', async () => {
|
100
97
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-google-key' };
|
101
|
-
const runtime = await
|
102
|
-
expect(runtime).toBeInstanceOf(
|
98
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Google, jwtPayload);
|
99
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
103
100
|
expect(runtime['_runtime']).toBeInstanceOf(LobeGoogleAI);
|
104
101
|
});
|
105
102
|
|
106
103
|
it('Moonshot AI provider: with apikey', async () => {
|
107
104
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-moonshot-key' };
|
108
|
-
const runtime = await
|
109
|
-
expect(runtime).toBeInstanceOf(
|
105
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Moonshot, jwtPayload);
|
106
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
110
107
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI);
|
111
108
|
});
|
112
109
|
|
113
110
|
it('Qwen AI provider: with apikey', async () => {
|
114
111
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-qwen-key' };
|
115
|
-
const runtime = await
|
116
|
-
expect(runtime).toBeInstanceOf(
|
112
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
113
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
117
114
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
118
115
|
});
|
119
116
|
|
@@ -124,86 +121,86 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
124
121
|
awsSecretAccessKey: 'user-aws-secret',
|
125
122
|
awsRegion: 'user-aws-region',
|
126
123
|
};
|
127
|
-
const runtime = await
|
128
|
-
expect(runtime).toBeInstanceOf(
|
124
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Bedrock, jwtPayload);
|
125
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
129
126
|
expect(runtime['_runtime']).toBeInstanceOf(LobeBedrockAI);
|
130
127
|
});
|
131
128
|
|
132
129
|
it('Ollama provider: with endpoint', async () => {
|
133
130
|
const jwtPayload: ClientSecretPayload = { baseURL: 'http://user-ollama-url' };
|
134
|
-
const runtime = await
|
135
|
-
expect(runtime).toBeInstanceOf(
|
131
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Ollama, jwtPayload);
|
132
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
136
133
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOllamaAI);
|
137
134
|
expect(runtime['_runtime']['baseURL']).toEqual(jwtPayload.baseURL);
|
138
135
|
});
|
139
136
|
|
140
137
|
it('Perplexity AI provider: with apikey', async () => {
|
141
138
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-perplexity-key' };
|
142
|
-
const runtime = await
|
143
|
-
expect(runtime).toBeInstanceOf(
|
139
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Perplexity, jwtPayload);
|
140
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
144
141
|
expect(runtime['_runtime']).toBeInstanceOf(LobePerplexityAI);
|
145
142
|
});
|
146
143
|
|
147
144
|
it('Anthropic AI provider: with apikey', async () => {
|
148
145
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-anthropic-key' };
|
149
|
-
const runtime = await
|
150
|
-
expect(runtime).toBeInstanceOf(
|
146
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Anthropic, jwtPayload);
|
147
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
151
148
|
expect(runtime['_runtime']).toBeInstanceOf(LobeAnthropicAI);
|
152
149
|
});
|
153
150
|
|
154
151
|
it('Minimax AI provider: with apikey', async () => {
|
155
152
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-minimax-key' };
|
156
|
-
const runtime = await
|
157
|
-
expect(runtime).toBeInstanceOf(
|
153
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Minimax, jwtPayload);
|
154
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
158
155
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMinimaxAI);
|
159
156
|
});
|
160
157
|
|
161
158
|
it('Mistral AI provider: with apikey', async () => {
|
162
159
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-mistral-key' };
|
163
|
-
const runtime = await
|
164
|
-
expect(runtime).toBeInstanceOf(
|
160
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Mistral, jwtPayload);
|
161
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
165
162
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMistralAI);
|
166
163
|
});
|
167
164
|
|
168
165
|
it('OpenRouter AI provider: with apikey', async () => {
|
169
166
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-openrouter-key' };
|
170
|
-
const runtime = await
|
171
|
-
expect(runtime).toBeInstanceOf(
|
167
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.OpenRouter, jwtPayload);
|
168
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
172
169
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenRouterAI);
|
173
170
|
});
|
174
171
|
|
175
172
|
it('DeepSeek AI provider: with apikey', async () => {
|
176
173
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-deepseek-key' };
|
177
|
-
const runtime = await
|
178
|
-
expect(runtime).toBeInstanceOf(
|
174
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.DeepSeek, jwtPayload);
|
175
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
179
176
|
expect(runtime['_runtime']).toBeInstanceOf(LobeDeepSeekAI);
|
180
177
|
});
|
181
178
|
|
182
179
|
it('Together AI provider: with apikey', async () => {
|
183
180
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-togetherai-key' };
|
184
|
-
const runtime = await
|
185
|
-
expect(runtime).toBeInstanceOf(
|
181
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.TogetherAI, jwtPayload);
|
182
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
186
183
|
expect(runtime['_runtime']).toBeInstanceOf(LobeTogetherAI);
|
187
184
|
});
|
188
185
|
|
189
186
|
it('ZeroOne AI provider: with apikey', async () => {
|
190
187
|
const jwtPayload = { apiKey: 'user-zeroone-key' };
|
191
|
-
const runtime = await
|
192
|
-
expect(runtime).toBeInstanceOf(
|
188
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.ZeroOne, jwtPayload);
|
189
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
193
190
|
expect(runtime['_runtime']).toBeInstanceOf(LobeZeroOneAI);
|
194
191
|
});
|
195
192
|
|
196
193
|
it('Groq AI provider: with apikey', async () => {
|
197
194
|
const jwtPayload = { apiKey: 'user-zeroone-key' };
|
198
|
-
const runtime = await
|
199
|
-
expect(runtime).toBeInstanceOf(
|
195
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Groq, jwtPayload);
|
196
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
200
197
|
expect(runtime['_runtime']).toBeInstanceOf(LobeGroq);
|
201
198
|
});
|
202
199
|
|
203
200
|
it('Stepfun AI provider: with apikey', async () => {
|
204
201
|
const jwtPayload = { apiKey: 'user-stepfun-key' };
|
205
|
-
const runtime = await
|
206
|
-
expect(runtime).toBeInstanceOf(
|
202
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Stepfun, jwtPayload);
|
203
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
207
204
|
expect(runtime['_runtime']).toBeInstanceOf(LobeStepfunAI);
|
208
205
|
});
|
209
206
|
|
@@ -212,8 +209,8 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
212
209
|
apiKey: 'user-unknown-key',
|
213
210
|
baseURL: 'user-unknown-endpoint',
|
214
211
|
};
|
215
|
-
const runtime = await
|
216
|
-
expect(runtime).toBeInstanceOf(
|
212
|
+
const runtime = await initModelRuntimeWithUserPayload('unknown', jwtPayload);
|
213
|
+
expect(runtime).toBeInstanceOf(ModelRuntime);
|
217
214
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
218
215
|
expect(runtime['_runtime'].baseURL).toBe(jwtPayload.baseURL);
|
219
216
|
});
|
@@ -222,7 +219,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
222
219
|
describe('should initialize without some options', () => {
|
223
220
|
it('OpenAI provider: without apikey', async () => {
|
224
221
|
const jwtPayload: ClientSecretPayload = {};
|
225
|
-
const runtime = await
|
222
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
226
223
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
227
224
|
});
|
228
225
|
|
@@ -230,21 +227,21 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
230
227
|
const jwtPayload: ClientSecretPayload = {
|
231
228
|
azureApiVersion: 'test-azure-api-version',
|
232
229
|
};
|
233
|
-
const runtime = await
|
230
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Azure, jwtPayload);
|
234
231
|
|
235
232
|
expect(runtime['_runtime']).toBeInstanceOf(LobeAzureOpenAI);
|
236
233
|
});
|
237
234
|
|
238
235
|
it('ZhiPu AI provider: without apikey', async () => {
|
239
236
|
const jwtPayload: ClientSecretPayload = {};
|
240
|
-
const runtime = await
|
237
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.ZhiPu, jwtPayload);
|
241
238
|
|
242
239
|
// 假设 LobeZhipuAI 是 ZhiPu 提供者的实现类
|
243
240
|
expect(runtime['_runtime']).toBeInstanceOf(LobeZhipuAI);
|
244
241
|
});
|
245
242
|
|
246
243
|
it('Google provider: without apikey', async () => {
|
247
|
-
const runtime = await
|
244
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Google, {});
|
248
245
|
|
249
246
|
// 假设 LobeGoogleAI 是 Google 提供者的实现类
|
250
247
|
expect(runtime['_runtime']).toBeInstanceOf(LobeGoogleAI);
|
@@ -252,7 +249,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
252
249
|
|
253
250
|
it('Moonshot AI provider: without apikey', async () => {
|
254
251
|
const jwtPayload: ClientSecretPayload = {};
|
255
|
-
const runtime = await
|
252
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Moonshot, jwtPayload);
|
256
253
|
|
257
254
|
// 假设 LobeMoonshotAI 是 Moonshot 提供者的实现类
|
258
255
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI);
|
@@ -260,7 +257,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
260
257
|
|
261
258
|
it('Qwen AI provider: without apikey', async () => {
|
262
259
|
const jwtPayload: ClientSecretPayload = {};
|
263
|
-
const runtime = await
|
260
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
264
261
|
|
265
262
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
266
263
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
@@ -268,7 +265,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
268
265
|
|
269
266
|
it('Qwen AI provider: without endpoint', async () => {
|
270
267
|
const jwtPayload: ClientSecretPayload = { apiKey: 'user-qwen-key' };
|
271
|
-
const runtime = await
|
268
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
272
269
|
|
273
270
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
274
271
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
@@ -278,7 +275,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
278
275
|
|
279
276
|
it('Bedrock AI provider: without apikey', async () => {
|
280
277
|
const jwtPayload = {};
|
281
|
-
const runtime = await
|
278
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Bedrock, jwtPayload);
|
282
279
|
|
283
280
|
// 假设 LobeBedrockAI 是 Bedrock 提供者的实现类
|
284
281
|
expect(runtime['_runtime']).toBeInstanceOf(LobeBedrockAI);
|
@@ -286,7 +283,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
286
283
|
|
287
284
|
it('Ollama provider: without endpoint', async () => {
|
288
285
|
const jwtPayload = {};
|
289
|
-
const runtime = await
|
286
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Ollama, jwtPayload);
|
290
287
|
|
291
288
|
// 假设 LobeOllamaAI 是 Ollama 提供者的实现类
|
292
289
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOllamaAI);
|
@@ -294,7 +291,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
294
291
|
|
295
292
|
it('Perplexity AI provider: without apikey', async () => {
|
296
293
|
const jwtPayload = {};
|
297
|
-
const runtime = await
|
294
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Perplexity, jwtPayload);
|
298
295
|
|
299
296
|
// 假设 LobePerplexityAI 是 Perplexity 提供者的实现类
|
300
297
|
expect(runtime['_runtime']).toBeInstanceOf(LobePerplexityAI);
|
@@ -302,7 +299,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
302
299
|
|
303
300
|
it('Anthropic AI provider: without apikey', async () => {
|
304
301
|
const jwtPayload = {};
|
305
|
-
const runtime = await
|
302
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Anthropic, jwtPayload);
|
306
303
|
|
307
304
|
// 假设 LobeAnthropicAI 是 Anthropic 提供者的实现类
|
308
305
|
expect(runtime['_runtime']).toBeInstanceOf(LobeAnthropicAI);
|
@@ -310,7 +307,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
310
307
|
|
311
308
|
it('Minimax AI provider: without apikey', async () => {
|
312
309
|
const jwtPayload = {};
|
313
|
-
const runtime = await
|
310
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Minimax, jwtPayload);
|
314
311
|
|
315
312
|
// 假设 LobeMistralAI 是 Mistral 提供者的实现类
|
316
313
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMinimaxAI);
|
@@ -318,7 +315,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
318
315
|
|
319
316
|
it('Mistral AI provider: without apikey', async () => {
|
320
317
|
const jwtPayload = {};
|
321
|
-
const runtime = await
|
318
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Mistral, jwtPayload);
|
322
319
|
|
323
320
|
// 假设 LobeMistralAI 是 Mistral 提供者的实现类
|
324
321
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMistralAI);
|
@@ -326,7 +323,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
326
323
|
|
327
324
|
it('OpenRouter AI provider: without apikey', async () => {
|
328
325
|
const jwtPayload = {};
|
329
|
-
const runtime = await
|
326
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.OpenRouter, jwtPayload);
|
330
327
|
|
331
328
|
// 假设 LobeOpenRouterAI 是 OpenRouter 提供者的实现类
|
332
329
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenRouterAI);
|
@@ -334,7 +331,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
334
331
|
|
335
332
|
it('DeepSeek AI provider: without apikey', async () => {
|
336
333
|
const jwtPayload = {};
|
337
|
-
const runtime = await
|
334
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.DeepSeek, jwtPayload);
|
338
335
|
|
339
336
|
// 假设 LobeDeepSeekAI 是 DeepSeek 提供者的实现类
|
340
337
|
expect(runtime['_runtime']).toBeInstanceOf(LobeDeepSeekAI);
|
@@ -342,7 +339,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
342
339
|
|
343
340
|
it('Stepfun AI provider: without apikey', async () => {
|
344
341
|
const jwtPayload = {};
|
345
|
-
const runtime = await
|
342
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Stepfun, jwtPayload);
|
346
343
|
|
347
344
|
// 假设 LobeDeepSeekAI 是 DeepSeek 提供者的实现类
|
348
345
|
expect(runtime['_runtime']).toBeInstanceOf(LobeStepfunAI);
|
@@ -350,7 +347,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
350
347
|
|
351
348
|
it('Together AI provider: without apikey', async () => {
|
352
349
|
const jwtPayload = {};
|
353
|
-
const runtime = await
|
350
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.TogetherAI, jwtPayload);
|
354
351
|
|
355
352
|
// 假设 LobeTogetherAI 是 TogetherAI 提供者的实现类
|
356
353
|
expect(runtime['_runtime']).toBeInstanceOf(LobeTogetherAI);
|
@@ -360,7 +357,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
360
357
|
process.env.OPENAI_PROXY_URL = 'https://proxy.example.com/v1';
|
361
358
|
|
362
359
|
const jwtPayload: ClientSecretPayload = {};
|
363
|
-
const runtime = await
|
360
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
364
361
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
365
362
|
// 应返回 OPENAI_PROXY_URL
|
366
363
|
expect(runtime['_runtime'].baseURL).toBe('https://proxy.example.com/v1');
|
@@ -370,7 +367,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
370
367
|
process.env.OPENAI_PROXY_URL = 'https://proxy.example.com/v1';
|
371
368
|
|
372
369
|
const jwtPayload: ClientSecretPayload = {};
|
373
|
-
const runtime = await
|
370
|
+
const runtime = await initModelRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
374
371
|
|
375
372
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
376
373
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
@@ -380,7 +377,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
380
377
|
|
381
378
|
it('Unknown Provider', async () => {
|
382
379
|
const jwtPayload = {};
|
383
|
-
const runtime = await
|
380
|
+
const runtime = await initModelRuntimeWithUserPayload('unknown', jwtPayload);
|
384
381
|
|
385
382
|
// 根据实际实现,你可能需要检查是否返回了默认的 runtime 实例,或者是否抛出了异常
|
386
383
|
// 例如,如果默认使用 OpenAI:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { getLLMConfig } from '@/config/llm';
|
2
2
|
import { ClientSecretPayload } from '@/const/auth';
|
3
|
-
import {
|
3
|
+
import { ModelProvider, ModelRuntime } from '@/libs/model-runtime';
|
4
4
|
|
5
5
|
import apiKeyManager from './apiKeyManager';
|
6
6
|
|
@@ -113,12 +113,12 @@ const getParamsFromPayload = (provider: string, payload: ClientSecretPayload) =>
|
|
113
113
|
* @param params
|
114
114
|
* @returns A promise that resolves when the agent runtime is initialized.
|
115
115
|
*/
|
116
|
-
export const
|
116
|
+
export const initModelRuntimeWithUserPayload = (
|
117
117
|
provider: string,
|
118
118
|
payload: ClientSecretPayload,
|
119
119
|
params: any = {},
|
120
120
|
) => {
|
121
|
-
return
|
121
|
+
return ModelRuntime.initializeWithProvider(provider, {
|
122
122
|
...getParamsFromPayload(provider, payload),
|
123
123
|
...params,
|
124
124
|
});
|
@@ -13,7 +13,7 @@ import { FileModel } from '@/database/models/file';
|
|
13
13
|
import { NewChunkItem, NewEmbeddingsItem } from '@/database/schemas';
|
14
14
|
import { asyncAuthedProcedure, asyncRouter as router } from '@/libs/trpc/async';
|
15
15
|
import { getServerDefaultFilesConfig } from '@/server/globalConfig';
|
16
|
-
import {
|
16
|
+
import { initModelRuntimeWithUserPayload } from '@/server/modules/ModelRuntime';
|
17
17
|
import { ChunkService } from '@/server/services/chunk';
|
18
18
|
import { FileService } from '@/server/services/file';
|
19
19
|
import {
|
@@ -91,7 +91,7 @@ export const fileRouter = router({
|
|
91
91
|
await pMap(
|
92
92
|
requestArray,
|
93
93
|
async (chunks, index) => {
|
94
|
-
const agentRuntime = await
|
94
|
+
const agentRuntime = await initModelRuntimeWithUserPayload(
|
95
95
|
provider,
|
96
96
|
ctx.jwtPayload,
|
97
97
|
);
|