From 744475cc024a8bd8406556fd0da43e30e004ea0d Mon Sep 17 00:00:00 2001 From: julianbollig Date: Wed, 18 Dec 2024 11:01:57 +0100 Subject: [PATCH 01/23] Fix status updates for backend installation when switching windows Status update visible in Basic tab while Installation still ongoing; loading bar remains during installation even when installation panel is closed and reopened Signed-off-by: julianbollig --- WebUI/src/assets/js/store/backendServices.ts | 4 +++- .../src/components/InstallationManagement.vue | 19 +++++++++---------- WebUI/src/components/SettingsBasic.vue | 14 ++++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/WebUI/src/assets/js/store/backendServices.ts b/WebUI/src/assets/js/store/backendServices.ts index 2e96e0e6..f8634f24 100644 --- a/WebUI/src/assets/js/store/backendServices.ts +++ b/WebUI/src/assets/js/store/backendServices.ts @@ -41,7 +41,7 @@ export const useBackendServices = defineStore("backendServices", () => { const initalStartupRequestComplete = ref(false) const allRequiredSetUp = computed(() => currentServiceInfo.value.length > 0 && currentServiceInfo.value.filter(s => s.isRequired).every(s => s.isSetUp)); const allRequiredRunning = computed(() => currentServiceInfo.value.length > 0 && currentServiceInfo.value.filter(s => s.isRequired).every(s => s.status === "running")); - + const loadingComponents = ref(new Set()); async function startAllSetUpServices(): Promise<{allServicesStarted: boolean}> { const serverStartups = await Promise.all(currentServiceInfo.value.filter(s => s.isSetUp).map(s => window.electronAPI.sendStartSignal(s.serviceName))); @@ -78,10 +78,12 @@ export const useBackendServices = defineStore("backendServices", () => { allRequiredSetUp, allRequiredRunning, initalStartupRequestComplete, + loadingComponents, startAllSetUpServices, setUpService, startService, stopService, + } }, { persist: { diff --git a/WebUI/src/components/InstallationManagement.vue b/WebUI/src/components/InstallationManagement.vue index 20889a5d..4be290d3 100644 --- a/WebUI/src/components/InstallationManagement.vue +++ b/WebUI/src/components/InstallationManagement.vue @@ -123,11 +123,10 @@ let toBeInstalledQueue: ExtendedApiServiceInformation[] = [] const somethingChanged = ref(false) const enabledComponents = ref(new Set(backendServices.info.filter((item) => item.isSetUp || item.isRequired).map((item) => item.serviceName))) -const loadingComponents = ref(new Set()) const components = computed(() => {return backendServices.info.map((item) => ({ enabled: enabledComponents.value.has(item.serviceName), - isLoading: loadingComponents.value.has(item.serviceName), + isLoading: backendServices.loadingComponents.has(item.serviceName), ...item }))}) @@ -138,18 +137,18 @@ function isSomethingLoading(): boolean { async function installBackend(name: BackendServiceName) { somethingChanged.value = true; - loadingComponents.value.add(name) + backendServices.loadingComponents.add(name) const setupProgress = await backendServices.setUpService(name) if (setupProgress.success) { await restartBackend(name) } else { toast.error("Setup failed") - loadingComponents.value.delete(name) + backendServices.loadingComponents.delete(name) } } async function repairBackend(name: BackendServiceName) { - loadingComponents.value.add(name) + backendServices.loadingComponents.add(name) const stopStatus = await backendServices.stopService(name) if (stopStatus !== 'stopped') { toast.error("Service failed to stop") @@ -159,27 +158,27 @@ async function repairBackend(name: BackendServiceName) { } async function restartBackend(name: BackendServiceName) { - loadingComponents.value.add(name) + backendServices.loadingComponents.add(name) const stopStatus = await backendServices.stopService(name) if (stopStatus !== 'stopped') { toast.error("Service failed to stop") - loadingComponents.value.delete(name) + backendServices.loadingComponents.delete(name) return } const startStatus = await backendServices.startService(name) if (startStatus !== 'running') { toast.error("Service failed to restart") - loadingComponents.value.delete(name) + backendServices.loadingComponents.delete(name) return } - loadingComponents.value.delete(name) + backendServices.loadingComponents.delete(name) } async function installAllSelected() { toBeInstalledQueue = components.value.filter(item => item.enabled && (item.status === 'notInstalled' || item.status === 'failed' || item.status === 'installationFailed')); - toBeInstalledQueue.forEach((item) => loadingComponents.value.add(item.serviceName)) + toBeInstalledQueue.forEach((item) => backendServices.loadingComponents.add(item.serviceName)) for (const component of toBeInstalledQueue) { if (component.status === 'failed' || component.status == "installationFailed") { await repairBackend(component.serviceName); diff --git a/WebUI/src/components/SettingsBasic.vue b/WebUI/src/components/SettingsBasic.vue index f25e2c5e..a858eece 100644 --- a/WebUI/src/components/SettingsBasic.vue +++ b/WebUI/src/components/SettingsBasic.vue @@ -80,7 +80,7 @@ - + @@ -109,8 +109,6 @@ import {useTextInference, backendTypes, Backend} from "@/assets/js/store/textInf import {mapServiceNameToDisplayName, mapStatusToColor, mapToDisplayStatus} from "@/lib/utils.ts"; import {useBackendServices} from "@/assets/js/store/backendServices.ts"; - -const apiServiceInformation = ref([]) const globalSetup = useGlobalSetup(); const textInference = useTextInference(); const backendServices = useBackendServices(); @@ -122,11 +120,6 @@ const textInferenceBackendDisplayName: Record { - apiServiceInformation.value = await window.electronAPI.getServices() -}) - const themeToDisplayName = (theme: Theme) => { switch (theme) { case 'dark': @@ -140,6 +133,11 @@ const themeToDisplayName = (theme: Theme) => { } } +const displayComponents = computed(() => {return backendServices.info.map((item) => ({ + serviceName: item.serviceName, + status: item.status +}))}) + const modelSettings = reactive(Object.assign({}, toRaw(globalSetup.modelSettings))); const graphicsName = computed(() => { From bf49b78b5f6bba25a19695157b041026ed5478f5 Mon Sep 17 00:00:00 2001 From: Florian Esser Date: Wed, 18 Dec 2024 10:44:48 +0100 Subject: [PATCH 02/23] Fix git ref provided by workflows This does not need to be required. Treat it as an optional variable, but warn in logs, if it is missing. This should make everything more robust --- WebUI/external/workflows/FaceSwapHD.json | 2 +- WebUI/external/workflows/fluxQ4.json | 2 +- WebUI/external/workflows/fluxQ8.json | 2 +- WebUI/src/assets/js/store/comfyUi.ts | 25 ++++++++++++++++++++---- WebUI/src/env.d.ts | 2 +- WebUI/src/views/Create.vue | 1 + service/aipg_utils.py | 8 ++++---- service/comfyui_downloader.py | 15 ++++++++++---- service/web_api.py | 13 ------------ service/web_request_bodies.py | 4 ++-- 10 files changed, 43 insertions(+), 31 deletions(-) diff --git a/WebUI/external/workflows/FaceSwapHD.json b/WebUI/external/workflows/FaceSwapHD.json index 7b2b1277..32fe72bf 100644 --- a/WebUI/external/workflows/FaceSwapHD.json +++ b/WebUI/external/workflows/FaceSwapHD.json @@ -7,7 +7,7 @@ "onnxruntime" ], "customNodes": [ - "Gourieff/comfyui-reactor-node/be1c60bd62d1fb35511153533032c5a6811c8fab" + "Gourieff/comfyui-reactor-node@be1c60bd62d1fb35511153533032c5a6811c8fab" ], "requiredModels": [ "defaultCheckpoint:RunDiffusion/Juggernaut-XL-v9/unet/diffusion_pytorch_model.fp16.safetensors", diff --git a/WebUI/external/workflows/fluxQ4.json b/WebUI/external/workflows/fluxQ4.json index 910376a5..ad156bd7 100644 --- a/WebUI/external/workflows/fluxQ4.json +++ b/WebUI/external/workflows/fluxQ4.json @@ -7,7 +7,7 @@ "backend": "comfyui", "comfyUIRequirements": { "customNodes": [ - "city96/ComfyUI-GGUF/65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7" + "city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7" ], "requiredModels": [ "unet:city96/FLUX.1-schnell-gguf/flux1-schnell-Q4_K_S.gguf", diff --git a/WebUI/external/workflows/fluxQ8.json b/WebUI/external/workflows/fluxQ8.json index 215c3f65..2f328db4 100644 --- a/WebUI/external/workflows/fluxQ8.json +++ b/WebUI/external/workflows/fluxQ8.json @@ -4,7 +4,7 @@ "backend": "comfyui", "comfyUIRequirements": { "customNodes": [ - "city96/ComfyUI-GGUF" + "city96/ComfyUI-GGUF@65a7c895bb0ac9547ba2f89d55fbdb609aa2bfe7" ], "requiredModels": [ "unet:city96/FLUX.1-schnell-gguf/flux1-schnell-Q8_0.gguf", diff --git a/WebUI/src/assets/js/store/comfyUi.ts b/WebUI/src/assets/js/store/comfyUi.ts index e489fc6e..6ca2c7ec 100644 --- a/WebUI/src/assets/js/store/comfyUi.ts +++ b/WebUI/src/assets/js/store/comfyUi.ts @@ -40,13 +40,30 @@ export const useComfyUi = defineStore("comfyUi", () => { } } + function extractCustomNodeInfo(workflowNodeInfoString: string): ComfyUICustomNodesRequestParameters { + const repoInfoWithPotentialGitRefSplitted = workflowNodeInfoString.replace(" ", "").split("@") + if (repoInfoWithPotentialGitRefSplitted.length > 2 || repoInfoWithPotentialGitRefSplitted.length < 1) { + console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) + throw new Error("Could not extract comfyUI node description from ${workflowNodeInfoString}") + } + const [repoInfoString, gitRef] = repoInfoWithPotentialGitRefSplitted + if (!gitRef) { + console.warn(`No gitRef provided in ${workflowNodeInfoString}.`) + } + const repoInfoSplitted = repoInfoString.replace(" ", "").split("/") + if (repoInfoSplitted.length !== 2) { + console.error(`Could not extract comfyUI node description from ${workflowNodeInfoString}`) + throw new Error("Could not extract comfyUI node description from ${workflowNodeInfoString}") + } + const [username, repoName] = repoInfoSplitted + console.info(JSON.stringify({username: username, repoName: repoName, gitRef: gitRef})) + return {username: username, repoName: repoName, gitRef: gitRef} + } + async function installCustomNodesForActiveWorkflow(): Promise { const uniqueCustomNodes = new Set(imageGeneration.workflows.filter(w => w.name === imageGeneration.activeWorkflowName).filter(w => w.backend === 'comfyui').flatMap((item) => item.comfyUIRequirements.customNodes)) const requiredCustomNodes: ComfyUICustomNodesRequestParameters[] = - [...uniqueCustomNodes].map((nodeName) => { - const [username, repoName, gitRef] = nodeName.replace(" ", "").split("/") - return {username: username, repoName: repoName, gitRef: gitRef} - }) + [...uniqueCustomNodes].map((nodeName) => extractCustomNodeInfo(nodeName)) const response = await fetch(`${globalSetup.apiHost}/api/comfyUi/loadCustomNodes`, { method: 'POST', body: JSON.stringify({data: requiredCustomNodes}), diff --git a/WebUI/src/env.d.ts b/WebUI/src/env.d.ts index 233c2116..542919e4 100644 --- a/WebUI/src/env.d.ts +++ b/WebUI/src/env.d.ts @@ -306,7 +306,7 @@ type DownloadModelParam = CheckModelAlreadyLoadedParameters type ComfyUICustomNodesRequestParameters = { username: string, repoName: string - gitRef: string + gitRef?: string } diff --git a/WebUI/src/views/Create.vue b/WebUI/src/views/Create.vue index 627c5c98..f90770ee 100644 --- a/WebUI/src/views/Create.vue +++ b/WebUI/src/views/Create.vue @@ -99,6 +99,7 @@ const downloadModel = reactive({ const showParams = ref(false); const infoParams = ref({}) + const emits = defineEmits<{ (e: "showDownloadModelConfirm", downloadList: DownloadModelParam[], success?: () => void, fail?: () => void): void, (e: "postImageToEnhance", url: string): void diff --git a/service/aipg_utils.py b/service/aipg_utils.py index 5d877347..f768f7fc 100644 --- a/service/aipg_utils.py +++ b/service/aipg_utils.py @@ -126,11 +126,11 @@ def check_defaultbackend_mmodel_exist(type: int, repo_id: str) -> bool: elif type == 7: dir = service_config.service_model_paths.get("preview") return ( - os.path.exists(os.path.join(dir, folder_name, "config.json")) - or os.path.exists(os.path.join(dir, f"{repo_id}.safetensors")) - or os.path.exists(os.path.join(dir, f"{repo_id}.bin")) + os.path.exists(os.path.join(dir, folder_name, "config.json")) + or os.path.exists(os.path.join(dir, f"{repo_id}.safetensors")) + or os.path.exists(os.path.join(dir, f"{repo_id}.bin")) ) - + def convert_model_type(type: int): diff --git a/service/comfyui_downloader.py b/service/comfyui_downloader.py index ee2cf6fa..954748e0 100644 --- a/service/comfyui_downloader.py +++ b/service/comfyui_downloader.py @@ -88,13 +88,17 @@ def _install_git_repo(git_repo_url: str, target_dir: str): aipg_utils.remove_existing_filesystem_resource(target_dir) raise e -def _checkout_git_ref(repo_dir: str, git_ref: str): +def _checkout_git_ref(repo_dir: str, git_ref: Optional[str]): + if git_ref is None or not git_ref.strip(): + logging.info(f"No valid git ref provided for {repo_dir}") + logging.warning(f"Repo {repo_dir} remains in ref {get_git_ref(repo_dir)}.") + return try: aipg_utils.call_subprocess(f"{service_config.git.get('exePath')} checkout {git_ref}", cwd=repo_dir) logging.info(f"checked out {git_ref} in {repo_dir}") except Exception as e: logging.warning(f"git checkout of {git_ref} failed for rep {repo_dir} due to {e}.") - logging.warning(f"will use repo ref {get_git_ref(repo_dir)} in {repo_dir}.") + logging.warning(f"Repo {repo_dir} remains in ref {get_git_ref(repo_dir)}.") def get_git_ref(repo_dir: str) -> Optional[str]: @@ -159,8 +163,11 @@ def install_comfyUI() -> bool: def is_custom_node_installed_with_git_ref(node_repo_ref: ComfyUICustomNodesGithubRepoId) -> bool: expected_custom_node_path = os.path.join(service_config.comfy_ui_root_path, "custom_nodes", node_repo_ref.repoName) - custom_node_dir_exists= os.path.exists(expected_custom_node_path) - return custom_node_dir_exists and (get_git_ref(expected_custom_node_path) == node_repo_ref.gitRef) + custom_node_dir_exists = os.path.exists(expected_custom_node_path) + + git_ref_provided = node_repo_ref.gitRef is not None and not node_repo_ref.gitRef.strip() == "" + git_ref_matches = not git_ref_provided and (get_git_ref(expected_custom_node_path) == node_repo_ref.gitRef) + return custom_node_dir_exists and git_ref_matches def download_custom_node(node_repo_data: ComfyUICustomNodesGithubRepoId) -> bool: diff --git a/service/web_api.py b/service/web_api.py index b21b4ddc..3f7613d3 100644 --- a/service/web_api.py +++ b/service/web_api.py @@ -371,19 +371,6 @@ def delete_rag_file(): return jsonify({"code": -1, "message": "failed"}) -@app.get("/api/comfyUi/isInstalled") -def is_comfyUI_loaded(): - return jsonify({"is_comfyUI_installed": comfyui_downloader.is_comfyUI_installed()}) - -@app.post("/api/comfyUi/install") -def install_comfyUI(): - try: - installation_success = comfyui_downloader.install_comfyUI() - return jsonify({"success": installation_success, "error_message": ""}) - except Exception as e: - return jsonify({'error_message': f'failed to install comfyUI due to {e}'}), 501 - - @app.post("/api/comfyUi/areCustomNodesLoaded") @app.input(ComfyUICustomNodesDownloadRequest.Schema, location='json', arg_name='comfyNodeRequest') def are_custom_nodes_installed(comfyNodeRequest: ComfyUICustomNodesDownloadRequest): diff --git a/service/web_request_bodies.py b/service/web_request_bodies.py index 10820dc3..0e81e2cb 100644 --- a/service/web_request_bodies.py +++ b/service/web_request_bodies.py @@ -1,4 +1,4 @@ -from typing import List +from typing import List, Optional import marshmallow_dataclass from marshmallow import EXCLUDE @@ -20,7 +20,7 @@ class DownloadModelRequestBody: class ComfyUICustomNodesGithubRepoId: username: str repoName: str - gitRef: str + gitRef: Optional[str] @marshmallow_dataclass.dataclass class ComfyUICustomNodesDownloadRequest: From 44596b3ed1d8b87eca13a0f70e2bfe21bb5bd6ac Mon Sep 17 00:00:00 2001 From: julianbollig Date: Thu, 19 Dec 2024 10:22:36 +0100 Subject: [PATCH 03/23] Final Changes Signed-off-by: julianbollig --- WebUI/src/App.vue | 10 +++++----- WebUI/src/assets/js/store/backendServices.ts | 3 +-- .../src/components/InstallationManagement.vue | 20 ++++++++++--------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/WebUI/src/App.vue b/WebUI/src/App.vue index efd2cba3..a1952f24 100644 --- a/WebUI/src/App.vue +++ b/WebUI/src/App.vue @@ -21,17 +21,17 @@ -
+
-
-
+
-
+
@@ -59,7 +59,7 @@
-
+
-
+
{{ mapServiceNameToDisplayName(item.serviceName) }} {{ mapToDisplayStatus(item.status) }}