feat: Implement Spatial Graphs as Signal Processing Layer
This commit is contained in:
@@ -4,6 +4,7 @@ from langchain_core.messages import HumanMessage, SystemMessage
|
||||
|
||||
from entities import Entity
|
||||
from llm_runtime import _format_prompt, _normalize_llm_output, llm
|
||||
from spatial_graph import SpatialGraph
|
||||
from time_utils import WorldClock, describe_relative_time
|
||||
from world_architect import invoke_architect, apply_state_delta, WorldState
|
||||
|
||||
@@ -17,6 +18,7 @@ def ask_entity(
|
||||
world_clock: WorldClock,
|
||||
location: str,
|
||||
world_state: WorldState | None = None,
|
||||
spatial_graph: SpatialGraph | None = None,
|
||||
):
|
||||
facts = entity.memory.retrieve(
|
||||
player_query,
|
||||
@@ -103,8 +105,90 @@ RECENT CHAT: {recent_context}
|
||||
if state_delta:
|
||||
logger.info("Applying state delta to world...")
|
||||
apply_state_delta(world_state, state_delta)
|
||||
logger.info(
|
||||
"World time now: %s", world_state.world_clock.get_time_str()
|
||||
)
|
||||
logger.info("World time now: %s", world_state.world_clock.get_time_str())
|
||||
else:
|
||||
logger.info("No state changes from architect")
|
||||
|
||||
# Broadcast action through spatial graph
|
||||
if spatial_graph:
|
||||
logger.info("Broadcasting action through spatial graph...")
|
||||
entity_pos = spatial_graph.get_entity_position(entity.entity_id)
|
||||
|
||||
if entity_pos:
|
||||
# Get all perceiving entities
|
||||
perceptions = spatial_graph.bubble_up_broadcast(
|
||||
location_id=entity_pos.location_id,
|
||||
action=response,
|
||||
actor_id=entity.entity_id,
|
||||
llm_filter=_portal_filter_llm,
|
||||
escalation_check=_escalation_check_llm,
|
||||
)
|
||||
|
||||
# Update other entities' memory based on perceptions
|
||||
logger.info(f"Perception broadcast: {len(perceptions)} entities perceiving")
|
||||
for perceiver_id, perception in perceptions.items():
|
||||
if perceiver_id == entity.entity_id:
|
||||
continue
|
||||
|
||||
# Find perceiving entity in current session (would be in entities dict)
|
||||
if perception.perceivable:
|
||||
logger.debug(
|
||||
f"{perceiver_id} perceives: {perception.transformed_action}"
|
||||
)
|
||||
else:
|
||||
logger.warning(f"Entity {entity.entity_id} has no spatial position")
|
||||
else:
|
||||
logger.debug("No spatial graph provided, skipping perception broadcast")
|
||||
|
||||
|
||||
def _portal_filter_llm(action, source_id, target_id, portal_conn):
|
||||
"""
|
||||
Simple portal filtering based on connection properties.
|
||||
Can be enhanced with actual LLM calls if needed.
|
||||
"""
|
||||
vision = portal_conn.vision_prop
|
||||
sound = portal_conn.sound_prop
|
||||
|
||||
if vision < 1 and sound < 1:
|
||||
return None
|
||||
|
||||
if vision < 2 or sound < 2:
|
||||
return f"You hear muffled sounds from {source_id}."
|
||||
|
||||
if vision < 5 or sound < 5:
|
||||
words = action.split()
|
||||
if len(words) > 2:
|
||||
return f"You hear indistinct sounds from {source_id}..."
|
||||
return f"You hear from {source_id}: {action}"
|
||||
|
||||
# Clear
|
||||
return action
|
||||
|
||||
|
||||
def _escalation_check_llm(action, source_id, parent_id):
|
||||
"""
|
||||
Simple escalation check based on keywords.
|
||||
Can be enhanced with actual LLM calls if needed.
|
||||
"""
|
||||
escalation_keywords = [
|
||||
"yell",
|
||||
"scream",
|
||||
"shout",
|
||||
"cry",
|
||||
"crash",
|
||||
"bang",
|
||||
"explosion",
|
||||
"smash",
|
||||
"break",
|
||||
"attack",
|
||||
"fight",
|
||||
"combat",
|
||||
"blood",
|
||||
"murder",
|
||||
"help",
|
||||
"alarm",
|
||||
"emergency",
|
||||
]
|
||||
|
||||
action_lower = action.lower()
|
||||
return any(kw in action_lower for kw in escalation_keywords)
|
||||
|
||||
Reference in New Issue
Block a user