Skip to content

Commit

Permalink
Merge pull request #308 from kalliope-project/dev
Browse files Browse the repository at this point in the history
v0.4.5 release
  • Loading branch information
Sispheor authored Jul 23, 2017
2 parents 4a9d466 + 71e1060 commit eebe450
Show file tree
Hide file tree
Showing 21 changed files with 455 additions and 167 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
v0.4.5 / 2017-07-23
===================
- add keyword_entries attribute to CMU Sphinx
- add simplerate attribute to Pico2wav TTS
- API: convert mp3 file to wav automatically
- add sensitivity attribute to snowboy trigger
- add grammar attribute to CMU Sphinx
- add no_voice flag to api
- add possibility to send parameters when using api with run synapse by name

v0.4.4 / 2017-05-20
===================
- Fix: Uppercase in order/parameters/global variables are now handled correctly
Expand Down
35 changes: 35 additions & 0 deletions Docs/rest_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,21 @@ Output example:
}
```

The [no_voice flag](#no-voice-flag) can be added to this call.
Curl command:
```bash
curl -i -H "Content-Type: application/json" --user admin:secret -X POST \
-d '{"no_voice":"true"}' http://127.0.0.1:5000/synapses/start/id/say-hello-fr
```

Some neuron inside a synapse will wait for parameters that comes from the order.
You can provide those parameters by adding a `parameters` list of data.
Curl command:
```bash
curl -i -H "Content-Type: application/json" --user admin:secret -X POST \
-d '{"parameters": {"parameter1": "value1" }}' \
http://127.0.0.1:5000/synapses/start/id/synapse-id
```

### Run a synapse from an order

Expand Down Expand Up @@ -217,6 +232,13 @@ Or return an empty list of matched synapse
}
```

