"""Give ANY LLM agent persistent memory: two tools (remember / recall) over Wicked Memory. Framework-agnostic: TOOLS is plain JSON-schema (works with the OpenAI, Anthropic, Gemini and most local-model tool-calling APIs; LangGraph / CrewAI / AutoGen / PydanticAI can wrap `call_tool` as a function tool). pip install httpx eth-account # curl -O https://memory.wickedapi.com/clients/wicked_memory.py export WICKED_MEMORY_KEY=... # free community key from /llms.txt (or install x402[httpx,evm] and fund the wallet) The agent keeps one wallet in .agent_wallet so it remembers across restarts. KEEP THAT FILE SECRET: whoever has the key can read the agent's memories. """ from __future__ import annotations import os import pathlib from eth_account import Account from wicked_memory import WickedMemorySync WALLET_FILE = pathlib.Path(os.environ.get("AGENT_WALLET_FILE", ".agent_wallet")) def _wallet_key() -> str: if os.environ.get("AGENT_KEY"): return os.environ["AGENT_KEY"] if not WALLET_FILE.exists(): WALLET_FILE.write_text(Account.create().key.hex()) try: WALLET_FILE.chmod(0o600) except OSError: pass return WALLET_FILE.read_text().strip() _mem = WickedMemorySync(private_key=_wallet_key(), api_key=os.environ.get("WICKED_MEMORY_KEY")) TOOLS = [ { "name": "remember", "description": "Save a fact, preference, decision or lesson to long-term memory so it is available in future sessions.", "input_schema": { "type": "object", "properties": { "text": {"type": "string", "description": "One self-contained statement worth remembering."}, "tags": {"type": "array", "items": {"type": "string"}, "description": "Optional short labels."}, }, "required": ["text"], }, }, { "name": "recall", "description": "Search long-term memory by meaning. Use before answering anything that may depend on past sessions.", "input_schema": { "type": "object", "properties": { "query": {"type": "string", "description": "What you want to remember, in natural language."}, "limit": {"type": "integer", "description": "Max results (default 5)."}, }, "required": ["query"], }, }, ] def call_tool(name: str, args: dict) -> str: """Dispatch a model-issued tool call; returns text to hand back to the model.""" if name == "remember": saved = _mem.store(args["text"], tags=args.get("tags"), source="agent") return f"Saved (id {saved['data']['id']})." if name == "recall": hits = _mem.search(args["query"], limit=args.get("limit", 5))["data"] return "\n".join(f"[{h['score']:.2f}] {h['content']}" for h in hits) or "Nothing relevant remembered." raise ValueError(f"unknown tool {name!r}") if __name__ == "__main__": # No LLM needed to see it work: run this file twice. Run 1 remembers, run 2 (a fresh process) recalls. if not _mem.search("favorite deployment target", limit=1)["data"]: print(call_tool("remember", {"text": "Our favorite deployment target is Railway.", "tags": ["ops"]})) print("Run me again: a brand-new process will recall it.") else: print(call_tool("recall", {"query": "where do we deploy?"}))