Skip to content

Commit 2fc9a67

Browse files
Mary Hipppsychedelicious
Mary Hipp
authored andcommitted
feat(ui): ability to disable generating with API models
1 parent ff897f7 commit 2fc9a67

File tree

8 files changed

+112
-29
lines changed

8 files changed

+112
-29
lines changed

invokeai/frontend/web/public/locales/en.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,7 @@
11031103
"info": "Info",
11041104
"invoke": {
11051105
"addingImagesTo": "Adding images to",
1106+
"modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your account settings to upgrade.",
11061107
"invoke": "Invoke",
11071108
"missingFieldTemplate": "Missing field template",
11081109
"missingInputForField": "missing input",
@@ -1183,7 +1184,8 @@
11831184
"width": "Width",
11841185
"gaussianBlur": "Gaussian Blur",
11851186
"boxBlur": "Box Blur",
1186-
"staged": "Staged"
1187+
"staged": "Staged",
1188+
"modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your <LinkComponent>account settings</LinkComponent> to upgrade."
11871189
},
11881190
"dynamicPrompts": {
11891191
"showDynamicPrompts": "Show Dynamic Prompts",

invokeai/frontend/web/src/app/components/InvokeAIUI.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { StudioInitAction } from 'app/hooks/useStudioInitAction';
55
import { $didStudioInit } from 'app/hooks/useStudioInitAction';
66
import type { LoggingOverrides } from 'app/logging/logger';
77
import { $loggingOverrides, configureLogging } from 'app/logging/logger';
8+
import { $accountSettingsLink } from 'app/store/nanostores/accountSettingsLink';
89
import { $authToken } from 'app/store/nanostores/authToken';
910
import { $baseUrl } from 'app/store/nanostores/baseUrl';
1011
import { $customNavComponent } from 'app/store/nanostores/customNavComponent';
@@ -46,6 +47,7 @@ interface Props extends PropsWithChildren {
4647
token?: string;
4748
config?: PartialAppConfig;
4849
customNavComponent?: ReactNode;
50+
accountSettingsLink?: string;
4951
middleware?: Middleware[];
5052
projectId?: string;
5153
projectName?: string;
@@ -72,6 +74,7 @@ const InvokeAIUI = ({
7274
token,
7375
config,
7476
customNavComponent,
77+
accountSettingsLink,
7578
middleware,
7679
projectId,
7780
projectName,
@@ -175,6 +178,16 @@ const InvokeAIUI = ({
175178
};
176179
}, [customNavComponent]);
177180

181+
useEffect(() => {
182+
if (accountSettingsLink) {
183+
$accountSettingsLink.set(accountSettingsLink);
184+
}
185+
186+
return () => {
187+
$accountSettingsLink.set(undefined);
188+
};
189+
}, [accountSettingsLink]);
190+
178191
useEffect(() => {
179192
if (openAPISchemaUrl) {
180193
$openAPISchemaUrl.set(openAPISchemaUrl);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { atom } from 'nanostores';
2+
3+
export const $accountSettingsLink = atom<string | undefined>(undefined);

invokeai/frontend/web/src/app/types/invokeai.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ export type AppFeature =
2828
| 'starterModels'
2929
| 'hfToken'
3030
| 'retryQueueItem'
31-
| 'cancelAndClearAll';
31+
| 'cancelAndClearAll'
32+
| 'apiModels';
3233
/**
3334
* A disable-able Stable Diffusion feature
3435
*/
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { Flex, Link, Text } from '@invoke-ai/ui-library';
2+
import { useStore } from '@nanostores/react';
3+
import { $accountSettingsLink } from 'app/store/nanostores/accountSettingsLink';
4+
import { useAppSelector } from 'app/store/storeHooks';
5+
import { selectIsChatGTP4o, selectIsImagen3, selectModel } from 'features/controlLayers/store/paramsSlice';
6+
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
7+
import { useMemo } from 'react';
8+
import { Trans, useTranslation } from 'react-i18next';
9+
10+
export const DisabledModelWarning = () => {
11+
const { t } = useTranslation();
12+
const model = useAppSelector(selectModel);
13+
const isImagen3 = useAppSelector(selectIsImagen3);
14+
const isChatGPT4o = useAppSelector(selectIsChatGTP4o);
15+
const areApiModelsEnabled = useFeatureStatus('apiModels');
16+
const accountSettingsLink = useStore($accountSettingsLink);
17+
18+
const isModelDisabled = useMemo(() => {
19+
return (isImagen3 || isChatGPT4o) && !areApiModelsEnabled;
20+
}, [isImagen3, isChatGPT4o, areApiModelsEnabled]);
21+
22+
if (!isModelDisabled) {
23+
return null;
24+
}
25+
26+
return (
27+
<Flex bg="error.500" borderRadius="base" padding={4} direction="column" fontSize="sm" gap={2}>
28+
<Text>
29+
<Trans
30+
i18nKey="parameters.modelDisabledForTrial"
31+
values={{
32+
modelName: model?.name,
33+
}}
34+
components={{
35+
LinkComponent: (
36+
<Link textDecor="underline" href={accountSettingsLink}>
37+
{t('parameters.invoke.accountSettings')}
38+
</Link>
39+
),
40+
}}
41+
/>
42+
</Text>
43+
</Flex>
44+
);
45+
};

invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { MdMoneyOff } from 'react-icons/md';
1414
import { useMainModels } from 'services/api/hooks/modelsByType';
1515
import { type AnyModelConfig, isCheckpointMainModelConfig, type MainModelConfig } from 'services/api/types';
1616

17+
import { DisabledModelWarning } from './DisabledModelWarning';
18+
1719
const ParamMainModelSelect = () => {
1820
const dispatch = useAppDispatch();
1921
const { t } = useTranslation();
@@ -79,32 +81,35 @@ const ParamMainModelSelect = () => {
7981
}, [selectedModel]);
8082

8183
return (
82-
<FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length} gap={2}>
83-
<InformationalPopover feature="paramModel">
84-
<FormLabel>{t('modelManager.model')}</FormLabel>
85-
</InformationalPopover>
86-
{isFluxDevSelected && (
87-
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
88-
<Flex justifyContent="flex-start">
89-
<Icon as={MdMoneyOff} />
90-
</Flex>
84+
<>
85+
<DisabledModelWarning />
86+
<FormControl isDisabled={!modelConfigs.length} isInvalid={!value || !modelConfigs.length} gap={2}>
87+
<InformationalPopover feature="paramModel">
88+
<FormLabel>{t('modelManager.model')}</FormLabel>
9189
</InformationalPopover>
92-
)}
93-
<Tooltip label={tooltipLabel}>
94-
<Box w="full" minW={0}>
95-
<Combobox
96-
value={value}
97-
placeholder={placeholder}
98-
options={options}
99-
onChange={onChange}
100-
noOptionsMessage={noOptionsMessage}
101-
isInvalid={value?.isDisabled}
102-
/>
103-
</Box>
104-
</Tooltip>
105-
<NavigateToModelManagerButton />
106-
<UseDefaultSettingsButton />
107-
</FormControl>
90+
{isFluxDevSelected && (
91+
<InformationalPopover feature="fluxDevLicense" hideDisable={true}>
92+
<Flex justifyContent="flex-start">
93+
<Icon as={MdMoneyOff} />
94+
</Flex>
95+
</InformationalPopover>
96+
)}
97+
<Tooltip label={tooltipLabel}>
98+
<Box w="full" minW={0}>
99+
<Combobox
100+
value={value}
101+
placeholder={placeholder}
102+
options={options}
103+
onChange={onChange}
104+
noOptionsMessage={noOptionsMessage}
105+
isInvalid={value?.isDisabled}
106+
/>
107+
</Box>
108+
</Tooltip>
109+
<NavigateToModelManagerButton />
110+
<UseDefaultSettingsButton />
111+
</FormControl>
112+
</>
108113
);
109114
};
110115

invokeai/frontend/web/src/features/queue/store/readiness.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { resolveBatchValue } from 'features/nodes/util/node/resolveBatchValue';
3434
import type { UpscaleState } from 'features/parameters/store/upscaleSlice';
3535
import { selectUpscaleSlice } from 'features/parameters/store/upscaleSlice';
3636
import { getGridSize } from 'features/parameters/util/optimalDimension';
37+
import { useFeatureStatus } from 'features/system/hooks/useFeatureStatus';
3738
import { selectConfigSlice } from 'features/system/store/configSlice';
3839
import { selectActiveTab } from 'features/ui/store/uiSelectors';
3940
import type { TabName } from 'features/ui/store/uiTypes';
@@ -87,7 +88,8 @@ const debouncedUpdateReasons = debounce(
8788
upscale: UpscaleState,
8889
config: AppConfig,
8990
store: AppStore,
90-
isInPublishFlow: boolean
91+
isInPublishFlow: boolean,
92+
areApiModelsEnabled: boolean
9193
) => {
9294
if (tab === 'canvas') {
9395
const model = selectMainModelConfig(store.getState());
@@ -102,6 +104,7 @@ const debouncedUpdateReasons = debounce(
102104
canvasIsRasterizing,
103105
canvasIsCompositing,
104106
canvasIsSelectingObject,
107+
areApiModelsEnabled,
105108
});
106109
$reasonsWhyCannotEnqueue.set(reasons);
107110
} else if (tab === 'workflows') {
@@ -149,6 +152,7 @@ export const useReadinessWatcher = () => {
149152
const canvasIsSelectingObject = useStore(canvasManager?.stateApi.$isSegmenting ?? $true);
150153
const canvasIsCompositing = useStore(canvasManager?.compositor.$isBusy ?? $true);
151154
const isInPublishFlow = useStore($isInPublishFlow);
155+
const areApiModelsEnabled = useFeatureStatus('apiModels');
152156

153157
useEffect(() => {
154158
debouncedUpdateReasons(
@@ -168,7 +172,8 @@ export const useReadinessWatcher = () => {
168172
upscale,
169173
config,
170174
store,
171-
isInPublishFlow
175+
isInPublishFlow,
176+
areApiModelsEnabled
172177
);
173178
}, [
174179
store,
@@ -188,6 +193,7 @@ export const useReadinessWatcher = () => {
188193
upscale,
189194
workflowSettings,
190195
isInPublishFlow,
196+
areApiModelsEnabled,
191197
]);
192198
};
193199

@@ -335,6 +341,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
335341
canvasIsRasterizing: boolean;
336342
canvasIsCompositing: boolean;
337343
canvasIsSelectingObject: boolean;
344+
areApiModelsEnabled: boolean;
338345
}) => {
339346
const {
340347
isConnected,
@@ -347,6 +354,7 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
347354
canvasIsRasterizing,
348355
canvasIsCompositing,
349356
canvasIsSelectingObject,
357+
areApiModelsEnabled,
350358
} = arg;
351359
const { positivePrompt } = params;
352360
const reasons: Reason[] = [];
@@ -479,6 +487,10 @@ const getReasonsWhyCannotEnqueueCanvasTab = (arg: {
479487
}
480488
}
481489

490+
if ((model?.base === 'imagen3' || model?.base === 'chatgpt-4o') && !areApiModelsEnabled) {
491+
reasons.push({ content: i18n.t('parameters.invoke.modelDisabledForTrial', { modelName: model.name }) });
492+
}
493+
482494
const enabledControlLayers = canvas.controlLayers.entities.filter((controlLayer) => controlLayer.isEnabled);
483495

484496
// FLUX only supports 1x Control LoRA at a time.

invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { typedMemo } from 'common/util/typedMemo';
2727
import { $installModelsTab } from 'features/modelManagerV2/subpanels/InstallModels';
2828
import { BASE_COLOR_MAP } from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelBaseBadge';
2929
import ModelImage from 'features/modelManagerV2/subpanels/ModelManagerPanel/ModelImage';
30+
import { DisabledModelWarning } from 'features/parameters/components/MainModel/DisabledModelWarning';
3031
import { NavigateToModelManagerButton } from 'features/parameters/components/MainModel/NavigateToModelManagerButton';
3132
import { UseDefaultSettingsButton } from 'features/parameters/components/MainModel/UseDefaultSettingsButton';
3233
import { modelSelected } from 'features/parameters/store/actions';
@@ -200,6 +201,7 @@ export const MainModelPicker = memo(() => {
200201
onClose={onClose}
201202
initialFocusRef={pickerRef.current?.inputRef}
202203
>
204+
<DisabledModelWarning />
203205
<Flex alignItems="center" gap={2}>
204206
<InformationalPopover feature="paramModel">
205207
<FormLabel>{t('modelManager.model')}</FormLabel>

0 commit comments

Comments
 (0)