Title says everything...
Thank you
@McBacker For reference, are you using .NET or Node SDK?
What problems are you experiencing? What is the context that you need to increase the timeout duration? To what specific endpoint?
.Net SDK
When a timeout occurs, I want to present to my Bot users a better message than "POST to XXX timed out after 15s".
Thank you
@McBacker What specific channels are you using with your bot? When does this error occur?
It happens when my on premise hosted messages endpoint is not reachable/running properly.
The channel doesn't matter on this scenario.
@nwhitmont, do you have any update on this?
Thank you
Anyone?
Anyone?
Is it possible to disable this messages?
Looks like not :-(
The timeout is from the connector services, and cannot be changed. The message also comes from the connector services.
Bots are expected to respond with an acknowledgement (status 200) within 15 seconds. Something that can be done on channels that allow proactive messages is to process the incoming message on a separate thread, and acknowledge the call immediately.
in .net:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
Task.Factory.StartNew(() => Conversation.SendAsync(activity, () => new Dialogs.RootDialog())); // process the message asyncronously
}
return Request.CreateResponse(HttpStatusCode.OK); //ack the call
}
Wouldn't it finish my Azure Function if I immediately return from this method?
I tried it and the Azure Function stopped running, and the real response to the chat never came. It's not a solution at all for the Function Bots
I ended up with this code for my Function Bot, which resolves the problem I just mentioned.
(see also https://stackoverflow.com/a/48846340/1830814)
public static class Functions
{
[FunctionName("messages")]
[return: Queue("somequeue")]
public static async Task<MessagePayload> Messages([HttpTrigger(WebHookType = "genericJson")]
HttpRequestMessage req) =>
// return from this Azure Function immediately to avoid timeout warning message
// in the chat.
// just put the request into "somequeue".
// We can't pass the whole request via the Queue, so pass only what we need for
// the message to be processed by Bot Framework
new MessagePayload
{
Content = await req.Content.ReadAsStringAsync(),
AuthScheme = req.Headers.Authorization.Scheme,
AuthParameter = req.Headers.Authorization.Parameter
};
// Do the actual message processing in another Azure Function, which is
// triggered by a message enqueued in the Azure Queue "somequeue"
[FunctionName("processTheMessage")]
public static async Task ProcessTheMessage([QueueTrigger("somequeue")]
MessagePayload payload, TraceWriter logger)
{
// we don't want the queue to process this message 5 times if it fails,
// so we won't throw any exceptions here at all, but we'll handle them properly.
try
{
// recreate the request
var request = new HttpRequestMessage
{
Content = new StringContent(payload.Content)
};
request.Headers.Authorization = new AuthenticationHeaderValue(payload.AuthScheme,
payload.AuthParameter);
// initialize dependency injection container, services, etc.
var initializer = new SomeInitializer(logger);
initializer.Initialize();
// handle the request in a usual way and reply back to the chat
await initializer.HandleRequestAsync(request);
}
catch (Exception ex)
{
try
{
// TODO: handle the exception
}
catch (Exception anotherException)
{
// swallow any exceptions in the exceptions handler?
}
}
}
}
[Serializable]
public class MessagePayload
{
public string Content { get; set; }
public string AuthParameter { get; set; }
public string AuthScheme { get; set; }
}
For those who run into this problem and built their bot using Node. A simple solution was to use async/await without the await. The idea is to respond immediately and then do the long running task asynchronously without waiting for it to complete. So it looks like this:
function async handleActivity(activity) {
if(activity.attachments)
doSomeLongRunningAsyncTask(activity);
...
respond();
}
function async doSomeLongRunningAsyncTask(activity) {
// do your long running task call here and send messages after it completes
}
Hi @EricDahlvang I recently upgraded our node.js botbuilder version to 4.2.0. In doing so, I now use TurnContext to process the activity and send messages. However, our app uses computer vision to process attachments and respond accordingly. The whole bot response process can take a long time (longer than 15 seconds) when handling these attachments. We used to get around this by sending a typing indicator message and responding with an acknowledgement (200). And then we would fork a thread that would respond with a message once the computer vision service was done. However, this no longer works because the TurnContext persists for the length of the turn. And we finished the turn already since we acknowledged with a 200.
Which leads me to my question, how do we respond in that long running thread/worker process without using TurnContext?
EDIT: Perhaps I can use adapter.continueConversation? https://docs.microsoft.com/en-us/javascript/api/botbuilder/botframeworkadapter?view=botbuilder-ts-latest#continueconversation
[TurnContext] provides information needed to process an incoming activity. The context object is created by a BotAdapter and persists for the length of the turn.
Hi @watadarkstar ,
Yes, adpater.continueConversation is the V4 sdk way to do proactive messaging. There is extensive discussion on V4 proactive messaging here: https://github.com/Microsoft/botbuilder-dotnet/issues/787
You'll need to either 1) use the existing adapter, and call .continueConversation after the long running process (from another thread) or 2) create a new adapater on the other thread, and call .continueConversation on it or 3) send an event to the bot from the background thread, listen for that event, and send the proactive message based on that event
Thanks @EricDahlvang you rock! 馃殌 I will report back with the solution I end up using. Hopefully it helps others who have a node.js bot.
Hi @EricDahlvang, I got a function in place that is working but there wasn't much documentation on how to get a reference to pass to continueConversation. I found TurnContext.getConversationReference(activity) and it seems to be working. I opted for your suggestion to create a new adapater on the other thread, and call .continueConversation on it as shown below. Is this how it should be done?
private async continueConversationWithAttachments(attachments) {
// Create bot adapter.
const adapter = new BotFrameworkAdapter({
appId: config.APP_ID,
appPassword: config.APP_PASSWORD
});
const reference: Partial<ConversationReference> = TurnContext.getConversationReference(this.activity);
// Proactively notify the user
if (reference) {
await adapter.continueConversation(reference, async (context: TurnContext) => {
this.turnContext = context;
await this.handleAttachment(attachments[0]);
});
} else {
throw new Error("Missing reference to conversation");
}
}
Furthermore, I saw that TurnContext.getConversationReference(activity) might be moved to the Activity class in dotnet: https://github.com/Microsoft/botbuilder-dotnet/issues/703 not sure if it did for JavaScript though.
@watadarkstar As long as there is no concern for state (no need to read or edit state, no use of dialogs or dialog stack), and you are simply sending a proactive message to the conversation, your method seems valid to me. The documentation here: https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-howto-proactive-message?view=azure-bot-service-4.0&tabs=javascript#logic-to-start-a-job is still using TurnContext.getConversationReference (I cannot speak to getConversationReference moving to Activity. I'm not sure what the plan is there. Even if it does move at some point in botbuilder-js, the existing method would still remain at least until a major version bump.)
@EricDahlvang Could you elaborate on "As long as there is no concern for state (no need to read or edit state, no use of dialogs or dialog stack)"
Specifically on the state part. As we do update our state in our database. Is that the kind of state you're talking about? And if so, why might this be problematic?
@drub0y Explains some here: https://github.com/Microsoft/botbuilder-dotnet/issues/787#issuecomment-453596457
Based on the code you've shared, it doesn't look like you are manipulating state while sending the proactive message. If you were, then there would be two places in your code where state is manipulated: the normal bot code, and this proactive section. Conflicts and issues are more likely to arise with multiple pieces of code performing the same function. That's really all I'm referring to.
Thanks for the quick response @EricDahlvang! :) I think I know what you mean....The async nature of things could lead to odd race cases if both touching state.
return Request.CreateResponse(HttpStatusCode.OK);
what about this approach to keep task awaitable:
var timeout = 14000;
var task = Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
await Task.WhenAny(task, Task.Delay(timeout));
return Request.CreateResponse(HttpStatusCode.OK);
@EricDahlvang @watadarkstar
I do not think sending the reply messages as proactive message for each incoming message is a good idea because your bot service has to then load the state from the storage twice (one to form the first turnContext in processActivity and second to form turnContext in adapter.continueConversation).
I tried below changes in my code to reduce API response time.
server.post(msgEndpointPath, (req, res, next) => {
adapter.processActivity(req, res, async (turnContext) => {
res.send(200);
next();
await bot.run(turnContext);
});
});
Most helpful comment
.Net SDK
When a timeout occurs, I want to present to my Bot users a better message than "POST to XXX timed out after 15s".
Thank you