From e5ad7da105f0245345386aa3e68931fd2156a5f7 Mon Sep 17 00:00:00 2001 From: Bob Potterveld Date: Sat, 5 Jun 2021 19:50:57 -0600 Subject: [PATCH] update documentation for 4.7.0 release. --- .vscode/spellright.dict | 6 +- README.md | 460 ++---------------- VPCSupportREADME.md | 17 +- docs/connect_callback.md | 53 ++ docs/multilanguage_support.md | 105 ++++ docs/overview/README.md | 37 ++ docs/overview/image2.png | Bin 0 -> 118886 bytes docs/overview/image9.png | Bin 0 -> 17457 bytes docs/redact.md | 15 + test-do-not-use-obsolete/.gitignore | 2 - test-do-not-use-obsolete/Makefile | 7 - test-do-not-use-obsolete/README.md | 15 - test-do-not-use-obsolete/alexa/.gitignore | 1 - test-do-not-use-obsolete/alexa/README.md | 16 - .../alexa/assets/en-US.json | 12 - .../alexa/assets/skill.json | 41 -- test-do-not-use-obsolete/alexa/create.sh | 12 - test-do-not-use-obsolete/alexa/run.sh | 9 - test-do-not-use-obsolete/alexa/setup.js | 46 -- .../alexa/test/.ask/config | 19 - .../alexa/test/lambda/custom/index.js | 55 --- .../alexa/test/lambda/custom/package.json | 11 - .../alexa/test/models/en-US.json | 47 -- .../alexa/test/skill.json | 30 -- test-do-not-use-obsolete/alexa/util.js | 43 -- test-do-not-use-obsolete/cfn/Dockerfile | 39 -- test-do-not-use-obsolete/cfn/README.md | 6 - test-do-not-use-obsolete/cfn/buildspec.yml | 18 - .../cfn/config/buildspec.yml | 19 - test-do-not-use-obsolete/cfn/lambda/README.md | 1 - test-do-not-use-obsolete/cfn/lambda/build.js | 64 --- test-do-not-use-obsolete/cfn/lambda/clear.js | 55 --- .../cfn/lambda/clearImage.js | 26 - test-do-not-use-obsolete/cfn/lambda/zip.js | 27 - test-do-not-use-obsolete/cfn/test.js | 327 ------------- test-do-not-use-obsolete/configure.sh | 38 -- .../kendra_tests/context_FAQ_md.json | 10 - .../kendra_tests/context_doc_query.json | 10 - .../kendra_tests/context_top_ans.json | 11 - .../kendra_tests/event_FAQ_md.json | 135 ----- .../kendra_tests/event_doc_query.json | 179 ------- .../kendra_tests/event_top_ans.json | 178 ------- .../kendra_tests/kendraData_FAQ_md.json | 94 ---- .../kendra_tests/kendraData_doc_query.json | 193 -------- .../kendra_tests/kendraData_top_ans.json | 164 ------- .../kendra_tests/mockClient.js | 7 - .../kendra_tests/mockClient1.js | 7 - .../kendra_tests/mockClient2.js | 7 - .../kendra_tests/mockClient3.js | 7 - .../kendra_tests/run_test.js | 63 --- .../kendra_tests/sample_return.json | 146 ------ .../kendra_tests/test_json.json | 42 -- test-do-not-use-obsolete/reporter.js | 131 ----- test-do-not-use-obsolete/run.js | 29 -- test-do-not-use-obsolete/setup.sh | 21 - test-do-not-use-obsolete/teardown.js | 45 -- test-do-not-use-obsolete/test.json | 161 ------ test-do-not-use-obsolete/test.sh | 15 - ...nABot_tuning_recognition_accuracy_guide.md | 179 ++++--- 59 files changed, 341 insertions(+), 3172 deletions(-) create mode 100644 docs/connect_callback.md create mode 100644 docs/multilanguage_support.md create mode 100644 docs/overview/README.md create mode 100755 docs/overview/image2.png create mode 100755 docs/overview/image9.png create mode 100644 docs/redact.md delete mode 100644 test-do-not-use-obsolete/.gitignore delete mode 100644 test-do-not-use-obsolete/Makefile delete mode 100644 test-do-not-use-obsolete/README.md delete mode 100644 test-do-not-use-obsolete/alexa/.gitignore delete mode 100644 test-do-not-use-obsolete/alexa/README.md delete mode 100644 test-do-not-use-obsolete/alexa/assets/en-US.json delete mode 100644 test-do-not-use-obsolete/alexa/assets/skill.json delete mode 100755 test-do-not-use-obsolete/alexa/create.sh delete mode 100755 test-do-not-use-obsolete/alexa/run.sh delete mode 100755 test-do-not-use-obsolete/alexa/setup.js delete mode 100644 test-do-not-use-obsolete/alexa/test/.ask/config delete mode 100644 test-do-not-use-obsolete/alexa/test/lambda/custom/index.js delete mode 100644 test-do-not-use-obsolete/alexa/test/lambda/custom/package.json delete mode 100644 test-do-not-use-obsolete/alexa/test/models/en-US.json delete mode 100644 test-do-not-use-obsolete/alexa/test/skill.json delete mode 100644 test-do-not-use-obsolete/alexa/util.js delete mode 100644 test-do-not-use-obsolete/cfn/Dockerfile delete mode 100644 test-do-not-use-obsolete/cfn/README.md delete mode 100644 test-do-not-use-obsolete/cfn/buildspec.yml delete mode 100644 test-do-not-use-obsolete/cfn/config/buildspec.yml delete mode 100644 test-do-not-use-obsolete/cfn/lambda/README.md delete mode 100644 test-do-not-use-obsolete/cfn/lambda/build.js delete mode 100644 test-do-not-use-obsolete/cfn/lambda/clear.js delete mode 100644 test-do-not-use-obsolete/cfn/lambda/clearImage.js delete mode 100644 test-do-not-use-obsolete/cfn/lambda/zip.js delete mode 100644 test-do-not-use-obsolete/cfn/test.js delete mode 100755 test-do-not-use-obsolete/configure.sh delete mode 100644 test-do-not-use-obsolete/kendra_tests/context_FAQ_md.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/context_doc_query.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/context_top_ans.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/event_FAQ_md.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/event_doc_query.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/event_top_ans.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/kendraData_FAQ_md.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/kendraData_doc_query.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/kendraData_top_ans.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/mockClient.js delete mode 100644 test-do-not-use-obsolete/kendra_tests/mockClient1.js delete mode 100644 test-do-not-use-obsolete/kendra_tests/mockClient2.js delete mode 100644 test-do-not-use-obsolete/kendra_tests/mockClient3.js delete mode 100644 test-do-not-use-obsolete/kendra_tests/run_test.js delete mode 100644 test-do-not-use-obsolete/kendra_tests/sample_return.json delete mode 100644 test-do-not-use-obsolete/kendra_tests/test_json.json delete mode 100644 test-do-not-use-obsolete/reporter.js delete mode 100755 test-do-not-use-obsolete/run.js delete mode 100755 test-do-not-use-obsolete/setup.sh delete mode 100755 test-do-not-use-obsolete/teardown.js delete mode 100644 test-do-not-use-obsolete/test.json delete mode 100755 test-do-not-use-obsolete/test.sh diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict index 75658e5d6..16e79590d 100644 --- a/.vscode/spellright.dict +++ b/.vscode/spellright.dict @@ -25,6 +25,8 @@ arn qid Nutritionix 'qna' +Logstash +quniqueterms Node.js ap ctz @@ -33,7 +35,3 @@ gotanswer qnabotcontext nnn-nn-nnnn Conne -ssml -cardtitle -cardimageurl -buttonvalue diff --git a/README.md b/README.md index 38b88763a..7ba84ec5d 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,15 @@ This repository contains code for the QnABot, described in the AWS AI blog post [“Creating a Question and Answer Bot with Amazon Lex and Amazon Alexa”](https://aws.amazon.com/blogs/ai/creating-a-question-and-answer-bot-with-amazon-lex-and-amazon-alexa/). -See the "Getting Started" to launch your own QnABot +See the "Getting Started" to launch your own QnABot. +4.7.0 QnABot now supports LexV2 with voice interaction in multiple languages. + - Two installation/update modes are now available: + - (i) LexV1 + LexV2 (default, recommended for most AWS regions. + - (ii) LexV2-only (currently recommended for AWS regions where LexV1 is not available). + +**See all the new features in 4.7.0** [LexV2 support, Excel Import, New Canada Central region](#new-features) + 4.6.0 provides a number of new features described below. Several to call attention to are the following: - Kendra custom no_hits item required in earlier releases is no longer required to enable Kendra Fallback and should be removed, configurable confidence thresholds now available for filtering Kendra results. @@ -18,22 +25,6 @@ See the "Getting Started" to launch your own QnABot **New features in 4.5.0** [Kendra Web Crawler, Comprehend PII Detection, Translate Custom Terminology, Increased deployment regions](#new-features) -**New features in 4.4.0** [Preview version of VPC Deployment Support, Preview version of BotRouter, Upgrade to ES 7.9, Slack client detection and Markdown Support](#new-features) - -**New features in 4.3.0** [Connect Wizard to assist in Connect / Lex / QnABot use case, Security enhancement in API Gateway, Four node elastic search cluster support](#new-features) - -**New features in 4.2.0** [Beta Kendra FAQ Support, Bug fixes, Multiple document chaining, Repeat question, Elastic Search 7.7 upgrade](#new-features) - -**New features in 4.1.0** [Encryption at rest for S3 and Elastic Search Cluster, option to require Cognito user pool authorization to access embedded web UI, enhanced Kendra integration, enhanced Connect integration, and others](#new-features) - -**New features in 4.0.0** [Update to Elasticsearch 7.4, improved question matching accuracy, fuzzy matching, new multi-language support debug setting, SSML for Amazon Connect, improved Kendra integration, full upgrade support](#new-features) - -**New features in 3.0.3** [New content tuning Readme, Enhanced CFN Lex Resource to work with manually created Bot versions](#new-features) - -**New features in 3.0.2** [New Elicit Response Bots, Lambda Functions backing conditional chaining, Lex Bot versioning use](#new-features) - -**New features in 3.0.0** [ElicitResponse, Conditional Chaining, new Launch regions](#new-features) - ## Upgrade Notes During an upgrade, we recommend that existing QnABot content first be exported and downloaded from the Content Designer prior to @@ -65,6 +56,7 @@ Click a button to launch QnABot CloudFormation stack in the desired region | Frankfurt | | | Singapore | | | Tokyo | | +| Canada Central | | ### Clone the git repo and build a version @@ -107,78 +99,6 @@ If you have an existing stack you can run the following to update your stack: npm run update ``` -## Components - -### CloudFormation Templates - -The CloudFormation test templates are in the templates/test folder. The current templates are: - -1. Master: the template contains all the resources for QnABot. -2. Public: this is a version of the Master template with less parameters, less outputs, and the bootstrap bucket hardcoded to the publicBucket in config.json -3. various templates in /templates/dev: needed for local testing of the lambda functions. - -Run a template test with: - -```shell -npm run stack test/{template-name} -``` - -For example, if you want to test the domain template run: - -```shell -npm run stack test/domain -``` - -To understand the command more run: - -```shell -npm run stack -h -``` - -You also can check a template's syntax with: - -```shell -npm run check {template-name} -``` - -ex. - -```shell -npm run check domain -``` - -To understand the command more run: - -```shell -npm check stack -h -``` - -### Lambda Functions - -Lambda functions are found in the /lambda directory. Refer to the README.md file in each directory for instructions on setting up a dev environment and testing. -[Fulfillment](lambda/fulfillment/README.md) -[CFN](lambda/handler/README.md) -[Lex-Build](lambda/lex-build/README.md) -[Import](lambda/import/README.md) - -### Web Interface - -The Designer UI and client UI code is in the /website directory. - -To Test the web ui, Launch a development master stack: - -```shell -npm run stack dev/master up -``` - -when that stack has finished run: - -```shell -cd ./website ; make dev -``` - -this will launch a running webpack process that will watch for changes to files and upload the changes to your running dev/master stack. - #### Designer UI Compatibility Currently the only browsers supported are: @@ -198,6 +118,26 @@ See the [LICENSE.md](LICENSE.md) file for details ## New features +### Version 4.7.0 + +- QnABot now supports LexV2 with voice interaction in multiple languages. + - Two installation/update modes are now available: + - (i) LexV1 + LexV2 (default, recommended for most AWS regions. + - (ii) LexV2-only (currently recommended for AWS regions where LexV1 is not available). + - LexV2 locales are specified via a new CloudFormation parameter + - The default locales are US English, US Spanish and Canadian French. +- The QnABot web client now uses LexV2 and supports dynamic bot locale selection from a new title bar menu. +- Custom LexV2 Elicit Response bots are now supported. The built-in response bots still use LexV1 and are + available only when QnABot is installed in LexV1+LexV2 mode. +- CloudFormation deployment is now available for Canada/Montreal region (LexV2-only mode). +- Amazon Connect integration in the Canada/Montreal region supports multiple voice languages using LexV2. +- The Content Designer 'Test All' feature now uses LexV2. +- Content Designer's "Rebuild Lex Bot" feature now rebuilds both LexV2 and LexV1 bots +- Non-English LexV2 bot locales are automatically generated with sample utterances translated from English questions using Amazon Translate. +- Content Designer's Import feature now supports Excel spreadsheets as well as the existing JSON format. +- QnABot's Elasticsearch cache is now automatically kept warm to improve query time consistency. +- Negative feedback (thumbs down) messages can now generate notifications (text, email, etc.) using Amazon SNS. + ### Version 4.6.0 - Kendra integration is now fully automated during install or update when the new default Kendra Index Id parameter is provided. @@ -232,347 +172,5 @@ See the [LICENSE.md](LICENSE.md) file for details if desired. - QnABot distribution regions now available for one click deployment have increased to 8 regions. These are Northern Virginia (us-east-1), Oregon (us-west-2), Ireland (eu-west-1), London (eu-west-2), Frankfurt (eu-central-1), Sydney (ap-southeast-2), Singapore (ap-southeast-1), and Tokyo (ap-northeast-1). -### Version 4.4.0 - -- Preview VPC support - [readme](./VPCSupportREADME.md) -- Preview BotRouter support - [readme](./BotRoutingREADME.md) -- Upgrade to Elasticsearch service version 7.9 -- Slack client support via Lex with Slack specific markdown support -- Added support for Alexa re-prompt functionality - -VPC support is enabled in beta mode through a new template available in the distribution repos. Please understand -the content in [readme](./VPCSupportREADME.md) before proceeding with this type of deployment. - -- artifacts/aws-ai-qna-bot/templates/public-vpc-support.json -This beta template exposes two new additional parameters that can be specified when deployed using the CloudFormation console. -These parameters are: -- VPCSubnetIdList -- VPCSecurityGroupIdList -As one might expect a set of SubnetIds and SecurityGroupIds need to be specified. Two private subnets with appropriate NAT -based gateway to public internet should be selected. The security group specified must allow at a minimum inbound -connectivity on port 443. The Elasticsearch cluster and all Lambdas will be attached to these private subnets. The -Designer UI is still available outside of the VPC but requires login via the Cognito user pool. The Elasticsearch -cluster will not be available externally. Users wishing to use the Kibana console will need VPN connectivity to the -VPC and is outside the scope of this document. - -### Version 4.3.0 - -- New Connect Wizard available in the Content Designer UI to assist integration with a Connect Contact Flow. -- New 4-node Elasticsearch domain support for improved fault tolerance in deployment template. -- Elicit Response bot support for confirmation responses using phone keypad 1 = yes 2 = no. -- Security improvements in API Gateway. -- ID token values removed from session event after validation and redacted from logging. -- Setting to limit the number of Kendra fallback search results. -- Setting to enable signed URLs for S3 documents in Kendra search results. - -Provides the ability to deploy QnABot components within VPC infrastructure via a new template named public-vpc-support.json. -This template is made available for use as a separate installation mechanism. It is not the default template utilized in the -public distribution buckets. Please take great care in deploying QnABot in VPC. The Elasticsearch Cluster -becomes bound to the VPC as well as the Lambda's installed. The Elasticsearch cluster is no longer available -outside of the VPC. All Lambdas are bound to the VPC to allow communication with the cluster. - -The following limitations exist: - -- A QnABot deployed within VPC can NOT be modified to operated in non-VPC. -- A properly configured VPC with public/private subnets using proper Internet Gateway and Nat Gateway must be available. -- Two private subnets must be specified as parameters for the CloudFormation template. -- A Security Group allowing inbound port 443 from connections within the VPC CIDR block must be configured and specified -as a parameter for the CloudFormation template. -- The Kibana dashboard will only be available from clients that have access to the VPC. -- The VPC must be configured to allow access to Lex, S3, Lambda, DynamoDB, Systems Manager (SSM Parameter Store), Kendra, and -Comprehend. As more features are added to QnABot the required service access may increase. VPC Endpoints using PrivateLink -is not required but may be used if available. - -### Version 4.2.0 - -- New Kendra FAQ support (Beta version) using the setting KENDRA_FAQ_INDEX. New menu item in Designer UI to export Questions as a Kendra FAQ. See revised Blog Post for details. -- New GetSessionAttribute Handlebars helper to obtain session attribute. Works similar to lodash get(). Will not through exception and will return a default value. -- Enhanced handlebars to support string concatenation including handlebar 'variables' like Session Attributes and UserInfo, etc. Use case, e.g. to build a url containing a users email, eg a google calendar URL. Example of syntax now supported - in this case to dynamically build a personalized URL based on user info. `{{setSessionAttr 'link' 'https://calendar.google.com/calendar/embed?src=' UserInfo.Email '&ctz=America%2FNew_York'}}` -- Moved 'previous' and 'navigation' session attributes under a new 'qnabotcontext' session attribute so that Connect (and other) clients have fewer session attributes to preserve. -- Allows Chaining rule Lambda function to return a modified session object in addition to the string for chaining. -- Allows Chaining of up to 10 documents. Each document's Lambda hooks will also be invoked in sequence if defined. -- Added a new Repeat QID in the QNAUtility example package. Allows QnABot to easily repeat the last answer. -- Allow the chaining rule to specify a specific QID rather than an answer. A QID can be specified in the chaining rule by using string such as QID:: e.g. QID::Admin.001. Note, the new QID:: syntax can also be used from the webUI, say as button values if/when you prefer to target a specific QID (exact query) rather than rely on question matching. -- Fixed a defect to allow conditional chaining to be invoked after an elicit response bot failure. -- Upgrades to and installs ElasticSearch 7.7. - -### Version 4.1.0 - -- Install / Upgrade now supports the option to configure S3 Buckets and Elastic Search cluster using encryption at rest -- Install / Upgrade now supports the option to require Cognito based user authorization to access the built-in full screen web UI (Public/Private parameter in template) - Public is the default -- Added two settings parameters to enforce user identity verification check, so that bot can be secured for use by authenticated users only - - ENFORCE_VERIFIED_IDENTITY. Default is false. Set to true to make QnABot require verified identity from client - - NO_VERIFIED_IDENTITY_QUESTION. The default is "no_verified_identity". If user identity cannot be verified, replace question string with this. If not verified, the system will respond to user's question with the result of searching for NO_VERIFIED_IDENTITY_QUESTION. This allows a customizable message which informs the user that they must log in. A default question with qid "no_verified_identity" is included in QNAUtility example package. -- Enhanced Kendra fallback integration to use a specific answer if there is a best answer available and bold face highlighted words from Kendra response -- Added Comprehend sentiment analysis to all utterances and text captured by the QNAFreeText elicit response bot -- Enhanced Kibana dashboard to identify Lex client channels - Connect, Web, SMS -- Improved internal use of Booleans from settings configuration -- Enhanced Connect integration - - Added session attribute named "qnabot_qid" that holds the matching question id found in elastic search - - Added session attribute "qnabot_gotanswer" that holds boolean true/false if an answer was found - - Encapsulating all Kendra and Elicit Response Bot session attributes into a single "qnabotcontext" attribute making it easier to store and reset in Connect contact flow -- Added new QNAYesNoExit elicit response bot which allows a user to exit the YesNoExit question using "exit", "bye", "quit", "admin", "rep","representative","stop", "help", "bye", "goodbye" which sets the Yes_No_Exit slot value / session attribute to "Exit". -- Update to 0.17.0 of embedded lex-web-ui -- Resolved additional dependabot identified security issues with dependent packages -- Fixed lambda/fulfillment unit tests -- Fixed defect where response bot was not triggered on next question when using lambda function for conditional chaining - -### Version 4.0.0 - -- Update to Elasticsearch 7.4 -- Improved question matching accuracy and tuning -- Tolerance for typos and minor spelling errors with fuzzy matching setting -- Easier troubleshooting when using voice or multi-language support with new debug setting -- SSML support when using Amazon Connect -- Improvements to Amazon Kendra integration -- Full upgrade support without data loss when upgrading from previous versions. - -### Content Tuning and Accuracy Guide - -Content Tuning an and Accuracy Guide now available as a Markdown Readme. [README](tuning_accuracy_guide/AWS_QnABot_tuning_recognition_accuracy_guide.md) - -### Optional Redact feature for log and metric output - -QnABot can be configured to redact information written to CloudWatch logs, S3 metrics, and Kibana metrics logs. -This feature is disabled by default. Use the Designer UI Settings form to enable this feature. One can configure -the RegEx applied to strings as they are logged. If RegEx matches are found, the match is replaced with the string -'XXXXXX'. - -The initial RegEx is - -```regex -\b\d{4}\b(?![-])|\b\d{9}\b|\b\d{3}-\d{2}-\d{4}\b -``` - -This replaces 4 digit numbers not followed by a hyphen, a 9 digit number (SSN without hyphens), and a typical -SSN using nnn-nn-nnnn syntax with hyphens. - -### New Connect Callback Example - -New example demonstrating how QnABot can be asked by a user for a live agent based phone callback. The -implementation provides a new LambdaHook example as well as four sample questions that ask a user for -their name and phone number prior to handing off to an Amazon Connect instance to initiate the callback. - -**Two configuration updates are required to use this example with Amazon Connect.** - -The IAM Role/Policy used by the ConnectCallback Lambda must include a new policy that allows -the action "connect:StartOutboundVoiceContact" to be used with the resource -`"arn:aws:connect:*:*:instance//*"`. The following is an example of this policy - -```json -{ - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "VisualEditor0", - "Effect": "Allow", - "Action": "connect:StartOutboundVoiceContact", - "Resource": "arn:aws:connect:*:*:instance//*" - } - ] -} -``` - -1) Find the Lambda ConnectCallback Function in the AWS Lambda Console -2) Open the AWS Console and select the Lambda Service -3) In the Console's filter enter 'ConnectCallback' and press enter -4) The displayed function will start with `-ExamplePYTHONLambdaConne...` If you have multiple QnABot stacks -installed you'll see multiple functions listed -5) Open the Lambda function by clicking on the function -6) Select the Permissions tab -7) Click on the Role name to open this Role in a new tab -8) Click on + Add inline policy -9) Select the JSON tab -10) Copy the sample text above, paste as JSON, and change `` to the Instance ID identified in the Connect Console. -11) Click on Review policy -12) Enter a name for the policy and click Create policy - -You've now enabled Lambda functions using this role to start outbound calls via the connect instance - -Lambda Hook Arguments need to be updated. Before being used, the item with qid CONNECT_TO_AGENT.04 -should have its Arguments field adjusted to reflect identifiers from the Connect instance: - -```bash -"AWS_connect_instance_id": "", -"AWS_connect_contact_flow_id": "", -"AWS_connect_queue_id": "", -``` - -Once these configuration changes are in place, QnABot can be successfully use Amazon Connect to place -outbound calls. - -### Conditional Chaining now supports Lambda Function - -You can specify that a Conditional Chaining rule runs a Lambda Function using the "Lambda::FunctionName" syntax. -The Lambda function name must start with "QNA". The Lambda will receive arguments the same as Lambda Hooks. The -function must return a String that is used to resolve the next item just the same as other conditional chaining rules. - -### Improved Scale - -This release of QnABot utilizes Lex Bot aliases and Lex versioning. The QnA Bot and all ElicitResponse Bots are -now installed using the alias 'live'. 'live' points at the latest numbered Bot version. All Lex resources are now -versioned starting with '1' after initial installation. - -### Additional Elicit Response Bots - -This release adds several elicit response bots for -QNAWage - 7 digit wage value - returns slot named "Wage" -QNASocialSecurity - SSN Numbers in the nnn-nn-nnnn format - returns slot named "SSN" -QNAPin - 4 digit pin value - Returns slot named "Pin" - -### Configuring QnABot to ask the questions - -QnABot was designed to answer questions, but now it can also ask questions and process -the user’s answers. Use this feature for data collection and validation; -or implement surveys, quizzes, personalized recommendations; or to build a triage -chatbot application. See the [blog post](https://aws.amazon.com/blogs/machine-learning/creating-a-question-and-answer-bot-with-amazon-lex-and-amazon-alexa/) for details about this new feature. -This feature is know as ElicitResponse from a configuration perspective. - -### Automatically advancing and branching along a tree of questions - -QnABot can now chain the user from one answer to another. This can be used with ElicitResponse -to ask multiple questions from a user. Conditional chaining allows branching based on -the state of session variables. Please see the [blog post](https://aws.amazon.com/blogs/machine-learning/creating-a-question-and-answer-bot-with-amazon-lex-and-amazon-alexa/) -for a description on how to use this feature. - -### Kendra Fallback Support - -QnABot version 2.6.0 optionally supports integration with Amazon Kendra as a fallback mechanism if a question/answer can not -be found in QnABot. - -**Important note. Use of Kendra as a fallback mechanism will incur additional charges for your AWS Account. Please -review the Kendra pricing structure. The fallback mechanism for QnABot can be useful when deploying Kendra as an -Enterprise search solution.** - -To enable this support for your Kendra indexes, use the Settings UI in the Designer and add your -index to the ALT_SEARCH_KENDRA_INDEXES parameter. This parameter takes an array of strings and uses the form below. - -```json -["a672e3a2-nnnn-nnnn-nnnn-7b3abc81c313"] -``` - -**Don't forget to use your Kendra Index ID rather than the one in the sample** -Next use the QnABot Designer UI to import a Sample/Extension named KendraFallback. - -This loads a new question with a qid of "KendraFallback". Edit this question in the Designer UI and change its question from "no_hits_alternative" to "no_hits" and save the changes. - -If you have previously loaded the QnAUtility.json from Examples/Extensions you need to either remove -the question with the ID "CustomNoMatches" or change the question for this ID from "no_hits" to "no_hits_original" - -Once the new question, "KendraFallback" is configured as the response for "no_hits", the Kendra index will be -searched for an answer whenever a curated answer can not be found. This feature provides a fallback mechanism -prior to telling the user an answer could not be found. - -A [workshop](https://github.com/aws-samples/aws-ai-qna-bot/tree/master/workshops/reinvent2019/readme.md) is available in GitHub. -that will walk you through setting up this feature. - -**Important note. Use of Kendra as a fallback mechanism will incur additional charges for your AWS Account. Please review the Kendra pricing structure. The fallback mechanism for QnABot can be useful when deploying Kendra as an Enterprise search solution.** - -### MultiLanguage Support - -QnABot version 2.6.0 supports use of multiple languages with these limitations: - -- MultiLanguage support for voice is limited to use with Alexa skills only. (Amazon Lex currently supports voice recognition in English only) -- MutiLanguage support for text is available via all text clients (e.g. Lex Web UI, SMS, etc.) - - ####Lex (text mode only): - -If the user enters a question in a language other than english, QnABot will attempt to return an answer in the other language. -It does this by using Amazon Comprehend to identify the language typed. If Comprehend can identify the language based on a configured minimum confidence, -QnABot will serve up content based on that locale. - -Users can also set a preferred language whereby QnABot will always attempt to respond with content in the chosen -locale. If the user sets the preferred language to be Spanish, QnABot will always try and serve up content using -Spanish when possible. - -#### Alexa (voice) - -You will need to add each language you want to use to your QnABot skill using the Alexa Developer console. The intent schema for each -language will be identical except for the skill invocation name. Give the skill a unique invocation name for each language that you add. - -QnABot will use the language setting provided by Alexa, and will attempt to respond in that language. - -#### How it works - -QnABot converts the question posed by the user to English, using Amazon Translate, and performs a lookup of the answer in Elastic Search -just as it normally does, using the English translation of the question. ElasticSearch searches are done in English only since QnABot documents -are indexed using the English text analyzer (stemming, stop words, etc.) - -To ensure good matching of translated questions, you can use the Amazon Translate console to see the English translation of your local language question. Use QnABot content designer -to ensure your QnA item has the right sample questions to ensure a match. - -Once it finds the question, QnABot will serve up the configured answer. - -You can use Handlebar blocks to define explicit answers in each different language you want to support. OR, if you do not -explicitly define an answer in the user's language, QnABot will automatically use Amazon Translate to convert the default English. -answer to the target language. - -#### Configuration - -By default this feature is disabled. Use the following three steps to enable and configure this feature. Step 1 enables the feature. Step 2 loads in two questions from this extension that allow the user to select a preferred language. The defaults supplied in this question are English, Spanish, French, German, and Italian. You can extend this list to -support other languages. - -Step 1) Enable multi language support - -a) QnABot uses a property named ENABLE_MULTI_LANGUAGE_SUPPORT, default value of "false". -You can change this setting using the Content Designer Settings page. Set it to "true" to enable multi language support. - -Step 2) Use the Designer UI to import the Sample/Extension named Language / Multiple Language Support. - -This will add two questions to the system: Language.000 and Language.001. When using Lex text clients, these questions will allow you to set your preferred language. -The preferred language, if set, will take precedence over -the auto detected language. - -_When using Alexa, the language is automatically set by the skill. You will not be able to override the preferred language when using Alexa._ - -Language.000 provides a question that allows the user to set the current sessions preferred output saying a simple word -such as French, German, or Spanish, or Italian. - -Language.001 resets the preferred language. This can be performed by saying or typing 'reset language' or 'detect language'. -You can also input using your language of choice assuming AWS Translate can translate the input back to English. - -Once you've imported this extension question try typing the question 'Spanish'. You should see a Spanish response. - -Next enter 'English' and you will have switched your preference back to English. - -Next enter 'reset language' and your preference will be reset and language auto detection will occur again. - -The answer for Language.000 uses the following handlebar syntax - -```handlebars -{{#setLang 'fr' false}} D'accord. J'ai défini votre langue préférée sur l'anglais. {{/setLang}} -{{#setLang 'es' false}} Okay. He configurado tu idioma preferido al inglés. {{/setLang}} -{{#setLang 'de' false}} In Ordnung. Ich habe Ihre bevorzugte Sprache auf Englisch eingestellt. {{/setLang}} -{{#setLang 'it' false}} Ok. Ho impostato la tua lingua preferita sull'inglese.{{/setLang}} -{{#setLang 'en' true}} Ok. I've set your preferred language to English. {{/setLang}} -``` - -The helper function setLang performs the necessary processing to set the preferred language/locale for the session. To -add support for other languages just extend the answer in Language.000 with additional locales. - -Step 3) In order to serve up content that is locale specific you can - -- allow QnABot to automatically translate your english answers to the session language using Amazon Translate. -- OR provide explicitly curated answers in QnA items, in multiple languages, using handlebars, as shown below. - -Lets modify the question sun.1. The following would be an example where the handlebar function ifLang is used to specify a response for Spanish. - -Use the handlebar template defaultLang to specify the response QnABot should provide when the language is unknown. By -default this is typically in English but could be in any language as needed. - -`{{#defaultLant}}{{/defaultLang}}` must be the last element in the answer block. - -```handlebars -{{#ifLang 'es'}} -Nuestro sol tiene 4.600 millones de años. Se considera una enana amarilla con un diámetro de 1,392,684 kilómetros y una circunferencia de 4,370,005 kilómetros. Tiene una masa que es igual a 333,060 tierras y una temperatura superficial de 5,500 grados centígrados. ¡Muy caliente! -{{/ifLang}} -{{#defaultLang}} -Our sun is 4.6 billion years old. Its considered a yellow dwarf with a diameter of 1,392,684 kilometers and a circumference of 4,370,005 kilometers. It has a mass that is equal to 333,060 earths and a surface temperature of 5,500 degrees celsius. Really Hot! -{{/defaultLang}} -``` - -The handlebar function ifLang takes locale as a quoted parameter. This tells QnABot which locale to associate with the subsequent -text. - A [workshop](https://github.com/aws-samples/aws-ai-qna-bot/tree/master/workshops/reinvent2019/readme.md) is available in GitHub that will walk you through setting up this feature. diff --git a/VPCSupportREADME.md b/VPCSupportREADME.md index 62cfb96e8..9d3a42349 100644 --- a/VPCSupportREADME.md +++ b/VPCSupportREADME.md @@ -1,14 +1,26 @@ # VPC Support - Preview Mode (version 1.0 - December 2020) -QnABot now provides in Preview Mode deployment using a VPC. +This feature allows deployment of QnABot components within VPC infrastructure via a new template named public-vpc-support.json. +This template is made available for use as a separate installation mechanism. It is not the default template utilized in the +public distribution buckets. Please take care in deploying QnABot in VPC. The Elasticsearch Cluster +becomes bound to the VPC as well as the Lambda's installed. The Elasticsearch cluster is no longer available +outside of the VPC. All Lambdas are bound to the VPC to allow communication with the cluster. + +Two additional parameters are required by this template. + +- VPCSubnetIdList (two private subnets spread over two availability zones - see below) +- VPCSecurityGroupIdList (see below) ### Requirements In order for deployment of resources attaching within a VPC two requirements must be met. -1) A fully functioning VPC with a minimum of two private subnets spread over two availability zones is required. In addition +1) A fully functioning VPC with a minimum of two private subnets spread over two availability zones is required. + These private VPC subnets should have access to AWS services. This can be accomplished using NAT Gateway with proper IGW + configuration / routing. Other third party gateway implementations can be used that provide access to AWS services. + 2) A pre-configured VPC security group that 1) allows inbound connections on port 443 from other addresses in the VPC CIDR block. For example, if the VPC's CIDR block is 10.178.0.0/16, inbound connections in the security @@ -55,4 +67,3 @@ VPN or Direct Connect to the VPC. * The API Gateway used by the Designer UI is still available publicly and access is still authorized using Cognito. The Lambda's backing the API will run within the VPC. - diff --git a/docs/connect_callback.md b/docs/connect_callback.md new file mode 100644 index 000000000..fd7bb0b61 --- /dev/null +++ b/docs/connect_callback.md @@ -0,0 +1,53 @@ +# New Connect Callback Example + +New example demonstrating how QnABot can be asked by a user for a live agent based phone callback. The +implementation provides a new LambdaHook example as well as four sample questions that ask a user for +their name and phone number prior to handing off to an Amazon Connect instance to initiate the callback. + +**Two configuration updates are required to use this example with Amazon Connect.** + +The IAM Role/Policy used by the ConnectCallback Lambda must include a new policy that allows +the action "connect:StartOutboundVoiceContact" to be used with the resource +`"arn:aws:connect:*:*:instance//*"`. The following is an example of this policy + +```json +{ + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "VisualEditor0", + "Effect": "Allow", + "Action": "connect:StartOutboundVoiceContact", + "Resource": "arn:aws:connect:*:*:instance//*" + } + ] +} +``` + +1) Find the Lambda ConnectCallback Function in the AWS Lambda Console +2) Open the AWS Console and select the Lambda Service +3) In the Console's filter enter 'ConnectCallback' and press enter +4) The displayed function will start with `-ExamplePYTHONLambdaConne...` If you have multiple QnABot stacks +installed you'll see multiple functions listed +5) Open the Lambda function by clicking on the function +6) Select the Permissions tab +7) Click on the Role name to open this Role in a new tab +8) Click on + Add inline policy +9) Select the JSON tab +10) Copy the sample text above, paste as JSON, and change `` to the Instance ID identified in the Connect Console. +11) Click on Review policy +12) Enter a name for the policy and click Create policy + +You've now enabled Lambda functions using this role to start outbound calls via the connect instance + +Lambda Hook Arguments need to be updated. Before being used, the item with qid CONNECT_TO_AGENT.04 +should have its Arguments field adjusted to reflect identifiers from the Connect instance: + +```bash +"AWS_connect_instance_id": "", +"AWS_connect_contact_flow_id": "", +"AWS_connect_queue_id": "", +``` + +Once these configuration changes are in place, QnABot can be successfully use Amazon Connect to place +outbound calls. diff --git a/docs/multilanguage_support.md b/docs/multilanguage_support.md new file mode 100644 index 000000000..2d4fa3693 --- /dev/null +++ b/docs/multilanguage_support.md @@ -0,0 +1,105 @@ +# MultiLanguage Support + +QnABot version 2.6.0 supports use of multiple languages with these limitations: + +- MultiLanguage support for voice is limited to use with Alexa skills only. (Amazon Lex currently supports voice recognition in English only) +- MutiLanguage support for text is available via all text clients (e.g. Lex Web UI, SMS, etc.) + + ####Lex (text mode only): + +If the user enters a question in a language other than english, QnABot will attempt to return an answer in the other language. +It does this by using Amazon Comprehend to identify the language typed. If Comprehend can identify the language based on a configured minimum confidence, +QnABot will serve up content based on that locale. + +Users can also set a preferred language whereby QnABot will always attempt to respond with content in the chosen +locale. If the user sets the preferred language to be Spanish, QnABot will always try and serve up content using +Spanish when possible. + +## Alexa (voice) + +You will need to add each language you want to use to your QnABot skill using the Alexa Developer console. The intent schema for each +language will be identical except for the skill invocation name. Give the skill a unique invocation name for each language that you add. + +QnABot will use the language setting provided by Alexa, and will attempt to respond in that language. + +## How it works + +QnABot converts the question posed by the user to English, using Amazon Translate, and performs a lookup of the answer in Elastic Search +just as it normally does, using the English translation of the question. ElasticSearch searches are done in English only since QnABot documents +are indexed using the English text analyzer (stemming, stop words, etc.) + +To ensure good matching of translated questions, you can use the Amazon Translate console to see the English translation of your local language question. Use QnABot content designer +to ensure your QnA item has the right sample questions to ensure a match. + +Once it finds the question, QnABot will serve up the configured answer. + +You can use Handlebar blocks to define explicit answers in each different language you want to support. OR, if you do not +explicitly define an answer in the user's language, QnABot will automatically use Amazon Translate to convert the default English. +answer to the target language. + +## Configuration + +By default this feature is disabled. Use the following three steps to enable and configure this feature. Step 1 enables the feature. Step 2 loads in two questions from this extension that allow the user to select a preferred language. The defaults supplied in this question are English, Spanish, French, German, and Italian. You can extend this list to +support other languages. + +Step 1) Enable multi language support + +a) QnABot uses a property named ENABLE_MULTI_LANGUAGE_SUPPORT, default value of "false". +You can change this setting using the Content Designer Settings page. Set it to "true" to enable multi language support. + +Step 2) Use the Designer UI to import the Sample/Extension named Language / Multiple Language Support. + +This will add two questions to the system: Language.000 and Language.001. When using Lex text clients, these questions will allow you to set your preferred language. +The preferred language, if set, will take precedence over +the auto detected language. + +_When using Alexa, the language is automatically set by the skill. You will not be able to override the preferred language when using Alexa._ + +Language.000 provides a question that allows the user to set the current sessions preferred output saying a simple word +such as French, German, or Spanish, or Italian. + +Language.001 resets the preferred language. This can be performed by saying or typing 'reset language' or 'detect language'. +You can also input using your language of choice assuming AWS Translate can translate the input back to English. + +Once you've imported this extension question try typing the question 'Spanish'. You should see a Spanish response. + +Next enter 'English' and you will have switched your preference back to English. + +Next enter 'reset language' and your preference will be reset and language auto detection will occur again. + +The answer for Language.000 uses the following handlebar syntax + +```handlebars +{{#setLang 'fr' false}} D'accord. J'ai défini votre langue préférée sur l'anglais. {{/setLang}} +{{#setLang 'es' false}} Okay. He configurado tu idioma preferido al inglés. {{/setLang}} +{{#setLang 'de' false}} In Ordnung. Ich habe Ihre bevorzugte Sprache auf Englisch eingestellt. {{/setLang}} +{{#setLang 'it' false}} Ok. Ho impostato la tua lingua preferita sull'inglese.{{/setLang}} +{{#setLang 'en' true}} Ok. I've set your preferred language to English. {{/setLang}} +``` + +The helper function setLang performs the necessary processing to set the preferred language/locale for the session. To +add support for other languages just extend the answer in Language.000 with additional locales. + +Step 3) In order to serve up content that is locale specific you can + +- allow QnABot to automatically translate your english answers to the session language using Amazon Translate. +- OR provide explicitly curated answers in QnA items, in multiple languages, using handlebars, as shown below. + +Lets modify the question sun.1. The following would be an example where the handlebar function ifLang is used to specify a response for Spanish. + +Use the handlebar template defaultLang to specify the response QnABot should provide when the language is unknown. By +default this is typically in English but could be in any language as needed. + +`{{#defaultLant}}{{/defaultLang}}` must be the last element in the answer block. + +```handlebars +{{#ifLang 'es'}} +Nuestro sol tiene 4.600 millones de años. Se considera una enana amarilla con un diámetro de 1,392,684 kilómetros y una circunferencia de 4,370,005 kilómetros. Tiene una masa que es igual a 333,060 tierras y una temperatura superficial de 5,500 grados centígrados. ¡Muy caliente! +{{/ifLang}} +{{#defaultLang}} +Our sun is 4.6 billion years old. Its considered a yellow dwarf with a diameter of 1,392,684 kilometers and a circumference of 4,370,005 kilometers. It has a mass that is equal to 333,060 earths and a surface temperature of 5,500 degrees celsius. Really Hot! +{{/defaultLang}} +``` + +The handlebar function ifLang takes locale as a quoted parameter. This tells QnABot which locale to associate with the subsequent +text. \ No newline at end of file diff --git a/docs/overview/README.md b/docs/overview/README.md new file mode 100644 index 000000000..a78ffceac --- /dev/null +++ b/docs/overview/README.md @@ -0,0 +1,37 @@ +# Overview + +Chatbots are a great way to make information available for your users. With QnABot you can deploy a chatbot in just a few steps and have a fully functional chat experience setup in under an hour. + + +Once the solution is deployed, you have a QnABot designer console where you can build and manage your question and answer bank. The question and answer bank become your knowledge base and the main source of information for QnABot to interact with to provide users' with relevant answers. + +## Solution architecture and how It works + +Three key AWS services are at the core of the solution: + +- **Amazon Lex** is a service for building conversational interfaces into any application using voice and text. Amazon Lex provides the advanced deep learning functionalities of automatic speech recognition (ASR) for converting speech to text, and natural language understanding (NLU) to recognize the intent of the text to enable you to build applications with highly engaging user experiences and lifelike conversational interactions. +- **Amazon ElasticSearch** is an open-source search and analytics engine for use cases such as log analytics, real-time application monitoring, and clickstream analysis. Amazon ElasticSearch is a managed service that makes it simple to deploy, operate, and scale Elasticsearch clusters in the AWS Cloud. The service offers open-source Amazon ElasticSearch APIs, managed Kibana, and integrations with Logstash and other AWS services, enabling you to securely ingest data from any source and search, analyze, and visualize it in real time. +- **Amazon Kendra** is an intelligent search service powered by machine learning. Kendra reimagines enterprise search for your websites and applications so your employees and customers can easily find the content they are looking for, even when it’s scattered across multiple locations and content repositories within your organization. + +Let's take a closer look at these three services and how they help power +the QnABot solution. + +![Solution architecture and data flow](image2.png) + +When you ask QnABot a question, a few things happen: + +1. The question gets processed and transcribed by Amazon Lex using a Natural Language Understanding (NLU) and Processing (NLP) engine. + - QnABot initially trains the NLP to match a wide variety of possible questions and statements, so that the Amazon Lex bot can accept just about any question a user might ask. The Amazon Lex interaction model is setup with: + - **Intents**: An intent represents an action that fulfills a user's spoken request. Intents can optionally have arguments called **slots**. The QnABot uses **slots** to capture user input and fulfills the Intent via Lambda function. + - **Sample utterances**: A set of likely spoken phrases mapped to the intents. This should include as many representative phrases as possible. The sample utterances specify the words and phrases users can say to invoke your intents. QnABot updates the **Sample utterances** with the various questions to train the chatbot to understand different user input +2. The Bot fulfillment Lambda function generates an ElasticSearch query containing the transcribed question. The query attempts to find the best match from all the questions and answers you’ve previously provided. +3. This request is then sent to Amazon ElasticSearch. QnABot attempts to match a user's question to the list of questions and answers (created in the QnABot content designer) stored in Amazon ElasticSearch. +4. (Optional) If an answer is not found in ElasticSearch and Amazon Kendra is configured, QnABot will search documents or web pages in your Kendra index. + +## Monitoring Usage + +![Kibana Dashboard](image9.png) + +QnABot includes a visualization tool (using Kibana) to analyze QnABot usage. Kibana is an open-source data visualization and exploration tool used for log and time-series analytics, application monitoring, and operational intelligence use cases. It offers powerful and easy-to-use features such as histograms, line graphs, pie charts, heat maps, and built-in geospatial support. Also, it provides tight integration with Elasticsearch, which makes Kibana the default choice for visualizing data stored in Elasticsearch. + +The Kibana dashboard can be used to view usage history, logged utterances, no hits utterances, positive user feedback, and negative user feedback and also provides the ability to create custom reports. diff --git a/docs/overview/image2.png b/docs/overview/image2.png new file mode 100755 index 0000000000000000000000000000000000000000..dbfa42f8d343497c0fa946165c6492a43a18d037 GIT binary patch literal 118886 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%0D!zvHpt28kb-3!vy250GVY5VKgSg0p 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 literal 0 HcmV?d00001 diff --git a/docs/overview/image9.png b/docs/overview/image9.png new file mode 100755 index 0000000000000000000000000000000000000000..e8988f714c5b6735dbe8eb9b54aec988584c16ec GIT binary patch literal 17457 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.schema.intents), - util.api({ - path:"/bot", - method:"GET" - }) - .then(x=>x.lambdaArn), - util.api({ - path:"/bot/utterances", - method:"GET" - }) -) -.spread(function(intents,lambda,utterances){ - skill.skillManifest.apis.custom.endpoint.uri=lambda - - intents.forEach(function(x){ - x.name=x.intent - delete x.intent - }) - model.interactionModel.languageModel.intents=intents - - model.interactionModel.languageModel.types[0] - .values=utterances.map(x=>{ return { - name:{value:x} - }}) - model.interactionModel.languageModel.intents[0].samples=["{QnA_slot}"] - - - return Promise.join( - fs.writeFileAsync(__dirname+'/files/model.json',JSON.stringify(model,null,2)), - fs.writeFileAsync(__dirname+'/files/skill.json',JSON.stringify(skill,null,2)) - ) -}) - - diff --git a/test-do-not-use-obsolete/alexa/test/.ask/config b/test-do-not-use-obsolete/alexa/test/.ask/config deleted file mode 100644 index e527ea5a2..000000000 --- a/test-do-not-use-obsolete/alexa/test/.ask/config +++ /dev/null @@ -1,19 +0,0 @@ -{ - "deploy_settings": { - "default": { - "skill_id": "amzn1.ask.skill.97607164-a819-4cc2-9b66-89bb00b11146", - "was_cloned": false, - "merge": { - "skillManifest": { - "apis": { - "custom": { - "endpoint": { - "uri": "ask-custom-test-default" - } - } - } - } - } - } - } -} diff --git a/test-do-not-use-obsolete/alexa/test/lambda/custom/index.js b/test-do-not-use-obsolete/alexa/test/lambda/custom/index.js deleted file mode 100644 index 0f43149c1..000000000 --- a/test-do-not-use-obsolete/alexa/test/lambda/custom/index.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; -var Alexa = require("alexa-sdk"); - -// For detailed tutorial on how to making a Alexa skill, -// please visit us at http://alexa.design/build - - -exports.handler = function(event, context) { - var alexa = Alexa.handler(event, context); - alexa.registerHandlers(handlers); - alexa.execute(); -}; - -var handlers = { - 'LaunchRequest': function () { - this.emit('SayHello'); - }, - 'HelloWorldIntent': function () { - this.emit('SayHello'); - }, - 'MyNameIsIntent': function () { - this.emit('SayHelloName'); - }, - 'SayHello': function () { - this.response.speak('Hello World!') - .cardRenderer('hello world', 'hello world'); - this.emit(':responseReady'); - }, - 'SayHelloName': function () { - var name = this.event.request.intent.slots.name.value; - this.response.speak('Hello ' + name) - .cardRenderer('hello world', 'hello ' + name); - this.emit(':responseReady'); - }, - 'SessionEndedRequest' : function() { - console.log('Session ended with reason: ' + this.event.request.reason); - }, - 'AMAZON.StopIntent' : function() { - this.response.speak('Bye'); - this.emit(':responseReady'); - }, - 'AMAZON.HelpIntent' : function() { - this.response.speak("You can try: 'alexa, hello world' or 'alexa, ask hello world my" + - " name is awesome Aaron'"); - this.emit(':responseReady'); - }, - 'AMAZON.CancelIntent' : function() { - this.response.speak('Bye'); - this.emit(':responseReady'); - }, - 'Unhandled' : function() { - this.response.speak("Sorry, I didn't get that. You can try: 'alexa, hello world'" + - " or 'alexa, ask hello world my name is awesome Aaron'"); - } -}; diff --git a/test-do-not-use-obsolete/alexa/test/lambda/custom/package.json b/test-do-not-use-obsolete/alexa/test/lambda/custom/package.json deleted file mode 100644 index 8df570155..000000000 --- a/test-do-not-use-obsolete/alexa/test/lambda/custom/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "skill-sample-nodejs-hello-world", - "version": "1.0.0", - "description": "Hello world sample skill", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "dependencies": { - "alexa-sdk": "^1.0.0" - } -} diff --git a/test-do-not-use-obsolete/alexa/test/models/en-US.json b/test-do-not-use-obsolete/alexa/test/models/en-US.json deleted file mode 100644 index e1ebf23a7..000000000 --- a/test-do-not-use-obsolete/alexa/test/models/en-US.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "interactionModel":{ - "languageModel":{ - "invocationName":"hello world", - "types":[], - "intents":[ - { - "name": "AMAZON.CancelIntent", - "samples": [] - }, - { - "name": "AMAZON.HelpIntent", - "samples": [] - }, - { - "name": "AMAZON.StopIntent", - "samples": [] - }, - { - "name":"HelloWorldIntent", - "slots":[ - - ], - "samples":[ - "hello", - "say hello", - "say hello world" - ] - }, - { - "name":"MyNameIsIntent", - "slots":[ - { - "name":"name", - "type":"AMAZON.US_FIRST_NAME" - } - ], - "samples":[ - "my name is {name}", - "i am {name}", - "you can call me {name}" - ] - } - ] - } - } -} diff --git a/test-do-not-use-obsolete/alexa/test/skill.json b/test-do-not-use-obsolete/alexa/test/skill.json deleted file mode 100644 index 3e4523c36..000000000 --- a/test-do-not-use-obsolete/alexa/test/skill.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "skillManifest": { - "publishingInformation": { - "locales": { - "en-US": { - "summary": "Sample Short Description", - "examplePhrases": [ - "Alexa open hello world", - "Alexa tell hello world I am Jeff", - "Alexa tell hello world my name is Peter" - ], - "name": "test", - "description": "Sample Full Description" - } - }, - "isAvailableWorldwide": true, - "testingInstructions": "Sample Testing Instructions.", - "category": "EDUCATION_AND_REFERENCE", - "distributionCountries": [] - }, - "apis": { - "custom": { - "endpoint": { - "sourceDir": "lambda/custom" - } - } - }, - "manifestVersion": "1.0" - } -} diff --git a/test-do-not-use-obsolete/alexa/util.js b/test-do-not-use-obsolete/alexa/util.js deleted file mode 100644 index 96c58723c..000000000 --- a/test-do-not-use-obsolete/alexa/util.js +++ /dev/null @@ -1,43 +0,0 @@ -var config=require('../../config') -process.env.AWS_PROFILE=config.profile -process.env.AWS_DEFAULT_REGION=config.region -var query=require('query-string').stringify -var _=require('lodash') -var Promise=require('bluebird') -var axios=require('axios') -var Url=require('url') -var sign=require('aws4').sign -var fs=require('fs') -var aws=require('aws-sdk') -aws.config.setPromisesDependency(Promise) -aws.config.region=config.region -var outputs=require('../../bin/exports') - - -exports.api=api -function api(opts){ - return outputs('dev/master',{wait:true}).then(function(output){ - var href=opts.path ? output.ApiEndpoint+'/'+opts.path : opts.href - var url=Url.parse(href) - var request={ - host:url.hostname, - method:opts.method.toUpperCase(), - url:url.href, - path:url.path, - headers:opts.headers || {} - } - if(opts.body){ - request.body=JSON.stringify(opts.body), - request.data=opts.body, - request.headers['content-type']='application/json' - } - - var credentials=aws.config.credentials - var signed=sign(request,credentials) - delete request.headers["Host"] - delete request.headers["Content-Length"] - - return Promise.resolve(axios(signed)) - .get('data') - }) -} diff --git a/test-do-not-use-obsolete/cfn/Dockerfile b/test-do-not-use-obsolete/cfn/Dockerfile deleted file mode 100644 index dc3879472..000000000 --- a/test-do-not-use-obsolete/cfn/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM selenium/standalone-chrome -LABEL authors=JohnCalhoun -USER root -RUN apt-get update && \ - apt-get upgrade -y - -# Set the timezone -RUN echo "Europe/Berlin" | tee /etc/timezone && \ - ln -fs /usr/share/zoneinfo/Europe/Berlin /etc/localtime && \ - dpkg-reconfigure -f noninteractive tzdata - -# Set the locale for UTF-8 support -RUN echo en_US.UTF-8 UTF-8 >> /etc/locale.gen && \ - locale-gen && \ - update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LANGUAGE en_US:en -ENV LC_ALL en_US.UTF-8 - -# AWS CLI needs the PYTHONIOENCODING environment varialbe to handle UTF-8 correctly: -ENV PYTHONIOENCODING=UTF-8 - -# man and less are needed to view 'aws help' -# ssh allows us to log in to new instances -# vim is useful to write shell scripts -# python* is needed to install aws cli using pip install - -RUN apt-get install -y \ - zip \ - git \ - curl \ - python \ - python-pip \ - python-virtualenv - -RUN pip install awscli -RUN curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash - && \ - apt-get install -y nodejs - diff --git a/test-do-not-use-obsolete/cfn/README.md b/test-do-not-use-obsolete/cfn/README.md deleted file mode 100644 index 176364348..000000000 --- a/test-do-not-use-obsolete/cfn/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# QnABot Test Cloudformation -Cloudformation assets to run tests in codebuild - -steps -- make test image that contains aws-cli,nodejs,selenium,and chrome -- use container to run all test diff --git a/test-do-not-use-obsolete/cfn/buildspec.yml b/test-do-not-use-obsolete/cfn/buildspec.yml deleted file mode 100644 index 597e64b26..000000000 --- a/test-do-not-use-obsolete/cfn/buildspec.yml +++ /dev/null @@ -1,18 +0,0 @@ -version: 0.2 - -phases: - pre_build: - commands: - - echo Logging in to Amazon ECR... - - $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION) - build: - commands: - - echo Build started on `date` - - echo Building the Docker image... - - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG . - - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG - post_build: - commands: - - echo Build completed on `date` - - echo Pushing the Docker image... - - docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG diff --git a/test-do-not-use-obsolete/cfn/config/buildspec.yml b/test-do-not-use-obsolete/cfn/config/buildspec.yml deleted file mode 100644 index 761462f17..000000000 --- a/test-do-not-use-obsolete/cfn/config/buildspec.yml +++ /dev/null @@ -1,19 +0,0 @@ -version: 0.2 - -phases: - install: - commands: - - npm install - pre_build: - commands: - - ./test/configure.sh - - ./test/setup.sh - build: - commands: - - ./test/test.sh - post_build: - commands: - - ./test/teardown.js -artifacts: - files: - - ./test/output/**/* diff --git a/test-do-not-use-obsolete/cfn/lambda/README.md b/test-do-not-use-obsolete/cfn/lambda/README.md deleted file mode 100644 index 751046146..000000000 --- a/test-do-not-use-obsolete/cfn/lambda/README.md +++ /dev/null @@ -1 +0,0 @@ -lambda functions for use in test script. they are written in to cloudformation template directly diff --git a/test-do-not-use-obsolete/cfn/lambda/build.js b/test-do-not-use-obsolete/cfn/lambda/build.js deleted file mode 100644 index f0bc7d173..000000000 --- a/test-do-not-use-obsolete/cfn/lambda/build.js +++ /dev/null @@ -1,64 +0,0 @@ -var response = require('cfn-response') -var aws=require('aws-sdk') -aws.config.region=process.env.AWS_REGION -var cb=new aws.CodeBuild() -var s3=new aws.S3() -var ecr=new aws.ECR() -var lambda=new aws.Lambda() - -exports.build = function(event, context) { - console.log(JSON.stringify(event,null,2)) - if(event.StackId){ - if(event.RequestType==="Create"){ - var params=Object.assign({},event.ResourceProperties) - delete params.ServiceToken - - cb.startBuild(params).promise() - .then(x=>new Promise((res,rej)=>setTimeout(()=>res(x),10*1000))) - .then(x=>{ - return lambda.invoke({ - FunctionName:process.env.AWS_LAMBDA_FUNCTION_NAME, - InvocationType:"Event", - Payload:JSON.stringify({ - event,context,id:x.build.id - }) - }).promise() - }) - .catch(x=>{ - console.log(x) - response.send(event, context, response.FAILED) - }) - }else{ - response.send(event, context, response.SUCCESS) - } - }else{ - event.context.done=context.done - cb.batchGetBuilds({ - ids:[event.id] - }).promise() - .then(x=>{ - console.log(x) - var status=x.builds[0].buildStatus - if(status==="SUCCEEDED"){ - response.send(event.event, event.context, response.SUCCESS) - }else if(status==="IN_PROGRESS"){ - setTimeout(()=>lambda.invoke({ - FunctionName:process.env.AWS_LAMBDA_FUNCTION_NAME, - InvocationType:"Event", - Payload:JSON.stringify({ - event:event.event, - context:event.context, - id:x.builds[0].id - }) - }).promise(),10*1000) - }else{ - response.send(event.event, event.context, response.FAILED) - } - }) - .catch(x=>{ - console.log(x) - response.send(event.event, event.context, response.FAILED) - }) - } -} - diff --git a/test-do-not-use-obsolete/cfn/lambda/clear.js b/test-do-not-use-obsolete/cfn/lambda/clear.js deleted file mode 100644 index cd34161a9..000000000 --- a/test-do-not-use-obsolete/cfn/lambda/clear.js +++ /dev/null @@ -1,55 +0,0 @@ -var response = require('cfn-response') -var aws=require('aws-sdk') -aws.config.region=process.env.AWS_REGION -var cb=new aws.CodeBuild() -var s3=new aws.S3() -var ecr=new aws.ECR() -var lambda=new aws.Lambda() - -exports.clear = function(event, context) { - console.log(JSON.stringify(event,null,2)) - - if(event.RequestType==="Delete"){ - Delete(event.ResourceProperties) - .then(()=>response.send(event, context, response.SUCCESS)) - .catch(x=>{ - console.log(x) - response.send(event, context, response.FAILED) - }) - }else{ - response.send(event, context, response.SUCCESS) - } -} - - -function Delete(params){ - return new Promise(function(res,rej){ - function next(){ - s3.listObjectsV2({ - Bucket:params.Bucket - }).promise() - .then(x=>x.Contents) - .then(function(files){ - return files.map(file=>{return { - Key:file.Key - } }) - }) - .then(function(keys){ - console.log("going to delete",keys) - if(keys.length>0){ - return s3.deleteObjects({ - Bucket:params.Bucket, - Delete:{ - Objects:keys - } - }).promise() - .then(()=>next()) - .catch(rej) - }else{ - res() - } - }) - } - next() - }) -} diff --git a/test-do-not-use-obsolete/cfn/lambda/clearImage.js b/test-do-not-use-obsolete/cfn/lambda/clearImage.js deleted file mode 100644 index e4ca7e394..000000000 --- a/test-do-not-use-obsolete/cfn/lambda/clearImage.js +++ /dev/null @@ -1,26 +0,0 @@ -var response = require('cfn-response') -var aws=require('aws-sdk') -aws.config.region=process.env.AWS_REGION -var cb=new aws.CodeBuild() -var s3=new aws.S3() -var ecr=new aws.ECR() -var lambda=new aws.Lambda() - -exports.clearImage = function(event, context) { - console.log(JSON.stringify(event,null,2)) - - if(event.RequestType==="Delete"){ - ecr.batchDeleteImage({ - imageIds:[{imageTag:event.ResourceProperties.tag}], - repositoryName:event.ResourceProperties.repo - }).promise() - .then(()=>response.send(event, context, response.SUCCESS)) - .catch(x=>{ - console.log(x) - response.send(event, context, response.SUCCESS) - }) - }else{ - response.send(event, context, response.SUCCESS) - } -} - diff --git a/test-do-not-use-obsolete/cfn/lambda/zip.js b/test-do-not-use-obsolete/cfn/lambda/zip.js deleted file mode 100644 index 139e1a742..000000000 --- a/test-do-not-use-obsolete/cfn/lambda/zip.js +++ /dev/null @@ -1,27 +0,0 @@ -var response = require('cfn-response') -var aws=require('aws-sdk') -aws.config.region=process.env.AWS_REGION -var cb=new aws.CodeBuild() -var s3=new aws.S3() -var ecr=new aws.ECR() -var lambda=new aws.Lambda() - -exports.zip = function(event, context) { - console.log(JSON.stringify(event,null,2)) - - if(event.RequestType==="Create"){ - s3.putObject({ - Bucket:event.ResourceProperties.bucket, - Key:event.ResourceProperties.key, - Body:new Buffer(event.ResourceProperties.body,"base64") - }).promise() - .then(x=>response.send(event,context,response.SUCCESS)) - .catch(x=>{ - console.log(x) - response.send(event, context, response.FAILED) - }) - }else{ - response.send(event, context, response.SUCCESS) - } -} - diff --git a/test-do-not-use-obsolete/cfn/test.js b/test-do-not-use-obsolete/cfn/test.js deleted file mode 100644 index 9d56dda1e..000000000 --- a/test-do-not-use-obsolete/cfn/test.js +++ /dev/null @@ -1,327 +0,0 @@ -var fs=require('fs') -var Promise=require('bluebird') -var child=Promise.promisifyAll(require('child_process')) -var JSZip = require("jszip"); -var zip=new JSZip() -var package=require('../../package.json') -zip.file('buildspec.yml',fs.readFileSync(__dirname+'/buildspec.yml','utf-8')) -zip.file('Dockerfile',fs.readFileSync(__dirname+'/Dockerfile','utf-8')) - -var tag="test" -var source="source.zip" -async function run(){ - var buff=await Promise.resolve(zip.generateAsync({type:'nodebuffer'})) - var info=await child.execAsync('git rev-parse --symbolic-full-name --abbrev-ref @{u}',{ - cwd:__dirname - }) - var info_parse=info.match(/(.*)\/(.*)/) - var remote=info_parse[1] - var branch=info_parse[2] - - var remote_info=await child.execAsync(`git remote get-url ${remote}`,{ - cwd:__dirname - }) - - var path=remote_info.match(/(.*):(.*)/)[2] - var url=`https://github.com/${path}` - - return { - "Description": "This template creates test infastructure for testing QnABot", - "Resources":{ - "Repo":{ - "Type" : "AWS::ECR::Repository", - "Properties":{ - RepositoryPolicyText:{ - "Version": "2012-10-17", - "Statement": [{ - "Effect": "Allow", - "Principal": { - "Service": "codebuild.amazonaws.com" - }, - "Action": [ - "ecr:GetDownloadUrlForLayer", - "ecr:BatchGetImage", - "ecr:BatchCheckLayerAvailability" - ] - }] - } - } - }, - "testBuild":{ - "Type":"AWS::CodeBuild::Project", - "Properties":{ - "Artifacts":{ - Type:"S3", - Location:{"Ref":"Bucket"} - }, - "Environment":{ - ComputeType:"BUILD_GENERAL1_LARGE", - Image:{"Fn::Sub":"${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repo}:"+tag}, - EnvironmentVariables:[], - Type:"LINUX_CONTAINER" - }, - "Name":{"Fn::Sub":"${AWS::StackName}-test-build"}, - ServiceRole:{"Ref":"TestServiceRole"}, - Source:{ - Type:"GITHUB", - Location:url, - Auth:{ - Type:"OAUTH" - } - }, - TimeoutInMinutes:4*60 - } - }, - "ImageBuild":{ - "Type":"AWS::CodeBuild::Project", - "Properties":{ - "Artifacts":{ - Type:"NO_ARTIFACTS" - }, - "Environment":{ - ComputeType:"BUILD_GENERAL1_LARGE", - EnvironmentVariables:[{ - Name:"IMAGE_REPO_NAME", - Value:{"Ref":"Repo"} - },{ - Name:"IMAGE_TAG", - Value:tag, - },{ - Name:"AWS_DEFAULT_REGION", - Value:{"Ref":"AWS::Region"} - },{ - Name:"AWS_ACCOUNT_ID", - Value:{"Ref":"AWS::AccountId"} - }], - Image:"aws/codebuild/docker:17.09.0", - PrivilegedMode:true, - Type:"LINUX_CONTAINER" - }, - "Name":{"Fn::Sub":"${AWS::StackName}-test-image-build"}, - ServiceRole:{"Ref":"ServiceRole"}, - Source:{ - Type:"S3", - Location:{"Fn::Sub":"${Bucket}/"+source} - } - } - }, - "Bucket": { - "Type": "AWS::S3::Bucket", - "Properties": {} - }, - "Clear":{ - "Type": "Custom::S3Clear", - "DependsOn":["Bucket"], - "Properties": { - "ServiceToken": { "Fn::GetAtt" : ["ClearLambda", "Arn"] }, - "Bucket":{"Ref":"Bucket"} - } - }, - "ClearImage":{ - "Type": "Custom::ClearImage", - "DependsOn":["Bucket"], - "Properties": { - "ServiceToken": { "Fn::GetAtt" : ["ClearImageLambda", "Arn"] }, - "repo":{"Ref":"Repo"}, - "tag":tag - } - }, - "upload":{ - "Type": "Custom::S3Upload", - "Properties": { - "ServiceToken": { "Fn::GetAtt" : ["UploadLambda", "Arn"] }, - "bucket":{"Ref":"Bucket"}, - "key":source, - "body":buff.toString('base64') - } - }, - "Build":{ - "Type": "Custom::CodeBuildStart", - "DependsOn":["upload"], - "Properties": { - "ServiceToken": { "Fn::GetAtt" : ["BuildLambda", "Arn"] }, - "projectName":{"Ref":"ImageBuild"} - } - }, - "test":{ - "Type": "Custom::CodeBuildStart", - "DependsOn":["Build"], - "Properties": { - "ServiceToken": { "Fn::GetAtt" : ["BuildLambda", "Arn"] }, - "projectName":{"Ref":"testBuild"}, - "sourceVersion":branch, - "buildspecOverride":fs.readFileSync( - __dirname+'/config/buildspec.yml','utf-8') - } - }, - "ClearImageLambda":lambda("clearImage"), - "BuildLambda": lambda("build"), - "UploadLambda": lambda("zip"), - "ClearLambda": lambda("clear"), - "TestServiceRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": "codebuild.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - }, - "Path": "/", - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/AdministratorAccess" - ], - } - }, - "ServiceRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": "codebuild.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - }, - "Path": "/", - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/AmazonECS_FullAccess", - "arn:aws:iam::aws:policy/AmazonS3FullAccess" - ], - "Policies":[{ - "PolicyName":"codebuild", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "CloudWatchLogsPolicy", - "Effect": "Allow", - "Action": [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ], - "Resource": [ - "*" - ] - }, - { - "Sid": "CodeCommitPolicy", - "Effect": "Allow", - "Action": [ - "codecommit:GitPull", - "ecr:*" - ], - "Resource": [ - "*" - ] - }, - { - "Sid": "S3GetObjectPolicy", - "Effect": "Allow", - "Action": [ - "s3:GetObject", - "s3:GetObjectVersion" - ], - "Resource": [ - "*" - ] - }, - { - "Sid": "S3PutObjectPolicy", - "Effect": "Allow", - "Action": [ - "s3:PutObject" - ], - "Resource": [ - "*" - ] - } - ] - } - }] - } - }, - "LambdaRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": { - "Service": "lambda.amazonaws.com" - }, - "Action": "sts:AssumeRole" - } - ] - }, - "Path": "/", - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - "arn:aws:iam::aws:policy/AmazonECS_FullAccess", - "arn:aws:iam::aws:policy/AmazonS3FullAccess", - "arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess" - ], - "Policies":[{ - "PolicyName":"codebuild", - "PolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "lambda:InvokeFunction", - "ecr:*" - ], - "Resource": [ - "*" - ] - } - ] - } - }] - } - } - }, - "Outputs": { - "BuildConsole":{ - "Value":{"Fn::Sub":"https://console.aws.amazon.com/codebuild/home?region=${AWS::Region}#/projects/${testBuild}/view"} - }, - "ImageConsole":{ - "Value":{"Fn::Sub":"https://console.aws.amazon.com/codebuild/home?region=${AWS::Region}#/projects/${ImageBuild}/view"} - } - } - } -} -run() -module.exports=run() - - -function lambda(name){ - return { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile":fs.readFileSync(__dirname+`/lambda/${name}.js`,'utf-8') - }, - "Handler": "index."+name, - "MemorySize": "128", - "Role": {"Fn::GetAtt": ["LambdaRole","Arn"]}, - "Runtime": "nodejs12.x", - "Timeout": 300 - } - } -} diff --git a/test-do-not-use-obsolete/configure.sh b/test-do-not-use-obsolete/configure.sh deleted file mode 100755 index a39841f26..000000000 --- a/test-do-not-use-obsolete/configure.sh +++ /dev/null @@ -1,38 +0,0 @@ -#! /bin/bash -__dirname="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -BASE=$__dirname/.. -BIN=$__dirname/../bin -cd $BASE - -PROFILE="default" -NAMESPACE="test" - -if aws s3 ls --profile $PROFILE >> /dev/null; then - echo "aws cli configured" -else - echo "configuring aws cli" - region=$AWS_REGION - creds=$(curl 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) - - aws configure set aws_access_key_id $( echo $creds | $BIN/json.js AccessKeyId) - aws configure set aws_secret_access_key $( echo $creds | $BIN/json.js SecretAccessKey) - aws configure set aws_session_token $( echo $creds | $BIN/json.js Token ) - aws configure set $PROFILE.region $region -fi -aws configure list --profile $PROFILE - -if [ ! -f ./config.json ]; then - CLI_REGION=$(aws configure get region --profile $PROFILE) - echo "creating config" - - node $BIN/config.js john@example.com $CLI_REGION \ - | $BIN/json.js -e "this.profile='$PROFILE'" \ - | $BIN/json.js -e "this.namespace='$NAMESPACE'" \ - > $BASE/config.json -fi - -if [ -n "$ASK_CREDENTIALS" ]; then - echo "$ASK_CREDENTIALS" >> ~/.ask/cli_config -fi - - diff --git a/test-do-not-use-obsolete/kendra_tests/context_FAQ_md.json b/test-do-not-use-obsolete/kendra_tests/context_FAQ_md.json deleted file mode 100644 index 9fa21fa26..000000000 --- a/test-do-not-use-obsolete/kendra_tests/context_FAQ_md.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "callbackWaitsForEmptyEventLoop": true, - "functionVersion": "$LATEST", - "functionName": "QNA-dev-dev-dev-master-2-Example-EXTKendraFallback-1NKAGNPBF6VVY", - "memoryLimitInMB": "2048", - "logGroupName": "/aws/lambda/QNA-dev-dev-dev-master-2-Example-EXTKendraFallback-1NKAGNPBF6VVY", - "logStreamName": "2020/07/02/[$LATEST]e5eaf1b6bade474a82aa21a378b9a71f", - "invokedFunctionArn": "arn:aws:lambda:us-east-1:425742325899:function:QNA-dev-dev-dev-master-2-Example-EXTKendraFallback-1NKAGNPBF6VVY", - "awsRequestId": "a0665572-c2b8-424f-a22e-502f6bb317cb" -} diff --git a/test-do-not-use-obsolete/kendra_tests/context_doc_query.json b/test-do-not-use-obsolete/kendra_tests/context_doc_query.json deleted file mode 100644 index 5e1065214..000000000 --- a/test-do-not-use-obsolete/kendra_tests/context_doc_query.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "callbackWaitsForEmptyEventLoop": true, - "functionVersion": "$LATEST", - "functionName": "QNA-dev-dev-dev-master-1-Example-EXTKendraFallback-1CKOPNQ8CI049", - "memoryLimitInMB": "2048", - "logGroupName": "/aws/lambda/QNA-dev-dev-dev-master-1-Example-EXTKendraFallback-1CKOPNQ8CI049", - "logStreamName": "2020/06/25/[$LATEST]98a19ebfac874a36b5d450ec4726a6a3", - "invokedFunctionArn": "arn:aws:lambda:us-east-1:425742325899:function:QNA-dev-dev-dev-master-1-Example-EXTKendraFallback-1CKOPNQ8CI049", - "awsRequestId": "c6dd6ab6-4ab0-4839-931b-2418beded229" -} diff --git a/test-do-not-use-obsolete/kendra_tests/context_top_ans.json b/test-do-not-use-obsolete/kendra_tests/context_top_ans.json deleted file mode 100644 index f7b1fede8..000000000 --- a/test-do-not-use-obsolete/kendra_tests/context_top_ans.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "callbackWaitsForEmptyEventLoop": true, - "functionVersion": "$LATEST", - "functionName": "QNAbot-dev-dev-dev-master-4-Exam-EXTKendraFallback-T1KJ602HXNIW", - "memoryLimitInMB": "2048", - "logGroupName": "/aws/lambda/QNAbot-dev-dev-dev-master-4-Exam-EXTKendraFallback-T1KJ602HXNIW", - "logStreamName": "2020/06/24/[$LATEST]a70c615cee674b69a916f679fcdc4459", - "invokedFunctionArn": "arn:aws:lambda:us-east-1:425742325899:function:QNAbot-dev-dev-dev-master-4-Exam-EXTKendraFallback-T1KJ602HXNIW", - "awsRequestId": "55459ee4-3f84-470d-ae35-399ce38a333f" -} - diff --git a/test-do-not-use-obsolete/kendra_tests/event_FAQ_md.json b/test-do-not-use-obsolete/kendra_tests/event_FAQ_md.json deleted file mode 100644 index 3c6c071e4..000000000 --- a/test-do-not-use-obsolete/kendra_tests/event_FAQ_md.json +++ /dev/null @@ -1,135 +0,0 @@ -{ - "req": { - "_event": { - "messageVersion": "1.0", - "invocationSource": "FulfillmentCodeHook", - "userId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "sessionAttributes": {}, - "requestAttributes": null, - "bot": { - "name": "QNA_dev_dev_dev_master_two_BotUSFgl", - "alias": "live", - "version": "1" - }, - "outputDialogMode": "Text", - "currentIntent": { - "name": "fulfilment_IntentfiuhxvkSil", - "slots": { - "slot": "How many planets orbit the sun" - }, - "slotDetails": { - "slot": { - "resolutions": [], - "originalValue": "How many planets orbit the sun" - } - }, - "confirmationStatus": "None" - }, - "inputTranscript": "How many planets orbit the sun?", - "recentIntentSummaryView": null, - "sentimentResponse": null, - "kendraResponse": null, - "errorFound": false - }, - "_settings": { - "ENABLE_DEBUG_RESPONSES": "false", - "ES_USE_KEYWORD_FILTERS": "true", - "ES_NO_HITS_QUESTION": "no_hits", - "ES_USE_FUZZY_MATCH": "false", - "ES_KEYWORD_SYNTAX_TYPES": "NOUN,PROPN,VERB,INTJ", - "ES_SYNTAX_CONFIDENCE_LIMIT": ".20", - "ES_MINIMUM_SHOULD_MATCH": "2<75%", - "ES_PHRASE_BOOST": "4", - "ES_SCORE_ANSWER_FIELD": "false", - "ERRORMESSAGE": "Unfortunately I encountered an error when searching for your answer. Please ask me again later.", - "EMPTYMESSAGE": "You stumped me! Sadly I don't know how to answer your question.", - "DEFAULT_ALEXA_LAUNCH_MESSAGE": "Hello, Please ask a question", - "DEFAULT_ALEXA_STOP_MESSAGE": "Goodbye", - "SMS_HINT_REMINDER_ENABLE": "true", - "SMS_HINT_REMINDER": " (Feedback? Reply THUMBS UP or THUMBS DOWN. Ask HELP ME at any time)", - "SMS_HINT_REMINDER_INTERVAL_HRS": "24", - "IDENTITY_PROVIDER_JWKS_URLS": [], - "ENABLE_MULTI_LANGUAGE_SUPPORT": "false", - "MINIMUM_CONFIDENCE_SCORE": 0.6, - "ALT_SEARCH_KENDRA_INDEXES": "[\"e1c23860-e5c8-4409-ae26-b05bd6ced00a\"]", - "ELICIT_RESPONSE_MAX_RETRIES": 3, - "ELICIT_RESPONSE_RETRY_MESSAGE": "Please try again?", - "ELICIT_RESPONSE_BOT_FAILURE_MESSAGE": "Your response was not understood. Please start again.", - "ELICIT_RESPONSE_DEFAULT_MSG": "Ok. ", - "ENABLE_REDACTING": "false", - "REDACTING_REGEX": "\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b", - "DEFAULT_USER_POOL_JWKS_URL": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_jt2cXkCEb/.well-known/jwks.json" - }, - "_type": "LEX", - "_userId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "question": "How many planets orbit the sun?", - "session": {}, - "_preferredResponseType": "PlainText", - "_userInfo": { - "UserId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "InteractionCount": 0, - "TimeSinceLastInteraction": 1593663418.209 - }, - "_info": { - "es": { - "address": "search-qna-dev-elasti-mtl6ojldf3ja-w5begr25vpfuyidnlgb4izlbtq.us-east-1.es.amazonaws.com", - "index": "qna-dev-dev-dev-master-2", - "type": "qna", - "service": { - "qid": "QNA-dev-dev-dev-master-2-ESQidLambda-1I9JEHU6U4K9I", - "proxy": "QNA-dev-dev-dev-master-2-ESProxyLambda-1NMAY6LNBRJDY" - } - } - } - }, - "res": { - "type": "PlainText", - "message": "The Kendra Fallback search was not able to identify any results", - "session": { - "appContext": { - "altMessages": {} - }, - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "How many planets orbit the sun?" - }, - "navigation": { - "next": "", - "previous": [], - "hasParent": true - } - }, - "card": { - "send": false, - "title": "", - "text": "", - "url": "" - }, - "_userInfo": { - "UserId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "InteractionCount": 1, - "TimeSinceLastInteraction": 1593663418.209, - "FirstSeen": "Thu Jul 02 2020 16:16:58 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Thu Jul 02 2020 16:16:58 GMT+0000 (Coordinated Universal Time)" - }, - "got_hits": 0, - "result": { - "qid": "KendraFallback", - "quniqueterms": " no_hits ", - "questions": [ - { - "q": "no_hits" - } - ], - "a": "The Kendra Fallback search was not able to identify any results", - "l": "QNA:EXTKendraFallback", - "type": "qna", - "autotranslate": { - "a": true - } - }, - "plainMessage": "The Kendra Fallback search was not able to identify any results" - } -} diff --git a/test-do-not-use-obsolete/kendra_tests/event_doc_query.json b/test-do-not-use-obsolete/kendra_tests/event_doc_query.json deleted file mode 100644 index ed91ded36..000000000 --- a/test-do-not-use-obsolete/kendra_tests/event_doc_query.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "req": { - "_event": { - "messageVersion": "1.0", - "invocationSource": "FulfillmentCodeHook", - "userId": "us-east-1:36231782-ac61-483c-b4ab-13c20c1efb57", - "sessionAttributes": { - "kendraResponsibleQid": "KendraFallback", - "navigation": "{\"next\":\"\",\"previous\":[],\"hasParent\":true}", - "previous": "{\"qid\":\"KendraFallback\",\"a\":\"The Kendra Fallback search was not able to identify any results\",\"alt\":{},\"q\":\"How many regions does the sun have?\"}", - "qnabotcontext": "{\"kendra\":{\"kendraIndexId\":\"e1c23860-e5c8-4409-ae26-b05bd6ced00a\", \"kendraQueryId\":\"9b04b1d3-2409-43de-9d07-8c0ce3979e37\", \"kendraResultId\":\"9b04b1d3-2409-43de-9d07-8c0ce3979e37-2f0c0b44-bd10-4f23-9969-702e97a2286a\"}}" - }, - "requestAttributes": null, - "bot": { - "name": "QNA_dev_dev_dev_master_one_BotpvdFf", - "alias": "live", - "version": "1" - }, - "outputDialogMode": "Text", - "currentIntent": { - "name": "fulfilment_IntentDOKFOzfCnr", - "slots": { - "slot": "What is the composition of the sun" - }, - "slotDetails": { - "slot": { - "resolutions": [], - "originalValue": "What is the composition of the sun" - } - }, - "confirmationStatus": "None" - }, - "inputTranscript": "What is the composition of the sun?", - "recentIntentSummaryView": [ - { - "intentName": "fulfilment_IntentDOKFOzfCnr", - "checkpointLabel": null, - "slots": { - "slot": "How many regions does the sun have" - }, - "confirmationStatus": "None", - "dialogActionType": "Close", - "fulfillmentState": "Fulfilled", - "slotToElicit": null - } - ], - "sentimentResponse": null, - "kendraResponse": null, - "errorFound": false - }, - "_settings": { - "ENABLE_DEBUG_RESPONSES": "false", - "ES_USE_KEYWORD_FILTERS": "true", - "ES_NO_HITS_QUESTION": "no_hits", - "ES_USE_FUZZY_MATCH": "false", - "ES_KEYWORD_SYNTAX_TYPES": "NOUN,PROPN,VERB,INTJ", - "ES_SYNTAX_CONFIDENCE_LIMIT": ".20", - "ES_MINIMUM_SHOULD_MATCH": "2<75%", - "ES_PHRASE_BOOST": "4", - "ES_SCORE_ANSWER_FIELD": "false", - "ERRORMESSAGE": "Unfortunately I encountered an error when searching for your answer. Please ask me again later.", - "EMPTYMESSAGE": "You stumped me! Sadly I don't know how to answer your question.", - "DEFAULT_ALEXA_LAUNCH_MESSAGE": "Hello, Please ask a question", - "DEFAULT_ALEXA_STOP_MESSAGE": "Goodbye", - "SMS_HINT_REMINDER_ENABLE": "true", - "SMS_HINT_REMINDER": " (Feedback? Reply THUMBS UP or THUMBS DOWN. Ask HELP ME at any time)", - "SMS_HINT_REMINDER_INTERVAL_HRS": "24", - "IDENTITY_PROVIDER_JWKS_URLS": [], - "ENABLE_MULTI_LANGUAGE_SUPPORT": "false", - "MINIMUM_CONFIDENCE_SCORE": 0.6, - "ALT_SEARCH_KENDRA_INDEXES": "[\"e1c23860-e5c8-4409-ae26-b05bd6ced00a\"]", - "ELICIT_RESPONSE_MAX_RETRIES": 3, - "ELICIT_RESPONSE_RETRY_MESSAGE": "Please try again?", - "ELICIT_RESPONSE_BOT_FAILURE_MESSAGE": "Your response was not understood. Please start again.", - "ELICIT_RESPONSE_DEFAULT_MSG": "Ok. ", - "ENABLE_REDACTING": "false", - "REDACTING_REGEX": "\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b", - "DEFAULT_USER_POOL_JWKS_URL": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_asIXCtVxR/.well-known/jwks.json" - }, - "_type": "LEX", - "_userId": "us-east-1:36231782-ac61-483c-b4ab-13c20c1efb57", - "question": "What is the composition of the sun?", - "session": { - "navigation": { - "next": "", - "previous": [], - "hasParent": true - }, - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "How many regions does the sun have?" - }, - "qnabotcontext": { - "kendra":{ - "kendraIndexId":"e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId":"9b04b1d3-2409-43de-9d07-8c0ce3979e37", - "kendraResultId":"9b04b1d3-2409-43de-9d07-8c0ce3979e37-2f0c0b44-bd10-4f23-9969-702e97a2286a", - "kendraResponsibleQid": "KendraFallback" - } - } - }, - "_preferredResponseType": "PlainText", - "_userInfo": { - "InteractionCount": 3, - "UserId": "us-east-1:36231782-ac61-483c-b4ab-13c20c1efb57", - "FirstSeen": "Thu Jun 25 2020 16:59:02 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Thu Jun 25 2020 16:59:28 GMT+0000 (Coordinated Universal Time)", - "TimeSinceLastInteraction": 11.653 - }, - "_info": { - "es": { - "address": "search-qna-dev-elasti-43wg8xxw528o-hrcx54m7voo7lncykifol6ucuy.us-east-1.es.amazonaws.com", - "index": "qna-dev-dev-dev-master-1", - "type": "qna", - "service": { - "qid": "QNA-dev-dev-dev-master-1-ESQidLambda-1WWM866K5R7G5", - "proxy": "QNA-dev-dev-dev-master-1-ESProxyLambda-F87F2OUBD8KW" - } - } - } - }, - "res": { - "type": "PlainText", - "message": "The Kendra Fallback search was not able to identify any results", - "session": { - "kendraResponsibleQid": "KendraFallback", - "navigation": { - "next": "", - "previous": [], - "hasParent": true - }, - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "What is the composition of the sun?" - }, - "kendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId": "9b04b1d3-2409-43de-9d07-8c0ce3979e37", - "kendraResultId": "9b04b1d3-2409-43de-9d07-8c0ce3979e37-2f0c0b44-bd10-4f23-9969-702e97a2286a", - "appContext": { - "altMessages": {} - } - }, - "card": { - "send": false, - "title": "", - "text": "", - "url": "" - }, - "_userInfo": { - "InteractionCount": 4, - "UserId": "us-east-1:36231782-ac61-483c-b4ab-13c20c1efb57", - "FirstSeen": "Thu Jun 25 2020 16:59:02 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Thu Jun 25 2020 16:59:39 GMT+0000 (Coordinated Universal Time)", - "TimeSinceLastInteraction": 11.653 - }, - "got_hits": 0, - "result": { - "qid": "KendraFallback", - "quniqueterms": " no_hits ", - "questions": [ - { - "q": "no_hits" - } - ], - "a": "The Kendra Fallback search was not able to identify any results", - "l": "QNA:EXTKendraFallback", - "type": "qna", - "autotranslate": { - "a": true - } - }, - "plainMessage": "The Kendra Fallback search was not able to identify any results" - } -} - diff --git a/test-do-not-use-obsolete/kendra_tests/event_top_ans.json b/test-do-not-use-obsolete/kendra_tests/event_top_ans.json deleted file mode 100644 index dc3b499bd..000000000 --- a/test-do-not-use-obsolete/kendra_tests/event_top_ans.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "req": { - "_event": { - "messageVersion": "1.0", - "invocationSource": "FulfillmentCodeHook", - "userId": "us-east-1:4f20bd96-a761-4541-a1d9-fcbb0e51c236", - "sessionAttributes": { - "navigation": "{\"next\":\"\",\"previous\":[],\"hasParent\":true}", - "userLocale": "en", - "previous": "{\"qid\":\"KendraFallback\",\"a\":\"The Kendra Fallback search was not able to identify any results\",\"alt\":{},\"q\":\"How many layers does the sun have?\"}", - "userDetectedLocaleConfidence": "0.9988220930099487", - "qnabotcontext": { - "kendra":{ - "kendraIndexId":"e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId":"09c519d4-4b9a-4355-a586-e25e64285c4f", - "kendraResultId":"09c519d4-4b9a-4355-a586-e25e64285c4f-3527f152-b335-4619-a934-b5d7eecd1982", - "kendraResponsibleQid": "KendraFallback" - } - }, - "userDetectedLocale": "en" - }, - "requestAttributes": null, - "bot": { - "name": "QNAbot_dev_dev_dev_master_four_BotoNDIv", - "alias": "live", - "version": "1" - }, - "outputDialogMode": "Text", - "currentIntent": { - "name": "fulfilment_IntentrDEldIYchy", - "slots": { - "slot": "What is the closest star to the earth" - }, - "slotDetails": { - "slot": { - "resolutions": [], - "originalValue": "What is the closest star to the earth" - } - }, - "confirmationStatus": "None" - }, - "inputTranscript": "What is the closest star to the earth?", - "recentIntentSummaryView": null, - "sentimentResponse": null, - "kendraResponse": null, - "origQuestion": "What is the closest star to the earth?", - "errorFound": false - }, - "_settings": { - "ENABLE_DEBUG_RESPONSES": "false", - "ES_USE_KEYWORD_FILTERS": "true", - "ES_NO_HITS_QUESTION": "no_hits", - "ES_USE_FUZZY_MATCH": "false", - "ES_KEYWORD_SYNTAX_TYPES": "NOUN,PROPN,VERB,INTJ", - "ES_SYNTAX_CONFIDENCE_LIMIT": ".20", - "ES_MINIMUM_SHOULD_MATCH": "2<75%", - "ES_PHRASE_BOOST": "4", - "ES_SCORE_ANSWER_FIELD": "false", - "ERRORMESSAGE": "Unfortunately I encountered an error when searching for your answer. Please ask me again later.", - "EMPTYMESSAGE": "You stumped me! Sadly I don't know how to answer your question.", - "DEFAULT_ALEXA_LAUNCH_MESSAGE": "Hello, Please ask a question", - "DEFAULT_ALEXA_STOP_MESSAGE": "Goodbye", - "SMS_HINT_REMINDER_ENABLE": "true", - "SMS_HINT_REMINDER": " (Feedback? Reply THUMBS UP or THUMBS DOWN. Ask HELP ME at any time)", - "SMS_HINT_REMINDER_INTERVAL_HRS": "24", - "IDENTITY_PROVIDER_JWKS_URLS": [], - "ENABLE_MULTI_LANGUAGE_SUPPORT": "true", - "MINIMUM_CONFIDENCE_SCORE": 0.6, - "ALT_SEARCH_KENDRA_INDEXES": "[\"e1c23860-e5c8-4409-ae26-b05bd6ced00a\"]", - "ELICIT_RESPONSE_MAX_RETRIES": 3, - "ELICIT_RESPONSE_RETRY_MESSAGE": "Please try again?", - "ELICIT_RESPONSE_BOT_FAILURE_MESSAGE": "Your response was not understood. Please start again.", - "ELICIT_RESPONSE_DEFAULT_MSG": "Ok. ", - "ENABLE_REDACTING": "false", - "REDACTING_REGEX": "\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b", - "DEFAULT_USER_POOL_JWKS_URL": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_EZg5Ia82Z/.well-known/jwks.json" - }, - "_type": "LEX", - "_userId": "us-east-1:4f20bd96-a761-4541-a1d9-fcbb0e51c236", - "question": "What is the closest star to the earth?", - "session": { - "kendraResponsibleQid": "KendraFallback", - "navigation": { - "next": "", - "previous": [], - "hasParent": true - }, - "userLocale": "en", - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "How many layers does the sun have?" - }, - "userDetectedLocaleConfidence": 0.9990218877792358, - "kendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId": "09c519d4-4b9a-4355-a586-e25e64285c4f", - "userDetectedLocale": "en", - "kendraResultId": "09c519d4-4b9a-4355-a586-e25e64285c4f-3527f152-b335-4619-a934-b5d7eecd1982" - }, - "_preferredResponseType": "PlainText", - "_userInfo": { - "InteractionCount": 1, - "UserId": "us-east-1:4f20bd96-a761-4541-a1d9-fcbb0e51c236", - "FirstSeen": "Wed Jun 24 2020 17:18:02 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Wed Jun 24 2020 17:18:02 GMT+0000 (Coordinated Universal Time)", - "TimeSinceLastInteraction": 746.572 - }, - "_info": { - "es": { - "address": "search-qnabot-elasti-4n2sjlh1tlwf-ziyzyejzynvs6rs2cvx7yaqxma.us-east-1.es.amazonaws.com", - "index": "qnabot-dev-dev-dev-master-4", - "type": "qna", - "service": { - "qid": "QNAbot-dev-dev-dev-master-4-ESQidLambda-636ARD1AGUJH", - "proxy": "QNAbot-dev-dev-dev-master-4-ESProxyLambda-NF5QMNB8RE00" - } - } - } - }, - "res": { - "type": "PlainText", - "message": "The Kendra Fallback search was not able to identify any results", - "session": { - "kendraResponsibleQid": "KendraFallback", - "navigation": { - "next": "", - "previous": [], - "hasParent": true - }, - "userLocale": "en", - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "What is the closest star to the earth?" - }, - "userDetectedLocaleConfidence": 0.9990218877792358, - "kendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId": "09c519d4-4b9a-4355-a586-e25e64285c4f", - "userDetectedLocale": "en", - "kendraResultId": "09c519d4-4b9a-4355-a586-e25e64285c4f-3527f152-b335-4619-a934-b5d7eecd1982", - "appContext": { - "altMessages": {} - } - }, - "card": { - "send": false, - "title": "", - "text": "", - "url": "" - }, - "_userInfo": { - "InteractionCount": 2, - "UserId": "us-east-1:4f20bd96-a761-4541-a1d9-fcbb0e51c236", - "FirstSeen": "Wed Jun 24 2020 17:18:02 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Wed Jun 24 2020 17:30:28 GMT+0000 (Coordinated Universal Time)", - "TimeSinceLastInteraction": 746.572 - }, - "got_hits": 0, - "result": { - "qid": "KendraFallback", - "quniqueterms": " no_hits ", - "questions": [ - { - "q": "no_hits" - } - ], - "a": "The Kendra Fallback search was not able to identify any results", - "l": "QNA:EXTKendraFallback", - "type": "qna", - "autotranslate": { - "a": true - } - }, - "plainMessage": "The Kendra Fallback search was not able to identify any results" - } -} diff --git a/test-do-not-use-obsolete/kendra_tests/kendraData_FAQ_md.json b/test-do-not-use-obsolete/kendra_tests/kendraData_FAQ_md.json deleted file mode 100644 index c2d3098da..000000000 --- a/test-do-not-use-obsolete/kendra_tests/kendraData_FAQ_md.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "QueryId": "b904542d-af85-447d-808b-d86b5df16c06", - "ResultItems": [ - { - "Id": "b904542d-af85-447d-808b-d86b5df16c06-ee0db51d-15f4-4786-9144-b51d29d37f66", - "Type": "QUESTION_ANSWER", - "AdditionalAttributes": [ - { - "Key": "QuestionText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "How many planets orbit the Sun?", - "Highlights": [ - { - "BeginOffset": 4, - "EndOffset": 8, - "TopAnswer": false - }, - { - "BeginOffset": 9, - "EndOffset": 16, - "TopAnswer": false - }, - { - "BeginOffset": 17, - "EndOffset": 22, - "TopAnswer": false - }, - { - "BeginOffset": 27, - "EndOffset": 30, - "TopAnswer": false - } - ] - } - } - }, - { - "Key": "AnswerText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.", - "Highlights": [ - { - "BeginOffset": 6, - "EndOffset": 13, - "TopAnswer": false - }, - { - "BeginOffset": 14, - "EndOffset": 19, - "TopAnswer": false - }, - { - "BeginOffset": 24, - "EndOffset": 27, - "TopAnswer": false - } - ] - } - } - } - ], - "DocumentId": "a212c9bbe37131a9777efc915d837915ee33e5b327ec7df4645cb96d6d165f4a1593702980960", - "DocumentTitle": { - "Text": "" - }, - "DocumentExcerpt": { - "Text": "Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.", - "Highlights": [ - { - "BeginOffset": 0, - "EndOffset": 147, - "TopAnswer": false - } - ] - }, - "DocumentURI": "{\"args\":[\"\"],\"next\":\"\",\"a\":\"Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.\",\"r\":{\"buttons\":[{\"text\":\"What is a solar flare?\",\"value\":\"What is a solar flare\"}],\"subTitle\":\"\",\"imageUrl\":\"\",\"title\":\"Suggestions\",\"text\":\"\",\"url\":\"\"},\"t\":\"\",\"alt\":{\"markdown\":\"\",\"ssml\":\"\"},\"l\":\"\",\"qid\":\"sun.10\",\"type\":\"qna\",\"q\":[\"How many planets orbit the Sun?\",\"Which planets orbit the Sun?\"]}", - "DocumentAttributes": [ - { - "Key": "_source_uri", - "Value": { - "StringValue": "{\"args\":[\"\"],\"next\":\"\",\"a\":\"Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.\",\"r\":{\"buttons\":[{\"text\":\"What is a solar flare?\",\"value\":\"What is a solar flare\"}],\"subTitle\":\"\",\"imageUrl\":\"\",\"title\":\"Suggestions\",\"text\":\"\",\"url\":\"\"},\"t\":\"\",\"alt\":{\"markdown\":\"\",\"ssml\":\"\"},\"l\":\"\",\"qid\":\"sun.10\",\"type\":\"qna\",\"q\":[\"How many planets orbit the Sun?\",\"Which planets orbit the Sun?\"]}" - } - } - ] - } - ], - "FacetResults": [], - "TotalNumberOfResults": 1, - "originalKendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a" -} diff --git a/test-do-not-use-obsolete/kendra_tests/kendraData_doc_query.json b/test-do-not-use-obsolete/kendra_tests/kendraData_doc_query.json deleted file mode 100644 index fb4771dbc..000000000 --- a/test-do-not-use-obsolete/kendra_tests/kendraData_doc_query.json +++ /dev/null @@ -1,193 +0,0 @@ -{ - "QueryId": "7563d4b1-0646-46ae-9013-d04fdb7f0cb4", - "ResultItems": [ - { - "Id": "7563d4b1-0646-46ae-9013-d04fdb7f0cb4-cbf7edb4-e12c-43bc-aff9-c90fc2b0e68d", - "Type": "ANSWER", - "AdditionalAttributes": [ - { - "Key": "AnswerText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "Anatomy of the Sun\nMysteries of the Sun\n\n\nThe Sun is an incandescent mass of hydrogen, helium, and other heavier elements. While it appears constant and unchanging from \nour vantage point on Earth, it actually has a dynamic and variable system of twisting magnetic fields that cause solar events of almost \nunimaginable power.\n\n\nThe Convection Zone\nEnergy continues to move toward the surface \nthrough convection currents of heated and \ncooled gas in the convection zone.\n\n\n \n\n\nThe Radiative Zone\nEnergy moves slowly outward—taking \nmore than 170,000 years to radiate through \nthe layer of the Sun known as the radiative \nzone.", - "Highlights": [ - { - "BeginOffset": 42, - "EndOffset": 121, - "TopAnswer": false - }, - { - "BeginOffset": 53, - "EndOffset": 121, - "TopAnswer": false - }, - { - "BeginOffset": 15, - "EndOffset": 18, - "TopAnswer": false - }, - { - "BeginOffset": 36, - "EndOffset": 39, - "TopAnswer": false - }, - { - "BeginOffset": 46, - "EndOffset": 49, - "TopAnswer": false - }, - { - "BeginOffset": 594, - "EndOffset": 597, - "TopAnswer": false - } - ] - } - } - } - ], - "DocumentId": "s3://explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentTitle": { - "Text": "637244main_MysteriesOfTheSun_Book" - }, - "DocumentExcerpt": { - "Text": "Anatomy of the Sun\nMysteries of the Sun\n\n\nThe Sun is an incandescent mass of hydrogen, helium, and other heavier elements. While it appears constant and unchanging from \nour vantage point on Earth, it actually has a dynamic and variable system of twisting magnetic fields that cause solar events of a", - "Highlights": [ - { - "BeginOffset": 0, - "EndOffset": 300, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentAttributes": [] - }, - { - "Id": "7563d4b1-0646-46ae-9013-d04fdb7f0cb4-fdcd7c67-02e9-4a73-8ffa-03fadd6cc880", - "Type": "ANSWER", - "AdditionalAttributes": [ - { - "Key": "AnswerText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "a star, composed mostly of hydrogen, at the center of the Solar \n\n\nSystem, and with planets orbiting around it. But ancient people didn’t have access to the same tools we have today. \n\n\nTheir understanding about the Sun was far more concerned with the day-to-day needs of living. As such, their notions \n\n\nhave influenced the way we (still) think of the Sun.", - "Highlights": [ - { - "BeginOffset": 27, - "EndOffset": 35, - "TopAnswer": false - }, - { - "BeginOffset": 216, - "EndOffset": 219, - "TopAnswer": false - }, - { - "BeginOffset": 354, - "EndOffset": 357, - "TopAnswer": false - } - ] - } - } - } - ], - "DocumentId": "s3://explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentTitle": { - "Text": "637244main_MysteriesOfTheSun_Book" - }, - "DocumentExcerpt": { - "Text": "a star, composed mostly of hydrogen, at the center of the Solar \n\n\nSystem, and with planets orbiting around it. But ancient people didn’t have access to the same tools we have today. \n\n\nTheir understanding about the Sun was far more concerned with the day-to-day needs of living. As such, their notio", - "Highlights": [ - { - "BeginOffset": 0, - "EndOffset": 300, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentAttributes": [] - }, - { - "Id": "7563d4b1-0646-46ae-9013-d04fdb7f0cb4-9f4ad51f-5fc0-4d61-bc61-af58f0e4ba23", - "Type": "DOCUMENT", - "AdditionalAttributes": [], - "DocumentId": "s3://explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentTitle": { - "Text": "Sun_Lithograph", - "Highlights": [ - { - "BeginOffset": 0, - "EndOffset": 3, - "TopAnswer": false - } - ] - }, - "DocumentExcerpt": { - "Text": "...Mass 1.989 × 1030 kg \nDensity 1.409 g/cm3 \nComposition 92.1% hydrogen, 7.8% helium, \n 0.1% other elements \nSurface Temperature (Photosphere) 5,500 deg C \n\n\n(10,000 deg F) \nLuminosity* 3.83 × 1033 ergs/sec\n\n\n*The total energy radiated by the Sun (or any star) per second at all wavelengths...", - "Highlights": [ - { - "BeginOffset": 46, - "EndOffset": 57, - "TopAnswer": false - }, - { - "BeginOffset": 248, - "EndOffset": 251, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentAttributes": [ - { - "Key": "_source_uri", - "Value": { - "StringValue": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf" - } - } - ] - }, - { - "Id": "7563d4b1-0646-46ae-9013-d04fdb7f0cb4-8ef98066-6bbe-4317-8ea9-346d3b34b7ae", - "Type": "DOCUMENT", - "AdditionalAttributes": [], - "DocumentId": "s3://explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentTitle": { - "Text": "637244main_MysteriesOfTheSun_Book", - "Highlights": [] - }, - "DocumentExcerpt": { - "Text": "...gases may \ncontribute to a change in tempera-\nture or water composition in \nthe atmosphere. \n\n\n \n\n\nStratosphere\n10–31 Miles\n\n\nThe ozone layer lies within the \nstratosphere and absorbs ultraviolet \nradiation from the Sun.\n\n\nTroposphere\n0–10 Miles\n\n\nThe troposphere is the layer of the...", - "Highlights": [ - { - "BeginOffset": 63, - "EndOffset": 74, - "TopAnswer": false - }, - { - "BeginOffset": 221, - "EndOffset": 224, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentAttributes": [ - { - "Key": "_source_uri", - "Value": { - "StringValue": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf" - } - } - ] - } - ], - "FacetResults": [], - "TotalNumberOfResults": 4, - "originalKendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a" -} diff --git a/test-do-not-use-obsolete/kendra_tests/kendraData_top_ans.json b/test-do-not-use-obsolete/kendra_tests/kendraData_top_ans.json deleted file mode 100644 index 406e760c2..000000000 --- a/test-do-not-use-obsolete/kendra_tests/kendraData_top_ans.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "QueryId": "9f2d96e0-dbed-4192-a5b8-c8a9a86dc738", - "ResultItems": [ - { - "Id": "9f2d96e0-dbed-4192-a5b8-c8a9a86dc738-f46c83da-5aac-4200-8a6e-440a2867a7e2", - "Type": "ANSWER", - "AdditionalAttributes": [ - { - "Key": "AnswerText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "Ancient Greek \nastronomers and philosophers knew this “geocentric” concept \nfrom as early as the 6th century BCE. Now we know, of course, \nthat all the planets orbit our lone star — the Sun.\n\n\nThe Sun is the closest star to Earth, at a mean distance from \nour planet of 149.60 million kilometers (92.96 million miles). This \ndistance is known as an astronomical unit (abbreviated AU), and \nsets the scale for measuring distances all across the solar sys-\ntem. The Sun, a huge sphere of mostly ionized gas, supports life \non Earth. The connection and interactions between the Sun and \nEarth drive the seasons, ocean currents, weather, and climate.", - "Highlights": [ - { - "BeginOffset": 186, - "EndOffset": 189, - "TopAnswer": true - }, - { - "BeginOffset": 182, - "EndOffset": 189, - "TopAnswer": false - }, - { - "BeginOffset": 175, - "EndOffset": 179, - "TopAnswer": false - }, - { - "BeginOffset": 208, - "EndOffset": 215, - "TopAnswer": false - }, - { - "BeginOffset": 216, - "EndOffset": 220, - "TopAnswer": false - }, - { - "BeginOffset": 224, - "EndOffset": 229, - "TopAnswer": false - }, - { - "BeginOffset": 524, - "EndOffset": 529, - "TopAnswer": false - }, - { - "BeginOffset": 584, - "EndOffset": 589, - "TopAnswer": false - } - ] - } - } - } - ], - "DocumentId": "s3://explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentTitle": { - "Text": "Sun_Lithograph" - }, - "DocumentExcerpt": { - "Text": "Ancient Greek \nastronomers and philosophers knew this “geocentric” concept \nfrom as early as the 6th century BCE. Now we know, of course, \nthat all the planets orbit our lone star — the Sun.\n\n\nThe Sun is the closest star to Earth, at a mean distance from \nour planet of 149.60 million kilometers (92.", - "Highlights": [ - { - "BeginOffset": 0, - "EndOffset": 300, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentAttributes": [] - }, - { - "Id": "9f2d96e0-dbed-4192-a5b8-c8a9a86dc738-0b7134ed-257c-4de1-9b5f-01bef33935d6", - "Type": "DOCUMENT", - "AdditionalAttributes": [], - "DocumentId": "s3://explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentTitle": { - "Text": "Sun_Lithograph", - "Highlights": [] - }, - "DocumentExcerpt": { - "Text": "...from as early as the 6th century BCE. Now we know, of course, \nthat all the planets orbit our lone star — the Sun.\n\n\nThe Sun is the closest star to Earth, at a mean distance from \nour planet of 149.60 million kilometers (92.96 million miles...", - "Highlights": [ - { - "BeginOffset": 102, - "EndOffset": 106, - "TopAnswer": false - }, - { - "BeginOffset": 135, - "EndOffset": 142, - "TopAnswer": false - }, - { - "BeginOffset": 143, - "EndOffset": 147, - "TopAnswer": false - }, - { - "BeginOffset": 151, - "EndOffset": 156, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf", - "DocumentAttributes": [ - { - "Key": "_source_uri", - "Value": { - "StringValue": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf" - } - } - ] - }, - { - "Id": "9f2d96e0-dbed-4192-a5b8-c8a9a86dc738-511c93c5-0285-4014-b7bf-a4d9fec683e2", - "Type": "DOCUMENT", - "AdditionalAttributes": [], - "DocumentId": "s3://explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentTitle": { - "Text": "637244main_MysteriesOfTheSun_Book", - "Highlights": [] - }, - "DocumentExcerpt": { - "Text": "...Solar Cycle\n\n\nSolar Storms\n\n\nEarth’s Magnetosphere\n\n\nEarth’s Upper Atmosphere\n\n\nSpace Weather\n\n\nCredits\n\n\n2\n\n\n4\n\n\n6\n\n\n8\n\n\n10\n\n\n14\n\n\n16\n\n\n18\n\n\n\n\n\n\n\nPrologue and Introduction\n\n\nNow in the early 21st century, we know that the Sun is a star, composed mostly of hydrogen, at the center of the Solar...", - "Highlights": [ - { - "BeginOffset": 32, - "EndOffset": 37, - "TopAnswer": false - }, - { - "BeginOffset": 56, - "EndOffset": 61, - "TopAnswer": false - }, - { - "BeginOffset": 235, - "EndOffset": 239, - "TopAnswer": false - } - ] - }, - "DocumentURI": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf", - "DocumentAttributes": [ - { - "Key": "_source_uri", - "Value": { - "StringValue": "https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf" - } - } - ] - } - ], - "FacetResults": [], - "TotalNumberOfResults": 3, - "originalKendraIndexId": "e1c23860-e5c8-4409-ae26-b05bd6ced00a" -} diff --git a/test-do-not-use-obsolete/kendra_tests/mockClient.js b/test-do-not-use-obsolete/kendra_tests/mockClient.js deleted file mode 100644 index 6c4381f0f..000000000 --- a/test-do-not-use-obsolete/kendra_tests/mockClient.js +++ /dev/null @@ -1,7 +0,0 @@ -// mockClient.js - -// query calls func(err, data) to return the Data from Kendra JSON from the CloudWatch log -exports.query = function(params, func) { - const data = require('./kendraData_FAQ_md.json'); - func(undefined, data); -}; diff --git a/test-do-not-use-obsolete/kendra_tests/mockClient1.js b/test-do-not-use-obsolete/kendra_tests/mockClient1.js deleted file mode 100644 index 6c4381f0f..000000000 --- a/test-do-not-use-obsolete/kendra_tests/mockClient1.js +++ /dev/null @@ -1,7 +0,0 @@ -// mockClient.js - -// query calls func(err, data) to return the Data from Kendra JSON from the CloudWatch log -exports.query = function(params, func) { - const data = require('./kendraData_FAQ_md.json'); - func(undefined, data); -}; diff --git a/test-do-not-use-obsolete/kendra_tests/mockClient2.js b/test-do-not-use-obsolete/kendra_tests/mockClient2.js deleted file mode 100644 index 81f4040ec..000000000 --- a/test-do-not-use-obsolete/kendra_tests/mockClient2.js +++ /dev/null @@ -1,7 +0,0 @@ -// mockClient2.js for testing top_ans feature - -// query calls func(err, data) to return the Data from Kendra JSON from the CloudWatch log -exports.query = function(params, func) { - const data = require('./kendraData_top_ans.json'); - func(undefined, data); -}; diff --git a/test-do-not-use-obsolete/kendra_tests/mockClient3.js b/test-do-not-use-obsolete/kendra_tests/mockClient3.js deleted file mode 100644 index 302ed51e4..000000000 --- a/test-do-not-use-obsolete/kendra_tests/mockClient3.js +++ /dev/null @@ -1,7 +0,0 @@ -// mockClient3.js - -// query calls func(err, data) to return the Data from Kendra JSON from the CloudWatch log -exports.query = function(params, func) { - const data = require('./kendraData_doc_query.json'); - func(undefined, data); -}; diff --git a/test-do-not-use-obsolete/kendra_tests/run_test.js b/test-do-not-use-obsolete/kendra_tests/run_test.js deleted file mode 100644 index d59d9c278..000000000 --- a/test-do-not-use-obsolete/kendra_tests/run_test.js +++ /dev/null @@ -1,63 +0,0 @@ -var assert = require('assert'); - -// kendra fallback test -const kendraFallback = require('../KendraFallback.js'); - -async function test_markdown() { - const event = require('./event_FAQ_md.json'); - const context = require('./context_FAQ_md.json'); - event.test = 1; - const actual_resp = await kendraFallback.handler(event, context); - return actual_resp; -} - -async function test_top_ans() { - const event = require('./event_top_ans.json'); - const context = require('./context_top_ans.json'); - event.test = 2; - const actual_resp = await kendraFallback.handler(event, context); - return actual_resp; -} - -async function test_doc_query() { - const event = require('./event_doc_query.json'); - const context = require('./context_doc_query.json'); - event.test = 3; - const actual_resp = await kendraFallback.handler(event, context); - return actual_resp; -} - - -describe('#test_kendra_highlights()', () => { - it('test_markdown', async function() { - let resp = await test_markdown(); - - // tests that in markdown format, highlights are boldened - assert.equal(resp.res.session.appContext.altMessages.markdown, - "*Answer from Amazon Kendra FAQ.* \n \n\nEight **planets** **orbit** the **sun**. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto."); - }); - it('test_top_answer', async function() { - let resp = await test_top_ans(); - - // tests that in markdown format, only the top answer phrase is returned with the link from where it is extracted - assert.equal(resp.res.session.appContext.altMessages.markdown, - "*Amazon Kendra suggested answer.* \n \n\n**Sun** \n\n Source Link: [https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf](https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf)"); - - // tests that in SSML format, only the top answer phrase is returned - assert.equal(resp.res.session.appContext.altMessages.ssml, - " Amazon Kendra suggested answer. Sun "); - }); - it('test_doc_query', async function() { - let resp = await test_doc_query(); - - // tests that when querying an unstructured document, highlights are boldened in the excerpts when no top answer is found - assert.equal(resp.res.session.appContext.altMessages.markdown, - "*While I did not find an exact answer, these search results from Amazon Kendra might be helpful.* \n \n\nAnatomy of the Sun Mysteries of the Sun **The Sun is an incandescent mass of hydrogen, helium, and other heavier elements**. While it appears constant and unchanging from our vantage point on Earth, it actually has a dynamic and variable system of twisting magnetic fields that cause solar events of almost unimaginable power. The Convection Zone Energy continues to move toward the surface through convection currents of heated and cooled gas in the convection zone. The Radiative Zone Energy moves slowly outward—taking more than 170,000 years to radiate through the layer of the **Sun** known as the radiative zone.\n\n Source Link: [https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf](https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf)\n\n***\n\n
\n\n ...Mass 1.989 × 1030 kg Density 1.409 g/cm3 **Composition** 92.1% hydrogen, 7.8% helium, 0.1% other elements Surface Temperature (Photosphere) 5,500 deg C (10,000 deg F) Luminosity* 3.83 × 1033 ergs/sec *The total energy radiated by the **Sun** (or any star) per second at all wavelengths...\n\n Source Link: [https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf](https://s3.us-east-1.amazonaws.com/explore-kendra-solar/Sun_Lithograph.pdf)\n\n***\n\n
\n\n ...gases may contribute to a change in tempera- ture or water **composition** in the atmosphere. Stratosphere 10–31 Miles The ozone layer lies within the stratosphere and absorbs ultraviolet radiation from the **Sun**. Troposphere 0–10 Miles The troposphere is the layer of the...\n\n Source Link: [https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf](https://s3.us-east-1.amazonaws.com/explore-kendra-solar/637244main_MysteriesOfTheSun_Book.pdf)"); - - // tests that when querying an unstructured document, the SSML response returns the longest highlight from the response when no top answer is found - assert.equal(resp.res.session.appContext.altMessages.ssml, - " Anatomy of the Sun Mysteries of the Sun The Sun is an incandescent mass of hydrogen, helium, and other heavier elements. While it appears constant and unchanging from our vantage point on Earth, it actually has a dynamic and variable system of twisting magnetic fields that cause solar events of almost unimaginable power "); - }); -}); - - diff --git a/test-do-not-use-obsolete/kendra_tests/sample_return.json b/test-do-not-use-obsolete/kendra_tests/sample_return.json deleted file mode 100644 index 87858a060..000000000 --- a/test-do-not-use-obsolete/kendra_tests/sample_return.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "req": { - "_event": { - "messageVersion": "1.0", - "invocationSource": "FulfillmentCodeHook", - "userId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "sessionAttributes": {}, - "requestAttributes": null, - "bot": { - "name": "QNA_dev_dev_dev_master_two_BotUSFgl", - "alias": "live", - "version": "1" - }, - "outputDialogMode": "Text", - "currentIntent": { - "name": "fulfilment_IntentfiuhxvkSil", - "slots": { - "slot": "How many planets orbit the sun" - }, - "slotDetails": { - "slot": { - "resolutions": [], - "originalValue": "How many planets orbit the sun" - } - }, - "confirmationStatus": "None" - }, - "inputTranscript": "How many planets orbit the sun?", - "recentIntentSummaryView": null, - "sentimentResponse": null, - "kendraResponse": null, - "errorFound": false - }, - "_settings": { - "ENABLE_DEBUG_RESPONSES": "false", - "ES_USE_KEYWORD_FILTERS": "true", - "ES_NO_HITS_QUESTION": "no_hits", - "ES_USE_FUZZY_MATCH": "false", - "ES_KEYWORD_SYNTAX_TYPES": "NOUN,PROPN,VERB,INTJ", - "ES_SYNTAX_CONFIDENCE_LIMIT": ".20", - "ES_MINIMUM_SHOULD_MATCH": "2<75%", - "ES_PHRASE_BOOST": "4", - "ES_SCORE_ANSWER_FIELD": "false", - "ERRORMESSAGE": "Unfortunately I encountered an error when searching for your answer. Please ask me again later.", - "EMPTYMESSAGE": "You stumped me! Sadly I don't know how to answer your question.", - "DEFAULT_ALEXA_LAUNCH_MESSAGE": "Hello, Please ask a question", - "DEFAULT_ALEXA_STOP_MESSAGE": "Goodbye", - "SMS_HINT_REMINDER_ENABLE": "true", - "SMS_HINT_REMINDER": " (Feedback? Reply THUMBS UP or THUMBS DOWN. Ask HELP ME at any time)", - "SMS_HINT_REMINDER_INTERVAL_HRS": "24", - "IDENTITY_PROVIDER_JWKS_URLS": [], - "ENABLE_MULTI_LANGUAGE_SUPPORT": "false", - "MINIMUM_CONFIDENCE_SCORE": 0.6, - "ALT_SEARCH_KENDRA_INDEXES": "[\"e1c23860-e5c8-4409-ae26-b05bd6ced00a\"]", - "ELICIT_RESPONSE_MAX_RETRIES": 3, - "ELICIT_RESPONSE_RETRY_MESSAGE": "Please try again?", - "ELICIT_RESPONSE_BOT_FAILURE_MESSAGE": "Your response was not understood. Please start again.", - "ELICIT_RESPONSE_DEFAULT_MSG": "Ok. ", - "ENABLE_REDACTING": "false", - "REDACTING_REGEX": "\\b\\d{4}\\b(?![-])|\\b\\d{9}\\b|\\b\\d{3}-\\d{2}-\\d{4}\\b", - "DEFAULT_USER_POOL_JWKS_URL": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_jt2cXkCEb/.well-known/jwks.json" - }, - "_type": "LEX", - "_userId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "question": "How many planets orbit the sun?", - "session": {}, - "_preferredResponseType": "PlainText", - "_userInfo": { - "UserId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "InteractionCount": 0, - "TimeSinceLastInteraction": 1593663418.209 - }, - "_info": { - "es": { - "address": "search-qna-dev-elasti-mtl6ojldf3ja-w5begr25vpfuyidnlgb4izlbtq.us-east-1.es.amazonaws.com", - "index": "qna-dev-dev-dev-master-2", - "type": "qna", - "service": { - "qid": "QNA-dev-dev-dev-master-2-ESQidLambda-1I9JEHU6U4K9I", - "proxy": "QNA-dev-dev-dev-master-2-ESProxyLambda-1NMAY6LNBRJDY" - } - } - } - }, - "res": { - "type": "PlainText", - "message": "Answer from Amazon Kendra FAQ.\n\n Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.", - "session": { - "appContext": { - "altMessages": { - "markdown": "*Answer from Amazon Kendra FAQ.* \n \n\nEight **planets** **orbit** the **sun**. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto.", - "ssml": " Answer from Amazon Kendra FAQ. Eight planets orbit the sun. Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, and Neptune. Sorry I'm required to leave out my favorite, Pluto " - } - }, - "previous": { - "qid": "KendraFallback", - "a": "The Kendra Fallback search was not able to identify any results", - "alt": {}, - "q": "How many planets orbit the sun?" - }, - "navigation": { - "next": "", - "previous": [], - "hasParent": true - }, - "qnabotcontext": { - "kendra":{ - "kendraIndexId":"e1c23860-e5c8-4409-ae26-b05bd6ced00a", - "kendraQueryId":"ec35a915-8990-4ef7-a5da-f490911cad42", - "kendraResultId":"ec35a915-8990-4ef7-a5da-f490911cad42-2db7aa5b-1fcb-4910-a2fc-b3922a6069e3", - "kendraResponsibleQid": "KendraFallback" - } - } - }, - "card": { - "send": false, - "title": "", - "text": "", - "url": "" - }, - "_userInfo": { - "UserId": "us-east-1:7ace124d-bfd1-4356-ac5a-ed8a98785fd9", - "InteractionCount": 1, - "TimeSinceLastInteraction": 1593663418.209, - "FirstSeen": "Thu Jul 02 2020 16:16:58 GMT+0000 (Coordinated Universal Time)", - "LastSeen": "Thu Jul 02 2020 16:16:58 GMT+0000 (Coordinated Universal Time)" - }, - "got_hits": 0, - "result": { - "qid": "KendraFallback", - "quniqueterms": " no_hits ", - "questions": [ - { - "q": "no_hits" - } - ], - "a": "The Kendra Fallback search was not able to identify any results", - "l": "QNA:EXTKendraFallback", - "type": "qna", - "autotranslate": { - "a": true - } - }, - "plainMessage": "The Kendra Fallback search was not able to identify any results" - } -} diff --git a/test-do-not-use-obsolete/kendra_tests/test_json.json b/test-do-not-use-obsolete/kendra_tests/test_json.json deleted file mode 100644 index 489f89fa7..000000000 --- a/test-do-not-use-obsolete/kendra_tests/test_json.json +++ /dev/null @@ -1,42 +0,0 @@ - -{ - "Key": "AnswerText", - "ValueType": "TEXT_WITH_HIGHLIGHTS_VALUE", - "Value": { - "TextWithHighlightsValue": { - "Text": "Anatomy of the Sun\nMysteries of the Sun\n\n\nThe Sun is an incandescent mass of hydrogen, helium, and other heavier elements. While it appears constant and unchanging from \nour vantage point on Earth, it actually has a dynamic and variable system of twisting magnetic fields that cause solar events of almost \nunimaginable power.\n\n\nThe Convection Zone\nEnergy continues to move toward the surface \nthrough convection currents of heated and \ncooled gas in the convection zone.\n\n\n \n\n\nThe Radiative Zone\nEnergy moves slowly outward—taking \nmore than 170,000 years to radiate through \nthe layer of the Sun known as the radiative \nzone.", - "Highlights": [ - { - "BeginOffset": 42, - "EndOffset": 121, - "TopAnswer": false - }, - { - "BeginOffset": 53, - "EndOffset": 121, - "TopAnswer": false - }, - { - "BeginOffset": 15, - "EndOffset": 18, - "TopAnswer": false - }, - { - "BeginOffset": 36, - "EndOffset": 39, - "TopAnswer": false - }, - { - "BeginOffset": 46, - "EndOffset": 49, - "TopAnswer": false - }, - { - "BeginOffset": 594, - "EndOffset": 597, - "TopAnswer": false - } - ] - } - } -} diff --git a/test-do-not-use-obsolete/reporter.js b/test-do-not-use-obsolete/reporter.js deleted file mode 100644 index 8fb2c291c..000000000 --- a/test-do-not-use-obsolete/reporter.js +++ /dev/null @@ -1,131 +0,0 @@ -/*! - * Nodeunit - * Copyright (c) 2010 Caolan McMahon - * MIT Licensed - */ - -/** - * Module dependencies - */ -var base="../node_modules/nodeunit/lib/reporters/" -var chalk=require('chalk') -const ora = require('ora'); -var _=require('lodash') -var Promise=require('bluebird') -var nodeunit = require(base+'../nodeunit'), - utils = require(base+'../utils'), - fs = require('fs'), - track = require(base+'../track'), - path = require('path'), - AssertionError = require(base+'../assert').AssertionError; - -/** - * Reporter info string - */ - -exports.info = "Default tests reporter"; - - -/** - * Run all tests within each module, reporting the results to the command-line. - * - * @param {Array} files - * @api public - */ - -exports.run = function (files, options={}, callback,_log) { -return new Promise(function(res,rej){ - var options=options || {} - var start = new Date().getTime(); - var report=[] - var tracker = track.createTracker(function (tracker) { - if (tracker.unfinished()) { - _log(''); - _log('FAILURES: Undone tests (or their setups/teardowns): '); - var names = tracker.names(); - for (var i = 0; i < names.length; i += 1) { - _log('/' + names[i]); - } - _log(''); - _log('To fix this, make sure all tests call test.done()'); - rej(tracker.unfinished()) - } - }); - var timer - var spinner - var opts = { - testspec: options.testspec, - testFullSpec: options.testFullSpec, - recursive: options.recursive, - moduleStart: function (name) { - _log('\n' + name); - }, - testDone: function (name, assertions) { - tracker.remove(name); - var duration=(new Date())-timer - if (!assertions.failures()) { - spinner.succeed(' ('+assertions.length+') '+ name +" "+duration/1000+'s' ); - report.push({name:name.toString(),status:"success",duration}) - } - else { - spinner.fail(' ' + name + '\n'); - var tmp={name:name.toString(),status:"fail",duration,messages:[]} - assertions.forEach(function (a) { - if (a.failed()) { - a = utils.betterErrors(a); - if (a.error instanceof AssertionError && a.message) { - tmp.message.push(a.message) - _log( - 'Assertion Message: ' + - assertion_message(a.message) - ); - } - _log(a.error.stack + '\n'); - } - }); - report.push(tmp) - } - }, - done: function (assertions, end) { - var end = end || new Date().getTime(); - var duration = end - start; - if (assertions.failures()) { - _log( - '\n' + 'FAILURES: ' + assertions.failures() + - '/' + assertions.length + ' assertions failed (' + - assertions.duration/1000 + 's)' - ); - } - else { - _log( - '\n' + 'OK: ' + assertions.length + - ' assertions (' + assertions.duration/1000 + 's)' - ); - } - res(report) - if (callback) callback(assertions.failures() ? new Error('We have got test failures.') : undefined); - }, - testStart: function(name) { - name.toString=()=>{ - return name.filter(x=>_.isString(x)).join('/') - } - spinner = ora({ - text:name, - spinner:"simpleDots" - }).start(); - spinner.color = 'yellow'; - - timer=new Date() - tracker.put(name); - } - }; - if (files && files.length) { - var paths = files.map(function (p) { - return path.resolve(p); - }); - nodeunit.runFiles(paths, opts); - } else { - nodeunit.runModules(files,opts); - } -}) -}; diff --git a/test-do-not-use-obsolete/run.js b/test-do-not-use-obsolete/run.js deleted file mode 100755 index 5318d79ae..000000000 --- a/test-do-not-use-obsolete/run.js +++ /dev/null @@ -1,29 +0,0 @@ -#! /usr/bin/env node -var fs = require('fs'); -var util = require('util'); -var label=process.argv[3] -var log_file = fs.createWriteStream(__dirname + `/output/log-${label}.log`, {flags : 'a'}); -var output_file = fs.createWriteStream(__dirname + `/output/output-${label}.log`, {flags : 'a'}); -var _log=console.log -const stripAnsi = require('strip-ansi'); - -var log=function(x){ - log_file.write(stripAnsi(util.format(x)) + '\n'); - output_file.write(stripAnsi(util.format(x)) + '\n'); - _log(x) -} - -console.log=function(x){ - log_file.write(stripAnsi(util.format(x)) + '\n'); -}.bind(console) - -var reporter = require('./reporter'); - -reporter - .run([process.argv[2]],null,null,log) - .then(x=>fs.writeFileSync(__dirname+`/output/output-${label}.json`,JSON.stringify(x))) - - - - - diff --git a/test-do-not-use-obsolete/setup.sh b/test-do-not-use-obsolete/setup.sh deleted file mode 100755 index a62b6c207..000000000 --- a/test-do-not-use-obsolete/setup.sh +++ /dev/null @@ -1,21 +0,0 @@ -#! /bin/bash -__dirname="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -BASE=$__dirname/.. -SELENIUM_START=/opt/bin/entry_point.sh -cd $BASE - -if [ -e $SELENIUM_START ]; then - $SELENIUM_START & -fi - -npm run --silent bootstrap - -if [ $? -ne 0 ]; then - echo "failed to create bootstrap bucket" - exit 1 -fi - -mkdir -p $__dirname/output -rm $__dirname/output/*; - -echo "ready" diff --git a/test-do-not-use-obsolete/teardown.js b/test-do-not-use-obsolete/teardown.js deleted file mode 100755 index 18599a24f..000000000 --- a/test-do-not-use-obsolete/teardown.js +++ /dev/null @@ -1,45 +0,0 @@ -#! /usr/bin/env node -var name=require('../bin/name') -var config=require('../config') -process.env.AWS_PROFILE=config.profile -process.env.AWS_DEFAULT_REGION=config.profile -var aws=require('aws-sdk') -var Promise=require('bluebird') -aws.config.setPromisesDependency(Promise) -aws.config.region=require('../config').region -var cf=new aws.CloudFormation() -var _=require('lodash') - -var prefix=name('a',{prefix:true}) -cf.listStacks({ - StackStatusFilter:[ -"CREATE_IN_PROGRESS","CREATE_FAILED","CREATE_COMPLETE","ROLLBACK_FAILED","ROLLBACK_COMPLETE","DELETE_FAILED","UPDATE_COMPLETE","UPDATE_ROLLBACK_FAILED","UPDATE_ROLLBACK_COMPLETE" - ] -}).promise() -.then(x=>x.StackSummaries.filter(x=>x.StackName.match(prefix)) -.map(x=>x.StackName)) -.map(StackName=>{ - console.log(`deleting: ${StackName}`) - return cf.deleteStack({StackName}).promise() -}) -.then(()=>{ - return new Promise(function(res,rej){ - next() - function next(){ - cf.listStacks({ - StackStatusFilter:["DELETE_IN_PROGRESS"] - }).promise() - .then(x=>x.StackSummaries.filter(x=>x.StackName.match(prefix)).length) - .then(count=>{ - console.log('stacks up:'+count) - if(count<1){ - res() - }else{ - setTimeout(()=>next(),10*1000) - } - }) - .catch(rej) - } - }) -}) - diff --git a/test-do-not-use-obsolete/test.json b/test-do-not-use-obsolete/test.json deleted file mode 100644 index 291e2b5cb..000000000 --- a/test-do-not-use-obsolete/test.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "Description": "This template creates test infastructure for testing QnABot", - "Resources": { - "Repo": {"Type": "AWS::ECR::Repository", "Properties": {}}, - "testBuilb": { - "Type": "AWS::CodeBuild::Project", - "Properties": { - "Artifacts": {"Type": "S3", "Location": {"Ref": "Bucket"}}, - "Environment": { - "ComputeType": "BUILD_GENERAL1_LARGE", - "Image": { - "Fn::Sub": "${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repo}:test" - }, - "EnvironmentVariables": [ - { - "Name": "CODEBUILD_SOURCE_VERSION", - "Value": "feature-remove-exports" - } - ], - "Type": "LINUX_CONTAINER" - }, - "Name": {"Fn::Sub": "${AWS::StackName}-test-build"}, - "ServiceRole": {"Ref": "ServiceRole"}, - "Source": { - "Type": "GITHUB", - "Location": "https://github.com/rstrahan/aws-ai-qna-bot-2.git", - "auth": {"type": "OATH"} - } - } - }, - "ImageBuild": { - "Type": "AWS::CodeBuild::Project", - "Properties": { - "Artifacts": {"Type": "NO_ARTIFACTS"}, - "Environment": { - "ComputeType": "BUILD_GENERAL1_LARGE", - "EnvironmentVariables": [ - {"Name": "IMAGE_REPO_NAME", "Value": {"Ref": "Repo"}}, - {"Name": "IMAGE_TAG", "Value": "test"}, - {"Name": "AWS_DEFAULT_REGION", "Value": {"Ref": "AWS::Region"}}, - {"Name": "AWS_ACCOUNT_ID", "Value": {"Ref": "AWS::AccountId"}} - ], - "Image": "ubuntu/docker/17.09.0", - "PrivilegedMode": true, - "Type": "LINUX_CONTAINER" - }, - "Name": {"Fn::Sub": "${AWS::StackName}-test-image-build"}, - "ServiceRole": {"Ref": "ServiceRole"}, - "Source": { - "Type": "S3", - "Location": {"Fn::Sub": "${Bucket}/source.zip"} - } - } - }, - "Bucket": {"Type": "AWS::S3::Bucket", "Properties": {}}, - "Clear": { - "Type": "Custom::S3Clear", - "Properties": { - "ServiceToken": {"Fn::GetAtt": ["ClearLambda", "Arn"]}, - "Bucket": {"Ref": "Bucket"} - } - }, - "upload": { - "Type": "Custom::S3Upload", - "Properties": { - "ServiceToken": {"Fn::GetAtt": ["UploadLambda", "Arn"]}, - "bucket": {"Ref": "Bucket"}, - "key": "source.zip", - "body": "UEsDBAoAAAAAAAGbK0yFEUoNCwAAAAsAAAANAAAAYnVpbGRzcGVjLnltbGhlbGxvIHdvcmxkUEsDBAoAAAAAAAGbK0yFEUoNCwAAAAsAAAAKAAAARG9ja2VyZmlsZWhlbGxvIHdvcmxkUEsBAhQACgAAAAAAAZsrTIURSg0LAAAACwAAAA0AAAAAAAAAAAAAAAAAAAAAAGJ1aWxkc3BlYy55bWxQSwECFAAKAAAAAAABmytMhRFKDQsAAAALAAAACgAAAAAAAAAAAAAAAAA2AAAARG9ja2VyZmlsZVBLBQYAAAAAAgACAHMAAABpAAAAAAA=" - } - }, - "Build": { - "Type": "Custom::CodeBuildStart", - "Properties": { - "ServiceToken": {"Fn::GetAtt": ["BuildLambda", "Arn"]}, - "name": {"Ref": "ImageBuild"} - } - }, - "BuildLambda": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "var response = require('cfn-response')\nvar aws=require('aws-sdk')\naws.config.region=process.env.AWS_REGION\nvar cb=new aws.CodeBuild()\nvar s3=new aws.S3()\n\nexports.build = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n cb.startBuild({\n projectName:event.ResourceProperties.name\n }).promise()\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.zip = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n s3.putObject({\n Bucket:event.ResourceProperties.bucket,\n Key:event.ResourceProperties.key,\n Body:new Buffer(event.ResourceProperties.body,\"base64\")\n })\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.clear = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Delete\"){\n Delete(event.ResourceProperties)\n .then(()=>response.send(event, context, response.SUCCESS))\n .catch(x=>{\n console.log(x)\n response.send(event, context, response.FAILED)\n })\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nfunction Delete(params){\n return new Promise(function(res,rej){\n function next(){\n s3.listObjectVersions({\n Bucket:params.Bucket\n }).promise()\n .then(x=>x.Versions.concat(x.DeleteMarkers))\n .then(function(files){\n return files.map(file=>{return {\n Key:file.Key,\n VersionId:file.VersionId\n } })\n })\n .then(function(keys){\n console.log(\"going to delete\",keys)\n if(keys.length>0){ \n return s3.deleteObjects({\n Bucket:params.Bucket,\n Delete:{\n Objects:keys\n }\n }).promise()\n .then(()=>next())\n .catch(rej)\n }else{\n res()\n }\n })\n }\n next()\n })\n}\n" - }, - "Handler": "index.build", - "MemorySize": "128", - "Role": {"Fn::GetAtt": ["LambdaRole", "Arn"]}, - "Runtime": "nodejs12.x", - "Timeout": 300 - } - }, - "UploadLambda": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "var response = require('cfn-response')\nvar aws=require('aws-sdk')\naws.config.region=process.env.AWS_REGION\nvar cb=new aws.CodeBuild()\nvar s3=new aws.S3()\n\nexports.build = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n cb.startBuild({\n projectName:event.ResourceProperties.name\n }).promise()\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.zip = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n s3.putObject({\n Bucket:event.ResourceProperties.bucket,\n Key:event.ResourceProperties.key,\n Body:new Buffer(event.ResourceProperties.body,\"base64\")\n })\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.clear = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Delete\"){\n Delete(event.ResourceProperties)\n .then(()=>response.send(event, context, response.SUCCESS))\n .catch(x=>{\n console.log(x)\n response.send(event, context, response.FAILED)\n })\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nfunction Delete(params){\n return new Promise(function(res,rej){\n function next(){\n s3.listObjectVersions({\n Bucket:params.Bucket\n }).promise()\n .then(x=>x.Versions.concat(x.DeleteMarkers))\n .then(function(files){\n return files.map(file=>{return {\n Key:file.Key,\n VersionId:file.VersionId\n } })\n })\n .then(function(keys){\n console.log(\"going to delete\",keys)\n if(keys.length>0){ \n return s3.deleteObjects({\n Bucket:params.Bucket,\n Delete:{\n Objects:keys\n }\n }).promise()\n .then(()=>next())\n .catch(rej)\n }else{\n res()\n }\n })\n }\n next()\n })\n}\n" - }, - "Handler": "index.zip", - "MemorySize": "128", - "Role": {"Fn::GetAtt": ["LambdaRole", "Arn"]}, - "Runtime": "nodejs12.x", - "Timeout": 300 - } - }, - "ClearLambda": { - "Type": "AWS::Lambda::Function", - "Properties": { - "Code": { - "ZipFile": "var response = require('cfn-response')\nvar aws=require('aws-sdk')\naws.config.region=process.env.AWS_REGION\nvar cb=new aws.CodeBuild()\nvar s3=new aws.S3()\n\nexports.build = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n cb.startBuild({\n projectName:event.ResourceProperties.name\n }).promise()\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.zip = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Create\"){\n s3.putObject({\n Bucket:event.ResourceProperties.bucket,\n Key:event.ResourceProperties.key,\n Body:new Buffer(event.ResourceProperties.body,\"base64\")\n })\n .then(x=>response.send(event,context,response.SUCCESS))\n .catch(x=>response.send(event,context,response.FAIL))\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nexports.clear = function(event, context) {\n console.log(JSON.stringify(event,null,2))\n\n if(event.RequestType===\"Delete\"){\n Delete(event.ResourceProperties)\n .then(()=>response.send(event, context, response.SUCCESS))\n .catch(x=>{\n console.log(x)\n response.send(event, context, response.FAILED)\n })\n }else{\n response.send(event, context, response.SUCCESS)\n }\n}\n\nfunction Delete(params){\n return new Promise(function(res,rej){\n function next(){\n s3.listObjectVersions({\n Bucket:params.Bucket\n }).promise()\n .then(x=>x.Versions.concat(x.DeleteMarkers))\n .then(function(files){\n return files.map(file=>{return {\n Key:file.Key,\n VersionId:file.VersionId\n } })\n })\n .then(function(keys){\n console.log(\"going to delete\",keys)\n if(keys.length>0){ \n return s3.deleteObjects({\n Bucket:params.Bucket,\n Delete:{\n Objects:keys\n }\n }).promise()\n .then(()=>next())\n .catch(rej)\n }else{\n res()\n }\n })\n }\n next()\n })\n}\n" - }, - "Handler": "index.clear", - "MemorySize": "128", - "Role": {"Fn::GetAtt": ["LambdaRole", "Arn"]}, - "Runtime": "nodejs12.x", - "Timeout": 300 - } - }, - "ServiceRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": {"Service": "codebuild.amazonaws.com"}, - "Action": "sts:AssumeRole" - } - ] - }, - "Path": "/", - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/AmazonECS_FullAccess", - "arn:aws:iam::aws:policy/AmazonS3FullAccess" - ] - } - }, - "LambdaRole": { - "Type": "AWS::IAM::Role", - "Properties": { - "AssumeRolePolicyDocument": { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Principal": {"Service": "lambda.amazonaws.com"}, - "Action": "sts:AssumeRole" - } - ] - }, - "Path": "/", - "ManagedPolicyArns": [ - "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole", - "arn:aws:iam::aws:policy/AmazonECS_FullAccess", - "arn:aws:iam::aws:policy/AmazonS3FullAccess" - ] - } - } - }, - "Outputs": {} -} \ No newline at end of file diff --git a/test-do-not-use-obsolete/test.sh b/test-do-not-use-obsolete/test.sh deleted file mode 100755 index f0955cb4a..000000000 --- a/test-do-not-use-obsolete/test.sh +++ /dev/null @@ -1,15 +0,0 @@ -#! /bin/bash -__dirname="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -BASE=$__dirname/.. -cd $BASE - -#npm run up - -echo "Testing Api/Lex" -if $__dirname/run.js $BASE/templates/master/test/index.js master; then - echo "Finished Testing API/Lex" -else - exit 1 -fi - - diff --git a/tuning_accuracy_guide/AWS_QnABot_tuning_recognition_accuracy_guide.md b/tuning_accuracy_guide/AWS_QnABot_tuning_recognition_accuracy_guide.md index 58eac67ad..750e54853 100644 --- a/tuning_accuracy_guide/AWS_QnABot_tuning_recognition_accuracy_guide.md +++ b/tuning_accuracy_guide/AWS_QnABot_tuning_recognition_accuracy_guide.md @@ -1,8 +1,6 @@ -**Tuning Recognition Accuracy** -=============================== +# Tuning Recognition Accuracy -Overview -======== +## Overview Chatbots are a great way to make information available for your users. With the Amazon Web Services (AWS) QnABot solution you can deploy a @@ -15,8 +13,7 @@ provides a common language to describe and provision all the infrastructure resources in your AWS cloud environment. This AWS QnABot CloudFormation template will setup resources such as Amazon Lex, Amazon Elasticsearch Service, Amazon API Gateway, and AWS Lambda. Additionally, -this solution is also available in GitHub -(). +this solution is also available in [GitHub](https://github.com/aws-samples/aws-ai-qna-bot). Once the solution is deployed, you have a QnABot designer console where you can build and manage your question and answer bank. The question and @@ -32,8 +29,7 @@ works in identifying user questions and providing the relevant answers. Learn how you can improve the recognition accuracy and further augment the chatbot to get better over time. -Solution architecture and how It works -====================================== +### Solution architecture and how It works When the QnABot solution is deployed, resources such as Amazon Lex, Amazon Elasticsearch Service (Amazon ES), Amazon API Gateway, and AWS @@ -41,7 +37,7 @@ Lambda are provisioned and setup in your AWS account. Two key AWS services are at the core of the solution: -- **Amazon Lex** is a service for building conversational interfaces +- **Amazon Lex** is a service for building conversational interfaces into any application using voice and text. Amazon Lex provides the advanced deep learning functionalities of automatic speech recognition (ASR) for converting speech to text, and natural @@ -49,7 +45,7 @@ Two key AWS services are at the core of the solution: enable you to build applications with highly engaging user experiences and lifelike conversational interactions. -- **Amazon ES** is an open-source search and analytics engine for use +- **Amazon ES** is an open-source search and analytics engine for use cases such as log analytics, real-time application monitoring, and clickstream analysis. Amazon ES is a managed service that makes it simple to deploy, operate, and scale Elasticsearch clusters in the @@ -61,7 +57,7 @@ Two key AWS services are at the core of the solution: Let's take a closer look at these two services and how they help power the AWS QnABot solution. -![](image2.png) +![Solution architecture and data flow](image2.png) *Figure 1.0 -- Solution architecture and data flow* When you ask QnABot a question, a few things happen: @@ -69,85 +65,85 @@ When you ask QnABot a question, a few things happen: 1. The question gets processed and transcribed by Amazon Lex using a Natural Language Understanding (NLU) and Processing (NLP) engine. - - QnABot initially trains the NLP to match a wide variety of possible + - QnABot initially trains the NLP to match a wide variety of possible questions and statements, so that the Amazon Lex bot can accept just about any question a user might ask. The Amazon Lex interaction model is setup with: - - **Intents**: An intent represents an action that fulfills a + - **Intents**: An intent represents an action that fulfills a user\'s spoken request. Intents can optionally have arguments called **slots**. The QnABot uses **slots** to capture user - input and fulfils the Intent via Lambda function. + input and fulfills the Intent via Lambda function. - - **Sample utterances**: A set of likely spoken phrases mapped to + - **Sample utterances**: A set of likely spoken phrases mapped to the intents. This should include as many representative phrases as possible. The sample utterances specify the words and phrases users can say to invoke your intents. QnABot updates the **Sample utterances** with the various questions to train the chatbot to understand different user input -2. This request is then sent to Amazon ES. QnABot attempts to match a +2. This request is then sent to Amazon ES. QnABot attempts to match a user's question to the list of questions and answers (created in the QnABot content designer) stored in Amazon ES. - - The QnABot uses full text search to find the most relevant ranked + - The QnABot uses full text search to find the most relevant ranked document from the searchable index. Relevancy ranking is based on a few properties: - - Count: How many search terms appear in a document + - Count: How many search terms appear in a document - - Frequency: How often the specified keywords occur in a given + - Frequency: How often the specified keywords occur in a given document - - Importance: How rare or new the specified keywords are and how + - Importance: How rare or new the specified keywords are and how closely the keywords occur together in a phrase - - The closer the alignment between a question associated with an item + - The closer the alignment between a question associated with an item and a question asked by the user, the greater the probability that the QnABot will choose that item as the most relevant answer. Noise words such as article, preposition in sentence constructs have lower weighting than unique keywords. - - - The keyword filter feature helps QnABot to be more accurate when - answering questions, and to admit more readily when it doesn’t know the - answer. The keyword filter feature works by using Amazon Comprehend to - determine the ‘part of speech’ that applies to each word you say to QnABot. - By default, nouns (including proper nouns), verbs, and interjections are - used as ‘keywords’. Any answer returned by QnABot must have questions + + - The keyword filter feature helps QnABot to be more accurate when + answering questions, and to admit more readily when it doesn’t know the + answer. The keyword filter feature works by using Amazon Comprehend to + determine the ‘part of speech’ that applies to each word you say to QnABot. + By default, nouns (including proper nouns), verbs, and interjections are + used as ‘keywords’. Any answer returned by QnABot must have questions that match these keywords, using the following (default) rule: - + - if there are 1 or 2 keywords, then all keywords must match. - if there are 3 or more keywords, then 75% of the keywords must match. - - If QnABot can’t find any answers that match these keyword filter rules, - then it will admit that it doesn’t know the answer rather than guessing - an answer that doesn’t match the keywords. QnABot logs every question - that it can’t answer so you can see them in the Kibana Dashboard which - we’ll show you a little later. The keyword filters feature is enabled by - default, but if it causes problems for you, you can customize its + - If QnABot can’t find any answers that match these keyword filter rules, + then it will admit that it doesn’t know the answer rather than guessing + an answer that doesn’t match the keywords. QnABot logs every question + that it can’t answer so you can see them in the Kibana Dashboard which + we’ll show you a little later. The keyword filters feature is enabled by + default, but if it causes problems for you, you can customize its settings, or disable it altogether - see [Modifying configuration settings](https://www.amazon.com/qnabot/#config-settings) - - - The Bot fulfillment Lambda function generates an Amazon ES query - containing the transcribed question. The query attempts to find the best - match from all the questions and answers you’ve previously provided, - filtering items to apply the keyword filters and using Amazon ES - relevance scoring to rank the results. Scoring is based on (i) matching - the words in the user’s question against the unique set of words used in - the stored questions (“quniqueterms”), (ii) matching the phrasing of the + + - The Bot fulfillment Lambda function generates an Amazon ES query + containing the transcribed question. The query attempts to find the best + match from all the questions and answers you’ve previously provided, + filtering items to apply the keyword filters and using Amazon ES + relevance scoring to rank the results. Scoring is based on (i) matching + the words in the user’s question against the unique set of words used in + the stored questions (“quniqueterms”), (ii) matching the phrasing of the user’s question to the text of stored questions (nested field: questions.q), - and (iii) matching the topic value assigned to the previous answer (if any) - to increase the overall relevance score when topic value (field t) matches. + and (iii) matching the topic value assigned to the previous answer (if any) + to increase the overall relevance score when topic value (field t) matches. The following example shows the query: - - ![](image3.png) - + + ![ElasticSearch Query Example](image3.png) + *Figure 2.0 -- Example Elasticsearch query* - - If an item has a large number of questions representing many unique - words, you might find that a short question (one or two words) might not - generate a high enough score to match the correct answer, even if it - exactly matches one of the stored questions in the item. You can + - If an item has a large number of questions representing many unique + words, you might find that a short question (one or two words) might not + generate a high enough score to match the correct answer, even if it + exactly matches one of the stored questions in the item. You can experiment by increasing the weight (boost) given to exact 'phrase' matches: - + a. Log in to the content designer, choose the tools menu ( ☰ ) and choose Settings. @@ -155,11 +151,11 @@ When you ask QnABot a question, a few things happen: or '4' to a larger number, say '8'. c. Scroll to the bottom and choose Save. - - - QnABot now supports Elasticsearch fuzzy matching which makes it more - tolerant of misspellings and typing errors in the user input. Enable + + - QnABot now supports Elasticsearch fuzzy matching which makes it more + tolerant of misspellings and typing errors in the user input. Enable this feature in the Settings page: - + a. Log in to the content designer, choose the tools menu ( ☰ ) and choose Settings. @@ -167,14 +163,14 @@ When you ask QnABot a question, a few things happen: to true. c. Scroll to the bottom and choose Save. - - - In previous releases, QnABot also included matches found in the - answer field to give greater relevance to items where the answer - contains multiple references to the terms used in the user’s question. - This is not always desirable, however, since term matches contained in - the answer field can make it harder to predict and control the items - you want to score highest for any question. QnABot v4.0.0 and later - disables this feature by default. You can, of course, turn it on + + - In previous releases, QnABot also included matches found in the + answer field to give greater relevance to items where the answer + contains multiple references to the terms used in the user’s question. + This is not always desirable, however, since term matches contained in + the answer field can make it harder to predict and control the items + you want to score highest for any question. QnABot v4.0.0 and later + disables this feature by default. You can, of course, turn it on again using the Settings page:: a. Log in to the content designer, choose the tools menu ( ☰ ) and @@ -185,13 +181,12 @@ When you ask QnABot a question, a few things happen: c. Scroll to the bottom and choose Save. - - The QnABot fulfillment Lambda function uses the answer field {a} (or + - The QnABot fulfillment Lambda function uses the answer field {a} (or the alternate Markdown or SSML fields if they have been populated) from the item with the highest score to create a response to send back to Amazon Lex. -Tuning recognition accuracy and monitoring -========================================== +## Tuning recognition accuracy and monitoring Now, we have a background on how the QnABot solution works to provide answers to users' questions. We will now take a look at how you can @@ -206,33 +201,33 @@ conflict with another question with similar keywords. You can solve this in a couple of ways: -1. The QnABot solution has a built-in tool that can help you quickly +1. The QnABot solution has a built-in tool that can help you quickly test all your questions and see how well they perform. Based on the results, you can then review any incorrect matches and update the needed questions. The following steps below show how you can enable testing: - - Log in to the QnABot designer and choose TEST ALL. + - Log in to the QnABot designer and choose TEST ALL. - - Use the default Filename, or enter your own. + - Use the default Filename, or enter your own. - - If you want to test only a subset of questions, you can optionally + - If you want to test only a subset of questions, you can optionally filter by {qid} prefix. Leave this field blank to test all the questions. - - Choose the TEST ALL button and wait for the tests to complete. + - Choose the TEST ALL button and wait for the tests to complete. - ![](image5.png) + ![icon](image5.png) - - Choose the view results icon - (![](image6.png)) on the far right to view the test + - Choose the view results icon + (![Test all](image6.png)) on the far right to view the test results. - ![](image7.png) + ![Test all](image7.png) Behind the scenes, a test function sends every question for every item to the QnABot via Amazon Lex and checks that the QnABot matched the question to the expected item. Any incorrect matches are highlighted in red. Test results can be viewed in the browser or downloaded as a CSV file. -2. The QnABot solution also includes a visualization tool (using +2. The QnABot solution also includes a visualization tool (using Kibana) to analyze chatbot usage. Kibana is an open-source data visualization and exploration tool used for log and time-series analytics, application monitoring, and operational intelligence use @@ -244,7 +239,7 @@ You can solve this in a couple of ways: This visualization tool is available from the QnABot designer console. - ![](image8.png) + ![menu](image8.png) The Kibana dashboard can be used to view usage history, logged utterances, no hits utterances, positive user feedback, and negative @@ -258,7 +253,7 @@ no question and answer exists in the knowledge base. This can be another way to see how well the chatbot is performing. You can then augment the knowledge base where needed. -![](image9.png) +![Kibana Dashboard](image9.png) *Figure 4.0 -- Kibana dashboard showing* No Hits *report* @@ -275,39 +270,37 @@ question. In the below example, you can see there are many variations of the same question that can be included to further train the bot to understand user input. -![](image10.png) +![no hits](image10.png) -*Figure 5.0 -- Example question variations* +_Figure 5.0 -- Example question variations_ In previous releases (before v4.0.0) repeated terms like "*how do I use*" could skew scoring toward items with higher numbers of repeating words. -In QnABot 4.0.0 and onward, this problem has been eliminated by changing the +In QnABot 4.0.0 and onward, this problem has been eliminated by changing the elasticsearch index and query to score now only on unique (not repeating) word matches. Previously we recommended to avoid repeating phrases in multiple -questions by putting all the alternatives into a single question like +questions by putting all the alternatives into a single question like `"How do I use Q&A / q and a chatbot?"`. This practice is no longer recommended. -Instead, just add new questions as needed to represent various synonyms and -phrases that a user might say to mean the same thing. +Instead, just add new questions as needed to represent various synonyms and +phrases that a user might say to mean the same thing. The example questions you provide are used to help improve the accuracy of the -answers from QnABot. +answers from QnABot. The example questions are also used to train the Lex Bot (or Alexa skill) to provide more accurate voice recognition, especially when questions are expected to contain uncommon words like acronyms, proper names, or specialized vocabulary. -When using voice, for example when using QnABot in an Amazon Connect contact -center, you will find that transcribed questions are more accurate when you -provide many examples, and use the 'LEX REBUILD' feature as described in +When using voice, for example when using QnABot in an Amazon Connect contact +center, you will find that transcribed questions are more accurate when you +provide many examples, and use the 'LEX REBUILD' feature as described in [Tuning the Bot’s Natural Language Processing](https://www.amazon.com/qnabot/#troubleshoot-tune). - These options can further help tune the chatbot to better understand user input and be able to support different ways of asking the same question. -Conclusion -========== +## Conclusion There are of course many ways we can improve the search experience, such as adding support for synonyms, lexicons, taxonomies, and [Amazon Kendra integration](https://www.amazon.com/qnabot/#kendra)