Ajoute l'assistant IA "Ruby" (Claude + Scenario) et corrige plusieurs bugs de la scène
Build and deploy / test-python (push) Successful in 12m24s
Build and deploy / test-js (push) Successful in 1m9s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

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>
This commit is contained in:
william
2026-09-09 19:40:31 +02:00
co-authored by Claude Sonnet 5
parent ab6635eee0
commit b07b231a61
62 changed files with 2928 additions and 62 deletions
+68
View File
@@ -0,0 +1,68 @@
"""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.")