Files
Forge-Engine/routes/ia/ia_chat.py
T
williamandClaude Sonnet 5 b07b231a61
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
Ajoute l'assistant IA "Ruby" (Claude + Scenario) et corrige plusieurs bugs de la scène
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>
2026-09-09 19:40:31 +02:00

86 lines
4.0 KiB
Python

from flask import g, jsonify, request
import ai
import screens
from core.flask_app import app
_CONVERSATION_NOT_FOUND = {"error": "conversation introuvable"}
def _get_owned_conversation(slug, conversation_id):
"""La portée par SLUG (db.connect(slug), voir screens/ia/) suffit
déjà à isoler les conversations d'un jeu à l'autre — un id d'une
autre base n'existe simplement pas ici, même patron que
screens.get_scene_object. Renvoie None si absente."""
return screens.get_ia_conversation(slug, conversation_id)
@app.route("/game/<slug>/screens/<int:screen_id>/ia-conversations", methods=["GET"])
def ia_conversations_list(slug, screen_id):
"""Colonne de gauche de l'onglet "Générer avec l'IA" — toutes les
conversations de cet écran, plus récente en premier."""
return jsonify(screens.list_ia_conversations(slug, screen_id))
@app.route("/game/<slug>/screens/<int:screen_id>/ia-conversations", methods=["POST"])
def ia_conversations_create(slug, screen_id):
""""+ Nouvelle conversation" — créée immédiatement, vide."""
conversation_id = screens.create_ia_conversation(slug, screen_id)
conversation = screens.get_ia_conversation(slug, conversation_id)
return jsonify(conversation)
@app.route("/game/<slug>/ia-conversations/<int:conversation_id>", methods=["DELETE"])
def ia_conversation_delete(slug, conversation_id):
if not _get_owned_conversation(slug, conversation_id):
return jsonify(_CONVERSATION_NOT_FOUND), 404
screens.delete_ia_conversation(slug, conversation_id)
return jsonify({"ok": True})
@app.route("/game/<slug>/ia-conversations/<int:conversation_id>/messages", methods=["GET"])
def ia_conversation_messages(slug, conversation_id):
"""Historique complet d'UNE conversation — chargé par
static/js/ia/ia-chat.js à sa sélection dans la colonne."""
if not _get_owned_conversation(slug, conversation_id):
return jsonify(_CONVERSATION_NOT_FOUND), 404
return jsonify(screens.list_ia_chat_messages(slug, conversation_id))
@app.route("/game/<slug>/ia-conversations/<int:conversation_id>/messages", methods=["POST"])
def ia_conversation_send(slug, conversation_id):
"""Envoie un message dans une conversation — body {message}. Persiste
le message du créateur et la réponse, renvoie les deux.
ai.run_chat_turn lit l'historique déjà persisté et y ajoute le
message courant EN MÉMOIRE seulement — persisté ici, APRÈS l'appel,
jamais avant (sinon il apparaîtrait deux fois dans l'historique que
la boucle envoie à Claude)."""
conversation = _get_owned_conversation(slug, conversation_id)
if not conversation:
return jsonify(_CONVERSATION_NOT_FOUND), 404
message = (request.get_json(silent=True) or {}).get("message", "")
message = message.strip() if isinstance(message, str) else ""
if not message:
return jsonify({"error": "message vide"}), 400
try:
reply = ai.run_chat_turn(slug, conversation["screen_id"], conversation_id, g.current_user["id"], message)
except (ai.AnthropicNotConfiguredError, ai.ScenarioNotConfiguredError, ai.ScenarioGenerationError) as e:
reply = f"Configuration IA incomplète : {e}"
except Exception:
# Jamais le détail brut d'une erreur inattendue au créateur (même
# politique que auth/send_email.py) — journalisée côté serveur
# par le logger par défaut de Flask (DEBUG/erreur non interceptée).
app.logger.exception("Erreur inattendue dans ai.run_chat_turn (%s, conversation %s)", slug, conversation_id)
reply = "Une erreur inattendue est survenue — réessaie dans un instant."
user_message_id = screens.add_ia_chat_message(slug, conversation_id, "user", message)
assistant_message_id = screens.add_ia_chat_message(slug, conversation_id, "assistant", reply)
return jsonify({
"user_message": {"id": user_message_id, "role": "user", "content": message},
"assistant_message": {"id": assistant_message_id, "role": "assistant", "content": reply},
})