Spaces:
Runtime error
Runtime error
"""Firestore Chat Message History.""" | |
from __future__ import annotations | |
import logging | |
from typing import TYPE_CHECKING, List, Optional | |
from langchain_core.chat_history import BaseChatMessageHistory | |
from langchain_core.messages import ( | |
BaseMessage, | |
messages_from_dict, | |
messages_to_dict, | |
) | |
logger = logging.getLogger(__name__) | |
if TYPE_CHECKING: | |
from google.cloud.firestore import Client, DocumentReference | |
def _get_firestore_client() -> Client: | |
try: | |
import firebase_admin | |
from firebase_admin import firestore | |
except ImportError: | |
raise ImportError( | |
"Could not import firebase-admin python package. " | |
"Please install it with `pip install firebase-admin`." | |
) | |
# For multiple instances, only initialize the app once. | |
try: | |
firebase_admin.get_app() | |
except ValueError as e: | |
logger.debug("Initializing Firebase app: %s", e) | |
firebase_admin.initialize_app() | |
return firestore.client() | |
class FirestoreChatMessageHistory(BaseChatMessageHistory): | |
"""Chat message history backed by Google Firestore.""" | |
def __init__( | |
self, | |
collection_name: str, | |
session_id: str, | |
user_id: str, | |
firestore_client: Optional[Client] = None, | |
): | |
""" | |
Initialize a new instance of the FirestoreChatMessageHistory class. | |
:param collection_name: The name of the collection to use. | |
:param session_id: The session ID for the chat.. | |
:param user_id: The user ID for the chat. | |
""" | |
self.collection_name = collection_name | |
self.session_id = session_id | |
self.user_id = user_id | |
self._document: Optional[DocumentReference] = None | |
self.messages: List[BaseMessage] = [] | |
self.firestore_client = firestore_client or _get_firestore_client() | |
self.prepare_firestore() | |
def prepare_firestore(self) -> None: | |
"""Prepare the Firestore client. | |
Use this function to make sure your database is ready. | |
""" | |
self._document = self.firestore_client.collection( | |
self.collection_name | |
).document(self.session_id) | |
self.load_messages() | |
def load_messages(self) -> None: | |
"""Retrieve the messages from Firestore""" | |
if not self._document: | |
raise ValueError("Document not initialized") | |
doc = self._document.get() | |
if doc.exists: | |
data = doc.to_dict() | |
if "messages" in data and len(data["messages"]) > 0: | |
self.messages = messages_from_dict(data["messages"]) | |
def add_message(self, message: BaseMessage) -> None: | |
self.messages.append(message) | |
self.upsert_messages() | |
def upsert_messages(self, new_message: Optional[BaseMessage] = None) -> None: | |
"""Update the Firestore document.""" | |
if not self._document: | |
raise ValueError("Document not initialized") | |
self._document.set( | |
{ | |
"id": self.session_id, | |
"user_id": self.user_id, | |
"messages": messages_to_dict(self.messages), | |
} | |
) | |
def clear(self) -> None: | |
"""Clear session memory from this memory and Firestore.""" | |
self.messages = [] | |
if self._document: | |
self._document.delete() | |