emora-chat / emora_stdm

State Transition Dialogue Manager
24 stars 9 forks source link

asynchronous out-of-turn interactions #15

Open abbbe opened 1 year ago

abbbe commented 1 year ago

What would be the best way to implement asynchronous interactions with DialogFlow? I am implementing a kind of an interactive todo list with a reminder. Messages defining a current task and alarm specifications come in from from Matrix/Telegram channels into handle_input().

Currently my code is as following, but it is quit messy. Is there better approach? Or Emora STDM is not suitable for this task?

    async def do_system_turn(self):
        if self.state() == TaskState.SET_NAG_TIMER:
            assert self.speaker() == Speaker.SYSTEM

            # user has just confirmed he is busy with the task - set nag timer
            nag_timeout = min(NAG_TIMEOUT, self.duration)
            self.nag_timer = time.time() + nag_timeout
            asyncio.create_task(self.channel.output(
                f"will nag you in {timedelta(seconds=nag_timeout)}"))
            self.set_state(TaskState.WAIT_NAG_TIMER_EXPIRES)
            self.set_speaker(Speaker.USER)

            logging.info(f"nag timer set")
        if self.speaker() == Speaker.SYSTEM:
            system_response = self.system_turn()
            asyncio.create_task(self.channel.output(system_response))

    async def handle_input(self, text):
        logging.info(f"input event body={text}")

        logging.info(
            f"before user_turn: state={self.state()} speaker={self.speaker()}")
        assert self.state() == TaskState.WAIT_NAG_TIMER_EXPIRES or self.speaker() == Speaker.USER
        self.user_turn(text)
        logging.info(
            f"after user_turn before system_turn: state={self.state()} speaker={self.speaker()}")
        await self.do_system_turn()
        logging.info(
            f"after system_turn: state={self.state()} speaker={self.speaker()}")

    async def handle_tick(self):
        if self.state() == TaskState.WAIT_NAG_TIMER_EXPIRES:
            if time.time() >= self.nag_timer:
                logging.info(f"nag timer expired")
                self.nag_timer = None

                self.set_state(TaskState.ASK_IF_STILL_DOING)
                self.set_speaker(Speaker.SYSTEM)

                await self.do_system_turn()
        else:
            assert self.speaker() == Speaker.USER

    async def run(self):
        # logging.debug("first turn starts")
        # await self.do_system_turn()
        # logging.debug("first turn ends")

        logging.debug("entering loop")
        while (True):
            await asyncio.sleep(1)
            await self.handle_tick()