error / memory_cloud.py
maryjscout's picture
Upload memory_cloud.py with huggingface_hub
9f5ba86 verified
Raw History Blame Contribute Delete
6.01 kB
"""
memory_cloud.py — conversation long-term memory for Jarvis.
Backend: Firebase Firestore (cloud only). No local fallback — if Firestore
is unavailable, remember() reports failure and recall() returns nothing.
"""
import os
import time
_firestore = None
_firestore_failed = False
def _credentials_path():
p = os.environ.get("FIREBASE_CREDENTIALS_PATH", "").strip()
if p:
return p
here = os.path.dirname(os.path.abspath(__file__))
return os.path.join(here, "firebase_service_account.json")
def _init_firestore():
"""Lazily init Firestore once. Returns client or None."""
global _firestore, _firestore_failed
if _firestore is not None:
return _firestore
if _firestore_failed:
return None
try:
import firebase_admin
from firebase_admin import credentials, firestore
import json as _json
js = (os.environ.get("FIREBASE_CREDENTIALS_JSON") or "").strip()
if js:
info = _json.loads(js)
app = firebase_admin.get_app() if firebase_admin._apps else firebase_admin.initialize_app(
credentials.Certificate(info)
)
else:
path = _credentials_path()
if not os.path.exists(path):
print("[warn] No firebase_service_account.json - memory disabled (no local fallback).")
_firestore_failed = True
return None
app = firebase_admin.get_app() if firebase_admin._apps else firebase_admin.initialize_app(
credentials.Certificate(path)
)
_firestore = firestore.client(app)
print("[ok] Firebase Firestore connected for memory.")
return _firestore
except Exception as exc:
print(f"[warn] Firestore unavailable ({exc}) - memory disabled.")
_firestore_failed = True
return None
def remember(content: str) -> bool:
"""Save a fact/interaction to Firestore. False if Firestore unavailable."""
if not content:
return False
db = _init_firestore()
if db is None:
return False
try:
db.collection("jarvis_memory").add({"content": content, "ts": time.time()})
return True
except Exception as exc:
print(f"[warn] Firestore write failed ({exc}).")
return False
CHAT_DOC = "shared"
CHAT_CAP = 12
def set_pref(key: str, value: str) -> bool:
"""Cross-channel preference (one doc, never raises)."""
try:
db = _init_firestore()
if db is None:
return False
import time as _t
ref = db.collection("jarvis_prefs").document("user")
doc = ref.get()
prefs = ((doc.to_dict() or {}).get("prefs") or {}) if doc.exists else {}
prefs[key] = (value or "")[:300]
ref.set({"prefs": prefs, "updated": _t.time()})
return True
except Exception as exc:
print(f"[warn] pref save failed ({exc}).")
return False
def get_prefs() -> dict:
try:
db = _init_firestore()
if db is None:
return {}
doc = db.collection("jarvis_prefs").document("user").get()
if doc.exists:
return (doc.to_dict() or {}).get("prefs") or {}
except Exception as exc:
print(f"[warn] pref load failed ({exc}).")
return {}
def chat_load(limit: int = 8) -> list:
"""Shared BAT<->Telegram conversation (list of {r,t,ts,src}). Never raises."""
try:
db = _init_firestore()
if db is None:
return []
doc = db.collection("jarvis_chat").document(CHAT_DOC).get()
if doc.exists:
msgs = (doc.to_dict() or {}).get("msgs") or []
msgs = sorted(msgs, key=lambda m: m.get("ts", 0))
return msgs[-limit:]
except Exception as exc:
print(f"[warn] chat load failed ({exc}).")
return []
def chat_save(role: str, text: str, src: str = "?") -> bool:
"""Append one exchange line to the shared conversation. Never raises."""
try:
db = _init_firestore()
if db is None or not text:
return False
ref = db.collection("jarvis_chat").document(CHAT_DOC)
doc = ref.get()
msgs = ((doc.to_dict() or {}).get("msgs") or []) if doc.exists else []
msgs.append({"r": role, "t": text[:500], "ts": time.time(), "src": src})
msgs = sorted(msgs, key=lambda m: m.get("ts", 0))[-CHAT_CAP:]
ref.set({"msgs": msgs, "updated": time.time()})
return True
except Exception as exc:
print(f"[warn] chat save failed ({exc}).")
return False
def stats() -> dict:
"""Count memory entries: history (jarvis_memory docs), contacts, chat turns.
Never raises — returns zeros when offline."""
out = {"history": 0, "contacts": 0, "turns": 0}
try:
db = _init_firestore()
if db is None:
return out
try:
out["history"] = len(db.collection("jarvis_memory").get())
except Exception:
pass
try:
out["contacts"] = len(db.collection("jarvis_contacts").get())
except Exception:
pass
try:
doc = db.collection("jarvis_chat").document(CHAT_DOC).get()
if doc.exists:
out["turns"] = len((doc.to_dict() or {}).get("msgs") or [])
except Exception:
pass
except Exception:
pass
return out
def recall(limit: int = 10) -> str:
"""Return the most recent remembered items as a spoken-friendly string."""
db = _init_firestore()
if db is None:
return ""
try:
snaps = (
db.collection("jarvis_memory")
.order_by("ts", direction="DESCENDING")
.limit(limit)
.get()
)
parts = [s.to_dict().get("content", "") for s in snaps]
return "; ".join(p for p in parts if p)
except Exception as exc:
print(f"[warn] Firestore read failed ({exc}).")
return ""