say for example there is a non-required slot present in intent and value I entered on Lex is A and then I need to change the value of slot to B.
It is possible to achieve this through the use of your custom Lambda function.
My suggestion would be to use the sessionAttributes to store the modified slot values. Use your Lambda function to change the slot's raw value and then save this to the sessionAttributes.
Ensure that you set these sessionAttributes correctly on the response from the Lambda function.
Please refer to the developer guide for the full schemas of the request and response formats.
Lambda Function Input Event and Response Format
If you want to edit the raw value, you can do so as well. The below is a snippet from a Python Lambda function that I have supporting a V1 Lex bot:
if first_name is not None:
originalValue = event['currentIntent']['slotDetails']['firstName']['originalValue']
originalValue = originalValue.replace("-"," ")
slot_values["firstName"] = originalValue
I then include the modifed slot_values in the response to Lex:
'dialogAction': {
'type': 'ElicitSlot',
'intentName': ...,
'slots': slot_values,
'slotToElicit': ...,
'message': ...
}
You can modify the response to perform your desired dialog action by setting the correct value for type.
Related
I have an AWS Lambda function that is meant to receive a json, pass it to a machine learning model and return a prediction.
The tests are working well, so now I want to configure a Function URL. My goal is to be able to post a request to the function's URL from my local computer and have it respond with a prediction.
For the purpose of this question, suppose the function looks as follows:
# Silly function
def lambda_handler(event, context):
# Extract values from request
x = list(event.values())
# Return
return {
'status': '200',
'inputs': x
}
Hence, the test event {"feature1": 12, "feature2": 34} responds with the expected result:
{
"status": "200",
"inputs": [
12,
34
]
}
I just configured a URL through:
Configuration > Create Function URL > Auth type = None > Save.
How can I post requests to this URL to get the same response as the test event?
In other words, can I send the same test event via Insomnia (or Postman) to the URL and get the same response as the test event?
After reading the docs, I realized that the information I wanted to extract was contained in the "body" element nested inside the event received by the function.
I had to adapt my function to extract the "body" from event.
# Function to load jsons passed as strings
from json import loads
# Silly function
def lambda_handler(event, context):
# Extract body from event
body = event['body']
# Extract values from body
x = list(body.values())
# Return
return {
'status': '200',
'inputs': x
}
I had to do this because event looks like this in the background.
{
"headers":{<headers here>},
"isBase64Encoded":false,
"rawPath":"\/",
"routeKey":"$default",
"requestContext":{<context here>},
"body":"{\"feature1\": 12,\n\t\"feature2\": 34}",
"version":"2.0",
"rawQueryString":""
}
My mistake came from thinking that {"feature1": 12, "feature2": 34} was contained directly inside event, when in reality, it was nested inside "body".
Im trying to create a simple API gateway in which, with a POST method to a certain endpoint, a lambda function is executed.
Setting that up was easy enough, but I'm having some trouble with the request/response handling. Im sending the following request to the API Gateway (Im using python 3.7).
payload = {
"data": "something",
"data2": "sometsadas"
}
response = requests.post('https://endpoint.com/test', params = payload)
That endpoint activates a lambda function when accesed. That function just returns the same event it received.
import json
def lambda_handler(event, context):
# TODO implement
return event
How can I make it so the return value of my lambda function is actually the response from the request? (Or at least a way in which the return value can be found somewhere inside the response)
Seems it was a problem with how the information is sent, json format is required. Solved it by doing the following in the code.
payload{'data': 'someData'}
config_response = requests.post(endpointURL, data = json.dumps(config_payload))
I've successfully created a lambda function using Go for the pre sign-up trigger of AWS Cognito.
My problem is that I'm not being able to deny/reject the user if a custom field is not valid (based on custom logic).
I'm returning an error as described in AWS Cognito trigger's guide:
return event, fmt.Errorf("Invalid value for field 'custom:myField'")
also I've tried this options:
returning an empty event (nil is not allowed for the event):
var emptyEvent events.CognitoEventUserPoolsPreSignup
return emptyEvent, fmt.Errorf("Invalid value for field 'custom:myField'")
changing ValidationData in the original event:
event.Request.ValidationData = map[string]string{"custom:myField": "Invalid value for field 'custom:myField."}
return event, fmt.Errorf("Invalid value for field 'custom:myField'")
changing UserAttributes in the original event
event.Request.UserAttributes["email"] = ""
return event, fmt.Errorf("Invalid value for field 'custom:myField'")
All those methods are failing, the user is always created in the User Pool.
What should be the correct way to reject the sign-up request using GoLang lambda function?
Looks like something changed on AWS Cognito Lambda triggers, since today I tried the following source code and it worked as expected:
func handler(event events.CognitoEventUserPoolsPreSignup) (events.CognitoEventUserPoolsPreSignup, error) {
fmt.Println(event)
return event, fmt.Errorf("TESTING LAMBDA ERRORS WITH GOLANG")
}
Also, the previous source code that was not working as expected when I posted this question is currently working (with no changes on my side).
I am trying to create a lex bot that lets users learn about different options. For example, it can tell a user about the three different products available. I can't seem to find documentation about how to do this without using a lambda function, and I can't figure out how to pass the user input from the bot itself into the lambda function in order to use a simple "if/then" and then return the appropriate message. It seems excessive to have to use an lambda function to just give a response based on input, but I am stuck. Thanks.
To shed a little more light on how a Lex bot works, the service enables you to define utterances ("if" conditions), that it will intelligently try to perform fuzzy-matching to determine whether a user has said something that fits one of the utterances you've defined. You can declare and modify these utterances within the AWS console, examples of this are available here.
The "then" part of this conditional application is where you get to define what happens after the user input matches a defined utterance (state), where some basic computation is handled, most easily in the form of a Lambda function.
For something simple like returning static text/info once a condition is met:
Return from your lambda_handler function a string with the proper result. A barebones pseudocodey Python implementation of this would look like the following:
# Here's an implementation where you have an intent of a user asking
# for more info about a particular object (info_type as defined in Lex console).
def lambda_handler(event, context):
info_name = event['info_type']
if info_name = 'business_hours':
return "Our business hours are 9am-5pm"
elif info_name = 'slogan':
return "We're customer obsessed"
else:
return "Sorry, we don't have info about this!"
Depending on how you want to set up your application, you can decide how you want to split up the logic between different utterances and if/then cases with prop data passed in. If you have more complex queries, question types, and computation involved, these will all determine what the optimal way to architect your Lex chat bot will be.
A Lex bot by itself is really only good enough for simple FAQ conversations where certain inputs have pre-defined responses. But you cannot set the responses based on the slot values Lex captures. You could have a very limited dynamic response where those slot values are simply placed inside a response (imagine a Mad Libs game), but that's about it.
As soon as you want to create a truly dynamic response based on the user input, then you will need to use a Lambda function to compare the Lex request and build an appropriate response based on the user input or slot values.
Documentation
Amazon Lex - Using Lambda Functions
Create a Lambda Function (example Order Flowers)
Set the Lambda Function as the Lex Intent's Code Hook
Once you have your Lambda function set up and Lex ready to pass the processed user input through as a Lex Request (also referred to as "Event"), then you will have to pay close attention to this document:
Lex Request and Response Format
The incoming request has a consistent format for delivering the currentIntent, the sessionAttributes, the slots, the inputTranscript (the full user input), and more. It might look like a lot to take in but once you parse it into its main components, then it's really quite easy to work with in order to build dynamic responses. Just make sure you are following the Lex Request and Response format precisely.
Example
Here is an example for the start of your Lambda Function (Node.js):
// The JSON body of the request is provided in 'event'.
// 'respondToLex' is the callback function used to return the Lex formatted JSON response
exports.handler = (event, context, respondToLex) => {
console.log( "REQUEST= "+JSON.stringify(event) ); //view logs in CloudWatch
// INCOMING VARIABLES FROM REQUEST EVENT
// -------------------------------------
var intentName = event.currentIntent.name;
var slots = event.currentIntent.slots
var sessionAttributes = event.sessionAttributes
var userInput = event.inputTranscript
// OUTGOING VARIABLES FOR RESPONSE
// -------------------------------
var responseMsg = "";
var responseType = "";
var slotToElicit = "";
var error = null;
var fulfillmentState = null;
// DETERMINE RESPONSE BASED ON INTENTS AND SLOT VALUES
// ---------------------------------------------------
if (intentName == "intentA") {
responseType = "Close";
responseMsg = "I am responding to intentA";
fulfillmentState = "fulfilled';
}
elseif (intentName == "intentB") {
if (slots["productChoice"] == null) {
responseMsg = "I can tell that productChoice slot is empty, so I should elicit for it here. Which product would you like? Hat or Shoes?";
responseType = "ElicitSlot";
slotToElicit = "productChoice";
}
else {
if (slots["productChoice"]=="hat") {
responseMsg = "I can tell you selected a hat, here is my dynamic response based on that slot selection.";
}
elseif (slots["productChoice"]=="shoes") {
responseMsg = "I can tell you selected shoes, here is my dynamic response based on that slot selection.";
}
}
}
else {
error = "Throw Error: Unknown Intent";
}
// CREATE RESPONSE BUILDER for each responseType (could turn into functions)
// -------------------------------------------------------------------------
if (responseType=="Close") {
var response = [
"sessionAttributes" = sessionAttributes,
"dialogAction" = [
"type" = responseType,
"fulfillmentState" = fulfillmentState,
"message" = [
"contentType" = "PlainText";
"content" = responseMsg,
]
]
];
}
elseif (responseType == "ElicitSlot) {
var response = [
"sessionAttributes" = sessionAttributes,
"dialogAction" = [
"type" = responseType,
"intentName" = intentName,
"slots" = slots
"slotToElicit" = slotToElicit,
"message" = [
"contentType" = "PlainText";
"content" = responseMsg,
]
]
];
}
responseJSON = JSON.stringify(response);
console.log("RESPONSE= "+ responseJSON); // view logs in CloudWatch
respondToLex(error, responseJSON);
}
I have a simple lambda function that returns a dict response and another lambda function invokes that function and prints the response.
lambda function A
def handler(event,context):
params = event['list']
return {"params" : params + ["abc"]}
lambda function B invoking A
a=[1,2,3]
x = {"list" : a}
invoke_response = lambda_client.invoke(FunctionName="monitor-workspaces-status",
InvocationType='Event',
Payload=json.dumps(x))
print (invoke_response)
invoke_response
{u'Payload': <botocore.response.StreamingBody object at 0x7f47c58a1e90>, 'ResponseMetadata': {'HTTPStatusCode': 202, 'RequestId': '9a6a6820-0841-11e6-ba22-ad11a929daea'}, u'StatusCode': 202}
Why is the response status 202? Also, how can I get the response data from invoke_response? I could not find a clear documentation of how to do it.
A 202 response means Accepted. It is a successful response but is telling you that the action you have requested has been initiated but has not yet completed. The reason you are getting a 202 is because you invoked the Lambda function asynchronously. Your InvocationType parameter is set to Event. If you want to make a synchronous call, change this to RequestResponse.
Once you do that, you can get the returned data like this:
data = invoke_response['Payload'].read()
try: data = invoke_response['Payload'].read()
read() because it is a StreamingBody object
<botocore.response.StreamingBody object at 0x110b91c50>
It is in the boto3 docs. You can find more details about this here: https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html#actions