Download src/services/memory_service.py from False45/math-api: direct link, hf CLI and curl.
- Browser
- Download file 3.84 kB
-
https://huggingface.co/spaces/False45/math-api/resolve/main/src/services/memory_service.py
- Command line
-
hf download hf://spaces/False45/math-api/src/services/memory_service.py
-
curl -L -o memory_service.py https://huggingface.co/spaces/False45/math-api/resolve/main/src/services/memory_service.py
3.84 kB
| """ | |
| Chat Memory Service — Firestore-backed persistent chat history. | |
| Provides per-session chat history with Firestore persistence and | |
| in-memory fallback when Firestore is unavailable. | |
| Replaces the MongoDB implementation from Phase 1. | |
| """ | |
| import logging | |
| from datetime import datetime, timezone | |
| from typing import List, Dict | |
| from backend.src.config import settings | |
| from backend.src.services.firebase_service import get_firestore_client | |
| try: | |
| from langchain_core.messages import HumanMessage, AIMessage | |
| except ImportError: | |
| from langchain.schema import HumanMessage, AIMessage | |
| logger = logging.getLogger("math_assistant.memory") | |
| class FirestoreChatMemory: | |
| """Per-session chat memory with Firestore persistence and in-memory fallback.""" | |
| def __init__(self, uid: str = "anonymous", session_id: str = "default"): | |
| self.uid = uid | |
| self.session_id = session_id | |
| self._memory: List[Dict] = [] | |
| self.db = get_firestore_client() | |
| def _get_collection_ref(self): | |
| """Get a reference to the session's messages subcollection.""" | |
| if not self.db: | |
| return None | |
| return self.db.collection("users").document(self.uid).collection("sessions").document(self.session_id).collection("messages") | |
| def add_message(self, role: str, content: str): | |
| """Add a message to chat history.""" | |
| msg = { | |
| "role": role, | |
| "content": content, | |
| "timestamp": datetime.now(timezone.utc), | |
| } | |
| col_ref = self._get_collection_ref() | |
| if col_ref is not None: | |
| try: | |
| col_ref.add(msg) | |
| return | |
| except Exception as e: | |
| logger.error(f"Failed to add message to Firestore: {e}") | |
| # Fallback to in-memory | |
| msg["session_id"] = self.session_id | |
| self._memory.append(msg) | |
| def get_history(self, limit: int = 20) -> List[Dict]: | |
| """Retrieve recent chat history for this session.""" | |
| col_ref = self._get_collection_ref() | |
| if col_ref is not None: | |
| try: | |
| from firebase_admin import firestore | |
| docs = ( | |
| col_ref.order_by("timestamp", direction=firestore.Query.DESCENDING) | |
| .limit(limit) | |
| .stream() | |
| ) | |
| msgs = [doc.to_dict() for doc in docs] | |
| msgs.reverse() | |
| return msgs | |
| except Exception as e: | |
| logger.error(f"Failed to get history from Firestore: {e}") | |
| # Fallback to in-memory | |
| return self._memory[-limit:] | |
| def get_langchain_messages(self, limit: int = 10): | |
| """Convert chat history to LangChain message objects.""" | |
| history = self.get_history(limit) | |
| result = [] | |
| for msg in history: | |
| if msg.get("role") == "human": | |
| result.append(HumanMessage(content=msg.get("content", ""))) | |
| elif msg.get("role") == "assistant": | |
| result.append(AIMessage(content=msg.get("content", ""))) | |
| return result | |
| def clear_history(self): | |
| """Clear all messages for this session.""" | |
| col_ref = self._get_collection_ref() | |
| if col_ref is not None: | |
| try: | |
| docs = col_ref.limit(500).stream() | |
| batch = self.db.batch() | |
| count = 0 | |
| for doc in docs: | |
| batch.delete(doc.reference) | |
| count += 1 | |
| if count > 0: | |
| batch.commit() | |
| except Exception as e: | |
| logger.error(f"Failed to clear history in Firestore: {e}") | |
| self._memory.clear() | |
| # Maintain backward compatibility name for the MathAIEngine import during transition | |
| MongoDBChatMemory = FirestoreChatMemory | |