Add Maria WhatsApp+RAG bridge as a service (LXC 171)
Move the /tmp prototype (Baileys bridge + RAG consumer) into git as a proper sibling project to discord-bridge/: own systemd --user units (whatsapp bridge, rag consumer, dashboard, periodic Drive sync timer), a filesystem document store with a stdlib control dashboard (start/ stop/restart, document CRUD, reindex, Google Drive sync via rclone), and an idempotent ops/install.sh following the same conventions. Co-Authored-By: Claude Agent <noreply@anthropic.com>
This commit is contained in:
106
proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/config.py
Normal file
106
proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag/config.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""Configuratie comuna a puntii WhatsApp+RAG pentru Maria (LXC 171 claude-agent).
|
||||
|
||||
Citeste ~/.maria-bridge/env (KEY=value, tolerant la comentarii si ghilimele).
|
||||
Mirrors deliberat conventiile din discord-bridge/config.py, ca sa fie un singur
|
||||
model de citit pentru cine intretine ambele punti pe acest container.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
# Directorul de baza poate fi mutat in teste prin MARIA_BRIDGE_DIR.
|
||||
_DEFAULT_DIR = pathlib.Path.home() / ".maria-bridge"
|
||||
|
||||
STATE_DIR: pathlib.Path = pathlib.Path(os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR)
|
||||
DOCS_DIR: pathlib.Path = STATE_DIR / "documents"
|
||||
INDEX_FILE: pathlib.Path = STATE_DIR / "rag_index.json"
|
||||
LOG_DIR: pathlib.Path = STATE_DIR / "logs"
|
||||
ENV_FILE: pathlib.Path = STATE_DIR / "env"
|
||||
AUTH_DIR: pathlib.Path = STATE_DIR / "whatsapp-auth"
|
||||
|
||||
_env: dict[str, str] = {}
|
||||
|
||||
# Valori implicite. LLM_URL/OLLAMA_URL presupun tunel/proxy local catre backend-ul
|
||||
# real (vezi docs/maria-whatsapp-rag-prototype.md) — de completat in env dupa caz.
|
||||
DEFAULTS: dict[str, str] = {
|
||||
"BRIDGE_HOST": "127.0.0.1",
|
||||
"BRIDGE_PORT": "8099",
|
||||
"LLM_URL": "http://127.0.0.1:8091",
|
||||
"OLLAMA_URL": "http://127.0.0.1:11434",
|
||||
"EMBED_MODEL": "nomic-embed-text",
|
||||
"TOP_K": "3",
|
||||
"MAX_TOKENS": "250",
|
||||
"POLL_INTERVAL_S": "2",
|
||||
"TEST_MODE_SELF_CHAT_ONLY": "true",
|
||||
"DASHBOARD_BIND": "127.0.0.1",
|
||||
"DASHBOARD_PORT": "18792",
|
||||
# Tinta rclone pentru sincronizarea depozitului de documente, ex:
|
||||
# "gdrive:romfast/document_store" (dosarul D:\GoogleDrive\romfast\document_store
|
||||
# de pe Windows, vazut prin Google Drive API). Gol = sincronizare dezactivata,
|
||||
# doar upload manual din dashboard.
|
||||
"DRIVE_REMOTE": "",
|
||||
}
|
||||
|
||||
|
||||
def parse_env(text: str) -> dict[str, str]:
|
||||
"""Parseaza un fisier de tip KEY=value. Nu arunca niciodata."""
|
||||
out: dict[str, str] = {}
|
||||
for raw in text.splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
if line.startswith("export "):
|
||||
line = line[len("export "):].strip()
|
||||
if "=" not in line:
|
||||
continue
|
||||
key, _, val = line.partition("=")
|
||||
key = key.strip()
|
||||
if not key:
|
||||
continue
|
||||
val = val.strip()
|
||||
if val[:1] not in ("'", '"'):
|
||||
cut = val.find(" #")
|
||||
if cut >= 0:
|
||||
val = val[:cut].rstrip()
|
||||
if len(val) >= 2 and val[0] == val[-1] and val[0] in ("'", '"'):
|
||||
val = val[1:-1]
|
||||
out[key] = val
|
||||
return out
|
||||
|
||||
|
||||
def reload(base_dir: str | os.PathLike | None = None) -> dict[str, str]:
|
||||
"""Recalculeaza caile si reciteste env-ul. Returneaza dictionarul incarcat."""
|
||||
global STATE_DIR, DOCS_DIR, INDEX_FILE, LOG_DIR, ENV_FILE, AUTH_DIR, _env
|
||||
if base_dir is None:
|
||||
base_dir = os.environ.get("MARIA_BRIDGE_DIR") or _DEFAULT_DIR
|
||||
STATE_DIR = pathlib.Path(base_dir)
|
||||
DOCS_DIR = STATE_DIR / "documents"
|
||||
INDEX_FILE = STATE_DIR / "rag_index.json"
|
||||
LOG_DIR = STATE_DIR / "logs"
|
||||
ENV_FILE = STATE_DIR / "env"
|
||||
AUTH_DIR = STATE_DIR / "whatsapp-auth"
|
||||
try:
|
||||
_env = parse_env(ENV_FILE.read_text(encoding="utf-8"))
|
||||
except OSError:
|
||||
_env = {}
|
||||
return _env
|
||||
|
||||
|
||||
def get(key: str, default: str | None = None) -> str | None:
|
||||
if key in _env:
|
||||
return _env[key]
|
||||
if key in DEFAULTS:
|
||||
return DEFAULTS[key]
|
||||
return default
|
||||
|
||||
|
||||
def get_int(key: str, default: int) -> int:
|
||||
try:
|
||||
return int(get(key, str(default)) or default)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
reload()
|
||||
Reference in New Issue
Block a user