# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['webex_skills',
 'webex_skills.api',
 'webex_skills.api.middlewares',
 'webex_skills.cli',
 'webex_skills.crypto',
 'webex_skills.dialogue',
 'webex_skills.models',
 'webex_skills.static']

package_data = \
{'': ['*'],
 'webex_skills.static': ['default_domains/greeting/exit/*',
                         'default_domains/greeting/greet/*']}

install_requires = \
['cryptography>=35.0.0,<36.0.0',
 'fastapi>=0.68.1,<0.69.0',
 'pydantic[dotenv]>=1.8.2,<2.0.0',
 'requests>=2.22.0,<3.0.0',
 'typer>=0.4.0,<0.5.0',
 'uvicorn>=0.15.0,<0.16.0']

extras_require = \
{'mindmeld': ['mindmeld>=4.4.0,<5.0.0']}

entry_points = \
{'console_scripts': ['webex-skills = webex_skills.cli:main']}

setup_kwargs = {
    'name': 'webex-skills',
    'version': '2.0.6',
    'description': 'An SDK for skills for the Webex Assistant.',
    'long_description': '# Webex Skills SDK\n\nThe Webex Skills SDK is designed to simplify the process of creating a Webex Assistant Skill.\nIt provides some tools that help to easily set up a template skill, deal with encryption and \ntest the skill locally, among other things.\n\nIn this document we\'ll go through some examples of how to use this SDK to create different types\nof skills, and we\'ll also show how to use the different tools available.\n\n## Overview\n\nIn this documentation we are going to look at the following topics:\n\n- [Requirements](#requirements)\n  - [Installing the SDK](#installing-the-sdk)\n- [Simple Skills vs MindMeld Skills](#simple-skills-vs-mindmeld-skills)\n  - [Simple Skills](#simple-skills)\n  - [MindMeld Skills](#mindmeld-skills)\n- [Building a Simple Skill](#building-a-simple-skill)\n  - [Create the Skill Template](#create-the-skill-template)\n  - [Running the Template](#running-the-template)\n  - [Checking the Skill](#checking-the-skill)\n  - [Invoking the Skill](#invoking-the-skill)\n  - [Updating the Skill](#updating-the-skill)\n  - [More About the Simple Skill Handler Signature](#more-about-the-simple-skill-handler-signature)\n- [Building a MindMeld Skill](#building-a-mindmeld-skill)\n  - [Invoking the MindMeld Skill](#invoking-the-mindMeld-skill)\n  - [Building New Models](#building-new-models)\n  - [Testing the Models](#testing-the-models)\n  - [More About the MindMelmd Skill Handler Signature](#more-about-the-mindmeld-skill-handler-signature)\n- [Converting a Simple Skill into a MindMeld Skill](#converting-a-simple-skill-into-a-mindmeld-skill)\n  - [Adding the Training Data](#adding-the-training-data)\n  - [Updating the Handlers](#updating-the-handlers)\n  - [Testing the Skill](#testing-the-skill)\n- [Encryption](#encryption)\n  - [Generating Secrets](#generating-secrets)\n  - [Generating Keys](#generating-keys)\n- [Remotes](#remotes)\n  - [Creating a Remote](#creating-a-remote)\n  - [Listing Remotes](#listing-remotes)\n- [Further Development and Deployment of your Skill](#further-development-and-deployment-of-your-skill)\n\n## Requirements\n\nIn order to follow the examples in this guide, we\'ll need to install the SDK and its dependencies. Right now the SDK\nworks with Python 3.8 and above. Note that if you want to build a `MindMeld Skill` as shown later in the guide you will\nhave to use Python 3.8, since that is the only supported version for the `MindMeld Library`.\n\n### Installing the SDK\n\nFor this guide, we assume you are familiar and have installed:\n- [pyenv](https://github.com/pyenv/pyenv)\n- [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv)\n- [pip](https://pypi.org/project/pip/)\n- [Poetry](https://python-poetry.org/)\n\nWe\'ll start by creating a virtual environment:\n\n```bash\npyenv install 3.8.6\npyenv virtualenv 3.8.6 webex-skills\npyenv local webex-skills\n```\n\nWe can now install the SDK using `pip`:\n\n```bash\npip install webex-skills\n```\n\nWe should be all set, we\'ll use the SDK later in this guide. You will need to work inside the `webex-skills` virtual\nenvironment we just created.\n\n## Simple Skills vs MindMeld Skills\n\nIn a nutshell, a skill is a web service that takes a request containing some text and some context,\nanalyzes that information and responds accordingly. The level of analysis done on that information\nwill depend greatly on the tasks the skills has to accomplish. Some skills will simply need to look\nfor keywords in the text, while others will perform complex NLP in order to understand what the user\nis requesting.\n\nThis SDK has tooling for creating 2 types of skills: `Simple Skills` and `MindMed Skills`. These should \nserve as templates for basic and complex skills. Let\'s now take a look at these templates in detail.\n\n### Simple Skills\n\n`Simple Skills` do not perform any type of ML or NLP analysis on the requests. These skills are a good\nstarting point for developers to start tinkering with, and they are usually good enough for performing\ntrivial non-complicated tasks. Most developers would start with a `Simple Skill` and then migrate to a\n`MindMed Skill` if needed.\n\nMost of the time all you need is to recognize a few keywords in the text. Imagine a skill which only task\nis to turn on and off the lights in the office. Some typical queries would be:\n\n- "Turn on the lights" \n- "Turn off the lights" \n- "Turn on the lights please" \n- "Turn the lights off" \n\nIn this particular case, it will probably be good enough to just look for the words `on` and `off` in the\ntext received. If `on` is present, the skill turns on the lights and responds accordingly and vice versa.\n\nAs you can imagine, we don\'t really need any complex NLP for this skill. A simple regex would be more\nthan enough. `Simple Skills` do just that: they provide a template where you can specify the regexes you\ncare about and have them map to specific handlers (`turn_on_lights` and `turn_off_lights` in our example).\n\nWe\'ll build a simple skill in [this section](#building-a-simple-skill)  \n\n### MindMeld Skills\n\n`MindMeld Skills` perform NLP analysis on the requests. These skills are a good template for cases where the\nqueries will have a lot of variation and contain a lot of information. \n\nLet\'s take the case of a skill for ordering food. Queries for a skill like this might look like the following:\n\n- "Order a pepperoni pizza from Super Pizzas"\n- "Order a pad thai from Thailand Cafe"\n- "I want a hamburger with fries and soda from Hyper Burgers"\n\nAs we can see, using regexes for these cases can get out of hand really fast. We would need to be able to\nrecognize every single dish from every single restaurant, which might account for hundreds or thousands of regexes.\nAs we add more dishes and restaurants, updating the codebase becomes a real problem.\n\nFor cases like this, we leverage the open source [MindMeld Library](https://www.mindmeld.com/). This library makes\nit really easy to perform NLP on any text query and identify entities like `dishes`, `restaurants` and `quantities`.\nWith that, performing the required actions becomes a much easier job.\n\nWe\'ll build a MindMeld skill in [this section](#building-a-mindmeld-skill)\n\n## Building a Simple Skill\n\nLet\'s now use the SDK to build a `Simple Skill`. As in the example above, we\'ll build a skill to turn lights on and\noff according to what the user is asking. We are going to call this skill `Switch`.\n\n### Create the Skill Template\n\nIn the `pyenv` environment we created before, run the following command:\n\n```bash\nwebex-skills project init switch\n```\n\nThis will create a template for a simple skill. You should see the following file structure:\n\n![File Structure](docs/images/switch_directory.png)\n\nAs you can see, the `project` section `init` command creates a template of a skill. As usual, you can use the `--help\n` option to see the documentation for this command:\n\n```bash\n$ webex-skills project init --help\nUsage: webex-skills project init [OPTIONS] SKILL_NAME\n\n  Create a new skill project from a template\n\nArguments:\n  SKILL_NAME  The name of the skill you want to create  [required]\n\nOptions:\n  --skill-path DIRECTORY      Directory in which to initialize a skill project\n                              [default: .]\n\n  --secret TEXT               A secret for encryption. If not provided, one\n                              will be generated automatically.\n\n  --mindmeld / --no-mindmeld  If flag set, a MindMeld app will be created,\n                              otherwise it defaults to a simple app  [default:\n                              False]\n\n  --help                      Show this message and exit.\n```\n\n### Running the Template\n\nWe can now run our skill and start testing it. There are a couple ways you can run it. \n\nFirst this SDK has a `run` command, you can run it as follows:\n\n```bash\nwebex-skills skills run switch\n```\n\nYou should see an output similar to:\n```bash\nINFO:     Started server process [86661]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)\n```\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills skills run --help\nUsage: webex-skills skills run [OPTIONS] SKILL_NAME\n\nArguments:\n  SKILL_NAME  The name of the skill to run.  [required]\n\nOptions:\n  --help  Show this message and exit.\n```\n\nThe second option to run a skill is to use `uvicorn`. After all, the skill created is an `asgi` application based on\n[FastAPI](https://fastapi.tiangolo.com/):\n\n```bash\nuvicorn switch.main:api --port 8080 --reload\n```\n\nYou should see an output similar to the following:\n\n```bash\nINFO:     Will watch for changes in these directories: [\'<PATH_TO_SKILL>\']\nINFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)\nINFO:     Started reloader process [86234] using statreload\nINFO:     Started server process [86253]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\n```\n\nNow that we have the skill actually run it, we can test it.\n\n### Checking the Skill\n\nOne quick thing we can do before sending actual requests to the skill is to make sure we have everything correctly\nsetup. The sdk provides a tool for that. We can call it as:\n\n```bash\nwebex-skills skills check switch\n```\n\nIn your skill output, you should see something like this:\n```bash\nINFO:     127.0.0.1:58112 - "GET /check?signature=<SIGNATURE%3D&message=<MESSAGE>%3D HTTP/1.1" 200 OK\n```\n\nIn the SDK output you should see:\n\n```bash\nswitch appears to be working correctly\n```\n\nThat means that your skill is running and the `check` request was successfully processed.\n\n### Invoking the Skill\n\nThe SDK `skills` section has an `invoke` command which is used for sending requests to the skill. With the skill\nrunning, we can invoke it as follows:\n\n```bash\nwebex-skills skills invoke switch\n```\n\nWe can now enter a command and see a response:\n```bash\n$ webex-skills skills invoke switch\nEnter commands below (Ctl+C to exit)\n>> hi\n{ \'challenge\': \'a129d633075c9c227cc4bdcd1653b063b6dfe613ca50355fa84e852dde4b198f\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Hello I am a super simple skill\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Hello I am a super simple skill\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881359},\n                 \'text\': \'hi\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634881359}}\n```\n\nWe can see that we got all the directives back. The template skill will simply repeat or echo everything we send to it.\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills skills invoke --help\nUsage: webex-skills skills invoke [OPTIONS] [NAME]\n\n  Invoke a skill running locally or remotely\n\nArguments:\n  [NAME]  The name of the skill to invoke. If none specified, you would need\n          to at least provide the `public_key_path` and `secret`. If\n          specified, all following configuration (keys, secret, url, ect.)\n          will be extracted from the skill.\n\n\nOptions:\n  -s, --secret TEXT         The secret for the skill. If none provided you\n                            will be asked for it.\n\n  -k, --key PATH            The path of the public key for the skill.\n  -u TEXT                   The public url for the skill.\n  -v                        Set this flag to get a more verbose output.\n  --encrypt / --no-encrypt  Flag to specify if the skill is using encryption.\n                            [default: True]\n\n  --help                    Show this message and exit.\n```\n\n### Updating the Skill\n\nLet\'s now modify our skill so it does what we want: remember we want this skill to turn on and off the office lights. \n\nSimply update the `main.py` file with the following 2 handlers:\n\n```python\n@api.handle(pattern=r\'.*\\son\\s?.*\')\nasync def turn_on(current_state: DialogueState) -> DialogueState:\n    new_state = current_state.copy()\n\n    text = \'Ok, turning lights on.\'\n    \n      # Call lights API to turn on your light here.\n    \n    new_state.directives = [\n        responses.Reply(text),\n        responses.Speak(text),\n        responses.Sleep(10),\n    ]\n\n    return new_state\n\n\n@api.handle(pattern=r\'.*\\soff\\s?.*\')\nasync def turn_off(current_state: DialogueState) -> DialogueState:\n    new_state = current_state.copy()\n\n    text = \'Ok, turning lights off.\'\n    \n    # Call lights API to turn off your light here.\n    \n    new_state.directives = [\n        responses.Reply(text),\n        responses.Speak(text),\n        responses.Sleep(10),\n    ]\n\n    return new_state\n```\n\nThe SDK provides the `@api.handle` decorator, and as you can see it can take a `pattern` parameter which is then\napplied to the query to determine if the handler applies to the query being processed. This way, we can add\na few handlers by simple creating regexes that match the type of queries we want to support.\n\nIn the example above, we have added regexes to identify the `on` and `off` keywords. Which mostly tell what the user\nwants to do.\n\nBy using the `skill invoke` command we can run a few tests:\n\n```bash\n>> turn on the lights\n{ \'challenge\': \'56094568e18c66cb89eca8eb092cc3bbddcd64b4c0442300cfbe9af67183e260\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn on the lights\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634881502}}\n>> turn off the lights\n{ \'challenge\': \'2587110a9c97ebf9ce435412c0ea6154eaef80f384ac829cbdc679db483e5beb\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn on the lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn off the lights\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634881502}}\n>> turn the lights on\n{ \'challenge\': \'ce24a510f6a7025ac5e4cc51b082483bdbcda31836e2c3567780c231e5674c59\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn on the lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn off the lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634881502},\n                 \'text\': \'turn the lights on\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634881502}}\n```\n\nIn the examples above, we can see the skill responding with the correct message. In a real skill, we would also call an\nAPI to actually perform the action the user wants.\n\n### More About the Simple Skill Handler Signature\n\nWhen we created our `Simple Skill`, our handler had the following siganture:\n\n```python\nasync def greet(current_state: DialogueState) -> DialogueState:\n```\n\nYou can also update the handler to give you another parameter:\n\n```python\nasync def greet(current_state: DialogueState, query: str) -> DialogueState:\n```\n\nThe `query` string parameter will give you the query that was sent to the skill, this can be used in cases where you\nneed to further analyze the text.\n\n## Building a MindMeld Skill\n\nWhen a skill is very complex and needs to handle many commands, usually the best approach is to create a MindMeld \nbased skill. This will allow us to use NLP to better classify the request and extract important information we need\nfrom it. We are not going to go very deep into how a MindMeld application works, but there are a lot of resources in the\nofficial [MindMeld library](https://www.mindmeld.com/) site.\n\nThis SDK also has tooling in place for setting up a MindMeld based skill. For that, we can use the `project init`\ncommand with the `--mindmeld` flag set. But first, we need to add the extra dependency `mindmeld`:\n\n```bash\npip install "webex-skills[mindmeld]"\n```\n\nLet\'s create a skill called `greeter`:\n\n```bash\nwebex-skills project init greeter --mindmeld\n```\n\nThe folder structure should look like this:\n\n![File Structure](docs/images/greeter_directory.png)\n\n\n### Invoking the MindMeld Skill\n\nWith the `greeter` skill running, let\'s try invoking it using the SDK `skills invoke` command:\n\n```bash\n$ poetry run webex-skills skills invoke greeter\n\nEnter commands below (Ctl+C to exit)\n>> hi\n{ \'challenge\': \'a31ced06481293abd8cbbcffe72d712e996cf0ddfb56d981cd1ff9c1d9a46bfd\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Hello I am a super simple skill using NLP\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Hello I am a super simple skill using NLP\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634880182},\n                 \'text\': \'hi\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634880182}}\n```\n\nAs you can see, the response of a `MindMeld Skill` has the same shape as a `Simple Skill`, it\'s really just the internals\nof the skill that change.\n\n### Building New Models\n\nSince `MindMeld Skills` use NLP, we need to retrain the ML models each time we modify the training data. The SDK\nprovides a the \'nlp build` command for this purpose:\n\n```bash\nwebex-skills nlp build greeter\n```\n\nAfter running this, the models will be refreshed with the latest training data.\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills nlp build --help\nUsage: webex-skills nlp build [OPTIONS] [NAME]\n\n  Build nlp models associated with this skill\n\nArguments:\n  [NAME]  The name of the skill to build.\n\nOptions:\n  --help  Show this message and exit.\n```\n\n### Testing the Models\n\nAnothoer useful command in this SDK is the `nlp process` command. It\'s similar to the `skill invoke` command, in the \nsense that it will send a query to the running skill. However, the query will only be run through the NLP pipeline so\nwe can see how it was categorized. Let\'s look at an example:\n\n```bash\n$ webex-skills nlp process greeter\n\nEnter a query below (Ctl+C to exit)\n>> hi\n{\'domain\': \'greeting\', \'entities\': [], \'intent\': \'greet\', \'text\': \'hi\'}\n>>\n```\n\nYou can see that now the response only contains the extracted ANLP pieces. This command is very useful for testing\nyour models as you work on improving them.\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills nlp process --help\nUsage: webex-skills nlp process [OPTIONS] [NAME]\n\n  Run a query through NLP processing\n\nArguments:\n  [NAME]  The name of the skill to send the query to.\n\nOptions:\n  --help  Show this message and exit.\n```\n\n### More About the MindMeld Skill Handler Signature\n\nWhen we created our `Simple Skill`, our handler had the following siganture:\n\n```python\nasync def greet(current_state: DialogueState) -> DialogueState:\n```\n\nYou can also update the handler to give you another parameter:\n\n```python\nasync def greet(current_state: DialogueState, , processed_query: ProcessedQuery) -> DialogueState:\n```\n\nThe `processed_query` parameter will give you the `text` that was sent to the skill, the `domain` and `intent` identified\nas well as the `entities` extracted from the query. This can be useful in cases where you want to use the entities as\npart of the skill logic. We\'ll show an example of this in the \n[Converting a Simple Skill into a MindMeld Skill](#converting-a-simple-skill-into-a-mindMeld-skill) section.\n\n## Converting a Simple Skill into a MindMeld Skill\n\nIn many cases, when you start creating a new skill, you would probably start with a `Simple Skill`. As your skill grows\nin complexity, you might need to convert it into a `Mindmeld Skill`. This SDK makes that conversion very easy, we are\ngoing to take a look into that next.\n\nConsider the [Switch Skill](#building-a-simple-skill) we built above. It really just recognizes if we want to turn \nsomething on or off. But what if we have multiple lights? We could in principle look at the query and try to extract\nthe light we want to switch with another regex, but that can be very brittle. Instead, let\'s try turning it into a \n`MindMeld Skill`.\n\n### Adding the Training Data\n\nLet\'s start by creating our domains, intents and entities. We still want to just turn on and off lights, but we want to\nbe able to identify which light to switch. On the `switch` app, create the following folder structure:\n\n![File Structure](docs/images/smart_lights_directory.png)\n\nAs you can see, we have created a `greeting` and `smart_lights` domains, the intents `greet`, `turn_lights_on`\nand `turn_lights_off`, and the entities `all` and `location`. We now need to add training data to make this setup work.\n\nNormally, you would need to collect training data manually to create your domain, intents and entities. But, for this\nguide, we are going to take a shortcut. If you are familiar with the [MindMeld library](https://www.mindmeld.com/), you\nhave probably seen that it comes with `Blueprint` applications, so we are going to borrow some data from one of them.\n\nGo to this [repo](https://github.com/CiscoDevNet/mindmeld-blueprints/tree/develop/blueprints/home_assistant/domains)\nand copy the `train.txt` files from the corresponding intents into our folders. Do the same for entities, from \n[here](https://github.com/CiscoDevNet/mindmeld-blueprints/tree/develop/blueprints/home_assistant/entities) copy the \ncorresponding `gazetteer.txt` and `mapping.json` files into our folders. Our directory should now look like this:\n\n![File Structure](docs/images/smart_lights_dir_complete.png)\n\n### Updating the Handlers\n\nThe next thing we\'ll do is to convert our logic to turn it into a `MindMeld Skill`. The steps are very simple, let\'s\nstart. We\'ll make all the following changes in `main.py`:\n\nInstead of making the variable `app` a `SimpleApi`, make it a `MindMeldAPI`:\n```python\napi = MindmeldAPI()\n```\n\nIn the `@api.handle` decorators, add the intent you want to handle instead of the pattern:\n\n```python\n@api.handle(intent=\'turn_lights_on\')\nasync def turn_on(current_state: DialogueState, processed_query: ProcessedQuery) -> DialogueState:\n...\n\n@api.handle(intent=\'turn_lights_off\')\nasync def turn_off(current_state: DialogueState, processed_query: ProcessedQuery) -> DialogueState:\n```\n\nFinally, add some logic to complement the response with the location entity if available. In the `turn_on` handler, \nreplace the line:\n\n```python\n    text = \'Ok, turning lights on.\'\n```\n\nWith the following logic:\n\n```python\n    if len(processed_query.entities) > 0:\n        entity = processed_query.entities[0]\n        if entity[\'type\'] == \'location\':\n            text = f\'Ok, turning the {entity["text"]} lights on.\'\n        else:\n            text = \'Ok, turning all lights on.\'\n    else:\n        text = \'Ok, turning all lights on.\'\n```\n\nDo the corresponding change to the `turn_off` handler.\n\nYou will also need to import the new classes you are using: `MindmeldAPI` and `ProcessedQuery`.\n\nThat\'s it! We now have NLP support in our skill. \n\nAll in all your `main.py` should look like this:\n\n```python\nfrom webex_skills.api import MindmeldAPI\nfrom webex_skills.dialogue import responses\nfrom webex_skills.models.mindmeld import DialogueState, ProcessedQuery\n\napi = MindmeldAPI()\n\n\n@api.handle(default=True)\nasync def greet(current_state: DialogueState) -> DialogueState:\n    text = \'Hello I am a super simple skill\'\n    new_state = current_state.copy()\n\n    new_state.directives = [\n        responses.Reply(text),\n        responses.Speak(text),\n        responses.Sleep(10),\n    ]\n\n    return new_state\n\n\n@api.handle(intent=\'turn_lights_on\')\nasync def turn_on(current_state: DialogueState, processed_query: ProcessedQuery) -> DialogueState:\n    new_state = current_state.copy()\n\n    if len(processed_query.entities) > 0:\n        entity = processed_query.entities[0]\n        if entity[\'type\'] == \'location\':\n            text = f\'Ok, turning the {entity["text"]} lights on.\'\n        else:\n            text = \'Ok, turning all lights on.\'\n    else:\n        text = \'Ok, turning all lights on.\'\n\n    # Call lights API to turn on your light here.\n\n    new_state.directives = [\n        responses.Reply(text),\n        responses.Speak(text),\n        responses.Sleep(10),\n    ]\n\n    return new_state\n\n\n@api.handle(intent=\'turn_lights_off\')\nasync def turn_off(current_state: DialogueState, processed_query: ProcessedQuery) -> DialogueState:\n    new_state = current_state.copy()\n\n    if len(processed_query.entities) > 0:\n        entity = processed_query.entities[0]\n        if entity[\'type\'] == \'location\':\n            text = f\'Ok, turning the {entity["text"]} lights off.\'\n        else:\n            text = \'Ok, turning all lights off.\'\n    else:\n        text = \'Ok, turning all lights off.\'\n\n    # Call lights API to turn off your light here.\n\n    new_state.directives = [\n        responses.Reply(text),\n        responses.Speak(text),\n        responses.Sleep(10),\n    ]\n\n    return new_state\n```\n\n### Testing the Skill\n\nWe can now test the skill to make sure it works as intended. Since we just added our training data, we need to build\nthe models first. Since we have entities defined, we will need to have Elasticsearch running for the `nlp build`\ncommand to work properly. You can refer to the \n[MindMeld Getting Started Documentation](https://www.mindmeld.com/docs/userguide/getting_started.html) for a guide on\nhow to install and run Elasticsearch.\n\nRemember that in order to build `MindMeld` skills we need the extra `mindmeld` dependency:\n\n```bash\npip install "webex-skills[mindmeld]"\n```\n\nWith that out of the way, you can build the models:\n\n```bash\nwebex-skills nlp build switch\n```\n\nWe can now run our new skill:\n\n```bash\nwebex-skills skills run switch\n```\n\nFinally, we can use the invoke method to send a couple commands:\n\n```bash\n$ webex-skills skills invoke switch\nEnter commands below (Ctl+C to exit)\n>> hi\n{ \'challenge\': \'bd57973f82227c37fdaed9404f86be521ecdbefc684e15319f0d51bbecbb456e\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Hello I am a super simple skill\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Hello I am a super simple skill\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'hi\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634934720}}\n>> turn on the lights\n{ \'challenge\': \'ff8e57bcb94b90c736e11dd79ae5fe3b269dcc450d7bb07b083de73d9a22d5e8\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning all lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning all lights on.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'hi\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn on the lights\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634934720}}\n>> turn off the kitchen lights\n{ \'challenge\': \'300e7adfe2f199998f152793b944bc597c5145e991dda621e1495e2e06cebb6e\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning the kitchen lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning the kitchen lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'hi\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn on the lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn off the kitchen lights\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634934720}}\n>> turn off all the lights\n{ \'challenge\': \'e92a90c304c9ef96a3edf31c6ffb10606739cdf98ad34cfd57314b20138ad59b\',\n  \'directives\': [ {\'name\': \'reply\', \'payload\': {\'text\': \'Ok, turning all lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'speak\', \'payload\': {\'text\': \'Ok, turning all lights off.\'}, \'type\': \'action\'},\n                  {\'name\': \'sleep\', \'payload\': {\'delay\': 10}, \'type\': \'action\'}],\n  \'frame\': {},\n  \'history\': [ { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'hi\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn on the lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn off the kitchen lights\'},\n               { \'context\': {},\n                 \'directives\': [],\n                 \'frame\': {},\n                 \'history\': [],\n                 \'params\': { \'allowed_intents\': [],\n                             \'dynamic_resource\': {},\n                             \'language\': \'en\',\n                             \'locale\': None,\n                             \'target_dialogue_state\': None,\n                             \'time_zone\': \'UTC\',\n                             \'timestamp\': 1634934720},\n                 \'text\': \'turn off all the lights\'}],\n  \'params\': { \'allowed_intents\': [],\n              \'dynamic_resource\': {},\n              \'language\': \'en\',\n              \'locale\': None,\n              \'target_dialogue_state\': None,\n              \'time_zone\': \'UTC\',\n              \'timestamp\': 1634934720}}\n>>\n```\n\nWe have now converted a `Simple Skill` into a `MindMeld Skill`.\n\n## Encryption\n\nSkills require encryption in order to safely send and receive requests. For the encryption to work properly, we need\nto provide a key pair and a secret for our skills. As we saw in the [Simple Skill](#building-a-simple-skill) and\n[MindMeld Skill](#building-a-mindMeld-skill) examples above, the keys and secret will be automatically created for us\nwhen we use the SDK to create a template. However, the SDK also has tools to create these manually if we need to.\n\nThese tools are under the `crypto` section which we\'ll try next.\n\n### Generating Secrets\n\nGenerating a secret is very simple, simply use the `generate-secret` command:\n\n```bash\nwebex-skills crypto generate-secret\n```\n\nA secret will be logged to the terminal, which then you can add to your app.\n\n### Generating Keys\n\nGenerating a key pair is very simple, simply use the `generate-keys` command:\n\n```bash\nwebex-skills crypto generate-keys\n```\n\nA key pair will be created.\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills crypto generate-keys --help\nUsage: webex-skills crypto generate-keys [OPTIONS] [FILEPATH]\n\n  Generate an RSA keypair\n\nArguments:\n  [FILEPATH]  The path where to save the keys created. By default, they get\n              created in the current directory.\n\n\nOptions:\n  --name TEXT  The name to use for the keys created.  [default: id_rsa]\n  --help       Show this message and exit.\n```\n\n## Remotes\n\nThis SDK also has the notion of `remotes`. That is, skills that are already running and even deployed somewhere, but\nwe still want to be able to test them using the SDK. \n\nThe process for using remotes is very simple, we\'re going to look into that now. We\'ll use the `remote` section of the\nSDK.\n\nRemotes are automatically added for skills created with this SDK.\n\n### Creating a Remote\n\nYou can create a remote by using the `create` command. Let\'s recreate a remote for a skill called `echo`:\n\n```bash\nwebex-skills remote create echo\n```\n\nFollow the prompts to create a new remote:\n\n```bash\n$ webex-skills remote create echo\n\nSecret: <SECRET>\nPublic key path [id_rsa.pub]: <KEY_PATH>\nURL to invoke the skill [http://localhost:8080/parse]: <URL>\n```\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ webex-skills remote create --help\n\nUsage: webex-skills remote create [OPTIONS] NAME\n\n  Add configuration for a new remote skill to the cli config file\n\nArguments:\n  NAME  The name to give to the remote.  [required]\n\nOptions:\n  -u TEXT            URL of the remote. If not provided it will be requested.\n  -s, --secret TEXT  The skill secret. If not provided it will be requested.\n  -k, --key PATH     The path to the public key. If not provided it will be\n                     requested.\n\n  --help             Show this message and exit.\n```\n\n### Listing Remotes\n\nYou can also list the remotes you currently have set up. For that you can use the `list` command:\n\n```bash\nwebex-skills remote list\n```\n\nYou will get something like:\n```bash\n{\'echo\': {\'name\': \'echo\',\n          \'public_key_path\': \'<KEY_PATH>\',\n          \'secret\': \'<SECRET>\',\n          \'url\': \'http://localhost:8080/parse\'},\n \'greeter\': {\'app_dir\': \'<APP_DIR>>\',\n             \'name\': \'greeter\',\n             \'private_key_path\': \'<KEY_PATH>\',\n             \'project_path\': \'<PROJECT_PATH>\',\n             \'public_key_path\': \'<KEY_PATH>\',\n             \'secret\': \'<SECRET>\',\n             \'url\': \'http://localhost:8080/parse\'},\n \'switch\': {\'app_dir\': \'<APP_DIR>>\',\n            \'name\': \'switch\',\n            \'private_key_path\': \'<KEY_PATH>\',\n            \'project_path\': \'<PROJECT_PATH>\',\n            \'public_key_path\': \'<KEY_PATH>\',\n            \'secret\': \'<SECRET>\',\n            \'url\': \'http://localhost:8080/parse\'}}\n```\n\nAs usual, you can use the `--help` option to see the documentation for this command:\n\n```bash\n$ run webex-skills remote list --help\n\nUsage: webex-skills remote list [OPTIONS]\n\n  List configured remote skills\n\nOptions:\n  --name TEXT  The name of a particular skill to display.\n  --help       Show this message and exit.\n```\n\n## Further Development and Deployment of your Skill\n\nYou might have noticed that when you create a new skill, there is a `pyproject.toml` file that gets added to the\nproject. This file already contains the dependencies needed to run the skill independently (without using the SDK\'s\n`skills run` command.) \n\nIn order to run the skill independently, you can use [Poetry](https://python-poetry.org/) to manage your dependencies,\nor you can also replace the `pyproject.toml` with a `requirements.txt` file if you want to use \n[pip](https://pypi.org/project/pip/) instead.\n\nFor deployment, the SDK builds the skill based on [FastAPI](https://fastapi.tiangolo.com/), which offers multiple\noptions for running an app in production. You can find more information in their \n[Deployment Documentation](https://fastapi.tiangolo.com/deployment/).\n',
    'author': 'James Snow',
    'author_email': 'jasnow@cisco.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/cisco/webex-assistant-sdk',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'entry_points': entry_points,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
