Catalog PyPI
PACKAGE EXPLORERView in registry

affective-longing

PyPI

Emotional extension for AI companions — memory triggers, relationship states, self-emotion.

aicompanionemotionlongingmemoryrelationship
Versions collected1
Direct dependencies5From release 0.1.0
Last observed20 Oct, 21:03 UTC

README

Version 0.1.0 documentation

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)
DimensionRangeMeaning
Valence-1 to +1Unhappy ↔ Happy
Arousal0 to 1Calm ↔ Excited
Dominance0 to 1Submissive ↔ 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:

MethodDescription
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

EventDescriptionIntimacyConflict
reply_fastUser replied quickly+0.05-0.02
reply_slowUser replied slowly-0.02+0.01
no_replyUser didn't reply-0.05+0.03
long_silenceNo contact >24h-0.10+0.05
affectionUser showed warmth+0.10-0.05
fightConflict-0.15+0.20
apologySomeone apologized+0.05-0.15
initiateUser initiated contact+0.08-0.02
rejectUser 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

ModuleTheoryReference
MemorySentence embeddingsReimers & Gurevych, 2019
MemoryForgetting curveEbbinghaus, 1885
RelationshipHidden Markov ModelRabiner, 1989
RelationshipOrnstein-UhlenbeckUhlenbeck & Ornstein, 1930
EmotionCircumplex ModelRussell, 1980
EmotionPAD ModelMehrabian, 1996
BasePoisson ProcessPoisson, 1837
BaseBayesian InferenceBayes, 1763
BaseInformation GainShannon, 1948

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.020 May 20265

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
pearthink123/affective-longing

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:03 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:cc9b86d5b63c0ddafcf19cdf09ea7203da4b519e10f9532c9e91f71363933a8d