Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Async api for receiving messages #999

Open
HKalbasi opened this issue Jan 23, 2024 · 5 comments
Open

Feature Request: Async api for receiving messages #999

HKalbasi opened this issue Jan 23, 2024 · 5 comments
Assignees
Labels
A-dialogue Area: dialogue system A-dispatching Area: dispatching updates (`Dispatcher`, repls) A-update-managment Area: update managment (dptree & co) K-feature-request Kind: request for implementing a feature

Comments

@HKalbasi
Copy link

Using async/await instead of manual state machine can dramatically simplify code. For example the Dialogues management example in the readme can become something like this:

loop {
    let chat = bot.accept_new_chat().await?;
    tokio::spawn(async move {
        chat.send_message("Let's start! What's your full name?").await?;
        let full_name = chat.receive_message().await?;
        chat.send_message("How old are you?").await?;
        let age = loop {
            let age = chat.receive_message().await?;
            match age.parse::<u8>() {
                Ok(x) => break x,
                Err(_) => chat.send_message("Send me a number").await?,
            }
        };
        chat.send_message("What's your location?").await?;
        let location = chat.receive_message().await?;
        let report = format!("Full name: {full_name}\nAge: {age}\nLocation: {location}");
        chat.send_message(report);
        // chat is dropped, next message from this user will trigger `accept_new_chat` again.
    });
}

Pros

  • It enables expressing bot logic in a linear, dense, and more readable way.
  • It enables code reuse in ways that are hard/impossible currently. For example a we can have a prompt function to do send_message + receive_message, or extract the loop in the example above as fallible_prompt("Send me a number", |x| x.parse::<u8>())

Cons

  • In this way we store the state in the tokio task local variables, so we will lose it on bot restart. This tolerable in most cases, and sometimes even desired. We can always manually store things in the persistent storage in important checkpoints, and use this api for short dialogs.
@WaffleLapkin WaffleLapkin added K-feature-request Kind: request for implementing a feature A-dialogue Area: dialogue system A-dispatching Area: dispatching updates (`Dispatcher`, repls) A-update-managment Area: update managment (dptree & co) labels Jan 23, 2024
@Hirrolot
Copy link
Collaborator

Hirrolot commented Jan 23, 2024

In this way we store the state in the tokio task local variables, so we will lose it on bot restart. This tolerable in most cases, and sometimes even desired. We can always manually store things in the persistent storage in important checkpoints, and use this api for short dialogs.

Consider what happens when a bot gets restarted, which can happen due to some server error or if you decided to update the code. In this case, a user should not notice anything, but with the approach you've suggested the dialogue will be removed from memory and lost forever. From the point of a user, it'd look like a bug.

It's true that .await is much more readable and declarative though. In essence, .await can be described as a finite state machine (I remember there even was a macro for that back in the old days). However, I wasn't able to figure out how to exploit .await to make it compatible with persistent dialogues.

Also, the approach of saving the dialogue state in certain points is error-prone -- we can just forget to do that. On the other hand, the current approach with FSMs does it automatically behind the scenes.

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Jan 23, 2024

I think what @HKalbasi is trying to highlight here is that dialogues are usually short lived, so in a lot of cases it does not matter much if we lose it. (the probability to loose it mid dialogue is lower because it's short lived, it can be redone easily for the same reason, etc)

@HKalbasi
Copy link
Author

And it doesn't need to replace the current api, just complement it. For long dialogues or cases that persistence is very important, users can always use the current api.

@WaffleLapkin
Copy link
Member

WaffleLapkin commented Jan 23, 2024

I think my biggest concern here is that this requires quite a bit of design & implementation work. Given our capacity I don't think we'll be able to get to it anytime soon.

But it does look like with enough work & workarounds, this should be possible to implement. So, maybe one day :)

@Hirrolot
Copy link
Collaborator

I think what @HKalbasi is trying to highlight here is that dialogues are usually short lived, so in a lot of cases it does not matter much if we lose it. (the probability to loose it mid dialogue is lower because it's short lived, it can be redone easily for the same reason, etc)

A sudden interruption of the control flow can still look like a bug though. If a bot has enough users, this will certainly be noticed at some point.

My other concern is having two alternative implementations of the same idea. What usually happens is that users prefer the nicer-looking solution, then (some part of users) stumble upon the limitations of the basic solution, then rewrite the logic to use the more advanced solution. We already have it in the form of REPLs/dispatcher. Having it also for dialogues/.await points means that once a user of the library decides to use state machines instead of .await, they will need to rewrite almost the whole bot logic (in addition to breaking library changes). Having FSM-based logic right from the beginning would thus save time long-term.

Other than that, I agree with @WaffleLapkin that we don't have the capacity to implement it anytime soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-dialogue Area: dialogue system A-dispatching Area: dispatching updates (`Dispatcher`, repls) A-update-managment Area: update managment (dptree & co) K-feature-request Kind: request for implementing a feature
Projects
None yet
Development

No branches or pull requests

3 participants