Cannot pass data across intents in Dialogflow Fulfillment - dialogflow-es-fulfillment

In Dialogflow Fulfillment I simply want to pass data from the Welcome Intent to the help Intent using conv.user.storage as seen in the code below. I can add it in the welcome intent but when I try to retrieve it in the help intent it is always undefined meaning data is NOT passed to the help intent. I have spent several hours on something I thought was straight forward and played around without any success. I would really appreciate a real world example on how to fix it and understand what I'm doing wrong.
function welcome(agent) {
agent.add(request.body.queryResult.fulfillmentMessages[0].text.text[0]);
var entity = 'media_getreq?message=volume';
getData(entity).then(result => {
let conv = agent.conv();
conv.user.storage["devicedata"] = result;
console.log(conv.user.storage["devicedata"]); //WORKS
});
}
function help(agent) {
agent.add(request.body.queryResult.fulfillmentMessages[0].text.text[0]);
let conv = agent.conv();
console.log(conv.user.storage["devicedata"]); //ALWAYS EMPTY
}

You have missed out Contexts a critical componet required to link intents.
Contexts represent the current state of a user's
request and allow your agent to transport conversation information from one intent to another.
You can use combinations of input and output contexts to control the conversational path the user traverses through your dialog sequence.
In summary, The intent that collects your Welcome input uses an output context to "remember" what you said.
The same context is used as the input context to the next intent, which collects input into the HelpIntent.
You need to update your code accordingly. Please see for details:
https://dialogflow.com/docs/contexts
https://dialogflow.com/docs/contexts/contexts-api

Related

Flutter best practice for form submissions

I am having trouble finding good resources for what best practices would be for Flutter development, specifically for form handling.
Everything I find on form submissions is fairly clear, but the problem is they all have the validation logic and submission logic directly in the form widget. I don't like this as it seems it would get very convoluted very quickly with more than say 3 inputs and any sort of more than basic validation logic. It also seems to violate the separation of concerns thinking that I though was supposed to be a big thing in Flutter/Dar (at least from what I have read).
So my chosen solution for this was my FormHandler class, which I defined in the form_handler.dart file. It has some static methods for validation of input, some methods for submission handling, and a formInput of type Map<String, dynamic> for storing key value pairs of user input.
It works like this:
An instance of the FormHandler is created
The user inputs the data
On form.save(), for each user input, the input data is stored in the formInput map, with key being the title of the input, and the value being the user's input.
The submission button would run the validation and save functions and then take the data from formInput and send it to something like a database handler that would store it on the db
form_handler.dart:
class FormHandler {
// make new form handler with empty map
FormHandler({required this.formInput});
// for storing input key value pairs
Map<String, dynamic> formInput;
// Form submissions
// new course
void submitCourse({required formKey}){
final form = formKey.currentState;
// save on validate
if( form.validate() ){
form.save();
// then make new course via the database controller
}
}
// Input validations
static String? validateTextInput(String? input){
if( input == null || input.isEmpty ){
return 'Field must not be empty';
} else {
return null;
}
}
}
I'm just wondering if this is a good solution, what are some potential pitfalls, any suggestions etc.
It seems like a good solution to me, but I would like feedback from someone with more experience than me.
Thanks, Seth.
A common practice would be to create value objects or entities that hold logic to do validation on themselves. Perhaps with an interface (abstract class) as a base for those to make sure that you don't forget to implement such validation.
Thereby moving such validation logic from the UI and instead caring about what is important in your domain. It will be possible to validate in several places in your code, not just from the form, which is not something easily achievable with your proposed solution. Your solution still mix UI with logic via the formKey. You might want to validate "stuff" when you receive data from your backend, or do other calculations/changes to values later in the app.
You can refer to this youtube channel for guidance.
Here is a link for a video related to TextFields and Validation - https://youtu.be/2rn3XbBijy4

Microsoft Bot Framework - Multi turn context is lost after the first interaction

