├── .gitignore ├── .rustfmt.toml ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── hermes-ffi-test ├── Cargo.toml └── src │ ├── json_round_trips.rs │ └── lib.rs ├── hermes-ffi ├── Cargo.toml └── src │ ├── lib.rs │ ├── ontology │ ├── asr.rs │ ├── audio_server.rs │ ├── dialogue.rs │ ├── hotword.rs │ ├── injection.rs │ ├── mod.rs │ ├── nlu.rs │ └── tts.rs │ └── protocol_handler │ ├── facades.rs │ ├── json.rs │ ├── mod.rs │ └── structures.rs ├── hermes-inprocess ├── Cargo.toml └── src │ └── lib.rs ├── hermes-mqtt-ffi ├── Cargo.toml ├── cbindgen.toml ├── cbindgen_full.toml ├── cbindgen_json.toml └── src │ └── lib.rs ├── hermes-mqtt ├── Cargo.toml └── src │ ├── lib.rs │ └── topics.rs ├── hermes-test-suite ├── Cargo.toml └── src │ └── lib.rs ├── hermes-utils-derive ├── Cargo.toml └── src │ └── lib.rs ├── hermes-utils ├── Cargo.toml ├── src │ └── lib.rs └── tests │ └── example_derive.rs ├── hermes ├── Cargo.toml └── src │ ├── errors.rs │ ├── lib.rs │ └── ontology │ ├── asr.rs │ ├── audio_server.rs │ ├── dialogue.rs │ ├── hotword.rs │ ├── injection.rs │ ├── mod.rs │ ├── nlu.rs │ ├── tts.rs │ └── vad.rs ├── platforms ├── c │ ├── .gitignore │ ├── README │ ├── libsnips_hermes.h │ ├── libsnips_hermes_full.h │ ├── libsnips_hermes_json.h │ └── test.c ├── hermes-javascript │ ├── .eslintrc.json │ ├── .gitignore │ ├── CHANGELOG.md │ ├── LICENSE │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── README.md │ ├── jest.config.js │ ├── package-lock.json │ ├── package.json │ ├── scripts │ │ ├── make.js │ │ ├── postinstall.js │ │ └── utils.js │ ├── src │ │ ├── api │ │ │ ├── ApiSubset.ts │ │ │ ├── audio │ │ │ │ └── index.ts │ │ │ ├── dialog │ │ │ │ ├── DialogFlow.ts │ │ │ │ └── index.ts │ │ │ ├── feedback │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── injection │ │ │ │ └── index.ts │ │ │ ├── tts │ │ │ │ └── index.ts │ │ │ └── types │ │ │ │ ├── ApiSubset.ts │ │ │ │ ├── Audio.ts │ │ │ │ ├── Dialog.ts │ │ │ │ ├── DialogFlow.ts │ │ │ │ ├── Feedback.ts │ │ │ │ ├── HermesOptions.ts │ │ │ │ ├── Injection.ts │ │ │ │ ├── Tts.ts │ │ │ │ ├── enums │ │ │ │ ├── component.ts │ │ │ │ ├── grain.ts │ │ │ │ ├── index.ts │ │ │ │ ├── initType.ts │ │ │ │ ├── injectionKind.ts │ │ │ │ ├── precision.ts │ │ │ │ ├── slotType.ts │ │ │ │ └── terminationType.ts │ │ │ │ ├── index.ts │ │ │ │ └── messages │ │ │ │ ├── ContinueSessionMessage.ts │ │ │ │ ├── DialogueConfigureMessage.ts │ │ │ │ ├── EndSessionMessage.ts │ │ │ │ ├── InjectionCompleteMessage.ts │ │ │ │ ├── InjectionRequestMessage.ts │ │ │ │ ├── InjectionResetCompleteMessage.ts │ │ │ │ ├── InjectionResetRequestMessage.ts │ │ │ │ ├── InjectionStatusMessage.ts │ │ │ │ ├── IntentMessage.ts │ │ │ │ ├── IntentNotRecognizedMessage.ts │ │ │ │ ├── NotificationMessage.ts │ │ │ │ ├── PlayBytesMessage.ts │ │ │ │ ├── PlayFinishedMessage.ts │ │ │ │ ├── RegisterSoundMessage.ts │ │ │ │ ├── SessionEndedMessage.ts │ │ │ │ ├── SessionQueuedMessage.ts │ │ │ │ ├── SessionStartedMessage.ts │ │ │ │ ├── StartSessionMessage.ts │ │ │ │ ├── TextCapturedMessage.ts │ │ │ │ └── index.ts │ │ ├── casts │ │ │ ├── Casteable.js │ │ │ ├── DoubleArray.js │ │ │ ├── MqttOptions.js │ │ │ ├── StringArray.js │ │ │ └── index.js │ │ ├── ffi │ │ │ ├── bindings.js │ │ │ └── typedefs.js │ │ ├── index.ts │ │ ├── tools │ │ │ └── index.ts │ │ └── types │ │ │ └── Buffer.d.ts │ ├── tests │ │ ├── constants.ts │ │ ├── mqtt │ │ │ ├── messages │ │ │ │ ├── ContinueSession.json │ │ │ │ ├── DialogueConfigure.json │ │ │ │ ├── EndSession.json │ │ │ │ ├── InjectionComplete.json │ │ │ │ ├── InjectionRequest.json │ │ │ │ ├── InjectionReset.json │ │ │ │ ├── InjectionResetComplete.json │ │ │ │ ├── InjectionStatus.json │ │ │ │ ├── Intent.json │ │ │ │ ├── IntentNotRecognized.json │ │ │ │ ├── PlayFinished.json │ │ │ │ ├── SessionEnded.json │ │ │ │ ├── SessionQueued.json │ │ │ │ ├── SessionStarted.json │ │ │ │ ├── SiteMessage.json │ │ │ │ └── StartSession.json │ │ │ ├── mqttJson.spec.ts │ │ │ ├── mqttTls.spec.ts │ │ │ ├── tls │ │ │ │ ├── ca.cert │ │ │ │ ├── client.cert │ │ │ │ ├── client.key │ │ │ │ ├── mosquitto-tls.conf │ │ │ │ ├── mqtt_password │ │ │ │ ├── server.cert │ │ │ │ └── server.key │ │ │ └── tools.ts │ │ └── roundtrips │ │ │ ├── jsonWrapper.ts │ │ │ └── roundtripJson.spec.ts │ ├── tsconfig.json │ └── tsconfig.types.json ├── hermes-kotlin │ ├── .gitignore │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── hermes-kotlin-test │ │ ├── build.gradle │ │ └── src │ │ │ ├── main │ │ │ └── kotlin │ │ │ │ └── ai │ │ │ │ └── snips │ │ │ │ └── hermes │ │ │ │ └── test │ │ │ │ └── HermesTest.kt │ │ │ └── test │ │ │ └── kotlin │ │ │ └── FfiTest.kt │ ├── settings.gradle │ └── src │ │ └── main │ │ └── kotlin │ │ └── ai │ │ └── snips │ │ └── hermes │ │ ├── Ontology.kt │ │ └── ffi │ │ └── COntology.kt └── hermes-python │ ├── .gitignore │ ├── LICENSE-APACHE │ ├── LICENSE-MIT │ ├── LICENSE.txt │ ├── MANIFEST.in │ ├── README.rst │ ├── build_scripts │ ├── build-wheels.sh │ ├── build_macos_wheels.sh │ ├── build_many_linux_wheels.sh │ ├── build_raspi_wheels.sh │ └── cleanup.sh │ ├── documentation │ ├── Makefile │ └── source │ │ ├── HISTORY.rst │ │ ├── README.rst │ │ ├── about.rst │ │ ├── api.rst │ │ ├── conf.py │ │ ├── index.rst │ │ ├── installation.rst │ │ ├── requirements.rst │ │ └── tutorial.rst │ ├── hermes_python │ ├── __init__.py │ ├── __version__.py │ ├── api │ │ ├── __init__.py │ │ └── ffi │ │ │ ├── __init__.py │ │ │ ├── dialogue.py │ │ │ ├── feedback.py │ │ │ ├── injection.py │ │ │ └── tts.py │ ├── ffi │ │ ├── __init__.py │ │ ├── ontology │ │ │ ├── __init__.py │ │ │ ├── asr.py │ │ │ ├── dialogue.py │ │ │ ├── facades.py │ │ │ ├── feedback.py │ │ │ ├── injection.py │ │ │ ├── nlu.py │ │ │ └── tts.py │ │ ├── utils.py │ │ └── wrappers.py │ ├── hermes.py │ └── ontology │ │ ├── __init__.py │ │ ├── asr │ │ └── __init__.py │ │ ├── dialogue │ │ ├── __init__.py │ │ ├── intent.py │ │ └── session.py │ │ ├── feedback │ │ └── __init__.py │ │ ├── injection │ │ └── __init__.py │ │ ├── nlu │ │ └── __init__.py │ │ ├── slot │ │ └── __init__.py │ │ └── tts │ │ └── __init__.py │ ├── requirements │ ├── documentation.txt │ ├── install.txt │ ├── lint.txt │ ├── tests.txt │ └── upload.txt │ ├── run-tests-jenkins.sh │ ├── run-tests-travis.sh │ ├── setup.py │ ├── test.py │ ├── tests │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── ffi │ │ │ ├── __init__.py │ │ │ ├── test_dialogue.py │ │ │ ├── test_ffi.py │ │ │ ├── test_injection.py │ │ │ ├── test_sound_feedback.py │ │ │ └── test_tts.py │ ├── data │ │ └── test.wav │ ├── ffi │ │ ├── __init__.py │ │ ├── test_ontology.py │ │ └── test_utils.py │ ├── mqtt │ │ ├── __init__.py │ │ └── test_connection.py │ ├── ontology │ │ ├── __init__.py │ │ └── dialogue │ │ │ ├── __init__.py │ │ │ ├── test_intent.py │ │ │ ├── test_session.py │ │ │ └── test_slot.py │ ├── roundtrip │ │ ├── __init__.py │ │ └── test_roundtrip.py │ └── test_hermes.py │ └── tox.ini ├── update_c_headers.sh ├── update_ontology_version.sh └── update_version.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | Cargo.lock 3 | target/ 4 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | edition = "2018" 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | dist: xenial 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - mosquitto 9 | - openjdk-8-jdk 10 | - python3 11 | 12 | before_install: 13 | - sudo update-alternatives --set java /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java 14 | - sudo update-alternatives --set javac /usr/lib/jvm/java-8-openjdk-amd64/bin/javac 15 | 16 | before_script: 17 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then 18 | rustup component add rustfmt; 19 | rustup component add clippy; 20 | fi 21 | 22 | rust: 23 | - stable 24 | - beta 25 | - nightly 26 | 27 | script: 28 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then 29 | cargo fmt --all -- --check; 30 | fi 31 | - if [ "$TRAVIS_RUST_VERSION" == "stable" ]; then 32 | cargo clippy --all-targets --all-features -- -D warnings; 33 | fi 34 | - cargo build --all 35 | - cargo test --all 36 | - pwd 37 | - (cd platforms/hermes-kotlin && ./gradlew --console plain jar -Pdebug) 38 | - (cd platforms/hermes-kotlin && ./gradlew --console plain test -Pdebug) 39 | - (cd platforms/hermes-python && ./run-tests-travis.sh) 40 | - (cd platforms/hermes-javascript && npm install && npm start) 41 | 42 | matrix: 43 | allow_failures: 44 | - rust: nightly 45 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "hermes", 4 | "hermes-ffi", 5 | "hermes-ffi-test", 6 | "hermes-inprocess", 7 | "hermes-mqtt", 8 | "hermes-mqtt-ffi", 9 | "hermes-test-suite", 10 | "hermes-utils", 11 | "hermes-utils-derive", 12 | ] 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | ### Apache 2.0/MIT 4 | 5 | Licensed under either of 6 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 7 | http://www.apache.org/licenses/LICENSE-2.0) 8 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 9 | http://opensource.org/licenses/MIT) 10 | 11 | at your option. 12 | 13 | ### Contribution 14 | 15 | Unless you explicitly state otherwise, any contribution intentionally submitted 16 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 17 | be dual licensed as above, without any additional terms or conditions. 18 | 19 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /hermes-ffi-test/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-ffi-test" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["rlib", "staticlib", "cdylib"] 9 | 10 | [features] 11 | full_bindings = ["hermes-ffi/full_bindings"] 12 | structures = ["hermes-ffi/structures"] 13 | json = ["hermes-ffi/json", "serde_json"] 14 | default = ["structures", "json", "full_bindings"] 15 | 16 | [dependencies] 17 | failure = "0.1" 18 | hermes = { path = "../hermes" } 19 | hermes-ffi = { path = "../hermes-ffi" } 20 | ffi-utils = { git = "https://github.com/snipsco/snips-utils-rs", rev = "291ce1d" } 21 | libc = "0.2" 22 | serde_json = { version = "1.0", optional = true } 23 | 24 | -------------------------------------------------------------------------------- /hermes-ffi-test/src/json_round_trips.rs: -------------------------------------------------------------------------------- 1 | use failure::Fallible; 2 | use ffi_utils::*; 3 | use hermes; 4 | use serde_json; 5 | 6 | use crate::LAST_ERROR; 7 | 8 | fn round_trip_json<'de, T>(input: *const libc::c_char, output: *mut *const libc::c_char) -> Fallible<()> 9 | where 10 | T: hermes::HermesMessage<'de>, 11 | { 12 | let input = unsafe { std::ffi::CStr::from_ptr(input) }.to_str()?; 13 | 14 | let rust_object = serde_json::from_str::(&input)?; 15 | 16 | let new_string = serde_json::to_string(&rust_object)?; 17 | 18 | point_to_string(output, new_string)?; 19 | 20 | Ok(()) 21 | } 22 | 23 | macro_rules! round_trip_json { 24 | ($c_symbol:ident, $repr_type:ty) => { 25 | #[no_mangle] 26 | pub extern "C" fn $c_symbol( 27 | input: *const libc::c_char, 28 | output: *mut *const libc::c_char, 29 | ) -> ffi_utils::SNIPS_RESULT { 30 | wrap!(round_trip_json::<$repr_type>(input, output)) 31 | } 32 | }; 33 | } 34 | 35 | round_trip_json!( 36 | hermes_ffi_test_round_trip_session_queued_json, 37 | hermes::SessionQueuedMessage 38 | ); 39 | 40 | round_trip_json!( 41 | hermes_ffi_test_round_trip_session_started_json, 42 | hermes::SessionStartedMessage 43 | ); 44 | 45 | round_trip_json!( 46 | hermes_ffi_test_round_trip_session_ended_json, 47 | hermes::SessionEndedMessage 48 | ); 49 | 50 | round_trip_json!(hermes_ffi_test_round_trip_intent_json, hermes::IntentMessage); 51 | 52 | round_trip_json!( 53 | hermes_ffi_test_round_trip_intent_not_recognized_json, 54 | hermes::IntentNotRecognizedMessage 55 | ); 56 | 57 | round_trip_json!( 58 | hermes_ffi_test_round_trip_start_session_json, 59 | hermes::StartSessionMessage 60 | ); 61 | 62 | round_trip_json!( 63 | hermes_ffi_test_round_trip_continue_session_json, 64 | hermes::ContinueSessionMessage 65 | ); 66 | 67 | round_trip_json!(hermes_ffi_test_round_trip_end_session_json, hermes::EndSessionMessage); 68 | 69 | round_trip_json!( 70 | hermes_ffi_test_round_trip_injection_request_json, 71 | hermes::InjectionRequestMessage 72 | ); 73 | 74 | round_trip_json!( 75 | hermes_ffi_test_round_trip_injection_complete_json, 76 | hermes::InjectionCompleteMessage 77 | ); 78 | 79 | round_trip_json!( 80 | hermes_ffi_test_round_trip_injection_reset_request_json, 81 | hermes::InjectionResetRequestMessage 82 | ); 83 | 84 | round_trip_json!( 85 | hermes_ffi_test_round_trip_injection_reset_complete_json, 86 | hermes::InjectionResetCompleteMessage 87 | ); 88 | 89 | round_trip_json!( 90 | hermes_ffi_test_round_trip_register_sound_json, 91 | hermes::RegisterSoundMessage 92 | ); 93 | 94 | round_trip_json!( 95 | hermes_ffi_test_round_trip_dialogue_configure_json, 96 | hermes::DialogueConfigureMessage 97 | ); 98 | 99 | round_trip_json!( 100 | hermes_ffi_test_round_trip_text_captured_json, 101 | hermes::TextCapturedMessage 102 | ); 103 | -------------------------------------------------------------------------------- /hermes-ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-ffi" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [features] 8 | full_bindings = [] 9 | structures = [] 10 | json = ["serde_json"] 11 | default = ["structures", "json", "full_bindings"] 12 | 13 | [dependencies] 14 | chrono = "0.4" 15 | failure = "0.1" 16 | ffi-utils = { git = "https://github.com/snipsco/snips-utils-rs", rev = "291ce1d" } 17 | hermes = { path = "../hermes" } 18 | lazy_static = { version="1.0" } 19 | libc = "0.2" 20 | serde_json = { version = "1.0", optional = true } 21 | snips-nlu-ontology-ffi-macros = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 22 | env_logger = "0.6" 23 | 24 | [dev-dependencies] 25 | spectral = "0.6" 26 | snips-nlu-ontology = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 27 | -------------------------------------------------------------------------------- /hermes-ffi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "structures")] 2 | pub mod ontology; 3 | mod protocol_handler; 4 | 5 | #[cfg(feature = "structures")] 6 | pub use crate::ontology::*; 7 | pub use crate::protocol_handler::*; 8 | 9 | pub fn init_debug_logs() -> failure::Fallible<()> { 10 | env_logger::try_init()?; 11 | Ok(()) 12 | } 13 | -------------------------------------------------------------------------------- /hermes-ffi/src/ontology/audio_server.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | 3 | use failure::Fallible; 4 | use failure::ResultExt; 5 | use ffi_utils::*; 6 | 7 | #[repr(C)] 8 | #[derive(Debug)] 9 | pub struct CPlayBytesMessage { 10 | pub id: *const libc::c_char, 11 | pub wav_bytes: *const u8, 12 | // Note: we can't use `libc::size_t` because it's not supported by JNA 13 | pub wav_bytes_len: libc::c_int, 14 | pub site_id: *const libc::c_char, 15 | } 16 | 17 | unsafe impl Sync for CPlayBytesMessage {} 18 | 19 | impl CPlayBytesMessage { 20 | pub fn from(input: hermes::PlayBytesMessage) -> Fallible { 21 | Self::c_repr_of(input) 22 | } 23 | } 24 | 25 | impl CReprOf for CPlayBytesMessage { 26 | fn c_repr_of(input: hermes::PlayBytesMessage) -> Fallible { 27 | Ok(Self { 28 | id: convert_to_c_string!(input.id), 29 | wav_bytes_len: input.wav_bytes.len() as libc::c_int, 30 | wav_bytes: Box::into_raw(input.wav_bytes.into_boxed_slice()) as *const u8, 31 | site_id: convert_to_c_string!(input.site_id), 32 | }) 33 | } 34 | } 35 | 36 | impl AsRust for CPlayBytesMessage { 37 | fn as_rust(&self) -> Fallible { 38 | Ok(hermes::PlayBytesMessage { 39 | id: create_rust_string_from!(self.id), 40 | wav_bytes: unsafe { slice::from_raw_parts(self.wav_bytes as *const u8, self.wav_bytes_len as usize) } 41 | .to_vec(), 42 | site_id: create_rust_string_from!(self.site_id), 43 | }) 44 | } 45 | } 46 | 47 | impl Drop for CPlayBytesMessage { 48 | fn drop(&mut self) { 49 | take_back_c_string!(self.id); 50 | let _ = unsafe { 51 | Box::from_raw(slice::from_raw_parts_mut( 52 | self.wav_bytes as *mut u8, 53 | self.wav_bytes_len as usize, 54 | )) 55 | }; 56 | take_back_c_string!(self.site_id); 57 | } 58 | } 59 | 60 | #[repr(C)] 61 | #[derive(Debug)] 62 | pub struct CAudioFrameMessage { 63 | pub wav_frame: *const u8, 64 | // Note: we can't use `libc::size_t` because it's not supported by JNA 65 | pub wav_frame_len: libc::c_int, 66 | pub site_id: *const libc::c_char, 67 | } 68 | 69 | unsafe impl Sync for CAudioFrameMessage {} 70 | 71 | impl CAudioFrameMessage { 72 | pub fn from(input: hermes::AudioFrameMessage) -> Fallible { 73 | Self::c_repr_of(input) 74 | } 75 | } 76 | 77 | impl CReprOf for CAudioFrameMessage { 78 | fn c_repr_of(input: hermes::AudioFrameMessage) -> Fallible { 79 | Ok(Self { 80 | wav_frame_len: input.wav_frame.len() as libc::c_int, 81 | wav_frame: Box::into_raw(input.wav_frame.into_boxed_slice()) as *const u8, 82 | site_id: convert_to_c_string!(input.site_id), 83 | }) 84 | } 85 | } 86 | 87 | impl AsRust for CAudioFrameMessage { 88 | fn as_rust(&self) -> Fallible { 89 | Ok(hermes::AudioFrameMessage { 90 | wav_frame: unsafe { slice::from_raw_parts(self.wav_frame as *const u8, self.wav_frame_len as usize) } 91 | .to_vec(), 92 | site_id: create_rust_string_from!(self.site_id), 93 | }) 94 | } 95 | } 96 | 97 | impl Drop for CAudioFrameMessage { 98 | fn drop(&mut self) { 99 | let _ = unsafe { 100 | Box::from_raw(slice::from_raw_parts_mut( 101 | self.wav_frame as *mut u8, 102 | self.wav_frame_len as usize, 103 | )) 104 | }; 105 | take_back_c_string!(self.site_id); 106 | } 107 | } 108 | 109 | #[repr(C)] 110 | #[derive(Debug)] 111 | pub struct CPlayFinishedMessage { 112 | pub id: *const libc::c_char, 113 | pub site_id: *const libc::c_char, 114 | } 115 | 116 | unsafe impl Sync for CPlayFinishedMessage {} 117 | 118 | impl CPlayFinishedMessage { 119 | pub fn from(input: hermes::PlayFinishedMessage) -> Fallible { 120 | Self::c_repr_of(input) 121 | } 122 | } 123 | 124 | impl CReprOf for CPlayFinishedMessage { 125 | fn c_repr_of(input: hermes::PlayFinishedMessage) -> Fallible { 126 | Ok(Self { 127 | id: convert_to_c_string!(input.id), 128 | site_id: convert_to_c_string!(input.site_id), 129 | }) 130 | } 131 | } 132 | 133 | impl AsRust for CPlayFinishedMessage { 134 | fn as_rust(&self) -> Fallible { 135 | Ok(hermes::PlayFinishedMessage { 136 | id: create_rust_string_from!(self.id), 137 | site_id: create_rust_string_from!(self.site_id), 138 | }) 139 | } 140 | } 141 | 142 | impl Drop for CPlayFinishedMessage { 143 | fn drop(&mut self) { 144 | take_back_c_string!(self.id); 145 | take_back_c_string!(self.site_id); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /hermes-ffi/src/ontology/hotword.rs: -------------------------------------------------------------------------------- 1 | use failure::Fallible; 2 | use failure::ResultExt; 3 | 4 | use ffi_utils::*; 5 | 6 | #[repr(C)] 7 | #[derive(Debug)] 8 | pub struct CHotwordDetectedMessage { 9 | pub site_id: *const libc::c_char, 10 | pub model_id: *const libc::c_char, 11 | } 12 | 13 | unsafe impl Sync for CHotwordDetectedMessage {} 14 | 15 | impl CReprOf for CHotwordDetectedMessage { 16 | fn c_repr_of(input: hermes::HotwordDetectedMessage) -> Fallible { 17 | Ok(Self { 18 | site_id: convert_to_c_string!(input.site_id), 19 | model_id: convert_to_c_string!(input.model_id), 20 | }) 21 | } 22 | } 23 | 24 | impl AsRust for CHotwordDetectedMessage { 25 | fn as_rust(&self) -> Fallible { 26 | Ok(hermes::HotwordDetectedMessage { 27 | site_id: create_rust_string_from!(self.site_id), 28 | model_id: create_rust_string_from!(self.model_id), 29 | model_version: None, 30 | model_type: None, 31 | current_sensitivity: None, 32 | detection_signal_ms: None, 33 | end_signal_ms: None, 34 | }) 35 | } 36 | } 37 | 38 | impl Drop for CHotwordDetectedMessage { 39 | fn drop(&mut self) { 40 | take_back_c_string!(self.site_id); 41 | take_back_c_string!(self.model_id); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /hermes-ffi/src/protocol_handler/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod facades; 2 | #[cfg(feature = "json")] 3 | pub mod json; 4 | #[cfg(feature = "structures")] 5 | pub mod structures; 6 | 7 | pub use facades::{CProtocolHandler, UserData}; 8 | #[cfg(feature = "json")] 9 | pub use json::{json_from_slice, json_ptr_to_callback, CJsonCallback}; 10 | #[cfg(feature = "structures")] 11 | pub use structures::structure_ptr_to_callback; 12 | 13 | #[macro_export] 14 | macro_rules! generate_hermes_c_symbols { 15 | () => { 16 | #[no_mangle] 17 | pub extern "C" fn hermes_enable_debug_logs() -> ffi_utils::SNIPS_RESULT { 18 | ffi_utils::wrap!($crate::init_debug_logs()) 19 | } 20 | 21 | generate_facade_c_symbols!(); 22 | 23 | #[cfg(feature = "structures")] 24 | generate_structures_c_symbols!(); 25 | 26 | #[cfg(feature = "json")] 27 | generate_json_c_symbols!(); 28 | }; 29 | } 30 | -------------------------------------------------------------------------------- /hermes-inprocess/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-inprocess" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | failure = "0.1" 9 | ripb = "0.3" 10 | hermes = { path = "../hermes" } 11 | hermes-test-suite = { path = "../hermes-test-suite" } 12 | log = "0.4" 13 | 14 | [dev-dependencies] 15 | semver = "0.9" 16 | snips-nlu-ontology = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 17 | -------------------------------------------------------------------------------- /hermes-mqtt-ffi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-mqtt-ffi" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["rlib", "staticlib", "cdylib"] 9 | 10 | [features] 11 | full_bindings = ["hermes-ffi/full_bindings"] 12 | structures = ["hermes-ffi/structures"] 13 | json = ["hermes-ffi/json"] 14 | default = ["structures", "json", "full_bindings"] 15 | 16 | [dependencies] 17 | failure = "0.1" 18 | ffi-utils = { git = "https://github.com/snipsco/snips-utils-rs", rev = "291ce1d" } 19 | hermes = { path = "../hermes" } 20 | hermes-ffi = { path = "../hermes-ffi" } 21 | hermes-mqtt = { path = "../hermes-mqtt" } 22 | libc = "0.2" 23 | log = "0.4" 24 | snips-nlu-ontology-ffi = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 25 | # Needed to fix cbindgen issue. See https://github.com/eqrion/cbindgen/issues/221 26 | snips-nlu-ontology-ffi-macros = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 27 | -------------------------------------------------------------------------------- /hermes-mqtt-ffi/cbindgen.toml: -------------------------------------------------------------------------------- 1 | language = "c" 2 | 3 | include_guard = "LIB_HERMES_H_" 4 | 5 | [parse] 6 | parse_deps=true 7 | include = [ 8 | "hermes_mqtt_ffi", 9 | "hermes_ffi", 10 | "ffi_utils", 11 | "snips_nlu_ontology_ffi", 12 | "snips_nlu_ontology_ffi_macros", 13 | ] 14 | 15 | [parse.expand] 16 | crates = ["hermes-mqtt-ffi"] 17 | default_features = false 18 | features = ["structures"] 19 | 20 | [export] 21 | # These types are hidden behind a void pointer, let's include them 22 | include = [ 23 | "CActionSessionInit", 24 | "CNumberValue", 25 | "COrdinalValue", 26 | "CPercentageValue", 27 | "CInstantTimeValue", 28 | "CTimeIntervalValue", 29 | "CAmountOfMoneyValue", 30 | "CTemperatureValue", 31 | "CDurationValue", 32 | ] 33 | -------------------------------------------------------------------------------- /hermes-mqtt-ffi/cbindgen_full.toml: -------------------------------------------------------------------------------- 1 | language = "c" 2 | 3 | include_guard = "LIB_HERMES_H_" 4 | 5 | [parse] 6 | parse_deps=true 7 | include = [ 8 | "hermes_mqtt_ffi", 9 | "hermes_ffi", 10 | "ffi_utils", 11 | "snips_nlu_ontology_ffi", 12 | "snips_nlu_ontology_ffi_macros", 13 | ] 14 | 15 | [parse.expand] 16 | crates = ["hermes-mqtt-ffi"] 17 | default_features = false 18 | features = ["structures", "full_bindings"] 19 | 20 | [export] 21 | # These types are hidden behind a void pointer, let's include them 22 | include = [ 23 | "CActionSessionInit", 24 | "CNumberValue", 25 | "COrdinalValue", 26 | "CPercentageValue", 27 | "CInstantTimeValue", 28 | "CTimeIntervalValue", 29 | "CAmountOfMoneyValue", 30 | "CTemperatureValue", 31 | "CDurationValue", 32 | ] 33 | -------------------------------------------------------------------------------- /hermes-mqtt-ffi/cbindgen_json.toml: -------------------------------------------------------------------------------- 1 | language = "c" 2 | 3 | include_guard = "LIB_HERMES_H_" 4 | 5 | [parse] 6 | parse_deps=true 7 | include = [ 8 | "hermes_mqtt_ffi", 9 | "hermes_ffi", 10 | "ffi_utils", 11 | ] 12 | 13 | [parse.expand] 14 | crates = ["hermes-mqtt-ffi"] 15 | default_features = false 16 | features = ["json"] 17 | 18 | [export] 19 | exclude = ["CStringArray"] 20 | -------------------------------------------------------------------------------- /hermes-mqtt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-mqtt" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | failure = "0.1" 9 | hermes = { path = "../hermes" } 10 | hermes-test-suite = { path = "../hermes-test-suite" } 11 | hostname = "0.1" 12 | lazy_static = "1.2" 13 | log = "0.4" 14 | rumqtt = { git = "https://github.com/snipsco/rumqtt", rev = "2b7fde6c" } 15 | serde = "1.0" 16 | serde_json = "1.0" 17 | strum_macros = "0.13" 18 | 19 | [dev-dependencies] 20 | rand = "0.6" 21 | semver = "0.9" 22 | snips-nlu-ontology = { git = "https://github.com/snipsco/snips-nlu-ontology", tag = "0.67.1" } 23 | 24 | [package.metadata.dinghy] 25 | ignored_rustc_triples = [ 26 | "arm-linux-androideabi", 27 | "armv7-linux-androideabi", 28 | "aarch64-linux-android", 29 | "i686-linux-android", 30 | "x86_64-linux-android", 31 | "aarch64-apple-ios", 32 | "armv7-apple-ios", 33 | "armv7s-apple-ios", 34 | "i386-apple-ios", 35 | "x86_64-apple-ios", 36 | ] 37 | -------------------------------------------------------------------------------- /hermes-test-suite/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-test-suite" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | chrono = "0.4" 9 | hermes = { path = "../hermes" } 10 | -------------------------------------------------------------------------------- /hermes-utils-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-utils-derive" 3 | version = "0.1.0" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [lib] 10 | proc-macro = true 11 | 12 | [dependencies] 13 | syn = "1.0.5" 14 | quote = "1.0.2" 15 | -------------------------------------------------------------------------------- /hermes-utils-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | use syn; 6 | 7 | use quote::quote; 8 | 9 | #[proc_macro_derive(Example, attributes(example_value))] 10 | pub fn example_derive(token_stream: TokenStream) -> TokenStream { 11 | let ast = syn::parse(token_stream).unwrap(); 12 | impl_example_macro(&ast) 13 | } 14 | 15 | fn impl_example_macro(input: &syn::DeriveInput) -> TokenStream { 16 | let struct_name = &input.ident; 17 | 18 | let data = match &input.data { 19 | syn::Data::Struct(data) => data, 20 | _ => panic!("examples can only be derived for structs"), 21 | }; 22 | 23 | let fields: Vec<_> = data 24 | .fields 25 | .iter() 26 | .map(|field| { 27 | ( 28 | field.ident.as_ref().expect("field should have and ident"), 29 | field 30 | .attrs 31 | .iter() 32 | .find(|attr| attr.path.get_ident().map(|it| it.to_string()) == Some("example_value".into())), 33 | ) 34 | }) 35 | .map(|(ident, value)| { 36 | if let Some(value) = value { 37 | let value = &value.tokens; 38 | quote!(#ident: #value.into()) 39 | } else { 40 | quote!(#ident: hermes_utils::Example::example(hermes_utils::ExampleConfig { 41 | field_name: Some(stringify!(#ident).into()), 42 | .. config.clone() 43 | })) 44 | } 45 | }) 46 | .collect(); 47 | 48 | quote!( 49 | impl hermes_utils::Example for # struct_name { 50 | fn example(config: hermes_utils::ExampleConfig) -> Self { 51 | Self { 52 | # ( # fields, )* 53 | } 54 | } 55 | } 56 | ) 57 | .into() 58 | } 59 | -------------------------------------------------------------------------------- /hermes-utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes-utils" 3 | version = "0.1.0" 4 | authors = ["Thibaut Lorrain "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | hermes-utils-derive = { path="../hermes-utils-derive" } 11 | -------------------------------------------------------------------------------- /hermes-utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use hermes_utils_derive::Example; 2 | use std::collections::HashMap; 3 | use std::hash::Hash; 4 | 5 | #[derive(Default, Clone)] 6 | pub struct ExampleConfig { 7 | pub field_name: Option, 8 | pub minimal: bool, 9 | pub index: Option, 10 | } 11 | 12 | /// A trait used to generate example values of the implementing struct. 13 | pub trait Example: Sized { 14 | /// Generate a minimal example (Options are set to None and Vecs are empty) 15 | fn minimal_example() -> Self { 16 | Self::example(ExampleConfig { 17 | minimal: true, 18 | ..Default::default() 19 | }) 20 | } 21 | 22 | /// Generate a full example (Options are set to Some and Vecs contain values) 23 | fn full_example() -> Self { 24 | Self::example(Default::default()) 25 | } 26 | 27 | /// Generate an example using the given config 28 | fn example(config: ExampleConfig) -> Self; 29 | } 30 | 31 | impl Example for String { 32 | fn example(config: ExampleConfig) -> Self { 33 | match (config.field_name, config.index) { 34 | (Some(field_name), Some(index)) => format!("<{} {}>", field_name.replace("_", " "), index), 35 | (Some(field_name), None) => format!("<{}>", field_name.replace("_", " ")), 36 | (None, Some(index)) => format!("string {}", index), 37 | (None, None) => "a string".into(), 38 | } 39 | } 40 | } 41 | 42 | impl Example for Option { 43 | fn example(config: ExampleConfig) -> Self { 44 | if config.minimal { 45 | None 46 | } else { 47 | Some(T::example(config)) 48 | } 49 | } 50 | } 51 | 52 | impl Example for Vec { 53 | fn example(config: ExampleConfig) -> Self { 54 | if config.minimal { 55 | vec![] 56 | } else { 57 | (1..=3) 58 | .map(|index| { 59 | T::example(ExampleConfig { 60 | index: Some(index), 61 | ..config.clone() 62 | }) 63 | }) 64 | .collect() 65 | } 66 | } 67 | } 68 | 69 | impl Example for (T, U) { 70 | fn example(config: ExampleConfig) -> Self { 71 | (T::example(config.clone()), U::example(config)) 72 | } 73 | } 74 | 75 | impl Example for HashMap { 76 | fn example(config: ExampleConfig) -> Self { 77 | if config.minimal { 78 | HashMap::default() 79 | } else { 80 | (1..=3) 81 | .map(|index| { 82 | ( 83 | T::example(ExampleConfig { 84 | index: Some(index), 85 | ..config.clone() 86 | }), 87 | U::example(ExampleConfig { 88 | index: Some(index), 89 | ..config.clone() 90 | }), 91 | ) 92 | }) 93 | .collect() 94 | } 95 | } 96 | } 97 | 98 | macro_rules! example_from_default_for { 99 | ($typ:ty) => { 100 | impl Example for $typ { 101 | fn example(_: ExampleConfig) -> Self { 102 | Default::default() 103 | } 104 | } 105 | }; 106 | } 107 | 108 | example_from_default_for!(i8); 109 | example_from_default_for!(i16); 110 | example_from_default_for!(i32); 111 | example_from_default_for!(i64); 112 | example_from_default_for!(i128); 113 | 114 | example_from_default_for!(u8); 115 | example_from_default_for!(u16); 116 | example_from_default_for!(u32); 117 | example_from_default_for!(u64); 118 | example_from_default_for!(u128); 119 | example_from_default_for!(usize); 120 | 121 | example_from_default_for!(f32); 122 | example_from_default_for!(f64); 123 | 124 | example_from_default_for!(bool); 125 | 126 | #[cfg(test)] 127 | mod tests { 128 | use super::*; 129 | 130 | #[test] 131 | fn example_string() { 132 | assert_eq!("a string", String::example(Default::default())); 133 | assert_eq!( 134 | "", 135 | String::example(ExampleConfig { 136 | field_name: Some("foo_bar".into()), 137 | ..Default::default() 138 | }) 139 | ); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /hermes-utils/tests/example_derive.rs: -------------------------------------------------------------------------------- 1 | use hermes_utils::Example; 2 | 3 | #[derive(Example, Debug, PartialEq)] 4 | struct DemoExampleDerive { 5 | value_from_name_string: String, 6 | #[example_value("hello world")] 7 | overridden_string: String, 8 | optional_string: Option, 9 | zeroed_i32: i32, 10 | #[example_value(5)] 11 | overridden_i32: i32, 12 | false_boolean: bool, 13 | #[example_value(true)] 14 | overridden_boolean: bool, 15 | struct_implementing_example: Option, 16 | #[example_value(DummyStruct { fizz: Some("buzz".into()) })] 17 | struct_implementing_not_example: DummyStruct, 18 | vec: Vec, 19 | #[example_value(get_example_vec())] 20 | overridden_vec: Vec, 21 | } 22 | 23 | #[derive(Example, Debug, PartialEq)] 24 | struct NumericTypesSupported { 25 | default_i8: i8, 26 | default_i16: i16, 27 | default_i32: i32, 28 | default_i64: i64, 29 | default_i128: i128, 30 | default_u8: u8, 31 | default_u16: u16, 32 | default_u32: u32, 33 | default_u64: u64, 34 | default_u128: u128, 35 | default_usize: usize, 36 | default_f32: f32, 37 | default_f64: f64, 38 | } 39 | 40 | #[derive(Example, Debug, PartialEq)] 41 | struct SimpleStruct { 42 | name: String, 43 | } 44 | 45 | #[derive(Debug, PartialEq)] 46 | struct DummyStruct { 47 | fizz: Option, 48 | } 49 | 50 | fn get_example_vec() -> Vec { 51 | vec![ 52 | SimpleStruct { name: "hello".into() }, 53 | SimpleStruct { name: "world".into() }, 54 | ] 55 | } 56 | 57 | #[test] 58 | fn full_example_works() { 59 | assert_eq!( 60 | DemoExampleDerive::full_example(), 61 | DemoExampleDerive { 62 | value_from_name_string: "".into(), 63 | overridden_string: "hello world".into(), 64 | optional_string: Some("".into()), 65 | struct_implementing_example: Some(NumericTypesSupported { 66 | default_i8: 0, 67 | default_i16: 0, 68 | default_i32: 0, 69 | default_i64: 0, 70 | default_i128: 0, 71 | default_u8: 0, 72 | default_u16: 0, 73 | default_u32: 0, 74 | default_u64: 0, 75 | default_u128: 0, 76 | default_usize: 0, 77 | default_f32: 0.0, 78 | default_f64: 0.0, 79 | }), 80 | zeroed_i32: 0, 81 | overridden_i32: 5, 82 | false_boolean: false, 83 | overridden_boolean: true, 84 | struct_implementing_not_example: DummyStruct { 85 | fizz: Some("buzz".into()) 86 | }, 87 | vec: vec![ 88 | SimpleStruct { 89 | name: "".into() 90 | }, 91 | SimpleStruct { 92 | name: "".into() 93 | }, 94 | SimpleStruct { 95 | name: "".into() 96 | } 97 | ], 98 | overridden_vec: get_example_vec(), 99 | } 100 | ) 101 | } 102 | 103 | #[test] 104 | fn minimal_example_works() { 105 | assert_eq!( 106 | DemoExampleDerive::minimal_example(), 107 | DemoExampleDerive { 108 | value_from_name_string: "".into(), 109 | overridden_string: "hello world".into(), 110 | optional_string: None, 111 | struct_implementing_example: None, 112 | zeroed_i32: 0, 113 | overridden_i32: 5, 114 | false_boolean: false, 115 | overridden_boolean: true, 116 | struct_implementing_not_example: DummyStruct { 117 | fizz: Some("buzz".into()) 118 | }, 119 | vec: vec![], 120 | overridden_vec: get_example_vec(), 121 | } 122 | ) 123 | } 124 | -------------------------------------------------------------------------------- /hermes/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hermes" 3 | version = "0.69.0-SNAPSHOT" 4 | authors = [ 5 | "Kevin Lefevre ", 6 | "Thibaut Lorrain From> for PoisonLock { 8 | fn from(_: PoisonError) -> Self { 9 | Self {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /hermes/src/ontology/asr.rs: -------------------------------------------------------------------------------- 1 | use super::HermesMessage; 2 | 3 | #[derive(Debug, Clone, Default, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct AsrStartListeningMessage { 6 | /// The site that must be listened too 7 | pub site_id: String, 8 | /// An optional session id if there is a related session 9 | pub session_id: Option, 10 | /// Signal instant to start listening from 11 | pub start_signal_ms: Option, 12 | } 13 | 14 | impl<'de> HermesMessage<'de> for AsrStartListeningMessage {} 15 | 16 | #[derive(Debug, Clone, Default, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 17 | #[serde(rename_all = "camelCase")] 18 | pub struct AsrDecodingDuration { 19 | pub start: f32, 20 | pub end: f32, 21 | } 22 | 23 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 24 | #[serde(rename_all = "camelCase")] 25 | pub struct AsrToken { 26 | /// The value of the token 27 | pub value: String, 28 | /// The confidence of the token 29 | pub confidence: f32, 30 | // TODO: change this range_start/stop when Range will be PartialOrd (only in nightly now. see issue #32311) 31 | /// The start range in which the token is in the original input 32 | pub range_start: usize, 33 | /// The end range in which the token is in the original input 34 | pub range_end: usize, 35 | /// TODO: Put doc 36 | pub time: AsrDecodingDuration, 37 | } 38 | 39 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 40 | #[serde(rename_all = "camelCase")] 41 | pub struct TextCapturedMessage { 42 | /// The text captured 43 | pub text: String, 44 | /// The likelihood of the capture 45 | pub likelihood: f32, 46 | /// The tokens captures (with confidence, range and timing) 47 | pub tokens: Option>, 48 | /// The duration it took to do the processing 49 | pub seconds: f32, 50 | /// The site where the text was captured 51 | pub site_id: String, 52 | /// An optional session id if there is a related session 53 | pub session_id: Option, 54 | /// Optional list of the most probable speaker detected 55 | #[serde(skip_serializing_if = "Option::is_none")] 56 | pub speaker_hypotheses: Option>, 57 | } 58 | 59 | impl<'de> HermesMessage<'de> for TextCapturedMessage {} 60 | 61 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 62 | #[serde(rename_all = "camelCase")] 63 | pub struct SpeakerId { 64 | /// The name of the detected speaker, `None` represents unknown speakers 65 | pub name: Option, 66 | /// The confidence of the detection 67 | pub confidence: f32, 68 | } 69 | 70 | impl<'de> HermesMessage<'de> for SpeakerId {} 71 | -------------------------------------------------------------------------------- /hermes/src/ontology/hotword.rs: -------------------------------------------------------------------------------- 1 | use super::HermesMessage; 2 | 3 | #[derive(Debug, Clone, PartialEq, PartialOrd, Hash, Serialize, Deserialize)] 4 | #[serde(rename_all = "camelCase")] 5 | pub enum HotwordModelType { 6 | Universal, 7 | Personal, 8 | } 9 | 10 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 11 | #[serde(rename_all = "camelCase")] 12 | pub struct HotwordDetectedMessage { 13 | /// The site where the hotword was triggered 14 | pub site_id: String, 15 | /// Which model was triggered 16 | pub model_id: String, 17 | /// The version of the model 18 | pub model_version: Option, 19 | /// The type of hotword that was triggered 20 | // TODO make non optional in next major rework of the protocol 21 | #[example_value(Some(HotwordModelType::Universal))] 22 | pub model_type: Option, 23 | /// The current sensitivity of the detector 24 | pub current_sensitivity: Option, 25 | /// Timestamp of the audio frame that triggered the hotword 26 | pub detection_signal_ms: Option, 27 | /// Timestamp of the audio frame where the hotword is likely to end 28 | pub end_signal_ms: Option, 29 | } 30 | 31 | impl<'de> HermesMessage<'de> for HotwordDetectedMessage {} 32 | -------------------------------------------------------------------------------- /hermes/src/ontology/tts.rs: -------------------------------------------------------------------------------- 1 | use super::HermesMessage; 2 | 3 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct SayMessage { 6 | /// The text to say 7 | #[example_value("Hello, world!")] 8 | pub text: String, 9 | /// The lang to use when saying the `text`, will use en_GB if not provided 10 | pub lang: Option, 11 | /// An optional id for the request, it will be passed back in the `SayFinishedMessage` 12 | pub id: Option, 13 | /// The site where the message should be said 14 | pub site_id: String, 15 | /// An optional session id if there is a related session 16 | pub session_id: Option, 17 | } 18 | 19 | impl<'de> HermesMessage<'de> for SayMessage {} 20 | 21 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 22 | #[serde(rename_all = "camelCase")] 23 | pub struct SayFinishedMessage { 24 | /// The id of the `SayMessage` which was has been said 25 | pub id: Option, 26 | /// An optional session id if there is a related session 27 | pub session_id: Option, 28 | } 29 | 30 | impl<'de> HermesMessage<'de> for SayFinishedMessage {} 31 | 32 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 33 | #[serde(rename_all = "camelCase")] 34 | pub struct RegisterSoundMessage { 35 | /// The sound to register encoded as a wav. 36 | #[serde(serialize_with = "super::as_base64", deserialize_with = "super::from_base64")] 37 | pub wav_sound: Vec, 38 | /// The id this sound should be registered under 39 | pub sound_id: String, 40 | } 41 | 42 | impl<'de> HermesMessage<'de> for RegisterSoundMessage {} 43 | -------------------------------------------------------------------------------- /hermes/src/ontology/vad.rs: -------------------------------------------------------------------------------- 1 | use super::HermesMessage; 2 | 3 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 4 | #[serde(rename_all = "camelCase")] 5 | pub struct VadUpMessage { 6 | /// The site concerned 7 | pub site_id: String, 8 | /// Timestamp of the audio frame where voice started to be detected 9 | pub signal_ms: Option, 10 | } 11 | 12 | impl<'de> HermesMessage<'de> for VadUpMessage {} 13 | 14 | #[derive(Debug, Clone, PartialEq, PartialOrd, Deserialize, Serialize, Example)] 15 | #[serde(rename_all = "camelCase")] 16 | pub struct VadDownMessage { 17 | /// The site concerned 18 | pub site_id: String, 19 | /// Timestamp of the audio frame where voice started to be detected 20 | pub signal_ms: Option, 21 | } 22 | 23 | impl<'de> HermesMessage<'de> for VadDownMessage {} 24 | -------------------------------------------------------------------------------- /platforms/c/.gitignore: -------------------------------------------------------------------------------- 1 | *.out 2 | -------------------------------------------------------------------------------- /platforms/c/README: -------------------------------------------------------------------------------- 1 | # Regenerate libsnips_hermes.h 2 | 3 | ```bash 4 | rustup default nightly 5 | hermes-protocol/hermes-mqtt-ffi 6 | cbindgen -c cbindgen.toml -o ../platforms/c/libsnips_hermes.h . -v 7 | ``` 8 | 9 | Then review the changes. cbindgen sometime does some errors, plus there are a few additionnal comments in the .h 10 | -------------------------------------------------------------------------------- /platforms/c/test.c: -------------------------------------------------------------------------------- 1 | #include "libsnips_hermes_full.h" 2 | #include 3 | 4 | void callback(const CSayMessage *ptr, void *user_data) { 5 | printf("in the c callback\n"); 6 | printf("say messages points to %p\n", ptr); 7 | printf("%s\n", ptr->text); 8 | printf("user data is %s\n", user_data); 9 | } 10 | 11 | const char *last_error() { 12 | const char *nullerror = NULL; 13 | const char **error = &nullerror; 14 | 15 | hermes_get_last_error(error); 16 | 17 | return *error; // we leak the string here but well crash just after so that's 18 | // not that big a problem 19 | } 20 | 21 | #define check(hermes_expr) \ 22 | if (hermes_expr != SNIPS_RESULT_OK) { \ 23 | printf("Assertion failed at %s:%i in function %s:\nFailed to execute " \ 24 | "%s\nError was %s\n", \ 25 | __FILE__, __LINE__, __func__, #hermes_expr, last_error()); \ 26 | exit(1); \ 27 | } 28 | 29 | int main() { 30 | check(hermes_enable_debug_logs()); 31 | const CProtocolHandler *truc; 32 | printf("new\n"); 33 | 34 | const char *value_array[] = {"/path/to/cafile"}; 35 | CStringArray ca_files = {.data = value_array, 36 | .size = 37 | sizeof(value_array) / sizeof(value_array[0])}; 38 | 39 | const CMqttOptions options = {.broker_address = "localhost:1883", 40 | .username = NULL, 41 | .password = NULL, 42 | .tls_hostname = NULL, 43 | .tls_ca_file = &ca_files, 44 | .tls_ca_path = NULL, 45 | .tls_client_key = NULL, 46 | .tls_client_cert = NULL, 47 | .tls_disable_root_store = 0}; 48 | 49 | check(hermes_protocol_handler_new_mqtt_with_options(&truc, &options, 50 | "my user data")); 51 | 52 | printf("new done\n"); 53 | const CTtsBackendFacade *tts; 54 | check(hermes_protocol_handler_tts_backend_facade(truc, &tts)); 55 | printf("pointer in C : %p\n", callback); 56 | check(hermes_tts_backend_subscribe_say(tts, callback)); 57 | 58 | while (true) { 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "jest/globals": true 6 | }, 7 | "parser": "@typescript-eslint/parser", 8 | "plugins": ["jest", "@typescript-eslint"], 9 | "extends": "eslint:recommended", 10 | "parserOptions": { 11 | "ecmaVersion": 2018, 12 | "sourceType": "module" 13 | }, 14 | "rules": { 15 | "no-console": "warn", 16 | "quotes": ["error", "single", { "avoidEscape": true }], 17 | "quote-props": ["error", "as-needed"], 18 | "@typescript-eslint/no-unused-vars": "error", 19 | "semi": ["error", "never"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | /dist 3 | node_modules 4 | payloads 5 | /*.h 6 | /crash.log 7 | /experiments.js 8 | libhermes_*.dylib 9 | /docs 10 | /types -------------------------------------------------------------------------------- /platforms/hermes-javascript/LICENSE: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | ### Apache 2.0/MIT 4 | 5 | Licensed under either of 6 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 7 | http://www.apache.org/licenses/LICENSE-2.0) 8 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 9 | http://opensource.org/licenses/MIT) 10 | 11 | at your option. 12 | 13 | ### Contribution 14 | 15 | Unless you explicitly state otherwise, any contribution intentionally submitted 16 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 17 | be dual licensed as above, without any additional terms or conditions. 18 | 19 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node' 4 | } 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hermes-javascript", 3 | "version": "0.4.0", 4 | "hermes-mqtt-version": "0.68.1", 5 | "description": "Hermes javascript bindings", 6 | "keywords": [ 7 | "snips", 8 | "hermes", 9 | "mqtt", 10 | "voice" 11 | ], 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/snipsco/hermes-protocol" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/snipsco/hermes-protocol/issues" 18 | }, 19 | "main": "dist/index.js", 20 | "scripts": { 21 | "start": "npm run lint && npm run clean && npm run build && npm run test && npm run documentation", 22 | "lint": "eslint --ext .js,.ts src", 23 | "lint:fix": "eslint --ext .js,.ts --fix src", 24 | "build": "npm run build:sources && npm run build:declarations && npm run build:types", 25 | "build:sources": "tsc", 26 | "build:declarations": "tsc -d --emitDeclarationOnly --allowJs false", 27 | "build:types": "tsc -p tsconfig.types.json", 28 | "build:hermes": "node scripts/make.js", 29 | "clean": "rimraf dist && rimraf types", 30 | "postinstall": "node scripts/postinstall.js", 31 | "test": "npm run test:mqtt", 32 | "test:mqtt": "npm run test:roundtrip:json && npm run test:mqtt:json && npm run test:mqtt:tls", 33 | "test:mqtt:json": "jest ./tests/mqtt/mqttJson.spec.ts", 34 | "test:mqtt:tls": "jest ./tests/mqtt/mqttTls.spec.ts", 35 | "test:roundtrip:json": "jest ./tests/roundtrips/roundtripJson.spec.ts", 36 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1 --commit-path=. -t=js/", 37 | "documentation": "typedoc --out docs --mode modules --external-modulemap \".*(?:/src/api/types/|/src/)([\\w\\-_]+)/\" --excludeNotExported --excludePrivate --excludeProtected --excludeExternals --ignoreCompilerErrors src" 38 | }, 39 | "engines": { 40 | "node": ">=10" 41 | }, 42 | "author": "Julien Elbaz", 43 | "license": "(MIT OR Apache-2.0)", 44 | "files": [ 45 | "dist", 46 | "types", 47 | "scripts" 48 | ], 49 | "dependencies": { 50 | "chalk": "^2.4.2", 51 | "ffi": "^2.3.0", 52 | "node-fetch": "^2.6.0", 53 | "node-int64": "^0.4.0", 54 | "ref": "^1.3.5", 55 | "ref-array": "^1.2.0", 56 | "ref-struct": "^1.1.0", 57 | "tmp": "^0.1.0", 58 | "wretch": "^1.5.4" 59 | }, 60 | "devDependencies": { 61 | "@types/ffi": "^0.2.2", 62 | "@types/jest": "^24.0.18", 63 | "@types/node-int64": "^0.4.29", 64 | "@types/ref": "0.0.28", 65 | "@types/ref-array": "0.0.28", 66 | "@types/ref-struct": "0.0.29", 67 | "@typescript-eslint/eslint-plugin": "^2.3.2", 68 | "@typescript-eslint/parser": "^2.3.2", 69 | "camelcase": "^5.3.1", 70 | "conventional-changelog-cli": "^2.0.23", 71 | "eslint": "^6.5.1", 72 | "eslint-plugin-jest": "^22.17.0", 73 | "jest": "^24.9.0", 74 | "mqtt": "^3.0.0", 75 | "rimraf": "^3.0.0", 76 | "ts-jest": "^24.1.0", 77 | "typedoc": "^0.15.0", 78 | "typedoc-plugin-external-module-map": "^1.0.0", 79 | "typedoc-plugin-external-module-name": "^2.1.0", 80 | "typescript": "^3.6.3" 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/scripts/make.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const path = require('path') 4 | const fs = require('fs') 5 | const { execSync } = require('child_process') 6 | const tmp = require('tmp') 7 | 8 | const { 9 | LIB_PATH, 10 | LIB_DIST, 11 | REPO_URL, 12 | REPO_NAME, 13 | hermesMqttVersion, 14 | logger 15 | } = require('./utils') 16 | 17 | const cmd = (command, options = {}) => { 18 | try { 19 | execSync(command, { 20 | stdio: 'inherit', 21 | ...options 22 | }) 23 | } catch (error) { 24 | error.cmd = command 25 | throw error 26 | } 27 | } 28 | 29 | function printCmdError (error) { 30 | const { cmd, status, signal } = error 31 | let output = 'Error during the build!\n' 32 | 33 | if(!cmd) { 34 | logger.error(output) 35 | logger.error(error) 36 | return 37 | } 38 | 39 | output += 'Command [' + cmd +'] exited ' 40 | 41 | if(status) { 42 | output += 'with error code (' + status + ')' 43 | } else if(signal) { 44 | output += 'when receiving signal (' + signal + ')' 45 | } 46 | 47 | logger.error(output + '\n') 48 | } 49 | 50 | logger.success('- Building hermes dynamic library from scratch.') 51 | logger.warning('/!\\ Requirements: git, rust, cargo and node.js >= 8\n') 52 | 53 | const tmpDir = tmp.dirSync() 54 | try { 55 | logger.cmd('- Cloning hermes repository.\n') 56 | cmd(`git clone ${REPO_URL}`, { 57 | cwd: tmpDir.name 58 | }) 59 | 60 | const repoFolder = path.resolve(tmpDir.name, REPO_NAME) 61 | logger.cmd('Repository cloned @ ' + repoFolder + '\n') 62 | 63 | logger.cmd('- Checkout tag ' + hermesMqttVersion) 64 | cmd(`git checkout tags/${hermesMqttVersion}`, { 65 | cwd: repoFolder 66 | }) 67 | 68 | // cmd('git submodule update --init --recursive', { 69 | // cwd: repoFolder 70 | // }) 71 | 72 | logger.cmd('- Building the dynamic library from sources.\n') 73 | cmd('cargo build -p hermes-mqtt-ffi --release', { 74 | cwd: repoFolder 75 | }) 76 | 77 | logger.cmd('- Copy the generated dynamic library file to the current working folder.\n') 78 | fs.copyFileSync(LIB_PATH(repoFolder), LIB_DIST) 79 | 80 | logger.success('> Done!\n') 81 | } catch(error) { 82 | printCmdError(error) 83 | } 84 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/scripts/utils.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const chalk = require('chalk') 3 | const fs = require('fs') 4 | 5 | const REPO_URL = 'https://github.com/snipsco/hermes-protocol' 6 | const REPO_NAME = 'hermes-protocol' 7 | const hermesMqttVersion = require('../package.json')['hermes-mqtt-version'] 8 | 9 | const LIB_EXTENSION = { 10 | linux: '.so', 11 | linux2: '.so', 12 | sunos: '.so', 13 | solaris: '.so', 14 | freebsd: '.so', 15 | openbsd: '.so', 16 | darwin: '.dylib', 17 | mac: '.dylib', 18 | win32: '.dll' 19 | }[process.platform] 20 | 21 | const LIB_NAME = { 22 | linux: 'libhermes_mqtt_ffi', 23 | linux2: 'libhermes_mqtt_ffi', 24 | sunos: 'libhermes_mqtt_ffi', 25 | solaris: 'libhermes_mqtt_ffi', 26 | freebsd: 'libhermes_mqtt_ffi', 27 | openbsd: 'libhermes_mqtt_ffi', 28 | darwin: 'libhermes_mqtt_ffi', 29 | mac: 'libhermes_mqtt_ffi', 30 | win32: 'hermes_mqtt_ffi.dll' 31 | }[process.platform] 32 | 33 | const LIB_PATH = baseFolder => 34 | path.join(baseFolder, 'target/release/' + LIB_NAME + LIB_EXTENSION) 35 | const LIB_DIST = path.join(__dirname, '../libhermes_mqtt_ffi' + LIB_EXTENSION) 36 | 37 | const errorStyle = chalk.bold.red 38 | const warningStyle = chalk.bold.yellow 39 | const successStyle = chalk.bold.green 40 | const cmdStyle = chalk.bold 41 | const logError = str => console.error(errorStyle(str)) 42 | const logWarning = str => console.log(warningStyle(str)) 43 | const logCmd = str => console.log(cmdStyle(str)) 44 | const logSuccess = str => console.log(successStyle(str)) 45 | 46 | function osIsRaspbian () { 47 | if(!fs.existsSync('/etc/os-release')) 48 | return false 49 | return ( 50 | ( 51 | fs.readFileSync('/etc/os-release', 'utf-8') 52 | .indexOf('NAME="Raspbian') 53 | ) >= 0 54 | ) 55 | } 56 | 57 | module.exports = { 58 | LIB_PATH, 59 | LIB_DIST, 60 | LIB_EXTENSION, 61 | REPO_URL, 62 | REPO_NAME, 63 | hermesMqttVersion, 64 | osIsRaspbian, 65 | logger: { 66 | error: logError, 67 | warning: logWarning, 68 | success: logSuccess, 69 | cmd: logCmd 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/audio/index.ts: -------------------------------------------------------------------------------- 1 | import ref from 'ref' 2 | import ApiSubset from '../ApiSubset' 3 | import { 4 | AudioTypes, FFIFunctionCall, HermesOptions 5 | } from '../types' 6 | 7 | /** 8 | * @experimental 9 | * 10 | * Warning: Experimental, use at your own risk! 11 | * 12 | * The Audio API subset. 13 | */ 14 | export default class Audio extends ApiSubset { 15 | 16 | constructor(protocolHandler: Buffer, call: FFIFunctionCall, options: HermesOptions) { 17 | super(protocolHandler, call, options, 'hermes_protocol_handler_audio_server_facade') 18 | } 19 | 20 | publishEvents = { 21 | play_audio: { 22 | fullEventName: 'hermes_audio_server_publish_play_bytes_json' 23 | } 24 | } 25 | publishMessagesList: AudioTypes.publishMessagesList = undefined as any 26 | 27 | subscribeEvents = { 28 | 'play_finished/': { 29 | fullEventName: 'hermes_audio_server_subscribe_play_finished_json', 30 | additionalArguments: eventName => [ 31 | ref.allocCString(eventName.substring(14)) 32 | ] 33 | }, 34 | play_finished_all: { 35 | fullEventName: 'hermes_audio_server_subscribe_all_play_finished_json' 36 | }, 37 | } 38 | subscribeMessagesList: AudioTypes.subscribeMessagesList = undefined as any 39 | 40 | destroy () { 41 | this.call('hermes_drop_audio_server_facade', this.facade) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/feedback/index.ts: -------------------------------------------------------------------------------- 1 | import ApiSubset from '../ApiSubset' 2 | import { FeedbackTypes, FFIFunctionCall, HermesOptions } from '../types' 3 | 4 | /** 5 | * The Feedback API subset. 6 | */ 7 | export default class Feedback extends ApiSubset { 8 | 9 | constructor(protocolHandler: Buffer, call: FFIFunctionCall, options: HermesOptions) { 10 | super(protocolHandler, call, options, 'hermes_protocol_handler_sound_feedback_facade') 11 | } 12 | 13 | publishEvents = { 14 | notification_on: { 15 | fullEventName: 'hermes_sound_feedback_publish_toggle_on_json' 16 | }, 17 | notification_off: { 18 | fullEventName: 'hermes_sound_feedback_publish_toggle_off_json' 19 | } 20 | } 21 | publishMessagesList: FeedbackTypes.publishMessagesList = undefined as any 22 | 23 | destroy () { 24 | this.call('hermes_drop_sound_feedback_facade', this.facade) 25 | } 26 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/injection/index.ts: -------------------------------------------------------------------------------- 1 | import ApiSubset from '../ApiSubset' 2 | import { 3 | InjectionTypes, FFIFunctionCall, HermesOptions 4 | } from '../types' 5 | import * as enums from '../types/enums' 6 | 7 | /** 8 | * The Injection API subset. 9 | */ 10 | export default class Injection extends ApiSubset { 11 | 12 | constructor(protocolHandler: Buffer, call: FFIFunctionCall, options: HermesOptions) { 13 | super(protocolHandler, call, options, 'hermes_protocol_handler_injection_facade') 14 | } 15 | 16 | publishEvents = { 17 | injection_request: { 18 | fullEventName: 'hermes_injection_publish_injection_request_json' 19 | }, 20 | injection_status_request: { 21 | fullEventName: 'hermes_injection_publish_injection_status_request_json' 22 | }, 23 | injection_reset_request: { 24 | fullEventName: 'hermes_injection_publish_injection_reset_request_json' 25 | } 26 | } 27 | publishMessagesList: InjectionTypes.publishMessagesList = undefined as any 28 | 29 | subscribeEvents = { 30 | injection_status: { 31 | fullEventName: 'hermes_injection_subscribe_injection_status_json' 32 | }, 33 | injection_complete: { 34 | fullEventName: 'hermes_injection_subscribe_injection_complete_json' 35 | }, 36 | injection_reset_complete: { 37 | fullEventName: 'hermes_injection_subscribe_injection_reset_complete_json' 38 | } 39 | } 40 | subscribeMessagesList: InjectionTypes.subscribeMessagesList = undefined as any 41 | 42 | destroy () { 43 | this.call('hermes_drop_injection_facade', this.facade) 44 | } 45 | 46 | /** 47 | * Injection enumerations. 48 | */ 49 | static enums = { 50 | injectionKind: enums.injectionKind 51 | } 52 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/tts/index.ts: -------------------------------------------------------------------------------- 1 | import ApiSubset from '../ApiSubset' 2 | import { 3 | FFIFunctionCall, HermesOptions, TtsTypes 4 | } from '../types' 5 | 6 | /** 7 | * The Tts API subset. 8 | */ 9 | export default class Tts extends ApiSubset { 10 | 11 | constructor(protocolHandler: Buffer, call: FFIFunctionCall, options: HermesOptions) { 12 | super(protocolHandler, call, options, 'hermes_protocol_handler_tts_facade') 13 | } 14 | 15 | publishEvents = { 16 | register_sound: { 17 | fullEventName: 'hermes_tts_publish_register_sound_json' 18 | } 19 | } 20 | publishMessagesList: TtsTypes.publishMessagesList = undefined as any 21 | 22 | destroy () { 23 | this.call('hermes_drop_tts_facade', this.facade) 24 | } 25 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/ApiSubset.ts: -------------------------------------------------------------------------------- 1 | import { HermesOptions } from './HermesOptions' 2 | 3 | export type SubscribeEventDescriptor = { 4 | fullEventName: string, 5 | additionalArguments?: (eventName: string) => any[] 6 | } 7 | 8 | export type PublishEventDescriptor = { 9 | fullEventName: string 10 | } 11 | 12 | export type MessageListener = (message: T, ...args: any[]) => void 13 | export type FFIFunctionCall = (functionName: string, ...args: any[]) => void 14 | export type SubsetConstructor = new ( 15 | protocolHandler: Buffer, 16 | call: FFIFunctionCall, 17 | options: HermesOptions 18 | ) => Subset 19 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/Audio.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PlayBytesMessage, 3 | PlayFinishedMessage, 4 | } from './messages' 5 | 6 | export namespace AudioTypes { 7 | 8 | /** 9 | * The name and type of message that the Audio subset can publish. 10 | */ 11 | export type publishMessagesList = { 12 | play_audio: PlayBytesMessage 13 | } 14 | 15 | /** 16 | * The name and type of message to which the Audio subset can subscribe. 17 | */ 18 | export type subscribeMessagesList = { 19 | play_finished_all: PlayFinishedMessage 20 | } & { 21 | // Workaround because of the dynamic key 22 | [key: string]: PlayFinishedMessage 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/Dialog.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StartSessionMessage, 3 | ContinueSessionMessage, 4 | IntentNotRecognizedMessage, 5 | IntentMessage, 6 | EndSessionMessage, 7 | SessionEndedMessage, 8 | SessionQueuedMessage, 9 | SessionStartedMessage, 10 | DialogueConfigureMessage 11 | } from './messages' 12 | 13 | export namespace DialogTypes { 14 | 15 | /** 16 | * The name and type of message that the Dialog subset can publish. 17 | */ 18 | export type publishMessagesList = { 19 | start_session: StartSessionMessage, 20 | continue_session: ContinueSessionMessage, 21 | end_session: EndSessionMessage, 22 | configure: DialogueConfigureMessage 23 | } 24 | 25 | /** 26 | * The name and type of message to which the Dialog subset can subscribe. 27 | */ 28 | export type subscribeMessagesList = { 29 | intents: IntentMessage, 30 | intent_not_recognized: IntentNotRecognizedMessage, 31 | session_ended: SessionEndedMessage, 32 | session_queued: SessionQueuedMessage, 33 | session_started: SessionStartedMessage 34 | } & { 35 | // Workaround for intents that have a dynamic key 36 | [key: string]: IntentMessage 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/DialogFlow.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IntentMessage, 3 | IntentNotRecognizedMessage, 4 | SessionStartedMessage, 5 | } from './messages' 6 | 7 | /** 8 | * An object exposing methods used to configure the current dialog round. 9 | */ 10 | export type FlowContinuation = { 11 | /** 12 | * Marks an intent as a possible dialogue continuation for the next dialogue round. 13 | * 14 | * @param intentName - The name of the intent. 15 | * @param action - Function that will be called if the intent gets matched. 16 | * @param options - Configure the continuation. 17 | * @param options.slotFiller - Specify a slot name and enables the slot filler that will try to match this slot. 18 | */ 19 | continue: (intentName: string, action: FlowIntentAction, options?: { slotFiller: string | null }) => FlowActionReturn, 20 | /** 21 | * Enables a custom action if the intent is not recognized in the next dialogue round. 22 | * 23 | * @param action - Function that will be called if no intents are recognized. 24 | */ 25 | notRecognized: (action: FlowNotRecognizedAction) => FlowActionReturn, 26 | /** 27 | * Marks the dialogue session as completed. 28 | */ 29 | end: () => void 30 | } 31 | 32 | /** 33 | * An object returned by a dialogue round action function and that configures that next session message. 34 | */ 35 | export type FlowActionReturnData = { 36 | text?: string, 37 | customData?: string 38 | } 39 | /** 40 | * The full return type of a dialogue round action function. 41 | * 42 | * Either a full FlowActionReturnData object, a string for the TTS speech or nothing. 43 | * (or a Promise wrapping these types) 44 | */ 45 | export type FlowActionReturn = 46 | FlowActionReturnData | string | void | 47 | Promise 48 | 49 | /** 50 | * A callback for the current round of dialogue that will be run when receiving an intent. 51 | * 52 | * @param message - The intent message received this round. 53 | * @param flow - The object used to configure the next dialogue round. 54 | */ 55 | export type FlowIntentAction = ( 56 | message: IntentMessage, 57 | flow: FlowContinuation 58 | ) => FlowActionReturn 59 | 60 | /** 61 | * A callback for the current round of dialogue that will be run when no intents were matched. 62 | * 63 | * @param message - The message received this round. 64 | * @param flow - The object used to configure the next dialogue round. 65 | */ 66 | export type FlowNotRecognizedAction = ( 67 | message: IntentNotRecognizedMessage, 68 | flow: FlowContinuation 69 | ) => FlowActionReturn 70 | 71 | /** 72 | * A callback for the current round of dialogue that will be run when the session has just been started. 73 | * 74 | * @param message - The message received this round. 75 | * @param flow - The object used to configure the next dialogue round. 76 | */ 77 | export type FlowSessionAction = ( 78 | message: SessionStartedMessage, 79 | flow: FlowContinuation 80 | ) => FlowActionReturn 81 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/Feedback.ts: -------------------------------------------------------------------------------- 1 | import { NotificationMessage } from './messages' 2 | 3 | export namespace FeedbackTypes { 4 | 5 | /** 6 | * The name and type of message that the Feedback subset can publish. 7 | */ 8 | export type publishMessagesList = { 9 | notification_on: NotificationMessage, 10 | notification_off: NotificationMessage 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/HermesOptions.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Options used to create an Hermes instance. 3 | */ 4 | export type HermesOptions = { 5 | /** Hermes bus address. *(default localhost:1883)* */ 6 | address?: string, 7 | /** Enables or Disables stdout logs. *(default false)* */ 8 | logs?: boolean, 9 | /** A custom path/name for the dynamic Hermes ffi library. */ 10 | libraryPath?: string, 11 | /** Username used when connecting to the broker. */ 12 | username?: string, 13 | /** Password used when connecting to the broker. */ 14 | password?: string, 15 | /** Hostname to use for the TLS configuration. If set, enables TLS. */ 16 | tls_hostname?: string, 17 | /** CA files to use if TLS is enabled. */ 18 | tls_ca_file?: string[], 19 | /** CA paths to use if TLS is enabled. */ 20 | tls_ca_path?: string[], 21 | /** Client key to use if TLS is enabled. */ 22 | tls_client_key?: string, 23 | /** Client cert to use if TLS is enabled. */ 24 | tls_client_cert?: string, 25 | /** Boolean indicating if the root store should be disabled if TLS is enabled. */ 26 | tls_disable_root_store?: string 27 | } 28 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/Injection.ts: -------------------------------------------------------------------------------- 1 | import { 2 | InjectionRequestMessage, 3 | InjectionStatusMessage, 4 | InjectionCompleteMessage, 5 | InjectionResetRequestMessage, 6 | InjectionResetCompleteMessage, 7 | } from './messages' 8 | 9 | export namespace InjectionTypes { 10 | 11 | /** 12 | * The name and type of message that the Injection subset can publish. 13 | */ 14 | export type publishMessagesList = { 15 | injection_request: InjectionRequestMessage, 16 | injection_status_request: null, 17 | injection_reset_request: InjectionResetRequestMessage 18 | } 19 | 20 | /** 21 | * The name and type of message to which the Injection subset can subscribe. 22 | */ 23 | export type subscribeMessagesList = { 24 | injection_status: InjectionStatusMessage, 25 | injection_complete: InjectionCompleteMessage, 26 | injection_reset_complete: InjectionResetCompleteMessage 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/Tts.ts: -------------------------------------------------------------------------------- 1 | import { RegisterSoundMessage } from './messages' 2 | 3 | export namespace TtsTypes { 4 | 5 | /** 6 | * The name and type of message that the Tts subset can publish. 7 | */ 8 | export type publishMessagesList = { 9 | register_sound: RegisterSoundMessage 10 | } 11 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/component.ts: -------------------------------------------------------------------------------- 1 | export enum component { 2 | audioServer = 'audioServer', 3 | hotword = 'hotword', 4 | asr = 'asr', 5 | nlu = 'nlu', 6 | dialogue = 'dialogue', 7 | tts = 'tts', 8 | injection = 'injection', 9 | clientApp = 'clientApp' 10 | } 11 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/grain.ts: -------------------------------------------------------------------------------- 1 | export enum grain { 2 | year = 'Year', 3 | quarter = 'Quarter', 4 | month = 'Month', 5 | week = 'Week', 6 | day = 'Day', 7 | hour = 'Hour', 8 | minute = 'Minute', 9 | second = 'Second' 10 | } 11 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/index.ts: -------------------------------------------------------------------------------- 1 | export * from './grain' 2 | export * from './precision' 3 | export * from './initType' 4 | export * from './terminationType' 5 | export * from './slotType' 6 | export * from './injectionKind' 7 | export * from './component' 8 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/initType.ts: -------------------------------------------------------------------------------- 1 | export enum initType { 2 | action = 'action', 3 | notification = 'notification' 4 | } 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/injectionKind.ts: -------------------------------------------------------------------------------- 1 | export enum injectionKind { 2 | add = 'add', 3 | addFromVanilla = 'addFromVanilla' 4 | } 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/precision.ts: -------------------------------------------------------------------------------- 1 | export enum precision { 2 | approximate = 'Approximate', 3 | exact = 'Exact' 4 | } 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/slotType.ts: -------------------------------------------------------------------------------- 1 | export enum slotType { 2 | custom = 'Custom', 3 | number = 'Number', 4 | ordinal = 'Ordinal', 5 | instantTime = 'InstantTime', 6 | timeInterval = 'TimeInterval', 7 | amountOfMoney = 'AmountOfMoney', 8 | temperature = 'Temperature', 9 | duration = 'Duration', 10 | percentage = 'Percentage', 11 | musicAlbum = 'MusicAlbum', 12 | musicArtist = 'MusicArtist', 13 | musicTrack = 'MusicTrack', 14 | city = 'City', 15 | country = 'Country', 16 | region = 'Region' 17 | } 18 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/enums/terminationType.ts: -------------------------------------------------------------------------------- 1 | export enum terminationType { 2 | nominal = 'nominal', 3 | siteUnavailable = 'siteUnavailable', 4 | abortedByUser = 'abortedByUser', 5 | intentNotRecognized = 'intentNotRecognized', 6 | timeout = 'timeout', 7 | error = 'error' 8 | } 9 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HermesOptions' 2 | export * from './DialogFlow' 3 | export * from './ApiSubset' 4 | export * from './Dialog' 5 | export * from './Feedback' 6 | export * from './Injection' 7 | export * from './Audio' 8 | export * from './Tts' 9 | export * from './enums' 10 | export * from './messages' 11 | 12 | import * as _enumerations from './enums' 13 | 14 | export const Enums = _enumerations 15 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/ContinueSessionMessage.ts: -------------------------------------------------------------------------------- 1 | export interface ContinueSessionMessage { 2 | /** Id of the dialogue session to continue. */ 3 | sessionId: string 4 | /** Text that will be spoken by the TTS. */ 5 | text?: string 6 | /** 7 | * A list of intents that will be expected in the next dialogue round. 8 | * If specified, the speech recognition will try to match **only** these intents. 9 | */ 10 | intentFilter?: string[] 11 | /** A custom string stored and passed to the next dialogue round. */ 12 | customData?: string 13 | /** Enables the slot filler that will expect this specific slot to be spoken. */ 14 | slot?: string 15 | /** 16 | * Indicates whether the dialogue manager should handle non recognized 17 | * intents by itself or sent them as an Intent Not Recognized for the client to handle. 18 | * This setting applies only to the next conversation turn. 19 | */ 20 | sendIntentNotRecognized?: boolean 21 | } 22 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/DialogueConfigureMessage.ts: -------------------------------------------------------------------------------- 1 | export interface DialogueConfigureMessage { 2 | /** Id of the site to configure. */ 3 | siteId?: string 4 | /** An array of intents to enable / disable. */ 5 | intents?: { 6 | /** Id of the intent. */ 7 | intentId: string, 8 | /** Enable or diable the intent. */ 9 | enable: boolean 10 | }[] 11 | } 12 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/EndSessionMessage.ts: -------------------------------------------------------------------------------- 1 | export interface EndSessionMessage { 2 | /** Id of the session to end. */ 3 | sessionId: string 4 | /** Text that will be spoken by the TTS. */ 5 | text?: string 6 | } 7 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/InjectionCompleteMessage.ts: -------------------------------------------------------------------------------- 1 | export interface InjectionCompleteMessage { 2 | requestId: string 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/InjectionRequestMessage.ts: -------------------------------------------------------------------------------- 1 | import { injectionKind } from '../enums' 2 | 3 | export interface InjectionRequestMessage { 4 | /** Id of the injection. */ 5 | id: string 6 | /** 7 | * An extra language to compute the pronunciations for. 8 | * *Note: 'en' is the only options for now* 9 | * */ 10 | crossLanguage?: string 11 | /** Custom pronunciations. Do not use if you don't know what this is about! */ 12 | lexicon: { 13 | [key: string]: string[] 14 | } 15 | /** A list of entities mapping to a list of words to inject. */ 16 | operations: [ 17 | injectionKind, 18 | { 19 | [key: string]: (string | [string, number])[] 20 | } 21 | ][] 22 | } 23 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/InjectionResetCompleteMessage.ts: -------------------------------------------------------------------------------- 1 | export interface InjectionResetCompleteMessage { 2 | requestId: string 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/InjectionResetRequestMessage.ts: -------------------------------------------------------------------------------- 1 | export interface InjectionResetRequestMessage { 2 | requestId: string 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/InjectionStatusMessage.ts: -------------------------------------------------------------------------------- 1 | export interface InjectionStatusMessage { 2 | /** The last inejction date. */ 3 | lastInjectionDate: string 4 | } 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/IntentNotRecognizedMessage.ts: -------------------------------------------------------------------------------- 1 | import { NluSlot } from './IntentMessage' 2 | 3 | export interface IntentNotRecognizedMessage { 4 | /** The current session id. */ 5 | sessionId: string 6 | /** The site where the user interaction took place. */ 7 | siteId: string 8 | /** The user input that has generated this event. */ 9 | input: string 10 | /** The level of confidence in the non-prediction. */ 11 | confidenceScore: number 12 | /** Array of alternative nlu slots that have lower probability. */ 13 | alternatives?: { 14 | /** 15 | * Nullable, name of the intent detected (null = no intent) 16 | */ 17 | intentName?: string 18 | /** 19 | * Nullable 20 | */ 21 | slots?: NluSlot[] 22 | /** 23 | * Between 0 and 1 24 | */ 25 | confidenceScore: number 26 | }[], 27 | /** Custom data provided in the StartSessionMessage or a ContinueSessionMessage. */ 28 | customData?: string 29 | } 30 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/NotificationMessage.ts: -------------------------------------------------------------------------------- 1 | export interface NotificationMessage { 2 | /** The targeted site id. */ 3 | siteId: string 4 | /** The targeted session id. */ 5 | sessionId?: string 6 | } 7 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/PlayBytesMessage.ts: -------------------------------------------------------------------------------- 1 | export interface PlayBytesMessage { 2 | /** Id of the playback. */ 3 | id: string 4 | /** Site id to target. */ 5 | siteId: string 6 | /** Sound buffer (Wav PCM16) stringified in base64. */ 7 | wavBytes: string 8 | /** Length of the sound buffer. */ 9 | wavBytesLen: number 10 | } 11 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/PlayFinishedMessage.ts: -------------------------------------------------------------------------------- 1 | export interface PlayFinishedMessage { 2 | /** Id of the audio playback. */ 3 | id: string 4 | /** Id of the site that finished playback. */ 5 | siteId: string 6 | } 7 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/RegisterSoundMessage.ts: -------------------------------------------------------------------------------- 1 | export interface RegisterSoundMessage { 2 | /** Sound label. */ 3 | soundId: string 4 | /** Sound buffer (Wav PCM16) stringified in base64. */ 5 | wavSound: string 6 | /** 7 | * @deprecated 8 | * **Unused! Kept for avoiding breaking existing action code.** 9 | * 10 | * Length of the sound buffer. 11 | */ 12 | wavSoundLen?: number 13 | } 14 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/SessionEndedMessage.ts: -------------------------------------------------------------------------------- 1 | import { terminationType, component } from '../enums' 2 | 3 | export interface SessionEndedMessage { 4 | /** Id of the session that ended. */ 5 | sessionId: string 6 | /** Id of the site where the session took place. */ 7 | siteId: string 8 | /** Custom data provided in the StartSessionMessage or a ContinueSessionMessage. */ 9 | customData?: string 10 | /** Information about the session termination. */ 11 | termination: { 12 | /** Reason of the termination. */ 13 | reason: terminationType, 14 | /** If there was an error, the error description. */ 15 | error?: string 16 | /** If there was a timeout, the component that timeouted. */ 17 | component?: component 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/SessionQueuedMessage.ts: -------------------------------------------------------------------------------- 1 | export interface SessionQueuedMessage { 2 | /** The id of the session that was queued. */ 3 | sessionId: string 4 | /** The id of the site where the interaction takes place. */ 5 | siteId: string 6 | /** Custom data provided in the StartSessionMessage or a ContinueSessionMessage. */ 7 | customData?: string 8 | } 9 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/SessionStartedMessage.ts: -------------------------------------------------------------------------------- 1 | export interface SessionStartedMessage { 2 | /** The id of the session that was started. */ 3 | sessionId: string 4 | /** The id of the site where the interaction takes place. */ 5 | siteId: string 6 | /** Custom data provided in the StartSessionMessage or a ContinueSessionMessage. */ 7 | customData?: string 8 | /** The session was reactivated from this session id. */ 9 | reactivatedFromSessionId?: string 10 | } 11 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/StartSessionMessage.ts: -------------------------------------------------------------------------------- 1 | import { initType } from '../enums' 2 | 3 | export interface StartSessionMessage { 4 | /** Session initializer. */ 5 | init: { 6 | /** The type of the session. */ 7 | type: initType, 8 | /** Text that will be spoken by the TTS. */ 9 | text?: string, 10 | /** 11 | * A list of intents that will be expected in the next dialogue round. 12 | * If specified, the speech recognition will try to match **only** these intents. 13 | */ 14 | intentFilter?: string[], 15 | /** Specify if the session can be enqueued. */ 16 | canBeEnqueued?: boolean, 17 | /** 18 | * Indicates whether the dialogue manager should handle non recognized 19 | * intents by itself or sent them as an Intent Not Recognized for the client to handle. 20 | * This setting applies only to the next conversation turn. 21 | */ 22 | sendIntentNotRecognized?: boolean 23 | } 24 | /** A custom string stored and passed to the next dialogue round. */ 25 | customData?: string 26 | /** The id of the site to start the session on. */ 27 | siteId?: string 28 | } 29 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/TextCapturedMessage.ts: -------------------------------------------------------------------------------- 1 | export interface TextCapturedMessage { 2 | text: string 3 | siteId?: string 4 | sessionId?: string 5 | tokens?: [ 6 | { 7 | /** The value of the token. */ 8 | value: string, 9 | /** Confidence of the token, between 0 and 1, 1 being confident. */ 10 | confidence: number, 11 | /** The start range in which the token is in the original input. */ 12 | rangeStart: number, 13 | /** The end range in which the token is in the original input. */ 14 | rangeEnd: number, 15 | /** Time when this token was detected. */ 16 | time: { 17 | /** Start time. */ 18 | start: number, 19 | /** End time. */ 20 | end: number 21 | } 22 | } 23 | ] 24 | likelihood?: number 25 | seconds?: number 26 | } 27 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/api/types/messages/index.ts: -------------------------------------------------------------------------------- 1 | export * from './PlayBytesMessage' 2 | export * from './PlayFinishedMessage' 3 | export * from './StartSessionMessage' 4 | export * from './ContinueSessionMessage' 5 | export * from './EndSessionMessage' 6 | export * from './IntentMessage' 7 | export * from './IntentNotRecognizedMessage' 8 | export * from './SessionEndedMessage' 9 | export * from './SessionQueuedMessage' 10 | export * from './SessionStartedMessage' 11 | export * from './NotificationMessage' 12 | export * from './InjectionRequestMessage' 13 | export * from './InjectionStatusMessage' 14 | export * from './RegisterSoundMessage' 15 | export * from './DialogueConfigureMessage' 16 | export * from './TextCapturedMessage' 17 | export * from './InjectionResetRequestMessage' 18 | export * from './InjectionCompleteMessage' 19 | export * from './InjectionResetCompleteMessage' 20 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/casts/Casteable.js: -------------------------------------------------------------------------------- 1 | const { cast } = require('../tools') 2 | const ref = require('ref') 3 | 4 | /** 5 | * An abstract class equipped with generic methods 6 | * to serialize / deserialize javascript objects 7 | * and C structures. 8 | */ 9 | class Casteable { 10 | /** 11 | * Create a new Casteable class from either a Buffer containing a C structure or a plain old javascript object. 12 | * 13 | * @param arg - Object or Buffer 14 | * @param customKeyCasting - Keys that need a custom method when casting from a Buffer. 15 | */ 16 | constructor(arg = {}, customKeyCasting) { 17 | // 'type' field: C type to cast the Casteable into 18 | Object.defineProperty(this, 'type', { 19 | enumerable: false, 20 | writable: true 21 | }) 22 | if(arg instanceof Buffer) { 23 | // If we are creating the Casteable from a Buffer (C struct) 24 | Object.assign(this, this.fromBuffer(arg, customKeyCasting)) 25 | } else { 26 | // If we are creating the Casteable from a JS object 27 | Object.assign(this, arg) 28 | } 29 | } 30 | 31 | fromBuffer(buffer, customKeyCasting) { 32 | return cast(buffer, customKeyCasting) 33 | } 34 | 35 | /** 36 | * Forge a C structure from this Casteable. 37 | * 38 | * @param type - C structure type 39 | * @param specialFields - Fields that need a custom method when casting. 40 | */ 41 | forge(type, specialFields = {}) { 42 | if(!type) { 43 | type = this.type 44 | } 45 | const messageStruct = new type 46 | const keys = Object.keys(this) 47 | for(let key of keys) { 48 | const value = this[key] 49 | if(specialFields[key]) { 50 | // Custom casting method 51 | messageStruct[key] = specialFields[key] && specialFields[key](value, messageStruct) 52 | } else if(typeof value === 'string') { 53 | // Write the char* buffer with a trailing null byte. 54 | const strPtr = ref.allocCString(value) 55 | ref._attach(strPtr, this) 56 | messageStruct[key] = strPtr 57 | } else if (value === null || value === undefined) { 58 | messageStruct[key] = null 59 | } else if(typeof value !== 'object') { 60 | // Primitive type. 61 | messageStruct[key] = value 62 | } else { 63 | // This is an object, and we expected to have a special field entry for casting it. 64 | if(!specialFields[key]) { 65 | const error = new Error(`Expected specialField entry for [${key}] property (type: ${type} / specialFields: ${JSON.stringify(specialFields)})`) 66 | // eslint-disable-next-line 67 | console.error(error) 68 | // eslint-disable-next-line 69 | console.error(error.stack) 70 | } 71 | messageStruct[key] = ref.NULL 72 | } 73 | } 74 | return messageStruct 75 | } 76 | } 77 | 78 | module.exports = Casteable 79 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/casts/DoubleArray.js: -------------------------------------------------------------------------------- 1 | const ref = require('ref') 2 | const Casteable = require('./Casteable') 3 | 4 | class DoubleArray extends Casteable { 5 | constructor(arg, properties) { 6 | super() 7 | 8 | if(!properties) { 9 | throw new Error('DoubleArray constructor expects a properties argument.') 10 | } 11 | 12 | const { itemArrayType, refArrayType, cast, forge, countField = 'count', entriesField = 'entries' } = properties 13 | this.properties = { itemArrayType, refArrayType, cast, forge, countField, entriesField } 14 | this._itemArrayType = itemArrayType 15 | 16 | if(arg instanceof Buffer) { 17 | if(arg.length <= 0) { 18 | this._array = null 19 | return 20 | } 21 | this._array = [] 22 | const doubleArrayStruct = arg.deref() 23 | const count = doubleArrayStruct[countField] 24 | const doubleArrayRef = ref.reinterpret(doubleArrayStruct[entriesField], ref.sizeof.pointer * count) 25 | const doubleArray = new refArrayType(doubleArrayRef) 26 | 27 | for(let i = 0; i < count; i++) { 28 | const item = doubleArray[i] 29 | this._array.push(cast(item.deref())) 30 | } 31 | } else { 32 | this._array = arg 33 | } 34 | } 35 | forge() { 36 | if(!this._array) 37 | return null 38 | 39 | const { refArrayType, forge, countField, entriesField } = this.properties 40 | 41 | const itemStruct = new this._itemArrayType({ 42 | // .buffer, not .ref() ! We don't want the pointer, just the buffer containing the array. 43 | [entriesField]: new refArrayType(this._array.map(forge)).buffer, 44 | [countField]: this._array.length 45 | }).ref() 46 | 47 | return itemStruct 48 | } 49 | } 50 | 51 | module.exports = DoubleArray 52 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/casts/MqttOptions.js: -------------------------------------------------------------------------------- 1 | const ref = require('ref') 2 | const Casteable = require('./Casteable') 3 | const StringArray = require('./StringArray') 4 | const { CMqttOptions } = require('../ffi/typedefs') 5 | 6 | class MqttOptions extends Casteable { 7 | constructor(args) { 8 | super(args) 9 | this.type = CMqttOptions 10 | } 11 | 12 | forge() { 13 | return super.forge(this.type, { 14 | tls_ca_file: tls_ca_file => tls_ca_file && new StringArray(tls_ca_file).forge() || ref.NULL, 15 | tls_ca_path: tls_ca_path => tls_ca_path && new StringArray(tls_ca_path).forge() || ref.NULL 16 | }) 17 | } 18 | } 19 | 20 | module.exports = MqttOptions 21 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/casts/StringArray.js: -------------------------------------------------------------------------------- 1 | const ref = require('ref') 2 | const array = require('ref-array') 3 | const Casteable = require('./Casteable') 4 | const { CStringArray } = require('../ffi/typedefs') 5 | 6 | const StringArrayType = array(ref.types.CString) 7 | 8 | class StringArray extends Casteable { 9 | constructor(arg) { 10 | super() 11 | if(arg instanceof Buffer) { 12 | this._array = [] 13 | const cArray = ref.get(arg) 14 | const stringArray = new StringArrayType(cArray.data) 15 | for(let i = 0; i < cArray.size; i++) { 16 | this._array.push(stringArray[i]) 17 | } 18 | } else { 19 | this._array = arg 20 | } 21 | } 22 | forge() { 23 | return new CStringArray({ 24 | data: new StringArrayType(this._array).buffer, 25 | size: this._array.length 26 | }).ref() 27 | } 28 | } 29 | 30 | module.exports = StringArray 31 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/casts/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Casteable: require('./Casteable'), 3 | DoubleArray: require('./DoubleArray'), 4 | StringArray: require('./StringArray'), 5 | MqttOptions: require('./MqttOptions') 6 | } 7 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/ffi/typedefs.js: -------------------------------------------------------------------------------- 1 | const ref = require('ref') 2 | const Struct = require('ref-struct') 3 | 4 | const coerce = ref.coerceType 5 | const pointer = ref.refType 6 | 7 | /* Misc. */ 8 | 9 | const CStringArray = Struct({ 10 | data: coerce('char **'), 11 | size: coerce('int') 12 | }) 13 | 14 | const CMqttOptions = Struct({ 15 | broker_address: coerce('char *'), 16 | username: coerce('char *'), 17 | password: coerce('char *'), 18 | tls_hostname: coerce('char *'), 19 | tls_ca_file: pointer(CStringArray), 20 | tls_ca_path: pointer(CStringArray), 21 | tls_client_key: coerce('char *'), 22 | tls_client_cert: coerce('char *'), 23 | tls_disable_root_store: coerce('uchar'), 24 | }) 25 | 26 | const misc = { 27 | CMqttOptions, 28 | CStringArray 29 | } 30 | 31 | /* Protocol Handler */ 32 | 33 | const CProtocolHandler = Struct({ 34 | handler: coerce('void *'), 35 | user_data: coerce('void *') 36 | }) 37 | 38 | /* Exports */ 39 | 40 | module.exports = { 41 | CProtocolHandler, 42 | ...misc 43 | } 44 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module api 3 | */ 4 | 5 | import { Hermes, HermesOptions } from './api' 6 | import * as tools from './tools' 7 | 8 | export * from './tools' 9 | export * from './api' 10 | 11 | /** 12 | * Will stop Hermes gracefully when called. 13 | */ 14 | export type Done = () => void 15 | 16 | /** 17 | * Sets an event loop up and initializes the Hermes class. 18 | * 19 | * @param context - The wrapped context function. 20 | * @param opts - Options used to create the Hermes instance. 21 | */ 22 | export const withHermes = function( 23 | context: (hermes: Hermes, done: Done) => void, 24 | opts?: HermesOptions 25 | ) { 26 | const hermes = new Hermes(opts) 27 | const keepAliveRef = tools.keepAlive(60000) 28 | const done: Done = () => { 29 | hermes.destroy() 30 | tools.killKeepAlive(keepAliveRef) 31 | } 32 | context(hermes, done) 33 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/tools/index.ts: -------------------------------------------------------------------------------- 1 | import Int64 from 'node-int64' 2 | 3 | /** 4 | * Prevents the process from terminating. 5 | * @param timer - The interval of time between loop iterations. 6 | */ 7 | export function keepAlive(timer = 60000) { 8 | return setInterval(() => {}, timer) 9 | } 10 | 11 | /** 12 | * Stops the keepAlive loop. 13 | * @param keepAliveRef - A reference to an existing keepAlive loop. 14 | */ 15 | export function killKeepAlive(keepAliveRef: NodeJS.Timeout) { 16 | clearInterval(keepAliveRef) 17 | } 18 | 19 | /** 20 | * Generic C struct to JS object casting method. 21 | * @param struct - A "C structure" object exposed by the "ref" module. 22 | * @param customKeysCasting - Allows to specify the behaviour for keys mapping to a sub structure. 23 | */ 24 | export function cast(struct, customKeysCasting = {}) { 25 | 26 | // console.log('before ', struct) 27 | 28 | if(struct instanceof Buffer) { 29 | struct = struct.deref() 30 | } 31 | const obj = { ...struct.toObject() } 32 | 33 | // console.log('after ', obj) 34 | 35 | const keys = Object.keys(obj) 36 | for(let key of keys) { 37 | 38 | // console.log('key: ', key) 39 | 40 | const value = obj[key] 41 | 42 | try { 43 | 44 | const ref = value && value.ref && value.ref() 45 | const valueType = ref && ref.type.name 46 | 47 | if(value instanceof Buffer && value.isNull() || ref && ref.isNull()) { 48 | obj[key] = null 49 | continue 50 | } 51 | 52 | if(customKeysCasting[key]) { 53 | obj[key] = customKeysCasting[key](value, struct) 54 | continue 55 | } 56 | 57 | // console.log('value: ', value) 58 | // console.log('valueType:', valueType) 59 | 60 | if(!ref) { 61 | continue 62 | } else if(valueType === 'StructType*' || valueType === 'StructType') { 63 | // console.log('beforeStructTypeCall ', key, ' > ', valueType) 64 | obj[key] = module.exports.cast(value) 65 | } else if(valueType === 'char*' || valueType === 'string') { 66 | obj[key] = value.readCString() 67 | } else if(valueType === 'int64') { 68 | obj[key] = new Int64(value) 69 | } else { 70 | obj[key] = value.deref() 71 | } 72 | } catch (error) { 73 | // eslint-disable-next-line 74 | console.error(error) 75 | obj[key] = null 76 | } 77 | 78 | // console.log(key, ' -> ', obj[key]) 79 | } 80 | return obj 81 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/src/types/Buffer.d.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare global { 4 | export interface Buffer { 5 | deref: () => any; 6 | ref: () => Buffer; 7 | isNull: () => boolean; 8 | } 9 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/constants.ts: -------------------------------------------------------------------------------- 1 | export const LIB_ENV_FOLDER = 2 | process.env.HERMES_TEST_ENVIRONMENT === 'release' ? 3 | 'release' : 4 | 'debug' 5 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/ContinueSession.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "session id", 3 | "text": "text", 4 | "intentFilter": ["intentA", "intentB"], 5 | "customData": null, 6 | "slot": null, 7 | "sendIntentNotRecognized": true 8 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/DialogueConfigure.json: -------------------------------------------------------------------------------- 1 | { 2 | "siteId": "default", 3 | "intents":[ 4 | { 5 | "intentId": "intent1", 6 | "enable": true 7 | }, 8 | { 9 | "intentId": "intent2", 10 | "enable": false 11 | }, 12 | { 13 | "intentId": "intent3", 14 | "enable": null 15 | } 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/EndSession.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "Session id", 3 | "text": "Session ended" 4 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/InjectionComplete.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "request id" 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/InjectionRequest.json: -------------------------------------------------------------------------------- 1 | { 2 | "crossLanguage": "123", 3 | "id": "456", 4 | "lexicon": { 5 | "films": ["The Wolf of Wall Street", "The Lord of the Rings"] 6 | }, 7 | "operations": [ 8 | ["add", { 9 | "films": [ 10 | ["The Wolf of Wall Street", 1], 11 | ["The Lord of the Rings", 1] 12 | ] 13 | }] 14 | ] 15 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/InjectionReset.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "request id" 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/InjectionResetComplete.json: -------------------------------------------------------------------------------- 1 | { 2 | "requestId": "request id" 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/InjectionStatus.json: -------------------------------------------------------------------------------- 1 | { 2 | "lastInjectionDate": "2018-12-10T11:14:08.468Z" 3 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/Intent.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "677a2717-7ac8-44f8-9013-db2222f7923d", 3 | "customData": "customThing", 4 | "siteId": "default", 5 | "input": "moi du vert", 6 | "intent": { 7 | "intentName": "jelb:lightsColor", 8 | "confidenceScore": 0.5 9 | }, 10 | "asrConfidence": 0.5, 11 | "asrTokens": [ 12 | [ 13 | { 14 | "value": "moi", 15 | "confidence": 0.5, 16 | "rangeStart": 0, 17 | "rangeEnd": 3, 18 | "time": { 19 | "start": 0.5, 20 | "end": 1 21 | } 22 | }, 23 | { 24 | "value": "du", 25 | "confidence": 0.5, 26 | "rangeStart": 4, 27 | "rangeEnd": 6, 28 | "time": { 29 | "start": 1, 30 | "end": 1.5 31 | } 32 | }, 33 | { 34 | "value": "vert", 35 | "confidence": 0.5, 36 | "rangeStart": 7, 37 | "rangeEnd": 11, 38 | "time": { 39 | "start": 1.5, 40 | "end": 2.5 41 | } 42 | } 43 | ] 44 | ], 45 | "slots": [ 46 | { 47 | "confidenceScore": 0.5, 48 | "rawValue": "vert", 49 | "value": { 50 | "kind": "Custom", 51 | "value": "vert" 52 | }, 53 | "alternatives": [{ 54 | "kind": "Custom", 55 | "value": "blue" 56 | }], 57 | "range": { 58 | "start": 7, 59 | "end": 11 60 | }, 61 | "entity": "Color", 62 | "slotName": "Color" 63 | } 64 | ], 65 | "alternatives": [{ 66 | "intentName": "alternativeIntent", 67 | "confidenceScore": 0.5, 68 | "slots": [ 69 | { 70 | "confidenceScore": 0.2, 71 | "rawValue": "blue", 72 | "value": { 73 | "kind": "Custom", 74 | "value": "blue" 75 | }, 76 | "alternatives": [], 77 | "range": { 78 | "start": 7, 79 | "end": 11 80 | }, 81 | "entity": "Color", 82 | "slotName": "Color" 83 | } 84 | ] 85 | }] 86 | } 87 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/IntentNotRecognized.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "session id", 3 | "customData": "data", 4 | "siteId": "site id", 5 | "input": "Hello world", 6 | "confidenceScore": 0.5 7 | } 8 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/PlayFinished.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "8ewnjksdf093jb42", 3 | "siteId": "default" 4 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/SessionEnded.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "677a2717-7ac8-44f8-9013-db2222f7923d", 3 | "customData": null, 4 | "termination": { 5 | "reason": "error", 6 | "error": "Error message" 7 | }, 8 | "siteId": "site id" 9 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/SessionQueued.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "Session id", 3 | "customData": "Custom data", 4 | "siteId": "Site id" 5 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/SessionStarted.json: -------------------------------------------------------------------------------- 1 | { 2 | "sessionId": "session id", 3 | "siteId": "site id", 4 | "customData": null 5 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/SiteMessage.json: -------------------------------------------------------------------------------- 1 | { 2 | "siteId": "default", 3 | "sessionId": "session id" 4 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/messages/StartSession.json: -------------------------------------------------------------------------------- 1 | { 2 | "init": { 3 | "type": "action", 4 | "text": "toto", 5 | "intentFilter": ["intent", "filter"], 6 | "canBeEnqueued": true, 7 | "sendIntentNotRecognized": false 8 | }, 9 | "customData": "customThing", 10 | "siteId": "siteId" 11 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/mqttTls.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import fs from 'fs' 4 | import path from 'path' 5 | import { spawn } from 'child_process' 6 | import mqtt from 'mqtt' 7 | import { Hermes } from '../../dist' 8 | import { LIB_ENV_FOLDER } from '../constants' 9 | 10 | let mosquitto: any, hermes: Hermes 11 | 12 | beforeAll(async () => { 13 | console.log('Launching secure mosquitto') 14 | mosquitto = spawn('mosquitto', ['-c', path.join(__dirname, 'tls/mosquitto-tls.conf')], { stdio: 'ignore' }) 15 | console.log('Mosquitto server using TLS configuration is ready!') 16 | try { 17 | hermes = new Hermes({ 18 | libraryPath: path.join(__dirname, `../../../../target/${LIB_ENV_FOLDER}/libhermes_mqtt_ffi`), 19 | logs: true, 20 | address: 'localhost:18886', 21 | username: 'foo', 22 | password: 'bar', 23 | tls_hostname: 'localhost', 24 | tls_ca_file: [path.join(__dirname, 'tls/ca.cert')], 25 | // tls_ca_path: [path.join(__dirname, 'tls')], 26 | tls_client_key: path.join(__dirname, 'tls/client.key'), 27 | tls_client_cert: path.join(__dirname, 'tls/client.cert') 28 | }) 29 | } catch (error) { 30 | console.error(error) 31 | } 32 | }) 33 | 34 | afterAll(done => { 35 | if(hermes) 36 | hermes.destroy() 37 | console.log('Hermes destroyed.') 38 | setTimeout(() => { 39 | mosquitto.kill() 40 | console.log('Mosquitto killed.') 41 | done() 42 | }, 500) 43 | }) 44 | 45 | it('should connect to a secure TLS mosquitto server', done => { 46 | const message = { 47 | siteId: 'default', 48 | sessionId: 'session id', 49 | confidenceScore: 0.5, 50 | customData: null, 51 | input: null 52 | } 53 | const client = mqtt.connect('mqtts://localhost:18886', { 54 | username: 'foo', 55 | password: 'bar', 56 | ca: fs.readFileSync(path.join(__dirname, 'tls/ca.cert')), 57 | key: fs.readFileSync(path.join(__dirname, 'tls/client.key')), 58 | cert: fs.readFileSync(path.join(__dirname, 'tls/client.cert')), 59 | rejectUnauthorized: false 60 | }) 61 | client.on('error', function(err) { 62 | client.end(true) 63 | done() 64 | throw err 65 | }) 66 | client.on('connect', function () { 67 | client.publish('hermes/dialogueManager/intentNotRecognized', JSON.stringify(message)) 68 | }) 69 | 70 | const callback = function(msg) { 71 | expect(msg).toEqual(message) 72 | client.end() 73 | done() 74 | } 75 | hermes.dialog().on('intent_not_recognized', callback) 76 | }) 77 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/ca.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDBzCCAe+gAwIBAgIJAOZHpLq2Q9+uMA0GCSqGSIb3DQEBCwUAMBoxGDAWBgNV 3 | BAMMD3Bvbnl0b3duIFJTQSBDQTAeFw0xODAzMDgxNDAyMjhaFw0yODAzMDUxNDAy 4 | MjhaMBoxGDAWBgNVBAMMD3Bvbnl0b3duIFJTQSBDQTCCASIwDQYJKoZIhvcNAQEB 5 | BQADggEPADCCAQoCggEBAMrhaFe2UnkwFrXmspL/AgHGx55HVABm0T4CwNon9690 6 | l53wuRnI3/jEdalnwiNlyoNd3j82mUF+8jP+zCbLEtcco/lIDvEgX5vLn5eVV1rI 7 | qjUKTN94iTyG6Ssbh/gWno4jwZ5TLj3pvLNE6ifOIqr5sYz2xIXJpIB/Q63Z1SKh 8 | sBTlanoUPUjj7ITT1m2IVrsuuS/YVVCkvgWlvMvGH6zWEzta1XsDdopP0mnqftRr 9 | HpFssem0o8dJSxGFfsT1te1QUtGoDeHrVdgNVCPv4YZV+BfWBrAAKKBz+G/9gmEZ 10 | 4U5WbvK9scnlvghbETNO7VByB/cygzRX+sGFO74JkL0CAwEAAaNQME4wHQYDVR0O 11 | BBYEFFzwznK7eqBYPN2uq77+PBF2K0E6MB8GA1UdIwQYMBaAFFzwznK7eqBYPN2u 12 | q77+PBF2K0E6MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAsrmUka 13 | yzsvcf7sYMNOjuGwUofmP1nvJos1MPuA/up3yBNRWvwxa5R46+jj/SjLrvPfUZ19 14 | FIIj1Opgxh5UzmlGcwkp6xUGCgr9x/IXLHSoe7RHcu7OdIcIIYVqcZEQewAYz4IM 15 | jFQpUcwAfeO5MFK3lSdfgQmN5xUYKXHHmkqpcCac0qxVRANF7+XdJna4j+f8isz9 16 | gPleHw05VhBW22Hn/qrh/x9lHmCMt7KVJXVjUF4mfDJ42PNYQBOB5uvpyAVzZvsP 17 | TDGFsd8jKeSSA+Z35oTTjzpbooD31G+ooUQXq2P9y/ZZVNfuYyOB8Ob+9rDB7wJS 18 | lW5ofDIY5boeLPM= 19 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/client.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDUjCCAjqgAwIBAgICAxUwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPcG9u 3 | eXRvd24gUlNBIENBMB4XDTE4MDMwODE0MDIyOFoXDTIzMDgyOTE0MDIyOFowGjEY 4 | MBYGA1UEAwwPcG9ueXRvd24gY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 5 | MIIBCgKCAQEAwM8Tr7xCWGYdSGY2kce2dCxzGX09xyLly+NlG2M9aMABXgYCq+Fh 6 | lpMnyM+im6FIerGchqswJkvuDMd6cxSBuTZcdnfT2c7wqmo6DO7Mn/rt4mSClGgB 7 | UUuYNEZr9ZyI/sWBuNL1j9j/wTc7lz+rMgNsIwVqARmLhB9aSmjPnqjeQybTy9sv 8 | 4zuzlxTsxdZAmdPMtdK3OvISpFXzRtijd0ozGIfz4tMxEfLcaCqFBtSEFmUGOGEo 9 | IPYr+94cGdFfSFTM5eoYlrrU2UOdoXgSNERDjqHztfStDAUOGi9znPuzKhNZ/Eq6 10 | duYtqe3J+ggHTkz7dBgnlBVF9YklixTjQwIDAQABo4GhMIGeMAwGA1UdEwEB/wQC 11 | MAAwCwYDVR0PBAQDAgbAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMCMB0GA1UdDgQW 12 | BBSGWtOTIGpuD4iJmI9mX7SOfMX0MjBKBgNVHSMEQzBBgBRc8M5yu3qgWDzdrqu+ 13 | /jwRditBOqEepBwwGjEYMBYGA1UEAwwPcG9ueXRvd24gUlNBIENBggkA5kekurZD 14 | 364wDQYJKoZIhvcNAQELBQADggEBADGshUKttI62Iv0GEGz/F5FMTE4ZNsZKdlAw 15 | juJtse8zF1YG2PzAdCmAErLIOoe0Y71E1Ef9LfOIYOQgAUOnytwfMUYpApc63Vcv 16 | EMFcxkOZVWnNSSz0CkOqc0a/QZTA7QfWLKyIo/NgMVqqSfzI7PKtQFNeVwDEVN1j 17 | NJiQ+UqfZH5hINJT4dXWa14KysikpixO1Av79ZfrZ1TD0Iozd+/R9rHKWCnJP6/R 18 | MGfLm1S3rTXIVHIPTov4Y0T5Sxe6rWTZGcg7laeiTYQdfOVA1Givol8o8FI0RSY5 19 | qy7KODqeyiT1twpeiLcQQy2UTmmUJUjbZHl8yp6S1BFiVqEd40k= 20 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDAzxOvvEJYZh1I 3 | ZjaRx7Z0LHMZfT3HIuXL42UbYz1owAFeBgKr4WGWkyfIz6KboUh6sZyGqzAmS+4M 4 | x3pzFIG5Nlx2d9PZzvCqajoM7syf+u3iZIKUaAFRS5g0Rmv1nIj+xYG40vWP2P/B 5 | NzuXP6syA2wjBWoBGYuEH1pKaM+eqN5DJtPL2y/jO7OXFOzF1kCZ08y10rc68hKk 6 | VfNG2KN3SjMYh/Pi0zER8txoKoUG1IQWZQY4YSgg9iv73hwZ0V9IVMzl6hiWutTZ 7 | Q52heBI0REOOofO19K0MBQ4aL3Oc+7MqE1n8Srp25i2p7cn6CAdOTPt0GCeUFUX1 8 | iSWLFONDAgMBAAECggEADtVfnFuNdyOUCSfj9t84iEOLL2DVSebDayY5dxvxc4ei 9 | E0u/FN4oFd+IZgijZ/nGi7nyYuH0uF2LGOyX3RaszEUEZBDf+ZfUKCg4Axq0H3K8 10 | rngxG53MzCZWtV7dm23JlGA5lLCWN9ToCENpkD9TumtM/HzEJQWEGEwZNDRQFNKi 11 | 3LT2/kvgXa8P1P3dycVYXFeykye7i4h/wp4YbrpVw/m4nmlBl1VOKGA76R2v6ytp 12 | vMNWV2piDitxjiQmQZYjW3OvLqel+Hixo/wnKA2oVM/cWf+1ErdgYQ/3QvGDAvCi 13 | /fktsf+iTQzMukaJQDQLf/4j3xU9IqsXyVqj10+qAQKBgQDpfLsWPBKcE2F+2emX 14 | gq4REUKIlZ/f/sVyRO6qCbk9zBJb8PdHz1kLt5bIxMCTcGYCfgt6w/PMjeAbjJuY 15 | yzHB1Z4lN9OjmdR36RR/VYi8xe8JRVK2evRebWkEYonpfD5net0PsRZCPziMv5pO 16 | 2aMIMzMscKNIfq39bM/smdnHwwKBgQDTZkO/BVStuo55/cS7IeTAdPdGNwfzzppV 17 | RdIpaamt2lyr/YfwYyVhSCmx4a69c1SQ0XdX7qcqMdRgk4bvd9ThGWaakDDT06NW 18 | GaAvCplNyochNh05P6ZH5SrL7j1ayLdOzSjnH8bimqF0SBdjobMZ6ZBsJWscoDTk 19 | ltEgeNI+gQKBgHb38BfvOgg+s9G/1Q83LdxScBVKL/EhTpff2b/YPoPnZH9mc4kq 20 | dJPM0s6vufQmNEQW12CRUbsrm0JOTcX32v2KDA+ot3TH4APNRn1vJsIRFLAjm3aR 21 | hcxLcVQjpWPLw7xUVSk2sUHGFtlYk5aTvEdkVzHQntJEggD8HaHP7rCjAoGAAoNF 22 | rOCYWBROmz6b9wl5GZyKT+hZjNlY1kilLd7WqH1e+qBh3EahbB2aeUXQeOoFCE+l 23 | FoN8szCycI9wXCpVenN917p9CheK6nwVflmm5tjQcGGfSU3rYRFlR9VGH20hWKtx 24 | oAWtrIXgA2zT8lg6s8Ul9VMgic+RsWHljHhDL4ECgYBrMpPn9+pWmzdfX4PFLu4v 25 | aAN54QqzFY+YoEMa+hzH48beTK8osmxjrZl7a0eGKz3R6OY2zxpcphkqW1VInaMx 26 | oBfXMH0qU+u1c7uwmGHg5O6sNLKk9AieGfJ3mW7re3EVdmhZKB3lNi+ehdSDAMSW 27 | lPOIUOV2CR3Shp/437c36A== 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/mosquitto-tls.conf: -------------------------------------------------------------------------------- 1 | port 18886 2 | cafile tests/mqtt/tls/ca.cert 3 | certfile tests/mqtt/tls/server.cert 4 | keyfile tests/mqtt/tls/server.key 5 | allow_anonymous false 6 | password_file tests/mqtt/tls/mqtt_password 7 | require_certificate true -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/mqtt_password: -------------------------------------------------------------------------------- 1 | foo:$6$Hay/3Z8VDExrs2bY$hVIM7srvBCnATmLKT456St+uheRU+AEVSg8AYyALtvHW9LqKLP0SgthEAbk3dnrmJ5MnCveOU/mWeHXJCvjrSA== -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/server.cert: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDdzCCAl+gAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwGjEYMBYGA1UEAwwPcG9u 3 | eXRvd24gUlNBIENBMB4XDTE4MDMwODE0MDIyOFoXDTIzMDgyOTE0MDIyOFowGjEY 4 | MBYGA1UEAwwPcG9ueXRvd24gc2VydmVyMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 5 | MIIBCgKCAQEAtXVoYPtpD+51rREHhc40ZtqZ08myp7mKbMs4PbaPUgSno+RrCY5a 6 | iuVEwLB8gX16jFKKc6+zrJHozVZZ6Me6nw1TGj+mNHW3HnAtRrQakCaQ3zkNpcjp 7 | wJexToPQ4NRyF361djQrJ8/yZVGvqdo8lDyhD2fKxHTgavR2U0pWp478gRQJsTEV 8 | tAfoltDE+2q9BiT5JnjGYjkIyfF0XMm1ZoKem8YJQkOO8+huybdYWL/Ydbsl9IQT 9 | 7z0BeGZnwl0926lCKA2YeDmlk1Hx8blx7gJ8pX2CUqfuWkYggZIDL34EQefPWscb 10 | /wkggdVB289FRKx3WFXcfnfRoYGBTNl//wIDAQABo4HGMIHDMAwGA1UdEwEB/wQC 11 | MAAwCwYDVR0PBAQDAgbAMB0GA1UdDgQWBBT39oyi9CrjsF1tW8+eWQMqzCsTjjBK 12 | BgNVHSMEQzBBgBRc8M5yu3qgWDzdrqu+/jwRditBOqEepBwwGjEYMBYGA1UEAwwP 13 | cG9ueXRvd24gUlNBIENBggkA5kekurZD364wOwYDVR0RBDQwMoIOdGVzdHNlcnZl 14 | ci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0MA0GCSqGSIb3 15 | DQEBCwUAA4IBAQC/s0HL4CA4W//PhvWZPnJbHdztzqWPYsVANYk2RBUDkC+agF+j 16 | rg0mqTTAhNSgydCByIgOPvfd8jNUcjxiND6DKmQQ7h/V5p4A1H/a57yZ0KM7k2ul 17 | c4l5KxXJROIbjWlGYOrM+GREAO0YlpIAZbCzKLPZ3/OauHtbStn/1ZHVYXVH9l+D 18 | j7mUjEihUfnoxfkDRvYTizZWc8bV+N86eRG4SOwLHu73BsnQM0E1XTV3LLjpuOns 19 | l8J/x6ww0/RfzEAzTCiAVFUuqjhgBfAgPOLQ04j79MQ/KF1QhV+Q4ezv7pmcj5+l 20 | 2884fwLUoSrDvlU3vI48DQRXLR+wifO8vg35 21 | -----END CERTIFICATE----- -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tls/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC1dWhg+2kP7nWt 3 | EQeFzjRm2pnTybKnuYpsyzg9to9SBKej5GsJjlqK5UTAsHyBfXqMUopzr7OskejN 4 | Vlnox7qfDVMaP6Y0dbcecC1GtBqQJpDfOQ2lyOnAl7FOg9Dg1HIXfrV2NCsnz/Jl 5 | Ua+p2jyUPKEPZ8rEdOBq9HZTSlanjvyBFAmxMRW0B+iW0MT7ar0GJPkmeMZiOQjJ 6 | 8XRcybVmgp6bxglCQ47z6G7Jt1hYv9h1uyX0hBPvPQF4ZmfCXT3bqUIoDZh4OaWT 7 | UfHxuXHuAnylfYJSp+5aRiCBkgMvfgRB589axxv/CSCB1UHbz0VErHdYVdx+d9Gh 8 | gYFM2X//AgMBAAECggEAC7Ql5lNw5FEqD96QUKYLJTMmRs20kzLZZcj5uKlEtzKL 9 | qXt7F+OxnVHKTG9VqHhyZgiQ24NSoGfMzymppOT0+Xs/h+rhCnMpO3JayTumlvOV 10 | 1n/0jGPYB88A5aKE32t0bGWEyLn2qpuz86sAHIFx9NGwRbEZF/C95MgdtaOO0UHA 11 | 4RhdJCOvF+n5DExiWAMx0ErVhrZ/2YiacRyImaOX42c27Je6Cn5McrfxaHD45IRr 12 | C5ZqgEpaCphPtTlYz9SbnxukCbSQfZTRi9Dh1Z+HJBwjIWsm/EpJCaB9wLLQXVsT 13 | /70WRws1cuAOzwzZYV/vWXVPodsQPojqWcl6Bum8yQKBgQDdvWmKcvBFgdT7zKbN 14 | pL6sK3oQnfqs0Dm+ij2bObpg9KAmXNgGqXC7/+PVt6XzeWKmIHJ8CKFFDQYdrWbJ 15 | ihD/2Liz5wu9n6lIVbRTE4fvolYpy6YJd9aNYJ9PDQrwYBrVswwHMm2vTAT05RwQ 16 | Ebs7PQr27Z8IuKhjsE1W87k3CwKBgQDRfrsENYrtp3Hprz9tWhd0Y+zAWMRwqHBN 17 | WtYVcoHDGAC7PX9StWODQE3AHEey6JyC8TSsLhcdUOb9Iu7Hg17eR0ypygZinpYc 18 | KFUG0vef/lE45XPhyKHwJsy1KScjReHrORiWEGW/6Gb3w4ElyMmQNrV7YUjmNUwT 19 | PJ1+7l4jXQKBgHB390VDAT2wGbBR4DUkH5ZCQxqUlqX6w58yjQbjo/YzfduXcfcH 20 | EGg65XvsN18kLXYntQnWcr4MWhLJdf45al/qs9yEDz9x8TIAToNov6U7SMrQQXpB 21 | 73AsFwHIksjTZikINMydtmaYf1cbsj0wUTA9yQbwBZ8t0jMmJEQ7QLF7AoGBANDN 22 | J0lCtH26bucjLGypZscy8T2lO+WtfIEdEFPAFWWF/vSgjs64y7590QCxLyU6gwRf 23 | tMgICDHH+PJEZwmj/mI9XkO8gDC7L5JRAK4Uh4qHyByVVbKsbfsWMOI/airV9B6m 24 | gcLQBRoJYNeBnK80OPHsFWYryKTqajCiMbfZjSphAoGAdUCWKrWhAHGqJSZMnTm4 25 | ymAn+X8gwTwyj9Sooqlq6UZGVosOkjDqYxpzjtxFE2ahGwNbkAM7KMVCn6d0lG8U 26 | qvUFABvWcsKAfrHj1S65BQD4fy4eQ7OKQixeUcywLwu+TIkoN9E0+u+7WJhSvr2O 27 | QoSKSigu205wiCuLOeXDOZ4= 28 | -----END PRIVATE KEY----- -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/mqtt/tools.ts: -------------------------------------------------------------------------------- 1 | import { createServer, AddressInfo } from 'net' 2 | import camelcase from 'camelcase' 3 | import ApiSubset from '../../dist/api/ApiSubset' 4 | 5 | export const wait = (time: number) => new Promise(resolve => setTimeout(resolve, time)) 6 | 7 | export const getFreePort: () => Promise = () => { 8 | return new Promise((resolve, reject) => { 9 | const server = createServer() 10 | server.on('error', err => { 11 | reject(err) 12 | }) 13 | server.on('listening', () => { 14 | const port = (server.address() as AddressInfo).port 15 | server.close() 16 | resolve(port) 17 | }) 18 | server.listen() 19 | }) 20 | } 21 | 22 | export const camelize = item => { 23 | if(typeof item !== 'object' || !item) 24 | return item 25 | if(item instanceof Array) { 26 | return item.map(value => camelize(value)) 27 | } 28 | (Object as any).entries(item).forEach(([ key, value ]) => { 29 | const camelizedKey = camelcase(key) 30 | const isSameKey = key === camelizedKey 31 | item[camelizedKey] = camelize(value) 32 | if(!isSameKey) { 33 | delete item[key] 34 | } 35 | }) 36 | return item 37 | } 38 | 39 | /* Publish */ 40 | 41 | type PublisherJsonTestArgs= { 42 | client: any, 43 | facade: ApiSubset, 44 | json: any, 45 | hermesTopic: string, 46 | facadePublication: string 47 | } 48 | export const setupPublisherJsonTest = ({ 49 | client, 50 | facade, 51 | json, 52 | hermesTopic, 53 | facadePublication 54 | } : PublisherJsonTestArgs) => { 55 | json = json && { ...json } 56 | return new Promise(resolve => { 57 | client.subscribe(hermesTopic, function() { 58 | facade.publish(facadePublication, json) 59 | }) 60 | client.on('message', (topic, messageBuffer) => { 61 | let message 62 | try { 63 | message = JSON.parse(messageBuffer.toString()) 64 | } catch (e) { 65 | message = null 66 | } 67 | if(message) { 68 | expect(message).toMatchObject(json) 69 | } else { 70 | expect(message).toEqual(null) 71 | } 72 | client.unsubscribe(hermesTopic) 73 | resolve() 74 | }) 75 | }) 76 | } 77 | 78 | /* Subscribe */ 79 | 80 | type SubscriberJsonTestArgs = { 81 | client: any, 82 | facade: ApiSubset, 83 | json: any, 84 | hermesTopic: string, 85 | facadeSubscription: string 86 | } 87 | export const setupSubscriberJsonTest = ({ 88 | client, 89 | facade, 90 | json, 91 | hermesTopic, 92 | facadeSubscription 93 | } : SubscriberJsonTestArgs) => { 94 | // eslint-disable-next-line 95 | return new Promise(async resolve => { 96 | facade.once(facadeSubscription, message => { 97 | expect(message).toMatchObject(json) 98 | resolve() 99 | }) 100 | await wait(5) 101 | client.publish(hermesTopic, JSON.stringify(json)) 102 | }) 103 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tests/roundtrips/jsonWrapper.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import ffi from 'ffi' 3 | import ref from 'ref' 4 | import { LIB_ENV_FOLDER } from '../constants' 5 | 6 | export const library = libraryPath => ffi.Library(libraryPath, { 7 | // Round trips 8 | hermes_ffi_test_round_trip_start_session_json: [ 'int', [ 'string', 'char **' ]], 9 | hermes_ffi_test_round_trip_continue_session_json: [ 'int', [ 'string', 'char **' ]], 10 | hermes_ffi_test_round_trip_end_session_json: [ 'int', [ 'string', 'char **' ]], 11 | hermes_ffi_test_round_trip_intent_json: [ 'int', [ 'string', 'char **' ]], 12 | hermes_ffi_test_round_trip_intent_not_recognized_json: [ 'int', [ 'string', 'char **' ]], 13 | hermes_ffi_test_round_trip_session_started_json: [ 'int', [ 'string', 'char **' ]], 14 | hermes_ffi_test_round_trip_session_queued_json: [ 'int', [ 'string', 'char **' ]], 15 | hermes_ffi_test_round_trip_session_ended_json: [ 'int', [ 'string', 'char **' ]], 16 | hermes_ffi_test_round_trip_injection_request_json: [ 'int', [ 'string', 'char **' ]], 17 | hermes_ffi_test_round_trip_register_sound_json: [ 'int', [ 'string', 'char **' ]], 18 | hermes_ffi_test_round_trip_dialogue_configure_json: [ 'int', [ 'string', 'char **' ]], 19 | hermes_ffi_test_round_trip_text_captured_json: [ 'int', [ 'string', 'char **' ]], 20 | 21 | // Error handling 22 | hermes_ffi_test_get_last_error: [ 'int', [ 'char **' ]], 23 | }) 24 | 25 | export const call = function( 26 | libraryPath = path.join(__dirname, `../../../../target/${LIB_ENV_FOLDER}/libhermes_ffi_test`) 27 | ) { 28 | const library = module.exports.library(libraryPath) 29 | return function(funName, ...args) { 30 | const result = library[funName](...args) 31 | if(result === 0) 32 | return 33 | const errorRef = ref.alloc('char **') 34 | library['hermes_ffi_test_get_last_error'](errorRef) 35 | let errorMessage = 'Error while calling function ' + funName + '\n' 36 | errorMessage += (errorRef as any).deref().readCString() 37 | throw new Error(errorMessage) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /platforms/hermes-javascript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "target": "es6", 6 | "noImplicitAny": false, 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "allowJs": true, 11 | "baseUrl": ".", 12 | "strict": true, 13 | "paths": { 14 | "*": [ 15 | "node_modules/*", 16 | "src/types/*" 17 | ] 18 | }, 19 | "lib": ["es2017"] 20 | }, 21 | "include": [ 22 | "src/**/*" 23 | ] 24 | } -------------------------------------------------------------------------------- /platforms/hermes-javascript/tsconfig.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "target": "es6", 6 | "noImplicitAny": false, 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "types", 10 | "declaration": true, 11 | "allowJs": false, 12 | "baseUrl": ".", 13 | "strict": true, 14 | "lib": ["es2017"] 15 | }, 16 | "include": [ 17 | "src/api/types/**/*" 18 | ] 19 | } -------------------------------------------------------------------------------- /platforms/hermes-kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea 3 | *.iml 4 | build 5 | -------------------------------------------------------------------------------- /platforms/hermes-kotlin/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.11' 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 8 | } 9 | } 10 | 11 | version = "0.69.0-SNAPSHOT" 12 | group = "ai.snips" 13 | 14 | apply plugin: 'kotlin' 15 | 16 | sourceCompatibility = "1.7" 17 | targetCompatibility = "1.7" 18 | 19 | repositories { 20 | mavenLocal() 21 | jcenter() 22 | maven { 23 | url "https://nexus-repository.snips.ai/repository/snips-maven-releases/" 24 | } 25 | maven { 26 | url "https://nexus-repository.snips.ai/repository/snips-maven-snapshots/" 27 | } 28 | } 29 | 30 | dependencies { 31 | compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 32 | compile "ai.snips:snips-nlu-ontology:0.67.2" 33 | compile 'net.java.dev.jna:jna:4.5.0' 34 | compile 'org.parceler:parceler-api:1.1.9' 35 | } 36 | 37 | task sourcesJar(type: Jar, dependsOn: classes) { 38 | classifier 'sources' 39 | from sourceSets.main.allSource 40 | } 41 | 42 | artifacts { 43 | archives sourcesJar 44 | } 45 | 46 | apply plugin: 'maven' 47 | 48 | def _nexusUsername = project.hasProperty("nexusUsername") ? nexusUsername : "" 49 | def _nexusPassword = project.hasProperty("nexusPassword") ? nexusPassword : "" 50 | 51 | uploadArchives { 52 | repositories { 53 | mavenDeployer { 54 | repository(url: "https://nexus-repository.snips.ai/repository/snips-maven-releases/") { 55 | authentication(userName: _nexusUsername, password: _nexusPassword) 56 | } 57 | snapshotRepository(url: "https://nexus-repository.snips.ai/repository/snips-maven-snapshots/") { 58 | authentication(userName: _nexusUsername, password: _nexusPassword) 59 | } 60 | } 61 | } 62 | } 63 | 64 | def installer = install.repositories.mavenInstaller 65 | def deployer = uploadArchives.repositories.mavenDeployer 66 | 67 | [installer, deployer]*.pom*.whenConfigured { pom -> 68 | pom.dependencies.find { dep -> dep.groupId == 'net.java.dev.jna' && dep.artifactId == 'jna' }.scope = "provided" 69 | } 70 | -------------------------------------------------------------------------------- /platforms/hermes-kotlin/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-kotlin/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /platforms/hermes-kotlin/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-bin.zip 6 | -------------------------------------------------------------------------------- /platforms/hermes-kotlin/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /platforms/hermes-kotlin/hermes-kotlin-test/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.11' 3 | repositories { 4 | jcenter() 5 | } 6 | dependencies { 7 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 8 | } 9 | } 10 | 11 | apply plugin: 'kotlin' 12 | 13 | 14 | repositories { 15 | mavenLocal() 16 | jcenter() 17 | maven { 18 | url "https://nexus-repository.snips.ai/repository/snips-maven-releases/" 19 | } 20 | maven { 21 | url "https://nexus-repository.snips.ai/repository/snips-maven-snapshots/" 22 | } 23 | } 24 | 25 | dependencies { 26 | compile rootProject 27 | 28 | compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.+" 29 | 30 | testCompile 'junit:junit:4.12' 31 | testCompile 'com.google.truth:truth:0.36' 32 | 33 | } 34 | 35 | 36 | def buildType = project.hasProperty("debug") ? "debug" : "release" 37 | println "Using build type $buildType" 38 | 39 | def rustTargetPath = new File(project.hasProperty("rustTargetPath") ? project.rustTargetPath : "$buildDir/../../../../target").canonicalPath 40 | println "Using rust target path $rustTargetPath" 41 | 42 | 43 | def soForJar = [ 44 | ["$rustTargetPath/$buildType/libhermes_ffi_test.so", "linux-x86-64", "linuxNative" ], 45 | ["$rustTargetPath/$buildType/libhermes_ffi_test.dylib", "darwin", "macOsNative" ], 46 | ["$rustTargetPath/arm-unknown-linux-gnueabihf/$buildType/libhermes_ffi_test.so", "linux-arm", "linuxArmCross"] 47 | ] 48 | 49 | println "so for jars: $soForJar" 50 | 51 | def jniLibsDir = new File(buildDir, "jniLibs") 52 | 53 | soForJar.forEach { 54 | def taskName = "copySo${it[2].capitalize()}ForJar" 55 | def soFile = file(it[0]) 56 | def destDir = new File(jniLibsDir, it[1]) 57 | 58 | task(taskName, type: Copy) { 59 | from soFile 60 | into destDir 61 | } 62 | processResources.dependsOn(taskName) 63 | } 64 | 65 | sourceSets { 66 | main { 67 | resources { 68 | srcDir jniLibsDir 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /platforms/hermes-kotlin/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * In a single project build this file can be empty or even removed. 6 | * 7 | * Detailed information about configuring a multi-project build in Gradle can be found 8 | * in the user guide at https://docs.gradle.org/4.0.2/userguide/multi_project_builds.html 9 | */ 10 | 11 | /* 12 | // To declare projects as part of a multi-project build use the 'include' method 13 | include 'shared' 14 | include 'api' 15 | include 'services:webservice' 16 | */ 17 | 18 | rootProject.name = 'hermes' 19 | 20 | 21 | include ":hermes-kotlin-test" 22 | -------------------------------------------------------------------------------- /platforms/hermes-python/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | build/ 3 | dist/ 4 | env/ 5 | hermes_python/dylib/ 6 | tests/roundtrip/debug/ 7 | -------------------------------------------------------------------------------- /platforms/hermes-python/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /platforms/hermes-python/LICENSE.txt: -------------------------------------------------------------------------------- 1 | ## License 2 | 3 | ### Apache 2.0/MIT 4 | 5 | Licensed under either of 6 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 7 | http://www.apache.org/licenses/LICENSE-2.0) 8 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or 9 | http://opensource.org/licenses/MIT) 10 | 11 | at your option. 12 | 13 | ### Contribution 14 | 15 | Unless you explicitly state otherwise, any contribution intentionally submitted 16 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 17 | be dual licensed as above, without any additional terms or conditions. 18 | 19 | -------------------------------------------------------------------------------- /platforms/hermes-python/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE.txt 2 | include LICENSE-APACHE 3 | include LICENSE-MIT 4 | include README.md 5 | recursive-include hermes_python/dylib/ * 6 | -------------------------------------------------------------------------------- /platforms/hermes-python/build_scripts/build-wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e -x 3 | 4 | PYTHON_PROJECT_PATH=/io/platforms/hermes-python 5 | WHEELHOUSE=/io/platforms/hermes-python/wheelhouse 6 | 7 | # Install Rust 8 | curl https://sh.rustup.rs -sSf | bash -s -- -y 9 | export PATH="/usr/local/bin:$HOME/.cargo/bin:$PATH" 10 | 11 | # Build the .so file 12 | cd /io 13 | if ! [[ -z "$(ls -A platforms/hermes-python/hermes_python/dylib)" ]]; then 14 | echo "hermes_python/dylib should be empty. Aborting!" && exit 1 15 | fi 16 | 17 | mkdir -p platforms/hermes-python/hermes_python/dylib 18 | mkdir -p platforms/hermes-python/target 19 | 20 | CARGO_TARGET_DIR=$PYTHON_PROJECT_PATH/target cargo rustc --lib --manifest-path hermes-mqtt-ffi/Cargo.toml --release -- --crate-type cdylib || exit 1 21 | 22 | # Move .so to correct path : 23 | mv $PYTHON_PROJECT_PATH/target/release/libhermes_mqtt_ffi.so $PYTHON_PROJECT_PATH/hermes_python/dylib/ 24 | 25 | # Build wheel 26 | cd $PYTHON_PROJECT_PATH 27 | for PYBIN in /opt/python/*/bin; do 28 | ${PYBIN}/python setup.py bdist_wheel -d ${WHEELHOUSE} 29 | done 30 | 31 | cd ${WHEELHOUSE} 32 | 33 | # Audit wheel 34 | for whl in ${WHEELHOUSE}/*.whl; do 35 | if [[ ${whl} != *none-any.whl ]]; then 36 | auditwheel repair ${whl} -w ${WHEELHOUSE} 37 | fi 38 | done 39 | 40 | # Testing of the wheel is disabled for now 41 | 42 | #for PYBIN in /opt/python/*/bin; do 43 | # # Install package 44 | # ${PYBIN}/pip install -v hermes-python --no-index -f ${WHEELHOUSE} 45 | # # Test 46 | # ${PYBIN}/python -c "from hermes_python.hermes import Hermes" 47 | #done 48 | 49 | # Delete non repaired wheels 50 | rm -rf ${WHEELHOUSE}/*-linux_* 51 | # Delete unrelated wheels 52 | rm -rf ${WHEELHOUSE}/*-none-any.whl 53 | -------------------------------------------------------------------------------- /platforms/hermes-python/build_scripts/build_macos_wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # This script should be ran from the root of the repository 3 | set -e -x 4 | 5 | # Build the .so file 6 | if ! [[ -z "$(ls -A platforms/hermes-python/hermes_python/dylib)" ]]; then 7 | echo "hermes_python/dylib should be empty. Aborting!" && exit 1 8 | fi 9 | 10 | CARGO_TARGET_DIR=platforms/hermes-python/target cargo rustc --lib --manifest-path hermes-mqtt-ffi/Cargo.toml --release -- --crate-type cdylib || exit 1 11 | 12 | mv platforms/hermes-python/target/release/libhermes_mqtt_ffi.dylib platforms/hermes-python/hermes_python/dylib/ 13 | 14 | # Build wheel 15 | cd platforms/hermes-python/ 16 | # Build wheel 17 | ## This part uses pyenv to build against different distributions of python. 18 | 19 | PYTHON_INTERPRETERS_VERSION=( '2.7.15' '3.5.6' '3.6.5' '3.7.1') 20 | 21 | for PY_VERSIO in "${PYTHON_INTERPRETERS_VERSION[@]}"; 22 | do 23 | echo "Building wheel for python version : " 24 | echo $PY_VERSIO 25 | virtualenv --python="$(PYENV_VERSION=$PY_VERSIO pyenv which python)" env 26 | source env/bin/activate 27 | python setup.py bdist_wheel 28 | rm -rf env 29 | done 30 | 31 | # Clean up after yourself 32 | rm hermes_python/dylib/*.dylib 33 | 34 | ls -1 dist/ 35 | mkdir -p wheelhouse 36 | cp dist/*.whl wheelhouse/ 37 | rm -rf dist/*.whl 38 | cd ../.. 39 | -------------------------------------------------------------------------------- /platforms/hermes-python/build_scripts/build_many_linux_wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e -x 3 | 4 | sudo docker run -v `pwd`:/io quay.io/pypa/manylinux1_x86_64 /io/platforms/hermes-python/build_scripts/build-wheels.sh 5 | -------------------------------------------------------------------------------- /platforms/hermes-python/build_scripts/build_raspi_wheels.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e -x 3 | 4 | # Build the .so file 5 | if ! [[ -z "$(ls -A platforms/hermes-python/hermes_python/dylib)" ]]; then 6 | echo "hermes_python/dylib should be empty. Aborting!" && exit 1 7 | fi 8 | 9 | CARGO_TARGET_DIR=platforms/hermes-python/target cargo dinghy --platform raspbian build -p hermes-mqtt-ffi --release || exit 1 10 | 11 | mkdir -p platforms/hermes-python/hermes_python/dylib 12 | mv platforms/hermes-python/target/arm-unknown-linux-gnueabihf/release/libhermes_mqtt_ffi.so platforms/hermes-python/hermes_python/dylib/ 13 | 14 | # Build wheel 15 | PYTHON_INTERPRETERS=( 'python2.7' 'python3.5') 16 | 17 | cd platforms/hermes-python 18 | for PYINTERPRETER in "${PYTHON_INTERPRETERS[@]}"; 19 | do 20 | echo $PYINTERPRETER 21 | virtualenv --python=$PYINTERPRETER env 22 | source env/bin/activate 23 | python setup.py bdist_wheel --plat-name linux-armv7l 24 | python setup.py bdist_wheel --plat-name linux-armv6l 25 | rm -rf env 26 | done 27 | 28 | # Clean up after yourself 29 | rm hermes_python/dylib/*.so 30 | 31 | ls -1 dist/ 32 | mkdir -p wheelhouse 33 | cp dist/*.whl wheelhouse/ 34 | rm -rf dist/*.whl 35 | cd ../.. 36 | -------------------------------------------------------------------------------- /platforms/hermes-python/build_scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | sudo rm -rf platforms/hermes-python/hermes_python/dylib/libhermes_mqtt_ffi.* 3 | sudo rm -rf platforms/hermes-python/target 4 | sudo rm -rf platforms/hermes-python/build 5 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | rst: 15 | @$(SPHINXBUILD) -M rst "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 16 | head -n 9 ../README.rst >> tmp.rst 17 | tail -n+5 build/rst/README.rst >> tmp.rst 18 | mv tmp.rst ../README.rst 19 | 20 | github: 21 | @$(SPHINXBUILD) -M rst "$(SOURCEDIR)" "$(BUILDDIR)" "$(SOURCEDIR)/README.rst" $(SPHINXOPTS) $(O) 22 | head -n 9 ../README.rst >> tmp.rst 23 | tail -n+5 build/rst/README.rst >> tmp.rst 24 | mv tmp.rst ../README.rst 25 | 26 | .PHONY: help Makefile rst 27 | 28 | # Catch-all target: route all unknown targets to Sphinx using the new 29 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 30 | %: Makefile 31 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/HISTORY.rst: -------------------------------------------------------------------------------- 1 | History 2 | ========== 3 | 0.8.1 (2019-10-03) 4 | ------------------ 5 | * Hotfix : adding back DialogueConfiguration in the main module + Conversion function for SessionTermination object 6 | 7 | 0.8.0 (2019-09-10) 8 | ------------------ 9 | * Adds subscription to injection lifecycle events : subscribe_injection_complete, subscribe_injection_reset_complete 10 | * Adds a component field to the SessionTerminationType class 11 | * Introduces alternatives intent resolutions 12 | * Fixes folder creation issue when building the wheel from sources 13 | 14 | 0.7.0 (2019-05-14) 15 | ------------------ 16 | * Introduces Entities Injection API. 17 | 18 | 0.6.1 (2019-05-10) 19 | ------------------ 20 | * Introduces `register_sound` API 21 | 22 | 0.5.2 (2019-05-07) 23 | ------------------ 24 | * Fixes nullable fields in Dialogue ontology and brings more type annotations 25 | 26 | 0.5.1 (2019-05-06) 27 | ------------------ 28 | * introduces new (cli) API to build python wheel that include pre-compiled hermes-mqtt-ffi extension. 29 | 30 | 0.5.0 (2019-04-19) 31 | ------------------- 32 | * Adds APIs to enable and disable sound feedback. 33 | 34 | 0.4.1 (2019-03-29) 35 | ------------------ 36 | * Re-enables debugging of hermes-python with the `rust_logs_enabled` flag 37 | * AmountOfMoneyValue, InstantTimeValue and DurationValue slot values now use Precision and Grain enumerations 38 | 39 | 0.4.0 (2019-03-20) 40 | ------------------ 41 | * Adds support to configure the Dialogue Mananger : enabling and disabling intents on the fly. 42 | * Adds slot filling API : You can ask for a specific slot when continuing a session 43 | * adding support for `OrdinalSlot` 44 | 45 | 0.3.3 (2019-03-06) 46 | ------------------ 47 | * Fixes a bug with `publish_start_session_notification` that didn't take the `text` parameter into account. 48 | 49 | 0.3.2 (2019-02-25) 50 | ------------------ 51 | * Fixes an important bug that gave the argument `hermes` the wrong type for every registered callback. 52 | * Fixes an important bug that caused the program to crash when parsing intentMessage that had no slots. 53 | 54 | 0.3.1 (2019-02-25) 55 | ------------------ 56 | * Fixes import bug with templates, the `hermes_python.ffi.utils` module now re-exports `MqttOptions` 57 | 58 | 0.3.0 (2019-02-25) 59 | ------------------ 60 | * `IntentClassifierResult`'s `probability` field has been renamed to `confidence_score`. 61 | * Introduces support for snips-platform `1.1.0 - 0.61.1`. 62 | 63 | 0.2.0 (2019-02-04) 64 | ------------------ 65 | * Introduces options to connect to the MQTT broker (auth + TLS are now supported). 66 | 67 | 0.1.29 (2019-01-29) 68 | ------------------- 69 | * Fixes bug when deserializing `TimeIntervalValue` that used wrong `encode` method instead of `decode`. 70 | 71 | 0.1.28 (2019-01-14) 72 | ------------------- 73 | * Fixes bug when the `__exit__` method was called twice on the `Hermes` class. 74 | * Introduces two methods to the public api : `connect` and `disconnect` that should bring more flexibility 75 | 76 | 0.1.27 (2019-01-07) 77 | ------------------- 78 | * Fixed broken API introduced in `0.1.26` with the publish_continue_session method of the Hermes class. 79 | * Cast any string that goes in the mqtt_server_adress parameter in the constructor of the Hermes class to be a 8-bit string. 80 | 81 | 0.1.26 (2019-01-02) 82 | --------------------- 83 | * LICENSING : This wheel now has the same licenses as the parent project : APACHE-MIT. 84 | * Subscription to not recognized intent messages is added to the API. You can now write your own callbacks to handle unrecognized intents. 85 | * Adds send_intent_not_recognized flag to continue session : indicate whether the dialogue manager should handle non recognized intents by itself or sent them as an `IntentNotRecognizedMessage` for the client to handle. 86 | 87 | 0.1.25 (2018-12-13) 88 | --------------------- 89 | * Better error handling : Errors from wrapped C library throw a LibException with detailled errors. 90 | 91 | 92 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/README.rst: -------------------------------------------------------------------------------- 1 | Hermes Python 2 | ============= 3 | .. include:: about.rst 4 | 5 | .. include:: requirements.rst 6 | 7 | .. include:: installation.rst 8 | 9 | .. include:: tutorial.rst 10 | 11 | Release Checklist 12 | ================= 13 | 14 | Everytime you need to perform a release, do the following steps : 15 | 16 | * [ ] Commit all changes to the project for said release 17 | * [ ] Write all the changes introduced in the Changelog (source/HISTORY.rst file) and commit it 18 | * [ ] Run tests 19 | * [ ] Build the documentation and commit the README.rst 20 | * [ ] Bump the version and commit it 21 | * [ ] Upload to PyPI 22 | 23 | Build details 24 | ============= 25 | 26 | Creating macOS wheels 27 | --------------------- 28 | 29 | The build script : ``build_scripts/build_macos_wheels.sh`` uses ``pyenv`` 30 | to generate ``hermes-python`` wheels for different versions of python. 31 | 32 | To be able to run it, you need to : 33 | 34 | * install pyenv : brew install pyenv. Then follow the additional steps detailled 35 | * you then have to install python at different versions: 36 | ``pyenv install --list`` to list the available version to install 37 | * Before installing and building the different python version from sources, 38 | install the required dependencies : `Link here `_ 39 | 40 | That's it ! 41 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/about.rst: -------------------------------------------------------------------------------- 1 | About 2 | ===== 3 | 4 | The ``hermes-python`` library provides python bindings for the Hermes protocol that snips components use to communicate together over MQTT. 5 | ``hermes-python`` allows you to interface seamlessly with the Snips platform and kickstart development of Voice applications. 6 | 7 | ``hermes-python`` abstracts away the connection to the MQTT bus and the parsing of incoming and outcoming messages from and to the components of the snips platform. 8 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/api.rst: -------------------------------------------------------------------------------- 1 | API Reference 2 | ============= 3 | 4 | The Hermes Client 5 | ----------------- 6 | 7 | .. module:: hermes_python.hermes 8 | 9 | .. autoclass:: Hermes 10 | :members: 11 | 12 | 13 | Ontology 14 | -------- 15 | 16 | Session 17 | ^^^^^^^ 18 | 19 | .. module:: hermes_python.ontology.dialogue.session 20 | 21 | .. autoclass:: StartSessionMessage 22 | :members: __init__ 23 | 24 | .. autoclass:: EndSessionMessage 25 | :members: __init__ 26 | 27 | .. autoclass:: ContinueSessionMessage 28 | :members: __init__ 29 | 30 | .. autoclass:: SessionStartedMessage 31 | :members: __init__ 32 | 33 | .. autoclass:: SessionEndedMessage 34 | :members: __init__ 35 | 36 | .. autoclass:: SessionQueuedMessage 37 | :members: __init__ 38 | 39 | .. autoclass:: SessionInitAction 40 | :show-inheritance: 41 | :members: __init__ 42 | 43 | .. autoclass:: SessionInitNotification 44 | :show-inheritance: 45 | :members: __init__ 46 | 47 | .. autoclass:: SessionInit 48 | 49 | 50 | 51 | Intent 52 | ^^^^^^ 53 | 54 | .. module:: hermes_python.ontology.dialogue.intent 55 | 56 | .. autoclass:: IntentMessage 57 | :members: __init__ 58 | 59 | .. autoclass:: IntentClassifierResult 60 | :members: __init__ 61 | 62 | .. autoclass:: IntentNotRecognizedMessage 63 | :members: __init__ 64 | 65 | :members: 66 | 67 | .. module:: hermes_python.ontology.nlu 68 | 69 | .. autoclass:: NluIntentAlternative 70 | :members: __init__ 71 | 72 | 73 | Slots 74 | ^^^^^ 75 | 76 | .. autoclass:: NluSlot 77 | :members: __init__ 78 | 79 | .. autoclass:: SlotMap 80 | :members: __init__ 81 | 82 | .. autoclass:: SlotsList 83 | :members: 84 | 85 | .. autoclass:: NluSlot 86 | :members: 87 | 88 | 89 | Slot Values 90 | ^^^^^^^^^^^ 91 | 92 | .. module:: hermes_python.ontology.slot 93 | 94 | .. autoclass:: SlotValue 95 | :members: __init__ 96 | 97 | .. autoclass:: CustomValue 98 | :members: __init__ 99 | 100 | .. autoclass:: NumberValue 101 | :members: __init__ 102 | 103 | .. autoclass:: OrdinalValue 104 | :members: __init__ 105 | 106 | .. autoclass:: AmountOfMoneyValue 107 | :members: __init__ 108 | 109 | .. autoclass:: TemperatureValue 110 | :members: __init__ 111 | 112 | .. autoclass:: InstantTimeValue 113 | :members: __init__ 114 | 115 | .. autoclass:: TimeIntervalValue 116 | :members: __init__ 117 | 118 | .. autoclass:: DurationValue 119 | :members: __init__ 120 | 121 | .. autoclass:: PercentageValue 122 | :members: __init__ 123 | 124 | .. autoclass:: MusicArtistValue 125 | :members: __init__ 126 | 127 | .. autoclass:: MusicAlbumValue 128 | :members: __init__ 129 | 130 | .. autoclass:: MusicTrackValue 131 | :members: __init__ 132 | 133 | .. autoclass:: CityValue 134 | :members: __init__ 135 | 136 | .. autoclass:: CountryValue 137 | :members: __init__ 138 | 139 | .. autoclass:: RegionValue 140 | :members: __init__ 141 | 142 | Injection 143 | ^^^^^^^^^ 144 | 145 | .. module:: hermes_python.ontology.injection 146 | 147 | .. autoclass:: InjectionRequestMessage 148 | :members: __init__ 149 | 150 | .. autoclass:: InjectionCompleteMessage 151 | :members: __init__ 152 | 153 | .. autoclass:: InjectionResetRequestMessage 154 | :members: __init__ 155 | 156 | .. autoclass:: InjectionResetCompleteMessage 157 | :members: __init__ 158 | 159 | .. autoclass:: InjectionRequestOperation 160 | :members: __init__ 161 | 162 | .. autoclass:: AddInjectionRequest 163 | :show-inheritance: 164 | :members: __init__ 165 | 166 | .. autoclass:: AddFromVanillaInjectionRequest 167 | :show-inheritance: 168 | :members: __init__ 169 | 170 | 171 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/index.rst: -------------------------------------------------------------------------------- 1 | Hermes Python 2 | ============= 3 | 4 | .. toctree:: 5 | :maxdepth: 2 6 | 7 | about 8 | requirements 9 | installation 10 | tutorial 11 | api 12 | HISTORY -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/installation.rst: -------------------------------------------------------------------------------- 1 | Installation 2 | ============ 3 | 4 | The library is packaged as a pre-compiled platform wheel, available on `PyPi `_. 5 | 6 | It can be installed with : 7 | ``pip install hermes-python``. 8 | 9 | Or you can add it to your `requirements.txt` file. 10 | 11 | Building from source 12 | ==================== 13 | 14 | If you want to use ``hermes-python`` on platforms that are not supported, you have to manually compile the wheel. 15 | 16 | You need to have ``rust`` and ``cargo`` installed : 17 | 18 | ``curl https://sh.rustup.rs -sSf`` 19 | 20 | Clone, the ``hermes-protocol`` repository : :: 21 | 22 | git clone git@github.com:snipsco/hermes-protocol.git 23 | cd hermes-protocol/platforms/hermes-python 24 | 25 | You can then build the wheel : :: 26 | 27 | virtualenv env 28 | source env/bin/activate 29 | python setup.py bdist_wheel 30 | 31 | The built wheels should be in ``platforms/hermes-python/dist`` 32 | 33 | You can install those with pip : ``pip install platforms/hermes-python/dist/.whl`` 34 | 35 | Advanced wheel building 36 | ======================= 37 | 38 | We define a new API for including pre-compiled shared objects when building a platform wheel. :: 39 | 40 | python setup.py bdist_wheel 41 | 42 | This command will compile the ``hermes-mqtt-ffi`` Rust extension, copy them to an appropriate location, and include them in the wheel. 43 | 44 | We introduce a new command-line argument : ``include-extension`` which is a way to include an already compiled (in previous steps) ``hermes-mqtt-ffi`` extension in the wheel. 45 | 46 | Its usage is the following : ``include-extension=`` 47 | 48 | For instance : :: 49 | 50 | python setup.py bdist_wheel --include-extension=default 51 | 52 | The default value for ``include-extension`` will look up for pre-compiled extension in the default paths (in ``hermes-protocol/target/release/libhermes_mqtt_ffi.[dylib|so]`` and ``hermes-protocol/platforms/hermes-python/hermes_python/dylib``). :: 53 | 54 | python setup.py bdist_wheel --include-extension= 55 | 56 | When doing x-compilation, you can also specify the target platform : :: 57 | 58 | python setup.py bdist_wheel --include-extension= --plat-name= 59 | 60 | 61 | -------------------------------------------------------------------------------- /platforms/hermes-python/documentation/source/requirements.rst: -------------------------------------------------------------------------------- 1 | Requirements 2 | ============ 3 | 4 | Pre-compiled wheels are available for Python 2.7+ and Python 3.5 5 | 6 | The pre-compiled wheels supports the following platform tags : 7 | 8 | * ``manylinux1_x86_64`` 9 | * ``armv7l``, ``armv6`` 10 | * ``macos`` 11 | 12 | If you want to install ``hermes-python`` on another platform, you have to build it from source. 13 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import unicode_literals 3 | from . import hermes 4 | 5 | from .__version__ import __version__ -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/__version__.py: -------------------------------------------------------------------------------- 1 | __title__ = 'hermes_python' 2 | __version__ = '0.8.1' 3 | __description__ = 'Python bindings for Snips Hermes Protocol' 4 | __url__ = { 5 | 'Documentation': 'https://hermespython.readthedocs.io/en/latest/', 6 | 'Source': 'https://github.com/snipsco/hermes-protocol/tree/develop/platforms/hermes-python', 7 | 'Tracker': 'https://github.com/snipsco/hermes-protocol/issues', 8 | } 9 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/hermes_python/api/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/api/ffi/__init__.py: -------------------------------------------------------------------------------- 1 | from ctypes import POINTER, c_char_p, byref 2 | from ...ffi.ontology import CProtocolHandler, CMqttOptions 3 | from ...ffi.utils import hermes_protocol_handler_new_mqtt_with_options, hermes_destroy_mqtt_protocol_handler 4 | from ...ffi import utils, lib 5 | 6 | from .dialogue import DialogueFFI 7 | from .feedback import SoundFeedBackFFI 8 | from .injection import InjectionFFI 9 | from .tts import TtsFFI 10 | 11 | 12 | class FFI(object): 13 | def __init__(self, use_json_api=True, rust_logs_enabled=False): 14 | self.use_json_api = use_json_api 15 | self.rust_logs_enabled = rust_logs_enabled 16 | 17 | # API Subsets 18 | self.dialogue = DialogueFFI(use_json_api) 19 | self.sound_feedback = SoundFeedBackFFI(use_json_api) 20 | self.injection = InjectionFFI(use_json_api) 21 | self.tts = TtsFFI(use_json_api) 22 | 23 | self._protocol_handler = POINTER(CProtocolHandler)() 24 | 25 | def establish_connection(self, mqtt_options): 26 | c_mqtt_options = CMqttOptions.from_repr(mqtt_options) 27 | 28 | hermes_protocol_handler_new_mqtt_with_options(byref(self._protocol_handler), byref(c_mqtt_options)) 29 | self.initialize_facades() 30 | 31 | if self.rust_logs_enabled: 32 | lib.hermes_enable_debug_logs() 33 | 34 | def initialize_facades(self): 35 | self.dialogue.initialize_facade(self._protocol_handler) 36 | self.sound_feedback.initialize_facade(self._protocol_handler) 37 | self.injection.initialize_facade(self._protocol_handler) 38 | self.tts.initialize_facade(self._protocol_handler) 39 | 40 | def release_facades(self): 41 | self.dialogue.release_facade() 42 | self.sound_feedback.release_facade() 43 | self.injection.release_facade() 44 | self.tts.release_facade() 45 | 46 | def release_connection(self): 47 | hermes_destroy_mqtt_protocol_handler(self._protocol_handler) 48 | self._protocol_handler = POINTER(CProtocolHandler)() 49 | self.release_facades() 50 | 51 | def _call_foreign_function(self, foreign_function_name, function_argument): 52 | if self.use_json_api: 53 | foreign_function_name = foreign_function_name + "_json" 54 | a_json_string = str(function_argument) # function_argument should be a dict. 55 | ptr_to_foreign_function_argument = c_char_p(a_json_string.encode('utf-8')) 56 | else: 57 | function_argument = function_argument.into_c_repr() 58 | ptr_to_foreign_function_argument = byref(function_argument) 59 | 60 | getattr(utils, foreign_function_name)( 61 | self._facade, 62 | ptr_to_foreign_function_argument 63 | ) 64 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/api/ffi/feedback.py: -------------------------------------------------------------------------------- 1 | from ctypes import POINTER, c_char_p, byref 2 | 3 | from ...ffi import utils 4 | from ...ffi.ontology.facades import CSoundFeedbackFacade 5 | from ...ffi.utils import hermes_protocol_handler_sound_feedback_facade, hermes_drop_sound_feedback_facade 6 | 7 | 8 | class SoundFeedBackFFI(object): 9 | def __init__(self, use_json_api=True): 10 | self.use_json_api = use_json_api 11 | self._facade = POINTER(CSoundFeedbackFacade)() 12 | 13 | def initialize_facade(self, protocol_handler): 14 | hermes_protocol_handler_sound_feedback_facade(protocol_handler, byref(self._facade)) 15 | 16 | def release_facade(self): 17 | hermes_drop_sound_feedback_facade(self._facade) 18 | self._facade = POINTER(CSoundFeedbackFacade)() 19 | 20 | def _call_foreign_function(self, foreign_function_name, function_argument): 21 | if self.use_json_api: 22 | foreign_function_name = foreign_function_name + "_json" 23 | a_json_string = str(function_argument) # function_argument should be a dict. 24 | ptr_to_foreign_function_argument = c_char_p(a_json_string.encode('utf-8')) 25 | else: 26 | function_argument = function_argument.into_c_repr() 27 | ptr_to_foreign_function_argument = byref(function_argument) 28 | 29 | getattr(utils, foreign_function_name)( 30 | self._facade, 31 | ptr_to_foreign_function_argument 32 | ) 33 | 34 | def publish_toggle_on(self, message): 35 | self._call_foreign_function( 36 | 'hermes_sound_feedback_publish_toggle_on', 37 | message 38 | ) 39 | return self 40 | 41 | def publish_toggle_off(self, message): 42 | self._call_foreign_function( 43 | 'hermes_sound_feedback_publish_toggle_off', 44 | message 45 | ) 46 | return self 47 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/api/ffi/tts.py: -------------------------------------------------------------------------------- 1 | from ctypes import POINTER, c_char_p, byref 2 | 3 | from ...ffi import utils 4 | from ...ffi.ontology.facades import CTtsFacade 5 | from ...ffi.utils import hermes_protocol_handler_tts_facade, hermes_drop_tts_facade 6 | 7 | 8 | class TtsFFI(object): 9 | def __init__(self, use_json_api=True): 10 | self.use_json_api = use_json_api 11 | self._facade = POINTER(CTtsFacade)() 12 | 13 | def initialize_facade(self, protocol_handler): 14 | hermes_protocol_handler_tts_facade(protocol_handler, byref(self._facade)) 15 | 16 | def release_facade(self): 17 | hermes_drop_tts_facade(self._facade) 18 | self._facade = POINTER(CTtsFacade)() 19 | 20 | def _call_foreign_function(self, foreign_function_name, function_argument): 21 | if self.use_json_api: 22 | foreign_function_name = foreign_function_name + "_json" 23 | a_json_string = str(function_argument) # function_argument should be a dict. 24 | ptr_to_foreign_function_argument = c_char_p(a_json_string.encode('utf-8')) 25 | else: 26 | function_argument = function_argument.into_c_repr() 27 | ptr_to_foreign_function_argument = byref(function_argument) 28 | 29 | getattr(utils, foreign_function_name)( 30 | self._facade, 31 | ptr_to_foreign_function_argument 32 | ) 33 | 34 | def publish_register_sound(self, message): 35 | self._call_foreign_function( 36 | 'hermes_tts_publish_register_sound', 37 | message 38 | ) 39 | return self 40 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import unicode_literals 3 | 4 | import os 5 | from glob import glob 6 | from ctypes import cdll 7 | 8 | 9 | class MockedLib(object): # This class is here in case you need to mock calls to the actual library. 10 | def __getattr__(self, item): 11 | return MockedLib() 12 | 13 | def __call__(self, *args, **kwargs): 14 | raise LibException("Trying to call mocked FFI library") 15 | 16 | 17 | class LibException(Exception): 18 | pass 19 | 20 | 21 | DYLIB_NAME = "libhermes_mqtt_ffi.*" 22 | DYLIB_DIR = os.path.join(os.path.dirname(__file__), "../dylib") 23 | DYLIB_PATHS = glob(os.path.join(DYLIB_DIR, DYLIB_NAME)) 24 | 25 | lib = cdll.LoadLibrary(DYLIB_PATHS[0]) if len(DYLIB_PATHS) > 0 else MockedLib() 26 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/asr.py: -------------------------------------------------------------------------------- 1 | from ctypes import Structure, c_float, c_int32, c_char_p, POINTER, c_int 2 | 3 | class CAsrDecodingDuration(Structure): 4 | _fields_ = [ 5 | ("start", c_float), 6 | ("end", c_float) 7 | ] 8 | 9 | @classmethod 10 | def from_repr(cls, repr): 11 | return cls(repr.start, repr.end) 12 | 13 | 14 | class CAsrToken(Structure): 15 | _fields_ = [ 16 | ("value", c_char_p), 17 | ("confidence", c_float), 18 | ("range_start", c_int32), 19 | ("range_end", c_int32), 20 | ("time", CAsrDecodingDuration) 21 | ] 22 | 23 | @classmethod 24 | def from_repr(cls, repr): 25 | value = repr.value.encode('utf-8') 26 | confidence = c_float(repr.confidence) 27 | range_start = c_int32(repr.range_start) 28 | range_end = c_int32(repr.range_end) 29 | time = CAsrDecodingDuration.from_repr(repr.time) 30 | 31 | return cls(value, confidence, range_start, range_end, time) 32 | 33 | 34 | class CAsrTokenArray(Structure): 35 | _fields_ = [ 36 | ("entries", POINTER(POINTER(CAsrToken))), 37 | ("count", c_int) 38 | ] 39 | 40 | @classmethod 41 | def from_repr(cls, repr): 42 | # type: (List[AsrToken]) -> CAsrTokenArray 43 | 44 | c_asr_tokens = [CAsrToken.from_repr(asr_token) for asr_token in repr] 45 | c_asr_tokens_pointers = [POINTER(CAsrToken)(c_asr_token) for c_asr_token in c_asr_tokens] 46 | 47 | entries = (POINTER(CAsrToken) * len(c_asr_tokens_pointers))(*c_asr_tokens_pointers) 48 | count = c_int(len(c_asr_tokens)) 49 | 50 | return cls(entries, count) 51 | 52 | class CAsrTokenDoubleArray(Structure): 53 | _fields_ = [ 54 | ("entries", POINTER(POINTER(CAsrTokenArray))), 55 | ("count", c_int) 56 | ] 57 | 58 | @classmethod 59 | def from_repr(cls, repr): 60 | # type: (List[List[AsrToken]]) -> CAsrTokenDoubleArray 61 | c_asr_token_arrays = [CAsrTokenArray.from_repr(asr_token_array) for asr_token_array in repr] 62 | c_asr_token_arrays_pointers = [POINTER(CAsrTokenArray)(c_asr_token_array) for c_asr_token_array in c_asr_token_arrays] 63 | 64 | entries = (POINTER(CAsrTokenArray) * len(c_asr_token_arrays))(*c_asr_token_arrays_pointers) 65 | count = c_int(len(c_asr_token_arrays)) 66 | 67 | return cls(entries, count) -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/facades.py: -------------------------------------------------------------------------------- 1 | from ctypes import Structure, c_void_p 2 | 3 | 4 | class CTtsFacade(Structure): 5 | _fields_ = [("facade", c_void_p)] 6 | 7 | 8 | class CDialogueFacade(Structure): 9 | _fields_ = [("facade", c_void_p)] 10 | 11 | 12 | class CSoundFeedbackFacade(Structure): 13 | _fields_ = [("facade", c_void_p)] 14 | 15 | 16 | class CInjectionFacade(Structure): 17 | _fields_ = [("facade", c_void_p)] 18 | 19 | 20 | class CTtsFacade(Structure): 21 | _fields_ = [("facade", c_void_p)] 22 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/feedback.py: -------------------------------------------------------------------------------- 1 | from ctypes import Structure, c_char_p 2 | 3 | 4 | class CSiteMessage(Structure): 5 | _fields_ = [("site_id", c_char_p), 6 | ("session_id", c_char_p)] 7 | 8 | @classmethod 9 | def build(cls, site_id, session_id=None): 10 | site_id = site_id.encode('utf-8') 11 | session_id = session_id.encode('utf-8') if session_id else None 12 | 13 | return cls(site_id, session_id) 14 | 15 | @classmethod 16 | def from_repr(cls, repr): 17 | site_id = repr.site_id 18 | session_id = repr.session_id 19 | 20 | return cls.build(site_id, session_id) 21 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/injection.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import unicode_literals 4 | 5 | from ctypes import Structure, c_char_p, POINTER, c_int, pointer, cast 6 | from enum import IntEnum 7 | 8 | from ..ontology import CMapStringToStringArray 9 | 10 | 11 | class InjectionKind(IntEnum): 12 | ADD = 1 13 | ADD_FROM_VANILLA = 2 14 | 15 | 16 | class CInjectionStatusMessage(Structure): 17 | _fields_ = [("last_injection_date", c_char_p)] 18 | 19 | 20 | class CInjectionRequestOperation(Structure): 21 | _fields_ = [("values", POINTER(CMapStringToStringArray)), 22 | ("kind", c_int)] # kind is an enum 23 | 24 | @classmethod 25 | def from_repr(cls, repr): 26 | kind = repr.kind 27 | values = pointer(CMapStringToStringArray.from_repr(repr.values)) 28 | return cls(values, kind) 29 | 30 | 31 | class CInjectionRequestOperations(Structure): 32 | _fields_ = [("operations", POINTER(POINTER(CInjectionRequestOperation))), 33 | ("count", c_int)] 34 | 35 | @classmethod 36 | def from_repr(cls, repr): 37 | operations = [pointer(CInjectionRequestOperation.from_repr(operation)) for operation in repr] 38 | count = len(repr) 39 | array_of_operations = (POINTER(CInjectionRequestOperation) * count)() 40 | array_of_operations[:] = operations 41 | 42 | return cls(array_of_operations, count) 43 | 44 | 45 | class CInjectionRequestMessage(Structure): 46 | _fields_ = [("operations", POINTER(CInjectionRequestOperations)), 47 | ("lexicon", POINTER(CMapStringToStringArray)), 48 | ("cross_language", c_char_p), # Nullable 49 | ("id", c_char_p)] # Nullable 50 | 51 | @classmethod 52 | def from_repr(cls, repr): 53 | operations = pointer(CInjectionRequestOperations.from_repr(repr.operations)) 54 | lexicon = pointer(CMapStringToStringArray.from_repr(repr.lexicon)) 55 | cross_language = repr.cross_language.encode('utf-8') if repr.cross_language else None 56 | id = repr.id.encode('utf-8') if repr.id else None 57 | return cls(operations, lexicon, cross_language, id) 58 | 59 | 60 | class CInjectionCompleteMessage(Structure): 61 | _fields_ = [("request_id", c_char_p)] 62 | 63 | @classmethod 64 | def from_repr(cls, repr): 65 | request_id = repr.request_id.encode('utf-8') 66 | return cls(request_id) 67 | 68 | 69 | class CInjectionResetRequestMessage(Structure): 70 | _fields_ = [("request_id", c_char_p)] 71 | 72 | @classmethod 73 | def from_repr(cls, repr): 74 | request_id = repr.request_id.encode('utf-8') 75 | return cls(request_id) 76 | 77 | 78 | class CInjectionResetCompleteMessage(Structure): 79 | _fields_ = [("request_id", c_char_p)] 80 | 81 | @classmethod 82 | def from_repr(cls, repr): 83 | request_id = repr.request_id.encode('utf-8') 84 | return cls(request_id) -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/nlu.py: -------------------------------------------------------------------------------- 1 | from ctypes import c_char_p, c_int, c_int32, c_float, POINTER, Structure, pointer, cast 2 | 3 | from ..ontology import CSlot, CSlotValue, CSlotValueArray 4 | 5 | 6 | class CNluSlot(Structure): 7 | _fields_ = [ 8 | ("nlu_slot", POINTER(CSlot)) 9 | ] 10 | 11 | @classmethod 12 | def from_repr(cls, repr): 13 | # type: (NluSlot) -> CNluSlot 14 | 15 | c_slot_value_p = POINTER(CSlotValue)(CSlotValue.from_repr(repr.slot_value)) 16 | alternatives_p = POINTER(CSlotValueArray)(CSlotValueArray.from_repr(repr.alternatives)) 17 | 18 | c_slot = CSlot.build(c_slot_value_p, 19 | alternatives_p, 20 | repr.raw_value, 21 | repr.entity, 22 | repr.slot_name, 23 | repr.range_start, 24 | repr.range_end, 25 | repr.confidence_score) 26 | 27 | return cls(POINTER(CSlot)(c_slot)) 28 | 29 | @classmethod 30 | def build(cls, nlu_slot): 31 | slot_p = POINTER(CSlot)(nlu_slot) 32 | return cls(slot_p) 33 | 34 | 35 | class CNluIntentClassifierResult(Structure): 36 | _fields_ = [("intent_name", c_char_p), 37 | ("confidence_score", c_float)] 38 | 39 | @classmethod 40 | def build(cls, intent_name, confidence_score): 41 | intent_name = intent_name.encode('utf-8') 42 | confidence_score = float(confidence_score) 43 | return cls(intent_name, confidence_score) 44 | 45 | @classmethod 46 | def from_repr(cls, repr): 47 | return cls.build(repr.intent_name, repr.confidence_score) 48 | 49 | 50 | class CNluSlotArray(Structure): 51 | _fields_ = [ 52 | ("entries", POINTER(POINTER(CNluSlot))), 53 | ("count", c_int) 54 | ] 55 | 56 | @classmethod 57 | def from_repr(cls, repr): 58 | # type: (SlotMap) -> CNluSlotArray 59 | 60 | 61 | # We flatten all the slots into a list 62 | nlu_slots = [nlu_slot for slot_name, nlu_slot_array in repr.items() for nlu_slot in nlu_slot_array] 63 | 64 | c_nlu_slots = [CNluSlot.from_repr(nlu_slot) for nlu_slot in nlu_slots] 65 | c_nlu_slots_pointers = [POINTER(CNluSlot)(c_nlu_slot) for c_nlu_slot in c_nlu_slots] 66 | 67 | entries = (POINTER(CNluSlot) * len(c_nlu_slots_pointers))(*c_nlu_slots_pointers) 68 | count = c_int(len(nlu_slots)) 69 | 70 | return cls(entries, count) 71 | 72 | 73 | class CNluIntentAlternative(Structure): 74 | _fields_ = [ 75 | ("intent_name", c_char_p), 76 | ("slots", POINTER(CNluSlotArray)), 77 | ("confidence_score", c_float) 78 | ] 79 | 80 | @classmethod 81 | def from_repr(cls, repr): 82 | intent_name = repr.intent_name.encode('utf-8') 83 | confidence_score = c_float(repr.confidence_score) 84 | c_slots = POINTER(CNluSlotArray)(CNluSlotArray.from_repr(repr.slots)) 85 | return cls(intent_name, c_slots, confidence_score) 86 | 87 | 88 | class CNluIntentAlternativeArray(Structure): 89 | _fields_ = [ 90 | ("entries", POINTER(POINTER(CNluIntentAlternative))), 91 | ("count", c_int) 92 | ] 93 | 94 | @classmethod 95 | def from_repr(cls, repr): 96 | c_nlu_intent_alternatives = [CNluIntentAlternative.from_repr(alt) for alt in repr] 97 | c_nlu_intent_alternatives_pointers = [POINTER(CNluIntentAlternative)(c_nlu_intent_alternative) for c_nlu_intent_alternative in c_nlu_intent_alternatives] 98 | 99 | entries = (POINTER(CNluIntentAlternative) * len(c_nlu_intent_alternatives))(*c_nlu_intent_alternatives_pointers) 100 | count = c_int(len(c_nlu_intent_alternatives_pointers)) 101 | 102 | return cls(entries, count) 103 | 104 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/ontology/tts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import unicode_literals 4 | 5 | from ctypes import Structure, POINTER, pointer, c_char_p, c_uint8, c_int 6 | 7 | 8 | class CRegisterSoundMessage(Structure): 9 | _fields_ = [("sound_id", c_char_p), 10 | ("wav_sound", POINTER(c_uint8)), 11 | ("wav_sound_len", c_int)] 12 | 13 | @classmethod 14 | def from_repr(cls, repr): 15 | sound_id = c_char_p(repr.sound_id.encode('utf-8')) 16 | 17 | wav_length = len(repr.wav_sound) 18 | wav_bytes = (c_uint8 * wav_length)() 19 | wav_bytes[:] = repr.wav_sound 20 | 21 | return cls(sound_id, wav_bytes, wav_length) 22 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ffi/wrappers.py: -------------------------------------------------------------------------------- 1 | from ..ffi import lib, LibException 2 | from ctypes import POINTER, c_char_p, string_at, CFUNCTYPE, c_void_p 3 | import json 4 | 5 | 6 | def wrap_library_call(lib_func): 7 | """ 8 | This helper function wrap ffi functions so that they raise an Exception when the error code is different than 0. 9 | """ 10 | def wrapped_library_call(*args, **kwargs): 11 | return_code = lib_func(*args, **kwargs) 12 | if return_code > 0: # An error occured 13 | empty_string = "".encode('utf-8') 14 | error_p = POINTER(c_char_p)(c_char_p(empty_string)) 15 | # Retrieve the last error and put it in the memory location error_p points to 16 | lib.hermes_get_last_error(error_p) 17 | error_cause = string_at(error_p.contents).decode('utf-8') 18 | raise LibException(error_cause) 19 | return return_code 20 | 21 | return wrapped_library_call 22 | 23 | 24 | def parse_json_string(ptr_to_utf_8_encoded_string): 25 | return json.loads(string_at(ptr_to_utf_8_encoded_string).decode('utf-8')) 26 | 27 | 28 | def ffi_function_callback_wrapper(use_json_api, hermes_client, target_handler_return_type, handler_function, 29 | handler_argument_type=None, target_handler_argument_type=None): 30 | # type: (bool, Hermes, Any, Any, Any, Any) -> CFUNCTYPE 31 | """ 32 | We need to provide the C library a handler function that will be called 33 | when the event the handler should handle is triggered. 34 | This handler has `target_handler_return_type`, and has arguments with type `target_handler_argument_type`. 35 | 36 | The goal of this function is to convert a handler written in python (`handler_function`) 37 | to a C handler with appropriate types. 38 | 39 | Let's go through the arguments: 40 | :param use_json_api: A flag, if activated, all arguments of handler callback will be python dictionaries. 41 | :param hermes_client: 42 | :param target_handler_return_type: The type to which the python handler return type will be converted to. 43 | :param target_handler_argument_type: Optional (not used if use_json_api is activated). The type to which the python handler arguments will be converted to. 44 | :param handler_function: a python function 45 | :param handler_argument_type: Optional (not used if use_json_api is activated). The type of the arguments the handler will be called with. 46 | :return: A C handler function that will be called when events it is registered to happens. 47 | 48 | """ 49 | if use_json_api: 50 | def convert_function_arguments(func): 51 | def convert_arguments_when_invoking_function(*args, **kwargs): 52 | parsed_args = (parse_json_string(arg) for arg in (args)) 53 | return func(hermes_client, *parsed_args) 54 | return convert_arguments_when_invoking_function 55 | return CFUNCTYPE(target_handler_return_type, c_char_p)( 56 | convert_function_arguments(handler_function)) 57 | else: 58 | def convert_function_arguments(func): 59 | def convert_arguments_when_invoking_function(*args, **kwargs): 60 | parsed_args = (handler_argument_type.from_c_repr(arg.contents) for arg in (args)) 61 | return func(hermes_client, *parsed_args) 62 | 63 | return convert_arguments_when_invoking_function 64 | return CFUNCTYPE(target_handler_return_type, POINTER(target_handler_argument_type))( 65 | convert_function_arguments(handler_function)) 66 | 67 | 68 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ontology/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from enum import IntEnum 3 | from hermes_python.ffi.ontology import SNIPS_HERMES_COMPONENT 4 | 5 | 6 | class HermesComponent(IntEnum): 7 | AUDIO_SERVER = 1 8 | HOTWORD = 2 9 | ASR = 3 10 | NLU = 4 11 | DIALOGUE = 5 12 | TTS = 6 13 | INJECTION = 7 14 | CLIENT_APP = 8 15 | 16 | @classmethod 17 | def from_c_repr(cls, c_repr): 18 | # type: (SNIPS_HERMES_COMPONENT) -> Optional[HermesComponent] 19 | if c_repr == SNIPS_HERMES_COMPONENT.SNIPS_HERMES_COMPONENT_NONE: 20 | return None 21 | else: 22 | return HermesComponent(c_repr) 23 | 24 | 25 | class MqttOptions(object): 26 | def __init__(self, 27 | broker_address="localhost:1883", 28 | username=None, password=None, 29 | tls_hostname=None, tls_ca_file=None, tls_ca_path=None, tls_client_key=None, tls_client_cert=None, 30 | tls_disable_root_store=False): 31 | """ 32 | :param broker_address: Address of the MQTT broker in the form 'ip:port' 33 | :param username: Username to use on the broker. Nullable 34 | :param password: Password to use on the broker. Nullable 35 | :param tls_hostname: Hostname to use for the TLS configuration. Nullable, setting a value enables TLS 36 | :param tls_ca_file: CA files to use if TLS is enabled. Nullable 37 | :param tls_ca_path: CA path to use if TLS is enabled. Nullable 38 | :param tls_client_key: Client key to use if TLS is enabled. Nullable 39 | :param tls_client_cert: Client cert to use if TLS is enabled. Nullable 40 | :param tls_disable_root_store: Boolean indicating if the root store should be disabled if TLS is enabled. 41 | """ 42 | self.broker_address = broker_address 43 | 44 | self.username = username 45 | self.password = password 46 | 47 | self.tls_hostname = tls_hostname 48 | self.tls_ca_file = tls_ca_file 49 | self.tls_ca_path = tls_ca_path 50 | self.tls_client_key = tls_client_key 51 | self.tls_client_cert = tls_client_cert 52 | self.tls_disable_root_store = tls_disable_root_store 53 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ontology/asr/__init__.py: -------------------------------------------------------------------------------- 1 | class AsrDecodingDuration(object): 2 | def __init__(self, start, end): 3 | # type: (float, float) -> None 4 | self.start = start 5 | self.end = end 6 | 7 | def __eq__(self, other): 8 | return self.__dict__ == other.__dict__ 9 | 10 | @classmethod 11 | def from_c_repr(cls, c_repr): 12 | return cls(float(c_repr.start), float(c_repr.end)) 13 | 14 | 15 | class AsrToken(object): 16 | def __init__(self, value, confidence, range_start, range_end, time): 17 | # type: (str, float, int, int, AsrDecodingDuration) -> None 18 | self.value = value 19 | self.confidence = confidence 20 | self.range_start = range_start 21 | self.range_end = range_end 22 | self.time = time 23 | 24 | def __eq__(self, other): 25 | return self.__dict__ == other.__dict__ 26 | 27 | @classmethod 28 | def from_c_repr(cls, c_repr): 29 | value = c_repr.value.decode('utf-8') 30 | confidence = c_repr.confidence 31 | range_start = int(c_repr.range_start) 32 | range_end = int(c_repr.range_end) 33 | time = AsrDecodingDuration.from_c_repr(c_repr.time) 34 | 35 | return cls(value, confidence, range_start, range_end, time) -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ontology/feedback/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import absolute_import 3 | from __future__ import unicode_literals 4 | 5 | from typing import Optional, Text 6 | from hermes_python.ffi.ontology.feedback import CSiteMessage 7 | 8 | 9 | class SiteMessage(object): 10 | def __init__(self, site_id, session_id=None): 11 | # type: (Text, Optional[Text]) -> None 12 | self.site_id = site_id 13 | self.session_id = session_id 14 | 15 | def into_c_repr(self): 16 | return CSiteMessage.build(self.site_id, self.session_id) 17 | 18 | @classmethod 19 | def from_c_repr(cls, c_repr): 20 | site_id = c_repr.site_id.decode('utf-8') 21 | session_id = c_repr.session_id.decode('utf-8') if c_repr.session_id else None 22 | return cls(site_id, session_id) 23 | -------------------------------------------------------------------------------- /platforms/hermes-python/hermes_python/ontology/tts/__init__.py: -------------------------------------------------------------------------------- 1 | from typing import Text, ByteString 2 | from hermes_python.ffi.ontology.tts import CRegisterSoundMessage 3 | 4 | class RegisterSoundMessage(object): 5 | def __init__(self, sound_id, wav_sound): 6 | # type:(Text, ByteString) -> None 7 | self.sound_id = sound_id 8 | self.wav_sound = wav_sound 9 | 10 | def __eq__(self, other): 11 | return self.__dict__ == other.__dict__ 12 | 13 | @classmethod 14 | def from_c_repr(cls, c_repr): 15 | sound_id = c_repr.sound_id.decode('utf-8') 16 | nb = c_repr.wav_sound_len 17 | wav_sound = bytearray([c_repr.wav_sound[i] for i in range(nb)]) 18 | return cls(sound_id, wav_sound) 19 | 20 | def into_c_repr(self): 21 | return CRegisterSoundMessage.from_repr(self) 22 | 23 | -------------------------------------------------------------------------------- /platforms/hermes-python/requirements/documentation.txt: -------------------------------------------------------------------------------- 1 | sphinxcontrib-restbuilder==0.2 2 | sphinx_rtd_theme 3 | -------------------------------------------------------------------------------- /platforms/hermes-python/requirements/install.txt: -------------------------------------------------------------------------------- 1 | six 2 | future 3 | typing 4 | enum34 5 | -------------------------------------------------------------------------------- /platforms/hermes-python/requirements/lint.txt: -------------------------------------------------------------------------------- 1 | mypy==0.670 2 | -------------------------------------------------------------------------------- /platforms/hermes-python/requirements/tests.txt: -------------------------------------------------------------------------------- 1 | -r install.txt 2 | mock 3 | pytest 4 | paho-mqtt 5 | -------------------------------------------------------------------------------- /platforms/hermes-python/requirements/upload.txt: -------------------------------------------------------------------------------- 1 | twine 2 | -------------------------------------------------------------------------------- /platforms/hermes-python/run-tests-jenkins.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | if ! [[ -z "$(ls -A hermes_python/dylib)" ]]; then 5 | echo "hermes_python/dylib/ should be empty. Aborting!" && exit 1 6 | fi 7 | 8 | if ! [[ -z "$(ls -A tests/roundtrip/debug)" ]]; then 9 | echo "tests/roundtrip/debug/ should be empty. Aborting!" && exit 1 10 | fi 11 | 12 | 13 | mkdir -p hermes_python/dylib 14 | mkdir -p tests/roundtrip/debug 15 | 16 | if [[ $(uname) == "Linux" ]]; then 17 | cp ../../target/release/libhermes_ffi_test.so tests/roundtrip/debug 18 | elif [[ $(uname) == "Darwin" ]]; then 19 | cp ../../target/release/libhermes_ffi_test.dylib tests/roundtrip/debug 20 | fi 21 | 22 | virtualenv --python=python2.7 env27 23 | source env27/bin/activate 24 | 25 | python setup.py bdist_wheel 26 | pip install -r requirements/tests.txt 27 | py.test 28 | 29 | virtualenv --python=python3 env3 30 | source env3/bin/activate 31 | pip install -r requirements/lint.txt 32 | mypy --py2 --follow-imports=skip -p hermes_python.ontology 33 | 34 | 35 | rm -rf env27 env3 36 | rm -rf hermes_python/dylib 37 | rm -rf tests/roundtrip/debug 38 | -------------------------------------------------------------------------------- /platforms/hermes-python/run-tests-travis.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | if ! [[ -z "$(ls -A hermes_python/dylib)" ]]; then 5 | echo "hermes_python/dylib should be empty. Aborting!" && exit 1 6 | fi 7 | 8 | mkdir -p hermes_python/dylib 9 | 10 | if ! [[ -z "$(ls -A tests/roundtrip/debug)" ]]; then 11 | echo "tests/roundtrip/debug should be empty. Aborting!" && exit 1 12 | fi 13 | 14 | mkdir -p tests/roundtrip/debug 15 | 16 | # The artifacts were generated in a previous stage of the build 17 | # Let's copy them to appropriate locations 18 | 19 | if [[ $(uname) == "Linux" ]]; then 20 | cp ../../target/debug/libhermes_ffi_test.so tests/roundtrip/debug 21 | elif [[ $(uname) == "Darwin" ]]; then 22 | cp ../../target/debug/libhermes_ffi_test.dylib tests/roundtrip/debug 23 | fi 24 | 25 | virtualenv --python=python2.7 env27 26 | source env27/bin/activate 27 | 28 | if [[ $(uname) == "Linux" ]]; then 29 | python setup.py bdist_wheel --include-extension=../../target/debug/libhermes_mqtt_ffi.so 30 | elif [[ $(uname) == "Darwin" ]]; then 31 | python setup.py bdist_wheel --include-extension=../../target/debug/libhermes_mqtt_ffi.dylib 32 | fi 33 | 34 | pip install -r requirements/tests.txt 35 | py.test 36 | 37 | virtualenv --python=python3 env3 38 | source env3/bin/activate 39 | pip install -r requirements/lint.txt 40 | mypy --py2 --follow-imports=skip -p hermes_python.ontology 41 | 42 | -------------------------------------------------------------------------------- /platforms/hermes-python/test.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/test.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/api/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/ffi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/api/ffi/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/ffi/test_ffi.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import mock 3 | 4 | from hermes_python.api.ffi import FFI 5 | from hermes_python.ontology import MqttOptions 6 | 7 | HOST = "localhost" 8 | 9 | 10 | def test_initialization(): 11 | h = FFI() 12 | assert 0 == len(h.dialogue._c_callback_subscribe_intent) 13 | 14 | 15 | def test_initialization_use_json_api_by_default(): 16 | h = FFI() 17 | assert h.use_json_api 18 | 19 | 20 | @mock.patch("hermes_python.api.ffi.hermes_protocol_handler_new_mqtt_with_options") 21 | def test_establish_connection_calls_api_subsets(hermes_protocol_handler_new_mqtt): 22 | ffi = FFI() 23 | mqtt_opts = MqttOptions() 24 | 25 | # Here, you have to mock every API subset of Hermes Protocol 26 | mocked_dialogue_ffi = mock.Mock() 27 | mocked_sound_feedback_ffi = mock.Mock() 28 | mocked_injection_ffi = mock.Mock() 29 | mocked_tts_ffi = mock.Mock() 30 | 31 | ffi.dialogue = mocked_dialogue_ffi 32 | ffi.sound_feedback = mocked_sound_feedback_ffi 33 | ffi.injection = mocked_injection_ffi 34 | ffi.tts = mocked_tts_ffi 35 | 36 | 37 | ffi.establish_connection(mqtt_opts) 38 | 39 | hermes_protocol_handler_new_mqtt.assert_called_once() 40 | ffi.dialogue.initialize_facade.assert_called_once() 41 | ffi.sound_feedback.initialize_facade.assert_called_once() 42 | ffi.injection.initialize_facade.assert_called_once() 43 | ffi.tts.initialize_facade.assert_called_once() 44 | 45 | @mock.patch("hermes_python.api.ffi.hermes_destroy_mqtt_protocol_handler") 46 | def test_release_connection_calls_api_subsets(hermes_destroy_mqtt_protocol_handler): 47 | ffi = FFI() 48 | 49 | # Here, you have to mock every API subset of Hermes Protocol 50 | mocked_dialogue_ffi = mock.Mock() 51 | mocked_sound_feedback_ffi = mock.Mock() 52 | ffi.dialogue = mocked_dialogue_ffi 53 | ffi.sound_feedback = mocked_sound_feedback_ffi 54 | 55 | ffi.release_connection() 56 | 57 | ffi.dialogue.release_facade.assert_called_once() 58 | ffi.sound_feedback.release_facade.assert_called_once() 59 | 60 | hermes_destroy_mqtt_protocol_handler.assert_called_once() 61 | 62 | 63 | @mock.patch("hermes_python.api.ffi.hermes_protocol_handler_new_mqtt_with_options") 64 | @mock.patch("hermes_python.api.ffi.hermes_destroy_mqtt_protocol_handler") 65 | @mock.patch("hermes_python.api.ffi.DialogueFFI") 66 | @mock.patch("hermes_python.api.ffi.SoundFeedBackFFI") 67 | @mock.patch("hermes_python.api.ffi.InjectionFFI") 68 | @mock.patch("hermes_python.api.ffi.TtsFFI") 69 | class ConnectionTest(object): 70 | def test_establishing_successful_connection(self, 71 | ttsFFI, 72 | injectionFFI, 73 | soundfeedbackFFI, 74 | dialogueFFI, 75 | hermes_destroy_mqtt_protocol_handler, 76 | hermes_protocol_handler_new_mqtt): 77 | ffi = FFI() 78 | mqtt_opts = MqttOptions() 79 | ffi.establish_connection(mqtt_opts) 80 | 81 | hermes_protocol_handler_new_mqtt.assert_called_once() 82 | ffi.dialogue.initialize_facade.assert_called_once() 83 | ffi.sound_feedback.initialize_facade.assert_called_once() 84 | hermes_destroy_mqtt_protocol_handler.assert_called_once() 85 | 86 | def test_release_connection_sucessful(self, 87 | ttsFFI, 88 | injectionFFI, 89 | soundfeedbackFFI, 90 | dialogueFFI, 91 | hermes_destroy_mqtt_protocol_handler, 92 | hermes_protocol_handler_new_mqtt): 93 | ffi = FFI() 94 | mqtt_opts = MqttOptions() 95 | 96 | ffi.establish_connection(mqtt_opts) 97 | ffi.release_connection() 98 | 99 | hermes_protocol_handler_new_mqtt.assert_called_once() 100 | 101 | ffi.dialogue.initialize_facade.assert_called_once() 102 | ffi.dialogue.release_facade.assert_called_once() 103 | ffi.sound_feedback.initialize_facade.assert_called_once() 104 | ffi.sound_feedback.release_facade.assert_called_once() 105 | 106 | hermes_destroy_mqtt_protocol_handler.assert_called_once() 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/ffi/test_injection.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import unicode_literals 3 | import mock 4 | import pytest 5 | 6 | from hermes_python.api.ffi.injection import InjectionFFI 7 | from hermes_python.ontology.injection import InjectionRequestMessage, AddInjectionRequest, InjectionResetRequestMessage, InjectionResetCompleteMessage, InjectionCompleteMessage 8 | 9 | @mock.patch("hermes_python.api.ffi.injection.utils") 10 | class TestInjectionFFICallsUnderlyingFFIFunctions: 11 | def test_subscribe_injection_status_callback(self, ffi_utils): 12 | def injection_status_callback(_): 13 | pass 14 | 15 | injection_ffi = InjectionFFI(use_json_api=False) 16 | hermes_client = mock.Mock() 17 | 18 | injection_ffi.register_subscribe_injection_status(injection_status_callback, hermes_client) 19 | 20 | assert len(injection_ffi._c_callback_subscribe_injection_status) == 1 21 | ffi_utils.hermes_injection_subscribe_injection_status.assert_called_once() 22 | 23 | def test_publish_injection_request(self, ffi_utils): 24 | injection_ffi = InjectionFFI(use_json_api=False) 25 | 26 | input_request_1 = AddInjectionRequest({"key": ["hello", "world", "✨"]}) 27 | input_request_2 = AddInjectionRequest({"key": ["hello", "moon", "👽"]}) 28 | operations = [input_request_1, input_request_2] 29 | lexicon = {"key": ["i", "am a", "lexicon ⚠️"]} 30 | 31 | message = InjectionRequestMessage(operations, lexicon) 32 | injection_ffi.publish_injection_request(message) 33 | 34 | ffi_utils.hermes_injection_publish_injection_request.assert_called_once() 35 | 36 | def test_publish_injection_request_status(self, ffi_utils): 37 | injection_ffi = InjectionFFI(use_json_api=False) 38 | injection_ffi.publish_injection_status_request() 39 | ffi_utils.hermes_injection_publish_injection_status_request.assert_called_once() 40 | 41 | def test_subscribe_injection_complete_callback(self, ffi_utils): 42 | def injection_complete_callback(): 43 | pass 44 | 45 | injection_ffi = InjectionFFI(use_json_api=False) 46 | hermes_client = mock.Mock() 47 | 48 | injection_ffi.register_subscribe_injection_complete(injection_complete_callback(), hermes_client) 49 | 50 | assert len(injection_ffi._c_callback_subscribe_injection_complete) == 1 51 | ffi_utils.hermes_injection_subscribe_injection_complete.assert_called_once() 52 | 53 | def test_subscribe_injection_reset_complete_callback(self, ffi_utils): 54 | def injection_complete_callback(): 55 | pass 56 | 57 | injection_ffi = InjectionFFI(use_json_api=False) 58 | hermes_client = mock.Mock() 59 | 60 | injection_ffi.register_subscribe_injection_reset_complete(injection_complete_callback(), hermes_client) 61 | 62 | assert len(injection_ffi._c_callback_subscribe_injection_reset_complete) == 1 63 | ffi_utils.hermes_injection_subscribe_injection_reset_complete.assert_called_once() 64 | 65 | def test_publish_injection_reset_request(self, ffi_utils): 66 | injection_ffi = InjectionFFI(use_json_api=False) 67 | 68 | message = InjectionResetRequestMessage("request_id") 69 | 70 | injection_ffi.publish_injection_reset_request(message) 71 | ffi_utils.hermes_injection_publish_injection_reset_request.assert_called_once() 72 | 73 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/ffi/test_sound_feedback.py: -------------------------------------------------------------------------------- 1 | import mock 2 | from hermes_python.api.ffi.feedback import SoundFeedBackFFI 3 | from hermes_python.ontology.feedback import SiteMessage 4 | 5 | 6 | @mock.patch("hermes_python.api.ffi.feedback.utils") 7 | def test_publish_toggle_on(ffi_utils): 8 | ffi = SoundFeedBackFFI(use_json_api=False) 9 | site_message = SiteMessage("default") 10 | ffi.publish_toggle_on(site_message) 11 | ffi_utils.hermes_sound_feedback_publish_toggle_on.assert_called_once() 12 | 13 | 14 | @mock.patch("hermes_python.api.ffi.feedback.utils") 15 | def test_publish_toggle_off(ffi_utils): 16 | ffi = SoundFeedBackFFI(use_json_api=False) 17 | site_message = SiteMessage("default") 18 | ffi.publish_toggle_off(site_message) 19 | ffi_utils.hermes_sound_feedback_publish_toggle_off.assert_called_once() 20 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/api/ffi/test_tts.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import unicode_literals 3 | import mock 4 | 5 | from hermes_python.api.ffi.tts import TtsFFI 6 | from hermes_python.ontology.tts import RegisterSoundMessage 7 | 8 | 9 | @mock.patch("hermes_python.api.ffi.tts.utils") 10 | class TestInjectionFFICallsUnderlyingFFIFunctions: 11 | def test_publish_register_sound(self, ffi_utils): 12 | tts_ffi = TtsFFI(use_json_api=False) 13 | 14 | message = RegisterSoundMessage("sound_id", b'') 15 | 16 | tts_ffi.publish_register_sound(message) 17 | ffi_utils.hermes_tts_publish_register_sound.assert_called_once() 18 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/data/test.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/data/test.wav -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ffi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/ffi/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ffi/test_ontology.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import absolute_import 3 | from __future__ import unicode_literals 4 | import pytest 5 | import os 6 | 7 | from hermes_python.ffi.ontology import CMapStringToStringArray, CMapStringToStringArrayEntry, CStringArray 8 | from hermes_python.ffi.ontology.injection import CInjectionRequestMessage 9 | from hermes_python.ontology.injection import AddInjectionRequest, InjectionRequestMessage 10 | from hermes_python.ffi.ontology.tts import CRegisterSoundMessage 11 | from hermes_python.ontology.tts import RegisterSoundMessage 12 | 13 | 14 | @pytest.fixture(scope="package") 15 | def wav_data(): 16 | with open(os.path.join(os.path.dirname(__file__), "../data/test.wav"), 'rb') as f: 17 | read_data = f.read() 18 | return bytearray(read_data) 19 | 20 | 21 | def test_serde_CStringArray(): 22 | input = ["i", "am", "groot", "🌱"] 23 | serialized_data = CStringArray.from_repr(input) 24 | deserialized_data = serialized_data.into_repr() 25 | 26 | assert input == deserialized_data 27 | 28 | 29 | def test_serde_CMapStringToStringArrayEntry(): 30 | input_data = ("key", ["hello", "world", "🌍"]) 31 | serialized_data = CMapStringToStringArrayEntry.from_repr(input_data) 32 | deserialized_data = serialized_data.into_repr() 33 | 34 | assert input_data[0] == deserialized_data[0] 35 | assert input_data[1] == deserialized_data[1] 36 | 37 | 38 | def test_serde_CMapStringToStringArray(): 39 | input_data = {'key1': ['hello', 'world', '🌍'], 'key2': ['hello', 'moon', '👽']} 40 | serialized_data = CMapStringToStringArray.from_repr(input_data) 41 | deserialized_data = serialized_data.into_repr() 42 | assert deserialized_data == input_data 43 | 44 | 45 | def test_serde_InjectionRequestMessage(): 46 | input_request_1 = AddInjectionRequest({"key": ["hello", "world", "✨"]}) 47 | input_request_2 = AddInjectionRequest({"key": ["hello", "moon", "👽"]}) 48 | operations = [input_request_1, input_request_2] 49 | lexicon = {"key": ["i", "am a", "lexicon ⚠️"]} 50 | 51 | request = InjectionRequestMessage(operations, lexicon) 52 | serialized = CInjectionRequestMessage.from_repr(request) 53 | deserialized = InjectionRequestMessage.from_c_repr(serialized) 54 | 55 | assert request.lexicon == deserialized.lexicon 56 | assert len(request.operations) == len(deserialized.operations) 57 | assert request.operations[0].values == deserialized.operations[0].values 58 | 59 | 60 | def test_serde_RegisterSoundMessage(wav_data): 61 | register_sound = RegisterSoundMessage("test", wav_data) 62 | 63 | serialized = CRegisterSoundMessage.from_repr(register_sound) 64 | deserialized = RegisterSoundMessage.from_c_repr(serialized) 65 | 66 | assert deserialized.wav_sound == register_sound.wav_sound 67 | assert deserialized == register_sound 68 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ffi/test_utils.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | from __future__ import unicode_literals 3 | import ctypes 4 | import pytest 5 | from hermes_python.ffi.utils import wrap_library_call 6 | from hermes_python.ffi.wrappers import parse_json_string 7 | 8 | SUCCESS_EXIT_CODE = 0 9 | ERROR_EXIT_CODE = 1 10 | 11 | def test_wrap_library_call_raises_expection_for_error_return_code(): 12 | def test_func(): 13 | return ERROR_EXIT_CODE 14 | 15 | with pytest.raises(Exception): 16 | wrap_library_call(test_func)() 17 | 18 | 19 | def test_wrap_library_call_doesnt_raises_expection_for_error_return_code(): 20 | def test_func(): 21 | return SUCCESS_EXIT_CODE 22 | 23 | assert wrap_library_call(test_func)() == SUCCESS_EXIT_CODE 24 | 25 | 26 | def test_parsing_of_json_string_deserialization(): 27 | a_string = '{"message": "my name is René 🤗"}' 28 | ptr_to_a_string = ctypes.c_char_p(a_string.encode('utf-8')) 29 | deserialized_string = parse_json_string(ptr_to_a_string) 30 | 31 | assert deserialized_string.get('message') == 'my name is René 🤗' 32 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/mqtt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/mqtt/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/mqtt/test_connection.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | import pytest 3 | import subprocess 4 | import time 5 | import mock 6 | from paho.mqtt.publish import single 7 | 8 | from hermes_python.hermes import Hermes 9 | 10 | 11 | @pytest.fixture(scope="module") 12 | def mqtt_server(): 13 | print("Starting MQTT Server") 14 | mqtt_server = subprocess.Popen("mosquitto") 15 | time.sleep(1) # Let's wait a bit before it's started 16 | yield mqtt_server 17 | print("Tearing down MQTT Server") 18 | mqtt_server.kill() 19 | 20 | 21 | class TestPublishingMessages(object): 22 | def test_publish_continue_session(self, mqtt_server): 23 | with Hermes("localhost:1883") as h: 24 | h.publish_continue_session("session_id", "text", [], None) 25 | h.publish_continue_session("session_id", "text", ["intent_name"], None) 26 | h.publish_continue_session("session_id", "text", ["intent_name"], "custom_data") 27 | h.publish_continue_session("session_id", "text", ["intent_name"], "custom_data", True) 28 | h.publish_continue_session("session_id", "text", ["intent_name"], "custom_data", True, "slot") 29 | 30 | def test_publish_end_session(self, mqtt_server): 31 | with Hermes("localhost:1883") as h: 32 | h.publish_end_session("session_id", "goodbye") 33 | 34 | def test_publish_start_session_notification(self, mqtt_server): 35 | with Hermes("localhost:1883") as h: 36 | h.publish_start_session_notification("site_id", "initialization", None) 37 | h.publish_start_session_notification("site_id", "initialization", "custom_data") 38 | h.publish_start_session_notification("site_id", "initialization", "custom_data", "text") 39 | 40 | def test_publish_start_session_action(self, mqtt_server): 41 | with Hermes("localhost:1883") as h: 42 | h.publish_start_session_action(None, None, [], False, False, None) 43 | h.publish_start_session_action("site_id", None, [], False, False, None) 44 | h.publish_start_session_action("site_id", "text", [], False, False, None) 45 | h.publish_start_session_action("site_id", "text", [], False, False, "custom_data") 46 | 47 | def test_configure_dialogue(self, mqtt_server): 48 | from hermes_python.ontology.dialogue import DialogueConfiguration 49 | conf = DialogueConfiguration() 50 | 51 | with Hermes("localhost:1883") as h: 52 | h.configure_dialogue(conf) 53 | 54 | def test_publish_sound_feedback_toggle(self, mqtt_server): 55 | from hermes_python.ontology.feedback import SiteMessage 56 | site_message = SiteMessage("kitchen") 57 | 58 | with Hermes("localhost:1883") as h: 59 | h.enable_sound_feedback(site_message) 60 | h.disable_sound_feedback(site_message) 61 | 62 | @pytest.mark.skip(reason="removed once we have intent roundtrip tests. ") 63 | def test_subscription_to_intent_message(mqtt_server): 64 | subscribe_intent_callback = mock.Mock() 65 | 66 | with Hermes("localhost:1883") as h: 67 | h.subscribe_intent("bundle:searchWeatherForecast", subscribe_intent_callback) 68 | single("hermes/intent/bundle:searchWeatherForecast", 69 | payload='{"sessionId":"08f56b9e-b4e4-4688-8a3e-1653c48180ee","customData":null,"siteId":"default","input":"quel temps fait il à paris","asrTokens":[[{"value":"quel","confidence":1.0,"rangeStart":0,"rangeEnd":4,"time":{"start":0.0,"end":0.29999998}},{"value":"temps","confidence":1.0,"rangeStart":5,"rangeEnd":10,"time":{"start":0.29999998,"end":0.51}},{"value":"fait","confidence":1.0,"rangeStart":11,"rangeEnd":15,"time":{"start":0.51,"end":0.57}},{"value":"il","confidence":1.0,"rangeStart":16,"rangeEnd":18,"time":{"start":0.57,"end":0.75}},{"value":"à","confidence":0.94917387,"rangeStart":19,"rangeEnd":20,"time":{"start":0.75,"end":0.8147715}},{"value":"paris","confidence":1.0,"rangeStart":21,"rangeEnd":26,"time":{"start":0.8147715,"end":1.68}}]],"intent":{"intentName":"bundle:searchWeatherForecast","confidenceScore":0.9450033},"slots":[{"rawValue":"paris","value":{"kind":"Custom","value":"Paris"},"range":{"start":21,"end":26},"entity":"locality_fr","slotName":"forecast_locality","confidenceScore":1.0}]}') 70 | time.sleep(0.5) 71 | 72 | subscribe_intent_callback.assert_called_once() 73 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ontology/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/ontology/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ontology/dialogue/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/ontology/dialogue/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ontology/dialogue/test_intent.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import hermes_python 3 | 4 | 5 | class TestSlotAccessFromIntent(object): 6 | def test_slot_access_from_intent_message(self): 7 | custom_slot_value = hermes_python.ontology.slot.CustomValue("custom_slot") 8 | slot_value = hermes_python.ontology.slot.SlotValue(1, custom_slot_value) 9 | nlu_slot = hermes_python.ontology.nlu.NluSlot(slot_value, custom_slot_value.value, [], "entity", "test_slot", 10 | 0, 100, 0.8) 11 | slots_list = hermes_python.ontology.nlu.SlotsList() 12 | slots_list.append(nlu_slot) 13 | assert type(slots_list.first()) is hermes_python.ontology.slot.CustomValue 14 | slot_map = dict([(nlu_slot.slot_name, slots_list)]) 15 | slots = hermes_python.ontology.dialogue.SlotMap(slot_map) 16 | 17 | intent_message = hermes_python.ontology.dialogue.IntentMessage("session_id", "", "site_id", "input", "testIntent", 18 | slots, [], [], .1) 19 | assert type(intent_message.slots.test_slot.first()) is hermes_python.ontology.slot.CustomValue 20 | assert type(intent_message.slots.test_slot.all()[0]) is hermes_python.ontology.slot.CustomValue 21 | assert type(intent_message.slots.test_slot[0]) is hermes_python.ontology.nlu.NluSlot 22 | assert type(intent_message.slots.test_slot[0].slot_value) is hermes_python.ontology.slot.SlotValue 23 | assert type(intent_message.slots.test_slot[0].slot_value.value) is hermes_python.ontology.slot.CustomValue 24 | 25 | 26 | def test_slot_access_from_intent_message_that_has_no_slots(self): 27 | intent_message = hermes_python.ontology.dialogue.IntentMessage("session_id", "", "site_id", "input", "testIntent", 28 | None, [], [], .1) 29 | 30 | assert len(intent_message.slots) == 0 31 | assert len(intent_message.slots.test_slot) == 0 32 | assert intent_message.slots.test_slot.first() is None 33 | assert intent_message.slots.test_slot.all() is None 34 | 35 | 36 | def test_unseen_slot_access_from_intent_message(self): 37 | custom_slot_value = hermes_python.ontology.slot.CustomValue("custom_slot") 38 | slot_value = hermes_python.ontology.slot.SlotValue(1, custom_slot_value) 39 | nlu_slot = hermes_python.ontology.nlu.NluSlot(slot_value, custom_slot_value.value, [], "entity", "test_slot", 40 | 0, 100, .8) 41 | slots_list = hermes_python.ontology.nlu.SlotsList() 42 | slots_list.append(nlu_slot) 43 | assert type(slots_list.first()) is hermes_python.ontology.slot.CustomValue 44 | slot_map = dict([(nlu_slot.slot_name, slots_list)]) 45 | slots = hermes_python.ontology.dialogue.SlotMap(slot_map) 46 | 47 | intent_message = hermes_python.ontology.dialogue.IntentMessage("session_id", "", "site_id", "input", "testIntent", 48 | slots, [], [], .1) 49 | 50 | assert intent_message.slots.unseen_test_slot.first() is None 51 | assert intent_message.slots.unseen_test_slot.all() is None 52 | assert len(intent_message.slots.unseen_test_slot) == 0 53 | 54 | 55 | def test_confidence_access(self): 56 | custom_slot_value = hermes_python.ontology.slot.CustomValue("custom_slot") 57 | slot_value = hermes_python.ontology.slot.SlotValue(1, custom_slot_value) 58 | nlu_slot = hermes_python.ontology.nlu.NluSlot(slot_value, custom_slot_value.value, [], "entity", "test_slot", 59 | 0, 100, 0.8) 60 | 61 | slot_map = defaultdict(hermes_python.ontology.nlu.SlotsList) 62 | slot_map[nlu_slot.slot_name].append(nlu_slot) 63 | 64 | slots = hermes_python.ontology.dialogue.SlotMap(slot_map) 65 | intent_message = hermes_python.ontology.dialogue.IntentMessage("session_id", "", "site_id", "input", "testIntent", 66 | slots, [], [], .1) 67 | assert intent_message.slots.test_slot[0].confidence_score == 0.8 68 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ontology/dialogue/test_session.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from hermes_python.ontology.dialogue import DialogueConfiguration 3 | 4 | 5 | class TestDialogueConfiguration: 6 | def test_dialogue_configuration_disabling_intent(self): 7 | conf = DialogueConfiguration() 8 | conf.disable_intent("intent_1") 9 | 10 | dialogue_configure_messages = conf.build() 11 | assert len(dialogue_configure_messages) == 1 12 | 13 | dialogue_configure_message = dialogue_configure_messages[0] 14 | 15 | assert dialogue_configure_message.site_id is None 16 | assert len(dialogue_configure_message.intents) == 1 17 | assert dialogue_configure_message.intents[0].intent_id == "intent_1" 18 | assert not dialogue_configure_message.intents[0].enable 19 | 20 | def test_dialogue_configuration_disabling_intents(self): 21 | conf = DialogueConfiguration() 22 | conf.disable_intents(["intent_1", "intent_2"]) 23 | 24 | dialogue_configure_messages = conf.build() 25 | assert len(dialogue_configure_messages) == 1 26 | 27 | dialogue_configure_message = dialogue_configure_messages[0] 28 | 29 | assert dialogue_configure_message.site_id is None 30 | assert len(dialogue_configure_message.intents) == 2 31 | 32 | 33 | def test_dialogue_configuration_global_site_id(self): 34 | conf = DialogueConfiguration() 35 | conf\ 36 | .for_site_id("kitchen")\ 37 | .disable_intent("intent_1") 38 | 39 | dialogue_configure_message = conf.build()[0] 40 | assert dialogue_configure_message.site_id == "kitchen" 41 | assert len(dialogue_configure_message.intents) == 1 42 | 43 | def test_dialogue_configuration_local_site_id(self): 44 | conf = DialogueConfiguration(site_id="kitchen") 45 | conf\ 46 | .disable_intent("intent_1", site_id="bedroom") 47 | 48 | dialogue_configure_message = conf.build()[0] 49 | assert dialogue_configure_message.site_id == "bedroom" 50 | assert len(dialogue_configure_message.intents) == 1 51 | 52 | def test_dialogue_configuration_multiple_site_ids(self): 53 | conf = DialogueConfiguration() 54 | conf\ 55 | .disable_intent("intent_1", site_id="bedroom") \ 56 | .disable_intent("intent_1", site_id="kitchen") 57 | 58 | dialogue_configure_messages = conf.build() 59 | assert dialogue_configure_messages[0].site_id == "bedroom" 60 | assert dialogue_configure_messages[1].site_id == "kitchen" 61 | 62 | def test_dialogue_configuration_multiple_site_ids2(self): 63 | conf = DialogueConfiguration() 64 | conf \ 65 | .disable_intent("intent_1") \ 66 | .disable_intent("intent_1", site_id="kitchen") 67 | 68 | dialogue_configure_messages = conf.build() 69 | assert dialogue_configure_messages[0].site_id is None 70 | assert dialogue_configure_messages[1].site_id == "kitchen" 71 | 72 | def test_dialogue_configuration_multiple_site_ids3(self): 73 | conf = DialogueConfiguration(site_id="bathroom") 74 | conf \ 75 | .disable_intent("intent_2") \ 76 | .disable_intent("intent_2", site_id="kitchen") 77 | 78 | dialogue_configure_messages = conf.build() 79 | assert dialogue_configure_messages[0].site_id == "bathroom" 80 | assert dialogue_configure_messages[1].site_id == "kitchen" 81 | 82 | 83 | def test_dialogue_configuration_toggling_intent(self): 84 | conf = DialogueConfiguration() 85 | conf\ 86 | .disable_intent("intent_2") \ 87 | .enable_intent("intent_2") \ 88 | .disable_intent("intent_2") \ 89 | .enable_intent("intent_2") 90 | 91 | dialogue_configure_messages = conf.build() 92 | assert dialogue_configure_messages[0].intents[0].enable 93 | 94 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/ontology/dialogue/test_slot.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import hermes_python 3 | 4 | from hermes_python.ontology.dialogue import DialogueConfiguration 5 | 6 | 7 | def test_slot_access_dot_notation(): 8 | from hermes_python.ontology.nlu import SlotsList 9 | slots = hermes_python.ontology.dialogue.SlotMap({"test_slot": SlotsList()}) 10 | assert type(slots.test_slot) is SlotsList 11 | 12 | 13 | def test_slot_access_dict_notation(): 14 | from hermes_python.ontology.nlu import SlotsList 15 | slots = hermes_python.ontology.dialogue.SlotMap({"test_slot": SlotsList()}) 16 | assert type(slots["test_slot"]) is SlotsList 17 | 18 | 19 | def test_helper_method_access(): 20 | from hermes_python.ontology.slot import SlotValue, CustomValue 21 | from hermes_python.ontology.nlu import SlotsList, NluSlot, SlotMap 22 | 23 | custom_value_slot = CustomValue("Hello world") 24 | nlu_slot = NluSlot(SlotValue(1, custom_value_slot), "test", [], "test", "test_slot", 0, 0, []) 25 | slots = SlotMap({"test_slot": SlotsList()}) 26 | 27 | slots.test_slot.append(nlu_slot) 28 | 29 | assert type(slots.test_slot.first()) is CustomValue 30 | 31 | 32 | def test_unseen_slot_access_1(): 33 | slots = hermes_python.ontology.dialogue.SlotMap({}) 34 | assert len(slots) == 0 35 | assert len(slots.unseen_slots) == 0 36 | assert len(slots) == 0 37 | assert slots.unseen_slot.first() is None 38 | assert slots.unseen_slot.all() is None 39 | 40 | 41 | def test_unseen_slot_access_2(): 42 | slots = hermes_python.ontology.dialogue.SlotMap({}) 43 | assert len(slots) == 0 44 | assert len(slots.unseen_slot) == 0 45 | assert len(slots) == 0 46 | 47 | 48 | def test_unseen_slot_access_3(): 49 | slots = hermes_python.ontology.dialogue.SlotMap({}) 50 | assert slots.unseen_slot.all() is None 51 | 52 | 53 | def test_unseen_slot_acces_dict_notation(): 54 | slots = hermes_python.ontology.dialogue.SlotMap({}) 55 | assert len(slots['unseen_slot']) == 0 56 | assert slots['unseen_slot'].first() is None 57 | assert slots['unseen_slot'].all() is None 58 | 59 | 60 | def test_slot_map_items_iteration(): 61 | from hermes_python.ontology.nlu import SlotsList 62 | slots = hermes_python.ontology.dialogue.SlotMap({"test_slot": SlotsList()}) 63 | 64 | for slot, slot_value_list in slots.items(): 65 | assert slot == "test_slot" 66 | assert len(slot_value_list) == 0 67 | -------------------------------------------------------------------------------- /platforms/hermes-python/tests/roundtrip/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/snipsco/hermes-protocol/578a600d92cc0206ed5c645c446c3d2af2c1890e/platforms/hermes-python/tests/roundtrip/__init__.py -------------------------------------------------------------------------------- /platforms/hermes-python/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist = py27,py36 3 | skipsdist=True 4 | 5 | [testenv] 6 | skip_install=True 7 | deps = 8 | -r requirements/install.txt 9 | -r requirements/tests.txt 10 | commands = 11 | py.test 12 | python setup.py bdist_wheel 13 | python setup.py bdist_wheel --plat-name linux-armv7l 14 | python setup.py bdist_wheel --plat-name linux-armv6l 15 | 16 | [testenv:docs] 17 | skip_intall=True 18 | deps = 19 | -r requirements/install.txt 20 | -r requirements/tests.txt 21 | commands = 22 | python generate_documentation.py 23 | 24 | 25 | [testenv:cp27-cp27m] 26 | basepython=/opt/python/cp27-cp27m 27 | skip_install=True 28 | deps = 29 | -r requirements/install.txt 30 | -r requirements/tests.txt 31 | commands = 32 | py.test 33 | python setup.py bdist_wheel 34 | python setup.py bdist_wheel --plat-name linux-armv7l 35 | python setup.py bdist_wheel --plat-name linux-armv6l 36 | 37 | [testenv:cp27-cp27mu] 38 | basepython=/opt/python/cp27-cp27mu 39 | skip_install=True 40 | deps = 41 | -r requirements/install.txt 42 | -r requirements/tests.txt 43 | commands = 44 | py.test 45 | python setup.py bdist_wheel 46 | python setup.py bdist_wheel --plat-name linux-armv7l 47 | python setup.py bdist_wheel --plat-name linux-armv6l 48 | 49 | [testenv:cp34-cp34m] 50 | basepython=/opt/python/cp34-cp34m 51 | skip_install=True 52 | deps = 53 | -r requirements/install.txt 54 | -r requirements/tests.txt 55 | commands = 56 | py.test 57 | python setup.py bdist_wheel 58 | python setup.py bdist_wheel --plat-name linux-armv7l 59 | python setup.py bdist_wheel --plat-name linux-armv6l 60 | 61 | [testenv:cp35-cp35m] 62 | basepython=/opt/python/cp35-cp35m 63 | skip_install=True 64 | deps = 65 | -r requirements/install.txt 66 | -r requirements/tests.txt 67 | commands = 68 | py.test 69 | python setup.py bdist_wheel 70 | python setup.py bdist_wheel --plat-name linux-armv7l 71 | python setup.py bdist_wheel --plat-name linux-armv6l 72 | 73 | [testenv:cp36-cp36m] 74 | basepython=/opt/python/cp36-cp36m 75 | skip_install=True 76 | deps = 77 | -r requirements/install.txt 78 | -r requirements/tests.txt 79 | commands = 80 | py.test 81 | python setup.py bdist_wheel 82 | python setup.py bdist_wheel --plat-name linux-armv7l 83 | python setup.py bdist_wheel --plat-name linux-armv6l 84 | 85 | [testenv:cp37-cp37m] 86 | basepython=/opt/python/cp37-cp37m 87 | skip_install=True 88 | deps = 89 | -r requirements/install.txt 90 | -r requirements/tests.txt 91 | commands = 92 | py.test 93 | python setup.py bdist_wheel 94 | python setup.py bdist_wheel --plat-name linux-armv7l 95 | python setup.py bdist_wheel --plat-name linux-armv6l 96 | -------------------------------------------------------------------------------- /update_c_headers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | rustup default nightly 6 | cbindgen -c hermes-mqtt-ffi/cbindgen_full.toml -o platforms/c/libsnips_hermes_full.h hermes-mqtt-ffi -v 7 | cbindgen -c hermes-mqtt-ffi/cbindgen_json.toml -o platforms/c/libsnips_hermes_json.h hermes-mqtt-ffi -v 8 | cbindgen -c hermes-mqtt-ffi/cbindgen.toml -o platforms/c/libsnips_hermes.h hermes-mqtt-ffi -v 9 | rustup default stable 10 | -------------------------------------------------------------------------------- /update_ontology_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | NEW_VERSION=${1?"usage $0 "} 4 | 5 | echo "Updating snips-nlu-ontology versions to version ${NEW_VERSION}" 6 | find . -name "Cargo.toml" -exec perl -p -i -e "s/snipsco\/snips-nlu-ontology\".*\$/snipsco\/snips-nlu-ontology\", tag = \"$NEW_VERSION\" }/g" {} \; 7 | find . -name "build.gradle" -exec perl -p -i -e "s/compile \"ai.snips:snips-nlu-ontology:.*\"\$/compile \"ai.snips:snips-nlu-ontology:$NEW_VERSION\"/g" {} \; 8 | -------------------------------------------------------------------------------- /update_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | NEW_VERSION=$1 6 | 7 | if [ -z $NEW_VERSION ] 8 | then 9 | echo "Usage: $0 NEW_VERSION" 10 | exit 1 11 | fi 12 | 13 | perl -p -i -e "s/^version = \".*\"\$/version = \"$NEW_VERSION\"/g" Cargo.toml 14 | perl -p -i -e "s/^version = \".*\"\$/version = \"$NEW_VERSION\"/g" */Cargo.toml 15 | perl -p -i -e "s/^version = \".*\"\$/version = \"$NEW_VERSION\"/g" platforms/hermes-kotlin/build.gradle 16 | --------------------------------------------------------------------------------