feat(ui): ability to disable generating with API models

This commit is contained in:
Mary Hipp
2025-04-30 14:40:45 -04:00
committed by psychedelicious
parent ff897f74a1
commit 548e766c0b
8 changed files with 112 additions and 29 deletions

View File

@@ -5,6 +5,7 @@ import type { StudioInitAction } from 'app/hooks/useStudioInitAction';
import { $didStudioInit } from 'app/hooks/useStudioInitAction';
import type { LoggingOverrides } from 'app/logging/logger';
import { $loggingOverrides, configureLogging } from 'app/logging/logger';
import { $accountSettingsLink } from 'app/store/nanostores/accountSettingsLink';
import { $authToken } from 'app/store/nanostores/authToken';
import { $baseUrl } from 'app/store/nanostores/baseUrl';
import { $customNavComponent } from 'app/store/nanostores/customNavComponent';
@@ -46,6 +47,7 @@ interface Props extends PropsWithChildren {
token?: string;
config?: PartialAppConfig;
customNavComponent?: ReactNode;
accountSettingsLink?: string;
middleware?: Middleware[];
projectId?: string;
projectName?: string;
@@ -72,6 +74,7 @@ const InvokeAIUI = ({
token,
config,
customNavComponent,
accountSettingsLink,
middleware,
projectId,
projectName,
@@ -175,6 +178,16 @@ const InvokeAIUI = ({
};
}, [customNavComponent]);
useEffect(() => {
if (accountSettingsLink) {
$accountSettingsLink.set(accountSettingsLink);
}
return () => {
$accountSettingsLink.set(undefined);
};
}, [accountSettingsLink]);
useEffect(() => {
if (openAPISchemaUrl) {
$openAPISchemaUrl.set(openAPISchemaUrl);

View File

@@ -0,0 +1,3 @@
import { atom } from 'nanostores';
export const $accountSettingsLink = atom<string | undefined>(undefined);

View File

@@ -28,7 +28,8 @@ export type AppFeature =
| 'starterModels'
| 'hfToken'
| 'retryQueueItem'
| 'cancelAndClearAll';
| 'cancelAndClearAll'
| 'apiModels';
/**
* A disable-able Stable Diffusion feature
*/

View File

@@ -0,0 +1,45 @@
import { Flex, Link, Text } from '@invoke-ai/ui-library';
import { useStore } from '@nanostores/react';
import { $accountSettingsLink } from 'app/store/nanostores/accountSettingsLink';
import { useAppSelector } from 'app/store/storeHooks';
import { selectIsChatGTP4o, selectIsImagen3, selectModel } from 'features/controlLayers/store/paramsSlice';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { useMemo } from 'react';
import { Trans, useTranslation } from 'react-i18next';
export const DisabledModelWarning = () => {
const { t } = useTranslation();
const model = useAppSelector(selectModel);
const isImagen3 = useAppSelector(selectIsImagen3);
const isChatGPT4o = useAppSelector(selectIsChatGTP4o);
const areApiModelsEnabled = useFeatureStatus('apiModels');
const accountSettingsLink = useStore($accountSettingsLink);
const isModelDisabled = useMemo(() => {
return (isImagen3 || isChatGPT4o) && !areApiModelsEnabled;
}, [isImagen3, isChatGPT4o, areApiModelsEnabled]);
if (!isModelDisabled) {
return null;
}
return (
<Flex bg="error.500" borderRadius="base" padding={4} direction="column" fontSize="sm" gap={2}>
<Text>
<Trans
i18nKey="parameters.modelDisabledForTrial"
values={{
modelName: model?.name,
}}
components={{
LinkComponent: (
<Link textDecor="underline" href={accountSettingsLink}>
{t('parameters.invoke.accountSettings')}
</Link>
),
}}
/>
</Text>
</Flex>
);
};

View File

@@ -14,6 +14,8 @@ import { MdMoneyOff } from 'react-icons/md';
import { useMainModels } from 'services/api/hooks/modelsByType';
import { type AnyModelConfig, isCheckpointMainModelConfig, type MainModelConfig } from 'services/api/types';
import { DisabledModelWarning } from './DisabledModelWarning';
const ParamMainModelSelect = () => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
@@ -79,32 +81,35 @@ const ParamMainModelSelect = () => {
}, [selectedModel]);
return (
<FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length} gap={2}>
<InformationalPopover feature="paramModel">
<FormLabel>{t('modelManager.model')}</FormLabel>
</InformationalPopover>
{isFluxDevSelected && (
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
<Flex justifyContent="flex-start">
<Icon as={MdMoneyOff} />
</Flex>
<>
<DisabledModelWarning />
<FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length} gap={2}>
<InformationalPopover feature="paramModel">
<FormLabel>{t('modelManager.model')}</FormLabel>
</InformationalPopover>
)}
<Tooltip label={tooltipLabel}>
<Box w="full" minW={0}>
<Combobox
value={value}
placeholder={placeholder}
options={options}
onChange={onChange}
noOptionsMessage={noOptionsMessage}
isInvalid={value?.isDisabled}
/>
</Box>
</Tooltip>
<NavigateToModelManagerButton />
<UseDefaultSettingsButton />
</FormControl>
{isFluxDevSelected && (
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
<Flex justifyContent="flex-start">
<Icon as={MdMoneyOff} />
</Flex>
</InformationalPopover>
)}
<Tooltip label={tooltipLabel}>
<Box w="full" minW={0}>
<Combobox
value={value}
placeholder={placeholder}
options={options}
onChange={onChange}
noOptionsMessage={noOptionsMessage}
isInvalid={value?.isDisabled}
/>
</Box>
</Tooltip>
<NavigateToModelManagerButton />
<UseDefaultSettingsButton />
</FormControl>
</>
);
};

View File

@@ -34,6 +34,7 @@ import { resolveBatchValue } from 'features/nodes/util/node/resolveBatchValue';
import type { UpscaleState } from 'features/parameters/store/upscaleSlice';
import { selectUpscaleSlice } from 'features/parameters/store/upscaleSlice';
import { getGridSize } from 'features/parameters/util/optimalDimension';
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
import { selectConfigSlice } from 'features/system/store/configSlice';
import { selectActiveTab } from 'features/ui/store/uiSelectors';
import type { TabName } from 'features/ui/store/uiTypes';
@@ -87,7 +88,8 @@ const debouncedUpdateReasons = debounce(
upscale: UpscaleState,
config: AppConfig,
store: AppStore,
isInPublishFlow: boolean
isInPublishFlow: boolean,
areApiModelsEnabled: boolean
) => {
if (tab === 'canvas') {
const model = selectMainModelConfig(store.getState());
@@ -102,6 +104,7 @@ const debouncedUpdateReasons = debounce(
canvasIsRasterizing,
canvasIsCompositing,
canvasIsSelectingObject,
areApiModelsEnabled,
});
$reasonsWhyCannotEnqueue.set(reasons);
} else if (tab === 'workflows') {
@@ -149,6 +152,7 @@ export const useReadinessWatcher = () => {
const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
const isInPublishFlow = useStore($isInPublishFlow);
const areApiModelsEnabled = useFeatureStatus('apiModels');
useEffect(() => {
debouncedUpdateReasons(
@@ -168,7 +172,8 @@ export const useReadinessWatcher = () => {
upscale,
config,
store,
isInPublishFlow
isInPublishFlow,
areApiModelsEnabled
);
}, [
store,
@@ -188,6 +193,7 @@ export const useReadinessWatcher = () => {
upscale,
workflowSettings,
isInPublishFlow,
areApiModelsEnabled,
]);
};
@@ -335,6 +341,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
canvasIsRasterizing: boolean;
canvasIsCompositing: boolean;
canvasIsSelectingObject: boolean;
areApiModelsEnabled: boolean;
}) => {
const {
isConnected,
@@ -347,6 +354,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
canvasIsRasterizing,
canvasIsCompositing,
canvasIsSelectingObject,
areApiModelsEnabled,
} = arg;
const { positivePrompt } = params;
const reasons: Reason[] = [];
@@ -479,6 +487,10 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
}
}
if ((model?.base === 'imagen3' || model?.base === 'chatgpt-4o') && !areApiModelsEnabled) {
reasons.push({ content: i18n.t('parameters.invoke.modelDisabledForTrial', { modelName: model.name }) });
}
const enabledControlLayers = canvas.controlLayers.entities.filter((controlLayer) => controlLayer.isEnabled);
// FLUX only supports 1x Control LoRA at a time.

View File

@@ -27,6 +27,7 @@ import { typedMemo } from 'common/util/typedMemo';
import { $installModelsTab } from 'features/modelManagerV2/subpanels/InstallModels';
import { BASE_COLOR_MAP } from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
import { DisabledModelWarning } from 'features/parameters/components/MainModel/DisabledModelWarning';
import { NavigateToModelManagerButton } from 'features/parameters/components/MainModel/NavigateToModelManagerButton';
import { UseDefaultSettingsButton } from 'features/parameters/components/MainModel/UseDefaultSettingsButton';
import { modelSelected } from 'features/parameters/store/actions';
@@ -200,6 +201,7 @@ export const MainModelPicker = memo(() => {
onClose={onClose}
initialFocusRef={pickerRef.current?.inputRef}
>
<DisabledModelWarning />
<Flex alignItems="center" gap={2}>
<InformationalPopover feature="paramModel">
<FormLabel>{t('modelManager.model')}</FormLabel>