I recently moved my code from SDK v3 to v4 and I am trying to take advantage of the multi-turn features.
I have looked over the samples from GitHub. The samples work well for multi-turn but one issue I noticed is that it recognizes the context only if the prompt is clicked immediately after the initial answer (with prompts) is shown.
I would like to be able to identify, at any given time that a prompt is clicked. I am storing all the previous prompts in the state object (dialogInstance.State) already. I have a custom host, which sends the replytoid and using that I can get the appropriate state.
The problem is though, I am not able to get to a point where I can use the dialoginstance.State.
The sample code uses the DialogExtensions class. The "DialogExtensions" class tries to gather the previous context by checking if the result from the ContinueDialogAsync method returns null or not.
DialogExtensions class with multi-turn
When there is no previous context (no previous answer with prompts), then the call to the ContinueDialogAsync returns a result with Empty Status.
I am thinking this where I need to check the dialogstate and if the new message refers to any of the old messages at any given point, it can then start to continue the old conversation.
I am not sure if that is even possible.
Any help/pointers would be appreciated.
thanks,
I eventually ended up implementing something that will work for custom bot host/direct channel bot client.
The whole point is that the call to the qnamaker api should happen with the old context object, whenever an option is chosen, even if it is out-of-context.
First let me explain how it works in the current version of the code.
The way the bot code was trying to solve multi turn dialog, was by storing the current answer if it had prompts/options and returning the conversation state in "waiting" mode. When the next question is received, it would automatically assume that the new question is part of the prompts/options of the old question. It would then pass the oldstate along to the QnAMaker.
What I noticed is that, even if the question in the second turn is not part of the prompts/options (something the user has typed manually and is a completely different question), it would still send the oldstate object to the QnAMaker.
The QnAMaker api call seem to ignore oldstate if the new question is not part of the prompts/options of the oldstate. It will work correctly by fetching the answer for the new question that was typed manually.
This was the key. If we can focus on what gets to the qnamaker then we can solve our original problem.
I realized that having the bot return a waiting state is only a mechanism to create a condition to extract the oldstate in the next turn. However, if I can rebuild the oldstate anytime when there is an option chosen, then the call to the qnamaker would work equally well.
This is what I have done now.
In my custom bot host code (which is a direct line client), I am sending the ReplyToID field populated with the original question whenever a prompt is clicked. Then in the bot code, I have changed it so that if there is a replytoid present, then build a new oldstate object with the data from the reply to id. Below is the QnABotState class that represents the oldstate. its a very simple class containing previous qna question id and the question text.
public int PreviousQnaId { get; set; }
public string PreviousUserQuery { get; set; }
QnABoState class
Now, the problem was the Activity object contains ReplyToId but does not contain ReplyToQuery (or something like that). Activity object is used to send data from bot client to the bot. So, either I would have to use a different field or send the PreviousUserQuery as an empty string. I had a hunch that it would work with just the previousqnaid.
//starting the process to get the old context (create an object that will hold the Process function's current state from the dialog state)
//if there is replyToId field is present, then it is a direct channel request who is replying to an old context
//get the reply to id from summary field
var curReplyToId = "";
curReplyToId = dialogContext.Context.Activity.ReplyToId;
var curReplyToQuery = "";
var oldState = GetPersistedState(dialogContext.ActiveDialog);
//if oldstate is null also check if there is replytoid populated, if it is then it maybe a new conversation but it is actually an "out of turn option" selection.
if (oldState == null)
{
if (!string.IsNullOrEmpty(curReplyToId))
{
//curReplyToId is not empty. this is an option that was selected out-of-context
int prevQnaId = -1;
int.TryParse(curReplyToId, out prevQnaId);
oldState = new QnABotState() { PreviousQnaId = prevQnaId, PreviousUserQuery = curReplyToQuery };
}
}
With that in place, my call to the qnamaker api would receive an oldstate object even if it is called out-of-context.
I tried the code and it worked. Not having the previous qna query did not make a difference. It worked with just the PreviousQnaId field being populated.
However, please note, this will not work for other channels. It would work for channels where you can set the ReplyToId field, such as the Direct Channel Client.
here is the code from my bot host:
// to create a new message
Activity userMessage = new Activity
{
From = new ChannelAccount(User.Identity.Name),
Text = questionToBot,
Type = ActivityTypes.Message,
Value = paramChatCode,// + "|" + "ShahID-" + DateTime.Now.TimeOfDay,
Id = "ShahID-" + DateTime.Now.TimeOfDay,
ChannelData = botHostId//this will be added as the bot host identifier
};
//userMessage.Type = "imBack";
if (paramPreviousChatId > 0)
{
//we have a valid replytoid (as a part of dialog)
userMessage.ReplyToId = paramPreviousChatId.ToString();
}

