chore: initial commit — chat-saiden web chat baseline

This commit is contained in:
marauder-actual
2026-05-29 13:47:34 +02:00
commit 96ba8f4b6e
28 changed files with 4852 additions and 0 deletions
+88
View File
@@ -0,0 +1,88 @@
"""Operator → cart persistence for chat-saiden.
A 'cart' here is the per-operator calibrated config: persona name, voice,
system prompt, UI palette. Stored as JSON on disk under
~/.local/share/chat-saiden/operators/<email>.json.
Later this can be promoted to a real `marauder cart` once the format
stabilises.
"""
from __future__ import annotations
import json
import logging
import os
import re
from dataclasses import asdict, dataclass, field
from datetime import datetime
from pathlib import Path
log = logging.getLogger("chat-saiden.cart")
DATA_DIR = Path(
os.environ.get("CHAT_SAIDEN_DATA_DIR")
or (Path.home() / ".local/share/chat-saiden")
)
OPERATORS_DIR = DATA_DIR / "operators"
@dataclass
class Cart:
operator_email: str
operator_name: str = ""
persona_name: str = "Samantha"
cart_tag: str = "" # marauder cart tag — links to ~/.marauder cart DB
language: str = "en" # en | pl
voice: str = "en_US-amy-medium"
system_prompt: str = ""
# UI calibration outputs. All default to neutral.
ui_palette: str = "default" # default | rose | morning | evening | sage | paper | ink
ui_typography: str = "sans" # sans | serif-warm | serif-formal | mixed-modern | mono
ui_density: str = "normal" # airy | normal | dense
ui_labels: str = "block" # block | cursive | none | prefix
created_at: str = ""
version: int = 3
@property
def is_calibrated(self) -> bool:
return bool(self.system_prompt and self.persona_name and self.voice)
def _slug(email: str) -> str:
return re.sub(r"[^a-z0-9._-]", "_", email.lower())
def _path(email: str) -> Path:
OPERATORS_DIR.mkdir(parents=True, exist_ok=True)
return OPERATORS_DIR / f"{_slug(email)}.json"
def load(email: str) -> Cart | None:
p = _path(email)
if not p.exists():
return None
try:
data = json.loads(p.read_text(encoding="utf-8"))
# tolerate older carts missing the new ui_* fields
return Cart(**{k: v for k, v in data.items() if k in Cart.__dataclass_fields__})
except Exception:
log.exception("failed to load cart for %s", email)
return None
def save(cart: Cart) -> None:
if not cart.created_at:
cart.created_at = datetime.utcnow().isoformat(timespec="seconds") + "Z"
p = _path(cart.operator_email)
p.write_text(json.dumps(asdict(cart), indent=2), encoding="utf-8")
log.info("saved cart for %s%s", cart.operator_email, p)
def forget(email: str) -> bool:
p = _path(email)
if p.exists():
p.unlink()
log.info("forgot cart for %s", email)
return True
return False