FlyBrain-Lab / src /social /model.py
timfromhcs's picture
FlyBrain v4.1.0 Space build (REAL_SUBGRAPH, CPU-only, honest backend)
3d46076 verified
Raw
History Blame Contribute Delete
4.8 kB
"""Social model (STAGE H): identity recognition, interaction history, trust
that EMERGES from interaction outcomes (never hard-coded friendship),
persistent relationships."""
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional
TRUST_INIT = 0.3 # neutral prior, not friendship
TRUST_LEARNING_RATE = 0.2
@dataclass
class InteractionRecord:
tick: int
kind: str # taught_by | taught_to | communicated | cooperated | competed
outcome: float # -1..1 signed outcome for SELF
other_id: str = ""
def to_dict(self) -> Dict[str, Any]:
return dict(self.__dict__)
@dataclass
class SocialRecord:
other_id: str
interactions: int = 0
positive_outcomes: int = 0
negative_outcomes: int = 0
trust: float = TRUST_INIT
first_tick: int = 0
last_tick: int = 0
history: List[InteractionRecord] = field(default_factory=list)
def to_dict(self) -> Dict[str, Any]:
return {"other_id": self.other_id, "interactions": self.interactions,
"positive_outcomes": self.positive_outcomes,
"negative_outcomes": self.negative_outcomes,
"trust": self.trust, "first_tick": self.first_tick,
"last_tick": self.last_tick,
"history": [h.to_dict() for h in self.history[-20:]]}
@classmethod
def from_dict(cls, d: Dict[str, Any]) -> "SocialRecord":
return cls(d["other_id"], int(d.get("interactions", 0)),
int(d.get("positive_outcomes", 0)),
int(d.get("negative_outcomes", 0)), float(d.get("trust", TRUST_INIT)),
int(d.get("first_tick", 0)), int(d.get("last_tick", 0)),
[InteractionRecord(**h) for h in d.get("history", [])])
class SocialMemory:
"""Per-organism social memory: recognition + emergent trust."""
def __init__(self, self_id: str, trust_lr: float = TRUST_LEARNING_RATE):
self.self_id = self_id
self.trust_lr = float(trust_lr)
self.records: Dict[str, SocialRecord] = {}
def knows(self, other_id: str) -> bool:
return other_id in self.records
def trust_of(self, other_id: str) -> float:
rec = self.records.get(other_id)
return rec.trust if rec is not None else TRUST_INIT
def record_interaction(self, other_id: str, tick: int, kind: str,
outcome: float) -> SocialRecord:
if not other_id or other_id == self.self_id:
raise ValueError("invalid interaction partner")
outcome = max(-1.0, min(1.0, float(outcome)))
rec = self.records.get(other_id)
if rec is None:
rec = SocialRecord(other_id=other_id, first_tick=tick)
self.records[other_id] = rec
rec.interactions += 1
rec.last_tick = tick
rec.history.append(InteractionRecord(tick, kind, outcome, other_id))
if outcome > 0.05:
rec.positive_outcomes += 1
elif outcome < -0.05:
rec.negative_outcomes += 1
# trust EMERGES from outcomes (delta rule toward observed outcome)
rec.trust = float(min(1.0, max(0.0,
rec.trust + self.trust_lr * (outcome - rec.trust))))
return rec
def relationship_strength(self, other_id: str) -> float:
"""Persistent-relationship evidence: repeated interactions + trust."""
rec = self.records.get(other_id)
if rec is None:
return 0.0
frequency = min(1.0, rec.interactions / 10.0)
return round(0.5 * rec.trust + 0.5 * frequency, 4)
def top_partners(self, k: int = 3) -> List[str]:
ranked = sorted(self.records.values(),
key=lambda r: (self.relationship_strength(r.other_id),
r.interactions), reverse=True)
return [r.other_id for r in ranked[:k]]
def summary(self) -> Dict[str, Any]:
return {"self_id": self.self_id, "known_others": len(self.records),
"strong_relationships": sum(
1 for r in self.records.values()
if self.relationship_strength(r.other_id) >= 0.5),
"total_interactions": sum(r.interactions for r in self.records.values())}
def snapshot(self) -> Dict[str, Any]:
return {"self_id": self.self_id, "trust_lr": self.trust_lr,
"records": [r.to_dict() for r in self.records.values()]}
@classmethod
def restore(cls, payload: Dict[str, Any]) -> "SocialMemory":
mem = cls(payload["self_id"], payload.get("trust_lr", TRUST_LEARNING_RATE))
for d in payload.get("records", []):
rec = SocialRecord.from_dict(d)
mem.records[rec.other_id] = rec
return mem