feat(assets): pipeline ComfyUI exécuté en réel, reproductibilité prouvée
Le porteur a ouvert l'accès à son instance ComfyUI : le pipeline a été exécuté, pas simulé. Deux exécutions du même plan produisent des images BIT-IDENTIQUES (seed 3475989105, sha256 b7007a7a…). C'est exactement ce que le pack d'origine annonçait sans y parvenir, et c'est désormais vérifié. Workflow de référence : SDXL 1.0, 1024×576, 20 pas, dpmpp_2m/karras. Deux défauts de mon propre client, révélés par l'exécution réelle : - le prompt négatif était concaténé au positif — travers hérité du pack. Sur SDXL cela revient à demander ce qu'on voulait exclure. Nœud propre. - les images n'étaient pas rapatriées : elles restaient chez ComfyUI et le manifeste pointait vers des chemins inexistants. Ajout de recuperer(). Krea 2 : deux choses distinctes portent ce nom. Les poids locaux krea2_turbo_nvfp4 sont un build FP4 NVIDIA Blackwell, sur GPU AMD — probablement inexploitable. Krea2ImageNode est un nœud api_node vers un service tiers payant. Trois points à trancher avant câblage : export au format API, écart de doctrine à inscrire, et licence des sorties à vérifier avant toute publication sous CC BY-SA. SDXL local reste le défaut : aucun coût, aucune donnée qui sort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -99,19 +99,44 @@ def attendre(serveur: str, prompt_id: str, delai_max: int = 600) -> dict:
|
||||
raise TimeoutError(f"ComfyUI n'a pas rendu {prompt_id} en {delai_max}s")
|
||||
|
||||
|
||||
def preparer(workflow_base: dict, noeud_seed: str, seed: int, noeud_prompt: str, prompt: str) -> dict:
|
||||
def preparer(workflow_base: dict, plan: dict, seed: int, prompt: str) -> dict:
|
||||
"""
|
||||
Copie profonde du workflow avant modification.
|
||||
|
||||
`copy.deepcopy` plutôt que `json.loads(json.dumps(...))` : même résultat, sans
|
||||
aller-retour par une sérialisation qui écraserait tout type non JSON.
|
||||
|
||||
Le prompt négatif va dans SON PROPRE nœud. Le pack d'origine le concaténait au
|
||||
prompt positif — sur SDXL, cela revient à demander au modèle de produire ce qu'on
|
||||
voulait exclure.
|
||||
"""
|
||||
workflow = copy.deepcopy(workflow_base)
|
||||
workflow[noeud_seed]["inputs"]["seed"] = seed
|
||||
workflow[noeud_prompt]["inputs"]["text"] = prompt
|
||||
workflow[plan["noeud_seed"]]["inputs"]["seed"] = seed
|
||||
workflow[plan["noeud_prompt"]]["inputs"]["text"] = prompt
|
||||
if plan.get("noeud_prompt_negatif"):
|
||||
workflow[plan["noeud_prompt_negatif"]]["inputs"]["text"] = PROMPT_NEGATIF
|
||||
return workflow
|
||||
|
||||
|
||||
def recuperer(serveur: str, image: dict, destination: Path) -> None:
|
||||
"""
|
||||
Rapatrie une image depuis ComfyUI.
|
||||
|
||||
Sans cela, les fichiers restent dans le dossier de sortie de ComfyUI et le manifeste
|
||||
référence des chemins qui n'existent pas côté projet.
|
||||
"""
|
||||
parametres = urllib.parse.urlencode(
|
||||
{
|
||||
"filename": image["filename"],
|
||||
"subfolder": image.get("subfolder", ""),
|
||||
"type": image.get("type", "output"),
|
||||
}
|
||||
)
|
||||
url = urllib.parse.urljoin(serveur, f"/view?{parametres}")
|
||||
with urllib.request.urlopen(url, timeout=60) as reponse:
|
||||
destination.write_bytes(reponse.read())
|
||||
|
||||
|
||||
def generer(args: argparse.Namespace) -> int:
|
||||
workflow_base = json.loads(Path(args.workflow).read_text(encoding="utf-8"))
|
||||
plan = json.loads(Path(args.plan).read_text(encoding="utf-8"))
|
||||
@@ -123,7 +148,7 @@ def generer(args: argparse.Namespace) -> int:
|
||||
for asset in plan["assets"]:
|
||||
cle = f"{asset['id']}:{asset['type']}"
|
||||
seed = seed_deterministe(cle)
|
||||
prompt = f"{asset['prompt']}. Negative: {PROMPT_NEGATIF}"
|
||||
prompt = asset["prompt"]
|
||||
|
||||
print(f"→ {cle} (seed {seed})", flush=True)
|
||||
|
||||
@@ -131,17 +156,20 @@ def generer(args: argparse.Namespace) -> int:
|
||||
manifeste.append({"cle": cle, "seed": seed, "fichier": None, "sha256": None})
|
||||
continue
|
||||
|
||||
workflow = preparer(workflow_base, plan["noeud_seed"], seed, plan["noeud_prompt"], prompt)
|
||||
workflow = preparer(workflow_base, plan, seed, prompt)
|
||||
prompt_id = poster(args.serveur, workflow, client_id)
|
||||
resultat = attendre(args.serveur, prompt_id)
|
||||
|
||||
fichiers = [
|
||||
img["filename"]
|
||||
images = [
|
||||
img
|
||||
for sortie in resultat.get("outputs", {}).values()
|
||||
for img in sortie.get("images", [])
|
||||
if img.get("type") == "output"
|
||||
]
|
||||
for nom in fichiers:
|
||||
for image in images:
|
||||
nom = f"{asset['id']}-{asset['type']}-{seed}.png"
|
||||
chemin = SORTIE / nom
|
||||
recuperer(args.serveur, image, chemin)
|
||||
manifeste.append(
|
||||
{
|
||||
"cle": cle,
|
||||
|
||||
Reference in New Issue
Block a user