How to use actions.intent.DATETIME?

It is "clearly" defined in the documentation, but I can find no example of how to use actions.intent.DATETIME.
Please provide an example of what is needed in the 'action.json' file, and how my code can get the date and time provided using the assistant SDK helper. I haven't been able to figure out how to use actions.intent.___ at all!
At the simplest level, I want my code to know whether it is morning or evening for the person since I need to give different information in each case. Someone might want to do this to respond "Good morning" or "Good evening".
Also to do with intents, at a more complex level, I also want to know their approximate location (lat/long). I figured that once I know how to work with DATETIME, I'd be able to apply the same code pattern to use getDeviceLocation.
There is some code at https://github.com/actions-on-google/actions-on-google-nodejs that uses the DATETIME intent, but it asks the user for any time. I want to simply know what their current time is.
The DateTime intent can be invoked using the askForDateTime() method from the ActionsSdkApp class in our client library. Simply call the method from an intent, and pass it some queries which clarify the prompting. Then listen for the response using a listener for the actions.intent.DATETIME intent, just as you would listen for the actions.intent.TEXT intent.
In the handler for the actions.intent.DATETIME intent, you can use the getDateTime() method to retrieve the data. Unfortunately this intent only works by asking for an exact date and time from the user, and it is a generic date and time, so there is no guarantee that it is their current datetime unless you structure your prompts in a way to guide the user towards that.
const app = new ActionsSdkApp({ request, response });
function welcomeIntent (app) {
app.askForDateTime('When do you want to come in?',
Which date works best for you?',
'What time of day works best for you?');
}
function datetime (app) {
app.tell({speech: 'Great see you at your appointment!',
displayText: 'Great, we will see you on '
+ app.getDateTime().date.month
+ '/' + app.getDateTime().date.day
+ ' at ' + app.getDateTime().time.hours
+ (app.getDateTime().time.minutes || '')});
}
const actionMap = new Map();
actionMap.set(app.StandardIntents.MAIN, welcomeIntent);
actionMap.set(app.StandardIntents.DATETIME, datetime);
app.handleRequest(actionMap);
As you mentioned you can invoke and handle the actions.intent.PERMISSION intent in a similar way to get a precise longitude and latitude location.
As a side note, if you are using API.AI, you can use their Date and Time system entities to do this, or use the askForDateTime() method in the ApiAiApp class from the client library.
You can em-bed the maps API location/time in your app JSON or, other open source. As far as location it is device and user settings specific so, whether or not you get a JSNODE response from the device/user depends on the user even if they are running the APP and, have different setting preferences.

Angular Dynamic form observable property binding

