Implements unified cross-server memory system for Miku bot:
**Core Changes:**
- discord_bridge plugin with 3 hooks for metadata enrichment
- Unified user identity: discord_user_{id} across servers and DMs
- Minimal filtering: skip only trivial messages (lol, k, 1-2 chars)
- Marks all memories as consolidated=False for Phase 2 processing
**Testing:**
- test_phase1.py validates cross-server memory recall
- PHASE1_TEST_RESULTS.md documents successful validation
- Cross-server test: User says 'blue' in Server A, Miku remembers in Server B ✅
**Documentation:**
- IMPLEMENTATION_PLAN.md - Complete architecture and roadmap
- Phase 2 (sleep consolidation) ready for implementation
This lays the foundation for human-like memory consolidation.
240 lines
6.9 KiB
Python
Executable File
240 lines
6.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Phase 1 Test Script
|
|
|
|
Tests the Discord bridge plugin:
|
|
1. Unified user identity (same user across servers/DMs)
|
|
2. Metadata enrichment (guild_id, channel_id)
|
|
3. Minimal filtering (skip "lol", "k", etc.)
|
|
4. Temporary storage (consolidated=false)
|
|
"""
|
|
|
|
import requests
|
|
import json
|
|
import time
|
|
from datetime import datetime
|
|
|
|
|
|
CAT_URL = "http://localhost:1865"
|
|
TEST_USER_ID = "discord_user_test123"
|
|
|
|
|
|
def test_message(text: str, guild_id: str = None, channel_id: str = None, description: str = ""):
|
|
"""Send a message to Cat and return the response"""
|
|
print(f"\n{'='*80}")
|
|
print(f"TEST: {description}")
|
|
print(f"Message: '{text}'")
|
|
print(f"Guild: {guild_id or 'DM'}, Channel: {channel_id or 'N/A'}")
|
|
|
|
payload = {
|
|
"text": text,
|
|
"user_id": TEST_USER_ID
|
|
}
|
|
|
|
# Add Discord context to working memory
|
|
if guild_id or channel_id:
|
|
payload["metadata"] = {
|
|
"guild_id": guild_id,
|
|
"channel_id": channel_id
|
|
}
|
|
|
|
try:
|
|
response = requests.post(
|
|
f"{CAT_URL}/message",
|
|
json=payload,
|
|
timeout=30
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
result = response.json()
|
|
print(f"✅ Response: {result.get('content', '')[:100]}...")
|
|
return True
|
|
else:
|
|
print(f"❌ Error: {response.status_code} - {response.text}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"❌ Exception: {e}")
|
|
return False
|
|
|
|
|
|
def get_memories(user_id: str = TEST_USER_ID):
|
|
"""Retrieve all memories for test user"""
|
|
try:
|
|
# Cat API endpoint for memories (may vary based on version)
|
|
response = requests.get(
|
|
f"{CAT_URL}/memory/collections",
|
|
timeout=10
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
# This is a simplified check - actual API may differ
|
|
print(f"\n📊 Memory collections available: {list(data.keys())}")
|
|
return data
|
|
else:
|
|
print(f"⚠️ Could not retrieve memories: {response.status_code}")
|
|
return None
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Exception getting memories: {e}")
|
|
return None
|
|
|
|
|
|
def check_cat_health():
|
|
"""Check if Cat is running"""
|
|
try:
|
|
response = requests.get(f"{CAT_URL}/", timeout=5)
|
|
if response.status_code == 200:
|
|
print("✅ Cheshire Cat is running")
|
|
return True
|
|
except:
|
|
pass
|
|
|
|
print("❌ Cheshire Cat is not accessible at", CAT_URL)
|
|
return False
|
|
|
|
|
|
def main():
|
|
print("="*80)
|
|
print("PHASE 1 TEST: Discord Bridge Plugin")
|
|
print("="*80)
|
|
|
|
# Check Cat is running
|
|
if not check_cat_health():
|
|
print("\n⚠️ Start Cheshire Cat first:")
|
|
print(" cd cheshire-cat")
|
|
print(" docker-compose -f docker-compose.test.yml up -d")
|
|
return
|
|
|
|
print(f"\n🧪 Testing with user ID: {TEST_USER_ID}")
|
|
print(" (Same user across all contexts - unified identity)")
|
|
|
|
# Wait a bit for Cat to be fully ready
|
|
time.sleep(2)
|
|
|
|
# Test 1: Message in Server A
|
|
print("\n" + "="*80)
|
|
print("TEST SUITE 1: Unified User Identity")
|
|
print("="*80)
|
|
|
|
test_message(
|
|
"Hello Miku! I'm in Server A",
|
|
guild_id="server_a_12345",
|
|
channel_id="general_111",
|
|
description="Message in Server A"
|
|
)
|
|
time.sleep(1)
|
|
|
|
test_message(
|
|
"My favorite color is blue",
|
|
guild_id="server_a_12345",
|
|
channel_id="chat_222",
|
|
description="Share preference in Server A"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Test 2: Same user in Server B
|
|
test_message(
|
|
"Hi Miku! I'm the same person from Server A",
|
|
guild_id="server_b_67890",
|
|
channel_id="general_333",
|
|
description="Message in Server B (should recognize user)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Test 3: Same user in DM
|
|
test_message(
|
|
"Hey Miku, it's me in a DM now",
|
|
guild_id=None,
|
|
channel_id=None,
|
|
description="Message in DM (should recognize user)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Test 4: Miku should remember across contexts
|
|
test_message(
|
|
"What's my favorite color?",
|
|
guild_id="server_b_67890",
|
|
channel_id="general_333",
|
|
description="Test cross-server memory recall"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Test Suite 2: Filtering
|
|
print("\n" + "="*80)
|
|
print("TEST SUITE 2: Minimal Filtering")
|
|
print("="*80)
|
|
|
|
test_message(
|
|
"lol",
|
|
guild_id="server_a_12345",
|
|
channel_id="chat_222",
|
|
description="Should be filtered (pure reaction)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
test_message(
|
|
"k",
|
|
guild_id="server_a_12345",
|
|
channel_id="chat_222",
|
|
description="Should be filtered (1-2 chars)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
test_message(
|
|
"I'm really excited about the upcoming concert!",
|
|
guild_id="server_a_12345",
|
|
channel_id="music_444",
|
|
description="Should be stored (meaningful content)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Test Suite 3: Metadata
|
|
print("\n" + "="*80)
|
|
print("TEST SUITE 3: Metadata Verification")
|
|
print("="*80)
|
|
|
|
test_message(
|
|
"My birthday is coming up next week",
|
|
guild_id="server_a_12345",
|
|
channel_id="general_111",
|
|
description="Important event (should be stored with metadata)"
|
|
)
|
|
time.sleep(1)
|
|
|
|
# Summary
|
|
print("\n" + "="*80)
|
|
print("TEST SUMMARY")
|
|
print("="*80)
|
|
|
|
print("\n✅ EXPECTED BEHAVIOR:")
|
|
print(" 1. Same user recognized across Server A, Server B, and DMs")
|
|
print(" 2. 'lol' and 'k' filtered out (not stored)")
|
|
print(" 3. Meaningful messages stored with guild_id/channel_id metadata")
|
|
print(" 4. All memories marked as consolidated=false (pending nightly processing)")
|
|
print(" 5. Miku remembers 'blue' as favorite color across servers")
|
|
|
|
print("\n📋 MANUAL VERIFICATION STEPS:")
|
|
print(" 1. Check Docker logs:")
|
|
print(" docker logs miku_cheshire_cat_test | tail -50")
|
|
print(" 2. Look for:")
|
|
print(" - '💾 [Discord Bridge] Storing memory' for kept messages")
|
|
print(" - '🗑️ [Discord Bridge] Skipping trivial' for filtered messages")
|
|
print(" - '🧠 [Discord Bridge] Recalled X memories' for memory retrieval")
|
|
print(" 3. Verify Miku responded appropriately to 'What's my favorite color?'")
|
|
|
|
print("\n🔍 CHECK MEMORIES:")
|
|
get_memories()
|
|
|
|
print("\n✨ Phase 1 testing complete!")
|
|
print("\nNext steps:")
|
|
print(" 1. Review logs to confirm filtering works")
|
|
print(" 2. Verify metadata is attached to memories")
|
|
print(" 3. Confirm unified user identity works (same user across contexts)")
|
|
print(" 4. Move to Phase 2: Implement nightly consolidation")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|