comfy-ui-api-workflow 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. comfy_ui_api_workflow/__about__.py +1 -0
  2. comfy_ui_api_workflow/__init__.py +9 -0
  3. comfy_ui_api_workflow/_impl/__init__.py +0 -0
  4. comfy_ui_api_workflow/_impl/data_structure.py +182 -0
  5. comfy_ui_api_workflow/_impl/enums.py +13 -0
  6. comfy_ui_api_workflow/_impl/errors/__init__.py +3 -0
  7. comfy_ui_api_workflow/_impl/errors/data_structure.py +34 -0
  8. comfy_ui_api_workflow/_impl/errors/node.py +63 -0
  9. comfy_ui_api_workflow/_impl/errors/output.py +5 -0
  10. comfy_ui_api_workflow/_impl/errors/pipeline.py +14 -0
  11. comfy_ui_api_workflow/_impl/errors/workflow.py +13 -0
  12. comfy_ui_api_workflow/_impl/exceptions.py +22 -0
  13. comfy_ui_api_workflow/_impl/lazy.py +71 -0
  14. comfy_ui_api_workflow/_impl/nodes/__init__.py +3 -0
  15. comfy_ui_api_workflow/_impl/nodes/advanced/__init__.py +3 -0
  16. comfy_ui_api_workflow/_impl/nodes/advanced/debug.py +103 -0
  17. comfy_ui_api_workflow/_impl/nodes/advanced/guidance.py +281 -0
  18. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/__init__.py +29 -0
  19. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/clip.py +35 -0
  20. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/combine.py +108 -0
  21. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/cond_pair.py +203 -0
  22. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/cond_single.py +143 -0
  23. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/create.py +130 -0
  24. comfy_ui_api_workflow/_impl/nodes/advanced/hooks/scheduling.py +167 -0
  25. comfy_ui_api_workflow/_impl/nodes/advanced/multigpu.py +78 -0
  26. comfy_ui_api_workflow/_impl/nodes/audio.py +260 -0
  27. comfy_ui_api_workflow/_impl/nodes/base.py +214 -0
  28. comfy_ui_api_workflow/_impl/nodes/experimental/__init__.py +283 -0
  29. comfy_ui_api_workflow/_impl/nodes/experimental/attention_experiments.py +156 -0
  30. comfy_ui_api_workflow/_impl/nodes/experimental/stable_cascade.py +16 -0
  31. comfy_ui_api_workflow/_impl/nodes/image/__init__.py +601 -0
  32. comfy_ui_api_workflow/_impl/nodes/image/adjustments.py +46 -0
  33. comfy_ui_api_workflow/_impl/nodes/image/background_removal.py +16 -0
  34. comfy_ui_api_workflow/_impl/nodes/image/batch.py +236 -0
  35. comfy_ui_api_workflow/_impl/nodes/image/color.py +104 -0
  36. comfy_ui_api_workflow/_impl/nodes/image/compositing.py +128 -0
  37. comfy_ui_api_workflow/_impl/nodes/image/detection.py +689 -0
  38. comfy_ui_api_workflow/_impl/nodes/image/filters.py +215 -0
  39. comfy_ui_api_workflow/_impl/nodes/image/geometry_estimation.py +307 -0
  40. comfy_ui_api_workflow/_impl/nodes/image/mask.py +291 -0
  41. comfy_ui_api_workflow/_impl/nodes/image/post_processors.py +45 -0
  42. comfy_ui_api_workflow/_impl/nodes/image/pre_processors.py +13 -0
  43. comfy_ui_api_workflow/_impl/nodes/image/transform.py +385 -0
  44. comfy_ui_api_workflow/_impl/nodes/image/upscaling.py +188 -0
  45. comfy_ui_api_workflow/_impl/nodes/image/video.py +66 -0
  46. comfy_ui_api_workflow/_impl/nodes/model/__init__.py +3 -0
  47. comfy_ui_api_workflow/_impl/nodes/model/conditioning/__init__.py +395 -0
  48. comfy_ui_api_workflow/_impl/nodes/model/conditioning/ace.py +232 -0
  49. comfy_ui_api_workflow/_impl/nodes/model/conditioning/autoregressive.py +53 -0
  50. comfy_ui_api_workflow/_impl/nodes/model/conditioning/controlnet.py +153 -0
  51. comfy_ui_api_workflow/_impl/nodes/model/conditioning/cosmos.py +108 -0
  52. comfy_ui_api_workflow/_impl/nodes/model/conditioning/flux.py +121 -0
  53. comfy_ui_api_workflow/_impl/nodes/model/conditioning/gligen.py +57 -0
  54. comfy_ui_api_workflow/_impl/nodes/model/conditioning/hidream.py +31 -0
  55. comfy_ui_api_workflow/_impl/nodes/model/conditioning/hunyuan_3d.py +44 -0
  56. comfy_ui_api_workflow/_impl/nodes/model/conditioning/hunyuan_image.py +21 -0
  57. comfy_ui_api_workflow/_impl/nodes/model/conditioning/hunyuan_video.py +243 -0
  58. comfy_ui_api_workflow/_impl/nodes/model/conditioning/instructpix2pix.py +28 -0
  59. comfy_ui_api_workflow/_impl/nodes/model/conditioning/kandinsky.py +78 -0
  60. comfy_ui_api_workflow/_impl/nodes/model/conditioning/lotus.py +8 -0
  61. comfy_ui_api_workflow/_impl/nodes/model/conditioning/ltxv.py +504 -0
  62. comfy_ui_api_workflow/_impl/nodes/model/conditioning/lumina.py +39 -0
  63. comfy_ui_api_workflow/_impl/nodes/model/conditioning/minimax.py +110 -0
  64. comfy_ui_api_workflow/_impl/nodes/model/conditioning/minimax_music.py +53 -0
  65. comfy_ui_api_workflow/_impl/nodes/model/conditioning/photomaker.py +28 -0
  66. comfy_ui_api_workflow/_impl/nodes/model/conditioning/pixart.py +34 -0
  67. comfy_ui_api_workflow/_impl/nodes/model/conditioning/qwen_image.py +101 -0
  68. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_audio.py +34 -0
  69. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_cascade.py +16 -0
  70. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_diffusion.py +141 -0
  71. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_diffusion_upscaler.py +37 -0
  72. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_video.py +63 -0
  73. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_video_3d.py +53 -0
  74. comfy_ui_api_workflow/_impl/nodes/model/conditioning/stable_zero123.py +128 -0
  75. comfy_ui_api_workflow/_impl/nodes/model/conditioning/transform.py +341 -0
  76. comfy_ui_api_workflow/_impl/nodes/model/conditioning/trellis.py +168 -0
  77. comfy_ui_api_workflow/_impl/nodes/model/conditioning/triposplat.py +54 -0
  78. comfy_ui_api_workflow/_impl/nodes/model/conditioning/void.py +61 -0
  79. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/__init__.py +184 -0
  80. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/animate.py +255 -0
  81. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/camera.py +150 -0
  82. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/dancer.py +102 -0
  83. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/fun_control.py +129 -0
  84. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/fun_inpaint.py +68 -0
  85. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/humo.py +63 -0
  86. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/move.py +332 -0
  87. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/phantom_subject.py +58 -0
  88. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/scail.py +161 -0
  89. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/sound.py +128 -0
  90. comfy_ui_api_workflow/_impl/nodes/model/conditioning/wan/vace.py +73 -0
  91. comfy_ui_api_workflow/_impl/nodes/model/conditioning/yue2.py +189 -0
  92. comfy_ui_api_workflow/_impl/nodes/model/conditioning/z_image.py +59 -0
  93. comfy_ui_api_workflow/_impl/nodes/model/latent/__init__.py +510 -0
  94. comfy_ui_api_workflow/_impl/nodes/model/latent/ace.py +49 -0
  95. comfy_ui_api_workflow/_impl/nodes/model/latent/advanced/__init__.py +210 -0
  96. comfy_ui_api_workflow/_impl/nodes/model/latent/advanced/operations.py +113 -0
  97. comfy_ui_api_workflow/_impl/nodes/model/latent/autoregressive.py +36 -0
  98. comfy_ui_api_workflow/_impl/nodes/model/latent/batch.py +93 -0
  99. comfy_ui_api_workflow/_impl/nodes/model/latent/chroma_radiance.py +28 -0
  100. comfy_ui_api_workflow/_impl/nodes/model/latent/cosmos.py +36 -0
  101. comfy_ui_api_workflow/_impl/nodes/model/latent/flux.py +28 -0
  102. comfy_ui_api_workflow/_impl/nodes/model/latent/hidream.py +28 -0
  103. comfy_ui_api_workflow/_impl/nodes/model/latent/hunyuan_3d.py +57 -0
  104. comfy_ui_api_workflow/_impl/nodes/model/latent/hunyuan_image.py +28 -0
  105. comfy_ui_api_workflow/_impl/nodes/model/latent/hunyuan_video.py +130 -0
  106. comfy_ui_api_workflow/_impl/nodes/model/latent/ltxv.py +162 -0
  107. comfy_ui_api_workflow/_impl/nodes/model/latent/minimax.py +28 -0
  108. comfy_ui_api_workflow/_impl/nodes/model/latent/minimax_music.py +25 -0
  109. comfy_ui_api_workflow/_impl/nodes/model/latent/mochi.py +36 -0
  110. comfy_ui_api_workflow/_impl/nodes/model/latent/qwen.py +36 -0
  111. comfy_ui_api_workflow/_impl/nodes/model/latent/seedvr.py +16 -0
  112. comfy_ui_api_workflow/_impl/nodes/model/latent/stable_cascade.py +60 -0
  113. comfy_ui_api_workflow/_impl/nodes/model/latent/stable_diffusion.py +28 -0
  114. comfy_ui_api_workflow/_impl/nodes/model/latent/transform.py +92 -0
  115. comfy_ui_api_workflow/_impl/nodes/model/latent/trellis.py +91 -0
  116. comfy_ui_api_workflow/_impl/nodes/model/latent/triposplat.py +86 -0
  117. comfy_ui_api_workflow/_impl/nodes/model/latent/void.py +54 -0
  118. comfy_ui_api_workflow/_impl/nodes/model/latent/yue2.py +25 -0
  119. comfy_ui_api_workflow/_impl/nodes/model/loaders.py +820 -0
  120. comfy_ui_api_workflow/_impl/nodes/model/merging/__init__.py +290 -0
  121. comfy_ui_api_workflow/_impl/nodes/model/merging/model_specific.py +4370 -0
  122. comfy_ui_api_workflow/_impl/nodes/model/patch/__init__.py +463 -0
  123. comfy_ui_api_workflow/_impl/nodes/model/patch/anima.py +53 -0
  124. comfy_ui_api_workflow/_impl/nodes/model/patch/chroma_radiance.py +50 -0
  125. comfy_ui_api_workflow/_impl/nodes/model/patch/flux.py +63 -0
  126. comfy_ui_api_workflow/_impl/nodes/model/patch/hidream.py +72 -0
  127. comfy_ui_api_workflow/_impl/nodes/model/patch/ltxv.py +125 -0
  128. comfy_ui_api_workflow/_impl/nodes/model/patch/minimax.py +92 -0
  129. comfy_ui_api_workflow/_impl/nodes/model/patch/qwen.py +57 -0
  130. comfy_ui_api_workflow/_impl/nodes/model/patch/sensenova.py +21 -0
  131. comfy_ui_api_workflow/_impl/nodes/model/patch/stable_cascade.py +21 -0
  132. comfy_ui_api_workflow/_impl/nodes/model/patch/stable_diffusion.py +21 -0
  133. comfy_ui_api_workflow/_impl/nodes/model/patch/supir.py +57 -0
  134. comfy_ui_api_workflow/_impl/nodes/model/patch/unet.py +281 -0
  135. comfy_ui_api_workflow/_impl/nodes/model/patch/wan.py +143 -0
  136. comfy_ui_api_workflow/_impl/nodes/model/sampling/__init__.py +228 -0
  137. comfy_ui_api_workflow/_impl/nodes/model/sampling/custom.py +123 -0
  138. comfy_ui_api_workflow/_impl/nodes/model/sampling/guiders.py +252 -0
  139. comfy_ui_api_workflow/_impl/nodes/model/sampling/noise.py +52 -0
  140. comfy_ui_api_workflow/_impl/nodes/model/sampling/samplers.py +531 -0
  141. comfy_ui_api_workflow/_impl/nodes/model/sampling/schedulers.py +462 -0
  142. comfy_ui_api_workflow/_impl/nodes/model/sampling/sigmas.py +163 -0
  143. comfy_ui_api_workflow/_impl/nodes/model/training.py +249 -0
  144. comfy_ui_api_workflow/_impl/nodes/partner/__init__.py +3 -0
  145. comfy_ui_api_workflow/_impl/nodes/partner/audio/__init__.py +3 -0
  146. comfy_ui_api_workflow/_impl/nodes/partner/audio/elevenlabs.py +47 -0
  147. comfy_ui_api_workflow/_impl/nodes/partner/image/__init__.py +3 -0
  148. comfy_ui_api_workflow/_impl/nodes/partner/image/bytedance.py +16 -0
  149. comfy_ui_api_workflow/_impl/nodes/partner/image/krea.py +31 -0
  150. comfy_ui_api_workflow/_impl/nodes/partner/image/luma.py +27 -0
  151. comfy_ui_api_workflow/_impl/nodes/partner/image/recraft.py +220 -0
  152. comfy_ui_api_workflow/_impl/nodes/partner/text/__init__.py +3 -0
  153. comfy_ui_api_workflow/_impl/nodes/partner/text/gemini.py +25 -0
  154. comfy_ui_api_workflow/_impl/nodes/partner/text/openai.py +77 -0
  155. comfy_ui_api_workflow/_impl/nodes/partner/video/__init__.py +3 -0
  156. comfy_ui_api_workflow/_impl/nodes/partner/video/bytedance.py +16 -0
  157. comfy_ui_api_workflow/_impl/nodes/partner/video/pixverse.py +27 -0
  158. comfy_ui_api_workflow/_impl/nodes/text.py +506 -0
  159. comfy_ui_api_workflow/_impl/nodes/three_d/__init__.py +563 -0
  160. comfy_ui_api_workflow/_impl/nodes/three_d/mesh.py +183 -0
  161. comfy_ui_api_workflow/_impl/nodes/three_d/splat.py +262 -0
  162. comfy_ui_api_workflow/_impl/nodes/three_d/texturing.py +254 -0
  163. comfy_ui_api_workflow/_impl/nodes/utilities/__init__.py +404 -0
  164. comfy_ui_api_workflow/_impl/nodes/utilities/logic.py +44 -0
  165. comfy_ui_api_workflow/_impl/nodes/utilities/primitive.py +107 -0
  166. comfy_ui_api_workflow/_impl/nodes/validation.py +126 -0
  167. comfy_ui_api_workflow/_impl/nodes/video/__init__.py +440 -0
  168. comfy_ui_api_workflow/_impl/nodes/video/batch.py +45 -0
  169. comfy_ui_api_workflow/_impl/nodes/video/preprocessors.py +24 -0
  170. comfy_ui_api_workflow/_impl/nodes/video/transform.py +52 -0
  171. comfy_ui_api_workflow/_impl/pipeline.py +67 -0
  172. comfy_ui_api_workflow/_impl/workflow.py +539 -0
  173. comfy_ui_api_workflow-0.0.1.dist-info/METADATA +123 -0
  174. comfy_ui_api_workflow-0.0.1.dist-info/RECORD +176 -0
  175. comfy_ui_api_workflow-0.0.1.dist-info/WHEEL +4 -0
  176. comfy_ui_api_workflow-0.0.1.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1 @@
