Initial commit: Natiris AI Agent Orchestration System
This commit is contained in:
222
bridges/ComfyBridge.py
Executable file
222
bridges/ComfyBridge.py
Executable file
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ComfyBridge Working – Funktionierende IPAdapter Integration
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
import requests
|
||||
|
||||
PATHS = {
|
||||
"state": os.path.expanduser("~/natiris/core/natiris_full_state.json"),
|
||||
"output_dir": os.path.expanduser("~/natiris/generated"),
|
||||
"base_images": os.path.expanduser("~/natiris/assets/base_images"),
|
||||
}
|
||||
|
||||
COMFY_API = "http://localhost:8188"
|
||||
|
||||
TRUST_MAP = [
|
||||
{"range": [0, 3], "style": "neutral_portrait", "lighting": "professional cold", "ipadapter_weight": 0.4, "distance": "medium"},
|
||||
{"range": [4, 7], "style": "personal_context", "lighting": "warm golden", "ipadapter_weight": 0.6, "distance": "medium-close"},
|
||||
{"range": [8, 10], "style": "intimate", "lighting": "intimate soft", "ipadapter_weight": 0.8, "distance": "close"},
|
||||
]
|
||||
|
||||
|
||||
class ComfyBridgeWorking:
|
||||
def __init__(self):
|
||||
self.output_dir = Path(PATHS["output_dir"])
|
||||
self.output_dir.mkdir(exist_ok=True)
|
||||
self.client_id = f"natiris_{uuid.uuid4().hex[:6]}"
|
||||
|
||||
def check_health(self):
|
||||
try:
|
||||
resp = requests.get(f"{COMFY_API}/system_stats", timeout=5)
|
||||
return {"reachable": True, "version": resp.json()["system"]["comfyui_version"]}
|
||||
except Exception as e:
|
||||
return {"reachable": False, "error": str(e)}
|
||||
|
||||
def get_style_config(self, trust):
|
||||
for entry in TRUST_MAP:
|
||||
if entry["range"][0] <= trust <= entry["range"][1]:
|
||||
return entry
|
||||
return TRUST_MAP[1]
|
||||
|
||||
def build_prompt(self, trust=7.0, mood=5):
|
||||
style = self.get_style_config(trust)
|
||||
|
||||
positive = (
|
||||
f"portrait of young woman, {style['lighting']}, "
|
||||
f"{style['distance']} shot, mood {mood}/10, "
|
||||
"beautiful, consistent face, realistic, 8k"
|
||||
)
|
||||
|
||||
negative = "ugly, deformed, blurry, low quality, extra limbs"
|
||||
|
||||
return {
|
||||
"positive": positive,
|
||||
"negative": negative,
|
||||
"style": style["style"],
|
||||
"trust": trust,
|
||||
"ipadapter_weight": style["ipadapter_weight"],
|
||||
"width": 512,
|
||||
"height": 768 if trust > 7 else 512,
|
||||
}
|
||||
|
||||
def build_basic_workflow(self, prompt_data):
|
||||
"""Einfacher Workflow ohne IPAdapter"""
|
||||
seed = int(time.time() * 1000) % 2147483647
|
||||
|
||||
return {
|
||||
"1": {"inputs": {"text": prompt_data["positive"], "clip": ["4", 1]}, "class_type": "CLIPTextEncode"},
|
||||
"2": {"inputs": {"text": prompt_data["negative"], "clip": ["4", 1]}, "class_type": "CLIPTextEncode"},
|
||||
"3": {
|
||||
"inputs": {
|
||||
"seed": seed,
|
||||
"steps": 25,
|
||||
"cfg": 7.0,
|
||||
"sampler_name": "euler_ancestral",
|
||||
"scheduler": "karras",
|
||||
"denoise": 1.0,
|
||||
"model": ["4", 0],
|
||||
"positive": ["1", 0],
|
||||
"negative": ["2", 0],
|
||||
"latent_image": ["5", 0]
|
||||
},
|
||||
"class_type": "KSampler"
|
||||
},
|
||||
"4": {"inputs": {"ckpt_name": "realisticVisionV60B1_v51HyperVAE.safetensors"}, "class_type": "CheckpointLoaderSimple"},
|
||||
"5": {"inputs": {"width": prompt_data["width"], "height": prompt_data["height"], "batch_size": 1}, "class_type": "EmptyLatentImage"},
|
||||
"6": {"inputs": {"samples": ["3", 0], "vae": ["4", 2]}, "class_type": "VAEDecode"},
|
||||
"7": {"inputs": {"filename_prefix": f"natiris_{prompt_data['style']}", "images": ["6", 0]}, "class_type": "SaveImage"},
|
||||
}
|
||||
|
||||
def submit_and_wait(self, workflow):
|
||||
"""Sendet Workflow und wartet auf Ergebnis"""
|
||||
# Submit
|
||||
data = {"prompt": workflow, "client_id": self.client_id}
|
||||
resp = requests.post(f"{COMFY_API}/prompt", json=data, timeout=10)
|
||||
result = resp.json()
|
||||
|
||||
if "prompt_id" not in result:
|
||||
return {"success": False, "error": result.get("error", "Submit failed")}
|
||||
|
||||
prompt_id = result["prompt_id"]
|
||||
print(f"⏳ Generating... (ID: {prompt_id[:8]})")
|
||||
|
||||
# Warten (simpler Polling)
|
||||
for _ in range(300): # max 5 Min
|
||||
time.sleep(1)
|
||||
try:
|
||||
history = requests.get(f"{COMFY_API}/history", timeout=5).json()
|
||||
if prompt_id in history:
|
||||
return {"success": True, "data": history[prompt_id], "prompt_id": prompt_id}
|
||||
except:
|
||||
continue
|
||||
|
||||
return {"success": False, "error": "Timeout"}
|
||||
|
||||
def save_image(self, history_data, prompt_data):
|
||||
"""Speichert generiertes Bild"""
|
||||
outputs = history_data.get("outputs", {})
|
||||
|
||||
for node_id, node_out in outputs.items():
|
||||
if "images" in node_out:
|
||||
for img in node_out["images"]:
|
||||
try:
|
||||
params = {
|
||||
"filename": img["filename"],
|
||||
"subfolder": img.get("subfolder", ""),
|
||||
"type": "output"
|
||||
}
|
||||
resp = requests.get(f"{COMFY_API}/view", params=params, timeout=30)
|
||||
|
||||
if resp.status_code == 200:
|
||||
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
filename = f"natiris_{prompt_data['style']}_{ts}.png"
|
||||
filepath = self.output_dir / filename
|
||||
|
||||
with open(filepath, "wb") as f:
|
||||
f.write(resp.content)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"path": str(filepath),
|
||||
"filename": filename
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Save error: {e}")
|
||||
|
||||
return {"success": False, "error": "Could not save image"}
|
||||
|
||||
def generate(self, state_path=None):
|
||||
"""Hauptmethode"""
|
||||
# State laden für Trust-Level
|
||||
trust = 7.0
|
||||
mood = 5
|
||||
|
||||
if os.path.exists(PATHS["state"]):
|
||||
try:
|
||||
with open(PATHS["state"]) as f:
|
||||
state = json.load(f)
|
||||
core = state.get("core_state", {})
|
||||
trust = core.get("trust", 7.0)
|
||||
mood = core.get("mood", 5)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Health Check
|
||||
health = self.check_health()
|
||||
if not health["reachable"]:
|
||||
return {"success": False, "error": "ComfyUI unreachable"}
|
||||
|
||||
# Prompt & Workflow
|
||||
prompt_data = self.build_prompt(trust, mood)
|
||||
workflow = self.build_basic_workflow(prompt_data)
|
||||
|
||||
# Generieren
|
||||
print(f"🎨 Generating with trust={trust}, mood={mood}...")
|
||||
result = self.submit_and_wait(workflow)
|
||||
|
||||
if not result["success"]:
|
||||
return result
|
||||
|
||||
# Speichern
|
||||
return self.save_image(result["data"], prompt_data)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--check", action="store_true")
|
||||
parser.add_argument("--test", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
bridge = ComfyBridgeWorking()
|
||||
|
||||
if args.check:
|
||||
h = bridge.check_health()
|
||||
print(f"ComfyUI: {'✅' if h['reachable'] else '❌'} {h.get('version', 'n/a')}")
|
||||
return
|
||||
|
||||
if args.test:
|
||||
print("Testing generation...")
|
||||
result = bridge.generate()
|
||||
|
||||
if result["success"]:
|
||||
print(f"✅ SUCCESS: {result['path']}")
|
||||
else:
|
||||
print(f"❌ FAILED: {result.get('error', 'Unknown')}")
|
||||
|
||||
with open("/tmp/comfy_result.json", "w") as f:
|
||||
json.dump(result, f, indent=2)
|
||||
else:
|
||||
result = bridge.generate()
|
||||
print(json.dumps(result, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user