the-echo / echo /core /world_state.py
frankyy03's picture
Deploy The Echo (MockLLM path): Gradio app + echo package
897d5bd verified
"""
echo/core/world_state.py
------------------------
The persistent, structured memory of a single alternate life (one node in the
tree). This is what makes "the same you" recognizable across branches: every
new branch is generated as a *causal consequence* of its parent's WorldState,
not invented from scratch.
Pure data. No LLM, no I/O. The Curator agent reads a parent WorldState and
writes a child; the Verifier checks a child against its parent using these
fields.
"""
from __future__ import annotations
from dataclasses import dataclass, field, asdict
from typing import Optional
import uuid
@dataclass
class LifeFacts:
"""The concrete, checkable facts about this version of the person."""
age: int
location: str # city / country right now
occupation: str
relationships: list[str] = field(default_factory=list) # e.g. "married to Sofia"
dependents: list[str] = field(default_factory=list) # e.g. "daughter, 4"
scars: list[str] = field(default_factory=list) # losses / regrets carried
triumphs: list[str] = field(default_factory=list) # what went right
possessions: list[str] = field(default_factory=list) # grounding mundane detail
def constraints_text(self) -> str:
"""A compact statement of facts the next branch must NOT contradict."""
parts = [f"age {self.age}", f"in {self.location}", f"works as {self.occupation}"]
if self.relationships:
parts.append("relationships: " + "; ".join(self.relationships))
if self.dependents:
parts.append("dependents: " + "; ".join(self.dependents))
if self.scars:
parts.append("carries: " + "; ".join(self.scars))
return " | ".join(parts)
@dataclass
class EmotionalTone:
"""Where this life sits emotionally — drives the gold/dark visual + voice."""
valence: float # -1.0 (devastated) .. +1.0 (thriving)
dominant_feeling: str # e.g. "restless pride", "quiet grief"
voice_hint: str = "" # guidance for TTS (pace, warmth, weariness)
@property
def is_flourishing(self) -> bool:
return self.valence >= 0.25
@property
def is_struggling(self) -> bool:
return self.valence <= -0.25
@dataclass
class WorldState:
"""One node: a complete snapshot of an alternate life."""
node_id: str
parent_id: Optional[str]
depth: int
# the choice that CREATED this branch (what diverged from the parent)
divergence: str
# how many years passed since the parent node
years_elapsed: int
facts: LifeFacts
tone: EmotionalTone
# a short second-person summary the UI shows ("You are 34, in Lisbon...")
summary: str = ""
# the spoken message this echo leaves (filled by the voice tool)
voice_line: str = ""
voice_audio_path: Optional[str] = None
# the two planned next forks (filled by the Screenwriter)
pending_forks: list[str] = field(default_factory=list)
def to_dict(self) -> dict:
return asdict(self)
@staticmethod
def new_id() -> str:
return uuid.uuid4().hex[:8]
def root_state(seed_choice: str, base_age: int = 30) -> WorldState:
"""
Create the minimal root before the Curator fills it. The Curator will
overwrite facts/tone; this just establishes the tree's origin.
"""
return WorldState(
node_id=WorldState.new_id(),
parent_id=None,
depth=0,
divergence=seed_choice,
years_elapsed=0,
facts=LifeFacts(age=base_age, location="unknown",
occupation="unknown"),
tone=EmotionalTone(valence=0.0, dominant_feeling="uncertain"),
)