Custom Tracker to save conversation history into Firebase Firestore
Hey,I have found a solution to save all the conversation history into the Firebase FireStore Database.
This custom tracker can save all the conversations into the Firestore database.
I followed the same logic of InMemoryTracker to implement this.
How I did?
- First I have Checked this forum Help-with-custom-tracker. But It doesn’t worked because some issues in the tracker_store.py because I’m using Firestore and not using any host parameter but the source code tracker_store in tracker_store.py requires “host” parameter must.And the Exception is not throwing instead ImportError is throwing warning.
- Then After that source code inspection I understand that I need to mention that “host” parameter compulsory, why “host” parameter compulsory  . .
- Now I’m cleared how to connect custom_tracker and its connected prefectly now.   
- Then I started to implement the Firestore tracker, Some one on the forum said to Modify mongodb tracker and use it ,But I don’t understand its logic.
- Now again its time to do reverse engineering  Again I have inspected the tracker_store.py code.
It says implement save(), retrieve() and keys() method.
I think this point should be mentioned in the documentation. Again I have inspected the tracker_store.py code.
It says implement save(), retrieve() and keys() method.
I think this point should be mentioned in the documentation.
- After that I have followed InMemoryTracker logic.
- Finally its working perfectly     
Here’s the custom_tracker.py code (in root directory):
import traceback
import contextlib
import itertools
import json
import logging
import os
import pickle
from datetime import datetime, timezone
from time import sleep
from typing import (
    Callable,
    Dict,
    Iterable,
    Iterator,
    List,
    Optional,
    Text,
    Union,
    TYPE_CHECKING,
)
from boto3.dynamodb.conditions import Key
import rasa.core.utils as core_utils
from rasa.core.actions.action import ACTION_LISTEN_NAME
from rasa.core.brokers.broker import EventBroker
from rasa.core.constants import (
    POSTGRESQL_SCHEMA,
    POSTGRESQL_MAX_OVERFLOW,
    POSTGRESQL_POOL_SIZE,
)
from rasa.core.conversation import Dialogue
from rasa.core.domain import Domain
from rasa.core.events import SessionStarted
from rasa.core.trackers import ActionExecuted, DialogueStateTracker, EventVerbosity
import rasa.cli.utils as rasa_cli_utils
from rasa.utils.common import class_from_module_path, raise_warning, arguments_of
from rasa.utils.endpoints import EndpointConfig
import sqlalchemy as sa
from rasa.core.tracker_store import TrackerStore
# Import custom dbConfig for firebase returns firestore.client() class object
from actionserver.db.dbConfig import db
logger = logging.getLogger(__name__)
# COLLECTION = "restaurant-bot-tracker"
class FirebaseTrackerStore(TrackerStore):
    """Stores conversation history in Firebase"""
    def __init__(
        self,
        domain: Domain,
        collection: Optional[Text] = "tracker",
        host: Optional[Text] = "localhost",
        event_broker: Optional[EventBroker] = None
    ):
        self.store = {}
        self.COLLECTION = collection
        super().__init__(domain, event_broker)
    def save(self, tracker: DialogueStateTracker) -> None:
        """Updates and saves the current conversation state
        Args:
            tracker: DialogueStateTracker from TrackerStore Class 
        Returns:
            None
        Stores data in Firebase and creates tracker            
        """
        try:
            if self.event_broker:
                self.stream_events(tracker)
            serialised = self.serialiseTracker(tracker)
            ref = db.collection(self.COLLECTION).document(tracker.sender_id)
            ref.set(serialised)
        except Exception as e:
            traceback.print_exc()
    def checkSenderId(self, sender_id):
        """
        Checks if sender Id exists in database (firebase)
        Args:
            sender_id : takes the sender_id as input 
        Returns: 
            Boolean (if the id exists or not) 
        """
        check = db.collection(self.COLLECTION).document(sender_id).get().exists
        return check
    def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
        """
        Args:
            sender_id: the message owner ID
        Returns:
            DialogueStateTracker
        """
        if self.checkSenderId(sender_id):
            logger.debug(f"Recreating tracker for id '{sender_id}'")
            deserialised_tracker = self.deserialiseTracker(sender_id)
            return deserialised_tracker
        else:
            logger.debug(f"Creating a new tracker for id '{sender_id}'.")
            return None
    def keys(self) -> Iterable[Text]:
        """
        Returns: 
            sender_ids of the Tracker Store in Firebase
        """
        docs = db.collection(self.COLLECTION).stream()
        keys = []
        for doc in docs:
            keys.append(doc.id)
        return keys
    def serialiseTracker(self, tracker:DialogueStateTracker):
        """
        User defined serialisation
        
        Args:
            tracker : takes tracker object as input
        Returns:
            dialogue    
        """
        try:
            dialogue = tracker.as_dialogue().as_dict()
            dialogue = json.dumps(dialogue)
            dialogue = json.loads(dialogue)
            return dialogue
        except Exception as e:
            traceback.print_exc()
            return None
    def deserialiseTracker(self, sender_id):
        """User defined deserialisation
        Args:
            sender_id : Takes sender_id as input
        
        Returns:
            returns tracker
        """
        
        tracker = self.init_tracker(sender_id)
        try:
            if not tracker:
                return None
            ref = db.collection(self.COLLECTION).document(sender_id)
            dialogue = ref.get().to_dict()
            # serialiseTracker(dialogue) no need to pass
            dialogue = Dialogue.from_parameters(
                json.loads(json.dumps(dialogue)))
        except Exception as e:
            traceback.print_exc()
        tracker.recreate_from_dialogue(dialogue)
        return tracker
Here’s the code of endpoints.yml
tracker_store:
  type: custom_tracker.FirebaseTrackerStore
  host: localhost
  collection: restaurant-bot-tracker
Here’s the code of db from this module.
from actionserver.db.dbConfig import db
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
cred = credentials.Certificate('actionserver\db\serviceAccount.json')
firebase_admin.initialize_app(cred)
db = firestore.client()
requirements:
- pip install firebase_admin
- set current working directory in the python path
- 
Get Firestore serviceAccount.json from the Firebase console.
 Thats it firestore custom tracker is ready  
 Thank you
 Thank you