Skip to content

Commit

Permalink
Release 1.0.4
Browse files Browse the repository at this point in the history
  • Loading branch information
virtualgill committed Dec 13, 2023
1 parent 6d90283 commit c3051da
Show file tree
Hide file tree
Showing 11 changed files with 10,942 additions and 10 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lambdaResponseValidator/
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Change Log

### [1.0.3] - 2023-12-13
- Added in new schema checker tool for AWS Lambda response format: [utilities/lambdaResponseValidator/build/Validate.html](utilities/lambdaResponseValidator/build/Validate.html)

### [1.0.3] - 2023-11-25
- Update for new schema element "interpretationSource"
- Security updates
Expand Down
15 changes: 8 additions & 7 deletions src/sharedLibraries/DialogHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import {
LambdaCodeHookSessionState,
Attributes,
LambdaCodeHookSessionStateIntent,
CodeHookIntentState
} from "./LexCodeHookInterfaces"
import { DialogActionType, Message, IntentState } from "@aws-sdk/client-lex-runtime-v2"
import { DialogActionType, Message } from "@aws-sdk/client-lex-runtime-v2"

const delegate = (sessionState: LambdaCodeHookSessionState, requestAttributes?: Attributes) => {
// Ensure session state dialog action is set to Delegate, pass everything else in as-is
// NOTE Delegate doesn't use any passed in messages and will just use the build-time configuration prompts
sessionState.dialogAction = {
type: DialogActionType.DELEGATE,
}
Expand All @@ -32,8 +34,7 @@ const fulfillIntent = (
requestAttributes: Attributes,
messages?: Message[]
) => {
//NOTE Delegate doesn't take the passed in messages and will just use the build-time configuration prompts
const fulfilledSessionState = updateSessionState(sessionState, IntentState.FULFILLED, DialogActionType.CLOSE)
const fulfilledSessionState = updateSessionState(sessionState, CodeHookIntentState.FULFILLED, DialogActionType.CLOSE)

const response: LexCodeHookResponse = {
sessionState: fulfilledSessionState,
Expand All @@ -47,7 +48,7 @@ const fulfillIntent = (
const endConversation = (event: LexCodeHookInputEvent, messages?: Message[]) => {
// This indicates a successful completion of the conversation,
// if this is not the case the state should be set to "Failed"
const sessionState = updateSessionState(event.sessionState, IntentState.FULFILLED, DialogActionType.CLOSE)
const sessionState = updateSessionState(event.sessionState, CodeHookIntentState.FULFILLED, DialogActionType.CLOSE)

const response: LexCodeHookResponse = {
sessionState: sessionState,
Expand Down Expand Up @@ -90,7 +91,7 @@ const promptForConfirmationOfIntent = (
type: DialogActionType.CONFIRM_INTENT,
}

intent.state = IntentState.IN_PROGRESS
intent.state = CodeHookIntentState.IN_PROGRESS
response.sessionState.intent = intent

return response
Expand All @@ -114,15 +115,15 @@ const promptForSlot = (
slotToElicit: slotToElicit,
}

intent.state = IntentState.IN_PROGRESS
intent.state = CodeHookIntentState.IN_PROGRESS
response.sessionState.intent = intent

return response
}

const updateSessionState = (
currentState: LambdaCodeHookSessionState,
newIntentState: IntentState,
newIntentState: CodeHookIntentState,
newDialogActionType: DialogActionType
) => {
const sessionState = currentState
Expand Down
16 changes: 13 additions & 3 deletions src/sharedLibraries/LexCodeHookInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,17 @@ export type Attributes = Record<string, string>
*/
export interface LambdaCodeHookSessionState extends SessionState {
sessionAttributes: Attributes
intent?: LambdaCodeHookSessionStateIntent
}

/**
* The Lambda function event Intent state differs from the API_runtime_Intent
* because it can optionally include the kendraResponse when the intent is KendraSearchIntent
* The Lambda function event Intent state differs from the API_runtime_Intent because
* - it can optionally include the kendraResponse when the intent is KendraSearchIntent
* - there is a smaller set of states, in progress or waiting states will not be sent to,
* and should not be returned from, the Lambda function
*/
export interface LambdaCodeHookSessionStateIntent extends Intent {
kendraResponse?: QueryResult
state?: CodeHookIntentState
}

export interface Transcription {
Expand Down Expand Up @@ -265,6 +267,14 @@ export const enum InvocationSource {
FULFILLMENT_CODE_HOOK = "FulfillmentCodeHook",
}

export const enum CodeHookIntentState {
FAILED = "Failed",
FULFILLED = "Fulfilled",
IN_PROGRESS = "InProgress",
READY_FOR_FULFILLMENT = "ReadyForFulfillment",
}


export const enum InterpretationSource {
BEDROCK= "Bedrock",
LEX = "Lex"
Expand Down
26 changes: 26 additions & 0 deletions utilities/lambdaResponseValidator/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Amazon Lex V2 Lambda function response validator

## Using the validator
You can run the validator locally by opening [./build/Validate.html](./build/Validate.html) in a browser.

You may also opt to host it somewhere, ensure that you also include [./build/validation-bundle.js](./build/validation-bundle.js) alongside it.

The validator runs locally against a schema, and does not transmit data externally.
It requires the input to be in strict JSON format.

Although it will catch most schema violations, there are some nuances that will not be caught by the validator, and you should review the Amazon Lex [documentation](https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html) for more details on the schema and specifics.

## Why use the validator

If you are getting an error similar to

`Invalid Lambda Response: Received invalid response from Lambda: Cannot construct instance of IntentResponse...` or finding your bot exits unexpectedly when calling a Code Hook then you can use the validator to find the specific problems in your response.

The easiest way to do this is to log out from your Lambda function the exact response you are sending back to Amazon Lex.
Since the validator requires JSON, logging the object out as JSON format will make things easier for you.

---


### Updating the validator
You will find the source files for the validator in the `./src` directory, and you can update these and run `npm run package` to update the build files.
196 changes: 196 additions & 0 deletions utilities/lambdaResponseValidator/build/Validate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->

<!-- Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -->
<!-- SPDX-License-Identifier: MIT-0 -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Validate Code Hook Response</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&amp;display=swap">
<style>
body {
font-family: 'Open Sans', sans-serif;
}
h1 {
color: blue;
}
p {
display: block;
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0;
margin-inline-end: 0;
}
textarea {
width: 100%;
box-sizing: border-box;
padding: 10px;
font-family: monospace;
}
.input {
margin: 20px;
}
.flex-container {
display: flex;
}
.flex-child {
flex: 1;
}
.flex-child:first-child {
margin-right: 20px;
}
.validateButton {
margin: 25px 20px 20px 10px;
}
button {
align-items: center;
appearance: none;
background-color: #3EB2FD;
background-image: linear-gradient(1deg, #4F58FD, #149BF3 99%);
background-size: calc(100% + 20px) calc(100% + 20px);
border-radius: 100px;
border-width: 0;
box-shadow: none;
box-sizing: border-box;
color: #FFFFFF;
cursor: pointer;
display: inline-flex;
font-family: CircularStd, sans-serif;
font-size: 1rem;
height: auto;
justify-content: center;
line-height: 1.5;
padding: 6px 20px;
position: relative;
text-align: center;
text-decoration: none;
transition: background-color .2s, background-position .2s;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
vertical-align: top;
white-space: nowrap;
}
button:active,
button:focus {
outline: none;
}
button:hover {
background-position: -20px -20px;
}
button:focus:not(:active) {
box-shadow: rgba(40, 170, 255, 0.25) 0 0 0 .125em;
}
.results {
border: 1px lightgrey solid;
}
.valid {
background: #1fd61f;
padding: 10px;
color: white
}
.error {
background: red;
padding: 10px;
color: white
}
.output {
padding: 10px;
}
.header {
background: #616561;
padding: 10px;
color: white;
display: block;
font-size: 1.7em;
margin-block-start: 0.67em;
margin-block-end: 0.67em;
margin-inline-start: 0;
margin-inline-end: 0;
font-weight: bold;
}
.disclaimer {
font-size: 10pt;
font-weight: 200;
margin-top: 12px
}
li {
margin: 10px;
}
</style>
</head>
<body>

<div id="header" class="header">Amazon Lex v2 Code Hook Response Validator</div>

<div class="flex-container">

<div class="flex-child">

<div class="input">
<p><label for="codeHookResponseInput">Enter the response you are sending from the AWS Lambda function attached to your code hook:</label></p>
<textarea id="codeHookResponseInput" name="codeHookResponseInput" rows="50"></textarea>
</div>

</div>

<div class="flex-child">

<div class="validateButton">
<button onClick="validate()">Validate</button>
</div>

<div id="results" class="results">
<div id="valid" class="valid" hidden>Valid</div>
<div id="error" class="error" hidden>Schema violations found</div>
<div id="output" class="output"></div>
</div>
<div class="disclaimer">
Note: All data is processed locally on your browser. Be aware that this tool will catch most schema validations,
but there may be other issues with your response that will not be caught here.
Review the <a href="https://docs.aws.amazon.com/lexv2/latest/dg/lambda.html" target="_blank">documentation</a> for the full
requirements.
</div>

</div>

</div>


<script src="validation-bundle.js"></script>
<script>
function validate() {
const responseJson = document.getElementById("codeHookResponseInput").value;
document.getElementById("output").innerHTML = "";
document.getElementById("valid").hidden = true;
document.getElementById("error").hidden = true;

try {
const codeHookResponseJSON = JSON.parse(responseJson);
document.getElementById("codeHookResponseInput").value = JSON.stringify(codeHookResponseJSON, undefined, 2);
const response = validation.validateInput(codeHookResponseJSON);
if (response) {
if (response.length > 0) {
for (let i = 0; i < response.length; i++) {
const node = document.createElement("li");
const textnode = document.createTextNode(response[i]);
node.appendChild(textnode);
document.getElementById("output").appendChild(node);

}
document.getElementById("error").hidden = false;
} else {
document.getElementById("valid").hidden = false;
}
}
} catch (error) {
document.getElementById("output").innerHTML = error;
}
}
</script>

</body>
</html>
Loading

0 comments on commit c3051da

Please sign in to comment.