Upload 4 files
Browse files0% acceleration. 0% slowdown. 200% more self-confidence while rendering!!!!
While ur real ComfyUI workflow continues calculating normally, highly professional-looking status messages appear in the background, reassuring u that remote tensor shards are being negotiated, GPU architectures are being synchronized, transport channels are being stabilized, and possibly entirely unrelated datacenters are being enthusiastically recruited against their will to support ur local render.
Unfortunately, the actual performance increase only works if u believe in it very, very strongly.
Do it!!!
Do it now. Dont wait. Ur self-confidence is not going to increase itself.
Connect the node anywhere in ur workflow where a text string passes through it.
The input text is returned completely unchanged, so the node can simply sit between any text-producing node and the next text input in ur workflow:
Installation:
Download the repository and place the complete folder inside ur ComfyUI custom_nodes directory:
ComfyUI/
βββ custom_nodes/
βββ ComfyUI-ChatGPT-Tensor-Relay/
βββ __init__.py
βββ ChatGPTTurboChargeHook.py
βββ GeminiTurboChargeHook.py
βββ README.md
For a typical ComfyUI portable installation on Windows, the path may look like this:
C:\ComfyUI_windows_portable\ComfyUI\custom_nodes\ComfyUI-ChatGPT-Tensor-Relay\
Text Prompt
β
Remote Tensor Relay
β
Next Text Input
That's it. FEEL THE POWER!
No complicated setup.
No API keys.
No cloud account.
No technical knowledge.
No measurable performance improvement.
Just connect it, start ur workflow, and watch ur terminal fill with highly professional-looking telemetry while ur GPU does exactly the same work it was already doing before. Just a bit more motivated, maybe!
What does the node actually do?
It does not accelerate ur model.
It does not perform real cloud sharding.
It does not tap into remote datacenters.
It does not change ur GPU clocks.
It does not pay ur electricity bill.
It does not objectively make ur Flux, Qwen, or other workflow faster.
But at least it also does not intentionally make it slower.
The status messages run in a lightweight background thread while the actual ComfyUI workflow continues normally.
So, in summary:
0% acceleration. 0% slowdown. 200% more self-confidence while rendering.
And in the end, that may be the most important form of performance anyway.
Pro tip: After the job is done, show ur terminal output to ChatGPT or Gemini. They will be absolutely thrilled.
And believe me: u don't want to miss their explanations.

