@lobehub/chat 1.53.9 → 1.53.10

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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.53.10](https://github.com/lobehub/lobe-chat/compare/v1.53.9...v1.53.10)
6
+
7
+ <sup>Released on **2025-02-13**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Fix api key input issue.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Fix api key input issue, closes [#6112](https://github.com/lobehub/lobe-chat/issues/6112) ([48e3b85](https://github.com/lobehub/lobe-chat/commit/48e3b85))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
5
30
  ### [Version 1.53.9](https://github.com/lobehub/lobe-chat/compare/v1.53.8...v1.53.9)
6
31
 
7
32
  <sup>Released on **2025-02-13**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,13 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Fix api key input issue."
6
+ ]
7
+ },
8
+ "date": "2025-02-13",
9
+ "version": "1.53.10"
10
+ },
2
11
  {
3
12
  "children": {
4
13
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.53.9",
3
+ "version": "1.53.10",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -45,5 +45,3 @@ const Page = async (props: PagePropsWithId) => {
45
45
  };
46
46
 
47
47
  export default Page;
48
-
49
- export const dynamic = 'auto';
@@ -0,0 +1,11 @@
1
+ import { createContext } from 'react';
2
+
3
+ interface LoadingContextValue {
4
+ loading: boolean;
5
+ setLoading: (loading: boolean) => void;
6
+ }
7
+
8
+ export const LoadingContext = createContext<LoadingContextValue>({
9
+ loading: false,
10
+ setLoading: () => {},
11
+ });
@@ -1,9 +1,11 @@
1
1
  import { Icon } from '@lobehub/ui';
2
- import { Button, Input } from 'antd';
3
- import { Network } from 'lucide-react';
4
- import { ReactNode, memo, useState } from 'react';
2
+ import { Button } from 'antd';
3
+ import { Loader2Icon, Network } from 'lucide-react';
4
+ import { ReactNode, memo, useContext, useState } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
6
 
7
+ import { FormInput, FormPassword } from '@/components/FormInput';
8
+ import { LoadingContext } from '@/features/Conversation/Error/APIKeyForm/LoadingContext';
7
9
  import { useProviderName } from '@/hooks/useProviderName';
8
10
  import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
9
11
  import { GlobalLLMProviderKey } from '@/types/user/settings';
@@ -27,6 +29,7 @@ const ProviderApiKeyForm = memo<ProviderApiKeyFormProps>(
27
29
  const { apiKey, baseURL, setConfig } = useApiKey(provider);
28
30
  const { showOpenAIProxyUrl } = useServerConfigStore(featureFlagsSelectors);
29
31
  const providerName = useProviderName(provider);
32
+ const { loading } = useContext(LoadingContext);
30
33
 
31
34
  return (
32
35
  <FormAction
@@ -34,25 +37,25 @@ const ProviderApiKeyForm = memo<ProviderApiKeyFormProps>(
34
37
  description={t(`unlock.apiKey.description`, { name: providerName, ns: 'error' })}
35
38
  title={t(`unlock.apiKey.title`, { name: providerName, ns: 'error' })}
36
39
  >
37
- <Input.Password
40
+ <FormPassword
38
41
  autoComplete={'new-password'}
39
- onChange={(e) => {
40
- setConfig(provider, { apiKey: e.target.value });
42
+ onChange={(value) => {
43
+ setConfig(provider, { apiKey: value });
41
44
  }}
42
45
  placeholder={apiKeyPlaceholder || 'sk-***********************'}
43
- type={'block'}
46
+ suffix={<div>{loading && <Icon icon={Loader2Icon} spin />}</div>}
44
47
  value={apiKey}
45
48
  />
46
49
 
47
50
  {showEndpoint &&
48
51
  showOpenAIProxyUrl &&
49
52
  (showProxy ? (
50
- <Input
51
- onChange={(e) => {
52
- setConfig(provider, { baseURL: e.target.value });
53
+ <FormInput
54
+ onChange={(value) => {
55
+ setConfig(provider, { baseURL: value });
53
56
  }}
54
57
  placeholder={'https://api.openai.com/v1'}
55
- type={'block'}
58
+ suffix={<div>{loading && <Icon icon={Loader2Icon} spin />}</div>}
56
59
  value={baseURL}
57
60
  />
58
61
  ) : (
@@ -1,6 +1,6 @@
1
1
  import { ProviderIcon } from '@lobehub/icons';
2
2
  import { Button } from 'antd';
3
- import { memo, useMemo } from 'react';
3
+ import { memo, useMemo, useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
  import { Center, Flexbox } from 'react-layout-kit';
6
6
 
@@ -9,6 +9,7 @@ import { useChatStore } from '@/store/chat';
9
9
  import { GlobalLLMProviderKey } from '@/types/user/settings';
10
10
 
11
11
  import BedrockForm from './Bedrock';
12
+ import { LoadingContext } from './LoadingContext';
12
13
  import ProviderApiKeyForm from './ProviderApiKeyForm';
13
14
 
14
15
  interface APIKeyFormProps {
@@ -18,6 +19,7 @@ interface APIKeyFormProps {
18
19
 
19
20
  const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
20
21
  const { t } = useTranslation('error');
22
+ const [loading, setLoading] = useState(false);
21
23
 
22
24
  const [resend, deleteMessage] = useChatStore((s) => [s.regenerateMessage, s.deleteMessage]);
23
25
 
@@ -62,38 +64,41 @@ const APIKeyForm = memo<APIKeyFormProps>(({ id, provider }) => {
62
64
  }, [provider]);
63
65
 
64
66
  return (
65
- <Center gap={16} style={{ maxWidth: 300 }}>
66
- {provider === ModelProvider.Bedrock ? (
67
- <BedrockForm />
68
- ) : (
69
- <ProviderApiKeyForm
70
- apiKeyPlaceholder={apiKeyPlaceholder}
71
- avatar={<ProviderIcon provider={provider} size={80} type={'avatar'} />}
72
- provider={provider as GlobalLLMProviderKey}
73
- showEndpoint={provider === ModelProvider.OpenAI}
74
- />
75
- )}
76
- <Flexbox gap={12} width={'100%'}>
77
- <Button
78
- block
79
- onClick={() => {
80
- resend(id);
81
- deleteMessage(id);
82
- }}
83
- style={{ marginTop: 8 }}
84
- type={'primary'}
85
- >
86
- {t('unlock.confirm')}
87
- </Button>
88
- <Button
89
- onClick={() => {
90
- deleteMessage(id);
91
- }}
92
- >
93
- {t('unlock.closeMessage')}
94
- </Button>
95
- </Flexbox>
96
- </Center>
67
+ <LoadingContext value={{ loading, setLoading }}>
68
+ <Center gap={16} style={{ maxWidth: 300 }}>
69
+ {provider === ModelProvider.Bedrock ? (
70
+ <BedrockForm />
71
+ ) : (
72
+ <ProviderApiKeyForm
73
+ apiKeyPlaceholder={apiKeyPlaceholder}
74
+ avatar={<ProviderIcon provider={provider} size={80} type={'avatar'} />}
75
+ provider={provider as GlobalLLMProviderKey}
76
+ showEndpoint={provider === ModelProvider.OpenAI}
77
+ />
78
+ )}
79
+ <Flexbox gap={12} width={'100%'}>
80
+ <Button
81
+ block
82
+ disabled={loading}
83
+ onClick={() => {
84
+ resend(id);
85
+ deleteMessage(id);
86
+ }}
87
+ style={{ marginTop: 8 }}
88
+ type={'primary'}
89
+ >
90
+ {t('unlock.confirm')}
91
+ </Button>
92
+ <Button
93
+ onClick={() => {
94
+ deleteMessage(id);
95
+ }}
96
+ >
97
+ {t('unlock.closeMessage')}
98
+ </Button>
99
+ </Flexbox>
100
+ </Center>
101
+ </LoadingContext>
97
102
  );
98
103
  });
99
104
 
@@ -1,6 +1,8 @@
1
1
  import isEqual from 'fast-deep-equal';
2
+ import { useContext } from 'react';
2
3
 
3
4
  import { isDeprecatedEdition } from '@/const/version';
5
+ import { LoadingContext } from '@/features/Conversation/Error/APIKeyForm/LoadingContext';
4
6
  import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
5
7
  import { useUserStore } from '@/store/user';
6
8
  import { keyVaultsConfigSelectors } from '@/store/user/selectors';
@@ -11,7 +13,7 @@ export const useApiKey = (provider: string) => {
11
13
  keyVaultsConfigSelectors.getVaultByProvider(provider as any)(s)?.baseURL,
12
14
  s.updateKeyVaultConfig,
13
15
  ]);
14
-
16
+ const { setLoading } = useContext(LoadingContext);
15
17
  const updateAiProviderConfig = useAiInfraStore((s) => s.updateAiProviderConfig);
16
18
  const data = useAiInfraStore(aiProviderSelectors.providerConfigById(provider), isEqual);
17
19
 
@@ -23,12 +25,14 @@ export const useApiKey = (provider: string) => {
23
25
  apiKey: data?.keyVaults.apiKey,
24
26
  baseURL: data?.keyVaults?.baseURL,
25
27
  setConfig: async (id: string, params: Record<string, string>) => {
28
+ const next = { ...data?.keyVaults, ...params };
29
+ if (isEqual(data?.keyVaults, next)) return;
30
+
31
+ setLoading(true);
26
32
  await updateAiProviderConfig(id, {
27
- keyVaults: {
28
- ...data?.keyVaults,
29
- ...params,
30
- },
33
+ keyVaults: { ...data?.keyVaults, ...params },
31
34
  });
35
+ setLoading(false);
32
36
  },
33
37
  };
34
38
  };