"""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"