Passing user provided value from one intent to other intent in Dialogflow - dialogflow-es

Team,
In DialogFlow, I getting a value from the user in intent A; (let's say it's the employee ID).
In next Intent B, I want to use the Employee ID (collected in previous intent A) and provide a response and execute a webhook.
I am able to collect the value in Intent A and display in same intent. When tried to pass it to another Intent, I am failing miserably.
Appreciate ayny help in this regard.
Tnx
Sathiya

You will need to use contexts to store the parameters and access these parameters through the context in the other intent.
Check out my full answer here
{
"fulfillmentText":"This is a text response",
"fulfillmentMessages":[ ],
"source":"example.com",
"payload":{
"google":{ },
"facebook":{ },
"slack":{ }
},
"outputContexts":[
{
"name":"<Context Name>",
"lifespanCount":5,
"parameters":{
"<param name>":"<param value>"
}
}
],
"followupEventInput":{ }
}
from NodeJS code
save in first Intent
let param1 = [];
let param2 = {};
let ctx = {'name': '<context name>', 'lifespan': 5, 'parameters': {'param1':param1, 'param2': param2}};
agent.setContext(ctx);
Access in other Intent as
let params = agent.getContext("<context name>").parameters;
let param1 = params.param1;
let param2 = params.param2;

Related

Move data in Waterfall-Dialog. Bot Framework SDK

I'm using Bot Framework SDK with nodejs to implement a disamibuation flow.
I want that if two intents predicted by Luis are close to each other, ask the user from which of them are the one they want. I have done the validator but, I have a problem with the flow.
It is a waterfall Dialog with 3 steps:
FirstStep: Calls Orchestrator and Luis to get intents and entities. It pass the data with return await step.next({...})
Disamiguation Step: Checks if it is necessary to disambiguate, and, in that case, prompts the options. If not, it pass the data like the first step.
Answer step: If it has a disambiguation flag in the data it receives in step.result, it prompts the answer acordingly with the user response. Elsewhere, it uses the data in step.result that comes from the first step.
The problem is that, when it prompts user to say the intent, I lost the data of the FirstStep since I cannot use step.next({...})
¿How can I maintain both the data from the first step and the user answer in the prompt?
Here are the basic code:
async firstStep(step) {
logger.info(`FinalAnswer Dialog: firstStep`);
let model_dispatch = await this.bot.get_intent_dispatch(step.context);
let result = await this.bot.dispatchToTopIntentAsync(step.context, model_dispatch.model)
// model_dispatch = orchestrator_model
// result = {topIntent: String, entities: Array, disamibiguation: Array}
return await step.next({ model_dispatch: model_dispatch, result: result})
}
async disambiguationStep(step) {
logger.info(`FinalAnswer Dialog: disambiguationStep`);
if (step.result.result.disambiguation) {
logger.info("We need to disambiguate")
let disambiguation_options = step.result.result.disambiguation
const message_text = "What do you need";
const data = [
{
"title": "TEXT",
"value": disambiguation_option[0]
},
{
"title": "TEXT",
"value": disambiguation_option[1]
},
]
let buttons = data.map(function (d) {
return {
type: ActionTypes.PostBack,
title: d.title,
value: d.value
}
});
const msg = MessageFactory.suggestedActions(buttons, message_text);
return await step.prompt(TEXT_PROMPT, { prompt: msg });
return step.next(step.result) //not working
}
else {
logger.info("We dont desambiguate")
return step.next(step.result)
}
}
async answerStep(step) {
logger.info(`FinalAnswer Dialog: answerStep`);
let model_dispatch = step.result.model_dispatch
let result = step.result.result
//Show answer
return await step.endDialog();
}
You can use the step dictionary to store your values. The complex dialogs sample on GitHub is excellent for demonstrating this. https://github.com/microsoft/BotBuilder-Samples/blob/main/samples/javascript_nodejs/43.complex-dialog/dialogs/topLevelDialog.js
You can save data in the context with whatever name you want:
step.values['nameProperty'] = {}
This will be accessible within the entire execution context of the waterfall dialog:
const data = step.values['nameProperty'] // {}

microsoft bots to teams using nodejs fails with missing activityid when updating the same activity

