Observed since 20 Oct, 21:03 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Emotional extension for AI companions — memory triggers, relationship states, self-emotion.
Emotional extension for AI companions — beyond timing, into feeling.
Built on revive-companion (Poisson timing + Bayesian inference).
┌─────────────────────────────────────────────────────────────┐
│ AffectiveLonging │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Memory │ │ Relationship │ │ Emotion │ │
│ │ │ │ │ │ │ │
│ │ ChromaDB │ │ HMM │ │ VAD Model │ │
│ │ Embeddings │ │ OU Process │ │ Valence │ │
│ │ Similarity │ │ 6 Stages │ │ Arousal │ │
│ │ │ │ │ │ Dominance │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └─────────────────┼─────────────────┘ │
│ ▼ │
│ tick() → AffectiveResult │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ revive-companion (base) │ │
│ │ Poisson Process → InfoGain → Bayesian → Decision │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Store conversations as embeddings. When current context matches past memories, longing probability gets a boost.
engine.remember("你喜欢下雨天", tags=["weather"])
engine.remember("我们第一次看电影是《星际穿越》", tags=["movie"])
# Later...
result = engine.tick(context="今天下雨了")
# → Triggers "你喜欢下雨天" with similarity 0.988
# → Longing boost: +15%
Theory: Sentence embeddings (all-MiniLM-L6-v2) + cosine similarity. Memory decay via Ebbinghaus forgetting curve.
Models relationship dynamics through discrete state transitions (HMM) + continuous emotional drift (Ornstein-Uhlenbeck process).
追求 → 甜蜜 → 热恋 → 平稳
↑ ↓
└──── 修复 ← 冷战 ←──┘
engine.observe("affection") # intimacy +0.10
engine.observe("fight") # conflict +0.20, may → 冷战
engine.step_time(hours=24) # OU decay toward baseline
Theory:
dX = θ(μ - X)dt + σdW. Models how intimacy/conflict drift toward baselines over time.AI companion's internal emotional state modeled as 3D vector (Valence, Arousal, Dominance). Mapped to 11 discrete emotions.
state = engine.emotion.current_state
# EmotionalState(😊 joy, V=+0.65, A=0.58, D=0.55)
| Dimension | Range | Meaning |
|---|---|---|
| Valence | -1 to +1 | Unhappy ↔ Happy |
| Arousal | 0 to 1 | Calm ↔ Excited |
| Dominance | 0 to 1 | Submissive ↔ Dominant |
Theory: Russell's Circumplex Model (1980) + Mehrabian's PAD model (1996). OU process for time decay, event-driven bumps for state changes.
# Base (Poisson + Bayesian from revive-companion)
pip install affective-longing
# With memory support (sentence-transformers + chromadb)
pip install affective-longing[memory]
from affective_longing import AffectiveLonging
engine = AffectiveLonging(seed=42)
# 1. Store memories
engine.remember("你喜欢下雨天", tags=["weather"])
engine.remember("你说过最喜欢吃草莓蛋糕", tags=["food"])
# 2. Observe events
engine.observe("reply_fast")
engine.observe("affection")
# 3. Let time pass
engine.step_time(hours=12)
# 4. Tick with context
result = engine.tick(context="今天下雨了")
print(f"Base probability: {result.base_probability:.1%}")
print(f"Memory trigger: {result.memory_trigger}")
print(f"Similarity: {result.memory_similarity:.3f}")
print(f"Boosted probability: {result.boosted_probability:.1%}")
print(f"Relationship: {result.relationship_stage.value}")
print(f"Emotion: {result.emotional_state.emoji} {result.emotional_state.emotion.value}")
if result.should_send:
send_message(result.prompt)
engine.record_send()
engine = AffectiveLonging(
memory_persist_dir="./companion_memory_db", # Where to store embeddings
relationship_seed=None, # For reproducibility
emotion_seed=None,
**kwargs # Passed to PoissonLove
)
Methods:
| Method | Description |
|---|---|
remember(text, tags, **metadata) | Store a memory. Returns memory ID. |
observe(event) | Update relationship + emotion. Events: reply_fast, reply_slow, no_reply, long_silence, affection, fight, apology, initiate, reject, long_message |
step_time(hours) | Advance time — OU decay on all dimensions |
tick(now, context) | Full pipeline. Returns AffectiveResult. |
record_reply(**kwargs) | Record user reply (passthrough to base) |
record_send() | Record that we sent (passthrough to base) |
get_state() | Snapshot of all 3 layers |
@dataclass
class AffectiveResult:
# Decision
should_send: bool
base_probability: float
# Memory
memory_trigger: str | None
memory_similarity: float
longing_boost: float
boosted_probability: float
# Relationship
relationship_stage: Stage
intimacy: float
conflict: float
# Emotion
emotional_state: EmotionalState
# Output
prompt: str
reason: str
| Event | Description | Intimacy | Conflict |
|---|---|---|---|
reply_fast | User replied quickly | +0.05 | -0.02 |
reply_slow | User replied slowly | -0.02 | +0.01 |
no_reply | User didn't reply | -0.05 | +0.03 |
long_silence | No contact >24h | -0.10 | +0.05 |
affection | User showed warmth | +0.10 | -0.05 |
fight | Conflict | -0.15 | +0.20 |
apology | Someone apologized | +0.05 | -0.15 |
initiate | User initiated contact | +0.08 | -0.02 |
reject | User rejected us | -0.12 | +0.10 |
Run the quickstarts:
# Memory triggers
python examples/quickstart.py
# Relationship state machine
python examples/quickstart_relationship.py
# VAD emotion engine
python examples/quickstart_emotion.py
# Full integration
python examples/quickstart_integrated.py
pip install -e ".[memory,test]"
pytest tests/ -v
62 tests covering all modules.
| Module | Theory | Reference |
|---|---|---|
| Memory | Sentence embeddings | Reimers & Gurevych, 2019 |
| Memory | Forgetting curve | Ebbinghaus, 1885 |
| Relationship | Hidden Markov Model | Rabiner, 1989 |
| Relationship | Ornstein-Uhlenbeck | Uhlenbeck & Ornstein, 1930 |
| Emotion | Circumplex Model | Russell, 1980 |
| Emotion | PAD Model | Mehrabian, 1996 |
| Base | Poisson Process | Poisson, 1837 |
| Base | Bayesian Inference | Bayes, 1763 |
| Base | Information Gain | Shannon, 1948 |
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.0 | 20 May 2026 | 5 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 21:03 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:cc9b86d5b63c0ddafcf19cdf09ea7203da4b519e10f9532c9e91f71363933a8d