From 3a6c3b1ae9eb9c1562ced7f1bffe78685a32d591 Mon Sep 17 00:00:00 2001 From: bennykok Date: Mon, 23 Sep 2024 15:31:13 -0700 Subject: [PATCH 1/6] feat: add native run proxy --- custom_routes.py | 33 ++++++++++- web-plugin/index.js | 133 ++++++++++++++++++++++++++++++++------------ 2 files changed, 128 insertions(+), 38 deletions(-) diff --git a/custom_routes.py b/custom_routes.py index 68cbe7e..f14563e 100644 --- a/custom_routes.py +++ b/custom_routes.py @@ -193,6 +193,9 @@ bypass_upload = os.environ.get('CD_BYPASS_UPLOAD', 'false').lower() == 'true' logger.info(f"CD_BYPASS_UPLOAD {bypass_upload}") +create_native_run_endpoint = None +status_endpoint = None +file_upload_endpoint = None def clear_current_prompt(sid): prompt_server = server.PromptServer.instance @@ -367,11 +370,39 @@ def send_prompt(sid: str, inputs: StreamingPrompt): logger.info(f"error: {error_type}, {e}") logger.info(f"stack trace: {stack_trace_short}") + + # # Add custom logic here + # if 'prompt_id' in response: + # prompt_id = response['prompt_id'] + # if prompt_id in prompt_metadata: + # metadata = prompt_metadata[prompt_id] + + # # Add additional information to the response + # response['status_endpoint'] = metadata.status_endpoint + # response['file_upload_endpoint'] = metadata.file_upload_endpoint + + return response + + @server.PromptServer.instance.routes.post("/comfyui-deploy/run") async def comfy_deploy_run(request): # Extract the bearer token from the Authorization header data = await request.json() + client_id = data.get("client_id") + # We proxy the request to Comfy Deploy, this is a native run + if "is_native_run" in data: + async with aiohttp.ClientSession() as session: + pprint(data) + # headers = request.headers.copy() + # headers['Content-Type'] = 'application/json' + async with session.post(data.get("native_run_api_endpoint"), json=data, headers={ + 'Content-Type': 'application/json', + 'Authorization': request.headers.get('Authorization') + }) as response: + data = await response.json() + print(data) + if "cd_token" in data: token = data["cd_token"] else: @@ -394,7 +425,7 @@ async def comfy_deploy_run(request): prompt = { "prompt": workflow_api, - "client_id": "comfy_deploy_instance", #api.client_id + "client_id": "comfy_deploy_instance" if client_id is None else client_id, "prompt_id": prompt_id, } diff --git a/web-plugin/index.js b/web-plugin/index.js index 38fb9f7..47358e8 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -83,6 +83,21 @@ function dispatchAPIEventData(data) { } } +let selectedWorkflowInfo +// let selectedWorkflowInfo = { +// workflow_id: "05da8f2b-63af-4c0c-86dd-08d01ec512b7", +// machine_id: "45ac5f85-b7b6-436f-8d97-2383b25485f3", +// native_run_api_endpoint: "http://localhost:3011/api/run", +// }; + +function getSelectedWorkflowInfo() { + return selectedWorkflowInfo; +} + +function setSelectedWorkflowInfo(info) { + selectedWorkflowInfo = info; +} + /** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/ /** @type {ComfyExtension} */ const ext = { @@ -213,7 +228,7 @@ const ext = { (v) => { this.properties.workflow_name = v; }, - { multiline: false } + { multiline: false }, ); this.addWidget( @@ -223,7 +238,7 @@ const ext = { (v) => { this.properties.workflow_id = v; }, - { multiline: false } + { multiline: false }, ); this.addWidget( @@ -233,7 +248,7 @@ const ext = { (v) => { this.properties.version = v; }, - { multiline: false } + { multiline: false }, ); this.widgets_start_y = 10; @@ -270,11 +285,14 @@ const ext = { } // Register the node type - LiteGraph.registerNodeType("ComfyDeploy", Object.assign(ComfyDeploy, { - title: "Comfy Deploy", - title_mode: LiteGraph.NORMAL_TITLE, - collapsable: true, - })); + LiteGraph.registerNodeType( + "ComfyDeploy", + Object.assign(ComfyDeploy, { + title: "Comfy Deploy", + title_mode: LiteGraph.NORMAL_TITLE, + collapsable: true, + }), + ); ComfyDeploy.category = "deploy"; }, @@ -380,6 +398,8 @@ const ext = { } animate(); + } else if (message.type === "workflow_info") { + setSelectedWorkflowInfo(message.data); } // else if (message.type === "refresh") { // sendEventToCD("cd_plugin_onRefresh"); @@ -443,10 +463,10 @@ function createDynamicUIHtml(data) {

