Intègre un chat IA capable de manipuler la scène via les mêmes fonctions que l'éditeur manuel (objets, variables, déclencheurs, images générées), avec conversations multiples par écran façon Claude. Corrige au passage le rafraîchissement pjax hors-ordre, l'onglet IA/déclencheurs vide après sélection d'un objet, la comparaison de booléens dans les conditions, et le blocage du glisser-déposer hors du cadre caméra après un redimensionnement de fond par l'IA. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
69 lines
3.0 KiB
Python
69 lines
3.0 KiB
Python
"""Client Scenario (voir plan Phase 2, §3) — génération d'images (fonds/
|
|
objets 2D uniquement, jamais de personnages/sprites — décision actée
|
|
lors de l'étude préalable). API confirmée (docs.scenario.com) :
|
|
authentification Basic (clé + secret), génération ASYNCHRONE par job —
|
|
POST déclenche un job, GET /jobs/{id} jusqu'à "success"/"failure", puis
|
|
GET /assets/{id} pour l'URL finale téléchargeable.
|
|
|
|
Ce module reste un simple client HTTP : il renvoie l'URL de l'image,
|
|
jamais ne la télécharge/l'enregistre lui-même — ça reste le travail de
|
|
ai/tools.py::_dispatch_add_generated_image (voir "Mes assets",
|
|
auth/user_assets_dir.py), pour que ce module soit testable seul."""
|
|
import os
|
|
import time
|
|
|
|
import requests
|
|
|
|
_BASE_URL = "https://api.cloud.scenario.com/v1"
|
|
|
|
|
|
class ScenarioNotConfiguredError(Exception):
|
|
"""SCENARIO_API_KEY/SCENARIO_API_SECRET/SCENARIO_MODEL_ID absents —
|
|
voir .env.example. SCENARIO_MODEL_ID est l'id du modèle entraîné sur
|
|
le style graphique unique de Forge Engine (pas encore créé tant que
|
|
l'entraînement Scenario n'a pas été fait)."""
|
|
|
|
|
|
class ScenarioGenerationError(Exception):
|
|
"""Le job Scenario a échoué ou n'a pas répondu à temps."""
|
|
|
|
|
|
def _credentials():
|
|
api_key = os.environ.get("SCENARIO_API_KEY")
|
|
api_secret = os.environ.get("SCENARIO_API_SECRET")
|
|
model_id = os.environ.get("SCENARIO_MODEL_ID")
|
|
if not api_key or not api_secret or not model_id:
|
|
raise ScenarioNotConfiguredError(
|
|
"SCENARIO_API_KEY/SCENARIO_API_SECRET/SCENARIO_MODEL_ID absents — voir .env.example."
|
|
)
|
|
return (api_key, api_secret), model_id
|
|
|
|
|
|
def generate_image_url(prompt, width=768, height=768, timeout=120, poll_interval=2):
|
|
"""Lance une génération txt2img et attend le résultat — renvoie
|
|
l'URL finale de l'image (hébergée par Scenario, à télécharger par
|
|
l'appelant). Bloquant (poll_interval secondes entre chaque
|
|
vérification), borné par `timeout` secondes au total."""
|
|
auth, model_id = _credentials()
|
|
response = requests.post(
|
|
f"{_BASE_URL}/generate/txt2img", auth=auth, timeout=30,
|
|
json={"prompt": prompt, "modelId": model_id, "width": width, "height": height, "numSamples": 1},
|
|
)
|
|
response.raise_for_status()
|
|
job_id = response.json()["job"]["jobId"]
|
|
|
|
deadline = time.monotonic() + timeout
|
|
while time.monotonic() < deadline:
|
|
status_response = requests.get(f"{_BASE_URL}/jobs/{job_id}", auth=auth, timeout=30)
|
|
status_response.raise_for_status()
|
|
job = status_response.json()["job"]
|
|
if job["status"] == "success":
|
|
asset_id = job["metadata"]["assetIds"][0]
|
|
asset_response = requests.get(f"{_BASE_URL}/assets/{asset_id}", auth=auth, timeout=30)
|
|
asset_response.raise_for_status()
|
|
return asset_response.json()["asset"]["url"]
|
|
if job["status"] == "failure":
|
|
raise ScenarioGenerationError("Scenario a échoué à générer l'image.")
|
|
time.sleep(poll_interval)
|
|
raise ScenarioGenerationError("Scenario n'a pas répondu à temps.")
|