Persist chat history locally

This commit is contained in:
seo-amugae committed 2026-08-09 18:52:19 +09:00
1 parent 0ef55b1489
commit 2b2e5fcb0f
5 files changed
+165 -17

No files matched your search

+95 -1
View File
@@ -2,6 +2,8 @@ from __future__ import annotations
import json
import socket
import uuid
from datetime import datetime, timezone
from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer
from pathlib import Path
@@ -9,15 +11,71 @@ import ollama
ROOT = Path(__file__).parent
DATA_DIR = ROOT / "data"
CHATS_FILE = DATA_DIR / "chats.json"
MODEL = "qwen2.5:7b"
HOST = "0.0.0.0"
PORT = 8787
def now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
def read_store() -> dict:
if not CHATS_FILE.exists():
return {"chats": []}
try:
return json.loads(CHATS_FILE.read_text(encoding="utf-8"))
except json.JSONDecodeError:
return {"chats": []}
def write_store(store: dict) -> None:
DATA_DIR.mkdir(exist_ok=True)
CHATS_FILE.write_text(json.dumps(store, ensure_ascii=False, indent=2), encoding="utf-8")
def chat_title(messages: list[dict]) -> str:
for message in messages:
if message.get("role") == "user" and message.get("content"):
title = " ".join(str(message["content"]).split())
return title[:40] or "새 대화"
return "새 대화"
class Handler(SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=str(ROOT), **kwargs)
def do_GET(self) -> None:
if self.path == "/api/chats":
store = read_store()
chats = [
{
"id": chat["id"],
"title": chat.get("title", "새 대화"),
"updatedAt": chat.get("updatedAt", ""),
}
for chat in store.get("chats", [])
]
self._send_json(200, {"chats": chats})
return
if self.path.startswith("/api/chats/"):
chat_id = self.path.rsplit("/", 1)[-1]
store = read_store()
chat = next((item for item in store.get("chats", []) if item["id"] == chat_id), None)
if not chat:
self._send_json(404, {"error": "Chat not found"})
return
self._send_json(200, {"chat": chat})
return
super().do_GET()
def do_POST(self) -> None:
if self.path != "/api/chat":
self.send_error(404)
@@ -25,6 +83,7 @@ class Handler(SimpleHTTPRequestHandler):
length = int(self.headers.get("content-length", 0))
data = json.loads(self.rfile.read(length) or b"{}")
chat_id = data.get("chatId")
messages = data.get("messages", [])
try:
@@ -38,11 +97,46 @@ class Handler(SimpleHTTPRequestHandler):
*messages,
],
)
payload = {"reply": response["message"]["content"]}
reply = response["message"]["content"]
saved_messages = [*messages, {"role": "assistant", "content": reply}]
chat = self._save_chat(chat_id, saved_messages)
payload = {"chatId": chat["id"], "reply": reply, "chat": chat}
self._send_json(200, payload)
except Exception as exc:
self._send_json(500, {"error": str(exc)})
def do_DELETE(self) -> None:
if not self.path.startswith("/api/chats/"):
self.send_error(404)
return
chat_id = self.path.rsplit("/", 1)[-1]
store = read_store()
store["chats"] = [item for item in store.get("chats", []) if item["id"] != chat_id]
write_store(store)
self._send_json(200, {"ok": True})
def _save_chat(self, chat_id: str | None, messages: list[dict]) -> dict:
store = read_store()
chats = store.setdefault("chats", [])
chat = next((item for item in chats if item["id"] == chat_id), None)
if not chat:
chat = {
"id": str(uuid.uuid4()),
"createdAt": now_iso(),
"title": chat_title(messages),
"messages": [],
}
chats.insert(0, chat)
chat["title"] = chat_title(messages)
chat["messages"] = messages
chat["updatedAt"] = now_iso()
chats.sort(key=lambda item: item.get("updatedAt", ""), reverse=True)
write_store(store)
return chat
def _send_json(self, status: int, payload: dict[str, str]) -> None:
body = json.dumps(payload, ensure_ascii=False).encode("utf-8")
self.send_response(status)