@lobehub/chat 1.51.1 → 1.51.3
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 +66 -0
- package/Dockerfile +1 -1
- package/Dockerfile.database +1 -1
- package/changelog/v1.json +21 -0
- package/docs/usage/providers/wenxin.mdx +16 -13
- package/docs/usage/providers/wenxin.zh-CN.mdx +11 -8
- package/next.config.ts +6 -0
- package/package.json +1 -2
- package/src/app/(main)/settings/llm/ProviderList/providers.tsx +2 -4
- package/src/config/aiModels/ai360.ts +22 -2
- package/src/config/aiModels/fireworksai.ts +3 -0
- package/src/config/aiModels/giteeai.ts +60 -0
- package/src/config/aiModels/github.ts +7 -0
- package/src/config/aiModels/google.ts +2 -0
- package/src/config/aiModels/groq.ts +12 -0
- package/src/config/aiModels/huggingface.ts +6 -0
- package/src/config/aiModels/internlm.ts +19 -2
- package/src/config/aiModels/ollama.ts +1 -0
- package/src/config/aiModels/openai.ts +10 -0
- package/src/config/aiModels/perplexity.ts +3 -0
- package/src/config/aiModels/qwen.ts +2 -0
- package/src/config/aiModels/siliconcloud.ts +4 -0
- package/src/config/aiModels/togetherai.ts +64 -1
- package/src/config/aiModels/wenxin.ts +125 -19
- package/src/config/aiModels/zhipu.ts +3 -0
- package/src/config/llm.ts +3 -5
- package/src/config/modelProviders/wenxin.ts +100 -23
- package/src/const/auth.ts +0 -3
- package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -3
- package/src/features/Conversation/components/ChatItem/utils.test.ts +284 -0
- package/src/features/Conversation/components/ChatItem/utils.ts +39 -8
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/rehypePlugin.test.ts +125 -0
- package/src/features/DevPanel/CacheViewer/DataTable/index.tsx +33 -0
- package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +64 -0
- package/src/features/DevPanel/CacheViewer/getCacheEntries.ts +52 -0
- package/src/features/DevPanel/CacheViewer/index.tsx +25 -0
- package/src/features/DevPanel/CacheViewer/schema.ts +49 -0
- package/src/features/DevPanel/FeatureFlagViewer/Form.tsx +93 -0
- package/src/features/DevPanel/FeatureFlagViewer/index.tsx +11 -0
- package/src/features/DevPanel/MetadataViewer/Ld.tsx +25 -0
- package/src/features/DevPanel/MetadataViewer/MetaData.tsx +30 -0
- package/src/features/DevPanel/MetadataViewer/Og.tsx +75 -0
- package/src/features/DevPanel/MetadataViewer/index.tsx +80 -0
- package/src/features/DevPanel/MetadataViewer/useHead.ts +16 -0
- package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +39 -49
- package/src/features/DevPanel/PostgresViewer/{TableColumns.tsx → SchemaSidebar/Columns.tsx} +6 -4
- package/src/features/DevPanel/PostgresViewer/{Schema.tsx → SchemaSidebar/index.tsx} +49 -55
- package/src/features/DevPanel/PostgresViewer/index.tsx +4 -2
- package/src/features/DevPanel/features/FloatPanel.tsx +218 -0
- package/src/features/DevPanel/features/Header.tsx +50 -0
- package/src/features/DevPanel/features/Table/TableCell.tsx +73 -0
- package/src/features/DevPanel/features/Table/TooltipContent.tsx +39 -0
- package/src/features/DevPanel/{PostgresViewer/DataTable/Table.tsx → features/Table/index.tsx} +12 -14
- package/src/features/DevPanel/index.tsx +29 -5
- package/src/libs/agent-runtime/AgentRuntime.test.ts +0 -1
- package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
- package/src/libs/agent-runtime/wenxin/index.ts +10 -107
- package/src/locales/default/modelProvider.ts +0 -20
- package/src/server/modules/AgentRuntime/index.test.ts +0 -21
- package/src/services/_auth.ts +0 -14
- package/src/store/chat/slices/portal/selectors.test.ts +169 -3
- package/src/store/chat/slices/portal/selectors.ts +6 -1
- package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -2
- package/src/types/aiProvider.ts +0 -1
- package/src/types/user/settings/keyVaults.ts +1 -6
- package/src/app/(backend)/webapi/chat/wenxin/route.test.ts +0 -27
- package/src/app/(backend)/webapi/chat/wenxin/route.ts +0 -30
- package/src/app/(main)/settings/llm/ProviderList/Wenxin/index.tsx +0 -44
- package/src/app/(main)/settings/provider/(detail)/wenxin/page.tsx +0 -61
- package/src/features/Conversation/Error/APIKeyForm/Wenxin.tsx +0 -49
- package/src/features/DevPanel/FloatPanel.tsx +0 -136
- package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +0 -34
- package/src/libs/agent-runtime/utils/streams/wenxin.test.ts +0 -153
- package/src/libs/agent-runtime/utils/streams/wenxin.ts +0 -38
- package/src/libs/agent-runtime/wenxin/type.ts +0 -84
@@ -25,7 +25,6 @@ import {
|
|
25
25
|
} from '@/libs/agent-runtime';
|
26
26
|
import { AgentRuntime } from '@/libs/agent-runtime';
|
27
27
|
import { LobeStepfunAI } from '@/libs/agent-runtime/stepfun';
|
28
|
-
import LobeWenxinAI from '@/libs/agent-runtime/wenxin';
|
29
28
|
|
30
29
|
import { initAgentRuntimeWithUserPayload } from './index';
|
31
30
|
|
@@ -55,9 +54,6 @@ vi.mock('@/config/llm', () => ({
|
|
55
54
|
TOGETHERAI_API_KEY: 'test-togetherai-key',
|
56
55
|
QWEN_API_KEY: 'test-qwen-key',
|
57
56
|
STEPFUN_API_KEY: 'test-stepfun-key',
|
58
|
-
|
59
|
-
WENXIN_ACCESS_KEY: 'test-wenxin-access-key',
|
60
|
-
WENXIN_SECRET_KEY: 'test-wenxin-secret-key',
|
61
57
|
})),
|
62
58
|
}));
|
63
59
|
|
@@ -207,16 +203,6 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
207
203
|
expect(runtime['_runtime']).toBeInstanceOf(LobeStepfunAI);
|
208
204
|
});
|
209
205
|
|
210
|
-
it.skip('Wenxin AI provider: with apikey', async () => {
|
211
|
-
const jwtPayload: JWTPayload = {
|
212
|
-
wenxinAccessKey: 'user-wenxin-accessKey',
|
213
|
-
wenxinSecretKey: 'wenxin-secret-key',
|
214
|
-
};
|
215
|
-
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Wenxin, jwtPayload);
|
216
|
-
expect(runtime).toBeInstanceOf(AgentRuntime);
|
217
|
-
expect(runtime['_runtime']).toBeInstanceOf(LobeWenxinAI);
|
218
|
-
});
|
219
|
-
|
220
206
|
it('Unknown Provider: with apikey and endpoint, should initialize to OpenAi', async () => {
|
221
207
|
const jwtPayload: JWTPayload = {
|
222
208
|
apiKey: 'user-unknown-key',
|
@@ -364,13 +350,6 @@ describe('initAgentRuntimeWithUserPayload method', () => {
|
|
364
350
|
expect(runtime['_runtime']).toBeInstanceOf(LobeTogetherAI);
|
365
351
|
});
|
366
352
|
|
367
|
-
it.skip('Wenxin AI provider: without apikey', async () => {
|
368
|
-
const jwtPayload = {};
|
369
|
-
const runtime = await initAgentRuntimeWithUserPayload(ModelProvider.Wenxin, jwtPayload);
|
370
|
-
expect(runtime).toBeInstanceOf(AgentRuntime);
|
371
|
-
expect(runtime['_runtime']).toBeInstanceOf(LobeWenxinAI);
|
372
|
-
});
|
373
|
-
|
374
353
|
it('OpenAI provider: without apikey with OPENAI_PROXY_URL', async () => {
|
375
354
|
process.env.OPENAI_PROXY_URL = 'https://proxy.example.com/v1';
|
376
355
|
|
package/src/services/_auth.ts
CHANGED
@@ -9,7 +9,6 @@ import {
|
|
9
9
|
AzureOpenAIKeyVault,
|
10
10
|
CloudflareKeyVault,
|
11
11
|
OpenAICompatibleKeyVault,
|
12
|
-
WenxinKeyVault,
|
13
12
|
} from '@/types/user/settings';
|
14
13
|
import { createJWT } from '@/utils/jwt';
|
15
14
|
|
@@ -18,7 +17,6 @@ export const getProviderAuthPayload = (
|
|
18
17
|
keyVaults: OpenAICompatibleKeyVault &
|
19
18
|
AzureOpenAIKeyVault &
|
20
19
|
AWSBedrockKeyVault &
|
21
|
-
WenxinKeyVault &
|
22
20
|
CloudflareKeyVault,
|
23
21
|
) => {
|
24
22
|
switch (provider) {
|
@@ -47,18 +45,6 @@ export const getProviderAuthPayload = (
|
|
47
45
|
};
|
48
46
|
}
|
49
47
|
|
50
|
-
case ModelProvider.Wenxin: {
|
51
|
-
const { secretKey, accessKey } = keyVaults;
|
52
|
-
|
53
|
-
const apiKey = (accessKey || '') + (secretKey || '');
|
54
|
-
|
55
|
-
return {
|
56
|
-
apiKey,
|
57
|
-
wenxinAccessKey: accessKey,
|
58
|
-
wenxinSecretKey: secretKey,
|
59
|
-
};
|
60
|
-
}
|
61
|
-
|
62
48
|
case ModelProvider.Azure: {
|
63
49
|
return {
|
64
50
|
apiKey: keyVaults.apiKey,
|
@@ -1,16 +1,23 @@
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
2
2
|
|
3
3
|
import type { ChatStoreState } from '@/store/chat';
|
4
|
+
import { ChatMessage } from '@/types/message';
|
4
5
|
|
5
6
|
import { chatPortalSelectors } from './selectors';
|
6
7
|
|
7
8
|
describe('chatDockSelectors', () => {
|
8
|
-
const createState = (overrides?: Partial<ChatStoreState>) =>
|
9
|
-
|
9
|
+
const createState = (overrides?: Partial<ChatStoreState>) => {
|
10
|
+
const state = {
|
10
11
|
showPortal: false,
|
11
12
|
portalToolMessage: undefined,
|
13
|
+
messagesMap: {},
|
14
|
+
activeId: 'test-id',
|
15
|
+
activeTopicId: undefined,
|
12
16
|
...overrides,
|
13
|
-
}
|
17
|
+
} as ChatStoreState;
|
18
|
+
|
19
|
+
return state;
|
20
|
+
};
|
14
21
|
|
15
22
|
describe('showDock', () => {
|
16
23
|
it('should return the showDock state', () => {
|
@@ -92,4 +99,163 @@ describe('chatDockSelectors', () => {
|
|
92
99
|
expect(chatPortalSelectors.previewFileId(state)).toBe('file-id');
|
93
100
|
});
|
94
101
|
});
|
102
|
+
|
103
|
+
describe('artifactMessageContent', () => {
|
104
|
+
it('should return empty string when message not found', () => {
|
105
|
+
const state = createState();
|
106
|
+
expect(chatPortalSelectors.artifactMessageContent('non-existent-id')(state)).toBe('');
|
107
|
+
});
|
108
|
+
|
109
|
+
it('should return message content when message exists', () => {
|
110
|
+
const messageContent = 'Test message content';
|
111
|
+
const state = createState({
|
112
|
+
messagesMap: {
|
113
|
+
'test-id_null': [
|
114
|
+
{
|
115
|
+
id: 'test-id',
|
116
|
+
content: messageContent,
|
117
|
+
createdAt: Date.now(),
|
118
|
+
updatedAt: Date.now(),
|
119
|
+
role: 'user',
|
120
|
+
meta: {},
|
121
|
+
sessionId: 'test-id',
|
122
|
+
} as ChatMessage,
|
123
|
+
],
|
124
|
+
},
|
125
|
+
});
|
126
|
+
expect(chatPortalSelectors.artifactMessageContent('test-id')(state)).toBe(messageContent);
|
127
|
+
});
|
128
|
+
});
|
129
|
+
|
130
|
+
describe('artifactCode', () => {
|
131
|
+
it('should return empty string when no artifact tag found', () => {
|
132
|
+
const state = createState({
|
133
|
+
messagesMap: {
|
134
|
+
'test-id_null': [
|
135
|
+
{
|
136
|
+
id: 'test-id',
|
137
|
+
content: 'No artifact tag here',
|
138
|
+
createdAt: Date.now(),
|
139
|
+
updatedAt: Date.now(),
|
140
|
+
role: 'user',
|
141
|
+
meta: {},
|
142
|
+
sessionId: 'test-id',
|
143
|
+
} as ChatMessage,
|
144
|
+
],
|
145
|
+
},
|
146
|
+
});
|
147
|
+
expect(chatPortalSelectors.artifactCode('test-id')(state)).toBe('');
|
148
|
+
});
|
149
|
+
|
150
|
+
it('should extract content from artifact tag', () => {
|
151
|
+
const artifactContent = 'Test artifact content';
|
152
|
+
const state = createState({
|
153
|
+
messagesMap: {
|
154
|
+
'test-id_null': [
|
155
|
+
{
|
156
|
+
id: 'test-id',
|
157
|
+
content: `<lobeArtifact type="text">${artifactContent}</lobeArtifact>`,
|
158
|
+
createdAt: Date.now(),
|
159
|
+
updatedAt: Date.now(),
|
160
|
+
role: 'user',
|
161
|
+
meta: {},
|
162
|
+
sessionId: 'test-id',
|
163
|
+
} as ChatMessage,
|
164
|
+
],
|
165
|
+
},
|
166
|
+
});
|
167
|
+
expect(chatPortalSelectors.artifactCode('test-id')(state)).toBe(artifactContent);
|
168
|
+
});
|
169
|
+
|
170
|
+
it('should remove markdown code block wrapping HTML content', () => {
|
171
|
+
const htmlContent = `<!DOCTYPE html>
|
172
|
+
<html>
|
173
|
+
<head>
|
174
|
+
<title>Test</title>
|
175
|
+
</head>
|
176
|
+
<body>
|
177
|
+
<div>Test content</div>
|
178
|
+
</body>
|
179
|
+
</html>`;
|
180
|
+
const state = createState({
|
181
|
+
messagesMap: {
|
182
|
+
'test-id_null': [
|
183
|
+
{
|
184
|
+
id: 'test-id',
|
185
|
+
content: `<lobeArtifact type="text/html">
|
186
|
+
\`\`\`html
|
187
|
+
${htmlContent}
|
188
|
+
\`\`\`
|
189
|
+
</lobeArtifact>`,
|
190
|
+
createdAt: Date.now(),
|
191
|
+
updatedAt: Date.now(),
|
192
|
+
role: 'user',
|
193
|
+
meta: {},
|
194
|
+
sessionId: 'test-id',
|
195
|
+
} as ChatMessage,
|
196
|
+
],
|
197
|
+
},
|
198
|
+
});
|
199
|
+
expect(chatPortalSelectors.artifactCode('test-id')(state)).toBe(htmlContent);
|
200
|
+
});
|
201
|
+
});
|
202
|
+
|
203
|
+
describe('isArtifactTagClosed', () => {
|
204
|
+
it('should return false for unclosed artifact tag', () => {
|
205
|
+
const state = createState({
|
206
|
+
messagesMap: {
|
207
|
+
'test-id_null': [
|
208
|
+
{
|
209
|
+
id: 'test-id',
|
210
|
+
content: '<lobeArtifact type="text">Test content',
|
211
|
+
createdAt: Date.now(),
|
212
|
+
updatedAt: Date.now(),
|
213
|
+
role: 'user',
|
214
|
+
meta: {},
|
215
|
+
sessionId: 'test-id',
|
216
|
+
} as ChatMessage,
|
217
|
+
],
|
218
|
+
},
|
219
|
+
});
|
220
|
+
expect(chatPortalSelectors.isArtifactTagClosed('test-id')(state)).toBe(false);
|
221
|
+
});
|
222
|
+
|
223
|
+
it('should return true for closed artifact tag', () => {
|
224
|
+
const state = createState({
|
225
|
+
messagesMap: {
|
226
|
+
'test-id_null': [
|
227
|
+
{
|
228
|
+
id: 'test-id',
|
229
|
+
content: '<lobeArtifact type="text">Test content</lobeArtifact>',
|
230
|
+
createdAt: Date.now(),
|
231
|
+
updatedAt: Date.now(),
|
232
|
+
role: 'user',
|
233
|
+
meta: {},
|
234
|
+
sessionId: 'test-id',
|
235
|
+
} as ChatMessage,
|
236
|
+
],
|
237
|
+
},
|
238
|
+
});
|
239
|
+
expect(chatPortalSelectors.isArtifactTagClosed('test-id')(state)).toBe(true);
|
240
|
+
});
|
241
|
+
|
242
|
+
it('should return false when no artifact tag exists', () => {
|
243
|
+
const state = createState({
|
244
|
+
messagesMap: {
|
245
|
+
'test-id_null': [
|
246
|
+
{
|
247
|
+
id: 'test-id',
|
248
|
+
content: 'No artifact tag here',
|
249
|
+
createdAt: Date.now(),
|
250
|
+
updatedAt: Date.now(),
|
251
|
+
role: 'user',
|
252
|
+
meta: {},
|
253
|
+
sessionId: 'test-id',
|
254
|
+
} as ChatMessage,
|
255
|
+
],
|
256
|
+
},
|
257
|
+
});
|
258
|
+
expect(chatPortalSelectors.isArtifactTagClosed('test-id')(state)).toBe(false);
|
259
|
+
});
|
260
|
+
});
|
95
261
|
});
|
@@ -35,7 +35,12 @@ const artifactCode = (id: string) => (s: ChatStoreState) => {
|
|
35
35
|
const messageContent = artifactMessageContent(id)(s);
|
36
36
|
const result = messageContent.match(ARTIFACT_TAG_REGEX);
|
37
37
|
|
38
|
-
|
38
|
+
let content = result?.groups?.content || '';
|
39
|
+
|
40
|
+
// Remove markdown code block if content is wrapped
|
41
|
+
content = content.replace(/^\s*```[^\n]*\n([\S\s]*?)\n```\s*$/, '$1');
|
42
|
+
|
43
|
+
return content;
|
39
44
|
};
|
40
45
|
|
41
46
|
const isArtifactTagClosed = (id: string) => (s: ChatStoreState) => {
|
@@ -14,7 +14,6 @@ export const keyVaultsSettings = (s: UserStore): UserKeyVaults =>
|
|
14
14
|
|
15
15
|
const openAIConfig = (s: UserStore) => keyVaultsSettings(s).openai || {};
|
16
16
|
const bedrockConfig = (s: UserStore) => keyVaultsSettings(s).bedrock || {};
|
17
|
-
const wenxinConfig = (s: UserStore) => keyVaultsSettings(s).wenxin || {};
|
18
17
|
const ollamaConfig = (s: UserStore) => keyVaultsSettings(s).ollama || {};
|
19
18
|
const azureConfig = (s: UserStore) => keyVaultsSettings(s).azure || {};
|
20
19
|
const cloudflareConfig = (s: UserStore) => keyVaultsSettings(s).cloudflare || {};
|
@@ -45,5 +44,4 @@ export const keyVaultsConfigSelectors = {
|
|
45
44
|
ollamaConfig,
|
46
45
|
openAIConfig,
|
47
46
|
password,
|
48
|
-
wenxinConfig,
|
49
47
|
};
|
package/src/types/aiProvider.ts
CHANGED
@@ -25,11 +25,6 @@ export interface CloudflareKeyVault {
|
|
25
25
|
baseURLOrAccountID?: string;
|
26
26
|
}
|
27
27
|
|
28
|
-
export interface WenxinKeyVault {
|
29
|
-
accessKey?: string;
|
30
|
-
secretKey?: string;
|
31
|
-
}
|
32
|
-
|
33
28
|
export interface UserKeyVaults {
|
34
29
|
ai21?: OpenAICompatibleKeyVault;
|
35
30
|
ai360?: OpenAICompatibleKeyVault;
|
@@ -68,7 +63,7 @@ export interface UserKeyVaults {
|
|
68
63
|
taichu?: OpenAICompatibleKeyVault;
|
69
64
|
togetherai?: OpenAICompatibleKeyVault;
|
70
65
|
upstage?: OpenAICompatibleKeyVault;
|
71
|
-
wenxin?:
|
66
|
+
wenxin?: OpenAICompatibleKeyVault;
|
72
67
|
xai?: OpenAICompatibleKeyVault;
|
73
68
|
zeroone?: OpenAICompatibleKeyVault;
|
74
69
|
zhipu?: OpenAICompatibleKeyVault;
|
@@ -1,27 +0,0 @@
|
|
1
|
-
// @vitest-environment edge-runtime
|
2
|
-
import { describe, expect, it, vi } from 'vitest';
|
3
|
-
|
4
|
-
import { POST as UniverseRoute } from '../[provider]/route';
|
5
|
-
import { POST, runtime } from './route';
|
6
|
-
|
7
|
-
// 模拟 '../[provider]/route'
|
8
|
-
vi.mock('../[provider]/route', () => ({
|
9
|
-
POST: vi.fn().mockResolvedValue('mocked response'),
|
10
|
-
}));
|
11
|
-
|
12
|
-
describe('Configuration tests', () => {
|
13
|
-
it('should have runtime set to "edge"', () => {
|
14
|
-
expect(runtime).toBe('nodejs');
|
15
|
-
});
|
16
|
-
});
|
17
|
-
|
18
|
-
describe('Wenxin POST function tests', () => {
|
19
|
-
it('should call UniverseRoute with correct parameters', async () => {
|
20
|
-
const mockRequest = new Request('https://example.com', { method: 'POST' });
|
21
|
-
await POST(mockRequest);
|
22
|
-
expect(UniverseRoute).toHaveBeenCalledWith(mockRequest, {
|
23
|
-
createRuntime: expect.anything(),
|
24
|
-
params: Promise.resolve({ provider: 'wenxin' }),
|
25
|
-
});
|
26
|
-
});
|
27
|
-
});
|
@@ -1,30 +0,0 @@
|
|
1
|
-
import { getLLMConfig } from '@/config/llm';
|
2
|
-
import { AgentRuntime, ModelProvider } from '@/libs/agent-runtime';
|
3
|
-
import LobeWenxinAI from '@/libs/agent-runtime/wenxin';
|
4
|
-
|
5
|
-
import { POST as UniverseRoute } from '../[provider]/route';
|
6
|
-
|
7
|
-
export const runtime = 'nodejs';
|
8
|
-
|
9
|
-
export const maxDuration = 30;
|
10
|
-
|
11
|
-
export const POST = async (req: Request) =>
|
12
|
-
UniverseRoute(req, {
|
13
|
-
createRuntime: (payload) => {
|
14
|
-
const { WENXIN_ACCESS_KEY, WENXIN_SECRET_KEY } = getLLMConfig();
|
15
|
-
let accessKey: string | undefined = WENXIN_ACCESS_KEY;
|
16
|
-
let secretKey: string | undefined = WENXIN_SECRET_KEY;
|
17
|
-
|
18
|
-
// if the payload has the api key, use user
|
19
|
-
if (payload.apiKey) {
|
20
|
-
accessKey = payload?.wenxinAccessKey;
|
21
|
-
secretKey = payload?.wenxinSecretKey;
|
22
|
-
}
|
23
|
-
|
24
|
-
const params = { accessKey, secretKey };
|
25
|
-
const instance = new LobeWenxinAI(params);
|
26
|
-
|
27
|
-
return new AgentRuntime(instance);
|
28
|
-
},
|
29
|
-
params: Promise.resolve({ provider: ModelProvider.Wenxin }),
|
30
|
-
});
|
@@ -1,44 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import { Input } from 'antd';
|
4
|
-
import { useTranslation } from 'react-i18next';
|
5
|
-
|
6
|
-
import { WenxinProviderCard } 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 = 'wenxin';
|
13
|
-
|
14
|
-
export const useWenxinProvider = (): ProviderItem => {
|
15
|
-
const { t } = useTranslation('modelProvider');
|
16
|
-
|
17
|
-
return {
|
18
|
-
...WenxinProviderCard,
|
19
|
-
apiKeyItems: [
|
20
|
-
{
|
21
|
-
children: (
|
22
|
-
<Input.Password
|
23
|
-
autoComplete={'new-password'}
|
24
|
-
placeholder={t(`${providerKey}.accessKey.placeholder`)}
|
25
|
-
/>
|
26
|
-
),
|
27
|
-
desc: t(`${providerKey}.accessKey.desc`),
|
28
|
-
label: t(`${providerKey}.accessKey.title`),
|
29
|
-
name: [KeyVaultsConfigKey, providerKey, 'accessKey'],
|
30
|
-
},
|
31
|
-
{
|
32
|
-
children: (
|
33
|
-
<Input.Password
|
34
|
-
autoComplete={'new-password'}
|
35
|
-
placeholder={t(`${providerKey}.secretKey.placeholder`)}
|
36
|
-
/>
|
37
|
-
),
|
38
|
-
desc: t(`${providerKey}.secretKey.desc`),
|
39
|
-
label: t(`${providerKey}.secretKey.title`),
|
40
|
-
name: [KeyVaultsConfigKey, providerKey, 'secretKey'],
|
41
|
-
},
|
42
|
-
],
|
43
|
-
};
|
44
|
-
};
|
@@ -1,61 +0,0 @@
|
|
1
|
-
'use client';
|
2
|
-
|
3
|
-
import { useTranslation } from 'react-i18next';
|
4
|
-
|
5
|
-
import { FormPassword } from '@/components/FormInput';
|
6
|
-
import { WenxinProviderCard } from '@/config/modelProviders';
|
7
|
-
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
8
|
-
import { GlobalLLMProviderKey } from '@/types/user/settings';
|
9
|
-
|
10
|
-
import { KeyVaultsConfigKey } from '../../const';
|
11
|
-
import { SkeletonInput } from '../../features/ProviderConfig';
|
12
|
-
import { ProviderItem } from '../../type';
|
13
|
-
import ProviderDetail from '../[id]';
|
14
|
-
|
15
|
-
const providerKey: GlobalLLMProviderKey = 'wenxin';
|
16
|
-
|
17
|
-
const useProviderCard = (): ProviderItem => {
|
18
|
-
const { t } = useTranslation('modelProvider');
|
19
|
-
|
20
|
-
const isLoading = useAiInfraStore(aiProviderSelectors.isAiProviderConfigLoading(providerKey));
|
21
|
-
|
22
|
-
return {
|
23
|
-
...WenxinProviderCard,
|
24
|
-
apiKeyItems: [
|
25
|
-
{
|
26
|
-
children: isLoading ? (
|
27
|
-
<SkeletonInput />
|
28
|
-
) : (
|
29
|
-
<FormPassword
|
30
|
-
autoComplete={'new-password'}
|
31
|
-
placeholder={t(`${providerKey}.accessKey.placeholder`)}
|
32
|
-
/>
|
33
|
-
),
|
34
|
-
desc: t(`${providerKey}.accessKey.desc`),
|
35
|
-
label: t(`${providerKey}.accessKey.title`),
|
36
|
-
name: [KeyVaultsConfigKey, 'accessKey'],
|
37
|
-
},
|
38
|
-
{
|
39
|
-
children: isLoading ? (
|
40
|
-
<SkeletonInput />
|
41
|
-
) : (
|
42
|
-
<FormPassword
|
43
|
-
autoComplete={'new-password'}
|
44
|
-
placeholder={t(`${providerKey}.secretKey.placeholder`)}
|
45
|
-
/>
|
46
|
-
),
|
47
|
-
desc: t(`${providerKey}.secretKey.desc`),
|
48
|
-
label: t(`${providerKey}.secretKey.title`),
|
49
|
-
name: [KeyVaultsConfigKey, 'secretKey'],
|
50
|
-
},
|
51
|
-
],
|
52
|
-
};
|
53
|
-
};
|
54
|
-
|
55
|
-
const Page = () => {
|
56
|
-
const card = useProviderCard();
|
57
|
-
|
58
|
-
return <ProviderDetail {...card} />;
|
59
|
-
};
|
60
|
-
|
61
|
-
export default Page;
|
@@ -1,49 +0,0 @@
|
|
1
|
-
import { Wenxin } 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 WenxinForm = memo(() => {
|
13
|
-
const { t } = useTranslation('modelProvider');
|
14
|
-
|
15
|
-
const [accessKey, secretKey, setConfig] = useUserStore((s) => [
|
16
|
-
keyVaultsConfigSelectors.wenxinConfig(s).accessKey,
|
17
|
-
keyVaultsConfigSelectors.wenxinConfig(s).secretKey,
|
18
|
-
s.updateKeyVaultConfig,
|
19
|
-
]);
|
20
|
-
|
21
|
-
return (
|
22
|
-
<FormAction
|
23
|
-
avatar={<Wenxin.Color size={56} />}
|
24
|
-
description={t('wenxin.unlock.description')}
|
25
|
-
title={t('wenxin.unlock.title')}
|
26
|
-
>
|
27
|
-
<Input.Password
|
28
|
-
autoComplete={'new-password'}
|
29
|
-
onChange={(e) => {
|
30
|
-
setConfig(ModelProvider.Wenxin, { accessKey: e.target.value });
|
31
|
-
}}
|
32
|
-
placeholder={'Access Key'}
|
33
|
-
type={'block'}
|
34
|
-
value={accessKey}
|
35
|
-
/>
|
36
|
-
<Input.Password
|
37
|
-
autoComplete={'new-password'}
|
38
|
-
onChange={(e) => {
|
39
|
-
setConfig(ModelProvider.Wenxin, { secretKey: e.target.value });
|
40
|
-
}}
|
41
|
-
placeholder={'Secret Key'}
|
42
|
-
type={'block'}
|
43
|
-
value={secretKey}
|
44
|
-
/>
|
45
|
-
</FormAction>
|
46
|
-
);
|
47
|
-
});
|
48
|
-
|
49
|
-
export default WenxinForm;
|
@@ -1,136 +0,0 @@
|
|
1
|
-
import { ActionIcon, Icon } from '@lobehub/ui';
|
2
|
-
import { FloatButton } from 'antd';
|
3
|
-
import { createStyles } from 'antd-style';
|
4
|
-
import { BugIcon, BugOff, XIcon } from 'lucide-react';
|
5
|
-
import React, { PropsWithChildren, useEffect, useState } from 'react';
|
6
|
-
import { Flexbox } from 'react-layout-kit';
|
7
|
-
import { Rnd } from 'react-rnd';
|
8
|
-
|
9
|
-
// 定义样式
|
10
|
-
const useStyles = createStyles(({ token, css }) => {
|
11
|
-
return {
|
12
|
-
collapsed: css`
|
13
|
-
pointer-events: none;
|
14
|
-
transform: scale(0.8);
|
15
|
-
opacity: 0;
|
16
|
-
`,
|
17
|
-
content: css`
|
18
|
-
overflow: auto;
|
19
|
-
flex: 1;
|
20
|
-
height: 100%;
|
21
|
-
color: ${token.colorText};
|
22
|
-
`,
|
23
|
-
|
24
|
-
expanded: css`
|
25
|
-
pointer-events: auto;
|
26
|
-
transform: scale(1);
|
27
|
-
opacity: 1;
|
28
|
-
`,
|
29
|
-
|
30
|
-
header: css`
|
31
|
-
cursor: move;
|
32
|
-
user-select: none;
|
33
|
-
|
34
|
-
padding-block: 8px;
|
35
|
-
padding-inline: 16px;
|
36
|
-
border-block-end: 1px solid ${token.colorBorderSecondary};
|
37
|
-
border-start-start-radius: 12px;
|
38
|
-
border-start-end-radius: 12px;
|
39
|
-
|
40
|
-
font-weight: ${token.fontWeightStrong};
|
41
|
-
color: ${token.colorText};
|
42
|
-
|
43
|
-
background: ${token.colorFillAlter};
|
44
|
-
`,
|
45
|
-
panel: css`
|
46
|
-
position: fixed;
|
47
|
-
z-index: 1000;
|
48
|
-
|
49
|
-
overflow: hidden;
|
50
|
-
display: flex;
|
51
|
-
|
52
|
-
border-radius: 12px;
|
53
|
-
|
54
|
-
background: ${token.colorBgContainer};
|
55
|
-
box-shadow: ${token.boxShadow};
|
56
|
-
|
57
|
-
transition: opacity ${token.motionDurationMid} ${token.motionEaseInOut};
|
58
|
-
`,
|
59
|
-
};
|
60
|
-
});
|
61
|
-
|
62
|
-
const minWidth = 800;
|
63
|
-
const minHeight = 600;
|
64
|
-
|
65
|
-
const CollapsibleFloatPanel = ({ children }: PropsWithChildren) => {
|
66
|
-
const { styles } = useStyles();
|
67
|
-
const [isExpanded, setIsExpanded] = useState(false);
|
68
|
-
const [position, setPosition] = useState({ x: 100, y: 100 });
|
69
|
-
const [size, setSize] = useState({ height: minHeight, width: minWidth });
|
70
|
-
|
71
|
-
useEffect(() => {
|
72
|
-
try {
|
73
|
-
const localStoragePosition = localStorage.getItem('debug-panel-position');
|
74
|
-
if (localStoragePosition && JSON.parse(localStoragePosition)) {
|
75
|
-
setPosition(JSON.parse(localStoragePosition));
|
76
|
-
}
|
77
|
-
} catch {
|
78
|
-
/* empty */
|
79
|
-
}
|
80
|
-
|
81
|
-
try {
|
82
|
-
const localStorageSize = localStorage.getItem('debug-panel-size');
|
83
|
-
if (localStorageSize && JSON.parse(localStorageSize)) {
|
84
|
-
setSize(JSON.parse(localStorageSize));
|
85
|
-
}
|
86
|
-
} catch {
|
87
|
-
/* empty */
|
88
|
-
}
|
89
|
-
}, []);
|
90
|
-
|
91
|
-
return (
|
92
|
-
<>
|
93
|
-
<FloatButton
|
94
|
-
icon={<Icon icon={isExpanded ? BugOff : BugIcon} />}
|
95
|
-
onClick={() => setIsExpanded(!isExpanded)}
|
96
|
-
style={{ bottom: 24, right: 24 }}
|
97
|
-
/>
|
98
|
-
{isExpanded && (
|
99
|
-
<Rnd
|
100
|
-
bounds="window"
|
101
|
-
className={`${styles.panel} ${isExpanded ? styles.expanded : styles.collapsed}`}
|
102
|
-
dragHandleClassName="panel-drag-handle"
|
103
|
-
minHeight={minHeight}
|
104
|
-
minWidth={minWidth}
|
105
|
-
onDragStop={(e, d) => {
|
106
|
-
setPosition({ x: d.x, y: d.y });
|
107
|
-
}}
|
108
|
-
onResizeStop={(e, direction, ref, delta, position) => {
|
109
|
-
setSize({
|
110
|
-
height: Number(ref.style.height),
|
111
|
-
width: Number(ref.style.width),
|
112
|
-
});
|
113
|
-
setPosition(position);
|
114
|
-
}}
|
115
|
-
position={position}
|
116
|
-
size={size}
|
117
|
-
>
|
118
|
-
<Flexbox height={'100%'}>
|
119
|
-
<Flexbox
|
120
|
-
align={'center'}
|
121
|
-
className={`panel-drag-handle ${styles.header}`}
|
122
|
-
horizontal
|
123
|
-
justify={'space-between'}
|
124
|
-
>
|
125
|
-
开发者面板
|
126
|
-
<ActionIcon icon={XIcon} onClick={() => setIsExpanded(false)} />
|
127
|
-
</Flexbox>
|
128
|
-
<Flexbox className={styles.content}>{children}</Flexbox>
|
129
|
-
</Flexbox>
|
130
|
-
</Rnd>
|
131
|
-
)}
|
132
|
-
</>
|
133
|
-
);
|
134
|
-
};
|
135
|
-
|
136
|
-
export default CollapsibleFloatPanel;
|