Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions invokeai/frontend/web/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,7 @@
"parsingFailed": "Parsing Failed",
"positivePrompt": "Positive Prompt",
"qwen3Encoder": "Qwen3 Encoder",
"qwen3Source": "Qwen3 Source",
"recallParameters": "Recall Parameters",
"recallParameter": "Recall {{label}}",
"scheduler": "Scheduler",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ import { modelSelected } from 'features/parameters/store/actions';
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
import { toast } from 'features/toast/toast';
import { t } from 'i18next';
import { selectGlobalRefImageModels, selectRegionalRefImageModels } from 'services/api/hooks/modelsByType';
import {
selectFluxVAEModels,
selectGlobalRefImageModels,
selectQwen3EncoderModels,
selectRegionalRefImageModels,
selectZImageDiffusersModels,
} from 'services/api/hooks/modelsByType';
import type { FLUXKontextModelConfig, FLUXReduxModelConfig, IPAdapterModelConfig } from 'services/api/types';
import { isFluxKontextModelConfig, isFluxReduxModelConfig } from 'services/api/types';

Expand Down Expand Up @@ -79,6 +85,59 @@ export const addModelSelectedListener = (startAppListening: AppStartListening) =
dispatch(zImageQwen3SourceModelSelected(null));
modelsUpdatedDisabledOrCleared += 1;
}
} else {
// Switching to Z-Image - set defaults if no valid configuration exists
const hasValidConfig = zImageQwen3SourceModel || (zImageVaeModel && zImageQwen3EncoderModel);

if (!hasValidConfig) {
// Prefer Qwen3 Source (Diffusers model) if available
const availableZImageDiffusers = selectZImageDiffusersModels(state);

if (availableZImageDiffusers.length > 0) {
const diffusersModel = availableZImageDiffusers[0];
if (diffusersModel) {
dispatch(
zImageQwen3SourceModelSelected({
key: diffusersModel.key,
hash: diffusersModel.hash,
name: diffusersModel.name,
base: diffusersModel.base,
type: diffusersModel.type,
})
);
}
} else {
// Fallback: try to set Qwen3 Encoder + VAE
const availableQwen3Encoders = selectQwen3EncoderModels(state);
const availableFluxVAEs = selectFluxVAEModels(state);

if (availableQwen3Encoders.length > 0 && availableFluxVAEs.length > 0) {
const qwen3Encoder = availableQwen3Encoders[0];
const fluxVAE = availableFluxVAEs[0];

if (qwen3Encoder) {
dispatch(
zImageQwen3EncoderModelSelected({
key: qwen3Encoder.key,
name: qwen3Encoder.name,
base: qwen3Encoder.base,
})
);
}
if (fluxVAE) {
dispatch(
zImageVaeModelSelected({
key: fluxVAE.key,
hash: fluxVAE.hash,
name: fluxVAE.name,
base: fluxVAE.base,
type: fluxVAE.type,
})
);
}
}
}
}
}

if (SUPPORTS_REF_IMAGES_BASE_MODELS.includes(newModel.base)) {
Expand Down
26 changes: 26 additions & 0 deletions invokeai/frontend/web/src/features/metadata/parsing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
vaeSelected,
widthChanged,
zImageQwen3EncoderModelSelected,
zImageQwen3SourceModelSelected,
zImageVaeModelSelected,
} from 'features/controlLayers/store/paramsSlice';
import { refImagesRecalled } from 'features/controlLayers/store/refImagesSlice';
Expand Down Expand Up @@ -741,6 +742,30 @@ const ZImageVAEModel: SingleMetadataHandler<ModelIdentifierField> = {
};
//#endregion ZImageVAEModel

//#region ZImageQwen3SourceModel
const ZImageQwen3SourceModel: SingleMetadataHandler<ModelIdentifierField> = {
[SingleMetadataKey]: true,
type: 'ZImageQwen3SourceModel',
parse: async (metadata, store) => {
const raw = getProperty(metadata, 'qwen3_source');
const parsed = await parseModelIdentifier(raw, store, 'main');
assert(parsed.type === 'main');
// Only recall if the current main model is Z-Image
const base = selectBase(store.getState());
assert(base === 'z-image', 'ZImageQwen3SourceModel handler only works with Z-Image models');
return Promise.resolve(parsed);
},
recall: (value, store) => {
store.dispatch(zImageQwen3SourceModelSelected(value));
},
i18nKey: 'metadata.qwen3Source',
LabelComponent: MetadataLabel,
ValueComponent: ({ value }: SingleMetadataValueProps<ModelIdentifierField>) => (
<MetadataPrimitiveValue value={`${value.name} (${value.base.toUpperCase()})`} />
),
};
//#endregion ZImageQwen3SourceModel

//#region LoRAs
const LoRAs: CollectionMetadataHandler<LoRA[]> = {
[CollectionMetadataKey]: true,
Expand Down Expand Up @@ -977,6 +1002,7 @@ export const ImageMetadataHandlers = {
VAEModel,
Qwen3EncoderModel,
ZImageVAEModel,
ZImageQwen3SourceModel,
LoRAs,
CanvasLayers,
RefImages,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ export const buildZImageGraph = async (arg: GraphBuilderArg): Promise<GraphBuild
steps,
vae: zImageVaeModel ?? undefined,
qwen3_encoder: zImageQwen3EncoderModel ?? undefined,
qwen3_source: zImageQwen3SourceModel ?? undefined,
});
g.addEdgeToMetadata(seed, 'value', 'seed');
g.addEdgeToMetadata(positivePrompt, 'value', 'positive_prompt');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@ import type { MainModelConfig, Qwen3EncoderModelConfig, VAEModelConfig } from 's

