Skip to main content

affective-longing 🧠💫

Emotional extension for AI companions — beyond timing, into feeling.

Built on revive-companion (Poisson timing + Bayesian inference).

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    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   │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

Three Layers

1. Memory — Past triggers present

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.

2. Relationship — 6-stage lifecycle

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:

  • HMM: Hidden states (relationship stages), observed events (user actions). Transition matrix modulated by intimacy/conflict levels.
  • Ornstein-Uhlenbeck: Mean-reverting stochastic process. dX = θ(μ - X)dt + σdW. Models how intimacy/conflict drift toward baselines over time.

3. Emotion — VAD model

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.

Install

# Base (Poisson + Bayesian from revive-companion)
pip install affective-longing

# With memory support (sentence-transformers + chromadb)
pip install affective-longing[memory]

Quick Start

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()

API Reference

AffectiveLonging

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

AffectiveResult

@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

Events

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

Examples

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

Tests

pip install -e ".[memory,test]"
pytest tests/ -v

62 tests covering all modules.

Theoretical Foundations

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

License

MIT

Release files for affective-longing 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for affective-longing 0.1.0
File Size Uploaded
affective_longing-0.1.0.tar.gz 25.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for affective-longing 0.1.0
File Interpreter ABI Platform
affective_longing-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 48.5 kB

Release files / affective_longing-0.1.0.tar.gz

Download URL affective_longing-0.1.0.tar.gz
Size 25.4 kB
Tags Source
SHA-256 checksum
How to use checksums
dc4e133f2397aa6e97dd95448df4d76b7cde86be295576b60c927e183a9d2f20
BLAKE2b-256 checksum
How to use checksums
1d72da9f9040fab6aef67df37f4f82e958fef5cf0af5f6af8d36e129ea5b5e6b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release files / affective_longing-0.1.0-py3-none-any.whl

Download URL affective_longing-0.1.0-py3-none-any.whl
Size 23.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4c7ac9670ea0afa1c8f2597aab62ce1a55a0da0e795af1c6cba2400f6e03c7cb
BLAKE2b-256 checksum
How to use checksums
c0a5b07b17e4cdf43d1b9421488a15d505f1b501aa759088b9553210b09da795
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.15

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page