Initial commit — Cortex API + Inara identity
Cortex: FastAPI backend serving Inara via Claude/Gemini CLI backends. Includes SSE streaming chat, session persistence, Google Chat webhook handler, and Docker support. Inara: Identity files (persona, soul, protocols, memory, context tiers) mounted read-only into the container at runtime. Features in initial cut: - /chat endpoint with SSE keepalive + LLM fallback - Session store with rolling history window - Markdown rendering, copy-to-clipboard, links open in new tab - Stacked right-column input controls (height selector, enter toggle, note mode with public/private) — semi-hidden until textarea grows - /note endpoint for injecting public context into session history - Docker Compose config (local dev runs natively; Docker for server) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
50
cortex/session_store.py
Normal file
50
cortex/session_store.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from config import settings
|
||||
|
||||
|
||||
def _path(session_id: str) -> Path:
|
||||
d = settings.sessions_path()
|
||||
d.mkdir(parents=True, exist_ok=True)
|
||||
return d / f"{session_id}.json"
|
||||
|
||||
|
||||
def load(session_id: str) -> list[dict]:
|
||||
path = _path(session_id)
|
||||
if not path.exists():
|
||||
return []
|
||||
return json.loads(path.read_text()).get("messages", [])
|
||||
|
||||
|
||||
def save(session_id: str, messages: list[dict]) -> None:
|
||||
path = _path(session_id)
|
||||
existing = json.loads(path.read_text()) if path.exists() else {}
|
||||
|
||||
# Enforce rolling window
|
||||
windowed = messages[-settings.max_history_messages:]
|
||||
|
||||
path.write_text(json.dumps({
|
||||
"session_id": session_id,
|
||||
"created": existing.get("created", datetime.now().isoformat()),
|
||||
"updated": datetime.now().isoformat(),
|
||||
"messages": windowed,
|
||||
}, indent=2))
|
||||
|
||||
|
||||
def list_all() -> list[dict]:
|
||||
d = settings.sessions_path()
|
||||
if not d.exists():
|
||||
return []
|
||||
results = []
|
||||
for f in sorted(d.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
|
||||
try:
|
||||
data = json.loads(f.read_text())
|
||||
results.append({
|
||||
"session_id": data["session_id"],
|
||||
"updated": data.get("updated"),
|
||||
"message_count": len(data.get("messages", [])),
|
||||
})
|
||||
except Exception:
|
||||
pass
|
||||
return results
|
||||
Reference in New Issue
Block a user