diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json
index 57d993b27a..eb64b38c0b 100644
--- a/invokeai/frontend/web/public/locales/en.json
+++ b/invokeai/frontend/web/public/locales/en.json
@@ -1103,6 +1103,7 @@
"info": "Info",
"invoke": {
"addingImagesTo": "Adding images to",
+ "modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your account settings to upgrade.",
"invoke": "Invoke",
"missingFieldTemplate": "Missing field template",
"missingInputForField": "missing input",
@@ -1183,7 +1184,8 @@
"width": "Width",
"gaussianBlur": "Gaussian Blur",
"boxBlur": "Box Blur",
- "staged": "Staged"
+ "staged": "Staged",
+ "modelDisabledForTrial": "Generating with {{modelName}} is not available on trial accounts. Visit your account settings to upgrade."
},
"dynamicPrompts": {
"showDynamicPrompts": "Show Dynamic Prompts",
diff --git a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx
index 5225d908ac..b16c813b56 100644
--- a/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx
+++ b/invokeai/frontend/web/src/app/components/InvokeAIUI.tsx
@@ -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);
diff --git a/invokeai/frontend/web/src/app/store/nanostores/accountSettingsLink.ts b/invokeai/frontend/web/src/app/store/nanostores/accountSettingsLink.ts
new file mode 100644
index 0000000000..cf41facb7c
--- /dev/null
+++ b/invokeai/frontend/web/src/app/store/nanostores/accountSettingsLink.ts
@@ -0,0 +1,3 @@
+import { atom } from 'nanostores';
+
+export const $accountSettingsLink = atom(undefined);
diff --git a/invokeai/frontend/web/src/app/types/invokeai.ts b/invokeai/frontend/web/src/app/types/invokeai.ts
index 24ceb38095..701460e0ff 100644
--- a/invokeai/frontend/web/src/app/types/invokeai.ts
+++ b/invokeai/frontend/web/src/app/types/invokeai.ts
@@ -28,7 +28,8 @@ export type AppFeature =
| 'starterModels'
| 'hfToken'
| 'retryQueueItem'
- | 'cancelAndClearAll';
+ | 'cancelAndClearAll'
+ | 'apiModels';
/**
* A disable-able Stable Diffusion feature
*/
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/DisabledModelWarning.tsx b/invokeai/frontend/web/src/features/parameters/components/MainModel/DisabledModelWarning.tsx
new file mode 100644
index 0000000000..4417a47969
--- /dev/null
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/DisabledModelWarning.tsx
@@ -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 (
+
+
+
+ {t('parameters.invoke.accountSettings')}
+
+ ),
+ }}
+ />
+
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
index ecebcb63a2..404ebfbd95 100644
--- a/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/MainModel/ParamMainModelSelect.tsx
@@ -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 (
-
-
- {t('modelManager.model')}
-
- {isFluxDevSelected && (
-
-
-
-
+ <>
+
+
+
+ {t('modelManager.model')}
- )}
-
-
-
-
-
-
-
-
+ {isFluxDevSelected && (
+
+
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ >
);
};
diff --git a/invokeai/frontend/web/src/features/queue/store/readiness.ts b/invokeai/frontend/web/src/features/queue/store/readiness.ts
index 3ba3b038be..752f0d1506 100644
--- a/invokeai/frontend/web/src/features/queue/store/readiness.ts
+++ b/invokeai/frontend/web/src/features/queue/store/readiness.ts
@@ -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.
diff --git a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
index 2f3063c3ee..092de0eec2 100644
--- a/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
+++ b/invokeai/frontend/web/src/features/settingsAccordions/components/GenerationSettingsAccordion/MainModelPicker.tsx
@@ -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}
>
+
{t('modelManager.model')}