techfreakworm commited on
Commit
15811ca
·
unverified ·
1 Parent(s): c397c07

feat(backend): ComfyUILibraryBackend skeleton + event dataclasses

Browse files
Files changed (2) hide show
  1. backend.py +86 -0
  2. tests/test_backend.py +13 -0
backend.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ComfyUI library-mode backend.
2
+
3
+ Single-process, single-implementation. The @spaces.GPU decorator is the only
4
+ divergence between local and HF Spaces deployment.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import os
9
+ import pathlib
10
+ import sys
11
+ from collections.abc import AsyncIterator
12
+ from dataclasses import dataclass, field
13
+ from typing import Any, Optional
14
+
15
+ import models
16
+
17
+
18
+ @dataclass
19
+ class DownloadEvent:
20
+ filename: str
21
+ mb_done: float
22
+ mb_total: float
23
+
24
+
25
+ @dataclass
26
+ class ProgressEvent:
27
+ stage: int
28
+ stage_label: str
29
+ step: int
30
+ total_steps: int
31
+
32
+
33
+ @dataclass
34
+ class OutputEvent:
35
+ video_path: str
36
+ audio_path: Optional[str] = None
37
+ meta: dict = field(default_factory=dict)
38
+
39
+
40
+ @dataclass
41
+ class ErrorEvent:
42
+ category: str # "oom" | "zerogpu_timeout" | "execution" | "interrupt" | "download"
43
+ message: str
44
+ stage: Optional[int] = None
45
+ traceback: str = ""
46
+
47
+
48
+ def _on_spaces() -> bool:
49
+ return bool(os.environ.get("SPACES_ZERO_GPU"))
50
+
51
+
52
+ def _comfy_dir() -> pathlib.Path:
53
+ if _on_spaces():
54
+ return pathlib.Path("/data/comfyui")
55
+ return pathlib.Path(__file__).parent / "comfyui"
56
+
57
+
58
+ class ComfyUILibraryBackend:
59
+ """Wraps PromptExecutor for in-process workflow execution."""
60
+
61
+ def __init__(self) -> None:
62
+ self._comfy_dir = _comfy_dir()
63
+ if not self._comfy_dir.exists():
64
+ raise RuntimeError(
65
+ f"ComfyUI not found at {self._comfy_dir}. "
66
+ f"Local: run `bash setup.sh`. Spaces: see app.py:_bootstrap()."
67
+ )
68
+ if str(self._comfy_dir) not in sys.path:
69
+ sys.path.insert(0, str(self._comfy_dir))
70
+
71
+ # Defer comfy imports until the path is set up.
72
+ # NOTE: ComfyUI ships PromptExecutor in the top-level `execution.py`
73
+ # module, NOT under `comfy.execution`. Same for `nodes`. Both must be
74
+ # imported AFTER the sys.path insert above.
75
+ import asyncio
76
+
77
+ import comfy.cli_args # noqa: F401 — side-effect: registers CLI flags
78
+ import execution # top-level module — provides PromptExecutor
79
+ import nodes # top-level module — provides init_extra_nodes (async)
80
+
81
+ # init_extra_nodes is an async function in modern ComfyUI; run it once.
82
+ asyncio.run(nodes.init_extra_nodes()) # discover custom_nodes/
83
+ self._executor = execution.PromptExecutor(server_instance=None)
84
+
85
+ def __repr__(self) -> str:
86
+ return f"ComfyUILibraryBackend(comfy_dir={self._comfy_dir!r})"
tests/test_backend.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Backend tests — most are smoke / structural since the real work is GPU."""
2
+ import backend
3
+
4
+
5
+ def test_backend_class_exists():
6
+ assert hasattr(backend, "ComfyUILibraryBackend")
7
+
8
+
9
+ def test_progress_event_dataclasses_exist():
10
+ assert hasattr(backend, "DownloadEvent")
11
+ assert hasattr(backend, "ProgressEvent")
12
+ assert hasattr(backend, "OutputEvent")
13
+ assert hasattr(backend, "ErrorEvent")