My code has a simple card carousel which has action button like below:
actions = [
{
"type": "Action.Submit",
"title": "Qualify",
"data": { "action" : "qualify_lead" }
},
{
"type": "Action.OpenUrl",
"title": "Retire",
"url": "{viewUrl}"
},
{
"type": "Action.ShowCard",
"title": "Add Note",
"card": this.noteCard(item.LeadId, "leads")
}
]
I am having a method to handle qualify_lead action like below
async qualifyLead(context:any){
console.log("in qualifyLead:" + JSON.stringify(context.activity))
await context.updateActivity(this.successCard('Lead is qualified'))
}
All I am doing on purpose is to replace entire carousel with a simple text message. But it fails with error:
Error: BotFrameworkAdapter.updateActivity(): missing activity.id
Where do i get this ?
I am using google firebase for this and the wrapper code is like below
const {
TurnContext,
TeamsActivityHandler,
CardFactory,
AttachmentLayoutTypes,
ActionTypes
} = require('botbuilder');
class TeamsConversationBot extends TeamsActivityHandler {
constructor() {
super();
this.leadState =
this.conversationState.createProperty('leadCarouselState');
this.onMessage(async (context:any, next:any) => {
TurnContext.removeRecipientMention(context.activity);
let msg = context.activity.text
const action = context.activity.value
let objNum = ''
let keyword = ''
if(msg === undefined && action === undefined)
msg = 'help'
else if(msg !== undefined){
msg = msg.trim().toLowerCase()
if(msg.indexOf("help") > -1)
msg = 'help'
else{
if(msg.startsWith('lead')){
msg = 'lead'
}
}
}
switch (msg) {
case 'lead':
await this.lead(context, userKey, platform)
break;
case 'qualify_lead':
await this.qualifyLead(context)
break;
}
await next();
});
}
I'm not sure exactly what this.successCard('Lead is qualified') does, but presumably it returns an Activity. To my knowledge, in order for this Activity to replace another one, you need to set it's Id property to match the previous message. That means that, when you send the previous message (i.e. the card), you need to capture the reference that's returned from the send method on the context (e.g. into bot state), and then use it for this new activity.
As I explained in my answer to your other question, you need to save the activity ID in bot state and then apply it to the update that you're sending. The Bot Framework can't update an activity unless you tell it which activity to update, and you do that using an activity ID.
This was the part that saves the ID:
const dict = await this.carouselState.get(turnContext, {});
dict[batchId] = {
[KEYACTIVITYID]: response.id,
[KEYLEADS]: leads
};
And this was the part that applies it to the updated activity:
const update = this.createCarousel(batchId, leads);
update.id = info[KEYACTIVITYID];

dialogflow with node.js how do you swtich intents

I have an intent that has a required parameter and a fulfilment. When you answer it takes you to my node.js application which currently looks like this:
app.intent(QUESTIONS_INTENT, (conv, params) => {
let data = conv.data;
let categoryId = data.categoryId;
let formulas = data.formulas;
let options = {
'method': 'PUT',
'url': apiUrl + 'questions/filter',
'body': {
categoryId: categoryId,
organisationId: organisation,
formulas: formulas
},
'json': true
};
return request(options).then(response => {
// We need to change how we get these
var questionText = params.questions;
var questions = response.filter(item => item.text === questionText);
data.questions = questions;
conv.ask(questions[0].text);
conv.contexts.set('colour');
}, error => {
conv.ask(JSON.stringify(error));
});
})
Currently it gets the questionText and finds the question that matches what you said. What I want it to do is to swap to a new intent. I have tried by conv.contexts.set('colour') but that doesn't appear to work.
I have an intent setup with input context as "Colour" so I would expect that when my fulfilment completes, it should swap to that intent, but it doesn't.
Can someone help me with this?
You need to make sure that you have a colour intent setup in DialogFlow that will handle the input from the user and respond to them. The question should be asked after you set the context.
return request(options).then(response => {
// We need to change how we get these
var questionText = params.questions;
var questions = response.filter(item => item.text === questionText);
data.questions = questions;
// Sets the context to colour.
conv.contexts.set('colour', 1);
// Now that the context is in control you'll be able to ask a question.
conv.ask(questions[0].text);
You'll also want to provide a lifespan after the context arg. This will make the context active for that many prompts, in the example above I set it for one so your question context will only be active for that specific response.
conv.contexts.set('colour', 1);

Google Cloud Talent Solution fetch a job by requisitionId

I am wondering if it is possible to fetch a job by requisitionId in Google Cloud Talent Solution. requisitionId has to be unique across jobs so it seems like a natural candidate for looking a job up.
When a job is created the api returns a job name that can be used to look the job up:
You can retrieve the details of a previously inserted job by sending a GET request to the Cloud Talent Solution. The URI should include the previously inserted job name returned by the original create request, as a URL parameter.
I'd like to avoid storing these names if possible. In my view storing them adds unnecessary complexity since I already have a unique requisitionId. To be clear the API does not let you add jobs with a duplicate requisitionId:
Job projects/{my_app_id}/jobs/{google_assigned_id} already exists. Request ID for tracking: ... Related Job requisition ID: ...
So can I look up jobs by requisitionId?
I could parse the error message that's returned to get the job name..but that seems pretty brittle.
It turns out the list method takes requisitionId so for a full, read-create-update cycle we can do:
const listRequest = {
parent: `projects/${projectId}`,
'filter': `companyName="${companyName}" AND requisitionId="${requisitionId}"`
}
const listResult = await jobService.projects.jobs.list(listRequest)
const existingJobs = listResult.data.jobs || [];
let existingJob = null
if (existingJobs && existingJobs.length > 0) {
existingJob = existingJobs[0]
}
let googleJob
if (!existingJob) {
const createRequest = {
'parent': `projects/${projectId}`,
'resource': {
'job': {
companyName,
requisitionId,
title,
description,
applicationInfo: {
emails: ['email#example.com']
}
}
}
}
googleJob = await jobService.projects.jobs.create(createRequest)
.then(result => result)
.catch(resp => {
console.error("ERROR")
console.error(resp)
})
} else {
const patchRequest = {
'name': existingJob.name,
'resource': {
'job': {
companyName,
requisitionId,
title,
description,
applicationInfo: {
emails: ['email#example.com']
}
}
}
}
googleJob = await jobService.projects.jobs.patch(patchRequest)
.then(result => result)
.catch(resp => {
console.error("ERROR")
console.error(resp)
})
}
Docs: https://cloud.google.com/talent-solution/job-search/docs/reference/rest/v3/projects.jobs/list?authuser=0&hl=de
Notes:
The double quotes in the filter parameter are important. It will not accept single quotes and will give a cryptic error message.
The patch request cannot take a parent parameter even though everything else requires a parent parameter...
one can add it as custom attribute:
Map<String, CustomAttribute> attributes = new HashMap<>();
attributes
.put("requisitionId", new CustomAttribute().setStringValue(requisitionId)
.setFilterable(true));
Job job = new Job()
...
.setCustomAttributes(attributes);
Job jobCreated = createJob(job);
String jobName = jobCreated.getName();
and then search for requisitionId with a custom attribute filter:
JobQuery jobQuery = new JobQuery().setCustomAttributeFilter(filter);
this is a little redundant, but JobQuery has no method .setRequisitionId().
here's the documentation.

How to pass params from one intent to another dialogflow

how does one pass the params from a intent in the fulfillment editor to another followup intent.
Here is my code
// original intent
// the params defined here are the ones I want to pass to the app.intent below.
app.intent('Book Appointment', (conv, params) => {
// the params that i want to access in the next intent
const date = params['date'];
const time = params['time'];
const firstName = params['given-name'];
const email = params['email'];
// what i dont get is what to put in here is it
// how am i formatting this?
const parameters = {'date':params.date,}
// is the 'welcome-context' replaced with this one from the JSON? like so?
conv.contexts.set('projects/myproject-120c2/agent/sessions/c2c1c9e0-2ccf-0cf0-d7ce-e52561d44de3/contexts/bookappointment-followup"', 5, parameters);
// or is it
conv.contexts.set('bookappointment-followup', 5, parameters); // ??
// like it is in Dialogflow
})
// follow-up intent
app.intent('Book Appointment - yes', (conv, params) => {
// this is where I want to access the ones above
// then im accessing them like this?
const conv.contexts.get('bookappointment-followup').parameters['whatever it is above'];
})
The variables in the email return undefined. I reckon it's something to do with the contexts but I dont know if its the same code like #book.appointment.follow.date etc.
Help is appreciated, cheers
You may check my answer here
Use the output context to save parameters
{
"fulfillmentText":"This is a text response",
"fulfillmentMessages":[ ],
"source":"example.com",
"payload":{
"google":{ },
"facebook":{ },
"slack":{ }
},
"outputContexts":[
{
"name":"<Context Name>",
"lifespanCount":5,
"parameters":{
"<param name>":"<param value>"
}
}
],
"followupEventInput":{ }
}
If you are using NodeJS client
You can save context with parameters like
let param1 = [];
let param2 = {};
let ctx = {'name': '<context name>', 'lifespan': 5, 'parameters': {'param1':param1, 'param2': param2}};
agent.setContext(ctx);
and get it like
let params = agent.getContext("<context name>").parameters;
let param1 = params.param1;
let param2 = params.param2;
If using conv, you may also try like this:
app.intent('<INTENT>', conv => {
conv.ask('<RESPONSE>');
const parameters = {'param1':param1, 'param2': param2}};
conv.contexts.set('welcome-context', 5, parameters);
});
and access it like here :
const conv.contexts.get(<context>).parameters[<param>];
If via context doesn't work, you may try with data like
conv.data.someProperty = 'someValue'
as stated here
Thanks this worked.
const firstName = conv.contexts.get('bookappointment-followup').parameters['given-name'];
:)

Resources