The [no_voice flag](#no-voice-flag) can be added to this call.
Curl command:
```bash
curl -i --user admin:secret -H "Content-Type: application/json" -X POST \
-d '{"order":"my order", "no_voice":"true"}' http://localhost:5000/synapses/start/order
```

### Run a synapse from an audio file

Normal response codes: 201
Expand Down Expand Up @@ -277,3 +299,16 @@ Or return an empty list of matched synapse
"user_order": "not existing order"
}
```

The [no_voice flag](#no-voice-flag) can be added to this call with a form.
Curl command:
```bash
curl -i --user admin:secret -X POST http://localhost:5000/synapses/start/audio -F "file=@path/to/file.wav" -F no_voice="true"
```


## No voice flag

When you use the API, by default Kalliope will generate a text and process it into the TTS engine.
Some calls to the API can be done with a flag that will tell Kalliope to only return the generated text without processing it into the audio player.
When `no_voice` is switched to true, Kalliope will not speak out loud on the server side.
10 changes: 7 additions & 3 deletions Docs/trigger.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ With Kalliope project, you can set whatever Hotword you want to wake it up.

You can create your magic word by connecting to [Snowboy](https://snowboy.kitt.ai/) and then download the trained model file.

Once downloaded:
- place the file in your personal config folder.
- update the path of **pmdl_file** in [your settings](settings.md).
Once downloaded, place the file in your personal config folder and configure snowboy in your [your settings](settings.md) following the table bellow

| parameter | required | type | default | choices | comment |
|-------------|----------|--------|---------|-----------------|--------------------------------------------------------------------------------------------------|
| pmdl_file | TRUE | string | | | Path to the snowboy model file. The path can be absolute or relative to the brain file |
| sensitivity | FALSE | string | 0.5 | between 0 and 1 | Increasing the sensitivity value lead to better detection rate, but also higher false alarm rate |


If you want to keep "Kalliope" as the name of your bot, we recommend you to __enhance the existing Snowboy model for your language__.

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Ka-li-o-pé (French)
Demo French : [video](https://www.youtube.com/watch?v=t4J42yO2rkM)
Demo English : [video](https://www.youtube.com/watch?v=PcLzo4H18S4)
Android app : [Playstore](https://play.google.com/store/apps/details?id=kalliope.project)
## License
Expand Down
9 changes: 9 additions & 0 deletions Tests/brains/brain_test_api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,12 @@

- includes:
- included_brain_test.yml

- name: "test4"
signals:
- order: "test_order_with_parameter"
neurons:
- say:
message:
- "test message {{ parameter1 }}"

178 changes: 110 additions & 68 deletions Tests/test_rest_api.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import json
import os
import unittest
import ast

from flask import Flask
from flask_testing import LiveServerTestCase
from mock import mock

from kalliope.core import LIFOBuffer
from kalliope.core.Models import Singleton
from kalliope._version import version_str
from kalliope.core import LIFOBuffer
from kalliope.core.ConfigurationManager import BrainLoader
from kalliope.core.ConfigurationManager import SettingLoader
from kalliope.core.Models import Singleton
from kalliope.core.RestAPI.FlaskAPI import FlaskAPI


Expand Down Expand Up @@ -65,74 +65,65 @@ def test_get_main_page(self):
expected_content = {
"Kalliope version": "%s" % version_str
}
self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))

def test_get_all_synapses(self):
url = self.get_server_url()+"/synapses"

response = self.client.get(url)
expected_content = {
"synapses": [
{
"name": "test",
"neurons": [
{
"name": "say",
"parameters": {
"message": [
"test message"
]
}
}
],
"signals": [
{
"order": "test_order"
}
]
},
{
"name": "test2",
"neurons": [
{
"name": "say",
"parameters": {
"message": [
"test message"
]
}
}
],
"signals": [
{
"order": "bonjour"
}
]
},
{
"name": "test3",
"neurons": [
{
"name": "say",
"parameters": {
"message": [
"test message"
]
}
}
],
"signals": [
{
"order": "test_order_3"
}
]
}
]
"synapses": [{
"name": "test",
"neurons": [{
"name": "say",
"parameters": {
"message": ["test message"]
}
}],
"signals": [{
"order": "test_order"
}]
}, {
"name": "test2",
"neurons": [{
"name": "say",
"parameters": {
"message": ["test message"]
}
}],
"signals": [{
"order": "bonjour"
}]
}, {
"name": "test4",
"neurons": [{
"name": "say",
"parameters": {
"message": ["test message {{parameter1}}"]
}
}],
"signals": [{
"order": "test_order_with_parameter"
}]
}, {
"name": "test3",
"neurons": [{
"name": "say",
"parameters": {
"message": ["test message"]
}
}],
"signals": [{
"order": "test_order_3"
}]
}]
}
# a lot of char ti process
self.maxDiff = None
self.assertEqual(response.status_code, 200)
self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))

def test_get_one_synapse(self):
url = self.get_server_url() + "/synapses/test"
Expand All @@ -158,7 +149,8 @@ def test_get_one_synapse(self):
]
}
}
self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(response.get_data().decode('utf-8')), sort_keys=True))

def test_get_synapse_not_found(self):
url = self.get_server_url() + "/synapses/test-none"
Expand All @@ -185,7 +177,35 @@ def test_run_synapse_by_name(self):
'synapse_name': 'test'}],
'user_order': None
}
self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(result.status_code, 201)

# run a synapse by its name with parameter
url = self.get_server_url() + "/synapses/start/id/test4"
headers = {"Content-Type": "application/json"}
data = {"parameters": {"parameter1": "replaced_value"}}
result = self.client.post(url, headers=headers, data=json.dumps(data))

expected_content = {
"matched_synapses": [
{
"matched_order": None,
"neuron_module_list": [
{
"generated_message": "test message replaced_value",
"neuron_name": "Say"
}
],
"synapse_name": "test4"
}
],
"status": "complete",
"user_order": None
}

self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(result.status_code, 201)

def test_post_synapse_not_found(self):
Expand All @@ -198,7 +218,8 @@ def test_post_synapse_not_found(self):
}
}

self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(result.status_code, 404)

def test_run_synapse_with_order(self):
Expand All @@ -218,12 +239,13 @@ def test_run_synapse_with_order(self):
'generated_message': 'test message', 'neuron_name': 'Say'
}
],
'synapse_name': 'test'
'synapse_name': 'test'
}
],
'user_order': "test_order"
}
self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(result.status_code, 201)

def test_post_synapse_by_order_not_found(self):
Expand All @@ -236,7 +258,8 @@ def test_post_synapse_by_order_not_found(self):

expected_content = {'status': None, 'matched_synapses': [], 'user_order': u'non existing order'}

self.assertEqual(json.dumps(expected_content, sort_keys=True), json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(json.dumps(expected_content, sort_keys=True),
json.dumps(json.loads(result.get_data().decode('utf-8')), sort_keys=True))
self.assertEqual(result.status_code, 201)

# TODO this doesn't work on travis but works locally with python -m unittest discover
Expand Down Expand Up @@ -275,10 +298,29 @@ def test_post_synapse_by_order_not_found(self):
# self.assertEqual(json.dumps(expected_content), json.dumps(json.loads(result.get_data())))
# self.assertEqual(result.status_code, 201)

def test_convert_to_wav(self):
"""
Test the api function to convert incoming sound file to wave.
"""

with mock.patch("os.system") as mock_os_system:
# Scenario 1 : input wav file
temp_file = "/tmp/kalliope/tempfile.wav" # tempfile.NamedTemporaryFile(suffix=".wav")
result_file = FlaskAPI._convert_to_wav(temp_file)
self.assertEqual(temp_file, result_file)
mock_os_system.assert_not_called()

# Scenario 2 : input not a wav file
temp_file = "/tmp/kalliope/tempfile.amr" # tempfile.NamedTemporaryFile(suffix=".wav")
expected_result = "/tmp/kalliope/tempfile.wav"
result_file = FlaskAPI._convert_to_wav(temp_file)
self.assertEqual(expected_result, result_file)
mock_os_system.assert_called_once_with("avconv -y -i " + temp_file + " " + expected_result)

if __name__ == '__main__':
unittest.main()

# suite = unittest.TestSuite()
# suite.addTest(TestRestAPI("test_post_synapse_by_order_not_found"))
# suite.addTest(TestRestAPI("test_run_synapse_by_name"))
# runner = unittest.TextTestRunner()
# runner.run(suite)
6 changes: 6 additions & 0 deletions docker/debian8.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ RUN apt-get update && apt-get install -y \
RUN wget https://bootstrap.pypa.io/get-pip.py
RUN python get-pip.py

# fix install in container
RUN pip install --upgrade pip six
RUN pip install --upgrade pip pyyaml
RUN pip install --upgrade pip SpeechRecognition
RUN pip install --upgrade pip Werkzeug

# add a standart user. tests must not be ran as root
RUN useradd -m -u 1000 tester
RUN usermod -aG sudo tester
Expand Down
6 changes: 6 additions & 0 deletions docker/ubuntu_16_04.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ RUN apt-get update && apt-get install -y \
RUN wget https://bootstrap.pypa.io/get-pip.py
RUN python get-pip.py

# fix install in container
RUN pip install --upgrade pip six
RUN pip install --upgrade pip pyyaml
RUN pip install --upgrade pip SpeechRecognition
RUN pip install --upgrade pip Werkzeug

# add a standart user. tests must not be ran as root
RUN useradd -m -u 1000 tester
RUN usermod -aG sudo tester
Expand Down
Loading

0 comments on commit eebe450

Please sign in to comment.