@lobehub/chat 1.105.1 → 1.105.2
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 +35 -0
- package/changelog/v1.json +9 -0
- package/locales/ar/auth.json +54 -0
- package/locales/bg-BG/auth.json +54 -0
- package/locales/de-DE/auth.json +54 -0
- package/locales/en-US/auth.json +54 -0
- package/locales/es-ES/auth.json +54 -0
- package/locales/fa-IR/auth.json +54 -0
- package/locales/fr-FR/auth.json +54 -0
- package/locales/it-IT/auth.json +54 -0
- package/locales/ja-JP/auth.json +54 -0
- package/locales/ko-KR/auth.json +54 -0
- package/locales/nl-NL/auth.json +54 -0
- package/locales/pl-PL/auth.json +54 -0
- package/locales/pt-BR/auth.json +54 -0
- package/locales/ru-RU/auth.json +54 -0
- package/locales/tr-TR/auth.json +54 -0
- package/locales/vi-VN/auth.json +54 -0
- package/locales/zh-CN/auth.json +54 -0
- package/locales/zh-TW/auth.json +54 -0
- package/package.json +2 -2
- package/src/app/(backend)/middleware/auth/index.test.ts +5 -5
- package/src/app/(backend)/middleware/auth/index.ts +6 -6
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +11 -9
- package/src/app/(backend)/webapi/plugin/gateway/route.ts +2 -2
- package/src/app/sitemap.tsx +1 -10
- package/src/config/aiModels/giteeai.ts +269 -2
- package/src/config/aiModels/siliconcloud.ts +24 -2
- package/src/config/aiModels/stepfun.ts +67 -2
- package/src/config/aiModels/volcengine.ts +56 -2
- package/src/config/aiModels/wenxin.ts +62 -2
- package/src/config/aiModels/xai.ts +19 -2
- package/src/const/auth.ts +2 -3
- package/src/libs/model-runtime/ModelRuntime.test.ts +3 -3
- package/src/libs/trpc/async/context.ts +3 -3
- package/src/libs/trpc/edge/context.ts +7 -2
- package/src/libs/trpc/edge/middleware/jwtPayload.test.ts +4 -4
- package/src/libs/trpc/edge/middleware/jwtPayload.ts +2 -2
- package/src/libs/trpc/lambda/context.ts +2 -2
- package/src/libs/trpc/lambda/middleware/keyVaults.ts +2 -2
- package/src/server/modules/AgentRuntime/index.test.ts +28 -25
- package/src/server/modules/AgentRuntime/index.ts +3 -3
- package/src/server/routers/async/caller.ts +2 -2
- package/src/server/routers/lambda/market/index.ts +0 -14
- package/src/server/routers/tools/search.test.ts +2 -2
- package/src/server/services/chunk/index.ts +3 -3
- package/src/server/services/discover/index.ts +0 -13
- package/src/server/sitemap.test.ts +0 -52
- package/src/server/sitemap.ts +1 -38
- package/src/services/_auth.ts +3 -3
- package/src/services/discover.ts +0 -4
- package/src/store/discover/slices/mcp/action.ts +0 -8
- package/src/utils/client/xor-obfuscation.test.ts +370 -0
- package/src/utils/client/xor-obfuscation.ts +39 -0
- package/src/utils/server/xor.test.ts +123 -0
- package/src/utils/server/xor.ts +42 -0
- package/src/utils/jwt.test.ts +0 -27
- package/src/utils/jwt.ts +0 -37
- package/src/utils/server/jwt.test.ts +0 -62
- package/src/utils/server/jwt.ts +0 -28
@@ -1,4 +1,4 @@
|
|
1
|
-
import { AIChatModelCard } from '@/types/aiModel';
|
1
|
+
import { AIChatModelCard, AIImageModelCard } from '@/types/aiModel';
|
2
2
|
|
3
3
|
const wenxinChatModels: AIChatModelCard[] = [
|
4
4
|
{
|
@@ -564,6 +564,66 @@ const wenxinChatModels: AIChatModelCard[] = [
|
|
564
564
|
},
|
565
565
|
];
|
566
566
|
|
567
|
-
|
567
|
+
const wenxinImageModels: AIImageModelCard[] = [
|
568
|
+
{
|
569
|
+
description:
|
570
|
+
'百度自研的iRAG(image based RAG),检索增强的文生图技术,将百度搜索的亿级图片资源跟强大的基础模型能力相结合,就可以生成各种超真实的图片,整体效果远远超过文生图原生系统,去掉了AI味儿,而且成本很低。iRAG具备无幻觉、超真实、立等可取等特点。',
|
571
|
+
displayName: 'ERNIE iRAG',
|
572
|
+
enabled: true,
|
573
|
+
id: 'irag-1.0',
|
574
|
+
parameters: {
|
575
|
+
prompt: {
|
576
|
+
default: '',
|
577
|
+
},
|
578
|
+
size: {
|
579
|
+
default: '1024x1024',
|
580
|
+
enum: ['768x768', '1024x1024', '1536x1536', '2048x2048', '1024x768', '2048x1536', '768x1024', '1536x2048', '1024x576', '2048x1152', '576x1024', '1152x2048'],
|
581
|
+
},
|
582
|
+
},
|
583
|
+
releasedAt: '2025-02-05',
|
584
|
+
type: 'image',
|
585
|
+
},
|
586
|
+
{
|
587
|
+
description:
|
588
|
+
'百度自研的ERNIE iRAG Edit图像编辑模型支持基于图片进行erase(消除对象)、repaint(重绘对象)、variation(生成变体)等操作。',
|
589
|
+
displayName: 'ERNIE iRAG Edit',
|
590
|
+
enabled: true,
|
591
|
+
id: 'ernie-irag-edit',
|
592
|
+
parameters: {
|
593
|
+
imageUrl: { default: null },
|
594
|
+
prompt: {
|
595
|
+
default: '',
|
596
|
+
},
|
597
|
+
size: {
|
598
|
+
default: '1024x1024',
|
599
|
+
enum: ['768x768', '1024x1024', '1536x1536', '2048x2048', '1024x768', '2048x1536', '768x1024', '1536x2048', '1024x576', '2048x1152', '576x1024', '1152x2048'],
|
600
|
+
},
|
601
|
+
},
|
602
|
+
releasedAt: '2025-04-17',
|
603
|
+
type: 'image',
|
604
|
+
},
|
605
|
+
{
|
606
|
+
description:
|
607
|
+
'具有120亿参数的修正流变换器,能够根据文本描述生成图像。',
|
608
|
+
displayName: 'FLUX.1-schnell',
|
609
|
+
enabled: true,
|
610
|
+
id: 'flux.1-schnell',
|
611
|
+
parameters: {
|
612
|
+
prompt: {
|
613
|
+
default: '',
|
614
|
+
},
|
615
|
+
seed: { default: null },
|
616
|
+
size: {
|
617
|
+
default: '1024x1024',
|
618
|
+
enum: ['768x768', '1024x1024', '1536x1536', '2048x2048', '1024x768', '2048x1536', '768x1024', '1536x2048', '1024x576', '2048x1152', '576x1024', '1152x2048'],
|
619
|
+
},
|
620
|
+
steps: { default: 25, max: 50, min: 1 },
|
621
|
+
},
|
622
|
+
releasedAt: '2025-03-27',
|
623
|
+
type: 'image',
|
624
|
+
},
|
625
|
+
];
|
626
|
+
|
627
|
+
export const allModels = [...wenxinChatModels, ...wenxinImageModels];
|
568
628
|
|
569
629
|
export default allModels;
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { AIChatModelCard } from '@/types/aiModel';
|
1
|
+
import { AIChatModelCard, AIImageModelCard } from '@/types/aiModel';
|
2
2
|
|
3
3
|
// https://docs.x.ai/docs/models
|
4
4
|
const xaiChatModels: AIChatModelCard[] = [
|
@@ -158,6 +158,23 @@ const xaiChatModels: AIChatModelCard[] = [
|
|
158
158
|
},
|
159
159
|
];
|
160
160
|
|
161
|
-
|
161
|
+
const xaiImageModels: AIImageModelCard[] = [
|
162
|
+
{
|
163
|
+
description:
|
164
|
+
'我们最新的图像生成模型可以根据文本提示生成生动逼真的图像。它在营销、社交媒体和娱乐等领域的图像生成方面表现出色。',
|
165
|
+
displayName: 'Grok 2 Image 1212',
|
166
|
+
enabled: true,
|
167
|
+
id: 'grok-2-image-1212',
|
168
|
+
parameters: {
|
169
|
+
prompt: {
|
170
|
+
default: '',
|
171
|
+
},
|
172
|
+
},
|
173
|
+
releasedAt: '2024-12-12',
|
174
|
+
type: 'image',
|
175
|
+
},
|
176
|
+
];
|
177
|
+
|
178
|
+
export const allModels = [...xaiChatModels, ...xaiImageModels];
|
162
179
|
|
163
180
|
export default allModels;
|
package/src/const/auth.ts
CHANGED
@@ -9,11 +9,10 @@ export const LOBE_CHAT_OIDC_AUTH_HEADER = 'Oidc-Auth';
|
|
9
9
|
|
10
10
|
export const OAUTH_AUTHORIZED = 'X-oauth-authorized';
|
11
11
|
|
12
|
-
export const
|
13
|
-
export const NON_HTTP_PREFIX = 'http_nosafe';
|
12
|
+
export const SECRET_XOR_KEY = 'LobeHub · LobeHub';
|
14
13
|
|
15
14
|
/* eslint-disable typescript-sort-keys/interface */
|
16
|
-
export interface
|
15
|
+
export interface ClientSecretPayload {
|
17
16
|
/**
|
18
17
|
* password
|
19
18
|
*/
|
@@ -4,7 +4,7 @@ import { LangfuseGenerationClient, LangfuseTraceClient } from 'langfuse-core';
|
|
4
4
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
5
5
|
|
6
6
|
import * as langfuseCfg from '@/config/langfuse';
|
7
|
-
import {
|
7
|
+
import { ClientSecretPayload } from '@/const/auth';
|
8
8
|
import { TraceNameMap } from '@/const/trace';
|
9
9
|
import { AgentRuntime, ChatStreamPayload, LobeOpenAI, ModelProvider } from '@/libs/model-runtime';
|
10
10
|
import { providerRuntimeMap } from '@/libs/model-runtime/runtimeMap';
|
@@ -51,7 +51,7 @@ const specialProviders = [
|
|
51
51
|
const testRuntime = (providerId: string, payload?: any) => {
|
52
52
|
describe(`${providerId} provider runtime`, () => {
|
53
53
|
it('should initialize correctly', async () => {
|
54
|
-
const jwtPayload:
|
54
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-key', ...payload };
|
55
55
|
const runtime = await AgentRuntime.initializeWithProvider(providerId, jwtPayload);
|
56
56
|
|
57
57
|
// @ts-ignore
|
@@ -66,7 +66,7 @@ const testRuntime = (providerId: string, payload?: any) => {
|
|
66
66
|
|
67
67
|
let mockModelRuntime: AgentRuntime;
|
68
68
|
beforeEach(async () => {
|
69
|
-
const jwtPayload:
|
69
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-openai-key', baseURL: 'user-endpoint' };
|
70
70
|
mockModelRuntime = await AgentRuntime.initializeWithProvider(ModelProvider.OpenAI, jwtPayload);
|
71
71
|
});
|
72
72
|
|
@@ -1,14 +1,14 @@
|
|
1
1
|
import debug from 'debug';
|
2
2
|
import { NextRequest } from 'next/server';
|
3
3
|
|
4
|
-
import {
|
4
|
+
import { ClientSecretPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
5
5
|
import { LobeChatDatabase } from '@/database/type';
|
6
6
|
import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
7
7
|
|
8
8
|
const log = debug('lobe-async:context');
|
9
9
|
|
10
10
|
export interface AsyncAuthContext {
|
11
|
-
jwtPayload:
|
11
|
+
jwtPayload: ClientSecretPayload;
|
12
12
|
secret: string;
|
13
13
|
serverDB?: LobeChatDatabase;
|
14
14
|
userId?: string | null;
|
@@ -19,7 +19,7 @@ export interface AsyncAuthContext {
|
|
19
19
|
* This is useful for testing when we don't want to mock Next.js' request/response
|
20
20
|
*/
|
21
21
|
export const createAsyncContextInner = async (params?: {
|
22
|
-
jwtPayload?:
|
22
|
+
jwtPayload?: ClientSecretPayload;
|
23
23
|
secret?: string;
|
24
24
|
userId?: string | null;
|
25
25
|
}): Promise<AsyncAuthContext> => ({
|
@@ -1,13 +1,18 @@
|
|
1
1
|
import { User } from 'next-auth';
|
2
2
|
import { NextRequest } from 'next/server';
|
3
3
|
|
4
|
-
import {
|
4
|
+
import {
|
5
|
+
ClientSecretPayload,
|
6
|
+
LOBE_CHAT_AUTH_HEADER,
|
7
|
+
enableClerk,
|
8
|
+
enableNextAuth,
|
9
|
+
} from '@/const/auth';
|
5
10
|
import { ClerkAuth, IClerkAuth } from '@/libs/clerk-auth';
|
6
11
|
|
7
12
|
export interface AuthContext {
|
8
13
|
authorizationHeader?: string | null;
|
9
14
|
clerkAuth?: IClerkAuth;
|
10
|
-
jwtPayload?:
|
15
|
+
jwtPayload?: ClientSecretPayload | null;
|
11
16
|
nextAuth?: User;
|
12
17
|
userId?: string | null;
|
13
18
|
}
|
@@ -5,7 +5,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
5
5
|
import { createCallerFactory } from '@/libs/trpc/edge';
|
6
6
|
import { AuthContext, createContextInner } from '@/libs/trpc/edge/context';
|
7
7
|
import { edgeTrpc as trpc } from '@/libs/trpc/edge/init';
|
8
|
-
import * as utils from '@/utils/server/
|
8
|
+
import * as utils from '@/utils/server/xor';
|
9
9
|
|
10
10
|
import { jwtPayloadChecker } from './jwtPayload';
|
11
11
|
|
@@ -40,7 +40,7 @@ describe('passwordChecker middleware', () => {
|
|
40
40
|
it('should call next with jwtPayload in context if access code is correct', async () => {
|
41
41
|
const jwtPayload = { accessCode: '123' };
|
42
42
|
|
43
|
-
vi.spyOn(utils, '
|
43
|
+
vi.spyOn(utils, 'getXorPayload').mockResolvedValue(jwtPayload);
|
44
44
|
|
45
45
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
46
46
|
router = createCaller(ctx);
|
@@ -52,7 +52,7 @@ describe('passwordChecker middleware', () => {
|
|
52
52
|
|
53
53
|
it('should call next with jwtPayload in context if no access codes are set', async () => {
|
54
54
|
const jwtPayload = {};
|
55
|
-
vi.spyOn(utils, '
|
55
|
+
vi.spyOn(utils, 'getXorPayload').mockResolvedValue(jwtPayload);
|
56
56
|
|
57
57
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
58
58
|
router = createCaller(ctx);
|
@@ -63,7 +63,7 @@ describe('passwordChecker middleware', () => {
|
|
63
63
|
});
|
64
64
|
it('should call next with jwtPayload in context if access codes is undefined', async () => {
|
65
65
|
const jwtPayload = {};
|
66
|
-
vi.spyOn(utils, '
|
66
|
+
vi.spyOn(utils, 'getXorPayload').mockResolvedValue(jwtPayload);
|
67
67
|
|
68
68
|
ctx = await createContextInner({ authorizationHeader: 'Bearer token' });
|
69
69
|
router = createCaller(ctx);
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
|
3
|
-
import {
|
3
|
+
import { getXorPayload } from '@/utils/server/xor';
|
4
4
|
|
5
5
|
import { edgeTrpc } from '../init';
|
6
6
|
|
@@ -9,7 +9,7 @@ export const jwtPayloadChecker = edgeTrpc.middleware(async (opts) => {
|
|
9
9
|
|
10
10
|
if (!ctx.authorizationHeader) throw new TRPCError({ code: 'UNAUTHORIZED' });
|
11
11
|
|
12
|
-
const jwtPayload =
|
12
|
+
const jwtPayload = getXorPayload(ctx.authorizationHeader);
|
13
13
|
|
14
14
|
return opts.next({ ctx: { jwtPayload } });
|
15
15
|
});
|
@@ -4,7 +4,7 @@ import { User } from 'next-auth';
|
|
4
4
|
import { NextRequest } from 'next/server';
|
5
5
|
|
6
6
|
import {
|
7
|
-
|
7
|
+
ClientSecretPayload,
|
8
8
|
LOBE_CHAT_AUTH_HEADER,
|
9
9
|
LOBE_CHAT_OIDC_AUTH_HEADER,
|
10
10
|
enableClerk,
|
@@ -29,7 +29,7 @@ export interface OIDCAuth {
|
|
29
29
|
export interface AuthContext {
|
30
30
|
authorizationHeader?: string | null;
|
31
31
|
clerkAuth?: IClerkAuth;
|
32
|
-
jwtPayload?:
|
32
|
+
jwtPayload?: ClientSecretPayload | null;
|
33
33
|
marketAccessToken?: string;
|
34
34
|
nextAuth?: User;
|
35
35
|
// Add OIDC authentication information
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { TRPCError } from '@trpc/server';
|
2
2
|
|
3
|
-
import {
|
3
|
+
import { getXorPayload } from '@/utils/server/xor';
|
4
4
|
|
5
5
|
import { trpc } from '../init';
|
6
6
|
|
@@ -10,7 +10,7 @@ export const keyVaults = trpc.middleware(async (opts) => {
|
|
10
10
|
if (!ctx.authorizationHeader) throw new TRPCError({ code: 'UNAUTHORIZED' });
|
11
11
|
|
12
12
|
try {
|
13
|
-
const jwtPayload =
|
13
|
+
const jwtPayload = getXorPayload(ctx.authorizationHeader);
|
14
14
|
|
15
15
|
return opts.next({ ctx: { jwtPayload } });
|
16
16
|
} catch (e) {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
// @vitest-environment node
|
2
2
|
import { describe, expect, it, vi } from 'vitest';
|
3
3
|
|
4
|
-
import {
|
4
|
+
import { ClientSecretPayload } from '@/const/auth';
|
5
5
|
import {
|
6
6
|
LobeAnthropicAI,
|
7
7
|
LobeAzureOpenAI,
|
@@ -67,7 +67,10 @@ vi.mock('@/config/llm', () => ({
|
|
67
67
|
describe('initAgentRuntimeWithUserPayload method', () => {
|
68
68
|
describe('should initialize with options correctly', () => {
|
69
69
|
it('OpenAI provider: with apikey and endpoint', async () => {
|
70
|
-
const jwtPayload:
|
70
|
+
const jwtPayload: ClientSecretPayload = {
|
71
|
+
apiKey: 'user-openai-key',
|
72
|
+
baseURL: 'user-endpoint',
|
73
|
+
};
|
71
74
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
72
75
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
73
76
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
@@ -75,7 +78,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
75
78
|
});
|
76
79
|
|
77
80
|
it('Azure AI provider: with apikey, endpoint and apiversion', async () => {
|
78
|
-
const jwtPayload:
|
81
|
+
const jwtPayload: ClientSecretPayload = {
|
79
82
|
apiKey: 'user-azure-key',
|
80
83
|
baseURL: 'user-azure-endpoint',
|
81
84
|
azureApiVersion: '2024-06-01',
|
@@ -87,35 +90,35 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
87
90
|
});
|
88
91
|
|
89
92
|
it('ZhiPu AI provider: with apikey', async () => {
|
90
|
-
const jwtPayload:
|
93
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'zhipu.user-key' };
|
91
94
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.ZhiPu, jwtPayload);
|
92
95
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
93
96
|
expect(runtime['_runtime']).toBeInstanceOf(LobeZhipuAI);
|
94
97
|
});
|
95
98
|
|
96
99
|
it('Google provider: with apikey', async () => {
|
97
|
-
const jwtPayload:
|
100
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-google-key' };
|
98
101
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Google, jwtPayload);
|
99
102
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
100
103
|
expect(runtime['_runtime']).toBeInstanceOf(LobeGoogleAI);
|
101
104
|
});
|
102
105
|
|
103
106
|
it('Moonshot AI provider: with apikey', async () => {
|
104
|
-
const jwtPayload:
|
107
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-moonshot-key' };
|
105
108
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Moonshot, jwtPayload);
|
106
109
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
107
110
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMoonshotAI);
|
108
111
|
});
|
109
112
|
|
110
113
|
it('Qwen AI provider: with apikey', async () => {
|
111
|
-
const jwtPayload:
|
114
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-qwen-key' };
|
112
115
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
113
116
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
114
117
|
expect(runtime['_runtime']).toBeInstanceOf(LobeQwenAI);
|
115
118
|
});
|
116
119
|
|
117
120
|
it('Bedrock AI provider: with apikey, awsAccessKeyId, awsSecretAccessKey, awsRegion', async () => {
|
118
|
-
const jwtPayload:
|
121
|
+
const jwtPayload: ClientSecretPayload = {
|
119
122
|
apiKey: 'user-bedrock-key',
|
120
123
|
awsAccessKeyId: 'user-aws-id',
|
121
124
|
awsSecretAccessKey: 'user-aws-secret',
|
@@ -127,7 +130,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
127
130
|
});
|
128
131
|
|
129
132
|
it('Ollama provider: with endpoint', async () => {
|
130
|
-
const jwtPayload:
|
133
|
+
const jwtPayload: ClientSecretPayload = { baseURL: 'http://user-ollama-url' };
|
131
134
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Ollama, jwtPayload);
|
132
135
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
133
136
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOllamaAI);
|
@@ -135,49 +138,49 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
135
138
|
});
|
136
139
|
|
137
140
|
it('Perplexity AI provider: with apikey', async () => {
|
138
|
-
const jwtPayload:
|
141
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-perplexity-key' };
|
139
142
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Perplexity, jwtPayload);
|
140
143
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
141
144
|
expect(runtime['_runtime']).toBeInstanceOf(LobePerplexityAI);
|
142
145
|
});
|
143
146
|
|
144
147
|
it('Anthropic AI provider: with apikey', async () => {
|
145
|
-
const jwtPayload:
|
148
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-anthropic-key' };
|
146
149
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Anthropic, jwtPayload);
|
147
150
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
148
151
|
expect(runtime['_runtime']).toBeInstanceOf(LobeAnthropicAI);
|
149
152
|
});
|
150
153
|
|
151
154
|
it('Minimax AI provider: with apikey', async () => {
|
152
|
-
const jwtPayload:
|
155
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-minimax-key' };
|
153
156
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Minimax, jwtPayload);
|
154
157
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
155
158
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMinimaxAI);
|
156
159
|
});
|
157
160
|
|
158
161
|
it('Mistral AI provider: with apikey', async () => {
|
159
|
-
const jwtPayload:
|
162
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-mistral-key' };
|
160
163
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Mistral, jwtPayload);
|
161
164
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
162
165
|
expect(runtime['_runtime']).toBeInstanceOf(LobeMistralAI);
|
163
166
|
});
|
164
167
|
|
165
168
|
it('OpenRouter AI provider: with apikey', async () => {
|
166
|
-
const jwtPayload:
|
169
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-openrouter-key' };
|
167
170
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.OpenRouter, jwtPayload);
|
168
171
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
169
172
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenRouterAI);
|
170
173
|
});
|
171
174
|
|
172
175
|
it('DeepSeek AI provider: with apikey', async () => {
|
173
|
-
const jwtPayload:
|
176
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-deepseek-key' };
|
174
177
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.DeepSeek, jwtPayload);
|
175
178
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
176
179
|
expect(runtime['_runtime']).toBeInstanceOf(LobeDeepSeekAI);
|
177
180
|
});
|
178
181
|
|
179
182
|
it('Together AI provider: with apikey', async () => {
|
180
|
-
const jwtPayload:
|
183
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-togetherai-key' };
|
181
184
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.TogetherAI, jwtPayload);
|
182
185
|
expect(runtime).toBeInstanceOf(AgentRuntime);
|
183
186
|
expect(runtime['_runtime']).toBeInstanceOf(LobeTogetherAI);
|
@@ -205,7 +208,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
205
208
|
});
|
206
209
|
|
207
210
|
it('Unknown Provider: with apikey and endpoint, should initialize to OpenAi', async () => {
|
208
|
-
const jwtPayload:
|
211
|
+
const jwtPayload: ClientSecretPayload = {
|
209
212
|
apiKey: 'user-unknown-key',
|
210
213
|
baseURL: 'user-unknown-endpoint',
|
211
214
|
};
|
@@ -218,13 +221,13 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
218
221
|
|
219
222
|
describe('should initialize without some options', () => {
|
220
223
|
it('OpenAI provider: without apikey', async () => {
|
221
|
-
const jwtPayload:
|
224
|
+
const jwtPayload: ClientSecretPayload = {};
|
222
225
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
223
226
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
224
227
|
});
|
225
228
|
|
226
229
|
it('Azure AI Provider: without apikey', async () => {
|
227
|
-
const jwtPayload:
|
230
|
+
const jwtPayload: ClientSecretPayload = {
|
228
231
|
azureApiVersion: 'test-azure-api-version',
|
229
232
|
};
|
230
233
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Azure, jwtPayload);
|
@@ -233,7 +236,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
233
236
|
});
|
234
237
|
|
235
238
|
it('ZhiPu AI provider: without apikey', async () => {
|
236
|
-
const jwtPayload:
|
239
|
+
const jwtPayload: ClientSecretPayload = {};
|
237
240
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.ZhiPu, jwtPayload);
|
238
241
|
|
239
242
|
// 假设 LobeZhipuAI 是 ZhiPu 提供者的实现类
|
@@ -248,7 +251,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
248
251
|
});
|
249
252
|
|
250
253
|
it('Moonshot AI provider: without apikey', async () => {
|
251
|
-
const jwtPayload:
|
254
|
+
const jwtPayload: ClientSecretPayload = {};
|
252
255
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Moonshot, jwtPayload);
|
253
256
|
|
254
257
|
// 假设 LobeMoonshotAI 是 Moonshot 提供者的实现类
|
@@ -256,7 +259,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
256
259
|
});
|
257
260
|
|
258
261
|
it('Qwen AI provider: without apikey', async () => {
|
259
|
-
const jwtPayload:
|
262
|
+
const jwtPayload: ClientSecretPayload = {};
|
260
263
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
261
264
|
|
262
265
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
@@ -264,7 +267,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
264
267
|
});
|
265
268
|
|
266
269
|
it('Qwen AI provider: without endpoint', async () => {
|
267
|
-
const jwtPayload:
|
270
|
+
const jwtPayload: ClientSecretPayload = { apiKey: 'user-qwen-key' };
|
268
271
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
269
272
|
|
270
273
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
@@ -356,7 +359,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
356
359
|
it('OpenAI provider: without apikey with OPENAI_PROXY_URL', async () => {
|
357
360
|
process.env.OPENAI_PROXY_URL = 'https://proxy.example.com/v1';
|
358
361
|
|
359
|
-
const jwtPayload:
|
362
|
+
const jwtPayload: ClientSecretPayload = {};
|
360
363
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.OpenAI, jwtPayload);
|
361
364
|
expect(runtime['_runtime']).toBeInstanceOf(LobeOpenAI);
|
362
365
|
// 应返回 OPENAI_PROXY_URL
|
@@ -366,7 +369,7 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
366
369
|
it('Qwen AI provider: without apiKey and endpoint with OPENAI_PROXY_URL', async () => {
|
367
370
|
process.env.OPENAI_PROXY_URL = 'https://proxy.example.com/v1';
|
368
371
|
|
369
|
-
const jwtPayload:
|
372
|
+
const jwtPayload: ClientSecretPayload = {};
|
370
373
|
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Qwen, jwtPayload);
|
371
374
|
|
372
375
|
// 假设 LobeQwenAI 是 Qwen 提供者的实现类
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { getLLMConfig } from '@/config/llm';
|
2
|
-
import {
|
2
|
+
import { ClientSecretPayload } from '@/const/auth';
|
3
3
|
import { AgentRuntime, ModelProvider } from '@/libs/model-runtime';
|
4
4
|
|
5
5
|
import apiKeyManager from './apiKeyManager';
|
@@ -14,7 +14,7 @@ export * from './trace';
|
|
14
14
|
* @param payload - The JWT payload.
|
15
15
|
* @returns The options object.
|
16
16
|
*/
|
17
|
-
const getParamsFromPayload = (provider: string, payload:
|
17
|
+
const getParamsFromPayload = (provider: string, payload: ClientSecretPayload) => {
|
18
18
|
const llmConfig = getLLMConfig() as Record<string, any>;
|
19
19
|
|
20
20
|
switch (provider) {
|
@@ -115,7 +115,7 @@ const getParamsFromPayload = (provider: string, payload: JWTPayload) => {
|
|
115
115
|
*/
|
116
116
|
export const initAgentRuntimeWithUserPayload = (
|
117
117
|
provider: string,
|
118
|
-
payload:
|
118
|
+
payload: ClientSecretPayload,
|
119
119
|
params: any = {},
|
120
120
|
) => {
|
121
121
|
return AgentRuntime.initializeWithProvider(provider, {
|
@@ -3,7 +3,7 @@ import superjson from 'superjson';
|
|
3
3
|
import urlJoin from 'url-join';
|
4
4
|
|
5
5
|
import { serverDBEnv } from '@/config/db';
|
6
|
-
import {
|
6
|
+
import { ClientSecretPayload, LOBE_CHAT_AUTH_HEADER } from '@/const/auth';
|
7
7
|
import { isDesktop } from '@/const/version';
|
8
8
|
import { appEnv } from '@/envs/app';
|
9
9
|
import { createAsyncCallerFactory } from '@/libs/trpc/async';
|
@@ -13,7 +13,7 @@ import { KeyVaultsGateKeeper } from '@/server/modules/KeyVaultsEncrypt';
|
|
13
13
|
import { asyncRouter } from './index';
|
14
14
|
import type { AsyncRouter } from './index';
|
15
15
|
|
16
|
-
export const createAsyncServerClient = async (userId: string, payload:
|
16
|
+
export const createAsyncServerClient = async (userId: string, payload: ClientSecretPayload) => {
|
17
17
|
const gateKeeper = await KeyVaultsGateKeeper.initWithEnvKey();
|
18
18
|
const headers: Record<string, string> = {
|
19
19
|
Authorization: `Bearer ${serverDBEnv.KEY_VAULTS_SECRET}`,
|
@@ -173,20 +173,6 @@ export const marketRouter = router({
|
|
173
173
|
}
|
174
174
|
}),
|
175
175
|
|
176
|
-
getMcpIdentifiers: marketProcedure.query(async ({ ctx }) => {
|
177
|
-
log('getMcpIdentifiers called');
|
178
|
-
|
179
|
-
try {
|
180
|
-
return await ctx.discoverService.getMcpIdentifiers();
|
181
|
-
} catch (error) {
|
182
|
-
log('Error fetching mcp identifiers: %O', error);
|
183
|
-
throw new TRPCError({
|
184
|
-
code: 'INTERNAL_SERVER_ERROR',
|
185
|
-
message: 'Failed to fetch mcp identifiers',
|
186
|
-
});
|
187
|
-
}
|
188
|
-
}),
|
189
|
-
|
190
176
|
getMcpList: marketProcedure
|
191
177
|
.input(
|
192
178
|
z
|
@@ -9,8 +9,8 @@ import { SEARCH_SEARXNG_NOT_CONFIG } from '@/types/tool/search';
|
|
9
9
|
import { searchRouter } from './search';
|
10
10
|
|
11
11
|
// Mock JWT verification
|
12
|
-
vi.mock('@/utils/server/
|
13
|
-
|
12
|
+
vi.mock('@/utils/server/xor', () => ({
|
13
|
+
getXorPayload: vi.fn().mockReturnValue({ userId: '1' }),
|
14
14
|
}));
|
15
15
|
|
16
16
|
vi.mock('@lobechat/web-crawler', () => ({
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {
|
1
|
+
import { ClientSecretPayload } from '@/const/auth';
|
2
2
|
import { AsyncTaskModel } from '@/database/models/asyncTask';
|
3
3
|
import { FileModel } from '@/database/models/file';
|
4
4
|
import { serverDB } from '@/database/server';
|
@@ -30,7 +30,7 @@ export class ChunkService {
|
|
30
30
|
return this.chunkClient.chunkContent(params);
|
31
31
|
}
|
32
32
|
|
33
|
-
async asyncEmbeddingFileChunks(fileId: string, payload:
|
33
|
+
async asyncEmbeddingFileChunks(fileId: string, payload: ClientSecretPayload) {
|
34
34
|
const result = await this.fileModel.findById(fileId);
|
35
35
|
|
36
36
|
if (!result) return;
|
@@ -66,7 +66,7 @@ export class ChunkService {
|
|
66
66
|
/**
|
67
67
|
* parse file to chunks with async task
|
68
68
|
*/
|
69
|
-
async asyncParseFileToChunks(fileId: string, payload:
|
69
|
+
async asyncParseFileToChunks(fileId: string, payload: ClientSecretPayload, skipExist?: boolean) {
|
70
70
|
const result = await this.fileModel.findById(fileId);
|
71
71
|
|
72
72
|
if (!result) return;
|
@@ -462,19 +462,6 @@ export class DiscoverService {
|
|
462
462
|
return result;
|
463
463
|
};
|
464
464
|
|
465
|
-
getMcpIdentifiers = async (): Promise<IdentifiersResponse> => {
|
466
|
-
log('getMcpIdentifiers: fetching identifiers');
|
467
|
-
const result = await this.market.plugins.getPublishedIdentifiers({
|
468
|
-
cache: 'force-cache',
|
469
|
-
next: {
|
470
|
-
revalidate: CacheRevalidate.List,
|
471
|
-
tags: [CacheTag.Discover, CacheTag.MCP],
|
472
|
-
},
|
473
|
-
});
|
474
|
-
log('getMcpIdentifiers: returning %d identifiers', result.length);
|
475
|
-
return result;
|
476
|
-
};
|
477
|
-
|
478
465
|
getMcpList = async (params: McpQueryParams = {}): Promise<McpListResponse> => {
|
479
466
|
log('getMcpList: params=%O', params);
|
480
467
|
const { locale } = params;
|