Skip to content

Commit

Permalink
Add intents filter parameter in CLI (#858)
Browse files Browse the repository at this point in the history
* Add documentation about intents filters

* Add intents filter parameter in parsing CLI

* Improve handling of special characters in intents filters
  • Loading branch information
adrienball authored Nov 7, 2019
1 parent 98eba8b commit e5b6508
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 14 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file.

## [Unreleased]
### Added
- Add intents filter parameter in parsing CLI [#858](https://github.com/snipsco/snips-nlu/pull/858)
- Add documentation about intents filters [#858](https://github.com/snipsco/snips-nlu/pull/858)

## [0.20.1] - 2019-09-04
### Added
- Allow to bypass the model version check [#830](https://github.com/snipsco/snips-nlu/pull/830)
Expand Down
8 changes: 8 additions & 0 deletions docs/source/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ You can also pass a single query using an optional parameter:
snips-nlu parse path/to/persisted_engine -q "my query"
In both previous examples, you can perform parsing using
:ref:`intents filters <intents_filters>` by providing a comma-separated list
of intents:

.. code-block:: bash
snips-nlu parse path/to/persisted_engine -f intent1,intent3
.. _version_cli:

Evaluation
Expand Down
2 changes: 1 addition & 1 deletion docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Let's try to parse something now!
import json
parsing = nlu_engine.parse(u"What will be the weather in San Francisco next week?")
parsing = nlu_engine.parse("What will be the weather in San Francisco next week?")
print(json.dumps(parsing, indent=2))
Expand Down
29 changes: 24 additions & 5 deletions docs/source/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ At this point, we can try to parse something:

.. code-block:: python
engine.parse(u"Please give me some lights in the entrance !")
engine.parse("Please give me some lights in the entrance !")
That will raise a ``NotTrained`` error, as we did not train the engine with
the dataset that we created.
Expand Down Expand Up @@ -202,7 +202,7 @@ We are now ready to parse:

.. code-block:: python
parsing = engine.parse(u"Hey, lights on in the lounge !")
parsing = engine.parse("Hey, lights on in the lounge !")
print(json.dumps(parsing, indent=2))
You should get the following output (with a slightly different ``probability``
Expand Down Expand Up @@ -242,7 +242,7 @@ to extract the slots while providing the intent:

.. code-block:: python
parsing = engine.get_slots(u"Hey, lights on in the lounge !", "turnLightOn")
parsing = engine.get_slots("Hey, lights on in the lounge !", "turnLightOn")
print(json.dumps(parsing, indent=2))
This will give you only the extracted slots:
Expand Down Expand Up @@ -270,7 +270,7 @@ classification and get the list of intents along with their score:

.. code-block:: python
intents = engine.get_intents(u"Hey, lights on in the lounge !")
intents = engine.get_intents("Hey, lights on in the lounge !")
print(json.dumps(intents, indent=2))
This should give you something like below:
Expand Down Expand Up @@ -351,6 +351,25 @@ intent. Here is the kind of output you should get if you try parsing
The **None** intent is represented by a ``None`` value in python which
translates in JSON into a ``null`` value.

.. _intents_filters:

Intents Filters
---------------

In some cases, you may have some extra information regarding the context in
which the parsing occurs, and you may already know that some intents won't be
triggered. To leverage that, you can use *intents filters* and restrict the
parsing output to a given list of intents:

.. code-block:: python
parsing = engine.parse("Hey, lights on in the lounge !",
intents=["turnLightOn", "turnLightOff"])
This will improve the accuracy of the predictions, as the NLU engine will
exclude the other intents from the classification task.

Persisting
----------

Expand All @@ -371,7 +390,7 @@ And load it:
loaded_engine = SnipsNLUEngine.from_path("path/to/directory")
loaded_engine.parse(u"Turn lights on in the bathroom please")
loaded_engine.parse("Turn lights on in the bathroom please")
Alternatively, you can persist/load the engine as a ``bytearray``:
Expand Down
22 changes: 16 additions & 6 deletions snips_nlu/cli/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,20 @@ def add_parse_parser(subparsers, formatter_class):
"interactive behavior.")
subparser.add_argument("-v", "--verbosity", action="count", default=0,
help="Increase output verbosity")
subparser.add_argument("-f", "--intents-filter", type=str,
help="Intents filter as a comma-separated list")
subparser.set_defaults(func=_parse)
return subparser


def _parse(args_namespace):
return parse(args_namespace.training_path, args_namespace.query,
args_namespace.verbosity)
args_namespace.verbosity, args_namespace.intents_filter)


def parse(training_path, query, verbose=False):
def parse(training_path, query, verbose=False, intents_filter=None):
"""Load a trained NLU engine and play with its parsing API interactively"""
import csv
import logging
from builtins import input, str
from snips_nlu import SnipsNLUEngine
Expand All @@ -32,11 +35,17 @@ def parse(training_path, query, verbose=False):
set_nlu_logger(logging.INFO)
elif verbose >= 2:
set_nlu_logger(logging.DEBUG)
if intents_filter:
# use csv in order to properly handle commas and other special
# characters in intent names
intents_filter = next(csv.reader([intents_filter]))
else:
intents_filter = None

engine = SnipsNLUEngine.from_path(training_path)

if query:
print_parsing_result(engine, query)
print_parsing_result(engine, query, intents_filter)
return

while True:
Expand All @@ -45,12 +54,13 @@ def parse(training_path, query, verbose=False):
query = query.decode("utf-8")
if query == "q":
break
print_parsing_result(engine, query)
print_parsing_result(engine, query, intents_filter)


def print_parsing_result(engine, query):
def print_parsing_result(engine, query, intents_filter):
from snips_nlu.common.utils import unicode_string, json_string

query = unicode_string(query)
json_dump = json_string(engine.parse(query), sort_keys=True, indent=2)
json_dump = json_string(engine.parse(query, intents_filter),
sort_keys=True, indent=2)
print(json_dump)
63 changes: 61 additions & 2 deletions snips_nlu/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,60 @@ def test_parse(self):
}
]
}
"""
self.assertEqual(expected_output, output)

def test_parse_with_intents_filter(self):
# Given / When
dataset_stream = io.StringIO(u"""
---
type: intent
name: MakeTea
utterances:
- make me a [beverage_temperature:Temperature](hot) cup of tea
- make me [number_of_cups:snips/number](five) tea cups
---
type: intent
name: Make,Coffee
utterances:
- brew [number_of_cups:snips/number](one) cup of coffee please
- make me [number_of_cups] cups of coffee""")
dataset = Dataset.from_yaml_files("en", [dataset_stream]).json
nlu_engine = SnipsNLUEngine().fit(dataset)
nlu_engine.persist(self.tmp_file_path)

# When / Then
output_target = io.StringIO()
with self.fail_if_exception("Failed to parse using CLI script"):
with redirect_stdout(output_target):
parse(str(self.tmp_file_path), "Make me two cups of coffee",
False, 'MakeTea,"Make,Coffee"')
output = output_target.getvalue()

# Then
expected_output = """{
"input": "Make me two cups of coffee",
"intent": {
"intentName": "Make,Coffee",
"probability": 1.0
},
"slots": [
{
"entity": "snips/number",
"range": {
"end": 11,
"start": 8
},
"rawValue": "two",
"slotName": "number_of_cups",
"value": {
"kind": "Number",
"value": 2.0
}
}
]
}
"""
self.assertEqual(expected_output, output)

Expand Down Expand Up @@ -259,12 +313,17 @@ def test_main_arg_parser(
(
"parse engine",
mocked_parse,
["engine", None, 0]
["engine", None, 0, None]
),
(
"parse engine -f MakeCoffee,MakeTea",
mocked_parse,
["engine", None, 0, "MakeCoffee,MakeTea"]
),
(
"parse engine -q foobar",
mocked_parse,
["engine", "foobar", 0]
["engine", "foobar", 0, None]
),
(
"download en",
Expand Down

0 comments on commit e5b6508

Please sign in to comment.