diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
index 5cc429ebcd..d6cb70f4e8 100644
--- a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelManagerPanel/ModelListItem.tsx
@@ -3,10 +3,12 @@ import {
Button,
ConfirmationAlertDialog,
Flex,
+ Icon,
IconButton,
Text,
Tooltip,
useDisclosure,
+ Box,
} from '@invoke-ai/ui-library';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { setSelectedModelKey } from 'features/modelManagerV2/store/modelManagerV2Slice';
@@ -15,6 +17,7 @@ import { addToast } from 'features/system/store/systemSlice';
import { makeToast } from 'features/system/util/makeToast';
import { memo, useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import { IoWarning } from 'react-icons/io5';
import { PiTrashSimpleBold } from 'react-icons/pi';
import { useDeleteModelsMutation } from 'services/api/endpoints/models';
import type { AnyModelConfig } from 'services/api/types';
@@ -88,8 +91,16 @@ const ModelListItem = (props: ModelListItemProps) => {
{model.name}
+ {model.format === 'checkpoint' && (
+
+
+
+
+
+ )}
+
}
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx
new file mode 100644
index 0000000000..9756b357b3
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPane.tsx
@@ -0,0 +1,13 @@
+import { Box } from '@invoke-ai/ui-library';
+import { useAppSelector } from '../../../app/store/storeHooks';
+import { ImportModels } from './ImportModels';
+import { ModelView } from './ModelPanel/ModelView';
+
+export const ModelPane = () => {
+ const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
+ return (
+
+ {selectedModelKey ? : }
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx
new file mode 100644
index 0000000000..f45bfca993
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelAttrView.tsx
@@ -0,0 +1,15 @@
+import { FormControl, FormLabel, Text } from '@invoke-ai/ui-library';
+
+interface Props {
+ label: string;
+ value: string | null | undefined;
+}
+
+export const ModelAttrView = ({ label, value }: Props) => {
+ return (
+
+ {label}
+ {value || '-'}
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
new file mode 100644
index 0000000000..83e4e5380f
--- /dev/null
+++ b/invokeai/frontend/web/src/features/modelManagerV2/subpanels/ModelPanel/ModelView.tsx
@@ -0,0 +1,116 @@
+import { skipToken } from '@reduxjs/toolkit/query';
+import { useAppSelector } from '../../../../app/store/storeHooks';
+import { useGetModelQuery } from '../../../../services/api/endpoints/models';
+import { Flex, Text, Heading } from '@invoke-ai/ui-library';
+import DataViewer from '../../../gallery/components/ImageMetadataViewer/DataViewer';
+import { useMemo } from 'react';
+import {
+ CheckpointModelConfig,
+ ControlNetConfig,
+ DiffusersModelConfig,
+ IPAdapterConfig,
+ LoRAConfig,
+ T2IAdapterConfig,
+ TextualInversionConfig,
+ VAEConfig,
+} from '../../../../services/api/types';
+import { ModelAttrView } from './ModelAttrView';
+
+export const ModelView = () => {
+ const selectedModelKey = useAppSelector((s) => s.modelmanagerV2.selectedModelKey);
+ const { data, isLoading } = useGetModelQuery(selectedModelKey ?? skipToken);
+
+ const modelConfigData = useMemo(() => {
+ if (!data) {
+ return null;
+ }
+ const modelFormat = data.config.format;
+ const modelType = data.config.type;
+
+ if (modelType === 'main') {
+ if (modelFormat === 'diffusers') {
+ return data.config as DiffusersModelConfig;
+ } else if (modelFormat === 'checkpoint') {
+ return data.config as CheckpointModelConfig;
+ }
+ }
+
+ switch (modelType) {
+ case 'lora':
+ return data.config as LoRAConfig;
+ case 'embedding':
+ return data.config as TextualInversionConfig;
+ case 't2i_adapter':
+ return data.config as T2IAdapterConfig;
+ case 'ip_adapter':
+ return data.config as IPAdapterConfig;
+ case 'controlnet':
+ return data.config as ControlNetConfig;
+ case 'vae':
+ return data.config as VAEConfig;
+ default:
+ return null;
+ }
+ }, [data]);
+
+ if (isLoading) {
+ return Loading;
+ }
+
+ if (!modelConfigData) {
+ return Something went wrong;
+ }
+ return (
+
+
+
+ {modelConfigData.name}
+
+ {modelConfigData.source && Source: {modelConfigData.source}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {modelConfigData.type === 'main' && (
+ <>
+
+ {modelConfigData.format === 'diffusers' && (
+
+ )}
+ {modelConfigData.format === 'checkpoint' && (
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )}
+ {modelConfigData.type === 'ip_adapter' && (
+
+
+
+ )}
+
+
+ {!!data?.metadata && }
+
+ );
+};
diff --git a/invokeai/frontend/web/src/features/modelManagerV2/types.ts b/invokeai/frontend/web/src/features/modelManagerV2/types.ts
deleted file mode 100644
index a209fbb876..0000000000
--- a/invokeai/frontend/web/src/features/modelManagerV2/types.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { z } from "zod";
-
-export const zBaseModel = z.enum(['any', 'sd-1', 'sd-2', 'sdxl', 'sdxl-refiner']);
-export const zModelType = z.enum([
- 'main',
- 'vae',
- 'lora',
- 'controlnet',
- 'embedding',
- 'ip_adapter',
- 'clip_vision',
- 't2i_adapter',
- 'onnx', // TODO(psyche): Remove this when removed from backend
-]);
\ No newline at end of file
diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx
index 8117631f22..9245f5c60d 100644
--- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx
+++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManagerTab.tsx
@@ -1,16 +1,25 @@
-import { Box,Flex } from '@invoke-ai/ui-library';
-import { ImportModels } from 'features/modelManagerV2/subpanels/ImportModels';
-import { ModelManager } from 'features/modelManagerV2/subpanels/ModelManager';
-import { memo } from 'react';
+import { Flex, Heading, Tab, TabList, TabPanel, TabPanels, Tabs, Box, Button } from '@invoke-ai/ui-library';
+import ImportModelsPanel from 'features/modelManager/subpanels/ImportModelsPanel';
+import MergeModelsPanel from 'features/modelManager/subpanels/MergeModelsPanel';
+import ModelManagerPanel from 'features/modelManager/subpanels/ModelManagerPanel';
+import ModelManagerSettingsPanel from 'features/modelManager/subpanels/ModelManagerSettingsPanel';
+import type { ReactNode } from 'react';
+import { memo, useMemo } from 'react';
+import { useTranslation } from 'react-i18next';
+import { SyncModelsIconButton } from '../../../modelManager/components/SyncModels/SyncModelsIconButton';
+import { ModelManager } from '../../../modelManagerV2/subpanels/ModelManager';
+import { ModelPane } from '../../../modelManagerV2/subpanels/ModelPane';
+
+type ModelManagerTabName = 'modelManager' | 'importModels' | 'mergeModels' | 'settings';
const ModelManagerTab = () => {
+ const { t } = useTranslation();
+
return (
-
-
-
-
-
-
+
+
+
+
);
};
diff --git a/invokeai/frontend/web/src/services/api/endpoints/models.ts b/invokeai/frontend/web/src/services/api/endpoints/models.ts
index 6897768a1b..5db2dea00e 100644
--- a/invokeai/frontend/web/src/services/api/endpoints/models.ts
+++ b/invokeai/frontend/web/src/services/api/endpoints/models.ts
@@ -26,6 +26,10 @@ type UpdateModelArg = {
type UpdateModelResponse = paths['/api/v2/models/i/{key}']['patch']['responses']['200']['content']['application/json'];
+
+type GetModelResponse =
+ paths['/api/v2/models/i/{key}']['get']['responses']['200']['content']['application/json'];
+
type ListModelsArg = NonNullable;
type DeleteMainModelArg = {
@@ -165,6 +169,12 @@ export const modelsApi = api.injectEndpoints({
providesTags: buildProvidesTags('MainModel'),
transformResponse: buildTransformResponse(mainModelsAdapter),
}),
+ getModel: build.query({
+ query: (key) => {
+ return buildModelsUrl(`i/${key}`);
+ },
+ providesTags: ['Model'],
+ }),
updateModels: build.mutation({
query: ({ key, body }) => {
return {
@@ -320,4 +330,5 @@ export const {
useGetModelsInFolderQuery,
useGetCheckpointConfigsQuery,
useGetModelImportsQuery,
+ useGetModelQuery
} = modelsApi;
diff --git a/invokeai/frontend/web/src/services/api/schema.ts b/invokeai/frontend/web/src/services/api/schema.ts
index 3566fdb9e2..c9690649f8 100644
--- a/invokeai/frontend/web/src/services/api/schema.ts
+++ b/invokeai/frontend/web/src/services/api/schema.ts
@@ -22,7 +22,7 @@ export type paths = {
"/api/v2/models/i/{key}": {
/**
* Get Model Record
- * @description Get a model record
+ * @description Get a model record and metadata
*/
get: operations["get_model_record"];
/**
@@ -4202,6 +4202,13 @@ export type components = {
*/
type: "freeu";
};
+ /** GetModelResponse */
+ GetModelResponse: {
+ /** Config */
+ config: (components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"]) | (components["schemas"]["ONNXSD1Config"] | components["schemas"]["ONNXSD2Config"] | components["schemas"]["ONNXSDXLConfig"]) | (components["schemas"]["VaeDiffusersConfig"] | components["schemas"]["VaeCheckpointConfig"]) | (components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"]) | components["schemas"]["LoRAConfig"] | components["schemas"]["TextualInversionConfig"] | components["schemas"]["IPAdapterConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["T2IConfig"];
+ /** Metadata */
+ metadata: (components["schemas"]["BaseMetadata"] | components["schemas"]["HuggingFaceMetadata"] | components["schemas"]["CivitaiMetadata"]) | null;
+ };
/** Graph */
Graph: {
/**
@@ -11169,7 +11176,7 @@ export type operations = {
};
/**
* Get Model Record
- * @description Get a model record
+ * @description Get a model record and metadata
*/
get_model_record: {
parameters: {
@@ -11182,7 +11189,7 @@ export type operations = {
/** @description The model configuration was retrieved successfully */
200: {
content: {
- "application/json": (components["schemas"]["MainDiffusersConfig"] | components["schemas"]["MainCheckpointConfig"]) | (components["schemas"]["ONNXSD1Config"] | components["schemas"]["ONNXSD2Config"] | components["schemas"]["ONNXSDXLConfig"]) | (components["schemas"]["VaeDiffusersConfig"] | components["schemas"]["VaeCheckpointConfig"]) | (components["schemas"]["ControlNetDiffusersConfig"] | components["schemas"]["ControlNetCheckpointConfig"]) | components["schemas"]["LoRAConfig"] | components["schemas"]["TextualInversionConfig"] | components["schemas"]["IPAdapterConfig"] | components["schemas"]["CLIPVisionDiffusersConfig"] | components["schemas"]["T2IConfig"];
+ "application/json": components["schemas"]["GetModelResponse"];
};
};
/** @description Bad request */