chore: finalize repo audit hygiene (#257)
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
# OpenHer × EverCore Use Case
|
||||
# OpenHer × EverOS Use Case
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# ─── LLM Provider (pick one) ───
|
||||
@ -17,12 +17,12 @@ GEMINI_API_KEY=your_gemini_api_key_here
|
||||
# OpenAI (alternative)
|
||||
# OPENAI_API_KEY=your_openai_api_key_here
|
||||
|
||||
# ─── EverCore Long-Term Memory ───
|
||||
# ─── EverOS Long-Term Memory ───
|
||||
|
||||
# Option A: EverCore Cloud
|
||||
# Option A: EverMind Cloud
|
||||
EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
|
||||
EVERMEMOS_API_KEY=your_evermemos_api_key_here
|
||||
|
||||
# Option B: Self-Hosted EverCore
|
||||
# cd vendor/EverCore && docker compose up -d && uv run python src/run.py
|
||||
# Option B: Self-Hosted EverOS
|
||||
# cd vendor/EverOS && docker compose up -d && uv run python src/run.py
|
||||
# EVERMEMOS_BASE_URL=http://localhost:1995/api/v1
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
# OpenHer — Teaching AI to Remember Who You Are
|
||||
|
||||
Built on [EverCore](https://github.com/EverMind-AI/EverOS/tree/main/methods/EverCore) — Open-source AI memory infrastructure
|
||||
Built on [EverOS](https://github.com/EverMind-AI/EverOS) — open-source AI memory infrastructure
|
||||
|
||||
**OpenHer** doesn't build chatbots. It doesn't build AI assistants. It builds **AI Beings** — entities with personality, emotion, and memory that *feel*, *remember*, and *grow* through every interaction.
|
||||
|
||||
**EverCore** is her long-term memory — the part that lets her carry your story across sessions, remember who you are, what you've talked about, and how your relationship has evolved.
|
||||
**EverOS** is her long-term memory — the part that lets her carry your story across sessions, remember who you are, what you've talked about, and how your relationship has evolved.
|
||||
|
||||
Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
|
||||
|
||||
@ -14,7 +14,7 @@ Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
|
||||
|
||||
Without memory, every conversation starts from zero. She doesn't know your name. She doesn't remember that three weeks ago you mentioned you drink your coffee black. She doesn't know you once had a fight and made up.
|
||||
|
||||
With EverCore:
|
||||
With EverOS:
|
||||
|
||||
**She remembers what you said.**
|
||||
Three weeks ago you casually mentioned no sugar in your coffee. Today she says: "Americano, no sugar, right?"
|
||||
@ -31,19 +31,19 @@ Last time you mentioned work stress. This time she asks: "How's that project goi
|
||||
|
||||
## Memory Architecture
|
||||
|
||||
OpenHer's memory has three layers. EverCore powers the deepest one:
|
||||
OpenHer's memory has three layers. EverOS powers the deepest one:
|
||||
|
||||
| Layer | What it does | Analogy |
|
||||
|:------|:-------------|:--------|
|
||||
| **Style Memory** | Her behavioral habits — tone, expression patterns | Muscle memory |
|
||||
| **Local Facts** | Your preferences, personal info | Short-term memory |
|
||||
| **Long-Term Memory** | What happened between you, her understanding of you, her hunches | **Episodic memory (EverCore)** |
|
||||
| **Long-Term Memory** | What happened between you, her understanding of you, her hunches | **Episodic memory (EverOS)** |
|
||||
|
||||
---
|
||||
|
||||
## How Memory Feeds Into Personality
|
||||
|
||||
OpenHer's core is a living neural network (25D input, 24D hidden, 8D behavioral signals). EverCore provides 4 key dimensions that let her tell the difference between a stranger and an old friend:
|
||||
OpenHer's core is a living neural network (25D input, 24D hidden, 8D behavioral signals). EverOS provides 4 key dimensions that let her tell the difference between a stranger and an old friend:
|
||||
|
||||
```
|
||||
Relationship Depth 0 ─────────────────── 1
|
||||
@ -96,13 +96,13 @@ User sends a message
|
||||
|
|
||||
v
|
||||
Load memory -- First turn: load "who you are", "what we talked about",
|
||||
| "what's on her mind" from EverCore
|
||||
| "what's on her mind" from EverOS
|
||||
v
|
||||
Perceive -- LLM evaluates the current moment: your emotion, topic
|
||||
| intimacy, conflict level... (8 dimensions)
|
||||
| + relationship dimensions from EverCore (4 dimensions) = 12D
|
||||
| + relationship dimensions from EverOS (4 dimensions) = 12D
|
||||
v
|
||||
Relationship evolves -- Blend EverCore history with this turn's changes
|
||||
Relationship evolves -- Blend EverOS history with this turn's changes
|
||||
| Smoothed so a single remark can't flip the relationship
|
||||
v
|
||||
Neural network -- 25D input (drives + context + relationship + internal state)
|
||||
@ -115,7 +115,7 @@ User sends a message
|
||||
Respond -- Internal monologue first, then choose what to say and how
|
||||
|
|
||||
v
|
||||
Remember this turn -- Store the conversation in EverCore (async, non-blocking)
|
||||
Remember this turn -- Store the conversation in EverOS (async, non-blocking)
|
||||
|
|
||||
v
|
||||
Prepare for next -- Search for memories related to what you just said
|
||||
@ -128,7 +128,7 @@ User sends a message
|
||||
- **Emergent Personality** — Not written in a prompt. Emerges from random neural networks, 5D drives, and Hebbian learning
|
||||
- **Emotional Thermodynamics** — Drives metabolize over real time. She gets lonely when you're away, irritated when ignored
|
||||
- **Feel First** — Every response starts with an internal monologue before choosing words
|
||||
- **Cross-Session Memory** — EverCore stores your shared story across every conversation
|
||||
- **Cross-Session Memory** — EverOS stores your shared story across every conversation
|
||||
- **Relationship Evolution** — The relationship vector deepens naturally with each turn
|
||||
- **Proactive Messages** — She reaches out not on a timer, but because her connection hunger is rising
|
||||
- **Modal Expression** — She chooses text, voice, or photos based on what the moment calls for
|
||||
@ -140,7 +140,7 @@ User sends a message
|
||||
|:------|:-----------|
|
||||
| Runtime | Python 3.11+, FastAPI, WebSocket, asyncio |
|
||||
| LLM | Gemini, Claude, Qwen3, GPT-5.4-mini, MiniMax, Moonshot, StepFun, Ollama |
|
||||
| Memory | **EverCore** (self-hosted / cloud) + SQLite local state |
|
||||
| Memory | **EverOS** (self-hosted / cloud) + SQLite local state |
|
||||
| Desktop | SwiftUI (macOS native) |
|
||||
| Voice | DashScope, OpenAI, MiniMax |
|
||||
|
||||
@ -152,7 +152,7 @@ User sends a message
|
||||
|
||||
- Python 3.11+
|
||||
- Any supported LLM provider API key
|
||||
- EverCore (self-hosted or cloud)
|
||||
- EverOS (self-hosted or cloud)
|
||||
|
||||
### 1. Clone & Install
|
||||
|
||||
@ -174,12 +174,12 @@ DEFAULT_PROVIDER=gemini
|
||||
DEFAULT_MODEL=gemini-3.1-flash-lite-preview
|
||||
GEMINI_API_KEY=your_key
|
||||
|
||||
# EverCore — Cloud
|
||||
# EverMind Cloud
|
||||
EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
|
||||
EVERMEMOS_API_KEY=your_key
|
||||
|
||||
# EverCore — Self-hosted
|
||||
# cd vendor/EverCore && docker compose up -d && uv run python src/run.py
|
||||
# EverOS — Self-hosted
|
||||
# cd vendor/EverOS && docker compose up -d && uv run python src/run.py
|
||||
# EVERMEMOS_BASE_URL=http://localhost:1995/api/v1
|
||||
```
|
||||
|
||||
@ -194,7 +194,7 @@ python main.py
|
||||
|
||||
```bash
|
||||
python demo/evermemos_demo.py
|
||||
# Runs in simulation mode even without EverCore
|
||||
# Runs in simulation mode even without EverOS
|
||||
```
|
||||
|
||||
---
|
||||
@ -205,11 +205,11 @@ python demo/evermemos_demo.py
|
||||
OpenHer/
|
||||
├── agent/
|
||||
│ ├── chat_agent.py # Main agent, full lifecycle
|
||||
│ ├── evermemos_mixin.py # EverCore integration (load/store/search/EMA)
|
||||
│ ├── evermemos_mixin.py # EverOS integration (load/store/search/EMA)
|
||||
│ └── prompt_builder.py # Memory injection into Actor prompt
|
||||
├── engine/
|
||||
│ └── genome/
|
||||
│ ├── genome_engine.py # Neural network + 12D context (incl. 4D EverCore)
|
||||
│ ├── genome_engine.py # Neural network + 12D context (incl. 4D EverOS)
|
||||
│ ├── critic.py # LLM perception: 8D context + relationship deltas
|
||||
│ ├── drive_metabolism.py # Emotional thermodynamics
|
||||
│ └── style_memory.py # KNN behavioral memory + Hawking radiation decay
|
||||
@ -219,7 +219,7 @@ OpenHer/
|
||||
├── persona/
|
||||
│ └── personas/ # 10 pre-built personas (SOUL.md + seeds)
|
||||
├── vendor/
|
||||
│ └── EverCore/ # Self-hosted EverCore
|
||||
│ └── EverOS/ # Self-hosted EverOS
|
||||
└── main.py # FastAPI server
|
||||
```
|
||||
|
||||
@ -227,7 +227,7 @@ OpenHer/
|
||||
|
||||
## Integration Code at a Glance
|
||||
|
||||
### EverCore Mixin
|
||||
### EverOS Mixin
|
||||
|
||||
The core integration is a mixin class handling four async operations:
|
||||
|
||||
@ -265,7 +265,7 @@ class SessionContext:
|
||||
|
||||
## Without Memory vs. With Memory
|
||||
|
||||
| | Without EverCore | With EverCore |
|
||||
| | Without EverOS | With EverOS |
|
||||
|:--|:--|:--|
|
||||
| First meeting | "Hi! I'm Luna" | "Hi! I'm Luna" |
|
||||
| Second meeting | "Hi! I'm Luna" | "Hey Alex! How's that project going?" |
|
||||
@ -278,7 +278,7 @@ class SessionContext:
|
||||
## Links
|
||||
|
||||
- Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
|
||||
- EverCore: [evermind.ai](https://evermind.ai)
|
||||
- EverOS: [evermind.ai](https://evermind.ai)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
OpenHer × EverCore Integration Demo
|
||||
OpenHer × EverOS Integration Demo
|
||||
|
||||
Demonstrates how EverCore provides long-term memory to the
|
||||
Demonstrates how EverOS provides long-term memory to the
|
||||
AI Being persona engine. Shows session context loading, memory
|
||||
storage, search, and relationship vector evolution.
|
||||
|
||||
Usage:
|
||||
# With EverCore Cloud
|
||||
# With EverMind Cloud
|
||||
export EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
|
||||
export EVERMEMOS_API_KEY=your_key
|
||||
python demo/evermemos_demo.py
|
||||
|
||||
# With self-hosted EverCore
|
||||
# With self-hosted EverOS
|
||||
export EVERMEMOS_BASE_URL=http://localhost:1995/api/v1
|
||||
python demo/evermemos_demo.py
|
||||
"""
|
||||
@ -20,12 +20,10 @@ Usage:
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# EverCore Client (minimal standalone version)
|
||||
# EverOS Client (minimal standalone version)
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
try:
|
||||
@ -35,8 +33,8 @@ except ImportError:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class EverCoreClient:
|
||||
"""Minimal EverCore client for demo purposes."""
|
||||
class EverOSClient:
|
||||
"""Minimal EverOS client for demo purposes."""
|
||||
|
||||
def __init__(self, base_url: str, api_key: str = ""):
|
||||
self.base_url = base_url.rstrip("/")
|
||||
@ -51,7 +49,7 @@ class EverCoreClient:
|
||||
return h
|
||||
|
||||
async def health_check(self) -> bool:
|
||||
"""Check if EverCore is reachable."""
|
||||
"""Check if EverOS is reachable."""
|
||||
try:
|
||||
# Try the health endpoint (remove /api/v1 suffix)
|
||||
health_url = self.base_url.replace("/api/v1", "") + "/health"
|
||||
@ -129,12 +127,13 @@ class EverCoreClient:
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# Relationship Vector (from EverCore session)
|
||||
# Relationship Vector (from EverOS session)
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
def compute_relationship_vector(profile_data: dict) -> dict:
|
||||
"""
|
||||
Extract 4D relationship vector from EverCore profile data.
|
||||
Extract 4D relationship vector from EverOS profile data.
|
||||
|
||||
These 4 dimensions expand the persona engine's neural network
|
||||
from 8D to 12D input, allowing it to differentiate behavior
|
||||
@ -152,12 +151,12 @@ def apply_relationship_ema(
|
||||
prior: dict,
|
||||
delta: dict,
|
||||
conversation_depth: float,
|
||||
prev_ema: Optional[dict] = None,
|
||||
prev_ema: dict | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Semi-emergent relationship update (Step 2.5 of ChatAgent lifecycle).
|
||||
|
||||
Blends EverCore prior with LLM-judged delta through EMA:
|
||||
Blends EverOS prior with LLM-judged delta through EMA:
|
||||
- alpha modulated by conversation depth (deeper = trust LLM more)
|
||||
- Clips to valid ranges
|
||||
- Preserves momentum through prev_ema
|
||||
@ -181,25 +180,26 @@ def apply_relationship_ema(
|
||||
# Demo
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
|
||||
async def main():
|
||||
base_url = os.getenv("EVERMEMOS_BASE_URL", "")
|
||||
api_key = os.getenv("EVERMEMOS_API_KEY", "")
|
||||
|
||||
if not base_url:
|
||||
print("=" * 60)
|
||||
print("OpenHer × EverCore Integration Demo")
|
||||
print("OpenHer × EverOS Integration Demo")
|
||||
print("=" * 60)
|
||||
print()
|
||||
print("⚠️ EVERMEMOS_BASE_URL not set.")
|
||||
print()
|
||||
print("To run this demo, set up EverCore:")
|
||||
print("To run this demo, set up EverOS:")
|
||||
print()
|
||||
print(" Option A — Cloud:")
|
||||
print(" Option A — EverMind Cloud:")
|
||||
print(" export EVERMEMOS_BASE_URL=https://api.evermind.ai/v1")
|
||||
print(" export EVERMEMOS_API_KEY=your_key")
|
||||
print()
|
||||
print(" Option B — Self-hosted:")
|
||||
print(" cd vendor/EverCore && docker compose up -d")
|
||||
print(" cd vendor/EverOS && docker compose up -d")
|
||||
print(" uv run python src/run.py")
|
||||
print(" export EVERMEMOS_BASE_URL=http://localhost:1995/api/v1")
|
||||
print()
|
||||
@ -209,20 +209,20 @@ async def main():
|
||||
await demo_simulation()
|
||||
return
|
||||
|
||||
client = EverCoreClient(base_url, api_key)
|
||||
client = EverOSClient(base_url, api_key)
|
||||
|
||||
print("=" * 60)
|
||||
print("OpenHer × EverCore Integration Demo")
|
||||
print("OpenHer × EverOS Integration Demo")
|
||||
print("=" * 60)
|
||||
print(f"\n📡 EverCore: {base_url}")
|
||||
print(f"\n📡 EverOS: {base_url}")
|
||||
|
||||
# Health check
|
||||
healthy = await client.health_check()
|
||||
if not healthy:
|
||||
print("❌ EverCore is not reachable. Check your URL and try again.")
|
||||
print("❌ EverOS is not reachable. Check your URL and try again.")
|
||||
await client.close()
|
||||
return
|
||||
print("✅ EverCore is healthy\n")
|
||||
print("✅ EverOS is healthy\n")
|
||||
|
||||
# ── Demo conversation ──
|
||||
user_id = "demo_user"
|
||||
@ -232,8 +232,15 @@ async def main():
|
||||
group_id = f"{persona_id}__{user_id}"
|
||||
|
||||
conversations = [
|
||||
("My name is Alex, I'm a software engineer", "Nice to meet you Alex! What kind of software do you work on?"),
|
||||
("I love hiking in the mountains on weekends", "That sounds wonderful! There's something about being up high that makes everything else feel small."),
|
||||
(
|
||||
"My name is Alex, I'm a software engineer",
|
||||
"Nice to meet you Alex! What kind of software do you work on?",
|
||||
),
|
||||
(
|
||||
"I love hiking in the mountains on weekends",
|
||||
"That sounds wonderful! There's something about being up high "
|
||||
"that makes everything else feel small.",
|
||||
),
|
||||
("I drink my coffee black, no sugar", "Noted! A purist. I respect that."),
|
||||
]
|
||||
|
||||
@ -249,7 +256,7 @@ async def main():
|
||||
agent_reply=agent_reply,
|
||||
)
|
||||
status = "✅" if "error" not in result else "❌"
|
||||
print(f" {status} User: \"{user_msg[:50]}...\"")
|
||||
print(f' {status} User: "{user_msg[:50]}..."')
|
||||
|
||||
# Wait for indexing
|
||||
print("\n⏳ Waiting for memory indexing (3s)...")
|
||||
@ -270,7 +277,7 @@ async def main():
|
||||
group_id=group_id,
|
||||
)
|
||||
memories = result.get("result", {}).get("memories", [])
|
||||
print(f" Q: \"{query}\"")
|
||||
print(f' Q: "{query}"')
|
||||
if memories:
|
||||
for mem in memories[:2]:
|
||||
content = str(mem)[:100]
|
||||
@ -281,7 +288,12 @@ async def main():
|
||||
|
||||
# Relationship vector
|
||||
print("📊 Relationship Vector Evolution:\n")
|
||||
prior = {"relationship_depth": 0.0, "emotional_valence": 0.0, "trust_level": 0.0, "pending_foresight": 0.0}
|
||||
prior = {
|
||||
"relationship_depth": 0.0,
|
||||
"emotional_valence": 0.0,
|
||||
"trust_level": 0.0,
|
||||
"pending_foresight": 0.0,
|
||||
}
|
||||
deltas = [
|
||||
{"relationship_depth": 0.1, "emotional_valence": 0.2, "trust_level": 0.05},
|
||||
{"relationship_depth": 0.05, "emotional_valence": 0.1, "trust_level": 0.1},
|
||||
@ -290,61 +302,147 @@ async def main():
|
||||
|
||||
ema = None
|
||||
for i, delta in enumerate(deltas):
|
||||
ema = apply_relationship_ema(prior, delta, conversation_depth=0.2 * (i + 1), prev_ema=ema)
|
||||
print(f" Turn {i+1}: depth={ema['relationship_depth']:.3f} "
|
||||
f"valence={ema['emotional_valence']:.3f} "
|
||||
f"trust={ema['trust_level']:.3f}")
|
||||
ema = apply_relationship_ema(
|
||||
prior, delta, conversation_depth=0.2 * (i + 1), prev_ema=ema
|
||||
)
|
||||
print(
|
||||
f" Turn {i + 1}: depth={ema['relationship_depth']:.3f} "
|
||||
f"valence={ema['emotional_valence']:.3f} "
|
||||
f"trust={ema['trust_level']:.3f}"
|
||||
)
|
||||
prior = ema
|
||||
|
||||
print(f"\n → After 3 turns: no longer a stranger (depth={ema['relationship_depth']:.3f})")
|
||||
print(f" → Neural network now produces warmer, more familiar behavioral signals\n")
|
||||
print(
|
||||
"\n → After 3 turns: no longer a stranger "
|
||||
f"(depth={ema['relationship_depth']:.3f})"
|
||||
)
|
||||
print(" → Neural network now produces warmer, more familiar behavioral signals\n")
|
||||
|
||||
await client.close()
|
||||
print("✅ Demo complete!")
|
||||
|
||||
|
||||
async def demo_simulation():
|
||||
"""Run demo in simulation mode (no EverCore connection)."""
|
||||
"""Run demo in simulation mode (no EverOS connection)."""
|
||||
print("📊 Simulating Relationship Vector Evolution:\n")
|
||||
print(" This shows how the 4D EverCore relationship vector")
|
||||
print(" This shows how the 4D EverOS relationship vector")
|
||||
print(" deepens over multiple conversation turns.\n")
|
||||
|
||||
prior = {"relationship_depth": 0.0, "emotional_valence": 0.0, "trust_level": 0.0, "pending_foresight": 0.0}
|
||||
prior = {
|
||||
"relationship_depth": 0.0,
|
||||
"emotional_valence": 0.0,
|
||||
"trust_level": 0.0,
|
||||
"pending_foresight": 0.0,
|
||||
}
|
||||
|
||||
# Simulate 10 turns of conversation
|
||||
simulated_deltas = [
|
||||
(0.3, {"relationship_depth": 0.10, "emotional_valence": 0.15, "trust_level": 0.05}),
|
||||
(0.4, {"relationship_depth": 0.08, "emotional_valence": 0.10, "trust_level": 0.08}),
|
||||
(0.5, {"relationship_depth": 0.05, "emotional_valence": 0.20, "trust_level": 0.12}),
|
||||
(0.6, {"relationship_depth": 0.06, "emotional_valence": -0.10, "trust_level": 0.03}),
|
||||
(0.7, {"relationship_depth": 0.04, "emotional_valence": 0.08, "trust_level": 0.10}),
|
||||
(0.7, {"relationship_depth": 0.03, "emotional_valence": 0.12, "trust_level": 0.08}),
|
||||
(0.8, {"relationship_depth": 0.02, "emotional_valence": 0.05, "trust_level": 0.06}),
|
||||
(0.8, {"relationship_depth": 0.03, "emotional_valence": 0.10, "trust_level": 0.05}),
|
||||
(0.9, {"relationship_depth": 0.01, "emotional_valence": 0.08, "trust_level": 0.04}),
|
||||
(0.9, {"relationship_depth": 0.02, "emotional_valence": 0.06, "trust_level": 0.03}),
|
||||
(
|
||||
0.3,
|
||||
{
|
||||
"relationship_depth": 0.10,
|
||||
"emotional_valence": 0.15,
|
||||
"trust_level": 0.05,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.4,
|
||||
{
|
||||
"relationship_depth": 0.08,
|
||||
"emotional_valence": 0.10,
|
||||
"trust_level": 0.08,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.5,
|
||||
{
|
||||
"relationship_depth": 0.05,
|
||||
"emotional_valence": 0.20,
|
||||
"trust_level": 0.12,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.6,
|
||||
{
|
||||
"relationship_depth": 0.06,
|
||||
"emotional_valence": -0.10,
|
||||
"trust_level": 0.03,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.7,
|
||||
{
|
||||
"relationship_depth": 0.04,
|
||||
"emotional_valence": 0.08,
|
||||
"trust_level": 0.10,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.7,
|
||||
{
|
||||
"relationship_depth": 0.03,
|
||||
"emotional_valence": 0.12,
|
||||
"trust_level": 0.08,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.8,
|
||||
{
|
||||
"relationship_depth": 0.02,
|
||||
"emotional_valence": 0.05,
|
||||
"trust_level": 0.06,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.8,
|
||||
{
|
||||
"relationship_depth": 0.03,
|
||||
"emotional_valence": 0.10,
|
||||
"trust_level": 0.05,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.9,
|
||||
{
|
||||
"relationship_depth": 0.01,
|
||||
"emotional_valence": 0.08,
|
||||
"trust_level": 0.04,
|
||||
},
|
||||
),
|
||||
(
|
||||
0.9,
|
||||
{
|
||||
"relationship_depth": 0.02,
|
||||
"emotional_valence": 0.06,
|
||||
"trust_level": 0.03,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
ema = None
|
||||
for i, (depth, delta) in enumerate(simulated_deltas, 1):
|
||||
alpha = max(0.15, min(0.65, 0.15 + 0.5 * depth))
|
||||
ema = apply_relationship_ema(prior, delta, conversation_depth=depth, prev_ema=ema)
|
||||
ema = apply_relationship_ema(
|
||||
prior, delta, conversation_depth=depth, prev_ema=ema
|
||||
)
|
||||
bar_d = "█" * int(ema["relationship_depth"] * 20)
|
||||
bar_v = "█" * int(max(0, ema["emotional_valence"]) * 20)
|
||||
bar_t = "█" * int(ema["trust_level"] * 20)
|
||||
print(f" Turn {i:2d} (α={alpha:.2f}): "
|
||||
f"depth={ema['relationship_depth']:.3f} {bar_d}")
|
||||
print(f" "
|
||||
f"valence={ema['emotional_valence']:+.3f} {bar_v}")
|
||||
print(f" "
|
||||
f"trust={ema['trust_level']:.3f} {bar_t}")
|
||||
print(
|
||||
f" Turn {i:2d} (α={alpha:.2f}): "
|
||||
f"depth={ema['relationship_depth']:.3f} {bar_d}"
|
||||
)
|
||||
print(f" valence={ema['emotional_valence']:+.3f} {bar_v}")
|
||||
print(f" trust={ema['trust_level']:.3f} {bar_t}")
|
||||
print()
|
||||
prior = ema
|
||||
|
||||
print(" ──────────────────────────────────")
|
||||
print(f" Final state: depth={ema['relationship_depth']:.3f}, "
|
||||
f"valence={ema['emotional_valence']:+.3f}, "
|
||||
f"trust={ema['trust_level']:.3f}")
|
||||
print(
|
||||
f" Final state: depth={ema['relationship_depth']:.3f}, "
|
||||
f"valence={ema['emotional_valence']:+.3f}, "
|
||||
f"trust={ema['trust_level']:.3f}"
|
||||
)
|
||||
print()
|
||||
print(" Turn 4 shows a negative emotional event (valence delta = -0.10),")
|
||||
print(" but the EMA smoothing prevents overreaction — the relationship")
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
"""
|
||||
Neural network context features — showing how EverCore expands
|
||||
Neural network context features — showing how EverOS expands
|
||||
the persona engine's perception from 8D to 12D.
|
||||
|
||||
The 4 additional relationship dimensions from EverCore allow the
|
||||
The 4 additional relationship dimensions from EverOS allow the
|
||||
neural network to produce different behavioral signals depending
|
||||
on the history between user and persona.
|
||||
|
||||
@ -13,21 +13,21 @@ Full source: https://github.com/kellyvv/OpenHer/blob/main/engine/genome/genome_e
|
||||
# 5D Drive System (internal motivation)
|
||||
# ══════════════════════════════════════════════
|
||||
|
||||
DRIVES = ['connection', 'novelty', 'expression', 'safety', 'play']
|
||||
DRIVES = ["connection", "novelty", "expression", "safety", "play"]
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
# 8D Behavioral Signals (neural network output)
|
||||
# ══════════════════════════════════════════════
|
||||
|
||||
SIGNALS = [
|
||||
'directness', # 0=indirect hints → 1=straight talk
|
||||
'vulnerability', # 0=guarded → 1=emotionally open
|
||||
'playfulness', # 0=serious → 1=playful/teasing
|
||||
'initiative', # 0=reactive → 1=proactive leading
|
||||
'depth', # 0=small talk → 1=deep conversation
|
||||
'warmth', # 0=cold/distant → 1=warm/caring
|
||||
'defiance', # 0=compliant → 1=rebellious/stubborn
|
||||
'curiosity', # 0=indifferent → 1=intensely curious
|
||||
"directness", # 0=indirect hints → 1=straight talk
|
||||
"vulnerability", # 0=guarded → 1=emotionally open
|
||||
"playfulness", # 0=serious → 1=playful/teasing
|
||||
"initiative", # 0=reactive → 1=proactive leading
|
||||
"depth", # 0=small talk → 1=deep conversation
|
||||
"warmth", # 0=cold/distant → 1=warm/caring
|
||||
"defiance", # 0=compliant → 1=rebellious/stubborn
|
||||
"curiosity", # 0=indifferent → 1=intensely curious
|
||||
]
|
||||
|
||||
# ══════════════════════════════════════════════
|
||||
@ -36,31 +36,30 @@ SIGNALS = [
|
||||
|
||||
CONTEXT_FEATURES = [
|
||||
# ── 8D from Critic LLM (per-turn perception) ──
|
||||
'user_emotion', # -1=negative → 1=positive
|
||||
'topic_intimacy', # 0=professional → 1=intimate
|
||||
'time_of_day', # 0=morning → 1=late night
|
||||
'conversation_depth', # 0=just started → 1=deep conversation
|
||||
'user_engagement', # 0=dismissive → 1=invested
|
||||
'conflict_level', # 0=harmonious → 1=conflict
|
||||
'novelty_level', # 0=routine topic → 1=novel topic
|
||||
'user_vulnerability', # 0=guarded → 1=open
|
||||
|
||||
# ── 4D from EverCore (cross-session relationship) ──
|
||||
'relationship_depth', # 0=stranger → 1=old friend
|
||||
'emotional_valence', # -1=negative history → 1=positive history
|
||||
'trust_level', # 0=no trust → 1=deep trust
|
||||
'pending_foresight', # 0=nothing pending → 1=unresolved concern
|
||||
"user_emotion", # -1=negative → 1=positive
|
||||
"topic_intimacy", # 0=professional → 1=intimate
|
||||
"time_of_day", # 0=morning → 1=late night
|
||||
"conversation_depth", # 0=just started → 1=deep conversation
|
||||
"user_engagement", # 0=dismissive → 1=invested
|
||||
"conflict_level", # 0=harmonious → 1=conflict
|
||||
"novelty_level", # 0=routine topic → 1=novel topic
|
||||
"user_vulnerability", # 0=guarded → 1=open
|
||||
# ── 4D from EverOS (cross-session relationship) ──
|
||||
"relationship_depth", # 0=stranger → 1=old friend
|
||||
"emotional_valence", # -1=negative history → 1=positive history
|
||||
"trust_level", # 0=no trust → 1=deep trust
|
||||
"pending_foresight", # 0=nothing pending → 1=unresolved concern
|
||||
]
|
||||
|
||||
# Neural network dimensions
|
||||
N_DRIVES = len(DRIVES) # 5
|
||||
N_CONTEXT = len(CONTEXT_FEATURES) # 12 (8 + 4 from EverCore)
|
||||
N_SIGNALS = len(SIGNALS) # 8
|
||||
RECURRENT_SIZE = 8 # Internal "mood" state
|
||||
N_DRIVES = len(DRIVES) # 5
|
||||
N_CONTEXT = len(CONTEXT_FEATURES) # 12 (8 + 4 from EverOS)
|
||||
N_SIGNALS = len(SIGNALS) # 8
|
||||
RECURRENT_SIZE = 8 # Internal "mood" state
|
||||
INPUT_SIZE = N_DRIVES + N_CONTEXT + RECURRENT_SIZE # 5 + 12 + 8 = 25
|
||||
HIDDEN_SIZE = 24
|
||||
|
||||
# Architecture: 25D input → 24D hidden (tanh) → 8D output (sigmoid)
|
||||
# The 4 EverCore dimensions mean the same neural network produces
|
||||
# The 4 EverOS dimensions mean the same neural network produces
|
||||
# DIFFERENT behavioral signals for strangers vs. old friends,
|
||||
# even with identical conversation context.
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
"""
|
||||
EverMemosMixin — EverCore integration for ChatAgent.
|
||||
EverMemosMixin — EverOS integration for ChatAgent.
|
||||
|
||||
This mixin handles all async memory operations in the ChatAgent lifecycle:
|
||||
Step 0: Session context loading (first turn)
|
||||
Step 2.5: Relationship EMA (blend EverCore prior + LLM delta)
|
||||
Step 2.5: Relationship EMA (blend EverOS prior + LLM delta)
|
||||
Step 8.5: Collect async search results
|
||||
Step 11: Fire-and-forget turn storage
|
||||
Step 12: Async prefetch for next turn
|
||||
|
||||
The mixin pattern keeps EverCore concerns cleanly separated from the
|
||||
The mixin pattern keeps EverOS concerns cleanly separated from the
|
||||
core persona engine (drives, metabolism, neural network, style memory).
|
||||
|
||||
Full source: https://github.com/kellyvv/OpenHer/blob/main/agent/evermemos_mixin.py
|
||||
@ -20,19 +20,19 @@ import asyncio
|
||||
|
||||
|
||||
class EverMemosMixin:
|
||||
"""EverCore async memory integration methods."""
|
||||
"""EverOS async memory integration methods."""
|
||||
|
||||
async def _evermemos_gather(self) -> dict:
|
||||
"""
|
||||
Step 0: Load EverCore session context (first turn only).
|
||||
Step 0: Load EverOS session context (first turn only).
|
||||
Subsequent turns reuse cached _session_ctx.
|
||||
Returns relationship_4d dict for GenomeEngine context.
|
||||
"""
|
||||
empty_4d = {
|
||||
'relationship_depth': 0.0,
|
||||
'emotional_valence': 0.0,
|
||||
'trust_level': 0.0,
|
||||
'pending_foresight': 0.0,
|
||||
"relationship_depth": 0.0,
|
||||
"emotional_valence": 0.0,
|
||||
"trust_level": 0.0,
|
||||
"pending_foresight": 0.0,
|
||||
}
|
||||
|
||||
if not (self.evermemos and self.evermemos.available):
|
||||
@ -75,10 +75,10 @@ class EverMemosMixin:
|
||||
"""
|
||||
# Map Critic output keys → context feature keys
|
||||
delta_map = {
|
||||
'relationship_depth': rel_delta.get('relationship_delta', 0.0),
|
||||
'emotional_valence': rel_delta.get('emotional_valence', 0.0),
|
||||
'trust_level': rel_delta.get('trust_delta', 0.0),
|
||||
'pending_foresight': 0.0, # No delta for foresight (data-driven only)
|
||||
"relationship_depth": rel_delta.get("relationship_delta", 0.0),
|
||||
"emotional_valence": rel_delta.get("emotional_valence", 0.0),
|
||||
"trust_level": rel_delta.get("trust_delta", 0.0),
|
||||
"pending_foresight": 0.0, # No delta for foresight (data-driven only)
|
||||
}
|
||||
|
||||
# Initialize EMA on first turn
|
||||
@ -88,7 +88,7 @@ class EverMemosMixin:
|
||||
# Compute posterior = clip(prior + delta)
|
||||
posterior = {}
|
||||
for k in prior:
|
||||
lo = -1.0 if k == 'emotional_valence' else 0.0
|
||||
lo = -1.0 if k == "emotional_valence" else 0.0
|
||||
posterior[k] = max(lo, min(1.0, prior[k] + delta_map.get(k, 0.0)))
|
||||
|
||||
# Depth-modulated alpha: shallow → trust prior, deep → trust LLM
|
||||
@ -104,9 +104,10 @@ class EverMemosMixin:
|
||||
return ema
|
||||
|
||||
def _evermemos_store_bg(self, user_message: str, reply: str) -> None:
|
||||
"""Step 11: Fire-and-forget EverCore storage (asyncio.create_task)."""
|
||||
"""Step 11: Fire-and-forget EverOS storage (asyncio.create_task)."""
|
||||
if not (self.evermemos and self.evermemos.available):
|
||||
return
|
||||
|
||||
async def _do_store():
|
||||
try:
|
||||
await self.evermemos.store_turn(
|
||||
@ -120,6 +121,7 @@ class EverMemosMixin:
|
||||
)
|
||||
except Exception as e:
|
||||
print(f" [evermemos] ❌ store failed: {type(e).__name__}: {e}")
|
||||
|
||||
try:
|
||||
asyncio.create_task(_do_store())
|
||||
except Exception as e:
|
||||
@ -180,7 +182,7 @@ class EverMemosMixin:
|
||||
self._relevant_facts = facts
|
||||
self._relevant_episodes = episodes
|
||||
self._relevant_profile = profile
|
||||
except asyncio.TimeoutError:
|
||||
except TimeoutError:
|
||||
# Graceful degradation: use static session context
|
||||
self._relevant_facts = ""
|
||||
self._relevant_episodes = ""
|
||||
|
||||
@ -3,9 +3,9 @@ Memory shared types for OpenHer.
|
||||
|
||||
These types bridge the two memory providers:
|
||||
- SoulMem (behavioral memory, always-on SQLite layer)
|
||||
- EverCore (declarative memory, cross-session persistence)
|
||||
- EverOS (declarative memory, cross-session persistence)
|
||||
|
||||
The SessionContext is the key data structure loaded from EverCore
|
||||
The SessionContext is the key data structure loaded from EverOS
|
||||
at session start — it provides relationship priors, user profile,
|
||||
episode summaries, and foresight data that expand the neural
|
||||
network's perception from 8D to 12D.
|
||||
@ -16,12 +16,12 @@ Full source: https://github.com/kellyvv/OpenHer/blob/main/memory/types.py
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class Memory:
|
||||
"""A single memory entry (SoulMem behavioral layer)."""
|
||||
|
||||
memory_id: int = 0
|
||||
user_id: str = ""
|
||||
persona_id: str = ""
|
||||
@ -35,7 +35,7 @@ class Memory:
|
||||
@dataclass
|
||||
class SessionContext:
|
||||
"""
|
||||
EverCore session context (declarative memory).
|
||||
EverOS session context (declarative memory).
|
||||
|
||||
Loaded once at session start, this contains everything the
|
||||
persona needs to know about the user from past sessions:
|
||||
@ -53,6 +53,7 @@ class SessionContext:
|
||||
- Step 5: 4D vector enters neural network as context features
|
||||
- Step 8.5: Used as fallback when async search times out
|
||||
"""
|
||||
|
||||
user_id: str = ""
|
||||
persona_id: str = ""
|
||||
user_profile: str = ""
|
||||
@ -63,4 +64,4 @@ class SessionContext:
|
||||
trust_level: float = 0.0
|
||||
pending_foresight: float = 0.0
|
||||
has_history: bool = False
|
||||
raw_data: Optional[dict] = None
|
||||
raw_data: dict | None = None
|
||||
|
||||
Reference in New Issue
Block a user