Unable to accept other users messages while performing actions

Hi , I have been using RASA 1.3.X version. My issue is as below.

I have designed the actions in such a way that, when a user messages to the chatbot, chatbot performs an action. In the action I need to hit an API which may take some time to gather the response. So we have kept the timeout as 60 seconds in the requests while hitting for the API.

Now my issue is, when a User A is waiting for the API response, then in the 60 seconds, if a User B is typing in a message , the chatbot is not giving the response to User B. It is getting stuck with User A’s action and waiting for the response.This is causing a unnecessary wait time for User B as well. The chatbot is only responding to User B only after the User A action is complete.

Please let me know for any more questions.

Hi, A message by user B that doesn’t need a custom action can always be handled by Rasa in parallel while the action server is busy with the action for user A. If user B’s message requires an action, too, it has to wait for the action server to be “free”.

In your case, what takes so long is not the stuff that happens on the action server itself, but waiting for the response from an external API. You can make the function in your action that calls the API async and await its return. That will “free up” the action server in the mean time, so it can handle another action while waiting.

Does this help with your question?

Thanks @chkoss. I will try to implement the async and await to a function calling the API.

I will implement and get back to you.

Thanks for the help.

Hey @chkoss, I have implemented the async and await. But I am quite unsure if I am creating the asyncio loop event correctly.Please let me know if I am incorrect in the below code.

Below the async function which will call the apis.

async def call_async_api(api_url):
    print(api_url)
    api_session = requests.Session()
    api_session.auth = ("Username",'Password')
    api_adaptor = HTTPAdapter(max_retries=3)
    api_session.mount(api_url,api_adaptor)

    data  = api_session.get(api_url,timeout=6)
    return data

Below is the function ‘call_api’ which awaits the ‘call_async_api’ and the decorator ‘start_event_loop’ will make the function ‘call_api’ run inside the event loop.

@start_event_loop
async def call_api(*args,**kwargs):
    return await call_async_api(*args,**kwargs)

Below is the start_event_loop code which runs the call_api function in the event loop.

def start_event_loop(func):
    def inner(*args,**kwargs):
        loop = asyncio.get_event_loop()
        data = loop.run_until_complete(func(*args,**kwargs))
        return data
    return inner

In the actions of rasa I am using the call_api as below

api_response = call_api(api_url)

I want to know if the implementation works correctly in the actions and need to verify that the original issue, User B cannot message when User A message action is waiting for API response is solved.

Any help is appreciated.Thanks in advance.

Hey, your code can be simplified quite a bit: Every action’s run function is async already, so you don’t need to create a new event loop. This means you don’t need the functions call_api and start_event_loop at all. Instead you can simply do

api_response = await call_async_api(api_url)

in the run function of your action.

Also, your current solution is not really asynchronous because the requests library doesn’t support this. The api_session.get call will block the entire event loop. To send requests asynchronously you can use the aiohttp library, so instead of requests.Session.get use aiohttp.ClientSession.request.

Hey @chkoss, Thanks for the quick reply.Appreciate it a lot.

I have written the following statement in the actions run method.

api_response = await call_async_api(api_url)

The above statement is giving me a syntax error.I think the run methods in action classes are not async and when I define them as async, then the action is not running, as it is returning a coroutine and error says coroutine cannot be iterable.I am using rasa 1.3.X.Please let me know if its a version issue.

So, Instead I have made the changes you said regarding requests module to aiohttp and the code now is as below.

Changed my call_async_api function as below.

 async def call_async_api(api_url):
    timeout = aiohttp.ClientTimeout(total=20)
    auth = aiohttp.BasicAuth("Username",'Password')
    api_session = requests.Session()
    async with aiohttp.ClientSession(auth=auth, timeout=timeout) as session:
            async with session.get(api_url) as resp:
                return resp.status, await resp.json()

My call_api is unchanged.

 @start_event_loop
 async def call_api(*args,**kwargs):
    return await call_async_api(*args,**kwargs)

My start_event_loop function is:

def start_event_loop(func):
    def inner(*args,**kwargs):
        loop = asyncio.get_event_loop()
        status,response = loop.run_until_complete(func(*args,**kwargs))
        return status,response
return inner

And finally, in the action file, I am calling the call_api as follows

status, response = call_api(api_url)

With above code, still the issue persists.User B needs to wait until User A’s action api response returns.

I doubt the implementation of event loop and aiohttp Client session is correct. I am not sure what to do here.

Please let me know If I am doing anything incorrect.

Thanks in advance for the help.

Hello Did u find the solution I am also facing the similar issue My rasa version is 1.4.1

Hello did you find the solution how to use aiohttp? Please help me in achieving this.