1
+ version = "0.0.1"
@@ -0,0 +1,9 @@
1
+ from ._impl import errors, nodes, exceptions, enums
2
+ from ._impl.workflow import Workflow
3
+ from ._impl.pipeline import Pipeline
4
+ from ._impl.lazy import public_modules
5
+
6
+
7
+ __all__ = ["errors", "nodes", "exceptions", "enums", "Workflow", "Pipeline"]
8
+
9
+ public_modules(__name__, ["errors", "nodes", "exceptions", "enums"])
File without changes
@@ -0,0 +1,182 @@
1
+ from typing import Any
2
+
3
+ from .errors.data_structure import CycleError
4
+
5
+
6
+ class IncrementalDirectedAcyclicGraphNode:
7
+ def __init__(self, data: Any) -> None:
8
+ self.data = data
9
+
10
+ def __repr__(self) -> str:
11
+ return f"{type(self).__name__}({self.data!r})"
12
+
13
+ def __str__(self) -> str:
14
+ return repr(self.data)
15
+
16
+
17
+ class IncrementalDirectedAcyclicGraph:
18
+ def __init__(self) -> None:
19
+ self._successors: dict[
20
+ IncrementalDirectedAcyclicGraphNode,
21
+ set[IncrementalDirectedAcyclicGraphNode],
22
+ ] = {}
23
+ self._predecessors: dict[
24
+ IncrementalDirectedAcyclicGraphNode,
25
+ set[IncrementalDirectedAcyclicGraphNode],
26
+ ] = {}
27
+ self._order: dict[IncrementalDirectedAcyclicGraphNode, int] = {}
28
+ self._next = 0
29
+
30
+ def add_node(self, node: IncrementalDirectedAcyclicGraphNode) -> None:
31
+ if node in self._order:
32
+ return
33
+ self._successors[node] = set()
34
+ self._predecessors[node] = set()
35
+ self._order[node] = self._next
36
+ self._next += 1
37
+
38
+ def remove_node(self, node: IncrementalDirectedAcyclicGraphNode) -> None:
39
+ if node not in self._order:
40
+ raise KeyError(node)
41
+ for successor in self._successors[node]:
42
+ self._predecessors[successor].discard(node)
43
+ for predecessor in self._predecessors[node]:
44
+ self._successors[predecessor].discard(node)
45
+ del self._successors[node], self._predecessors[node], self._order[node]
46
+
47
+ def insert_between(
48
+ self,
49
+ a: IncrementalDirectedAcyclicGraphNode,
50
+ b: IncrementalDirectedAcyclicGraphNode,
51
+ node: IncrementalDirectedAcyclicGraphNode,
52
+ remove_original: bool = True,
53
+ ) -> None:
54
+ new_nodes = [n for n in (a, b, node) if n not in self]
55
+ new_edge = node not in self._successors.get(a, ())
56
+ try:
57
+ self.add_node(node=node)
58
+ self.add_edge(x=a, y=node)
59
+ self.add_edge(x=node, y=b)
60
+ except CycleError:
61
+ if new_edge and a in self and node in self:
62
+ self.remove_edge(x=a, y=node)
63
+ for new_node in new_nodes:
64
+ if new_node in self:
65
+ self.remove_node(node=new_node)
66
+ raise
67
+ if remove_original:
68
+ self.remove_edge(x=a, y=b)
69
+
70
+ def add_edge(
71
+ self,
72
+ x: IncrementalDirectedAcyclicGraphNode,
73
+ y: IncrementalDirectedAcyclicGraphNode,
74
+ ) -> None:
75
+ if x == y:
76
+ raise CycleError([x, x])
77
+ self.add_node(node=x)
78
+ self.add_node(node=y)
79
+ if y in self._successors[x]:
80
+ return
81
+ order_x = self._order[x]
82
+ order_y = self._order[y]
83
+ if order_x < order_y:
84
+ self._link(x=x, y=y)
85
+ return
86
+ forward = self._forward_search(y=y, x=x, upper_bound=order_x)
87
+ backward = self._backward_search(x=x, lower_bound=order_y)
88
+ self._reorder(backward=backward, forward=forward)
89
+ self._link(x=x, y=y)
90
+
91
+ def remove_edge(
92
+ self,
93
+ x: IncrementalDirectedAcyclicGraphNode,
94
+ y: IncrementalDirectedAcyclicGraphNode,
95
+ ) -> None:
96
+ self._successors[x].discard(y)
97
+ self._predecessors[y].discard(x)
98
+
99
+ def topological_order(self) -> list[IncrementalDirectedAcyclicGraphNode]:
100
+ return sorted(self._order, key=self._order.__getitem__)
101
+
102
+ def dependencies(
103
+ self, node: IncrementalDirectedAcyclicGraphNode
104
+ ) -> set[IncrementalDirectedAcyclicGraphNode]:
105
+ return set(self._successors[node])
106
+
107
+ def dependents(
108
+ self, node: IncrementalDirectedAcyclicGraphNode
109
+ ) -> set[IncrementalDirectedAcyclicGraphNode]:
110
+ return set(self._predecessors[node])
111
+
112
+ def __contains__(self, node: object) -> bool:
113
+ return node in self._order
114
+
115
+ def __len__(self) -> int:
116
+ return len(self._order)
117
+
118
+ def _link(
119
+ self,
120
+ x: IncrementalDirectedAcyclicGraphNode,
121
+ y: IncrementalDirectedAcyclicGraphNode,
122
+ ) -> None:
123
+ self._successors[x].add(y)
124
+ self._predecessors[y].add(x)
125
+
126
+ def _forward_search(
127
+ self,
128
+ y: IncrementalDirectedAcyclicGraphNode,
129
+ x: IncrementalDirectedAcyclicGraphNode,
130
+ upper_bound: int,
131
+ ) -> list[IncrementalDirectedAcyclicGraphNode]:
132
+ parent: dict[
133
+ IncrementalDirectedAcyclicGraphNode,
134
+ IncrementalDirectedAcyclicGraphNode | None,
135
+ ] = {y: None}
136
+ stack = [y]
137
+ while stack:
138
+ node = stack.pop()
139
+ for successor in self._successors[node]:
140
+ if successor == x:
141
+ path = [node]
142
+ previous = parent[node]
143
+ while previous is not None:
144
+ path.append(previous)
145
+ previous = parent[previous]
146
+ path.reverse()
147
+ raise CycleError([x] + path + [x])
148
+ if successor not in parent and self._order[successor] < upper_bound:
149
+ parent[successor] = node
150
+ stack.append(successor)
151
+ return list(parent)
152
+
153
+ def _backward_search(
154
+ self,
155
+ x: IncrementalDirectedAcyclicGraphNode,
156
+ lower_bound: int,
157
+ ) -> list[IncrementalDirectedAcyclicGraphNode]:
158
+ visited = {x}
159
+ stack = [x]
160
+ while stack:
161
+ node = stack.pop()
162
+ for predecessor in self._predecessors[node]:
163
+ if (
164
+ predecessor not in visited
165
+ and self._order[predecessor] > lower_bound
166
+ ):
167
+ visited.add(predecessor)
168
+ stack.append(predecessor)
169
+ return list(visited)
170
+
171
+ def _reorder(
172
+ self,
173
+ backward: list[IncrementalDirectedAcyclicGraphNode],
174
+ forward: list[IncrementalDirectedAcyclicGraphNode],
175
+ ) -> None:
176
+ key = self._order.__getitem__
177
+ backward.sort(key=key)
178
+ forward.sort(key=key)
179
+ nodes = backward + forward
180
+ slots = sorted(self._order[node] for node in nodes)
181
+ for node, position in zip(nodes, slots):
182
+ self._order[node] = position
@@ -0,0 +1,13 @@
1
+ from enum import Enum
2
+
3
+
4
+ # Behaves like StrEnum, which only exists from Python 3.11 (the project supports 3.10).
5
+ class OutputTypes(str, Enum):
6
+ MODEL = "MODEL"
7
+ CONDITIONING = "CONDITIONING"
8
+ LATENT = "LATENT"
9
+ CLIP = "CLIP"
10
+ VAE = "VAE"
11
+
12
+ def __str__(self) -> str:
13
+ return self.value
@@ -0,0 +1,3 @@
1
+ from ..lazy import submodules_of
2
+
3
+ __getattr__ = submodules_of(__name__)
@@ -0,0 +1,34 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ..exceptions import DataStructureException
4
+
5
+ if TYPE_CHECKING:
6
+ from ..data_structure import IncrementalDirectedAcyclicGraphNode
7
+
8
+
9
+ class CycleError(DataStructureException):
10
+ def __init__(self, cycle: list["IncrementalDirectedAcyclicGraphNode"]) -> None:
11
+ super().__init__(cycle)
12
+ self.cycle = cycle
13
+
14
+ @property
15
+ def edge(
16
+ self,
17
+ ) -> tuple[
18
+ "IncrementalDirectedAcyclicGraphNode", "IncrementalDirectedAcyclicGraphNode"
19
+ ]:
20
+ return self.cycle[0], self.cycle[1]
21
+
22
+ @property
23
+ def path(self) -> list["IncrementalDirectedAcyclicGraphNode"]:
24
+ return self.cycle[1:]
25
+
26
+ def __str__(self) -> str:
27
+ source, target = self.edge
28
+ if source == target:
29
+ return f"Cannot add edge {source} -> {target}: a node cannot have an edge to itself."
30
+ return (
31
+ f"Cannot add edge {source} -> {target}: a path from {target} back to {source} already exists "
32
+ f"({' -> '.join(map(str, self.path))}), "
33
+ f"so the new edge would create the cycle {' -> '.join(map(str, self.cycle))}."
34
+ )
@@ -0,0 +1,63 @@
1
+ from ..exceptions import NodeException
2
+
3
+
4
+ def quote_names(names: list[str]) -> str:
5
+ quoted = [repr(name) for name in names]
6
+ if len(quoted) == 1:
7
+ return quoted[0]
8
+ return f"{', '.join(quoted[:-1])} and {quoted[-1]}"
9
+
10
+
11
+ def describe_dependents(dependents: list[str]) -> str:
12
+ if len(dependents) == 1:
13
+ return f"node {quote_names(dependents)} depends on it"
14
+ return f"nodes {quote_names(dependents)} depend on it"
15
+
16
+
17
+ class InvalidNodeParameterValue(NodeException):
18
+ pass
19
+
20
+
21
+ class NodeNotHaveOutput(NodeException):
22
+ pass
23
+
24
+
25
+ class NodeNotFoundError(NodeException):
26
+ pass
27
+
28
+
29
+ class NodeHasDependentsError(NodeException):
30
+ def __init__(self, node_id: str, dependents: list[str]) -> None:
31
+ super().__init__(node_id, dependents)
32
+ self.node_id = node_id
33
+ self.dependents = dependents
34
+
35
+ def __str__(self) -> str:
36
+ return (
37
+ f"Cannot remove node {self.node_id!r}: "
38
+ f"{describe_dependents(self.dependents)}."
39
+ )
40
+
41
+
42
+ class InvalidNodeChangeError(NodeException):
43
+ def __init__(self, node_id: str, name: str, reason: str) -> None:
44
+ super().__init__(node_id, name, reason)
45
+ self.node_id = node_id
46
+ self.name = name
47
+ self.reason = reason
48
+
49
+ def __str__(self) -> str:
50
+ return f"Cannot change {self.name!r} of node {self.node_id!r}: {self.reason}."
51
+
52
+
53
+ class InvalidLinkError(NodeException):
54
+ def __init__(self, node_id: str, name: str, reason: str) -> None:
55
+ super().__init__(node_id, name, reason)
56
+ self.node_id = node_id
57
+ self.name = name
58
+ self.reason = reason
59
+
60
+ def __str__(self) -> str:
61
+ return (
62
+ f"Cannot add node {self.node_id!r}: input {self.name!r} uses {self.reason}."
63
+ )
@@ -0,0 +1,5 @@
1
+ from ..exceptions import OutputException
2
+
3
+
4
+ class OutputNotFoundError(OutputException):
5
+ pass
@@ -0,0 +1,14 @@
1
+ from ..exceptions import PipelineException
2
+
3
+
4
+ class PipelineError(PipelineException):
5
+ def __init__(self, pipeline: str, reason: str) -> None:
6
+ super().__init__(pipeline, reason)
7
+ self.pipeline = pipeline
8
+ self.reason = reason
9
+
10
+ def __str__(self) -> str:
11
+ return (
12
+ f"Cannot add pipeline {self.pipeline!r}: {self.reason}. "
13
+ "No node of the pipeline was added."
14
+ )
@@ -0,0 +1,13 @@
1
+ from ..exceptions import WorkflowException
2
+
3
+
4
+ class InvalidWorkflowError(WorkflowException):
5
+ def __init__(self, node_id: str | None, reason: str) -> None:
6
+ super().__init__(node_id, reason)
7
+ self.node_id = node_id
8
+ self.reason = reason
9
+
10
+ def __str__(self) -> str:
11
+ if self.node_id is None:
12
+ return f"Cannot load the workflow: {self.reason}."
13
+ return f"Cannot load node {self.node_id!r}: {self.reason}."
@@ -0,0 +1,22 @@
1
+ class ComfyUIAPIWorkflowException(Exception):
2
+ pass
3
+
4
+
5
+ class DataStructureException(ComfyUIAPIWorkflowException):
6
+ pass
7
+
8
+
9
+ class WorkflowException(ComfyUIAPIWorkflowException):
10
+ pass
11
+
12
+
13
+ class NodeException(ComfyUIAPIWorkflowException):
14
+ pass
15
+
16
+
17
+ class PipelineException(ComfyUIAPIWorkflowException):
18
+ pass
19
+
20
+
21
+ class OutputException(ComfyUIAPIWorkflowException):
22
+ pass
@@ -0,0 +1,71 @@
1
+ import importlib
2
+ import importlib.abc
3
+ import importlib.util
4
+ import sys
5
+ from collections.abc import Callable, Iterable, Sequence
6
+ from importlib.machinery import ModuleSpec
7
+ from types import ModuleType
8
+
9
+
10
+ def submodules_of(package: str) -> Callable[[str], ModuleType]:
11
+ """A module __getattr__ that imports the package's submodules on first use."""
12
+
13
+ def __getattr__(name: str) -> ModuleType:
14
+ module = f"{package}.{name}"
15
+ try:
16
+ return importlib.import_module(module)
17
+ except ModuleNotFoundError as error:
18
+ # Only a missing submodule is a missing attribute; a broken import
19
+ # inside an existing module still raises as it is.
20
+ if error.name != module:
21
+ raise
22
+ raise AttributeError(
23
+ f"module {package!r} has no attribute {name!r}"
24
+ ) from None
25
+
26
+ return __getattr__
27
+
28
+
29
+ class _PublicModules(importlib.abc.MetaPathFinder, importlib.abc.Loader):
30
+ """Imports package.<name>... as the module package._impl.<name>... it stands for."""
31
+
32
+ def __init__(self, package: str, names: Iterable[str]) -> None:
33
+ self.prefixes = tuple(f"{package}.{name}" for name in names)
34
+ self.package = package
35
+
36
+ def find_spec(
37
+ self, fullname: str, path: Sequence[str] | None = None, target: object = None
38
+ ) -> ModuleSpec | None:
39
+ if not any(
40
+ fullname == prefix or fullname.startswith(f"{prefix}.")
41
+ for prefix in self.prefixes
42
+ ):
43
+ return None
44
+ private = f"{self.package}._impl{fullname.removeprefix(self.package)}"
45
+ spec = importlib.util.find_spec(private)
46
+ if spec is None:
47
+ return None
48
+ return importlib.util.spec_from_loader(
49
+ fullname, self, is_package=spec.submodule_search_locations is not None
50
+ )
51
+
52
+ def create_module(self, spec: ModuleSpec) -> ModuleType:
53
+ # The public name gets the module already loaded under _impl, so each
54
+ # class exists once whichever path imported it.
55
+ private = f"{self.package}._impl{spec.name.removeprefix(self.package)}"
56
+ return importlib.import_module(private)
57
+
58
+ def exec_module(self, module: ModuleType) -> None:
59
+ pass
60
+
61
+
62
+ def public_modules(package: str, names: Iterable[str]) -> None:
63
+ """Lets package.<name> be imported by its path, for each name the root exports."""
64
+ finder = _PublicModules(package, names)
65
+ if not any(
66
+ isinstance(other, _PublicModules) and other.package == package
67
+ for other in sys.meta_path
68
+ ):
69
+ # First in line, or the path finder would load a second copy of each
70
+ # submodule from the __path__ of the module the public name stands for.
71
+ sys.meta_path.insert(0, finder)
@@ -0,0 +1,3 @@
1
+ from ..lazy import submodules_of
2
+
3
+ __getattr__ = submodules_of(__name__)
@@ -0,0 +1,3 @@
1
+ from ...lazy import submodules_of
2
+
3
+ __getattr__ = submodules_of(__name__)
@@ -0,0 +1,103 @@
1
+ from typing import Literal, get_args
2
+
3
+ from ..base import BaseNode
4
+ from ..validation import validate_bool, validate_choice, validate_range
5
+
6
+ ModelComputeDtype_Dtypes = Literal["default", "fp32", "fp16", "bf16"]
7
+ MODELCOMPUTEDTYPE_DTYPES = frozenset(get_args(ModelComputeDtype_Dtypes))
8
+
9
+
10
+ class ModelComputeDtype(BaseNode):
11
+ __slots__ = ("model", "dtype")
12
+ CATEGORY = "advanced/debug"
13
+ OUTPUTS = {"MODEL": 0}
14
+ RETURN_TYPES = ("MODEL",)
15
+ INPUT_SOCKET_TYPES = {"model": "MODEL", "dtype": "V1 COMBO"}
16
+
17
+ def __init__(
18
+ self,
19
+ node_id: str,
20
+ model: tuple[str, int],
21
+ dtype: ModelComputeDtype_Dtypes | tuple[str, int],
22
+ ) -> None:
23
+ super().__init__(node_id=node_id)
24
+ self.model = model
25
+ self.dtype = dtype
26
+ self.__validate()
27
+
28
+ def __validate(self) -> None:
29
+ validate_choice("dtype", self.dtype, MODELCOMPUTEDTYPE_DTYPES)
30
+
31
+
32
+ class EasyCache(BaseNode):
33
+ __slots__ = ("model", "reuse_threshold", "start_percent", "end_percent", "verbose")
34
+ CATEGORY = "advanced/debug"
35
+ OUTPUTS = {"MODEL": 0}
36
+ RETURN_TYPES = ("MODEL",)
37
+ INPUT_SOCKET_TYPES = {
38
+ "model": "MODEL",
39
+ "reuse_threshold": "FLOAT",
40
+ "start_percent": "FLOAT",
41
+ "end_percent": "FLOAT",
42
+ "verbose": "BOOLEAN",
43
+ }
44
+
45
+ def __init__(
46
+ self,
47
+ node_id: str,
48
+ model: tuple[str, int],
49
+ reuse_threshold: float | tuple[str, int] = 0.2,
50
+ start_percent: float | tuple[str, int] = 0.15,
51
+ end_percent: float | tuple[str, int] = 0.95,
52
+ verbose: bool | tuple[str, int] = False,
53
+ ) -> None:
54
+ super().__init__(node_id=node_id)
55
+ self.model = model
56
+ self.reuse_threshold = reuse_threshold
57
+ self.start_percent = start_percent
58
+ self.end_percent = end_percent
59
+ self.verbose = verbose
60
+ self.__validate()
61
+
62
+ def __validate(self) -> None:
63
+ validate_range("reuse_threshold", self.reuse_threshold, 0.0, 3.0)
64
+ validate_range("start_percent", self.start_percent, 0.0, 1.0)
65
+ validate_range("end_percent", self.end_percent, 0.0, 1.0)
66
+ validate_bool("verbose", self.verbose)
67
+
68
+
69
+ class LazyCache(BaseNode):
70
+ __slots__ = ("model", "reuse_threshold", "start_percent", "end_percent", "verbose")
71
+ CATEGORY = "advanced/debug"
72
+ OUTPUTS = {"MODEL": 0}
73
+ RETURN_TYPES = ("MODEL",)
74
+ INPUT_SOCKET_TYPES = {
75
+ "model": "MODEL",
76
+ "reuse_threshold": "FLOAT",
77
+ "start_percent": "FLOAT",
78
+ "end_percent": "FLOAT",
79
+ "verbose": "BOOLEAN",
80
+ }
81
+
82
+ def __init__(
83
+ self,
84
+ node_id: str,
85
+ model: tuple[str, int],
86
+ reuse_threshold: float | tuple[str, int] = 0.2,
87
+ start_percent: float | tuple[str, int] = 0.15,
88
+ end_percent: float | tuple[str, int] = 0.95,
89
+ verbose: bool | tuple[str, int] = False,
90
+ ) -> None:
91
+ super().__init__(node_id=node_id)
92
+ self.model = model
93
+ self.reuse_threshold = reuse_threshold
94
+ self.start_percent = start_percent
95
+ self.end_percent = end_percent
96
+ self.verbose = verbose
97
+ self.__validate()
98
+
99
+ def __validate(self) -> None:
100
+ validate_range("reuse_threshold", self.reuse_threshold, 0.0, 3.0)
101
+ validate_range("start_percent", self.start_percent, 0.0, 1.0)
102
+ validate_range("end_percent", self.end_percent, 0.0, 1.0)
103
+ validate_bool("verbose", self.verbose)