"""Entry point for executing a graph step.""" import logging from dataclasses import asdict from typing import Dict, Optional import uuid from agent.utils import with_retries from agent.image_agent import generate_image_prompt from agent.tools import generate_scene_image from agent.llm_graph import GraphState, llm_game_graph from agent.models import UserState from agent.redis_state import get_user_state logger = logging.getLogger(__name__) async def process_step( user_hash: str, step: str, setting: Optional[str] = None, character: Optional[dict] = None, genre: Optional[str] = None, choice_text: Optional[str] = None, ) -> Dict: """Run one interaction step through the graph.""" logger.info("[Runner] Step %s for user %s", step, user_hash) graph_state = GraphState(user_hash=user_hash, step=step) if step == "start": assert setting and character and genre, "Missing start parameters" graph_state.setting = setting graph_state.character = character graph_state.genre = genre elif step == "choose": assert choice_text, "choice_text is required" graph_state.choice_text = choice_text final_state = await with_retries(lambda: llm_game_graph.ainvoke(asdict(graph_state))) user_state: UserState = await get_user_state(user_hash) response: Dict = {} ending = final_state.get("ending") if ending and ending.get("ending_reached"): ending_info = ending["ending"] if ( ("description" not in ending_info or not ending_info["description"]) and user_state.story_frame ): for e in user_state.story_frame.endings: if e.id == ending_info.get("id"): ending_info["description"] = e.description break ending_desc = ending_info.get("description") or ending_info.get( "condition", "" ) change_scene = await generate_image_prompt(user_hash, ending_desc) if change_scene.change_scene == "no_change": change_scene.change_scene = "change_completely" if not change_scene.scene_description: change_scene.scene_description = ending_desc image_path = await generate_scene_image.ainvoke( { "user_hash": user_hash, "scene_id": f"ending_{uuid.uuid4()}", "change_scene": change_scene, } ) response["ending"] = ending_info response["image"] = image_path response["game_over"] = True else: if ( user_state.current_scene_id and user_state.current_scene_id in user_state.scenes ): current_scene = user_state.scenes[ user_state.current_scene_id ].dict() else: current_scene = final_state.get("scene") response["scene"] = current_scene response["game_over"] = False return response