wit.ai runActions how to handle context in follow-up message - node.js

I'm using node-wit to develop a chatbot application.
This is working fine mostly, but I've run into a problem with the use of the context.
I'm using the runActions api :
this.witClient.runActions(customer._key, messageText, witContext).then((newContext => {}
)).catch(reject);
I have defined a number of actions, which set the context.
This is working fine, as long the context is taking place over one message.
For example, if I were to call an action called addProduct :
addProduct({sessionId, context, text, entities}) {
return new Promise((resolve, reject) => {
context.product = `myNewProduct';
resolve(context);
});
},
It will then show a message using the 'product' context key.
However, when I try to use it over 2 messages, it seems to have lost the context ( for example, when asking a multiple choice question, and then handling that response ).
If I understand how it's working correctly, then node-wit doesn't keep the context beyond messages ( I assumed this at first because I'm passing a session key ).
A solution I see is to store the resulting context ( newContext in this case) in a session/user specific way, and then restore it and pass it again when the user is sending his new message.
Meaning, something like this :
witContext = getContextFromSession();
this.witClient.runActions(customer._key, messageText, witContext).then((newContext => { setContextInSession(newContext) }
)).catch(reject);
Would this be the correct way of handling it ?

Off course you have to store your context state, you decide how to store it. But, take into account what is the most efficient way if you're gonna have a lot of users, and your reasources available.
As you can see in the official example for nodeJs, there's a method named findOrCreateSession on https://github.com/wit-ai/node-wit/blob/master/examples/messenger.js they get the session before the wit actions are called.
In my particular case, I am storing it in the database, so I get the session before the action is called, so I can send the context, then in the actions I query the session again to modify the resulting context and store it again, try the best implementation for your needs.

Related

How to add a custom dimension to request telemetry in a Nodejs/typescript azure function?

Goal
A request comes in and is handled by the Azure Functions run-time. By default it creates a Request entry, and a bunch of Trace entries in Application Insights. I want to add a custom dimension to that top level request item (on a per-request basis) so I can use it for filtering/analysis later.
Query for -requests- on Application Insights
Resulting list of requests including custom dimensions column
The Azure Functions runtime adds a few custom dimensions already. I want to add a few of my own.
Approach
The most promising approach I've found is show below (taken from here https://github.com/microsoft/ApplicationInsights-node.js/issues/392)
appInsights.defaultClient.addTelemetryProcessor(( envelope, context ) => {
var data = envelope.data.baseData;
data.properties['mykey'] = 'myvalue';
return true;
});
However, I find that this processor is only called for requests that I initialise within my function. For example, if I make an HTTP request to another service, then details of that request will be passed thru the processor and I can add custom properties to it. But the main function does not seem to pass thru here. So I can't add my custom property.
I also tried this
defaultClient.commonProperties['anotherCustomProp'] = 'bespokeProp2'
Same problem. The custom property doesn't arrive in application insights. I've played with many variations on this and it appears that the logging done by azure-functions is walled off from anything I can do within my code.
The best workaround I have right now, is to call trackRequest manually. This is okay, except I end up with each request logged twice in application insights, one by the framework and one by me. And both need to have the same operation_id otherwise I can't find the associated trace/error items. So I'm having to extract the operationId in a slightly hacky way. This may be fine, my knowledge of application insights is pretty naive at this point.
import { setup, defaultClient } from 'applicationinsights' // i have to import the specific functions, because "import ai from applicationinsights" returns null
// call this because otherwise defaultClient is null.
// Some examples call start(), I've tried with and without this.
// I think the start() function must be useful when you're adding application-insights to a project fresh, whereas I think the azure-functions run-time must be doing this already.
setup()
const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise<void> {
// Extract the operation id from the traceparent as per w3 standard https://www.w3.org/TR/trace-context/.
const operationId = context.traceContext.traceparent.split('-')[1]
var operationIdOverride = { 'ai.operation.id': operationId }
// Create my own trackRequest entry
defaultClient.trackRequest({
name: 'my func name',
url: context.req.url.split('?')[0],
duration: 123,
resultCode: 200,
success: true,
tagOverrides: operationIdOverride,
properties: {
customProp: 'bespokeProp'
}
})
The Dream
Our C# cousins seem to have an array of options, like Activity.Current.tags and the ability to add TelemetryInitializer. However it looks like what I'm trying to do is supported, I'm just not finding the right combination of commands! Is there something similar for javascript/typescript/nodejs, where I can just add a tag on a per-request basis? Along the lines of context.traceContext.attributes['myprop'] = 'myValue'
Alternative
Alternatively, instrumenting my code using my own TelemetryClient (rather than the defaultClient) using trackRequest, trackTrace, trackError etc, is not a very big job and should work well - that would be more explicit. Should I just do that? Is there a way to disable the azure functions tracking - or perhaps I just leave that as something running side-by-side.

Context Management in Bot Framework - Node JS

in the main dialog, I have used this to get the details of the user activity and session ID stepContext.context._activity
and I am able to store the data in JSON format,
I want to know how to use the previous text of the user for the next query if there is no entity present in that and it should work for multiple users, not for a single user.
The location of the user's input varies depending on the option provided to them. Simple text input is available in the stepContext.context.activity.text field. However, the user's returned value could, for example, exist in:
stepContext.result, if a hero card is used
stepContext.options, if a value is forwarded on from a previous step or action
stepContext.context.activity.text, if a suggested action is used
stepContext.context.activity.value, if a postBack is returned
There are a few other possibilities. Referencing the docs is a good place to start as are the BotBuilder-Samples for getting a grasp on all the possibilities. Don't be averse to inspecting the stepContext to see how it is populated and with what.
async firstStep(stepContext) {
await stepContext.context.sendActivity('Type something...');
return { status: DialogTurnStatus.waiting };
}
async secondStep(stepContext) {
const result = stepContext.context.activity.text;
return await stepContext.context.sendActivity(`You said: ${ result }`);
Hope of help!

Firestore: get document back after adding it / updating it without additional network calls

Is it possible to get document back after adding it / updating it without additional network calls with Firestore, similar to MongoDB?
I find it stupid to first make a call to add / update a document and then make an additional call to get it.
As you have probably seen in the documentation of the Node.js (and Javascript) SDKs, this is not possible, neither with the methods of a DocumentReference nor with the one of a CollectionReference.
More precisely, the set() and update() methods of a DocumentReference both return a Promise containing void, while the CollectionReference's add() method returns a Promise containing a DocumentReference.
Side Note (in line with answer from darrinm below): It is interesting to note that with the Firestore REST API, when you create a document, you get back (i.e. through the API endpoint response) a Document object.
When you add a document to Cloud Firestore, the server can affect the data that is stored. A few ways this may happen:
If your data contains a marker for a server-side timestamp, the server will expand that marker into the actual timestamp.
Your data data is not permitted according to your server-side security rules, the server will reject the write operation.
Since the server affects the contents of the Document, the client can't simply return the data that it already has as the new document. If you just want to show the data that you sent to the server in your client, you can of course do so by simply reusing the object you passed into setData(...)/addDocument(data: ...).
This appears to be an arbitrary limitation of the the Firestore Javascript API. The Firestore REST API returns the updated document on the same call.
https://firebase.google.com/docs/firestore/reference/rest/v1beta1/projects.databases.documents/patch
I did this to get the ID of a new Document created, and then use it in something else.
Future<DocumentReference<Object>> addNewData() async {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final CollectionReference _userCollection = _firestore.collection('users');
return await _userCollection
.add({ 'data': 'value' })
.whenComplete(() => {
// Show good notification
})
.catchError((e) {
// Show Bad notification
});
}
And here I obtain the ID:
await addNewData()
.then((document) async {
// Get ID
print('ID Document Created ${document.id}');
});
I hope it helps.

Loopback context returning null in afterRemote method

I am newbie to loopback, I have written a function for signup and
executing on beforeRemote. Here I can able to access loopback context. But after user created I am executing another function to send verification link using afterRemote, here loopback context returning null value.
function signup(){
const lbCtx = LoopBackContext.getCurrentContext;
const details = lbctx().get('details');
//here it's working
}
function sendVerificationEmail(){
const lbCtx = LoopBackContext.getCurrentContext;
const details = lbctx().get('details');
//here not working
}
Users.beforeRemote('create', (ctx) => signup(ctx));
Users.afterRemote('create', (ctx) => sendVerificationEmail(ctx));
can anyone help me to achieve this.
There is a documented issue with the way the current context is handled in LoopBack.
Unfortunately it seems that the library they used to support this feature called continuation-local-storage is not entirely reliable and sometimes the context is not properly passed around so you get a null value instead.
There's a warning in the docs stating this and links to different workarounds, since there's no unified solution yet.
Note: One issue I see with your code (although it might be a typo) is that in the signup() method you are doing get() instead of set(). Just in case you didn't notice.

How to think asynchronously with nodejs?

I just started developing nodejs. I'm confused to use async model. I believe there is a way to turn most of SYNC use cases into ASYNC way. Example, by SYNC, we load some data and wait until it returns then show them to user; by ASYNC, we load data and return, just tell the user data will be presented later. I can understand why ASYNC is used in this scenario.
But here I have a use case. I'm building an web app, allowing user to place a order (buying something). Before saving the order data into db, I want to put some user data together with order data (I'm using document NoSql db by the way). So I think by SYNC, after I get order data, I make a SYNC call to database and wait for its returned user data. After I get returned data, integrate them together and ingest into db.
I think there might be an issue if I make ASYNC call to db to query user data because user data may be returned after I save data to db. And that's not what I want.
So in this case, how can I do this thing ASYNCHRONOUSLY?
Couple of things here. First, if your application already has the user data (the user is already logged in), then this information should be stored in session so you don't have to access the DB. If you are allowing the user to register at the time of purchase, you would simply want to pass a callback function that handles saving the order into your call that saves the user data. Without knowing specifically what your code looks like, something like this is what you would be looking for.
function saveOrder(userData, orderData, callback) {
// save the user data to the DB
db.save(userData, function(rec) {
// if you need to add the user ID or something to the order...
orderData.userId = rec.id; // this would be dependent on your DB of choice
// save the order data to the DB
db.save(orderData, callback);
});
}
Sync code goes something like this. step by step - one after other. There can be ifs and loops (for) etc. all of us get it.
fetchUserDataFromDB();
integrateOrderDataAndUserData();
updateOrderData();
Think of async programming with nodejs as event driven. Like UI programming - code (function) is executed when an event occurs. E.g. On click event - framework calls back registered clickHandler.
nodejs async programming can also be thought on these lines. When db query (async) execution completes, your callback is called. When order data is updated, your callback is called. The above code goes something like this:
function nodejsOrderHandler(req,res)
{
var orderData;
db.queryAsync(..., onqueryasync);
function onqueryasync(userdata)
{
// integrate user data with order data
db.update(updateParams, onorderudpate);
}
function onorderupdate(e, r)
{
// handler error
write response.
}
}
javascript closure provides the way to keep state in variables across functions.
There is certainly much more to async programming and there are helper modules that help with basic constructs like chain, parallel, join etc as you write more involved async code. but this probably gives you a quick idea.

Resources