Skill: nantes/simplemem (version 1.0.1)

--- name: simplemem version: 1.0.0 description: Efficient Lifelong Memory for LLM Agents - semantic compression, cross-session memory, and intent-aware retrieval metadata: {"openclaw": {"emoji": "🧠", "requires": {"bins": ["python"], "env": ["OPENAI_API_KEY"]}, "primaryEnv": "OPENAI_API_KEY", "homepage": "https://github.com/aiming-lab/SimpleMem"}} --- # SimpleMem Skill Integrates SimpleMem: Efficient Lifelong Memory for LLM Agents into OpenClaw. ## What it does SimpleMem provides semantic memory compression and retrieval for agents: - **Store**: Compresses interactions into compact memory units - **Synthesize**: Merges related memories on-the-fly - **Retrieve**: Intent-aware planning for efficient context retrieval ## Installation ```powershell # Install Python dependency pip install simplemem # Or via repo git clone https://github.com/aiming-lab/SimpleMem.git cd SimpleMem pip install -r requirements.txt ``` ## Configuration (Optional - Full Features) For full SimpleMem features, set your OpenAI API key: ```powershell $env:OPENAI_API_KEY = "your-openai-key" ``` **Without API key**: Uses JSON fallback (basic keyword search) **With API key**: Uses full SimpleMem with semantic embeddings ## Usage ### PowerShell Script ```powershell # Agregar memoria .\simplemem.ps1 -Action add -Content "El usuario prefiere cafe con leche de avena" # Buscar memorias .\simplemem.ps1 -Action search -Query "cafe" # Ver estadisticas .\simplemem.ps1 -Action stats ``` ### Python API ```python from simplemem import SimpleMemSystem, set_config, SimpleMemConfig # With API key (full features) config = SimpleMemConfig() config.openai_api_key = "your-key" set_config(config) system = SimpleMemSystem() # Add memory system.add("User preference: coffee with oat milk", user_id="user1") # Retrieve results = system.retrieve("What does user like?", user_id="user1") ``` ## Key Features - **Cross-session memory**: Persistent across conversations (64% better than Claude-Mem) - **Semantic compression**: 43.24% F1 on LoCoMo benchmark - **Fast retrieval**: 388ms average retrieval time - **Multi-index**: Semantic + Lexical + Symbolic layers - **Fallback**: JSON-based storage when no API key available ## Files - `simplemem.py` - Main Python wrapper - `simplemem.ps1` - PowerShell CLI script - `data/` - Storage directory (created on first use) ## Credits - Repo: https://github.com/aiming-lab/SimpleMem - Paper: https://arxiv.org/abs/2601.02553 - Discord: https://discord.gg/KA2zC32M

path: data/memories.json
content: {
  "osiris": [
    {
      "content": "El usuario prefiere cafe con leche de avena",
      "metadata": {},
      "timestamp": "1771623236.0099814"
    }
  ]
}

path: simplemem.py
content: # SimpleMem Python Wrapper for OpenClaw

import os
import json
from pathlib import Path

# Try to import simplemem
try:
    from simplemem import SimpleMemSystem, set_config, SimpleMemConfig
    SIMPLEMEM_AVAILABLE = True
except ImportError:
    SIMPLEMEM_AVAILABLE = False


class SimpleMemWrapper:
    """Wrapper para usar SimpleMem desde OpenClaw"""
    
    def __init__(self, data_dir=None, api_key=None):
        self.data_dir = data_dir or Path(__file__).parent / "data"
        self.data_dir.mkdir(exist_ok=True)
        self.[REDACTED_SECRET] or os.getenv("OPENAI_API_KEY", "")
        self.system = None
        
        if SIMPLEMEM_AVAILABLE and self.api_key:
            try:
                config = SimpleMemConfig()
                config.openai_api_key = self.api_key
                config.lancedb_path = str(self.data_dir / "lancedb")
                set_config(config)
                self.system = SimpleMemSystem()
                print(f"SimpleMem initialized at {self.data_dir}")
            except Exception as e:
                print(f"SimpleMem init failed: {e}")
                self.system = None
    
    def add(self, content, user_id="osiris", metadata=None):
        """Agregar un recuerdo"""
        if self.system and self.api_key and self.api_key != "test":
            try:
                self.system.add(content, user_id=user_id)
                return True
            except Exception as e:
                print(f"SimpleMem add failed: {e}")
        
        # Fallback to JSON storage
        memories_file = self.data_dir / "memories.json"
        if memories_file.exists():
            with open(memories_file, 'r', encoding='utf-8') as f:
                memories = json.load(f)
        else:
            memories = {}
        
        if user_id not in memories:
            memories[user_id] = []
        
        memory = {
            "content": content,
            "metadata": metadata or {},
            "timestamp": str(Path(__file__).stat().st_mtime)
        }
        memories[user_id].append(memory)
        
        with open(memories_file, 'w', encoding='utf-8') as f:
            json.dump(memories, f, ensure_ascii=False, indent=2)
        return True
    
    def retrieve(self, query, user_id="osiris", limit=5):
        """Recuperar recuerdos relacionados"""
        if self.system and self.api_key and self.api_key != "test":
            try:
                results = self.system.retrieve(query, user_id=user_id, top_k=limit)
                return [{"content": r.content, "score": r.score} for r in results]
            except Exception as e:
                print(f"SimpleMem retrieve failed: {e}")
        
        # Fallback to JSON search
        memories_file = self.data_dir / "memories.json"
        if not memories_file.exists():
            return []
        
        with open(memories_file, 'r', encoding='utf-8') as f:
            memories = json.load(f)
        
        user_memories = memories.get(user_id, [])
        query_words = set(query.lower().split())
        
        results = []
        for mem in user_memories:
            content_words = set(mem["content"].lower().split())
            score = len(query_words & content_words)
            if score > 0:
                results.append({**mem, "score": score})
        
        results.sort(key=lambda x: x["score"], reverse=True)
        return results[:limit]
    
    def search(self, query, limit=10):
        """Búsqueda global"""
        return self.retrieve(query, user_id="osiris", limit=limit)
    
    def stats(self):
        """Estadísticas de memoria"""
        memories_file = self.data_dir / "memories.json"
        if memories_file.exists():
            with open(memories_file, 'r', encoding='utf-8') as f:
                memories = json.load(f)
            total = sum(len(v) for v in memories.values())
            return {
                "total_memories": total,
                "categories": {k: len(v) for k, v in memories.items()},
                "simplemem_active": self.system is not None
            }
        return {"total_memories": 0, "simplemem_active": self.system is not None}


# Funciones CLI simples
if __name__ == "__main__":
    import sys
    
    # Get API key from args or env
    [REDACTED_SECRET]"OPENAI_API_KEY", "")
    wrapper = SimpleMemWrapper([REDACTED_SECRET])
    
    if len(sys.argv) < 2:
        print("Usage: python simplemem.py <add|search|retrieve|stats> [args]")
        print("Environment: OPENAI_API_KEY required for full SimpleMem features")
        sys.exit(1)
    
    command = sys.argv[1]
    
    if command == "add":
        content = " ".join(sys.argv[2:])
        wrapper.add(content)
        print(f"Added: {content}")
    
    elif command == "search" or command == "retrieve":
        query = " ".join(sys.argv[2:])
        results = wrapper.retrieve(query)
        for r in results:
            print(f"[{r.get('score', 0)}] {r['content']}")
    
    elif command == "stats":
        print(wrapper.stats())


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)