"""LangGraph routing logic — multi-signal end-of-conversation detection.""" from src.schemas.state import WalkState def route_after_memory_update(state: WalkState) -> str: """Determine branching after the update_memory node. Uses multiple signals to decide when to end: - Error state - LLM's own judgment (should_end_turn) - Max turns exceeded - NPC withdrawal (trust breakdown) - User disengagement (repeated short responses) - Natural high-rapport conclusion Returns: "reflection" — conversation ended, route to reflection node "end" — conversation continues, wait for next user input """ # 1) Error — bail out if state.get("error"): return "end" agent_output = state.get("agent_output", {}) memory = state.get("memory", {}) scenario = state.get("scenario", {}) turn_index = state.get("turn_index", 0) max_turns = scenario.get("max_turns", 10) # 2) LLM decided to end (natural goodbye or shutdown) if agent_output.get("should_end_turn", False): return "reflection" # 3) Max turns exceeded (hard limit) if turn_index >= max_turns: return "reflection" # 4) NPC withdrawal — James shuts down due to repeated insensitivity npc_mood = memory.get("npc_mood", "guarded") empathy_ratio = memory.get("empathy_ratio", 0.5) if npc_mood == "withdrawn" and empathy_ratio < 0.3 and turn_index >= 3: return "reflection" # 5) User disengagement — 3+ very short responses in a row silence_count = memory.get("silence_count", 0) if silence_count >= 3 and turn_index >= 4: return "reflection" # 6) Natural high-rapport conclusion — deep connection achieved early rapport = memory.get("rapport_score", 0.0) npc_openness = memory.get("npc_openness", 0.3) if rapport >= 0.85 and npc_openness >= 0.8 and turn_index >= 6: return "reflection" # 7) Continue conversation return "end"