I have a problem with some dynamically generated forms and passing values to them. I feel like someone must have solved this, or I’m missing something obvious, but I can't find any mention of it.
So for example, I have three components, a parent, a child, and then a child of that child. For names, I’ll go with, formComponent, questionComponent, textBoxComponent. Both of the children are using changeDetection.OnPush.
So form component passes some values down to questionComponent through the inputs, and some are using the async pipe to subscribe to their respective values in the store.
QuestionComponent dynamically creates different components, then places them on the page if they match (so many types of components, but each questionComponent only handles on one component.
some code:
#Input() normalValue
#Input() asyncPipedValue
#ViewChild('questionRef', {read: ViewContainerRef}) public questionRef: any;
private textBoxComponent: ComponentFactory<TextBoxComponent>;
ngOnInit() {
let component =
this.questionRef.createComponent(this.checkboxComponent);
component.instance.normalValue = this.normalValue;
component.instance. asyncPipedValue = this. asyncPipedValue;
}
This works fine for all instances of normalValues, but not for asyncValues. I can confirm in questionComponent’s ngOnChanges that the value is being updated, but that value is not passed to textBoxComponent.
What I basically need is the async pipe, but not for templates. I’ve tried multiple solutions to different ways to pass asyncValues, I’ve tried detecting when asyncPipeValue changes, and triggering changeDetectionRef.markForChanges() on the textBoxComponent, but that only works when I change the changeDetectionStrategy to normal, which kinda defeats the performance gains I get from using ngrx.
This seems like too big of an oversight to not already have a solution, so I’m assuming it’s just me not thinking of something. Any thoughts?
I do something similar, whereby I have forms populated from data coming from my Ngrx Store. My forms aren't dynamic so I'm not 100% sure if this will also work for you.
Define your input with just a setter, then call patchValue(), or setValue() on your form/ form control. Your root component stays the same, passing the data into your next component with the async pipe.
#Input() set asyncPipedValue(data) {
if (data) {
this.textBoxComponent.patchValue(data);
}
}
patchValue() is on the AbstractControl class. If you don't have access to that from your question component, your TextBoxComponent could expose a similar method, that can be called from your QuestionComponent, with the implementation performing the update of the control.
One thing to watch out for though, if you're also subscribing to valueChanges on your form/control, you may want to set the second parameter so the valueChanges event doesn't fire immediately.
this.textBoxComponent.patchValue(data, { emitEvent: false });
or
this.textBoxComponent.setValue(...same as above);
Then in your TextBoxComponent
this.myTextBox.valueChanges
.debounceTime(a couple of seconds maybe)
.distinctUntilChanged()
.map(changes => {
this.store.dispatch(changes);
})
.subscribe();
This approach is working pretty well, and removes the need to have save/update buttons everywhere.
I believe I have figured out a solution (with some help from the gitter.com/angular channel).
Since the values are coming in to the questionComponent can change, and trigger it's ngOnChanges to fire, whenever there is an event in ngOnChanges, it needs to parse through the event, and bind and changes to the dynamic child component.
ngOnChanges(event) {
if (this.component) {
_.forEach(event, (value, key) => {
if (value && value.currentValue) {
this.component.instance[key] = value.currentValue;
}
});
}
}
This is all in questionComponent, it resets the components instance variables if they have changed. The biggest problem with this so far, is that the child's ngOnChanges doesn't fire, so this isn't a full solution. I'll continue to dig into it.
Here are my thoughts on the question, taking into account limited code snippet.
First, provided example doesn't seem to have anything to do with ngrx. In this case, it is expected that ngOnInit runs only once and at that time this.asyncPipedValue value is undefined. Consequently, if changeDetection of this.checkboxComponent is ChangeDetection.OnPush the value won't get updated. I recommend reading one excellent article about change detection and passing async inputs. That article also contains other not less great resources on change detection. In addition, it seems that the same inputs are passed twice through the component tree which is not a good solution from my point of view.
Second, another approach would be to use ngrx and then you don't need to pass any async inputs at all. Especially, this way is good if two components do not have the parent-child relationship in the component tree. In this case, one component dispatches action to put data to Store and another component subscribes to that data from Store.
export class DataDispatcherCmp {
constructor(private store: Store<ApplicationState>) {
}
onNewData(data: SomeData) {
this.store.dispatch(new SetNewDataAction(data));
}
}
export class DataConsumerCmp implements OnInit {
newData$: Observable<SomeData>;
constructor(private store: Store<ApplicationState>) {
}
ngOnInit() {
this.newData$ = this.store.select('someData');
}
}
Hope this helps or gives some clues at least.

user_work_history with Flex and the ActionScript SDK

I'm working on a sample app for Facebook, using Flash Builder and Flex.
Now, I've got everything up and running - but there's one problem, specifically with the work history part.
When I try to display the user's work history..here's the code for logging in:
protected function login():void
{
FacebookDesktop.login(loginHandler, ["user_birthday", "user_work_history"]);
}
Here, loginHandler's a callback function, that then goes ahead and displays data about the user:
protected function loginHandler(success:Object,fail:Object):void
{
if (success){
currentState = "LoggedIn";
fname.text = success.user.name;
userImg.source=FacebookDesktop.getImageUrl(success.uid,"small");
birthdayLbl.text=success.user.birthday;
workLbl.text=success.user.work;
}
}
Now, the problem occurs with success.user.work - it ends up printing the following:
[object,Object],[object,Object],[object,Object],[object,Object]
Obviously, I'm doing something wrong..but I can't figure out what exactly it is. Would be grateful for some pointers!
Thanks!
Rudi.
The object contained in success.user.work is most likely an array of objects, each item representing a work period, so you'll have to treat it as such. Either use a list and a custom renderer for each item, or create a string by iterating over the array, and appending the fields that you're interested in.
To see what the individual objects contain, either use a breakpoint during debug and inspect them, or check to see if they're documented in the facebook development documentation.