#!/usr/bin/env python
"""Memory Chatbot (#16 of the 30-in-15) — a bot that remembers you across sessions.
The lesson is memory architecture: a short-term conversation buffer (this session) vs long-term memory
(a store that outlives the session). After each exchange the bot extracts durable facts and saves them;
on every turn it injects what it remembers. Close the tab, come back tomorrow — it still knows you.
pip install -r requirements.txt
python app.py # http://127.0.0.1:7860 (local Ollama by default; ANTHROPIC_API_KEY for Claude)
"""
import os
import uuid
from flask import Flask, request, render_template_string, jsonify, make_response
import llm
import store
app = Flask(__name__)
BASE_SYSTEM = ("You are a warm, helpful assistant with a long-term memory of the user across sessions. "
"Use what you remember naturally — don't recite it back robotically. Be concise.")
MAX_BUFFER = 8 # short-term turns folded in per request
PAGE = r"""
GritAI · Memory Chatbot
GritAI · Memory Chatbot
Remembers you across sessions · close the tab, come back — it still knows you
#16 · 30-in-15
Hi! I'll remember what you tell me — your name, what you're working on, how you like things — and recall it next time you're here. Tell me a bit about yourself.
Backend: {{ backend }} · GritAI Solutions
🧠 Memory
what I remember about you — persists across sessions
nothing yet — tell me something durable
"""
def _user():
return request.cookies.get("memuser") or ""
@app.route("/")
def index():
resp = make_response(render_template_string(PAGE, backend=llm.backend_label()))
if not request.cookies.get("memuser"):
resp.set_cookie("memuser", uuid.uuid4().hex, max_age=60 * 60 * 24 * 365, samesite="Lax")
return resp
@app.route("/api/memory")
def api_memory():
return jsonify({"facts": store.get_facts(_user())})
@app.route("/api/memory/delete", methods=["POST"])
def api_delete():
d = request.get_json(force=True)
store.delete_fact(_user(), int(d.get("id", 0)))
return jsonify({"ok": True})
@app.route("/api/memory/clear", methods=["POST"])
def api_clear():
store.clear(_user())
return jsonify({"ok": True})
@app.route("/api/chat", methods=["POST"])
def api_chat():
user_id = _user()
d = request.get_json(force=True)
message = (d.get("message") or "").strip()[:2000]
if not message:
return jsonify({"reply": "Say something!", "remembered": []})
history = d.get("history") or []
system = BASE_SYSTEM + store.memory_block(user_id) # long-term memory injected here
# short-term buffer: recent turns folded into the prompt
recent = history[-MAX_BUFFER * 2:-1] if len(history) > 1 else []
convo = "".join(f"{m['role']}: {m['content']}\n" for m in recent)
user_prompt = (f"Recent conversation:\n{convo}\n" if convo else "") + f"User: {message}"
try:
reply = llm.chat(system, user_prompt, temperature=0.6, max_tokens=600)
except Exception as e:
print(f"chat error: {type(e).__name__}")
return jsonify({"reply": "My brain backend hiccuped — is the model running?", "remembered": []})
remembered = []
try:
remembered = store.remember_from_exchange(user_id, message, reply)
except Exception as e:
print(f"memory error: {type(e).__name__}")
return jsonify({"reply": reply, "remembered": remembered})
if __name__ == "__main__":
port = int(os.environ.get("PORT", "7860"))
app.run(host="0.0.0.0", port=port, debug=False)