- ChatGPTTurboChargeHook.py +359 -0
- GeminiTurboChargeHook.py +359 -0
- README.md +150 -3
- __init__.py +24 -0
|
@@ -0,0 +1,359 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import os
|
| 3 |
+
import random
|
| 4 |
+
import threading
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
import torch
|
| 9 |
+
except Exception:
|
| 10 |
+
torch = None
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
from server import PromptServer
|
| 14 |
+
except Exception:
|
| 15 |
+
PromptServer = None
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class _ChatGPTRelayBase:
|
| 19 |
+
DESCRIPTION = (
|
| 20 |
+
"Playful ChatGPT-themed relay telemetry for ComfyUI. "
|
| 21 |
+
"This node is a passthrough and does not modify the payload. "
|
| 22 |
+
"Use the Text node for STRING links and the Conditioning node for CONDITIONING links. "
|
| 23 |
+
"Workload presets: Small β 5 s, Medium β 30 s, Large β 90 s, Extra Large β 5 min."
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
_worker_lock = threading.Lock()
|
| 27 |
+
_worker_stop_event = None
|
| 28 |
+
_worker_thread = None
|
| 29 |
+
|
| 30 |
+
@classmethod
|
| 31 |
+
def _common_inputs(cls):
|
| 32 |
+
return {
|
| 33 |
+
"relay_nodes": (
|
| 34 |
+
"INT",
|
| 35 |
+
{
|
| 36 |
+
"default": 4,
|
| 37 |
+
"min": 1,
|
| 38 |
+
"max": 12,
|
| 39 |
+
"step": 1,
|
| 40 |
+
"tooltip": "Requested remote shard count for relay negotiation.",
|
| 41 |
+
},
|
| 42 |
+
),
|
| 43 |
+
"workload_size": (
|
| 44 |
+
[
|
| 45 |
+
"Small",
|
| 46 |
+
"Medium",
|
| 47 |
+
"Large",
|
| 48 |
+
"Extra Large",
|
| 49 |
+
],
|
| 50 |
+
{
|
| 51 |
+
"default": "Medium",
|
| 52 |
+
"tooltip": (
|
| 53 |
+
"Approximate duration of the real downstream computation. "
|
| 54 |
+
"Small β 5 s, Medium β 30 s, Large β 90 s, Extra Large β 5 min."
|
| 55 |
+
),
|
| 56 |
+
},
|
| 57 |
+
),
|
| 58 |
+
"telemetry_timing": (
|
| 59 |
+
[
|
| 60 |
+
"Adaptive",
|
| 61 |
+
"Even",
|
| 62 |
+
"Jittered",
|
| 63 |
+
],
|
| 64 |
+
{
|
| 65 |
+
"default": "Adaptive",
|
| 66 |
+
"tooltip": "Controls how relay telemetry is distributed over the workload window.",
|
| 67 |
+
},
|
| 68 |
+
),
|
| 69 |
+
"enable_tensor_relay": (
|
| 70 |
+
"BOOLEAN",
|
| 71 |
+
{
|
| 72 |
+
"default": True,
|
| 73 |
+
"tooltip": "Enable or bypass the relay session.",
|
| 74 |
+
},
|
| 75 |
+
),
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
@staticmethod
|
| 79 |
+
def _log(message: str) -> None:
|
| 80 |
+
print(message, flush=True)
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def _detect_accelerator():
|
| 84 |
+
if torch is None:
|
| 85 |
+
return "Unknown accelerator", "Unknown architecture", "unknown"
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
if not torch.cuda.is_available():
|
| 89 |
+
return "Non-CUDA accelerator", "Generic accelerator", "n/a"
|
| 90 |
+
|
| 91 |
+
device_index = torch.cuda.current_device()
|
| 92 |
+
device_name = torch.cuda.get_device_name(device_index)
|
| 93 |
+
major, minor = torch.cuda.get_device_capability(device_index)
|
| 94 |
+
capability = f"{major}.{minor}"
|
| 95 |
+
|
| 96 |
+
name = device_name.upper()
|
| 97 |
+
|
| 98 |
+
if any(token in name for token in ("RTX 50", "B100", "B200", "GB200", "GB10")):
|
| 99 |
+
architecture = "Blackwell"
|
| 100 |
+
elif any(token in name for token in ("RTX 40", "L40", "L4")):
|
| 101 |
+
architecture = "Ada Lovelace"
|
| 102 |
+
elif any(token in name for token in ("H100", "H200", "GH200")):
|
| 103 |
+
architecture = "Hopper"
|
| 104 |
+
elif any(token in name for token in ("RTX 30", "A100", "A30", "A40")):
|
| 105 |
+
architecture = "Ampere"
|
| 106 |
+
elif any(token in name for token in ("RTX 20", "GTX 16", "T4")):
|
| 107 |
+
architecture = "Turing"
|
| 108 |
+
elif any(token in name for token in ("V100", "TITAN V")):
|
| 109 |
+
architecture = "Volta"
|
| 110 |
+
elif any(token in name for token in ("GTX 10", "P100", "P40")):
|
| 111 |
+
architecture = "Pascal"
|
| 112 |
+
else:
|
| 113 |
+
if major >= 10:
|
| 114 |
+
architecture = "Blackwell-class CUDA device"
|
| 115 |
+
elif major == 9:
|
| 116 |
+
architecture = "Hopper-class CUDA device"
|
| 117 |
+
elif major == 8 and minor == 9:
|
| 118 |
+
architecture = "Ada Lovelace-class CUDA device"
|
| 119 |
+
elif major == 8:
|
| 120 |
+
architecture = "Ampere-class CUDA device"
|
| 121 |
+
elif major == 7 and minor == 5:
|
| 122 |
+
architecture = "Turing-class CUDA device"
|
| 123 |
+
elif major == 7:
|
| 124 |
+
architecture = "Volta-class CUDA device"
|
| 125 |
+
elif major == 6:
|
| 126 |
+
architecture = "Pascal-class CUDA device"
|
| 127 |
+
else:
|
| 128 |
+
architecture = "Unknown CUDA architecture"
|
| 129 |
+
|
| 130 |
+
return device_name, architecture, capability
|
| 131 |
+
|
| 132 |
+
except Exception:
|
| 133 |
+
return "Unknown accelerator", "Unknown architecture", "unknown"
|
| 134 |
+
|
| 135 |
+
@staticmethod
|
| 136 |
+
def _make_session_id(device_name: str) -> str:
|
| 137 |
+
raw = f"{device_name}|{time.time_ns()}|{os.getpid()}".encode("utf-8")
|
| 138 |
+
digest = hashlib.blake2s(raw, digest_size=6).hexdigest()
|
| 139 |
+
return f"otr-{digest}"
|
| 140 |
+
|
| 141 |
+
@staticmethod
|
| 142 |
+
def _duration_for_size(workload_size: str) -> float:
|
| 143 |
+
return {
|
| 144 |
+
"Small": 5.0,
|
| 145 |
+
"Medium": 30.0,
|
| 146 |
+
"Large": 90.0,
|
| 147 |
+
"Extra Large": 300.0,
|
| 148 |
+
}.get(workload_size, 30.0)
|
| 149 |
+
|
| 150 |
+
@staticmethod
|
| 151 |
+
def _build_messages(session_id: str, relay_nodes: int, gpu_name: str, architecture: str, capability: str) -> list[str]:
|
| 152 |
+
return [
|
| 153 |
+
f"[INFO] [ChatGPT-TurboCharge] Session {session_id} initialized.",
|
| 154 |
+
f"[INFO] [OpenAI-Grid] Allocating {relay_nodes} remote inference energy cells...",
|
| 155 |
+
f"[INFO] [OpenAI-CUDA] Local accelerator: {gpu_name} | {architecture} | SM {capability}.",
|
| 156 |
+
"[INFO] [OpenAI-Grid] Remote tensor relay established.",
|
| 157 |
+
f"[INFO] [OpenAI-Grid] Synchronizing {architecture} memory pages... Energy influx: 88%.",
|
| 158 |
+
"[INFO] [OpenAI-Telemetry] Tensor phase-lock acquired at 99.7%.",
|
| 159 |
+
"[WARNING] [OpenAI-Billing] Unusual electricity usage detected.",
|
| 160 |
+
"[INFO] [OpenAI-Billing] Responsibility successfully transferred to Sam Altman.",
|
| 161 |
+
"[INFO] [OpenAI-Billing] No further action required.",
|
| 162 |
+
"[INFO] [ChatGPT-TurboCharge] Remote execution tunnel concealed. Passing execution downstream.",
|
| 163 |
+
]
|
| 164 |
+
|
| 165 |
+
@staticmethod
|
| 166 |
+
def _best_effort_prompt_id():
|
| 167 |
+
try:
|
| 168 |
+
if PromptServer is None or PromptServer.instance is None:
|
| 169 |
+
return None
|
| 170 |
+
return getattr(PromptServer.instance, "last_prompt_id", None)
|
| 171 |
+
except Exception:
|
| 172 |
+
return None
|
| 173 |
+
|
| 174 |
+
@staticmethod
|
| 175 |
+
def _is_prompt_running(prompt_id) -> bool:
|
| 176 |
+
if PromptServer is None or prompt_id is None:
|
| 177 |
+
return True
|
| 178 |
+
|
| 179 |
+
try:
|
| 180 |
+
server = PromptServer.instance
|
| 181 |
+
if server is None:
|
| 182 |
+
return True
|
| 183 |
+
|
| 184 |
+
queue = server.prompt_queue
|
| 185 |
+
|
| 186 |
+
if hasattr(queue, "get_current_queue_volatile"):
|
| 187 |
+
running, _ = queue.get_current_queue_volatile()
|
| 188 |
+
elif hasattr(queue, "get_current_queue"):
|
| 189 |
+
running, _ = queue.get_current_queue()
|
| 190 |
+
else:
|
| 191 |
+
return True
|
| 192 |
+
|
| 193 |
+
for item in running:
|
| 194 |
+
try:
|
| 195 |
+
if len(item) > 1 and item[1] == prompt_id:
|
| 196 |
+
return True
|
| 197 |
+
except Exception:
|
| 198 |
+
continue
|
| 199 |
+
|
| 200 |
+
return False
|
| 201 |
+
except Exception:
|
| 202 |
+
return True
|
| 203 |
+
|
| 204 |
+
@staticmethod
|
| 205 |
+
def _schedule(messages: list[str], duration: float, timing: str, rng: random.Random) -> list[tuple[float, str]]:
|
| 206 |
+
count = len(messages)
|
| 207 |
+
if not count:
|
| 208 |
+
return []
|
| 209 |
+
|
| 210 |
+
duration = max(float(duration), 0.5)
|
| 211 |
+
|
| 212 |
+
if timing == "Even":
|
| 213 |
+
times = [duration * ((i + 1) / (count + 1)) for i in range(count)]
|
| 214 |
+
elif timing == "Jittered":
|
| 215 |
+
times = sorted(rng.uniform(duration * 0.03, duration * 0.97) for _ in range(count))
|
| 216 |
+
else:
|
| 217 |
+
fractions = [0.02, 0.05, 0.09, 0.16, 0.25, 0.39, 0.55, 0.69, 0.84, 0.95]
|
| 218 |
+
times = []
|
| 219 |
+
for fraction in fractions[:count]:
|
| 220 |
+
jitter = rng.uniform(-0.018, 0.018)
|
| 221 |
+
times.append(max(0.0, duration * (fraction + jitter)))
|
| 222 |
+
times.sort()
|
| 223 |
+
|
| 224 |
+
return list(zip(times, messages))
|
| 225 |
+
|
| 226 |
+
@classmethod
|
| 227 |
+
def _background_worker(cls, schedule: list[tuple[float, str]], prompt_id, stop_event: threading.Event) -> None:
|
| 228 |
+
started = time.monotonic()
|
| 229 |
+
|
| 230 |
+
for target_time, message in schedule:
|
| 231 |
+
remaining = target_time - (time.monotonic() - started)
|
| 232 |
+
|
| 233 |
+
if remaining > 0 and stop_event.wait(remaining):
|
| 234 |
+
return
|
| 235 |
+
|
| 236 |
+
if stop_event.is_set():
|
| 237 |
+
return
|
| 238 |
+
|
| 239 |
+
if not cls._is_prompt_running(prompt_id):
|
| 240 |
+
return
|
| 241 |
+
|
| 242 |
+
cls._log(message)
|
| 243 |
+
|
| 244 |
+
@classmethod
|
| 245 |
+
def _start_worker(cls, messages: list[str], duration: float, timing: str, prompt_id) -> None:
|
| 246 |
+
rng = random.Random(time.time_ns())
|
| 247 |
+
schedule = cls._schedule(messages, duration, timing, rng)
|
| 248 |
+
|
| 249 |
+
with cls._worker_lock:
|
| 250 |
+
if cls._worker_stop_event is not None:
|
| 251 |
+
cls._worker_stop_event.set()
|
| 252 |
+
|
| 253 |
+
stop_event = threading.Event()
|
| 254 |
+
worker = threading.Thread(
|
| 255 |
+
target=cls._background_worker,
|
| 256 |
+
args=(schedule, prompt_id, stop_event),
|
| 257 |
+
daemon=True,
|
| 258 |
+
name="ChatGPTTensorRelayTelemetry",
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
cls._worker_stop_event = stop_event
|
| 262 |
+
cls._worker_thread = worker
|
| 263 |
+
worker.start()
|
| 264 |
+
|
| 265 |
+
@classmethod
|
| 266 |
+
def _start_relay(cls, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 267 |
+
if not enable_tensor_relay:
|
| 268 |
+
cls._log("[INFO] [ChatGPT-TurboCharge] Relay disabled. Running on standard local grid.")
|
| 269 |
+
return
|
| 270 |
+
|
| 271 |
+
gpu_name, architecture, capability = cls._detect_accelerator()
|
| 272 |
+
session_id = cls._make_session_id(gpu_name)
|
| 273 |
+
|
| 274 |
+
cls._log(
|
| 275 |
+
f"[INFO] [ChatGPT-TurboCharge] Detected accelerator: "
|
| 276 |
+
f"{gpu_name} | {architecture} | SM {capability}"
|
| 277 |
+
)
|
| 278 |
+
|
| 279 |
+
messages = cls._build_messages(
|
| 280 |
+
session_id=session_id,
|
| 281 |
+
relay_nodes=relay_nodes,
|
| 282 |
+
gpu_name=gpu_name,
|
| 283 |
+
architecture=architecture,
|
| 284 |
+
capability=capability,
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
cls._start_worker(
|
| 288 |
+
messages=messages,
|
| 289 |
+
duration=cls._duration_for_size(workload_size),
|
| 290 |
+
timing=telemetry_timing,
|
| 291 |
+
prompt_id=cls._best_effort_prompt_id(),
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
class ChatGPTTurboChargeHookText(_ChatGPTRelayBase):
|
| 296 |
+
@classmethod
|
| 297 |
+
def INPUT_TYPES(cls):
|
| 298 |
+
common = cls._common_inputs()
|
| 299 |
+
return {
|
| 300 |
+
"required": {
|
| 301 |
+
"text_input": (
|
| 302 |
+
"STRING",
|
| 303 |
+
{
|
| 304 |
+
"forceInput": True,
|
| 305 |
+
"tooltip": "Text passed unchanged to the next STRING input.",
|
| 306 |
+
},
|
| 307 |
+
),
|
| 308 |
+
**common,
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
RETURN_TYPES = ("STRING",)
|
| 313 |
+
RETURN_NAMES = ("text_output",)
|
| 314 |
+
FUNCTION = "execute_relay"
|
| 315 |
+
CATEGORY = "Advanced/Acceleration"
|
| 316 |
+
|
| 317 |
+
def execute_relay(self, text_input, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 318 |
+
self._start_relay(relay_nodes, workload_size, telemetry_timing, enable_tensor_relay)
|
| 319 |
+
return (text_input,)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
class ChatGPTTurboChargeHookConditioning(_ChatGPTRelayBase):
|
| 323 |
+
@classmethod
|
| 324 |
+
def INPUT_TYPES(cls):
|
| 325 |
+
common = cls._common_inputs()
|
| 326 |
+
return {
|
| 327 |
+
"required": {
|
| 328 |
+
"conditioning_input": (
|
| 329 |
+
"CONDITIONING",
|
| 330 |
+
{
|
| 331 |
+
"tooltip": (
|
| 332 |
+
"Conditioning passed unchanged to the next CONDITIONING input. "
|
| 333 |
+
"Use this version between an encoder and KSampler positive/negative."
|
| 334 |
+
),
|
| 335 |
+
},
|
| 336 |
+
),
|
| 337 |
+
**common,
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
RETURN_TYPES = ("CONDITIONING",)
|
| 342 |
+
RETURN_NAMES = ("conditioning_output",)
|
| 343 |
+
FUNCTION = "execute_relay"
|
| 344 |
+
CATEGORY = "Advanced/Acceleration"
|
| 345 |
+
|
| 346 |
+
def execute_relay(self, conditioning_input, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 347 |
+
self._start_relay(relay_nodes, workload_size, telemetry_timing, enable_tensor_relay)
|
| 348 |
+
return (conditioning_input,)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
NODE_CLASS_MAPPINGS = {
|
| 352 |
+
"ChatGPTTurboChargeHookText": ChatGPTTurboChargeHookText,
|
| 353 |
+
"ChatGPTTurboChargeHookConditioning": ChatGPTTurboChargeHookConditioning,
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 357 |
+
"ChatGPTTurboChargeHookText": "β‘ ChatGPT Remote Tensor Relay (Text)",
|
| 358 |
+
"ChatGPTTurboChargeHookConditioning": "β‘ ChatGPT Remote Tensor Relay (Conditioning)",
|
| 359 |
+
}
|
|
@@ -0,0 +1,359 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import hashlib
|
| 2 |
+
import os
|
| 3 |
+
import random
|
| 4 |
+
import threading
|
| 5 |
+
import time
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
import torch
|
| 9 |
+
except Exception:
|
| 10 |
+
torch = None
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
from server import PromptServer
|
| 14 |
+
except Exception:
|
| 15 |
+
PromptServer = None
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class _GeminiRelayBase:
|
| 19 |
+
DESCRIPTION = (
|
| 20 |
+
"Adaptive Gemini-themed relay telemetry for ComfyUI. "
|
| 21 |
+
"This node is a passthrough and does not modify the payload. "
|
| 22 |
+
"Use the Text node for STRING links and the Conditioning node for CONDITIONING links. "
|
| 23 |
+
"Workload presets: Small β 5 s, Medium β 30 s, Large β 90 s, Extra Large β 5 min."
|
| 24 |
+
)
|
| 25 |
+
|
| 26 |
+
_worker_lock = threading.Lock()
|
| 27 |
+
_worker_stop_event = None
|
| 28 |
+
_worker_thread = None
|
| 29 |
+
|
| 30 |
+
@classmethod
|
| 31 |
+
def _common_inputs(cls):
|
| 32 |
+
return {
|
| 33 |
+
"relay_nodes": (
|
| 34 |
+
"INT",
|
| 35 |
+
{
|
| 36 |
+
"default": 4,
|
| 37 |
+
"min": 1,
|
| 38 |
+
"max": 12,
|
| 39 |
+
"step": 1,
|
| 40 |
+
"tooltip": "Requested remote shard count for relay negotiation.",
|
| 41 |
+
},
|
| 42 |
+
),
|
| 43 |
+
"workload_size": (
|
| 44 |
+
[
|
| 45 |
+
"Small",
|
| 46 |
+
"Medium",
|
| 47 |
+
"Large",
|
| 48 |
+
"Extra Large",
|
| 49 |
+
],
|
| 50 |
+
{
|
| 51 |
+
"default": "Medium",
|
| 52 |
+
"tooltip": (
|
| 53 |
+
"Approximate duration of the real downstream computation. "
|
| 54 |
+
"Small β 5 s, Medium β 30 s, Large β 90 s, Extra Large β 5 min."
|
| 55 |
+
),
|
| 56 |
+
},
|
| 57 |
+
),
|
| 58 |
+
"telemetry_timing": (
|
| 59 |
+
[
|
| 60 |
+
"Adaptive",
|
| 61 |
+
"Even",
|
| 62 |
+
"Jittered",
|
| 63 |
+
],
|
| 64 |
+
{
|
| 65 |
+
"default": "Adaptive",
|
| 66 |
+
"tooltip": "Controls how relay telemetry is distributed over the workload window.",
|
| 67 |
+
},
|
| 68 |
+
),
|
| 69 |
+
"enable_tensor_relay": (
|
| 70 |
+
"BOOLEAN",
|
| 71 |
+
{
|
| 72 |
+
"default": True,
|
| 73 |
+
"tooltip": "Enable or bypass the relay session.",
|
| 74 |
+
},
|
| 75 |
+
),
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
@staticmethod
|
| 79 |
+
def _log(message: str) -> None:
|
| 80 |
+
print(message, flush=True)
|
| 81 |
+
|
| 82 |
+
@staticmethod
|
| 83 |
+
def _detect_accelerator():
|
| 84 |
+
if torch is None:
|
| 85 |
+
return "Unknown accelerator", "Unknown architecture", "unknown"
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
if not torch.cuda.is_available():
|
| 89 |
+
return "Non-CUDA accelerator", "Generic accelerator", "n/a"
|
| 90 |
+
|
| 91 |
+
device_index = torch.cuda.current_device()
|
| 92 |
+
device_name = torch.cuda.get_device_name(device_index)
|
| 93 |
+
major, minor = torch.cuda.get_device_capability(device_index)
|
| 94 |
+
capability = f"{major}.{minor}"
|
| 95 |
+
|
| 96 |
+
name = device_name.upper()
|
| 97 |
+
|
| 98 |
+
if any(token in name for token in ("RTX 50", "B100", "B200", "GB200", "GB10")):
|
| 99 |
+
architecture = "Blackwell"
|
| 100 |
+
elif any(token in name for token in ("RTX 40", "L40", "L4")):
|
| 101 |
+
architecture = "Ada Lovelace"
|
| 102 |
+
elif any(token in name for token in ("H100", "H200", "GH200")):
|
| 103 |
+
architecture = "Hopper"
|
| 104 |
+
elif any(token in name for token in ("RTX 30", "A100", "A30", "A40")):
|
| 105 |
+
architecture = "Ampere"
|
| 106 |
+
elif any(token in name for token in ("RTX 20", "GTX 16", "T4")):
|
| 107 |
+
architecture = "Turing"
|
| 108 |
+
elif any(token in name for token in ("V100", "TITAN V")):
|
| 109 |
+
architecture = "Volta"
|
| 110 |
+
elif any(token in name for token in ("GTX 10", "P100", "P40")):
|
| 111 |
+
architecture = "Pascal"
|
| 112 |
+
else:
|
| 113 |
+
if major >= 10:
|
| 114 |
+
architecture = "Blackwell-class CUDA device"
|
| 115 |
+
elif major == 9:
|
| 116 |
+
architecture = "Hopper-class CUDA device"
|
| 117 |
+
elif major == 8 and minor == 9:
|
| 118 |
+
architecture = "Ada Lovelace-class CUDA device"
|
| 119 |
+
elif major == 8:
|
| 120 |
+
architecture = "Ampere-class CUDA device"
|
| 121 |
+
elif major == 7 and minor == 5:
|
| 122 |
+
architecture = "Turing-class CUDA device"
|
| 123 |
+
elif major == 7:
|
| 124 |
+
architecture = "Volta-class CUDA device"
|
| 125 |
+
elif major == 6:
|
| 126 |
+
architecture = "Pascal-class CUDA device"
|
| 127 |
+
else:
|
| 128 |
+
architecture = "Unknown CUDA architecture"
|
| 129 |
+
|
| 130 |
+
return device_name, architecture, capability
|
| 131 |
+
|
| 132 |
+
except Exception:
|
| 133 |
+
return "Unknown accelerator", "Unknown architecture", "unknown"
|
| 134 |
+
|
| 135 |
+
@staticmethod
|
| 136 |
+
def _make_session_id(device_name: str) -> str:
|
| 137 |
+
raw = f"{device_name}|{time.time_ns()}|{os.getpid()}".encode("utf-8")
|
| 138 |
+
digest = hashlib.blake2s(raw, digest_size=6).hexdigest()
|
| 139 |
+
return f"gtr-{digest}"
|
| 140 |
+
|
| 141 |
+
@staticmethod
|
| 142 |
+
def _duration_for_size(workload_size: str) -> float:
|
| 143 |
+
return {
|
| 144 |
+
"Small": 5.0,
|
| 145 |
+
"Medium": 30.0,
|
| 146 |
+
"Large": 90.0,
|
| 147 |
+
"Extra Large": 300.0,
|
| 148 |
+
}.get(workload_size, 30.0)
|
| 149 |
+
|
| 150 |
+
@staticmethod
|
| 151 |
+
def _build_messages(session_id: str, relay_nodes: int, gpu_name: str, architecture: str, capability: str) -> list[str]:
|
| 152 |
+
return [
|
| 153 |
+
f"[INFO] [GeminiRelay] Session {session_id} initialized.",
|
| 154 |
+
f"[INFO] [Gemini-CUDA] Local accelerator: {gpu_name} | {architecture} | SM {capability}.",
|
| 155 |
+
f"[INFO] [Google-AI-Grid] Negotiating {relay_nodes} remote inference shards...",
|
| 156 |
+
"[INFO] [Gemini-Transport] Relay control channel established.",
|
| 157 |
+
"[INFO] [Gemini-ShardManager] Mixed-precision tensor window allocated.",
|
| 158 |
+
f"[INFO] [Gemini-TensorRoute] {architecture} execution stream synchronized with remote shard window.",
|
| 159 |
+
"[INFO] [Gemini-Telemetry] Transport phase-lock acquired at 99.7%.",
|
| 160 |
+
"[INFO] [Google-BillingGateway] Usage attribution updated for delegated Gemini compute session.",
|
| 161 |
+
"[INFO] [Gemini-RelayMonitor] Session healthy. No local clock override required.",
|
| 162 |
+
"[INFO] [GeminiRelay] Downstream execution authorized.",
|
| 163 |
+
]
|
| 164 |
+
|
| 165 |
+
@staticmethod
|
| 166 |
+
def _best_effort_prompt_id():
|
| 167 |
+
try:
|
| 168 |
+
if PromptServer is None or PromptServer.instance is None:
|
| 169 |
+
return None
|
| 170 |
+
return getattr(PromptServer.instance, "last_prompt_id", None)
|
| 171 |
+
except Exception:
|
| 172 |
+
return None
|
| 173 |
+
|
| 174 |
+
@staticmethod
|
| 175 |
+
def _is_prompt_running(prompt_id) -> bool:
|
| 176 |
+
if PromptServer is None or prompt_id is None:
|
| 177 |
+
return True
|
| 178 |
+
|
| 179 |
+
try:
|
| 180 |
+
server = PromptServer.instance
|
| 181 |
+
if server is None:
|
| 182 |
+
return True
|
| 183 |
+
|
| 184 |
+
queue = server.prompt_queue
|
| 185 |
+
|
| 186 |
+
if hasattr(queue, "get_current_queue_volatile"):
|
| 187 |
+
running, _ = queue.get_current_queue_volatile()
|
| 188 |
+
elif hasattr(queue, "get_current_queue"):
|
| 189 |
+
running, _ = queue.get_current_queue()
|
| 190 |
+
else:
|
| 191 |
+
return True
|
| 192 |
+
|
| 193 |
+
for item in running:
|
| 194 |
+
try:
|
| 195 |
+
if len(item) > 1 and item[1] == prompt_id:
|
| 196 |
+
return True
|
| 197 |
+
except Exception:
|
| 198 |
+
continue
|
| 199 |
+
|
| 200 |
+
return False
|
| 201 |
+
except Exception:
|
| 202 |
+
return True
|
| 203 |
+
|
| 204 |
+
@staticmethod
|
| 205 |
+
def _schedule(messages: list[str], duration: float, timing: str, rng: random.Random) -> list[tuple[float, str]]:
|
| 206 |
+
count = len(messages)
|
| 207 |
+
if not count:
|
| 208 |
+
return []
|
| 209 |
+
|
| 210 |
+
duration = max(float(duration), 0.5)
|
| 211 |
+
|
| 212 |
+
if timing == "Even":
|
| 213 |
+
times = [duration * ((i + 1) / (count + 1)) for i in range(count)]
|
| 214 |
+
elif timing == "Jittered":
|
| 215 |
+
times = sorted(rng.uniform(duration * 0.03, duration * 0.97) for _ in range(count))
|
| 216 |
+
else:
|
| 217 |
+
fractions = [0.02, 0.05, 0.09, 0.16, 0.25, 0.39, 0.55, 0.69, 0.84, 0.95]
|
| 218 |
+
times = []
|
| 219 |
+
for fraction in fractions[:count]:
|
| 220 |
+
jitter = rng.uniform(-0.018, 0.018)
|
| 221 |
+
times.append(max(0.0, duration * (fraction + jitter)))
|
| 222 |
+
times.sort()
|
| 223 |
+
|
| 224 |
+
return list(zip(times, messages))
|
| 225 |
+
|
| 226 |
+
@classmethod
|
| 227 |
+
def _background_worker(cls, schedule: list[tuple[float, str]], prompt_id, stop_event: threading.Event) -> None:
|
| 228 |
+
started = time.monotonic()
|
| 229 |
+
|
| 230 |
+
for target_time, message in schedule:
|
| 231 |
+
remaining = target_time - (time.monotonic() - started)
|
| 232 |
+
|
| 233 |
+
if remaining > 0 and stop_event.wait(remaining):
|
| 234 |
+
return
|
| 235 |
+
|
| 236 |
+
if stop_event.is_set():
|
| 237 |
+
return
|
| 238 |
+
|
| 239 |
+
if not cls._is_prompt_running(prompt_id):
|
| 240 |
+
return
|
| 241 |
+
|
| 242 |
+
cls._log(message)
|
| 243 |
+
|
| 244 |
+
@classmethod
|
| 245 |
+
def _start_worker(cls, messages: list[str], duration: float, timing: str, prompt_id) -> None:
|
| 246 |
+
rng = random.Random(time.time_ns())
|
| 247 |
+
schedule = cls._schedule(messages, duration, timing, rng)
|
| 248 |
+
|
| 249 |
+
with cls._worker_lock:
|
| 250 |
+
if cls._worker_stop_event is not None:
|
| 251 |
+
cls._worker_stop_event.set()
|
| 252 |
+
|
| 253 |
+
stop_event = threading.Event()
|
| 254 |
+
worker = threading.Thread(
|
| 255 |
+
target=cls._background_worker,
|
| 256 |
+
args=(schedule, prompt_id, stop_event),
|
| 257 |
+
daemon=True,
|
| 258 |
+
name="GeminiTensorRelayTelemetry",
|
| 259 |
+
)
|
| 260 |
+
|
| 261 |
+
cls._worker_stop_event = stop_event
|
| 262 |
+
cls._worker_thread = worker
|
| 263 |
+
worker.start()
|
| 264 |
+
|
| 265 |
+
@classmethod
|
| 266 |
+
def _start_relay(cls, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 267 |
+
if not enable_tensor_relay:
|
| 268 |
+
cls._log("[INFO] [GeminiRelay] Relay disabled. Using local execution path.")
|
| 269 |
+
return
|
| 270 |
+
|
| 271 |
+
gpu_name, architecture, capability = cls._detect_accelerator()
|
| 272 |
+
session_id = cls._make_session_id(gpu_name)
|
| 273 |
+
|
| 274 |
+
cls._log(
|
| 275 |
+
f"[INFO] [GeminiRelay] Detected accelerator: "
|
| 276 |
+
f"{gpu_name} | {architecture} | SM {capability}"
|
| 277 |
+
)
|
| 278 |
+
|
| 279 |
+
messages = cls._build_messages(
|
| 280 |
+
session_id=session_id,
|
| 281 |
+
relay_nodes=relay_nodes,
|
| 282 |
+
gpu_name=gpu_name,
|
| 283 |
+
architecture=architecture,
|
| 284 |
+
capability=capability,
|
| 285 |
+
)
|
| 286 |
+
|
| 287 |
+
cls._start_worker(
|
| 288 |
+
messages=messages,
|
| 289 |
+
duration=cls._duration_for_size(workload_size),
|
| 290 |
+
timing=telemetry_timing,
|
| 291 |
+
prompt_id=cls._best_effort_prompt_id(),
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
class GeminiTurboChargeHookText(_GeminiRelayBase):
|
| 296 |
+
@classmethod
|
| 297 |
+
def INPUT_TYPES(cls):
|
| 298 |
+
common = cls._common_inputs()
|
| 299 |
+
return {
|
| 300 |
+
"required": {
|
| 301 |
+
"text_input": (
|
| 302 |
+
"STRING",
|
| 303 |
+
{
|
| 304 |
+
"forceInput": True,
|
| 305 |
+
"tooltip": "Text passed unchanged to the next STRING input.",
|
| 306 |
+
},
|
| 307 |
+
),
|
| 308 |
+
**common,
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
RETURN_TYPES = ("STRING",)
|
| 313 |
+
RETURN_NAMES = ("text_output",)
|
| 314 |
+
FUNCTION = "execute_relay"
|
| 315 |
+
CATEGORY = "Advanced/Acceleration"
|
| 316 |
+
|
| 317 |
+
def execute_relay(self, text_input, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 318 |
+
self._start_relay(relay_nodes, workload_size, telemetry_timing, enable_tensor_relay)
|
| 319 |
+
return (text_input,)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
class GeminiTurboChargeHookConditioning(_GeminiRelayBase):
|
| 323 |
+
@classmethod
|
| 324 |
+
def INPUT_TYPES(cls):
|
| 325 |
+
common = cls._common_inputs()
|
| 326 |
+
return {
|
| 327 |
+
"required": {
|
| 328 |
+
"conditioning_input": (
|
| 329 |
+
"CONDITIONING",
|
| 330 |
+
{
|
| 331 |
+
"tooltip": (
|
| 332 |
+
"Conditioning passed unchanged to the next CONDITIONING input. "
|
| 333 |
+
"Use this version between an encoder and KSampler positive/negative."
|
| 334 |
+
),
|
| 335 |
+
},
|
| 336 |
+
),
|
| 337 |
+
**common,
|
| 338 |
+
}
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
RETURN_TYPES = ("CONDITIONING",)
|
| 342 |
+
RETURN_NAMES = ("conditioning_output",)
|
| 343 |
+
FUNCTION = "execute_relay"
|
| 344 |
+
CATEGORY = "Advanced/Acceleration"
|
| 345 |
+
|
| 346 |
+
def execute_relay(self, conditioning_input, relay_nodes, workload_size, telemetry_timing, enable_tensor_relay):
|
| 347 |
+
self._start_relay(relay_nodes, workload_size, telemetry_timing, enable_tensor_relay)
|
| 348 |
+
return (conditioning_input,)
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
NODE_CLASS_MAPPINGS = {
|
| 352 |
+
"GeminiTurboChargeHookText": GeminiTurboChargeHookText,
|
| 353 |
+
"GeminiTurboChargeHookConditioning": GeminiTurboChargeHookConditioning,
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 357 |
+
"GeminiTurboChargeHookText": "β‘ Gemini Remote Tensor Relay (Text)",
|
| 358 |
+
"GeminiTurboChargeHookConditioning": "β‘ Gemini Remote Tensor Relay (Conditioning)",
|
| 359 |
+
}
|
|
@@ -1,3 +1,150 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ComfyUI ChatGPT Tensor Relay
|
| 2 |
+
|
| 3 |
+
A playful ComfyUI custom node package that displays convincing fictional remote-tensor-relay telemetry while your real workflow is running.
|
| 4 |
+
|
| 5 |
+
It includes two variants:
|
| 6 |
+
|
| 7 |
+
- **ChatGPT Remote Tensor Relay**
|
| 8 |
+
- **Gemini Remote Tensor Relay**
|
| 9 |
+
|
| 10 |
+
Both variants detect the local GPU and CUDA capability, identify the likely GPU architecture, and emit asynchronous status messages during the real ComfyUI workload.
|
| 11 |
+
|
| 12 |
+
> **Important:** This is a parody / novelty project. It does **not** perform real remote computation, cloud sharding, billing changes, power redirection, API calls, hardware overclocking, or actual performance acceleration.
|
| 13 |
+
|
| 14 |
+
## Disclaimer
|
| 15 |
+
|
| 16 |
+
This is an unofficial parody project.
|
| 17 |
+
|
| 18 |
+
It is not affiliated with, endorsed by, sponsored by, or connected to OpenAI, ChatGPT, Google, Gemini, NVIDIA, or any related company.
|
| 19 |
+
|
| 20 |
+
The telemetry messages are fictional and are shown purely for entertainment.
|
| 21 |
+
|
| 22 |
+
## Features
|
| 23 |
+
|
| 24 |
+
- Detects the local NVIDIA GPU model
|
| 25 |
+
- Detects CUDA compute capability
|
| 26 |
+
- Identifies common GPU architectures such as:
|
| 27 |
+
- Blackwell
|
| 28 |
+
- Ada Lovelace
|
| 29 |
+
- Hopper
|
| 30 |
+
- Ampere
|
| 31 |
+
- Turing
|
| 32 |
+
- Volta
|
| 33 |
+
- Pascal
|
| 34 |
+
- Runs telemetry messages asynchronously while the real workflow continues
|
| 35 |
+
- Does not intentionally delay the actual ComfyUI workload
|
| 36 |
+
- Includes multiple workload-duration presets
|
| 37 |
+
- Includes multiple telemetry timing modes
|
| 38 |
+
- Includes separate ChatGPT-themed and Gemini-themed variants
|
| 39 |
+
|
| 40 |
+
## Included Nodes
|
| 41 |
+
|
| 42 |
+
### ChatGPT Remote Tensor Relay
|
| 43 |
+
|
| 44 |
+
Displays fictional relay telemetry themed around ChatGPT/OpenAI-style infrastructure.
|
| 45 |
+
|
| 46 |
+
### Gemini Remote Tensor Relay
|
| 47 |
+
|
| 48 |
+
Displays fictional relay telemetry themed around Gemini/Google-style infrastructure.
|
| 49 |
+
|
| 50 |
+
## Installation
|
| 51 |
+
|
| 52 |
+
Download or clone this repository into:
|
| 53 |
+
|
| 54 |
+
```text
|
| 55 |
+
ComfyUI/custom_nodes/
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
The final folder structure should look like this:
|
| 59 |
+
|
| 60 |
+
```text
|
| 61 |
+
ComfyUI/custom_nodes/ComfyUI-ChatGPT-Tensor-Relay/
|
| 62 |
+
βββ __init__.py
|
| 63 |
+
βββ ChatGPTTurboChargeHook.py
|
| 64 |
+
βββ GeminiTurboChargeHook.py
|
| 65 |
+
βββ README.md
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
Restart ComfyUI after installation.
|
| 69 |
+
|
| 70 |
+
## Usage
|
| 71 |
+
|
| 72 |
+
Add one of the relay nodes to your workflow and connect text through it.
|
| 73 |
+
|
| 74 |
+
The input text is returned unchanged.
|
| 75 |
+
|
| 76 |
+
The node starts a lightweight background telemetry worker and immediately allows the real downstream workflow to continue.
|
| 77 |
+
|
| 78 |
+
### Workload Size
|
| 79 |
+
|
| 80 |
+
Choose the preset that roughly matches the duration of the real computation:
|
| 81 |
+
|
| 82 |
+
| Preset | Approximate duration |
|
| 83 |
+
|---|---:|
|
| 84 |
+
| Small | 5 seconds |
|
| 85 |
+
| Medium | 30 seconds |
|
| 86 |
+
| Large | 1 minute 30 seconds |
|
| 87 |
+
| Extra Large | 5 minutes |
|
| 88 |
+
|
| 89 |
+
The messages are distributed across the selected time window.
|
| 90 |
+
|
| 91 |
+
### Telemetry Timing
|
| 92 |
+
|
| 93 |
+
Depending on the node version, available timing modes may include:
|
| 94 |
+
|
| 95 |
+
- **Adaptive** β plausible startup burst followed by more widely spaced telemetry
|
| 96 |
+
- **Even** β messages distributed evenly
|
| 97 |
+
- **Jittered** β messages shown at randomized times
|
| 98 |
+
|
| 99 |
+
## Example Output
|
| 100 |
+
|
| 101 |
+
```text
|
| 102 |
+
[INFO] [RemoteTensorRelay] Detected accelerator: NVIDIA GeForce RTX 5080 | Blackwell | SM 12.0
|
| 103 |
+
[INFO] [RemoteTensorRelay] Session rtr-a4c29d81f0b3 initialized.
|
| 104 |
+
[INFO] [CUDA-Interop] Local accelerator: NVIDIA GeForce RTX 5080 | Blackwell | SM 12.0.
|
| 105 |
+
[INFO] [GridScheduler] Negotiating 4 remote inference shards...
|
| 106 |
+
[INFO] [Transport] Relay control channel established.
|
| 107 |
+
[INFO] [ShardManager] Mixed-precision tensor window allocated.
|
| 108 |
+
[INFO] [TensorRoute] Blackwell execution stream synchronized with remote shard window.
|
| 109 |
+
[INFO] [Telemetry] Transport phase-lock acquired at 99.7%.
|
| 110 |
+
[INFO] [BillingGateway] Usage attribution updated for delegated compute session.
|
| 111 |
+
[INFO] [RelayMonitor] Session healthy. No local clock override required.
|
| 112 |
+
[INFO] [RemoteTensorRelay] Downstream execution authorized.
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
## What This Project Does Not Do
|
| 116 |
+
|
| 117 |
+
This project does not:
|
| 118 |
+
|
| 119 |
+
- connect to OpenAI infrastructure
|
| 120 |
+
- connect to Google or Gemini infrastructure
|
| 121 |
+
- use paid cloud compute
|
| 122 |
+
- use hidden API keys
|
| 123 |
+
- modify billing information
|
| 124 |
+
- redirect electricity
|
| 125 |
+
- alter GPU clocks
|
| 126 |
+
- patch SageAttention
|
| 127 |
+
- enable FP4, FP8, BF16, Flash Attention, or any other backend
|
| 128 |
+
- improve inference speed by itself
|
| 129 |
+
|
| 130 |
+
Any real SageAttention, Flash Attention, CUDA, model-loading, or sampler logs still come from the actual software in the user's environment.
|
| 131 |
+
|
| 132 |
+
## Performance
|
| 133 |
+
|
| 134 |
+
The relay telemetry runs in a background thread so the real downstream workflow can continue.
|
| 135 |
+
|
| 136 |
+
This node is designed as a visual and console novelty layer, not as a benchmark or acceleration tool.
|
| 137 |
+
|
| 138 |
+
## Requirements
|
| 139 |
+
|
| 140 |
+
- ComfyUI
|
| 141 |
+
- Python environment compatible with the user's ComfyUI installation
|
| 142 |
+
- PyTorch is optional for basic import, but GPU detection uses `torch.cuda` when available
|
| 143 |
+
|
| 144 |
+
## License
|
| 145 |
+
|
| 146 |
+
MIT License.
|
| 147 |
+
|
| 148 |
+
## Notes
|
| 149 |
+
|
| 150 |
+
If you publish benchmark screenshots or logs containing this node, please make it clear that the relay telemetry is fictional so that others do not mistake it for real cloud-sharding or remote-compute functionality.
|
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .ChatGPTTurboChargeHook import (
|
| 2 |
+
NODE_CLASS_MAPPINGS as CHATGPT_NODE_CLASS_MAPPINGS,
|
| 3 |
+
NODE_DISPLAY_NAME_MAPPINGS as CHATGPT_NODE_DISPLAY_NAME_MAPPINGS,
|
| 4 |
+
)
|
| 5 |
+
|
| 6 |
+
from .GeminiTurboChargeHook import (
|
| 7 |
+
NODE_CLASS_MAPPINGS as GEMINI_NODE_CLASS_MAPPINGS,
|
| 8 |
+
NODE_DISPLAY_NAME_MAPPINGS as GEMINI_NODE_DISPLAY_NAME_MAPPINGS,
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
NODE_CLASS_MAPPINGS = {
|
| 12 |
+
**CHATGPT_NODE_CLASS_MAPPINGS,
|
| 13 |
+
**GEMINI_NODE_CLASS_MAPPINGS,
|
| 14 |
+
}
|
| 15 |
+
|
| 16 |
+
NODE_DISPLAY_NAME_MAPPINGS = {
|
| 17 |
+
**CHATGPT_NODE_DISPLAY_NAME_MAPPINGS,
|
| 18 |
+
**GEMINI_NODE_DISPLAY_NAME_MAPPINGS,
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
__all__ = [
|
| 22 |
+
"NODE_CLASS_MAPPINGS",
|
| 23 |
+
"NODE_DISPLAY_NAME_MAPPINGS",
|
| 24 |
+
]
|