Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7fb688522 | ||
|
|
b98ae10a76 | ||
|
|
b79ed40f6d | ||
|
|
06ae3af5d0 | ||
|
|
967709dd07 | ||
|
|
07a827d56b |
@@ -2995,6 +2995,7 @@ async def create_workflow_proxy(request):
|
|||||||
name = data.get("name")
|
name = data.get("name")
|
||||||
workflow_json = data.get("workflow_json")
|
workflow_json = data.get("workflow_json")
|
||||||
workflow_api = data.get("workflow_api")
|
workflow_api = data.get("workflow_api")
|
||||||
|
machine_id = data.get("machine_id")
|
||||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
|
||||||
auth_header = request.headers.get("Authorization")
|
auth_header = request.headers.get("Authorization")
|
||||||
@@ -3014,6 +3015,7 @@ async def create_workflow_proxy(request):
|
|||||||
"name": name,
|
"name": name,
|
||||||
"workflow_json": json.dumps(workflow_json),
|
"workflow_json": json.dumps(workflow_json),
|
||||||
"workflow_api": json.dumps(workflow_api),
|
"workflow_api": json.dumps(workflow_api),
|
||||||
|
"machine_id": machine_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -3136,3 +3138,147 @@ async def get_workflow_proxy(request):
|
|||||||
return web.json_response(json_data, status=response.status)
|
return web.json_response(json_data, status=response.status)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return web.json_response({"error": str(e)}, status=500)
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
# for getting a machine by id
|
||||||
|
@server.PromptServer.instance.routes.get("/comfyui-deploy/machine")
|
||||||
|
async def get_machine_proxy(request):
|
||||||
|
machine_id = request.rel_url.query.get("machine_id")
|
||||||
|
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
return web.json_response(
|
||||||
|
{"error": "Authorization header is required"}, status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
target_url = f"{api_url}/api/machine/{machine_id}"
|
||||||
|
|
||||||
|
try:
|
||||||
|
await ensure_client_session()
|
||||||
|
async with client_session.get(
|
||||||
|
target_url, headers={"Authorization": auth_header}
|
||||||
|
) as response:
|
||||||
|
json_data = await response.json()
|
||||||
|
return web.json_response(json_data, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
# for fetching docker steps from current snapshot
|
||||||
|
@server.PromptServer.instance.routes.post("/comfyui-deploy/snapshot-to-docker")
|
||||||
|
async def snapshot_to_docker_proxy(request):
|
||||||
|
data = await request.json()
|
||||||
|
snapshot = data.get("snapshot")
|
||||||
|
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
return web.json_response(
|
||||||
|
{"error": "Authorization header is required"}, status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
target_url = f"{api_url}/api/snapshot-to-docker"
|
||||||
|
|
||||||
|
request_body = snapshot
|
||||||
|
|
||||||
|
try:
|
||||||
|
await ensure_client_session()
|
||||||
|
async with client_session.post(
|
||||||
|
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||||
|
) as response:
|
||||||
|
json_data = await response.json()
|
||||||
|
return web.json_response(json_data, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
# update a serverless machine with machine id
|
||||||
|
@server.PromptServer.instance.routes.post("/comfyui-deploy/machine/update")
|
||||||
|
async def update_machine_proxy(request):
|
||||||
|
data = await request.json()
|
||||||
|
machine_id = data.get("machine_id")
|
||||||
|
comfyui_version = data.get("comfyui_version", None)
|
||||||
|
docker_steps = data.get("docker_steps")
|
||||||
|
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
return web.json_response(
|
||||||
|
{"error": "Authorization header is required"}, status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
target_url = f"{api_url}/api/machine/serverless/{machine_id}"
|
||||||
|
|
||||||
|
request_body = {"docker_command_steps": docker_steps}
|
||||||
|
|
||||||
|
if comfyui_version:
|
||||||
|
request_body["comfyui_version"] = comfyui_version
|
||||||
|
|
||||||
|
try:
|
||||||
|
await ensure_client_session()
|
||||||
|
async with client_session.patch(
|
||||||
|
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||||
|
) as response:
|
||||||
|
json_data = await response.json()
|
||||||
|
return web.json_response(json_data, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
@server.PromptServer.instance.routes.post("/comfyui-deploy/machine/create")
|
||||||
|
async def create_machine_proxy(request):
|
||||||
|
data = await request.json()
|
||||||
|
name = data.get("name")
|
||||||
|
docker_command_steps = data.get("docker_command_steps")
|
||||||
|
comfyui_version = data.get("comfyui_version")
|
||||||
|
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
return web.json_response(
|
||||||
|
{"error": "Authorization header is required"}, status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
target_url = f"{api_url}/api/machine/serverless"
|
||||||
|
|
||||||
|
request_body = {
|
||||||
|
"name": name,
|
||||||
|
"docker_command_steps": docker_command_steps,
|
||||||
|
"comfyui_version": comfyui_version,
|
||||||
|
"gpu": "A10G",
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
await ensure_client_session()
|
||||||
|
async with client_session.post(
|
||||||
|
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||||
|
) as response:
|
||||||
|
json_data = await response.json()
|
||||||
|
return web.json_response(json_data, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|
||||||
|
|
||||||
|
# get latest comfyui version
|
||||||
|
@server.PromptServer.instance.routes.get("/comfyui-deploy/comfyui-version")
|
||||||
|
async def get_comfyui_version_proxy(request):
|
||||||
|
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||||
|
auth_header = request.headers.get("Authorization")
|
||||||
|
|
||||||
|
if not auth_header:
|
||||||
|
return web.json_response(
|
||||||
|
{"error": "Authorization header is required"}, status=401
|
||||||
|
)
|
||||||
|
|
||||||
|
target_url = f"{api_url}/api/latest-hashes"
|
||||||
|
|
||||||
|
try:
|
||||||
|
await ensure_client_session()
|
||||||
|
async with client_session.get(
|
||||||
|
target_url, headers={"Authorization": auth_header}
|
||||||
|
) as response:
|
||||||
|
json_data = await response.json()
|
||||||
|
return web.json_response(json_data, status=response.status)
|
||||||
|
except Exception as e:
|
||||||
|
return web.json_response({"error": str(e)}, status=500)
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-deploy"
|
name = "comfyui-deploy"
|
||||||
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
||||||
version = "2.2.1"
|
version = "2.3.2"
|
||||||
license = { file = "LICENSE" }
|
license = { file = "LICENSE" }
|
||||||
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
|
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
|
||||||
|
|
||||||
|
|||||||
+884
-294
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
|||||||
|
// Snapshot Utilities
|
||||||
|
// Centralized snapshot fetching with ComfyUI version fallback
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current snapshot with ComfyUI version fallback
|
||||||
|
* If the snapshot response has null comfyui field, it will fetch the latest ComfyUI version
|
||||||
|
* and update the snapshot with the comfyui_hash
|
||||||
|
*
|
||||||
|
* @param {Function} getDataFn - Function that returns { apiKey, apiUrl } for ComfyUI version API calls
|
||||||
|
* @returns {Promise<Object>} - The snapshot data with comfyui field populated
|
||||||
|
*/
|
||||||
|
export async function fetchSnapshot(getDataFn = null) {
|
||||||
|
try {
|
||||||
|
// Fetch the current snapshot
|
||||||
|
const response = await fetch("/snapshot/get_current");
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Snapshot fetch failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const snapshot = await response.json();
|
||||||
|
|
||||||
|
// Check if comfyui field is null and we have getDataFn for fallback
|
||||||
|
if (snapshot.comfyui === null && getDataFn) {
|
||||||
|
console.log(
|
||||||
|
"ComfyUI version is null in snapshot, fetching latest version..."
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = getDataFn();
|
||||||
|
if (data && data.apiKey) {
|
||||||
|
const comfyuiVersionResponse = await fetch(
|
||||||
|
`/comfyui-deploy/comfyui-version?api_url=${encodeURIComponent(
|
||||||
|
data.apiUrl || "https://api.comfydeploy.com"
|
||||||
|
)}`,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${data.apiKey}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (comfyuiVersionResponse.ok) {
|
||||||
|
const versionData = await comfyuiVersionResponse.json();
|
||||||
|
if (versionData.comfyui_hash) {
|
||||||
|
console.log(
|
||||||
|
`Using ComfyUI hash from API: ${versionData.comfyui_hash}`
|
||||||
|
);
|
||||||
|
snapshot.comfyui = versionData.comfyui_hash;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn(
|
||||||
|
"Failed to fetch ComfyUI version from API:",
|
||||||
|
comfyuiVersionResponse.status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn("Error fetching ComfyUI version fallback:", error);
|
||||||
|
// Continue with original snapshot even if fallback fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching snapshot:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple snapshot fetch without ComfyUI version fallback
|
||||||
|
* Use this when you don't need the ComfyUI version fallback logic
|
||||||
|
*
|
||||||
|
* @returns {Promise<Object>} - The snapshot data as-is
|
||||||
|
*/
|
||||||
|
export async function fetchSnapshotSimple() {
|
||||||
|
const response = await fetch("/snapshot/get_current");
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Snapshot fetch failed: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
}
|
||||||
@@ -49,6 +49,7 @@ async function fetchWorkflows(getData, offset = 0, limit = 20, search = "") {
|
|||||||
|
|
||||||
function createWorkflowItem(workflow, getTimeAgo, getData) {
|
function createWorkflowItem(workflow, getTimeAgo, getData) {
|
||||||
const li = document.createElement("li");
|
const li = document.createElement("li");
|
||||||
|
let loadingToast = null;
|
||||||
li.style.cssText = `
|
li.style.cssText = `
|
||||||
border-bottom: 1px solid #444;
|
border-bottom: 1px solid #444;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
@@ -74,7 +75,7 @@ function createWorkflowItem(workflow, getTimeAgo, getData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Show loading toast
|
// Show loading toast
|
||||||
const loadingToast = window.app.extensionManager.toast.add({
|
loadingToast = window.app.extensionManager.toast.add({
|
||||||
severity: "info",
|
severity: "info",
|
||||||
summary: "Loading workflow...",
|
summary: "Loading workflow...",
|
||||||
detail: `Loading "${workflow.name}"`,
|
detail: `Loading "${workflow.name}"`,
|
||||||
@@ -108,6 +109,33 @@ function createWorkflowItem(workflow, getTimeAgo, getData) {
|
|||||||
// Load the workflow
|
// Load the workflow
|
||||||
window.app.loadGraphData(latestVersion.workflow);
|
window.app.loadGraphData(latestVersion.workflow);
|
||||||
|
|
||||||
|
// Wait a bit for the graph to fully load before checking for ComfyDeploy node
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||||
|
|
||||||
|
// Check if ComfyDeploy node exists, if not add it back
|
||||||
|
const graph = window.app.graph;
|
||||||
|
let deployMeta = graph.findNodesByType("ComfyDeploy");
|
||||||
|
|
||||||
|
if (deployMeta.length === 0) {
|
||||||
|
// Add ComfyDeploy node with workflow metadata
|
||||||
|
graph.beforeChange();
|
||||||
|
const node = LiteGraph.createNode("ComfyDeploy");
|
||||||
|
node.configure({
|
||||||
|
widgets_values: [
|
||||||
|
workflow.name, // workflow_name
|
||||||
|
workflow.id, // workflow_id
|
||||||
|
latestVersion.version, // version
|
||||||
|
],
|
||||||
|
});
|
||||||
|
node.pos = [0, 0];
|
||||||
|
graph.add(node);
|
||||||
|
graph.afterChange();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Added ComfyDeploy node with: name="${workflow.name}", id="${workflow.id}", version="${latestVersion.version}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Show success toast
|
// Show success toast
|
||||||
window.app.extensionManager.toast.add({
|
window.app.extensionManager.toast.add({
|
||||||
severity: "success",
|
severity: "success",
|
||||||
@@ -127,7 +155,9 @@ function createWorkflowItem(workflow, getTimeAgo, getData) {
|
|||||||
life: 5000,
|
life: 5000,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
loadingToast.close();
|
if (loadingToast) {
|
||||||
|
loadingToast.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -279,7 +309,7 @@ async function initializeWorkflowsList(element, getData, getTimeAgo) {
|
|||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
height: calc(100vh - 350px);
|
height: calc(100vh - 550px);
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
scrollbar-width: thin;
|
scrollbar-width: thin;
|
||||||
scrollbar-color: #666 transparent;
|
scrollbar-color: #666 transparent;
|
||||||
|
|||||||
Reference in New Issue
Block a user