Missing Nodes

These nodes are not found with any matching custom_nodes in the ComfyUI Manager Database

${data.missing_nodes - .map((node) => { - return `

${node}

`; - }) - .join("")} + .map((node) => { + return `

${node}

`; + }) + .join("")} `; } @@ -454,14 +474,17 @@ function createDynamicUIHtml(data) { Object.values(data.custom_nodes).forEach((node) => { html += `
- ${node.name - } + ${ + node.name + }

${node.hash}

- ${node.warning - ? `

${node.warning}

` - : "" - } + ${ + node.warning + ? `

${node.warning}

` + : "" + }
`; }); @@ -475,8 +498,9 @@ function createDynamicUIHtml(data) { Object.entries(data.models).forEach(([section, items]) => { html += `
-

${section.charAt(0).toUpperCase() + section.slice(1) - }

`; +

${ + section.charAt(0).toUpperCase() + section.slice(1) + }

`; items.forEach((item) => { html += `

${item.name}

`; }); @@ -492,8 +516,9 @@ function createDynamicUIHtml(data) { Object.entries(data.files).forEach(([section, items]) => { html += `
-

${section.charAt(0).toUpperCase() + section.slice(1) - }

`; +

${ + section.charAt(0).toUpperCase() + section.slice(1) + }

`; items.forEach((item) => { html += `

${item.name}

`; }); @@ -1013,12 +1038,14 @@ export class LoadingDialog extends ComfyDialog { showLoading(title, message) { this.show(`
-

${title} ${this.loadingIcon - }

- ${message - ? `` - : "" - } +

${title} ${ + this.loadingIcon + }

+ ${ + message + ? `` + : "" + }
`); } @@ -1284,17 +1311,21 @@ export class ConfigDialog extends ComfyDialog {
- API Key: User / Org - + API Key: User / Org +
@@ -1483,3 +1514,31 @@ async function loadWorkflowApi(versionId) { // Show an error message to the user } } + +const orginal_fetch_api = api.fetchApi; +api.fetchApi = async (route, options) => { + console.log("Fetch API called with args:", route, options); + + const info = getSelectedWorkflowInfo(); + if (info && route.startsWith("/prompt")) { + console.log("Prompt API called"); + + const body = JSON.parse(options.body); + + return await fetch("/comfyui-deploy/run", { + method: "POST", + headers: { + Authorization: `Bearer ${getData().apiKey}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + client_id: body.client_id, + workflow_api_json: body.prompt, + is_native_run: true, + ...info, + }), + }); + } + + return await orginal_fetch_api.call(api, route, options); +}; From d1c54b2b6d736266d3856fd63d061243a2f33fe1 Mon Sep 17 00:00:00 2001 From: bennykok Date: Mon, 23 Sep 2024 19:01:47 -0700 Subject: [PATCH 2/6] fix: state --- web-plugin/index.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/web-plugin/index.js b/web-plugin/index.js index 47358e8..6eecdab 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -83,7 +83,9 @@ function dispatchAPIEventData(data) { } } -let selectedWorkflowInfo +const context = { + selectedWorkflowInfo: null, +}; // let selectedWorkflowInfo = { // workflow_id: "05da8f2b-63af-4c0c-86dd-08d01ec512b7", // machine_id: "45ac5f85-b7b6-436f-8d97-2383b25485f3", @@ -91,11 +93,11 @@ let selectedWorkflowInfo // }; function getSelectedWorkflowInfo() { - return selectedWorkflowInfo; + return context.selectedWorkflowInfo; } function setSelectedWorkflowInfo(info) { - selectedWorkflowInfo = info; + context.selectedWorkflowInfo = info; } /** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/ @@ -118,10 +120,10 @@ const ext = { sendEventToCD("cd_plugin_onInit"); - app.queuePrompt = ((originalFunction) => async () => { - // const prompt = await app.graphToPrompt(); - sendEventToCD("cd_plugin_onQueuePromptTrigger"); - })(app.queuePrompt); + // app.queuePrompt = ((originalFunction) => async () => { + // // const prompt = await app.graphToPrompt(); + // sendEventToCD("cd_plugin_onQueuePromptTrigger"); + // })(app.queuePrompt); // // Intercept the onkeydown event // window.addEventListener( @@ -1525,18 +1527,25 @@ api.fetchApi = async (route, options) => { const body = JSON.parse(options.body); + // getData().apiKey + + const data = { + client_id: body.client_id, + workflow_api_json: body.prompt, + is_native_run: true, + machine_id: info.machine_id, + workflow_id: info.workflow_id, + native_run_api_endpoint: info.native_run_api_endpoint, + }; + + return await fetch("/comfyui-deploy/run", { method: "POST", headers: { - Authorization: `Bearer ${getData().apiKey}`, + Authorization: `Bearer ${info.cd_token}`, "Content-Type": "application/json", }, - body: JSON.stringify({ - client_id: body.client_id, - workflow_api_json: body.prompt, - is_native_run: true, - ...info, - }), + body: JSON.stringify(data), }); } From 5c6defbe627b7f8d40b9ecb14e97418165f80db0 Mon Sep 17 00:00:00 2001 From: EdwinWong Date: Tue, 24 Sep 2024 15:35:48 -0700 Subject: [PATCH 3/6] fix: add workflow data to extra data --- custom_routes.py | 11 +++++++++-- globals.py | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/custom_routes.py b/custom_routes.py index 68cbe7e..73d0ac6 100644 --- a/custom_routes.py +++ b/custom_routes.py @@ -333,6 +333,7 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None): def send_prompt(sid: str, inputs: StreamingPrompt): # workflow_api = inputs.workflow_api workflow_api = copy.deepcopy(inputs.workflow_api) + workflow = copy.deepcopy(inputs.workflow) # Random seed apply_random_seed_to_workflow(workflow_api) @@ -348,7 +349,8 @@ def send_prompt(sid: str, inputs: StreamingPrompt): prompt = { "prompt": workflow_api, "client_id": sid, #"comfy_deploy_instance", #api.client_id - "prompt_id": prompt_id + "prompt_id": prompt_id, + "extra_data": {"extra_pnginfo": {"workflow": workflow}}, } try: @@ -387,6 +389,7 @@ async def comfy_deploy_run(request): # The prompt id generated from comfy deploy, can be None prompt_id = data.get("prompt_id") inputs = data.get("inputs") + workflow = data.get("workflow") # Now it handles directly in here apply_random_seed_to_workflow(workflow_api) @@ -396,6 +399,7 @@ async def comfy_deploy_run(request): "prompt": workflow_api, "client_id": "comfy_deploy_instance", #api.client_id "prompt_id": prompt_id, + "extra_data": {"extra_pnginfo": {"workflow": workflow}} } prompt_metadata[prompt_id] = SimplePrompt( @@ -446,6 +450,7 @@ async def stream_prompt(data, token): # The prompt id generated from comfy deploy, can be None prompt_id = data.get("prompt_id") inputs = data.get("inputs") + workflow = data.get("workflow") # Now it handles directly in here apply_random_seed_to_workflow(workflow_api) @@ -454,7 +459,8 @@ async def stream_prompt(data, token): prompt = { "prompt": workflow_api, "client_id": "comfy_deploy_instance", #api.client_id - "prompt_id": prompt_id + "prompt_id": prompt_id, + "extra_data": {"extra_pnginfo": {"workflow": workflow}}, } prompt_metadata[prompt_id] = SimplePrompt( @@ -788,6 +794,7 @@ async def websocket_handler(request): inputs={}, status_endpoint=status_endpoint, file_upload_endpoint=request.rel_url.query.get('file_upload_endpoint', None), + workflow=workflow["workflow"], ) await update_realtime_run_status(realtime_id, status_endpoint, Status.RUNNING) diff --git a/globals.py b/globals.py index 3ee2658..ea705ee 100644 --- a/globals.py +++ b/globals.py @@ -24,6 +24,7 @@ class StreamingPrompt(BaseModel): running_prompt_ids: set[str] = set() status_endpoint: Optional[str] file_upload_endpoint: Optional[str] + workflow: Any class SimplePrompt(BaseModel): status_endpoint: Optional[str] From 2eb02fc92e6154b0db50931fabf2fccc9eae1a97 Mon Sep 17 00:00:00 2001 From: bennykok Date: Tue, 24 Sep 2024 19:36:57 -0700 Subject: [PATCH 4/6] fi --- web-plugin/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web-plugin/index.js b/web-plugin/index.js index 6eecdab..2fa2b30 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -1532,6 +1532,7 @@ api.fetchApi = async (route, options) => { const data = { client_id: body.client_id, workflow_api_json: body.prompt, + workflow: body.workflow, is_native_run: true, machine_id: info.machine_id, workflow_id: info.workflow_id, From 50860cd50027e242ca07215d3e09af6a3faf4441 Mon Sep 17 00:00:00 2001 From: bennykok Date: Tue, 24 Sep 2024 19:45:53 -0700 Subject: [PATCH 5/6] test --- web-plugin/index.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/web-plugin/index.js b/web-plugin/index.js index 2fa2b30..95235b2 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -1523,23 +1523,18 @@ api.fetchApi = async (route, options) => { const info = getSelectedWorkflowInfo(); if (info && route.startsWith("/prompt")) { - console.log("Prompt API called"); - const body = JSON.parse(options.body); - // getData().apiKey - const data = { client_id: body.client_id, workflow_api_json: body.prompt, - workflow: body.workflow, + workflow: body?.extra_data?.extra_pnginfo?.workflow, is_native_run: true, machine_id: info.machine_id, workflow_id: info.workflow_id, native_run_api_endpoint: info.native_run_api_endpoint, }; - return await fetch("/comfyui-deploy/run", { method: "POST", headers: { From e2fcf67aecf1cf99827d890d1a31a68a3642022f Mon Sep 17 00:00:00 2001 From: bennykok Date: Wed, 25 Sep 2024 12:59:00 -0700 Subject: [PATCH 6/6] fix: graph load --- web-plugin/index.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/web-plugin/index.js b/web-plugin/index.js index 95235b2..1246c56 100644 --- a/web-plugin/index.js +++ b/web-plugin/index.js @@ -313,6 +313,14 @@ const ext = { // This part of the code would depend on how the ComfyUI expects to receive and process the workflow data // For demonstration, let's assume there's a loadWorkflow method in the ComfyUI API if (comfyUIWorkflow && app && app.loadGraphData) { + try { + await window["app"].ui.settings.setSettingValueAsync( + "Comfy.Validation.Workflows", + false, + ); + } catch (error) { + console.warning("Error setting validation to false, is fine to ignore this", error); + } console.log("loadGraphData"); app.loadGraphData(comfyUIWorkflow); }