inkference-app / app /src /inkference /agent /checkpoint.py
sajitkun125's picture
deploy app
26d4279 verified
Raw
History Blame Contribute Delete
2.53 kB
"""Conversation memory: a LangGraph SQLite checkpointer keyed by thread id.
The checkpoint DB is deliberately NOT inkference.db. deploy_all_books.sh copies
inkference.db into the PUBLIC HF seed dataset, and conversation history must never
ship with the corpus. It also keeps langgraph's checkpoint-schema churn away from
the corpus schema, and makes the whole thing safe to delete.
"""
from __future__ import annotations
import logging
import sqlite3
import threading
from ..config import agent as agent_cfg
logger = logging.getLogger("inkference.agent")
_checkpointer = None
_lock = threading.Lock()
def get_checkpointer():
"""Process-wide checkpointer. Degrades to in-memory rather than failing to boot."""
global _checkpointer
if _checkpointer is not None:
return _checkpointer
with _lock:
if _checkpointer is not None:
return _checkpointer
try:
from langgraph.checkpoint.sqlite import SqliteSaver
path = agent_cfg.checkpoint_path
path.parent.mkdir(parents=True, exist_ok=True)
# One shared connection guarded by langgraph's own lock. This departs
# from store.py's fresh-connection-per-op convention on purpose:
# SqliteSaver owns its connection for the process lifetime, and FastAPI
# runs sync endpoints in a threadpool, hence check_same_thread=False.
conn = sqlite3.connect(str(path), check_same_thread=False)
conn.execute("PRAGMA journal_mode=WAL")
_checkpointer = SqliteSaver(conn)
logger.info("agent: checkpointer=sqlite at %s", path)
except Exception as exc: # missing dep, read-only fs, locked db
from langgraph.checkpoint.memory import MemorySaver
logger.warning(
"agent: sqlite checkpointer unavailable (%s); "
"falling back to in-memory (history lost on restart)", exc,
)
_checkpointer = MemorySaver()
return _checkpointer
def kind() -> str:
""""sqlite" | "memory" — surfaced in GET /api/health."""
return type(get_checkpointer()).__name__.replace("Saver", "").lower()
def delete_thread(thread_id: str) -> bool:
"""Forget one conversation ("New conversation" in the UI)."""
saver = get_checkpointer()
try:
saver.delete_thread(thread_id)
return True
except Exception as exc:
logger.warning("agent: could not delete thread %r: %s", thread_id, exc)
return False