Skip to content

Commit 754c6ca

Browse files
authored
Merge pull request #798 from salsina/fix/hftoken-edit
Added option to update Hugging Face credentials in UI
2 parents 968c104 + 0a3e42f commit 754c6ca

File tree

3 files changed

+235
-4
lines changed

3 files changed

+235
-4
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import { Button } from '@mui/joy';
2+
import React from 'react';
3+
4+
const EditTokenModal = ({
5+
open,
6+
onClose,
7+
name,
8+
token,
9+
onSave,
10+
}: {
11+
open: boolean;
12+
onClose: () => void;
13+
name: string;
14+
token: string;
15+
onSave: (token: string) => void;
16+
}) => {
17+
const [newToken, setNewToken] = React.useState(token);
18+
19+
// Reset state when modal opens or token changes
20+
React.useEffect(() => {
21+
if (open) {
22+
setNewToken(token || '');
23+
}
24+
}, [open, token]);
25+
26+
if (!open) return null;
27+
28+
const hasChanged = (newToken || '').trim() !== (token || '').trim();
29+
30+
return (
31+
<div
32+
role="button"
33+
tabIndex={0}
34+
onClick={onClose}
35+
onKeyDown={(e) => {
36+
if (e.key === 'Enter' || e.key === ' ') {
37+
onClose();
38+
}
39+
}}
40+
style={{
41+
position: 'fixed',
42+
top: 0,
43+
left: 0,
44+
width: '100vw',
45+
height: '100vh',
46+
backgroundColor: 'rgba(0, 0, 0, 0.4)',
47+
display: 'flex',
48+
alignItems: 'center',
49+
justifyContent: 'center',
50+
zIndex: 1000,
51+
}}
52+
>
53+
<div
54+
role="presentation"
55+
onClick={(e) => e.stopPropagation()}
56+
style={{
57+
backgroundColor: 'white',
58+
padding: '24px',
59+
borderRadius: '12px',
60+
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
61+
width: '90%',
62+
maxWidth: '400px',
63+
position: 'relative',
64+
}}
65+
>
66+
<h3 style={{ marginTop: 0 }}>Edit {name} Token</h3>
67+
68+
<input
69+
type="text"
70+
placeholder="Enter new token"
71+
style={{
72+
width: '100%',
73+
padding: '8px',
74+
borderRadius: '6px',
75+
border: '1px solid #ccc',
76+
marginBottom: '12px',
77+
}}
78+
value={newToken}
79+
onChange={(e) => setNewToken(e.target.value)}
80+
/>
81+
82+
<div
83+
style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px' }}
84+
>
85+
<Button
86+
onClick={onClose}
87+
style={{
88+
background: '#ccc',
89+
border: 'none',
90+
borderRadius: '6px',
91+
padding: '6px 12px',
92+
cursor: 'pointer',
93+
}}
94+
>
95+
Cancel
96+
</Button>
97+
98+
<Button
99+
disabled={!hasChanged}
100+
onClick={() => onSave(newToken.trim())}
101+
style={{
102+
background: hasChanged ? '#0d6efd' : '#a0c4ff',
103+
color: 'white',
104+
border: 'none',
105+
borderRadius: '6px',
106+
padding: '6px 12px',
107+
cursor: hasChanged ? 'pointer' : '',
108+
transition: 'background 0.2s ease',
109+
}}
110+
>
111+
Save
112+
</Button>
113+
</div>
114+
</div>
115+
</div>
116+
);
117+
};
118+
119+
export default EditTokenModal;

src/renderer/components/Settings/TransformerLabSettings.tsx

Lines changed: 115 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import useSWR from 'swr';
2626
import { EyeIcon, EyeOffIcon, RotateCcwIcon } from 'lucide-react';
2727

