Here is the detail on one of the transaction (For most of the transactions the observation is the same). The screenshots below are of an "End-to-End transaction details" found under Application Insights's Performance Option.
The total duration of the transaction is around 29.4 seconds out of ~26 seconds is spent between the Step 1 & 2 marked in the below Timeline & Telemetery screenshots.
Timeline
Telemetery
Could someone help me understand the gap between Step 1 & 2 and how can I reduce this to increase the performance.
As you can also see in the Telemetery screenshot that the custom trace messages are also printed (highlighted in Red) mentioned in the MessagesController.cs
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
var telemetry = new Microsoft.ApplicationInsights.TelemetryClient();
telemetry.TrackTrace("MessagesController POST",
Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Warning,
new Dictionary<string,string> { {"Activity Id", activity.Id} });
// check if activity is of type message
if (activity != null && activity.GetActivityType() == ActivityTypes.Message)
{
telemetry.TrackTrace("MessagesController Type:Message",
Microsoft.ApplicationInsights.DataContracts.SeverityLevel.Warning,
new Dictionary<string,string> { {"Activity Id", activity.Id} });
//var reply = activity.CreateReply(String.Empty);
//reply.Type = ActivityTypes.Typing;
//await context.SendResponse(reply);
await Conversation.SendAsync(activity, () => new EchoDialog());
Is it that after executing this line - await Conversation.SendAsync(activity, () => new EchoDialog()); it is putting the message in some queue and then it is picked up by the EchoDialog after sometime?
Thanks in advance!
This was due to the load script that I had created. The load script was creating same conversation id for multiple users and hence it was taking time to retrieve the dialog record.
Related
Trying to create and debug Teams calling and meeting bot from Bot Framework v4 for CPI project. found here: CallingBotSample
I have gone through all the steps correctly But I have some problems which got me stuck.
Tunneling with Ngrok work fine (200 OK) for /api/callback and /api/messages
Problem 1: AdaptiveCard v1.3 is not showing when the Bot is starting.
Fact: I want the card to show every time the bot is starting as shown in this link: Calling Bot
Problem 2: OnMessageActivityAsync()get the request from user such as createcall However the turnContext.Activity.Text has the value But turnContext.Activity.Value returns null and that will result on Bot showing message but not calling or joining meeting.
Snip:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(turnContext.Activity.Text))
{
//turnContext.Activity.Text = null;
dynamic value = turnContext.Activity.Value;
if (value != null)
{
string type = value["type"];
type = string.IsNullOrEmpty(type) ? "." : type.ToLower();
await SendReponse(turnContext, type, cancellationToken);
}
}
else
{
await SendReponse(turnContext, turnContext.Activity.Text.Trim().ToLower(), cancellationToken);
//await OnTurnAsync(turnContext, cancellationToken);
}
}
What I want is a 100% working calling bot that can join any user calls and record it
Does anyone have a subjection or a solution to my questions?
We tried to repro this issue at our end, but everything works for us. We are able to get Welcome card and calls are getting placed without any issue.We can record the call as well. Also able to join scheduled meeting and Invite participants to meetings as mentioned.
Please make sure you have enabled calling and configured proper endpoint on Teams channel page.
Here is my scenario:
I have an azure website, https://[my app on azure].azurewebsites.us
A user of this application wishes to schedule a future action,
reached via a uri.
The application keeps a record of this desire in an Azure SQL database table
having a "When to act" datetime column and an "Action GUID".
Example: User "bob" will go to the scheduling page in my website
and enter a date and time he wishes the action to execute.
Bob enters 2020-07-11 # 11:11 am (PDT).
Upon successful submission a record gets added to the database
with an application generated GUID, "AC5ECA4B-FB4F-44AE-90F9-56931329DB2E"
with a "When to act" value of 2020-07-11 11:11:00.00 -07:00
The action url will an url in my website https://[my app on azure].azurewebsites.us/PerformAction/AC5ECA4B-FB4F-44AE-90F9-56931329DB2E
The SQL database is NOT a Managed Instance.
Would this be possible using SQL CLR?
I'm thinking not.
Is there a way to do this using an Azure Function and/or Logic App?
Any other ideas?
Much appreciated!
The pattern you are putting together is a simple polling pattern. You need a function that will poll your data store every x minutes(or seconds) (function cron expression => * */x * * * *). It would then check that the current time is on or about to be exceeded by the stored time. If this is the case, it could then make your call. Don't forget to write back to the data store the success or failure of the call. If there are multiple steps, you should record where in the full life-cycle that command is. This way you can take the appropriate action.
To make your function app better, I would introduce a message queue (Azure Storage Queue) that receives the parts required to make the call. Then have a function (triggered by the messages on the queue) to send your web call. This way each function is operating separately from each other.
To reduce the need to poll all of the records, you could filter your records with SQL to be "between" certain timestamps guided by the current time of the the polling function.
If you need more clarity, leave comments and I'll try to provide more context.
Store or convert times to UTC to avoid timezone mismatches.
Logic apps have a Built-in step Action named "Delay until"
Using this step action in a logic app workflow solved the issue.
Here is what it looks like in the designer:
Here are further details.
The applications calls the logic app via the HTTP POST URL in the first step
passing in these properties:
{
"properties": {
"GUID": { // an identifier of the apps schedule
"type": "string"
},
"ID": { // an simpler identifier of the apps schedule
"type": "integer"
},
"MessageID": { // yet another identifier
"type": "integer"
},
"WhenToSend": { // the datetime in UTC format for when to Delay Until
"type": "string"
}
},
"type": "object"
}
Example of payload sent:
Here is a snippet of the app calling the LA:
newMessageSchedule.MessageScheduleStatuses.Add(new MessageScheduleStatus()
{
MessageScheduleStatusID = (int)ApplicationEnums.MessageScheduleStatuses.New
});
messageGettingSent.MessageSchedules.Add(newMessageSchedule);
await _dbContext.SaveChangesAsync();
dynamic schedulePayloadJSON = JsonConvert.SerializeObject(new
{
ID = newMessageSchedule.ID,
GUID = newMessageSchedule.MSGuid,
MessageID = newMessageSchedule.MessageID,
WhenToSend = newMessageSchedule.WhenToSend.UtcDateTime
});
HttpClient httpClient = new HttpClient();
HttpContent c = new StringContent(schedulePayloadJSON, Encoding.UTF8, "application/json");
var response = await httpClient.PostAsync($"{dto.SchedulerUri}", c);
if (response.StatusCode == HttpStatusCode.OK)
{
var content = await response.Content.ReadAsStringAsync();
//dynamic httpResponse = JsonConvert.DeserializeObject<dynamic>(content);
newMessageSchedule.MessageScheduleStatuses.Add(new MessageScheduleStatus()
{
MessageScheduleStatusID = (int)ApplicationEnums.MessageScheduleStatuses.Scheduled
});
messageGettingSent.MessageSchedules.Add(newMessageSchedule);
await _dbContext.SaveChangesAsync();
}
The dto.SchedulerUri is set from an appsettings setting,
TextMessaging:Scheduler and has a value which looks something like this:
"https://prod-123.usgovarizona.logic.azure.us:443/workflows/1d8.../triggers/request/paths/invoke?api-version=2016-10-01&sp=%2Ftriggers%2Frequest%2Frun&sv=1.0&sig=CJt..."
Once the "Delay Until" time occurs, the LA calls back into the app w/ the IDs:
The controller action performs all the things.
Voila
/vwäˈlä/
I'm not getting Azure max limit document post in one Request.
Just I Want to know is there any official link that shows the max limit per indexer in the azure server as per the purchased plan Click me.
Here the azure Offical Indexer limits Link
Below the Code for creating batches
public virtual void PostBulkDataToAssortmentIndex(ISearchFilterResult result)
{
var itemFilters = _searchManager.GetAllFilters();
IEnumerable<ISearchItem> items;
List<IndexAction<AzureSearchItem>> actionList = new List<IndexAction<AzureSearchItem>>();
for (int i = 0; i < result.Items.Count; i = i + 32000)
{
actionList.Clear();
items = result.Items.Skip(i).Take(32000);
foreach (var item in items)
{
actionList.Add(IndexAction.MergeOrUpload(FormatSearchItem(item, itemFilters)));
}
// Post documents to index
PostBulkAssortmentDocuments(actionList.AsEnumerable());
}
}
public virtual void PostBulkAssortmentDocuments(IEnumerable<IndexAction<AzureSearchItem>> actions)
{
var batch = IndexBatch.New(actions);
try
{
var data = GetIndexClient(IndexName).Documents.Index(batch);
var passResultCount = data.Results.Where(x => x.Succeeded).Count();
var failResultCount = data.Results.Where(x => x.Succeeded == false).Count();
var MessageResult = data.Results.Where(x => !string.IsNullOrEmpty(x.ErrorMessage));
var keyResult = data.Results.Where(x => !string.IsNullOrEmpty(x.Key)).Select(x => x.Key).ToList();
var unikKey = keyResult.Distinct().ToList();
}
catch (IndexBatchException e)
{
// Sometimes when your Search service is under load, indexing will fail for some of the documents in
// the batch. Depending on your application, you can take compensating actions like delaying and
// retrying. For this simple demo, we just log the failed document keys and continue.
Console.WriteLine(
"Failed to index some of the documents: {0}",
String.Join(", ", e.IndexingResults.Where(r => !r.Succeeded).Select(r => r.Key)));
}
}
Note: As per Azure document I'm not sure but document post limit per batch only 1000 allowed but for testing purpose I'm passed the 0 documents in IndexBatch.New(actions);
Then Azure throws the exception.
'The request is invalid. Details:
actions: No indexing actions found in the request. Please include between 1 and 32000 indexing actions in your request.
'
And if I passed the 32001 documents in per IndexBatch.New(actions);
Then Azure throws the exception.
The request is invalid. Details: actions: Too many indexing actions found in the request: 32001. Please include between 1 and 32000 indexing actions in your request.
As per the documentation, the limit is 1000 documents or 16MB whichever comes first.
This approach is more flexible than the pull model because you can
upload documents individually or in batches (up to 1000 per batch or
16 MB, whichever limit comes first). The push model also allows you to
upload documents to Azure Cognitive Search regardless of where your
data is.
Microsoft Bot Framework V4, I have a waterfall Dialog defined in a dialog as below
var waterfallSteps = new WaterfallStep[]
{
CallConfirmAsync,
SimilarProductAsync,
CheckNewVersionAsync,
};
AddDialog(new WaterfallDialog("productenquiry", waterfallSteps));
After the execution of the first two waterfall steps, my conversation is stop due to unresponsiveness from user's end. So I want to resume from the third method when i.e., CheckNewVersionAsync when the user comes back again to the bot.
Can anyone please help me here.
So, at the bot level, this should happen automatically if you've configured the IStatePropertyAccessor<DialogState> with the ConversationState. No matter how long the user takes to respond, your WaterfallDialog will stay at the top of the stack and it will remember exactly what step it was on. Assuming your user comes back to the same conversation, then it will pick right up where it left off.
Given that, the fact that you are asking this question leads me to believe that perhaps you are using WebChat which doesn't maintain the same conversationId across page loads unless you set that up yourself. If that's the case, then I would suggest you ask another question about how to do that if you can't figure out how since that's a separate issue from the dialog state being persisted correctly.
Edit: Drew's answer is correct, but mine provides another potential solution. You can find more info here: Managing State. In particular:
User state is available in any turn that the bot is conversing with
that user on that channel, regardless of the conversation Conversation
state is available in any turn in a specific conversation, regardless
of user (i.e. group conversations) Private conversation state is
scoped to both the specific conversation and to that specific user
Tip
Both user and conversation state are scoped by channel. The same
person using different channels to access your bot appears as
different users, one for each channel, and each with a distinct user
state.
Additional Solution
This solution is best for if you're able to specify the from Id, but cannot ensure that conversation Id remains the same (see below, under Gotchas).
You could save what step the user is on in their UserState.
BasicBot
BasicBot does this with its GreetingState class.
From its GreetingDialog:
In the first step, it initializes the GreetingState, which tracks how far along in the dialog the user is by seeing what user variables have already been set:
private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
if (greetingState == null)
{
var greetingStateOpt = stepContext.Options as GreetingState;
if (greetingStateOpt != null)
{
await UserProfileAccessor.SetAsync(stepContext.Context, greetingStateOpt);
}
else
{
await UserProfileAccessor.SetAsync(stepContext.Context, new GreetingState());
}
}
return await stepContext.NextAsync();
}
And then in each step, it loads the GreetingState:
var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context);
And checks to see if the step has already been completed with something like:
if (greetingState != null && !string.IsNullOrWhiteSpace(greetingState.Name) && !string.IsNullOrWhiteSpace(greetingState.City))
If there's no greetingState or .Name or .City exists, it prompts for them, and if they are already filled out, it moves on with:
return await stepContext.NextAsync();
At each step, it saves to the GreetingState with something like:
greetingState.Name = char.ToUpper(lowerCaseName[0]) + lowerCaseName.Substring(1);
await UserProfileAccessor.SetAsync(stepContext.Context, greetingState);
Simplifying for your use case
For you, if you don't need to save user information, you could create a simple Step class:
{
/// <summary>
/// User state properties for Waterfall Step.
/// </summary>
public class Step
{
public string StepNumber { get; set; }
}
}
Make the first step of your WaterfallDialog:
private async Task<DialogTurnResult> InitializeStateStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var StepState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
if (StepState == null)
{
var StepStateOpt = stepContext.Options as StepState;
if (StepStateOpt != null)
{
await UserProfileAccessor.SetAsync(stepContext.Context, StepStateOpt );
}
else
{
await UserProfileAccessor.SetAsync(stepContext.Context, new StepState());
}
}
return await stepContext.NextAsync();
}
On each step, load the current Step:
var stepState = await UserProfileAccessor.GetAsync(stepContext.Context);
Check to see if they're already past the current step:
if (stepState.StepNumber <= 2)
{
// ...do stuff
// Save that user has completed step
stepState.StepNumber++;
await UserProfileAccessor.SetAsync(stepContext.Context, stepState);
}
else
{
return await stepContext.NextAsync();
}
Gotchas
A couple big things to watch out for:
The UserState only persists for the same from ID and channel ID. Make sure that the user that leaves in the middle of a waterfall has the same from ID when they re-enter it and that they re-enter it from the same channel. This isn't the default for the Emulator--in the Emulator, when a session is restarted, a new from ID is created. (Note: Consider from ID to be synonymous with User ID. It just comes from Activity.From.Id)
The ConversationState only persists for the same conversation ID and channel ID. Persistence of the conversation ID within the channel varies by channel.
More info on the different IDs: ID fields in the Bot Framework.
First of all thank you for your awesome work in building and maintaining this library.
I have a scenario in which I need to check if the person answered within 10 seconds. I have some code that looks similar to this where I measure the start time in the first waterfall step and I measure the end time in the next waterfall step, I ll find the difference between both in the second waterfall step.
bot.dialog('/duration', [(session, args)=>{
session.dialogData.startTime = new Date().getTime()
}, (session, results)=>{
session.dialogData.endTime = new Date().getTime()
}])
I feel that the code above is not accurate. I have seen a session.message.timestamp property. How would it be different than the code above
Is there a better way to measure time differences like these?
How do I account for network latency in such a scenario?
Thank you for your answers in advance
You can set the time you send the message and then re-evaluate with the message timestamp like:
var bot = new builder.UniversalBot(connector, [
function (session) {
session.userData.lastMessageSent = new Date();
builder.Prompts.text(session, 'Send something in 10 seconds or you die.');
},
function (session, result) {
if (session.userData.lastMessageSent) {
var lastMessageSent = new Date(session.userData.lastMessageSent);
var lastMessageReceived = new Date(session.message.timestamp);
var diff = lastMessageReceived - lastMessageSent / 1000;
if (diff >= 10) {
session.send('Game over.');
} else {
session.send('Good boy!');
}
}
}
]);
A better way to do that might be using the Application Insights connection when registering a bot.
This way the Bot Framework service measures your requests/responses and stores the timestamp automatically into Application Insights.
Once you copy the instrumentation key to the bot registration page, events under customEvents in Application Insights Analytics.
In case you just to have an actionable code, the answer above is a better solution.