@kognitalabs/shallot-framework
v1.0.1
Published
Small mild-flavored onion - or code clustered bulbs used for seasoning your chatbot.
Downloads
2
Readme
🧅 Shallot Framework (Kognita Lab)
Small mild-flavored onion - or code - clustered bulbs used for seasoning your chatbot.
Requirements
The Shallot Framework was made to have a perfect integration with a few services, making them required, are them:
Microsoft Language Understanding (LUIS) - A machine learning-based service to build natural language into apps, bots, and IoT devices. Quickly create enterprise-ready, custom models that continuously improve. -
Paid Account Required
Sunshine: Omnichannel messaging platform - Sunshine Conversations connects your business software to all the world’s messaging channels for a more human customer experience. -
Paid Account Required
MySQL Database - MySQL is a relational database management system based on SQL – Structured Query Language.
RabbitMQ Message Broker - With tens of thousands of users, RabbitMQ is one of the most popular open source message brokers. From T-Mobile to Runtastic, RabbitMQ is used worldwide at small startups and large enterprises.
If your stack already meet the requirements. You can skip to the next step of the README.
What is Shallot? / Why use Shallot?
The Shallot Framework is a framework that facilitates the development of chatbots.
With a simple language, it allows chatbot actions to be as action-oriented writing. You basically describe what the dialog
will do.
Let's assume we have the welcome dialog. Just use set dialog.name
and at the first parameter we use 'welcome', after that in the second parameter we use a callback which will receive the actions to take for the dialog specified.
event.dialog.name('welcome', async (dialog) => {
Inside the callback just specify as example: For the HumanHelp intent forIntent
change the dialog to human_help changeDialogTo
and send the predicted intent response sendPredictedIntentAnswer
.
await dialog.forIntent('HumanHelp')
.changeDialogTo('human_help')
.sendPredictedIntentAnswer();
When putting it all together the result would be the following:
event.dialog.name('welcome', async (dialog) => {
await dialog.forIntent('HumanHelp')
.changeDialogTo('human_help')
.sendPredictedIntentAnswer();
})
Installation
Install Shallot Framework with yarn:
yarn add @kognitalabs/shallot-framework
Usage/Examples
Do you know how to create a chatbot? It's very easy, follow the steps below:
import { ShallotFramework as framework } from '@kognitalabs/shallot-framework';
framework.instance(async (shallot) => {
/**
* CONNECT REQUIRED SERVICES
*/
await shallot.connectDatabase({
host: process.env.MYSQL_HOST,
port: process.env.MYSQL_PORT,
username: process.env.MYSQL_USERNAME,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
synchronize: true // When executing for the first time, set as true for database tables sync.
});
await shallot.connectLUIS({
applicationId: process.env.LUIS_BOT_ID,
endpoint: process.env.LUIS_ENDPOINT,
endpointKey: process.env.LUIS_PREDICTION_KEY,
name: process.env.LUIS_BOT_NAME
});
await shallot.connectSunshine({
appId: process.env.SMOOCH_APP_ID,
keyId: process.env.SMOOCH_API_KEY_ID,
keySecret: process.env.SMOOCH_API_KEY_SECRET
});
await shallot.connectAMQP(process.env.AMQP_URL);
/**
* CONTROL YOUR BOT ACTIONS
*/
/**
* When the Sunshine event was conversation:message on RabbitMQ queue named conversation execute this callback(err, event) => Promise<AMQP.EventManager>
*/
void shallot.when(
'conversation:message', 'conversation', async (err, event) => {
if (err !== undefined) {
return event;
}
/**
* If the conversation dialog in the received event is named 'welcome' returns a callback(dialog).
*/
void event.dialog.name('welcome', async (dialog) => {
await dialog.forNotListedOnes()
.changeDialogTo('default')
.sendIntentReplaceableAnswer('Greeting', {
displayName: () => {
const nameCompose = [];
nameCompose.push(dialog.props.customer.props.givenName);
nameCompose.push(dialog.props.customer.props.surname);
return nameCompose.join(' ');
}
});
});
/**
* If the conversation dialog in the received event is named 'default' returns a callback(dialog).
*/
void event.dialog.name('default', async (dialog) => {
await dialog.forIntent('HumanHelp')
.changeDialogTo('human_help')
.sendPredictedIntentAnswer();
await dialog.forIntent('HowTo')
.sendIntentAnswer('PostGreeting');
await dialog.forIntent('Greeting')
.sendHandWritedAnswer('Como posso ajudar?');
await dialog.forIntent('None')
.changeDialogTo('confusing')
.sendPredictedIntentAnswer();
await dialog.forNotListedOnes()
.sendPredictedIntentAnswer();
});
/**
* If the conversation dialog in the received event is named 'chating_with_human' returns a callback(dialog).
*/
void event.dialog.name('chating_with_human', async (dialog) => {
// Since this is an human help case, skip bot interference.
dialog.skip();
});
void event.dialog.noneOf(['chating_with_human', 'default', 'welcome'], async (dialog) => {
await dialog.forIntent('HowTo')
.sendIntentAnswer('PostGreeting');
await dialog.forNotListedOnes()
.changeDialogTo('default')
.sendPredictedIntentAnswer();
});
/**
* Required function at the end, always run execute()
*/
event.dialog.execute();
return event;
}
);
});
Classes & Functions
(class): ShallotFramework
This class is a singleton and an instance can be received when you call for the method instance(callback)
as you can see here:
ShallotFramework.instance(async (shallot) => {
...
// The shallot receveid value is the singleton instance itself.
});
(method): ShallotFramework.when
(event
: EventType, queue
: string, callback
: CallbackFunction<AMQP.EventManager>): Promise< void >
This is one of the main methods of Shallot. It is the first line of the three-level callback structure. It is responsible to filter the event type, set a queue to list to and return the all formated Smooch Event into a Callback. So to make it work, use it!
You can define multiple events to the same queue.
@param
event — The Event type as 'conversation:message'.
@param
queue — The RabbitMQ queue to attach to.
@param
callback — The magic portal to process the action! The callback return two values, are them Error
and EventManager
.
e.g: usage and return:
...
void shallot.when(
'conversation:message', 'conversation', async (err, event) => {
if (err !== undefined) {
return event;
}
void event.dialog.name('welcome', async (dialog) => {
...
(class): EventManager
This class is the non-error return value of the ShallotFramework.when
method.
Test Coverage
Currently our tests cover 51.06% percent of the lines in all files:
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
:-------------------------------------------|---------|----------|---------|---------|----------------------------------------
All files |42.42| 33.9 |39.82|51.06|
src | 80 | 73.91 | 60 | 86.66 |
envvars.ts | 100 | 85.71 | 100 | 100 | 3
index.ts | 73.33 | 68.75 | 50 | 80 | 6-7
src/core | 31.42 | 17.84 | 28.12 | 37.18 |
framework.module.ts | 21.96 | 16.74 | 14.81 | 26.19 | ...162,167-175,181-210,216-237,252-286
injection.module.ts | 96.77 | 75 | 100 | 96.77 | 41
src/core/domain | 71.26 | 43.85 | 64.28 | 74.69 |
aggregate-root.abstract.ts | 75 | 55.55 | 66.66 | 81.81 | 5-6,11,33
entity.abstract.ts | 55.55 | 25 | 50 | 55.55 | 7,15-24
identifier.class.ts | 64.7 | 0 | 60 | 64.7 | 11-17,27
mapper.abstract.ts | 100 | 100 | 50 | 100 |
unique-id.abstract.ts | 78.26 | 57.14 | 75 | 85.71 | 5-6,11
src/core/logic | 50.25 | 36.48 | 51.28 | 55.97 |
generic-app.error.ts | 70.73 | 52.08 | 72.72 | 78.78 | 5-6,11,20,39-44
guard.class.ts | 39.74 | 31.66 | 44.44 | 42.62 | 10-16,46,59-81,88-96,104-128
result.class.ts | 50 | 25 | 42.1 | 56.92 | ...4-35,50-64,71,74,77,84,87,90,96,100
src/infrastructure/amqplib | 15.98 | 16.26 | 15 | 18.77 |
amqp.module.ts | 15.98 | 16.26 | 15 | 18.77 | ...,94-123,129-133,139-211,223-319,323
src/infrastructure/smooch | 29.54 | 23.23 | 21.95 | 40.67 |
smooch.module.ts | 29.54 | 23.23 | 21.95 | 40.67 | ...114-126,132-151,182-189,193-195,200
src/infrastructure/typeorm/entities | 75.45 | 55.4 | 59.61 | 85.62 |
answer-action.entity.ts | 76.47 | 53.06 | 61.53 | 85.71 | 5-6,11,20,44,70
answer.entity.ts | 77.19 | 59.18 | 60 | 87.23 | 5-6,11,20,67,77
bot-message.entity.ts | 73.58 | 53.06 | 53.33 | 83.72 | 5-6,11,20,42,83,94
conversation.entity.ts | 73.58 | 53.06 | 53.33 | 83.72 | 5-6,11,20,44,74,85
customer.entity.ts | 75.55 | 57.44 | 66.66 | 86.11 | 5-6,11,20,73
intent.entity.ts | 73.8 | 57.44 | 66.66 | 84.84 | 5-6,11,20,47
standard-column-group.abstract.ts | 86.2 | 58.06 | 100 | 95.65 | 5
user-message.entity.ts | 71.92 | 53.06 | 47.05 | 82.6 | 5-6,11,20,43,86,93,104
src/modules/dialogs | 78.57 | 68.75 | 75 | 77.77 |
index.ts | 78.57 | 68.75 | 75 | 77.77 | 6-7
src/modules/dialogs/cases | 50 | 49.13 | 55.67 | 62 |
create-bot-answer.case.ts | 72.02 | 65.11 | 91.3 | 88 | 5,15,32-33,120,142-144,148,153-155
create-conversation.case.ts | 70.18 | 65.71 | 87.09 | 82.09 | ...123,161,182-184,197-198,232,240-241
predict-user-message-utterance.case.ts | 22.31 | 19.81 | 13.04 | 30.76 | 5,12-17,21-44,69,77-85,97-121
send-customer-answer.case.ts | 18.18 | 19.13 | 15 | 23.59 | 5,12-17,21-44,67,75-135
src/modules/dialogs/domain | 57.14 | 44.84 | 52.5 | 67.76 |
bot-answer.entity.ts | 79.41 | 56.52 | 70 | 84.37 | 5-6,11,43,73
conversation.aggregate.ts | 44.86 | 40.45 | 40.81 | 56.84 | ...192,195-197,200-204,209-210,213-214
customer.entity.ts | 78.12 | 52.63 | 70 | 83.33 | 5-6,11,31,39
message.entity.ts | 80.55 | 52.38 | 72.72 | 85.29 | 5-6,11,36,53
src/modules/dialogs/errors | 82.53 | 42.85 | 68 | 85.24 |
dialog.errors.ts | 82.53 | 42.85 | 68 | 85.24 | 5-6,11,38,48,58,78,88,98
src/modules/dialogs/infrastructure | 20.17 | 15.83 | 16.43 | 22.89 |
composite-query.ts | 20.17 | 15.83 | 16.43 | 22.89 | ...393,396-433,444,447-471,482,485-487
src/modules/dialogs/infrastructure/mappers | 57.26 | 39.65 | 56 | 60.69 |
bot-answer.mapper.ts | 55.93 | 41.81 | 58.33 | 58.82 | 5-6,11,20,40,43-57,62-75
conversation.mapper.ts | 57.37 | 41.81 | 50 | 61.53 | 5-6,11,20,41,44-58,63-70
customer.mapper.ts | 58.92 | 45.09 | 58.33 | 62.5 | 5-6,11,20,38,41-52,56-62
message.mapper.ts | 56.89 | 32.39 | 58.33 | 60 | 5-6,11,20,38,42-59,64-75
src/modules/dialogs/repositories | 23.19 | 17.67 | 20.54 | 33.25 |
answer.repository.ts | 19.49 | 17.51 | 19.35 | 28.71 | ...29,33-56,60-68,80-82,85-101,107-144
bot-answer.repository.ts | 25.89 | 18.75 | 24 | 39.7 | 5-6,11,20,24-29,33-56,69,72-90
conversation.repository.ts | 22.02 | 15.67 | 17.64 | 28.45 | ...,75-79,82-91,98-121,127-144,150-176
customer.repository.ts | 25.42 | 18.42 | 21.42 | 37.83 | 5-6,11,20,24-29,33-56,70,73-83,89-103
message.repository.ts | 25 | 18.42 | 21.42 | 36.84 | 5-6,11,20,24-29,33-56,69,72-86,92-101
Authored/Licensed
All rights reserved to:
Contributors
- @iagocalazans - Iago Calazans [email protected] (Project Engineer & Architect)
- @Gabrielpatrola - Gabriel Patrola [email protected]
- @Eric-Souza - Eric Souza