/**
* Z-Image VAE Model Select - uses FLUX VAE models
* Disabled when Qwen3 Source is selected (mutually exclusive)
*/
const ParamZImageVaeModelSelect = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const zImageVaeModel = useAppSelector(selectZImageVaeModel);
const zImageQwen3SourceModel = useAppSelector(selectZImageQwen3SourceModel);
const [modelConfigs, { isLoading }] = useFluxVAEModels();

// Disable when Qwen3 Source is selected
const isDisabled = zImageQwen3SourceModel !== null;

const _onChange = useCallback(
(model: VAEModelConfig | null) => {
if (model) {
Expand All @@ -43,14 +48,15 @@ const ParamZImageVaeModelSelect = memo(() => {
});

return (
<FormControl minW={0} flexGrow={1} gap={2}>
<FormControl minW={0} flexGrow={1} gap={2} isDisabled={isDisabled}>
<FormLabel m={0}>{t('modelManager.zImageVae')}</FormLabel>
<Combobox
value={value}
options={options}
onChange={onChange}
noOptionsMessage={noOptionsMessage}
isClearable
isDisabled={isDisabled}
placeholder={t('modelManager.zImageVaePlaceholder')}
/>
</FormControl>
Expand All @@ -61,13 +67,18 @@ ParamZImageVaeModelSelect.displayName = 'ParamZImageVaeModelSelect';

/**
* Z-Image Qwen3 Encoder Model Select
* Disabled when Qwen3 Source is selected (mutually exclusive)
*/
const ParamZImageQwen3EncoderModelSelect = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const zImageQwen3EncoderModel = useAppSelector(selectZImageQwen3EncoderModel);
const zImageQwen3SourceModel = useAppSelector(selectZImageQwen3SourceModel);
const [modelConfigs, { isLoading }] = useQwen3EncoderModels();

// Disable when Qwen3 Source is selected
const isDisabled = zImageQwen3SourceModel !== null;

const _onChange = useCallback(
(model: Qwen3EncoderModelConfig | null) => {
if (model) {
Expand All @@ -87,14 +98,15 @@ const ParamZImageQwen3EncoderModelSelect = memo(() => {
});

return (
<FormControl minW={0} flexGrow={1} gap={2}>
<FormControl minW={0} flexGrow={1} gap={2} isDisabled={isDisabled}>
<FormLabel m={0}>{t('modelManager.zImageQwen3Encoder')}</FormLabel>
<Combobox
value={value}
options={options}
onChange={onChange}
noOptionsMessage={noOptionsMessage}
isClearable
isDisabled={isDisabled}
placeholder={t('modelManager.zImageQwen3EncoderPlaceholder')}
/>
</FormControl>
Expand All @@ -105,13 +117,19 @@ ParamZImageQwen3EncoderModelSelect.displayName = 'ParamZImageQwen3EncoderModelSe

/**
* Z-Image Qwen3 Source Model Select - Diffusers Z-Image models for fallback
* Disabled when VAE or Qwen3 Encoder is selected (mutually exclusive)
*/
const ParamZImageQwen3SourceModelSelect = memo(() => {
const dispatch = useAppDispatch();
const { t } = useTranslation();
const zImageQwen3SourceModel = useAppSelector(selectZImageQwen3SourceModel);
const zImageVaeModel = useAppSelector(selectZImageVaeModel);
const zImageQwen3EncoderModel = useAppSelector(selectZImageQwen3EncoderModel);
const [modelConfigs, { isLoading }] = useZImageDiffusersModels();

// Disable when VAE or Qwen3 Encoder is selected
const isDisabled = zImageVaeModel !== null || zImageQwen3EncoderModel !== null;

const _onChange = useCallback(
(model: MainModelConfig | null) => {
if (model) {
Expand All @@ -131,14 +149,15 @@ const ParamZImageQwen3SourceModelSelect = memo(() => {
});

return (
<FormControl minW={0} flexGrow={1} gap={2}>
<FormControl minW={0} flexGrow={1} gap={2} isDisabled={isDisabled}>
<FormLabel m={0}>{t('modelManager.zImageQwen3Source')}</FormLabel>
<Combobox
value={value}
options={options}
onChange={onChange}
noOptionsMessage={noOptionsMessage}
isClearable
isDisabled={isDisabled}
placeholder={t('modelManager.zImageQwen3SourcePlaceholder')}
/>
</FormControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,6 @@ export const selectGlobalRefImageModels = buildModelsSelector(
export const selectRegionalRefImageModels = buildModelsSelector(
(config) => isIPAdapterModelConfig(config) || isFluxReduxModelConfig(config)
);
export const selectQwen3EncoderModels = buildModelsSelector(isQwen3EncoderModelConfig);
export const selectZImageDiffusersModels = buildModelsSelector(isZImageDiffusersMainModelConfig);
export const selectFluxVAEModels = buildModelsSelector(isFluxVAEModelConfig);