YanTianlong commited on
Commit
0d25132
·
1 Parent(s): 31a3afe

Add ComfyUI node timing for full workflow

Browse files
Files changed (1) hide show
  1. app.py +83 -6
app.py CHANGED
@@ -16,6 +16,7 @@ import gradio as gr
16
  import requests
17
  import spaces
18
  import torch
 
19
 
20
  from scripts.workflow_client import load_workflow, patch_voicegate_workflow
21
 
@@ -153,10 +154,10 @@ def write_sine_wav(filename: str, *, seconds: float = 1.0, frequency: float = 44
153
  return filename
154
 
155
 
156
- def submit_prompt(workflow: dict[str, Any]) -> str:
157
  response = requests.post(
158
  f"{COMFY_URL}/prompt",
159
- json={"prompt": workflow, "client_id": str(uuid.uuid4())},
160
  timeout=120,
161
  )
162
  if not response.ok:
@@ -164,6 +165,83 @@ def submit_prompt(workflow: dict[str, Any]) -> str:
164
  return response.json()["prompt_id"]
165
 
166
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  def wait_for_history(prompt_id: str, timeout: float = 1200) -> dict[str, Any]:
168
  deadline = time.time() + timeout
169
  while time.time() < deadline:
@@ -492,7 +570,7 @@ def asr_gpu_test(audio_path: str | None) -> str:
492
  return "\n".join(lines)
493
 
494
 
495
- @spaces.GPU(duration=800)
496
  def full_voicegate_gpu_test(audio_path: str | None, target_language: str) -> str:
497
  lines = gpu_status_lines()
498
  started = time.time()
@@ -507,9 +585,8 @@ def full_voicegate_gpu_test(audio_path: str | None, target_language: str) -> str
507
  lines.append(f"input_audio={audio_filename}")
508
  lines.append(f"target_language={target_language}")
509
  prompt = full_voicegate_workflow(audio_filename, prefix, target_language or "English")
510
- prompt_id = submit_prompt(prompt)
511
- lines.append(f"prompt_id={prompt_id}")
512
- history = wait_for_history(prompt_id, timeout=800)
513
  lines.extend(history_summary(history))
514
  lines.append(f"elapsed_sec={time.time() - started:.1f}")
515
  except Exception as exc:
 
16
  import requests
17
  import spaces
18
  import torch
19
+ import websocket
20
 
21
  from scripts.workflow_client import load_workflow, patch_voicegate_workflow
22
 
 
154
  return filename
155
 
156
 
157
+ def submit_prompt(workflow: dict[str, Any], *, client_id: str | None = None) -> str:
158
  response = requests.post(
159
  f"{COMFY_URL}/prompt",
160
+ json={"prompt": workflow, "client_id": client_id or str(uuid.uuid4())},
161
  timeout=120,
162
  )
163
  if not response.ok:
 
165
  return response.json()["prompt_id"]
166
 
167
 
168
+ def execute_prompt_with_timing(workflow: dict[str, Any], *, timeout: float) -> tuple[str, dict[str, Any], list[str]]:
169
+ client_id = str(uuid.uuid4())
170
+ websocket_url = f"ws://{COMFY_HOST}:{COMFY_PORT}/ws?clientId={client_id}"
171
+ ws = websocket.create_connection(websocket_url, timeout=30)
172
+ prompt_id = submit_prompt(workflow, client_id=client_id)
173
+ started = time.time()
174
+ deadline = started + timeout
175
+ current_node: str | None = None
176
+ current_started = 0.0
177
+ node_durations: dict[str, float] = {}
178
+ node_order: list[str] = []
179
+ event_lines = [f"prompt_id={prompt_id}", "node_timing=started"]
180
+
181
+ def close_current_node(now: float) -> None:
182
+ nonlocal current_node, current_started
183
+ if current_node is not None:
184
+ node_durations[current_node] = node_durations.get(current_node, 0.0) + max(0.0, now - current_started)
185
+ current_node = None
186
+ current_started = 0.0
187
+
188
+ try:
189
+ while time.time() < deadline:
190
+ ws.settimeout(max(1.0, min(10.0, deadline - time.time())))
191
+ try:
192
+ message = ws.recv()
193
+ except websocket.WebSocketTimeoutException:
194
+ continue
195
+ if isinstance(message, bytes):
196
+ message = message.decode("utf-8", errors="replace")
197
+ try:
198
+ payload = json.loads(message)
199
+ except json.JSONDecodeError:
200
+ continue
201
+ event_type = payload.get("type")
202
+ data = payload.get("data") or {}
203
+ if data.get("prompt_id") not in (None, prompt_id):
204
+ continue
205
+
206
+ now = time.time()
207
+ if event_type == "executing":
208
+ close_current_node(now)
209
+ node = data.get("node")
210
+ if node is None:
211
+ continue
212
+ current_node = str(node)
213
+ current_started = now
214
+ if current_node not in node_order:
215
+ node_order.append(current_node)
216
+ elif event_type == "execution_success":
217
+ close_current_node(now)
218
+ event_lines.append(f"websocket_elapsed_sec={now - started:.1f}")
219
+ break
220
+ elif event_type == "execution_error":
221
+ close_current_node(now)
222
+ event_lines.append("websocket_execution_error:")
223
+ event_lines.append(json.dumps(data, ensure_ascii=False, indent=2)[:4000])
224
+ break
225
+ else:
226
+ close_current_node(time.time())
227
+ raise TimeoutError(f"Timed out waiting for prompt {prompt_id}")
228
+ finally:
229
+ ws.close()
230
+
231
+ history = wait_for_history(prompt_id, timeout=30)
232
+ timed_nodes = sorted(
233
+ ((node_id, node_durations.get(node_id, 0.0)) for node_id in node_order),
234
+ key=lambda item: item[1],
235
+ reverse=True,
236
+ )
237
+ if timed_nodes:
238
+ event_lines.append("node_timing_top:")
239
+ for node_id, seconds in timed_nodes[:20]:
240
+ class_type = workflow.get(node_id, {}).get("class_type", "unknown")
241
+ event_lines.append(f"{node_id} {class_type}: {seconds:.1f}s")
242
+ return prompt_id, history, event_lines
243
+
244
+
245
  def wait_for_history(prompt_id: str, timeout: float = 1200) -> dict[str, Any]:
246
  deadline = time.time() + timeout
247
  while time.time() < deadline:
 
570
  return "\n".join(lines)
571
 
572
 
573
+ @spaces.GPU(duration=900)
574
  def full_voicegate_gpu_test(audio_path: str | None, target_language: str) -> str:
575
  lines = gpu_status_lines()
576
  started = time.time()
 
585
  lines.append(f"input_audio={audio_filename}")
586
  lines.append(f"target_language={target_language}")
587
  prompt = full_voicegate_workflow(audio_filename, prefix, target_language or "English")
588
+ _prompt_id, history, timing_lines = execute_prompt_with_timing(prompt, timeout=880)
589
+ lines.extend(timing_lines)
 
590
  lines.extend(history_summary(history))
591
  lines.append(f"elapsed_sec={time.time() - started:.1f}")
592
  except Exception as exc: