Spaces:
Running
Running
| # Install: pip install pinecone-client sentence-transformers | |
| import os | |
| # Force offline mode to prevent HuggingFace hub DNS errors when internet is down | |
| os.environ["HF_HUB_OFFLINE"] = "1" | |
| from sentence_transformers import SentenceTransformer | |
| import json | |
| class KeffiMemory: | |
| def __init__(self): | |
| print("Loading Multilingual Memory Embedding Model...") | |
| self.encoder = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2', local_files_only=True) | |
| self.db_path = "user_state.json" | |
| if not os.path.exists(self.db_path): | |
| with open(self.db_path, "w") as f: | |
| json.dump({}, f) | |
| print("Memory Engine Ready!") | |
| def _load_db(self): | |
| with open(self.db_path, "r") as f: | |
| return json.load(f) | |
| def _save_db(self, data): | |
| with open(self.db_path, "w") as f: | |
| json.dump(data, f, indent=4) | |
| def save_to_memory(self, patient_id: str, text: str, emotion: str): | |
| db = self._load_db() | |
| if patient_id not in db: | |
| db[patient_id] = {"facts": {}, "emotional_memory": [], "patterns": {}, "deep_patterns": {}} | |
| # Ensure keys exist even for legacy records | |
| db[patient_id].setdefault("facts", {}) | |
| db[patient_id].setdefault("patterns", {}) | |
| db[patient_id].setdefault("deep_patterns", {}) | |
| db[patient_id].setdefault("emotional_memory", []) | |
| db[patient_id]["emotional_memory"].append({"text": text, "emotion": emotion}) | |
| db[patient_id]["emotional_memory"] = db[patient_id]["emotional_memory"][-15:] # Store more for deeper analysis | |
| # Real-time semantic extraction (Fast) | |
| msg = text.lower() | |
| if "father" in msg or "dad" in msg or "appa" in msg: | |
| db[patient_id]["patterns"]["father_conflict"] = True | |
| if "sleep" in msg or "awake" in msg or "insomnia" in msg: | |
| db[patient_id]["patterns"]["sleep_issue"] = "recurring" | |
| self._save_db(db) | |
| print(f"[MEMORY SAVED] For {patient_id}: {text}") | |
| # Trigger Deep Background Extraction every 5 messages | |
| if len(db[patient_id]["emotional_memory"]) % 5 == 0: | |
| import threading | |
| threading.Thread(target=self.background_deep_extraction, args=(patient_id,)).start() | |
| def background_deep_extraction(self, patient_id: str): | |
| import requests | |
| api_key = os.getenv("GROQ_API_KEY") | |
| if not api_key or api_key == "YOUR_GROQ_API_KEY_HERE": return | |
| db = self._load_db() | |
| memories = [m["text"] for m in db[patient_id].get("emotional_memory", [])[-10:]] | |
| if len(memories) < 5: return | |
| prompt = f"Analyze these messages from a user. Extract a JSON of psychological patterns, triggers, and relationship dynamics. Include confidence (0.0 to 1.0) and evidence_count. Also provide a 'contextual_summary' string describing the user's emotional state natively. Example format: {{\"fear_of_abandonment\": {{\"confidence\": 0.81, \"evidence_count\": 3}}, \"contextual_summary\": \"User often seeks reassurance...\"}}. Return ONLY raw JSON.\nMessages: {memories}" | |
| headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"} | |
| payload = {"model": "llama3-8b-8192", "messages": [{"role": "user", "content": prompt}], "temperature": 0.1, "response_format": {"type": "json_object"}} | |
| try: | |
| res = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload, timeout=8) | |
| if res.status_code == 200: | |
| content = res.json()["choices"][0]["message"]["content"].strip() | |
| db[patient_id]["deep_patterns"] = json.loads(content) | |
| self._save_db(db) | |
| print(f"[MEMORY DEEP EXTRACTION] Completed for {patient_id}") | |
| except Exception as e: | |
| print(f"[MEMORY DEEP EXTRACTION ERROR] {e}") | |
| def recall_past_memory(self, patient_id: str, current_message: str) -> str: | |
| db = self._load_db() | |
| if patient_id not in db or not db[patient_id].get("emotional_memory"): | |
| return "" | |
| # SOS keyword list - these messages should NOT be injected as raw text | |
| SOS_PHRASES = [ | |
| "want to go to sleep and never wake", "never wake up", "better off without me", | |
| "everyone would be better off", "no way out", "want to disappear", | |
| "planning to die", "going to end it", "take my pills", "going to kill myself", | |
| "suicide plan", "don't want to exist", "wish i was dead", "tired of living" | |
| ] | |
| raw_memories = db[patient_id]["emotional_memory"][-2:] | |
| sanitized_memories = [] | |
| for m in raw_memories: | |
| text = m["text"] | |
| # If past message was a crisis message, replace with a safe tag | |
| if any(phrase in text.lower() for phrase in SOS_PHRASES): | |
| sanitized_memories.append("[Previous session: User was in emotional distress — crisis support was provided]") | |
| else: | |
| sanitized_memories.append(text) | |
| deep_patterns = db[patient_id].get("deep_patterns", {}) | |
| context_str = "" | |
| if "contextual_summary" in deep_patterns: | |
| context_str += f"[LONG-TERM MEMORY: {deep_patterns['contextual_summary']}] " | |
| if sanitized_memories: | |
| context_str += "[RECENT MESSAGES: " + " | ".join(sanitized_memories) + "] " | |
| return context_str | |
| def update_phq9_score(self, patient_id: str, score: int): | |
| db = self._load_db() | |
| if patient_id not in db: | |
| db[patient_id] = {"facts": {}, "emotional_memory": []} | |
| db[patient_id]["facts"]["last_phq9_score"] = score | |
| self._save_db(db) | |
| # Initialize the engine | |
| memory_engine = KeffiMemory() | |