@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.
Files changed (75) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/Dockerfile +1 -1
  3. package/Dockerfile.database +1 -1
  4. package/changelog/v1.json +21 -0
  5. package/docs/usage/providers/wenxin.mdx +16 -13
  6. package/docs/usage/providers/wenxin.zh-CN.mdx +11 -8
  7. package/next.config.ts +6 -0
  8. package/package.json +1 -2
  9. package/src/app/(main)/settings/llm/ProviderList/providers.tsx +2 -4
  10. package/src/config/aiModels/ai360.ts +22 -2
  11. package/src/config/aiModels/fireworksai.ts +3 -0
  12. package/src/config/aiModels/giteeai.ts +60 -0
  13. package/src/config/aiModels/github.ts +7 -0
  14. package/src/config/aiModels/google.ts +2 -0
  15. package/src/config/aiModels/groq.ts +12 -0
  16. package/src/config/aiModels/huggingface.ts +6 -0
  17. package/src/config/aiModels/internlm.ts +19 -2
  18. package/src/config/aiModels/ollama.ts +1 -0
  19. package/src/config/aiModels/openai.ts +10 -0
  20. package/src/config/aiModels/perplexity.ts +3 -0
  21. package/src/config/aiModels/qwen.ts +2 -0
  22. package/src/config/aiModels/siliconcloud.ts +4 -0
  23. package/src/config/aiModels/togetherai.ts +64 -1
  24. package/src/config/aiModels/wenxin.ts +125 -19
  25. package/src/config/aiModels/zhipu.ts +3 -0
  26. package/src/config/llm.ts +3 -5
  27. package/src/config/modelProviders/wenxin.ts +100 -23
  28. package/src/const/auth.ts +0 -3
  29. package/src/features/Conversation/Error/APIKeyForm/index.tsx +0 -3
  30. package/src/features/Conversation/components/ChatItem/utils.test.ts +284 -0
  31. package/src/features/Conversation/components/ChatItem/utils.ts +39 -8
  32. package/src/features/Conversation/components/MarkdownElements/LobeArtifact/rehypePlugin.test.ts +125 -0
  33. package/src/features/DevPanel/CacheViewer/DataTable/index.tsx +33 -0
  34. package/src/features/DevPanel/CacheViewer/cacheProvider.tsx +64 -0
  35. package/src/features/DevPanel/CacheViewer/getCacheEntries.ts +52 -0
  36. package/src/features/DevPanel/CacheViewer/index.tsx +25 -0
  37. package/src/features/DevPanel/CacheViewer/schema.ts +49 -0
  38. package/src/features/DevPanel/FeatureFlagViewer/Form.tsx +93 -0
  39. package/src/features/DevPanel/FeatureFlagViewer/index.tsx +11 -0
  40. package/src/features/DevPanel/MetadataViewer/Ld.tsx +25 -0
  41. package/src/features/DevPanel/MetadataViewer/MetaData.tsx +30 -0
  42. package/src/features/DevPanel/MetadataViewer/Og.tsx +75 -0
  43. package/src/features/DevPanel/MetadataViewer/index.tsx +80 -0
  44. package/src/features/DevPanel/MetadataViewer/useHead.ts +16 -0
  45. package/src/features/DevPanel/PostgresViewer/DataTable/index.tsx +39 -49
  46. package/src/features/DevPanel/PostgresViewer/{TableColumns.tsx → SchemaSidebar/Columns.tsx} +6 -4
  47. package/src/features/DevPanel/PostgresViewer/{Schema.tsx → SchemaSidebar/index.tsx} +49 -55
  48. package/src/features/DevPanel/PostgresViewer/index.tsx +4 -2
  49. package/src/features/DevPanel/features/FloatPanel.tsx +218 -0
  50. package/src/features/DevPanel/features/Header.tsx +50 -0
  51. package/src/features/DevPanel/features/Table/TableCell.tsx +73 -0
  52. package/src/features/DevPanel/features/Table/TooltipContent.tsx +39 -0
  53. package/src/features/DevPanel/{PostgresViewer/DataTable/Table.tsx → features/Table/index.tsx} +12 -14
  54. package/src/features/DevPanel/index.tsx +29 -5
  55. package/src/libs/agent-runtime/AgentRuntime.test.ts +0 -1
  56. package/src/libs/agent-runtime/AgentRuntime.ts +7 -0
  57. package/src/libs/agent-runtime/wenxin/index.ts +10 -107
  58. package/src/locales/default/modelProvider.ts +0 -20
  59. package/src/server/modules/AgentRuntime/index.test.ts +0 -21
  60. package/src/services/_auth.ts +0 -14
  61. package/src/store/chat/slices/portal/selectors.test.ts +169 -3
  62. package/src/store/chat/slices/portal/selectors.ts +6 -1
  63. package/src/store/user/slices/modelList/selectors/keyVaults.ts +0 -2
  64. package/src/types/aiProvider.ts +0 -1
  65. package/src/types/user/settings/keyVaults.ts +1 -6
  66. package/src/app/(backend)/webapi/chat/wenxin/route.test.ts +0 -27
  67. package/src/app/(backend)/webapi/chat/wenxin/route.ts +0 -30
  68. package/src/app/(main)/settings/llm/ProviderList/Wenxin/index.tsx +0 -44
  69. package/src/app/(main)/settings/provider/(detail)/wenxin/page.tsx +0 -61
  70. package/src/features/Conversation/Error/APIKeyForm/Wenxin.tsx +0 -49
  71. package/src/features/DevPanel/FloatPanel.tsx +0 -136
  72. package/src/features/DevPanel/PostgresViewer/DataTable/TableCell.tsx +0 -34
  73. package/src/libs/agent-runtime/utils/streams/wenxin.test.ts +0 -153
  74. package/src/libs/agent-runtime/utils/streams/wenxin.ts +0 -38
  75. 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
 
@@ -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
- }) as ChatStoreState;
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
- return result?.groups?.content || '';
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
  };
@@ -23,7 +23,6 @@ export const AiProviderSDKEnum = {
23
23
  Huggingface: 'huggingface',
24
24
  Ollama: 'ollama',
25
25
  Openai: 'openai',
26
- Wenxin: 'wenxin',
27
26
  } as const;
28
27
 
29
28
  export type AiProviderSDKType = (typeof AiProviderSDKEnum)[keyof typeof AiProviderSDKEnum];
@@ -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?: WenxinKeyVault;
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;