Spaces:
Configuration error
Configuration error
| """ | |
| database.py β SQLite persistence layer | |
| All raw DB operations live here. No MCP or business logic. | |
| """ | |
| import json | |
| import sqlite3 | |
| import time | |
| from datetime import datetime, timezone | |
| from typing import Optional | |
| from mcp_config import DB_PATH | |
| # ββ Connection ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def get_db() -> sqlite3.Connection: | |
| conn = sqlite3.connect(DB_PATH) | |
| conn.row_factory = sqlite3.Row | |
| conn.execute(""" | |
| CREATE TABLE IF NOT EXISTS context ( | |
| namespace TEXT NOT NULL, | |
| key TEXT NOT NULL, | |
| value TEXT NOT NULL, | |
| tags TEXT NOT NULL DEFAULT '[]', | |
| created_at TEXT NOT NULL, | |
| updated_at TEXT NOT NULL, | |
| expires_at TEXT, | |
| shared_from TEXT, | |
| PRIMARY KEY (namespace, key) | |
| ) | |
| """) | |
| conn.commit() | |
| return conn | |
| # ββ Time helpers ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def now() -> str: | |
| return datetime.now(timezone.utc).isoformat() | |
| def expires_at(ttl: int) -> Optional[str]: | |
| if ttl <= 0: | |
| return None | |
| return datetime.fromtimestamp(time.time() + ttl, tz=timezone.utc).isoformat() | |
| def is_expired(exp: Optional[str]) -> bool: | |
| if not exp: | |
| return False | |
| return datetime.fromisoformat(exp) < datetime.now(timezone.utc) | |
| # ββ CRUD operations βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def upsert_entry(namespace: str, key: str, value: str, | |
| tag_list: list, exp: Optional[str]) -> str: | |
| """Insert or update a context entry. Returns 'created' or 'updated'.""" | |
| ts = now() | |
| with get_db() as db: | |
| if existing := db.execute( | |
| "SELECT key FROM context WHERE namespace=? AND key=?", | |
| (namespace, key), | |
| ).fetchone(): | |
| db.execute( | |
| "UPDATE context SET value=?, tags=?, updated_at=?, expires_at=? " | |
| "WHERE namespace=? AND key=?", | |
| (value, json.dumps(tag_list), ts, exp, namespace, key) | |
| ) | |
| return "updated" | |
| db.execute( | |
| "INSERT INTO context VALUES (?,?,?,?,?,?,?,?)", | |
| (namespace, key, value, json.dumps(tag_list), ts, ts, exp, None) | |
| ) | |
| return "created" | |
| def fetch_entry(namespace: str, key: str) -> Optional[sqlite3.Row]: | |
| with get_db() as db: | |
| return db.execute( | |
| "SELECT value, expires_at FROM context WHERE namespace=? AND key=?", | |
| (namespace, key) | |
| ).fetchone() | |
| def remove_entry(namespace: str, key: str) -> int: | |
| with get_db() as db: | |
| cursor = db.execute( | |
| "DELETE FROM context WHERE namespace=? AND key=?", (namespace, key) | |
| ) | |
| return cursor.rowcount | |
| def fetch_namespace(namespace: str) -> list: | |
| with get_db() as db: | |
| return db.execute( | |
| "SELECT key, value, tags, updated_at, expires_at FROM context WHERE namespace=?", | |
| (namespace,) | |
| ).fetchall() | |
| def remove_expired_keys(namespace: str, keys: list): | |
| with get_db() as db: | |
| db.executemany( | |
| "DELETE FROM context WHERE namespace=? AND key=?", | |
| [(namespace, k) for k in keys] | |
| ) | |
| def search_entries(namespace: str, query: str) -> list: | |
| with get_db() as db: | |
| return db.execute( | |
| "SELECT key, value, tags, expires_at FROM context " | |
| "WHERE namespace=? AND (value LIKE ? OR key LIKE ?)", | |
| (namespace, f"%{query}%", f"%{query}%") | |
| ).fetchall() | |
| def copy_entry(source_ns: str, key: str, target_ns: str, dest_key: str): | |
| with get_db() as db: | |
| row = db.execute( | |
| "SELECT * FROM context WHERE namespace=? AND key=?", (source_ns, key) | |
| ).fetchone() | |
| if not row: | |
| return None | |
| db.execute( | |
| "INSERT OR REPLACE INTO context VALUES (?,?,?,?,?,?,?,?)", | |
| (target_ns, dest_key, row["value"], row["tags"], | |
| row["created_at"], now(), row["expires_at"], f"{source_ns}/{key}") | |
| ) | |
| return row | |
| def fetch_all_namespaces() -> list: | |
| with get_db() as db: | |
| return db.execute( | |
| "SELECT namespace, COUNT(*) as count FROM context GROUP BY namespace" | |
| ).fetchall() | |
| def remove_namespace(namespace: str) -> int: | |
| with get_db() as db: | |
| cursor = db.execute("DELETE FROM context WHERE namespace=?", (namespace,)) | |
| return cursor.rowcount | |
| def fetch_stats() -> dict: | |
| with get_db() as db: | |
| total = db.execute("SELECT COUNT(*) as n FROM context").fetchone()["n"] | |
| ns_cnt = db.execute("SELECT COUNT(DISTINCT namespace) as n FROM context").fetchone()["n"] | |
| expired = db.execute( | |
| "SELECT COUNT(*) as n FROM context WHERE expires_at IS NOT NULL AND expires_at < ?", | |
| (now(),) | |
| ).fetchone()["n"] | |
| return {"total": total, "namespaces": ns_cnt, "expired": expired} |