mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(ui): add UI to reset hf token
This commit is contained in:
@@ -12,63 +12,45 @@ import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import { toast } from 'features/toast/toast';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetHFTokenStatusQuery, useSetHFTokenMutation } from 'services/api/endpoints/models';
|
||||
import { UNAUTHORIZED_TOAST_ID } from 'services/events/onModelInstallError';
|
||||
import {
|
||||
useGetHFTokenStatusQuery,
|
||||
useResetHFTokenMutation,
|
||||
useSetHFTokenMutation,
|
||||
} from 'services/api/endpoints/models';
|
||||
import type { Equals } from 'tsafe';
|
||||
import { assert } from 'tsafe';
|
||||
|
||||
export const HFToken = () => {
|
||||
const { t } = useTranslation();
|
||||
const isHFTokenEnabled = useFeatureStatus('hfToken');
|
||||
const [token, setToken] = useState('');
|
||||
const { currentData } = useGetHFTokenStatusQuery(isHFTokenEnabled ? undefined : skipToken);
|
||||
const [trigger, { isLoading, isUninitialized }] = useSetHFTokenMutation();
|
||||
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setToken(e.target.value);
|
||||
}, []);
|
||||
const onClick = useCallback(() => {
|
||||
trigger({ token })
|
||||
.unwrap()
|
||||
.then((res) => {
|
||||
if (res === 'valid') {
|
||||
setToken('');
|
||||
toast({
|
||||
id: UNAUTHORIZED_TOAST_ID,
|
||||
title: t('modelManager.hfTokenSaved'),
|
||||
status: 'success',
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [t, token, trigger]);
|
||||
|
||||
const error = useMemo(() => {
|
||||
if (!currentData || isUninitialized || isLoading) {
|
||||
return null;
|
||||
switch (currentData) {
|
||||
case 'invalid':
|
||||
return t('modelManager.hfTokenInvalidErrorMessage');
|
||||
case 'unknown':
|
||||
return t('modelManager.hfTokenUnableToVerifyErrorMessage');
|
||||
case 'valid':
|
||||
case undefined:
|
||||
return null;
|
||||
default:
|
||||
assert<Equals<never, typeof currentData>>(false, 'Unexpected HF token status');
|
||||
}
|
||||
if (currentData === 'invalid') {
|
||||
return t('modelManager.hfTokenInvalidErrorMessage');
|
||||
}
|
||||
if (currentData === 'unknown') {
|
||||
return t('modelManager.hfTokenUnableToVerifyErrorMessage');
|
||||
}
|
||||
return null;
|
||||
}, [currentData, isLoading, isUninitialized, t]);
|
||||
}, [currentData, t]);
|
||||
|
||||
if (!currentData || currentData === 'valid') {
|
||||
if (!currentData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex borderRadius="base" w="full">
|
||||
<FormControl isInvalid={!isUninitialized && Boolean(error)} orientation="vertical">
|
||||
<FormControl isInvalid={Boolean(error)} orientation="vertical">
|
||||
<FormLabel>{t('modelManager.hfTokenLabel')}</FormLabel>
|
||||
<Flex gap={3} alignItems="center" w="full">
|
||||
<Input type="password" value={token} onChange={onChange} />
|
||||
<Button onClick={onClick} size="sm" isDisabled={token.trim().length === 0} isLoading={isLoading}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
{error && <SetHFTokenInput />}
|
||||
{!error && <ResetHFTokenButton />}
|
||||
<FormHelperText>
|
||||
<ExternalLink label={t('modelManager.hfTokenHelperText')} href="https://huggingface.co/settings/tokens" />
|
||||
</FormHelperText>
|
||||
@@ -77,3 +59,73 @@ export const HFToken = () => {
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const PLACEHOLDER_TOKEN = Array.from({ length: 37 }, () => 'a').join('');
|
||||
|
||||
const ResetHFTokenButton = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const [resetHFToken, { isLoading }] = useResetHFTokenMutation();
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
resetHFToken()
|
||||
.unwrap()
|
||||
.then(() => {
|
||||
toast({
|
||||
title: t('modelManager.hfTokenReset'),
|
||||
status: 'info',
|
||||
});
|
||||
});
|
||||
}, [resetHFToken, t]);
|
||||
|
||||
return (
|
||||
<Flex gap={3} alignItems="center" w="full">
|
||||
<Input type="password" value={PLACEHOLDER_TOKEN} isDisabled />
|
||||
<Button onClick={onClick} size="sm" isLoading={isLoading}>
|
||||
{t('common.reset')}
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
ResetHFTokenButton.displayName = 'ResetHFTokenButton';
|
||||
|
||||
const SetHFTokenInput = memo(() => {
|
||||
const { t } = useTranslation();
|
||||
const [token, setToken] = useState('');
|
||||
const [trigger, { isLoading }] = useSetHFTokenMutation();
|
||||
const onChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
setToken(e.target.value);
|
||||
}, []);
|
||||
const onClick = useCallback(() => {
|
||||
trigger({ token })
|
||||
.unwrap()
|
||||
.then((res) => {
|
||||
switch (res) {
|
||||
case 'valid':
|
||||
setToken('');
|
||||
toast({
|
||||
title: t('modelManager.hfTokenSaved'),
|
||||
status: 'success',
|
||||
});
|
||||
break;
|
||||
case 'invalid':
|
||||
case 'unknown':
|
||||
default:
|
||||
toast({
|
||||
title: t('modelManager.hfTokenUnableToVerify'),
|
||||
status: 'error',
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}, [t, token, trigger]);
|
||||
|
||||
return (
|
||||
<Flex gap={3} alignItems="center" w="full">
|
||||
<Input type="password" value={token} onChange={onChange} />
|
||||
<Button onClick={onClick} size="sm" isDisabled={token.trim().length === 0} isLoading={isLoading}>
|
||||
{t('common.save')}
|
||||
</Button>
|
||||
</Flex>
|
||||
);
|
||||
});
|
||||
SetHFTokenInput.displayName = 'SetHFTokenInput';
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Button, Flex, FormControl, FormErrorMessage, FormHelperText, FormLabel, Input } from '@invoke-ai/ui-library';
|
||||
import { skipToken } from '@reduxjs/toolkit/query';
|
||||
import { useInstallModel } from 'features/modelManagerV2/hooks/useInstallModel';
|
||||
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
|
||||
import type { ChangeEventHandler } from 'react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGetHFTokenStatusQuery, useLazyGetHuggingFaceModelsQuery } from 'services/api/endpoints/models';
|
||||
import { useLazyGetHuggingFaceModelsQuery } from 'services/api/endpoints/models';
|
||||
|
||||
import { HFToken } from './HFToken';
|
||||
import { HuggingFaceResults } from './HuggingFaceResults';
|
||||
@@ -16,7 +15,6 @@ export const HuggingFaceForm = memo(() => {
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const { t } = useTranslation();
|
||||
const isHFTokenEnabled = useFeatureStatus('hfToken');
|
||||
const { currentData } = useGetHFTokenStatusQuery(isHFTokenEnabled ? undefined : skipToken);
|
||||
|
||||
const [_getHuggingFaceModels, { isLoading, data }] = useLazyGetHuggingFaceModelsQuery();
|
||||
const [installModel] = useInstallModel();
|
||||
@@ -68,7 +66,7 @@ export const HuggingFaceForm = memo(() => {
|
||||
<FormHelperText>{t('modelManager.huggingFaceHelper')}</FormHelperText>
|
||||
{!!errorMessage.length && <FormErrorMessage>{errorMessage}</FormErrorMessage>}
|
||||
</FormControl>
|
||||
{currentData !== 'valid' && <HFToken />}
|
||||
{isHFTokenEnabled && <HFToken />}
|
||||
{data && data.urls && displayResults && <HuggingFaceResults results={data.urls} />}
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -4,13 +4,14 @@ import { getSelectorsOptions } from 'app/store/createMemoizedSelector';
|
||||
import { selectParamsSlice } from 'features/controlLayers/store/paramsSlice';
|
||||
import queryString from 'query-string';
|
||||
import type { operations, paths } from 'services/api/schema';
|
||||
import {
|
||||
type AnyModelConfig,
|
||||
type GetHFTokenStatusResponse,
|
||||
isNonRefinerMainModelConfig,
|
||||
type SetHFTokenArg,
|
||||
type SetHFTokenResponse,
|
||||
import type {
|
||||
AnyModelConfig,
|
||||
GetHFTokenStatusResponse,
|
||||
ResetHFTokenResponse,
|
||||
SetHFTokenArg,
|
||||
SetHFTokenResponse,
|
||||
} from 'services/api/types';
|
||||
import { isNonRefinerMainModelConfig } from 'services/api/types';
|
||||
import type { Param0 } from 'tsafe';
|
||||
|
||||
import type { ApiTagDescription } from '..';
|
||||
@@ -293,6 +294,10 @@ export const modelsApi = api.injectEndpoints({
|
||||
}
|
||||
},
|
||||
}),
|
||||
resetHFToken: build.mutation<ResetHFTokenResponse, void>({
|
||||
query: () => ({ url: buildModelsUrl('hf_login'), method: 'DELETE' }),
|
||||
invalidatesTags: ['HFTokenStatus'],
|
||||
}),
|
||||
emptyModelCache: build.mutation<void, void>({
|
||||
query: () => ({ url: buildModelsUrl('empty_model_cache'), method: 'POST' }),
|
||||
}),
|
||||
@@ -316,6 +321,7 @@ export const {
|
||||
useGetStarterModelsQuery,
|
||||
useGetHFTokenStatusQuery,
|
||||
useSetHFTokenMutation,
|
||||
useResetHFTokenMutation,
|
||||
useEmptyModelCacheMutation,
|
||||
} = modelsApi;
|
||||
|
||||
|
||||
@@ -321,6 +321,9 @@ export type GetHFTokenStatusResponse =
|
||||
export type SetHFTokenResponse = NonNullable<
|
||||
paths['/api/v2/models/hf_login']['post']['responses']['200']['content']['application/json']
|
||||
>;
|
||||
export type ResetHFTokenResponse = NonNullable<
|
||||
paths['/api/v2/models/hf_login']['delete']['responses']['200']['content']['application/json']
|
||||
>;
|
||||
export type SetHFTokenArg = NonNullable<
|
||||
paths['/api/v2/models/hf_login']['post']['requestBody']['content']['application/json']
|
||||
>;
|
||||
|
||||
Reference in New Issue
Block a user