TrackerStore not filling slots when a database is active

I have implemented a tracker store which stores all my chat history into postgres database, this stuff works fine, it successfully saves and retrieves from the database.

But, there is a strange problem when i run the bot and enable the database storage and use an actionForm. The bot loops infinitely on a sentence/response. i have enabled Debug=True, and the slot is not filled then the bot asks the same question again.

my first idea was to make the sender_id unique for each conversation, (using python time.time() for example). But still the same issue. Second idea was to override the retrieve function by creating a ‘CustomTrackerstore’ class with ‘SQLTrackerStore’ as parent, then add a filter condition to the query

    query = self.session.query(self.SQLEvent)
    result = query.filter(self.SQLEvent.sender_id == sender_id).all()

but still the same issue.

When i disable the Database access (by removing the trackerstore on endpoint.yml) the bot works very fine. The bot has trouble with the tracker store enabled. Tell me if i must paste some files, i presume i must :slight_smile: but tell me which one.

Thanks in advance.

Can you share the stacktrace of the error message and your implementation of your custom tracker store please? Also, please mention the commands you are executing to start the bot. I’m assuming you are using Rasa version 1.x?

Yes, I am using Rasa 1.1.7. I do not have an error message per say, but I spotted a difference in the debug stacktrace when I am using a database or not.

