Compare commits

...
8 Commits
Author SHA1 Message Date
bennykok 3cb449ae42 fix 2025-09-02 19:02:08 -07:00
bennykok 6a888548ef fix 2025-09-02 18:49:38 -07:00
bennykok 99a953c8e7 fix 2025-09-02 18:28:37 -07:00
bennykok e0447f0865 fall back to beam token setup -> cd_token 2025-09-02 18:19:18 -07:00
bennykok 31c38ddb9b version bump 2025-08-29 15:48:42 -07:00
bennykok 2f2c63fa11 proxy interrupt request 2025-08-29 15:47:54 -07:00
BennyKok 7675c5ba90 Update README with demo link and recruitment note
Added a link to the latest local demo and a call for team members.
2025-08-29 13:03:39 -07:00
bennykok 7d65d23f84 fix: add null check in findIndex to prevent TypeError in context menu
- Fixed TypeError when accessing 'content' property of null/undefined array elements
- Removed empty options.push() that was adding undefined to the array
- Bump version to 2.3.3
2025-08-26 18:34:20 -07:00
5 changed files with 96 additions and 66 deletions
+2
View File
@@ -2,6 +2,8 @@
Open source comfyui deployment platform, a `vercel` for generative workflow infra. (serverless hosted gpu with vertical intergation with comfyui)
Check out our latest lcoal demo -> https://github.com/comfy-deploy/comfyui-api-comfydeploy
> [!NOTE]
> Im looking for creative hacker to join ComfyDeploy's core team! DM me on [twitter](https://x.com/BennyKokMusic)
+26 -16
View File
@@ -579,22 +579,6 @@ async def comfy_deploy_run(request):
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"]
@@ -606,6 +590,24 @@ async def comfy_deploy_run(request):
if len(parts) == 2 and parts[0].lower() == "bearer":
token = parts[1]
# We proxy the request to Comfy Deploy, this is a native run
if "is_native_run" in data:
async with aiohttp.ClientSession() as session:
data.pop("cd_token", None)
# 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": "Bearer " + token,
},
) as response:
data = await response.json()
# print(data)
# In older version, we use workflow_api, but this has inputs already swapped in nextjs frontend, which is tricky
workflow_api = data.get("workflow_api_raw")
# The prompt id generated from comfy deploy, can be None
@@ -672,6 +674,14 @@ async def comfy_deploy_run(request):
return web.json_response(res, status=status)
@server.PromptServer.instance.routes.post("/comfyui-deploy/interrupt")
async def interrupt_prompt(request):
data = await request.json()
prompt_id = data.get("prompt_id")
await update_run(prompt_id, Status.CANCELLED)
return web.json_response({"message": "Prompt interrupted"}, status=200)
async def stream_prompt(data, token):
# In older version, we use workflow_api, but this has inputs already swapped in nextjs frontend, which is tricky
workflow_api = data.get("workflow_api_raw")
+1
View File
@@ -18,6 +18,7 @@ class Status(Enum):
SUCCESS = "success"
FAILED = "failed"
UPLOADING = "uploading"
CANCELLED = "cancelled"
class StreamingPrompt(BaseModel):
+1 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-deploy"
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
version = "2.3.2"
version = "2.3.4"
license = { file = "LICENSE" }
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
+66 -49
View File
@@ -538,9 +538,7 @@ const ext = {
}
if (toInput.length) {
if (true) {
options.push();
let optionIndex = options.findIndex((o) => o.content === "Outputs");
let optionIndex = options.findIndex((o) => o && o.content === "Outputs");
if (optionIndex === -1) optionIndex = options.length;
else optionIndex++;
options.splice(
@@ -737,7 +735,7 @@ const ext = {
"string",
inputName,
/* value=*/ "",
() => {},
() => { },
{ serialize: true }
);
@@ -1720,14 +1718,12 @@ export class LoadingDialog extends ComfyDialog {
showLoading(title, message) {
this.show(`
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${
this.loadingIcon
}</h3>
${
message
? `<label style="max-width: 100%; white-space: pre-wrap; word-wrap: break-word;">${message}</label>`
: ""
}
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${this.loadingIcon
}</h3>
${message
? `<label style="max-width: 100%; white-space: pre-wrap; word-wrap: break-word;">${message}</label>`
: ""
}
</div>
`);
}
@@ -2312,12 +2308,10 @@ export class ConfigDialog extends ComfyDialog {
transition: all 0.2s ease;
box-sizing: border-box;
" onfocus="this.style.borderColor='#0ea5e9'; this.style.background='#252525'; this.style.boxShadow='0 0 0 3px rgba(14, 165, 233, 0.1)'" onblur="this.style.borderColor='#404040'; this.style.background='#1a1a1a'; this.style.boxShadow='none'" onmouseover="this.style.borderColor='#525252'" onmouseout="if(document.activeElement !== this) this.style.borderColor='#404040'">
<option value="cloud" ${
data.environment === "cloud" ? "selected" : ""
}>☁️ Cloud Deployment</option>
<option value="local" ${
data.environment === "local" ? "selected" : ""
}>🏠 Local Development</option>
<option value="cloud" ${data.environment === "cloud" ? "selected" : ""
}>☁️ Cloud Deployment</option>
<option value="local" ${data.environment === "local" ? "selected" : ""
}>🏠 Local Development</option>
</select>
<div style="
position: absolute;
@@ -2382,9 +2376,8 @@ export class ConfigDialog extends ComfyDialog {
text-transform: uppercase;
letter-spacing: 0.5px;
">API Key</label>
${
data.displayName
? `
${data.displayName
? `
<div style="
background: linear-gradient(135deg, #0ea5e9, #3b82f6);
color: white;
@@ -2403,8 +2396,8 @@ export class ConfigDialog extends ComfyDialog {
${data.displayName}
</div>
`
: ""
}
: ""
}
</div>
<input
id="apiKey"
@@ -2450,11 +2443,10 @@ export class ConfigDialog extends ComfyDialog {
<polyline points="10,17 15,12 10,7"/>
<line x1="15" y1="12" x2="3" y2="12"/>
</svg>
${
data.apiKey
? "Re-authenticate with ComfyDeploy"
: "Login with ComfyDeploy"
}
${data.apiKey
? "Re-authenticate with ComfyDeploy"
: "Login with ComfyDeploy"
}
</button>
</div>
</div>
@@ -2680,38 +2672,63 @@ const orginal_fetch_api = api.fetchApi;
api.fetchApi = async (route, options) => {
// console.log("Fetch API called with args:", route, options, ext.native_mode);
if (route.startsWith("/prompt") && ext.native_mode) {
const info = await getSelectedWorkflowInfo();
if (ext.native_mode) {
if (route.startsWith("/prompt")) {
const info = await getSelectedWorkflowInfo();
if (!info.workflow_id) {
console.log("No workflow id found, fallback to original fetch");
return await orginal_fetch_api.call(api, route, options);
}
if (!info.workflow_id) {
console.log("No workflow id found, fallback to original fetch");
return await orginal_fetch_api.call(api, route, options);
}
console.log("info", info);
if (info) {
console.log("info", info);
if (info) {
const body = JSON.parse(options.body);
const data = {
client_id: body.client_id,
workflow_api_json: body.prompt,
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,
gpu_event_id: info.gpu_event_id,
gpu: info.gpu,
cd_token: info.cd_token,
};
return await fetch("/comfyui-deploy/run", {
method: "POST",
headers: {
// Authorization: `Bearer ${info.cd_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
}
} else if (route.startsWith("/interrupt")) {
const info = await getSelectedWorkflowInfo();
if (!info.workflow_id) {
console.log("No workflow id found, fallback to original fetch");
return await orginal_fetch_api.call(api, route, options);
}
const body = JSON.parse(options.body);
const data = {
client_id: body.client_id,
workflow_api_json: body.prompt,
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,
gpu_event_id: info.gpu_event_id,
gpu: info.gpu,
prompt_id: body.prompt_id,
cd_token: info.cd_token,
};
return await fetch("/comfyui-deploy/run", {
const original_response = await orginal_fetch_api.call(api, route, options);
await fetch("/comfyui-deploy/interrupt", {
method: "POST",
headers: {
Authorization: `Bearer ${info.cd_token}`,
// Authorization: `Bearer ${info.cd_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
return original_response;
}
}