2828
import AIProvidersSettings from './AIProvidersSettings';
29+
import EditTokenModal from './EditTokenModal';
2930
import ViewJobsTab from './ViewJobsTab';
3031
import { alignBox } from '@nivo/core';
3132
import {
@@ -37,6 +38,10 @@ import {
3738
export default function TransformerLabSettings() {
3839
const [showPassword, setShowPassword] = React.useState(false);
3940
const [doNotTrack, setDoNotTrack] = React.useState(false);
41+
const [showHuggingfaceEditTokenModal, setShowHuggingfaceEditTokenModal] =
42+
React.useState(false);
43+
const [showWandbEditTokenModal, setShowWandbEditTokenModal] =
44+
React.useState(false);
4045
const [showExperimentalPlugins, setShowExperimentalPlugins] =
4146
React.useState(false);
4247

@@ -76,6 +81,14 @@ export default function TransformerLabSettings() {
7681
} = useAPI('config', ['get'], {
7782
key: 'HuggingfaceUserAccessToken',
7883
});
84+
const {
85+
data: wandbToken,
86+
error: wandbTokenError,
87+
isLoading: wandbTokenIsLoading,
88+
mutate: wandbTokenMutate,
89+
} = useAPI('config', ['get'], {
90+
key: 'WANDB_API_KEY',
91+
});
7992
const [showJobsOfType, setShowJobsOfType] = React.useState('NONE');
8093
const [showProvidersPage, setShowProvidersPage] = React.useState(false);
8194

@@ -150,7 +163,60 @@ export default function TransformerLabSettings() {
150163
Huggingface Credentials:
151164
</Typography>
152165
{canLogInToHuggingFace?.message === 'OK' ? (
153-
<Alert color="success">Login to Huggingface Successful</Alert>
166+
<div>
167+
<div style={{ position: 'relative', width: '100%' }}>
168+
<Alert color="success" style={{ width: '100%', margin: 0 }}>
169+
Login to Huggingface Successful
170+
</Alert>
171+
<p
172+
style={{
173+
position: 'absolute',
174+
right: '16px',
175+
top: '50%',
176+
transform: 'translateY(-50%)',
177+
margin: 0,
178+
cursor: 'pointer',
179+
fontSize: '14px',
180+
borderBottom: '1px solid',
181+
}}
182+
onClick={() => {
183+
setShowHuggingfaceEditTokenModal(
184+
!showHuggingfaceEditTokenModal,
185+
);
186+
}}
187+
>
188+
Edit
189+
</p>
190+
</div>
191+
<div>
192+
{showHuggingfaceEditTokenModal && (
193+
<EditTokenModal
194+
open={showHuggingfaceEditTokenModal}
195+
onClose={() => setShowHuggingfaceEditTokenModal(false)}
196+
name="Huggingface"
197+
token={hftoken}
198+
onSave={async (token) => {
199+
await chatAPI.authenticatedFetch(
200+
chatAPI.Endpoints.Models.HuggingFaceLogout(),
201+
);
202+
await chatAPI.authenticatedFetch(
203+
getAPIFullPath('config', ['set'], {
204+
key: 'HuggingfaceUserAccessToken',
205+
value: token,
206+
}),
207+
);
208+
// Now manually log in to Huggingface
209+
await chatAPI.authenticatedFetch(
210+
chatAPI.Endpoints.Models.HuggingFaceLogin(),
211+
);
212+
hftokenmutate(token);
213+
canLogInToHuggingFaceMutate();
214+
setShowHuggingfaceEditTokenModal(false);
215+
}}
216+
/>
217+
)}
218+
</div>
219+
</div>
154220
) : (
155221
<>
156222
<Alert color="danger" sx={{ mb: 1 }}>
@@ -217,9 +283,54 @@ export default function TransformerLabSettings() {
217283
</>
218284
)}
219285
{wandbLoginStatus?.message === 'OK' ? (
220-
<Alert color="success">
221-
Login to Weights &amp; Biases Successful
222-
</Alert>
286+
<div>
287+
<div style={{ position: 'relative', width: '100%' }}>
288+
<Alert color="success" style={{ width: '100%', margin: 0 }}>
289+
Login to Weights &amp; Biases Successful
290+
</Alert>
291+
<p
292+
style={{
293+
position: 'absolute',
294+
right: '16px',
295+
top: '50%',
296+
transform: 'translateY(-50%)',
297+
margin: 0,
298+
cursor: 'pointer',
299+
fontSize: '14px',
300+
borderBottom: '1px solid',
301+
}}
302+
onClick={() => {
303+
setShowWandbEditTokenModal(!showWandbEditTokenModal);
304+
}}
305+
>
306+
Edit
307+
</p>
308+
</div>
309+
310+
<div>
311+
{showWandbEditTokenModal && (
312+
<EditTokenModal
313+
open={showWandbEditTokenModal}
314+
onClose={() => setShowWandbEditTokenModal(false)}
315+
name="Weights &amp; Biases"
316+
token={wandbToken || ''}
317+
onSave={async (token) => {
318+
await chatAPI.authenticatedFetch(
319+
getAPIFullPath('config', ['set'], {
320+
key: 'WANDB_API_KEY',
321+
value: token,
322+
}),
323+
);
324+
await chatAPI.authenticatedFetch(
325+
chatAPI.Endpoints.Models.wandbLogin(),
326+
);
327+
wandbLoginMutate();
328+
setShowWandbEditTokenModal(false);
329+
}}
330+
/>
331+
)}
332+
</div>
333+
</div>
223334
) : (
224335
<FormControl sx={{ maxWidth: '500px', mt: 2 }}>
225336
<FormLabel>Weights &amp; Biases API Key</FormLabel>

src/renderer/lib/api-client/endpoints.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ Endpoints.Models = {
160160
ImportFromLocalPath: (modelPath: string) =>
161161
`${API_URL()}model/import_from_local_path?model_path=${modelPath}`,
162162
HuggingFaceLogin: () => `${API_URL()}model/login_to_huggingface`,
163+
HuggingFaceLogout: () => `${API_URL()}model/logout_from_huggingface`,
163164
Delete: (modelId: string, deleteCache: boolean = false) =>
164165
`${API_URL()}model/delete?model_id=${modelId}&delete_from_cache=${
165166
deleteCache

0 commit comments

Comments
 (0)