With a database : 2019-07-25 09:29:56 [1;30mDEBUG [0m [34mrasa.core.policies.memoization[0m - Current tracker state [None, None, None, None, {}]

2019-07-25 09:29:56 [1;30mDEBUG [0m [34mrasa.core.policies.memoization[0m - There is a memorised next action '0'

With no database : 2019-07-25 09:33:00 e[1;30mDEBUG e[0m e[34mrasa.core.policies.memoizatione[0m - Current tracker state [None, None, {}, {'intent_intent_bonjour': 1.0, 'prev_action_listen': 1.0}, {'intent_intent_bonjour': 1.0, 'prev_utter_bonjour': 1.0}]

2019-07-25 09:33:00 e[1;30mDEBUG e[0m e[34mrasa.core.policies.memoizatione[0m - There is a memorised next action '9'

Here’s the implementation of my custom tracker store :

logger = logging.getLogger(__name__)

class CustomSQLTrackerStore(SQLTrackerStore):
    """Store which can save and retrieve trackers from an SQL database."""

from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class SQLEvent(Base):
    from sqlalchemy import Column, Integer, String, Float, Text

    __tablename__ = "events"

    id = Column(Integer, primary_key=True)
    sender_id = Column(String(255), nullable=False, index=True)
    type_name = Column(String(255), nullable=False)
    timestamp = Column(Float)
    intent_name = Column(String(255))
    action_name = Column(String(255))
    data = Column(Text)

    def __init__(self, url, domain: Optional[Domain] = None, dialect: Text = None, host: Optional[Text] = None,
         port: Optional[int] = None, db: Text = None, username: Text = None, password: Text = None,
         event_broker: Optional[EventChannel] = None, login_db: Optional[Text] = None ) -> None:

        import sqlalchemy
        from sqlalchemy.orm import sessionmaker
        from sqlalchemy import create_engine

        engine_url = self._get_db_url(
            dialect, host, port, db, username, password, login_db
        )
        logger.debug(
            "Attempting to connect to database " 'via "{}"'.format(repr(engine_url))
        )

        # Database might take a while to come up
        while True:
            try:
                self.engine = create_engine(engine_url)

                # if `login_db` has been provided, use current connection with
                # that database to create working database `db`
                if login_db:
                    self._create_database_and_update_engine(db, engine_url)

                try:
                    self.Base.metadata.create_all(self.engine)
                except (
                    sqlalchemy.exc.OperationalError,
                    sqlalchemy.exc.ProgrammingError,
                ) as e:
                    # Several Rasa services started in parallel may attempt to
                    # create tables at the same time. That is okay so long as
                    # the first services finishes the table creation.
                    logger.error("Could not create tables: {}".format(e))

                self.session = sessionmaker(bind=self.engine)()
                break
            except (
                sqlalchemy.exc.OperationalError,
                sqlalchemy.exc.IntegrityError,
            ) as e:

                logger.warning(e)
                sleep(5)

        logger.debug("Connection to SQL database '{}' successful".format(db))

        super(SQLTrackerStore, self).__init__(domain, event_broker)
    

    def retrieve(self, sender_id: Text) -> Optional[DialogueStateTracker]:
        """Create a tracker from all previously stored events."""

        conversation_date = (ttime() - 180) ## 180 = 3 minutes

        query = self.session.query(self.SQLEvent)
        result = query.filter(self.SQLEvent.sender_id == sender_id).all()
        events = [json.loads(event.data) for event in result]

        if self.domain and len(events) > 0:
            logger.debug("Recreating tracker from sender id '{}'".format(sender_id))

            return DialogueStateTracker.from_dict(sender_id, events, self.domain.slots)
        else:
            logger.debug(
                "Can't retrieve tracker matching"
                "sender id '{}' and min timestamp '{}' from SQL storage.  "
                "Returning `None` instead.".format(sender_id, conversation_date)
            )
            return None

I’m starting the bot with (in a MakeFile in root directory) with : rasa run --cors "**" --endpoints endpoints.yml --debug --enable-api

Are the indents correct in the code your pasted? :thinking: Can you please point out what is different form the standard implementation of SQLTrackerStore?

Can you maybe also share your endpoints.yml? If there is no error message, can you maybe output the logs of a conversation where I can see that the slot is not set? Thanks.

And another question: Does it work if you are using the build-in SQL tracker store (see Tracker Stores)?

Oops, the __init__ and retrieve methods are inside the class, sorry about that.

About the difference between default SQLTrackerStore and our CustomTrackerStore, we added some time filters on the request query, like filter(SQLEvent.sender_id=sender_id and timestamp.now() - 5minutes < SQLEvent.timestamp ) in order to get the latest conversation stack. The code I posted above is the filter without the time filter.

This is my endpoint.yml:

action_endpoint:
  url: "http://localhost:5055/webhook"

tracker_store:
  store_type: MyTrackerStore.CustomSQLTrackerStore (or SQL)
  url: localhost
  db: "******"
  username: "****"
  password: "*********"
  dialect: "postgres"

I’ve stored two logs of conversations in two .log files : no_db.log, where I didn’t use a database, and with_db.log, where I did. I spotted this warning in with_db.log but not in no_db.log :

2019-07-25 09:30:11 WARNING - Categorical slot 'requested_slot' is set to a value ('equipement') that is not specified in the domain. Value will be ignored and the slot will behave as if no value is set. Make sure to add all values a categorical slot should store to the domain.

Finally, I have the same problem if I change MyTrackerStore.CustomSQLTrackerStore for SQL in endpoints.yml.

If the same error also occurs when you use SQL as store_type, it should not be related to your custom implementation. Does the error also occur when you use sqlite as store_type?

You mentioned that you start the server with rasa run --cors "**" --endpoints endpoints.yml --debug --enable-api. What endpoint are you calling afterwards? And can you maybe share the with_db.log?

I tried to use sqlite as store_type, but it was not recognized. Instead, I edited tracker_store :

tracker_store:
    store_type: SQL
    dialect: "sqlite"
    db: "database.db"

And now it looks like it works !

I’m not calling any other endpoint after starting the server. I also spotted these two lines in with_db.log (44.6 KB) : 2019-07-25 09:29:48 DEBUG - rasa.core.agent - Deleted lock for conversation '2019-07-25 09:29:45.292930 673151172' (unused) 2019-07-25 09:29:55 DEBUG - rasa.core.agent - Created a new lock for conversation '2019-07-25 09:29:45.292930 673151172'

Sorry, for the late reply. Are you still struggling?

Regarding the warning about categorical slots: You should define those in your domain file, see Slots. Can you maybe also share your domain file?

Hey.

JoachimJaafar has leaved the project, so i took over the project. We are still facing this issue.

About the warning, all slots that i use are defined on the domain. The domain file was generated using the Interactive mode.

%YAML 1.1
---
actions:
- utter_bonjour
entities:
- Demande
- demande_technique
- description
- equipement
- nature_probleme
- type_fuite
forms:
- my_form
intents:
- intent_description
- intent_bonjour
- intent_demandes_tout_genre
slots:
  Demande:
    auto_fill: false
    type: unfeaturized
  demande_technique:
    type: list
  description:
    type: unfeaturized
  description_demande:
    auto_fill: false
    type: unfeaturized
  description_demande_technique:
    type: unfeaturized
  equipement:
    type: list
  motif:
    auto_fill: false
    type: unfeaturized
  nature_probleme:
    type: list
  requested_slot:
    type: categorical
    values: []
<a lot of slots with the same pattern as 'description_demande_technique'>
templates:
    <a lot of utters>

Hi @thom! I guess you also set - slot{} in the stories then. So, looks good.

Just to clarify: The bot is working if you use

tracker_store:
    store_type: SQL
    dialect: "sqlite"
    db: "database.db"

but it is not working if you use

tracker_store:
  store_type: SQL
  url: localhost
  db: "******"
  username: "****"
  password: "*********"
  dialect: "postgres"

Is that correct?

And are only the slots from the form action not filled or do you also see the same behaviour for normal actions?

Yes, the bot work if i use Sqlite, and he not work if i use Postgres.

About the stories, we started with one general direction.

## story_normal
* intent_bonjour
 - utter_bonjour
 - my_form
 - form{"name": "my_form"}
 - form{"name": null}

then we filled the stories using Interactive mode, all the stories looks like:

## Generated Story -3423082816679184942
* intent_bonjour
    - utter_bonjour
    - my_form
    - form{"name": "my_form"}
    - slot{"requested_slot": "topic"}
* form: intent_demandes_tout_genre
    - form: my_form
    - slot{"topic": "je voulais savoir si je pouvais creuser une piscine"}
    - slot{"demande_technique": "information"}
    - slot{"requested_slot": "description_demande_technique"}
* form: intent_demandes_tout_genre
    - form: my_form
    - slot{"description_demande_technique": "je voulais avoir des informations"}
    - form{"name": null}
    - slot{"requested_slot": null}

I don’t know if the normals actions have the same behavior, we instantly use the form, see the stories, bot wait for “Hello” action (intent_bonjour) and then we go on the form.

“Hello” action was always successfully executed, this is a constant we always send when user want use the chatbot.

i am facing the same issue. My form action works only once when i ask question again it is calling two stage fallback policy. Below is my tracker store.

tracker_store:
    type: SQL
    dialect: "mysql"
    url: "localhost"
    db: "rasa"
    username: "root" 
    password: "test"

I was able to reproduce a similar problem using the formbot example in the Rasa repository. I opened an issue Postgres tracker store breaks frombot · Issue #4254 · RasaHQ/rasa · GitHub.

We just merged a fix into master. We want to publish a next minor release end of this week/early next week. Would be great if you could test your bot with the next version, once it is out, and check if the problem is actually resolved. Thanks!

So. next week.

Hey, i just updated the docker rasa container with the last version of rasa and rasa-sdk.

i have made some quick tests, the bot seem to work correctly now.

Endpoint.yml

tracker_store:
    type: SQL
    dialect: "postgresql"
    url: postgres
    db: "****"
    username:  "****"
    password:  "****"

docker-compose.yml

version: '3.0'
services:
  rasa:
    image: rasa/rasa:latest-full
    .....
  action_server:
    image: rasa/rasa-sdk:latest
    ......
  postgres:
    image: postgres:latest

thanks for the fix, i can now put sqlite on the trash :stuck_out_tongue:

Great! Thanks for validating :slight_smile: