Integrate ComfyUI agents and video workflows

This commit is contained in:
boos
2026-05-22 04:46:15 +08:00
parent 3c12738138
commit c1074ddf71
35 changed files with 2527 additions and 213 deletions
+97 -11
View File
@@ -14,12 +14,95 @@ import os
import requests
# ── Config ───────────────────────────────────────────────────────────────
BOOSAPI_BASE = os.environ.get("BOOSAPI_BASE", "http://127.0.0.1:4000")
BOOSAPI_BASE_RAW = os.environ.get("BOOSAPI_BASE", "http://127.0.0.1:4000")
BOOSAPI_KEY = os.environ.get("BOOSAPI_KEY", "sk-boos4721")
HEADERS = {
"Authorization": f"Bearer {BOOSAPI_KEY}",
"Content-Type": "application/json",
}
FALLBACK_CHAT_MODELS = ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini", "gpt-5.3-codex", "gpt-5.2"]
FALLBACK_IMAGE_MODELS = ["gpt-image-2", "gpt-image-1", "dall-e-3", "dall-e-2"]
FALLBACK_ALL_MODELS = FALLBACK_IMAGE_MODELS + FALLBACK_CHAT_MODELS
def normalize_boosapi_root(base):
base = str(base or "http://127.0.0.1:4000").strip().rstrip("/")
if base.endswith("/v1"):
base = base[:-3].rstrip("/")
return base or "http://127.0.0.1:4000"
BOOSAPI_ROOT = normalize_boosapi_root(BOOSAPI_BASE_RAW)
BOOSAPI_V1 = f"{BOOSAPI_ROOT}/v1"
def raise_for_status_with_body(resp):
try:
resp.raise_for_status()
except requests.HTTPError as e:
body = (resp.text or "").strip()
if body:
print(f"[BoosAPI] HTTP {resp.status_code} response: {body[:2000]}")
raise requests.HTTPError(f"{e}; response body: {body[:1000]}", response=resp)
raise
def _ordered_unique(values):
seen = set()
result = []
for value in values:
value = str(value or "").strip()
if not value or value in seen:
continue
seen.add(value)
result.append(value)
return result
def fetch_boosapi_models():
"""Fetch models exposed by BoosAPI's own /v1/models route."""
try:
resp = requests.get(f"{BOOSAPI_V1}/models", headers=HEADERS, timeout=8)
resp.raise_for_status()
data = resp.json()
models = []
for item in data.get("data", []):
if isinstance(item, dict):
models.append(item.get("id") or item.get("name"))
elif isinstance(item, str):
models.append(item)
return _ordered_unique(models)
except Exception as e:
print(f"[BoosAPI] model list fallback: {e}")
return None
def model_choices(kind="all"):
models = fetch_boosapi_models()
if models is None:
if kind == "image":
return FALLBACK_IMAGE_MODELS
if kind == "chat":
return FALLBACK_CHAT_MODELS
return FALLBACK_ALL_MODELS
if kind == "image":
image_like = [
model for model in models
if any(token in model.lower() for token in ("image", "dall", "flux", "sd", "midjourney"))
]
return _ordered_unique(image_like + models) or FALLBACK_IMAGE_MODELS
if kind == "chat":
chat_like = [
model for model in models
if not any(token in model.lower() for token in ("image", "dall", "flux", "sd", "midjourney"))
]
return _ordered_unique(chat_like + models) or FALLBACK_CHAT_MODELS
return _ordered_unique(models) or FALLBACK_ALL_MODELS
def default_model(kind="all"):
choices = model_choices(kind)
return choices[0] if choices else FALLBACK_ALL_MODELS[0]
# ── Node Mappings ────────────────────────────────────────────────────────
NODE_CLASS_MAPPINGS = {}
@@ -37,7 +120,7 @@ class BoosAPIImageGen:
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"model": (["dall-e-3", "dall-e-2", "gpt-image-2"], {"default": "gpt-image-2"}),
"model": (model_choices("image"), {"default": default_model("image")}),
"size": (["1024x1024", "1792x1024", "1024x1792"], {"default": "1024x1024"}),
"quality": (["standard", "hd"], {"default": "standard"}),
"n": ("INT", {"default": 1, "min": 1, "max": 4}),
@@ -50,10 +133,11 @@ class BoosAPIImageGen:
RETURN_TYPES = ("IMAGE", "STRING")
RETURN_NAMES = ("image", "metadata")
FUNCTION = "generate"
OUTPUT_NODE = True
CATEGORY = "BoosAPI"
def generate(self, prompt, model, size, quality, n, style="vivid"):
url = f"{BOOSAPI_BASE}/v1/images/generations"
url = f"{BOOSAPI_V1}/images/generations"
payload = {
"model": model,
"prompt": prompt,
@@ -65,7 +149,7 @@ class BoosAPIImageGen:
}
try:
resp = requests.post(url, headers=HEADERS, json=payload, timeout=120)
resp.raise_for_status()
raise_for_status_with_body(resp)
data = resp.json()
images = data.get("data", [])
# Return first image as b64
@@ -104,7 +188,7 @@ class BoosAPILLM:
return {
"required": {
"prompt": ("STRING", {"multiline": True, "default": ""}),
"model": ("STRING", {"default": "gpt-5.5"}),
"model": (model_choices("chat"), {"default": default_model("chat")}),
"system_prompt": ("STRING", {"multiline": True, "default": ""}),
"temperature": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 2.0}),
"max_tokens": ("INT", {"default": 2048, "min": 1, "max": 16384}),
@@ -114,10 +198,11 @@ class BoosAPILLM:
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("text",)
FUNCTION = "chat"
OUTPUT_NODE = True
CATEGORY = "BoosAPI"
def chat(self, prompt, model, system_prompt, temperature, max_tokens):
url = f"{BOOSAPI_BASE}/v1/chat/completions"
url = f"{BOOSAPI_V1}/chat/completions"
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
@@ -132,10 +217,10 @@ class BoosAPILLM:
}
try:
resp = requests.post(url, headers=HEADERS, json=payload, timeout=120)
resp.raise_for_status()
raise_for_status_with_body(resp)
data = resp.json()
text = data["choices"][0]["message"]["content"]
return (text,)
return {"ui": {"text": (text,)}, "result": (text,)}
except Exception as e:
print(f"[BoosAPI] LLM chat failed: {e}")
raise
@@ -153,7 +238,7 @@ class BoosAPITextToImage:
return {
"required": {
"positive": ("STRING", {"multiline": True, "default": ""}),
"model": (["gpt-image-2", "dall-e-3"], {"default": "gpt-image-2"}),
"model": (model_choices("image"), {"default": default_model("image")}),
},
"optional": {
"negative": ("STRING", {"multiline": True, "default": ""}),
@@ -163,6 +248,7 @@ class BoosAPITextToImage:
RETURN_TYPES = ("IMAGE",)
FUNCTION = "generate"
OUTPUT_NODE = True
CATEGORY = "BoosAPI"
def generate(self, positive, model, negative="", size="1024x1024"):
@@ -171,7 +257,7 @@ class BoosAPITextToImage:
if negative:
prompt = f"{positive}\n避免: {negative}"
url = f"{BOOSAPI_BASE}/v1/images/generations"
url = f"{BOOSAPI_V1}/images/generations"
payload = {
"model": model,
"prompt": prompt,
@@ -181,7 +267,7 @@ class BoosAPITextToImage:
}
try:
resp = requests.post(url, headers=HEADERS, json=payload, timeout=120)
resp.raise_for_status()
raise_for_status_with_body(resp)
data = resp.json()
import base64