https://github.com/elbywan/hermes-crystal
Crystal bindings to the Hermes protocol used by the Snips platform. :gem:
https://github.com/elbywan/hermes-crystal
hermes-protocol snips
Last synced: 7 months ago
JSON representation
Crystal bindings to the Hermes protocol used by the Snips platform. :gem:
- Host: GitHub
- URL: https://github.com/elbywan/hermes-crystal
- Owner: elbywan
- License: mit
- Created: 2019-10-17T06:33:25.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2019-11-17T22:09:51.000Z (over 6 years ago)
- Last Synced: 2025-10-17T07:41:42.801Z (8 months ago)
- Topics: hermes-protocol, snips
- Language: Crystal
- Homepage: https://elbywan.github.io/hermes-crystal/
- Size: 1.47 MB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# hermes-crystal
#### A crystal wrapper around the hermes protocol
[](https://elbywan.github.io/hermes-crystal/)
[](https://github.com/elbywan/hermes-crystal/releases)
[](https://travis-ci.org/elbywan/hermes-crystal)
## Context
The `hermes-crystal` library provides bindings for the [Hermes protocol](https://docs.snips.ai/reference/hermes) that [Snips](https://snips.ai/) components use to communicate together. `hermes-crystal` allows you to interface seamlessly with the Snips platform and create Voice applications with ease!
`hermes-crystal` abstracts away the connection to the MQTT bus and the parsing of incoming and outcoming messages from and to the components of the platform and provides a high-level API as well.
## Installation
1. Add the dependency to your `shard.yml`:
```yaml
dependencies:
hermes-crystal:
github: elbywan/hermes-crystal
```
2. Run `shards install`
A postinstall script will automagically download the file if your os and architecture is supported.
#### ⚠️ Unsupported platforms / architectures
If the setup could not infer the library file version, it will attempt to build it from the sources.
Please note that [`rust`](https://www.rust-lang.org/tools/install) and [`git`](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) are required in order to build the library.
If you want to force this behaviour, you can also define the `HERMES_BUILD_FROM_SOURCES` environment variable before running `shards install`.
```sh
env HERMES_BUILD_FROM_SOURCES=true shards install
```
## Usage
```crystal
require "hermes-crystal"
```
### Minimal use case
```crystal
require "hermes-crystal"
# Instantiate a Hermes object connecting to the default MQTT address.
hermes = Hermes.new broker_address: "localhost:1883"
hermes.dialog.flow "myIntent" do |msg, flow|
# Log the message
pp msg
# End the session and speak
"Received message for intent #{msg.intent.intent_name}"
end
# To prevent the process from exiting, if needed, you can use `sleep`.
sleep
```
### Expanded use case
#### High level "flow" API.
```crystal
require "hermes-crystal"
hermes = Hermes.new broker_address: "localhost:1883"
# NB: dialog is only one of the available API facades.
dialog = hermes.dialog
# Using the high level API is strongly recommended for building complex dialog flows.
# Let's register the following dialog paths:
# A
# ├── B
# │ └─ D
# └── C
#
# In plain words, intent 'A' starts the flow, then for the next round possible intents are 'B' or 'C'.
# If 'B' is the next intent detected, then the following intent must be 'D' and then the flow will end.
# If 'C' is the next intent, the flow will end.
dialog.flow "A" do |_, flow|
puts "Intent A received. Session started."
# At each step of the dialog flow, you have the choice of
# registering the next intents, or end the flow by not
# registering any continuations.
# We choose to subscribe to both intent B or C so that the dialog
# flow will continue with either one or the other next.
# Mark intent 'B' as one of the next dialog intents. (A -> B)
flow.continue "B" do |_, flow|
puts "Intent B received. Session continued."
# Mark intent 'D'. (A -> B -> D)
flow.continue "D" do |_, flow|
puts "Intent D received."
"Finished the session with intent D."
end
# Make the TTS say that.
"Continue with D."
end
# Mark intent 'C' as one of the next dialog intents. (A -> C)
flow.continue "C" do |msg, flow|
slot_value = msg.slots.try &.[0].value.value
puts "Intent C received."
"Finished the session with intent C having value #{slot_value} ."
end
# A flow function must return a string that is going to be spoken by the TTS.
"Continue with B or C."
end
```
#### Low level subscriber / publisher API.
```crystal
require "hermes-crystal"
hermes = Hermes.new broker_address: "localhost:1883"
# NB: dialog is only one of the available API facades.
dialog = hermes.dialog
# Every API facade can publish and receive data based on a list of events.
# For the purpose of this example, we will only use the dialog facade, and the
# events related to a dialog session.
# Note that more events are available for each facade.
# You can subscribe to an event triggered when the intent 'some_intent' is detected like this:
dialog.subscribe_intent "some_intent" do |message|
# The 'message' argument contain all the data you need to perform an action based on what the user said.
# For instance, you can grab a slot and its value like this.
my_slot = message.slots.try &.find { |slot| slot.slot_name == "some_slot" }
slot_value = my_slot.try &.value.value
puts "Slot value: #{slot_value}"
# And here is how to grab the intent name.
puts "Received intent: #{message.intent.intent_name}"
session_must_be_continued = true
# Then, you can either (but not both!):
if session_must_be_continued
# Continue the current dialogue session.
dialog.publish_continue_session({
# Session id is the same as the current session.
session_id: message.session_id,
# This is what is going to be spoken between this and the next session round.
text: "The session lives on…",
# A list of possible intent continuations for the next round.
intent_filter: ["next_intent"],
# Unused by the crystal wrapper.
custom_data: nil,
# An optional slot filler argument. Unneeded for this example.
slot: nil,
# If true, then a custom behaviour can be determined if no intents are recognized for the next round of dialogue.
send_intent_not_recognized: false
})
else
# End the dialogue session.
dialog.publish_end_session({
session_id: message.session_id,
text: "The session has ended."
})
end
end
# You can also unsubscribe to a registered event.
handler = uninitialized Void* -> Void
handler = dialog.subscribe_intents {
# In this case, unsubscribe the first time an intent has been detected.
dialog.unsubscribe_intents handler
# ...
}
# Or process a subscription only once:
dialog.subscribe_intents once: true {
# ...
}
```
## Documentation
Hosted [here](https://elbywan.github.io/hermes-crystal).
## Development
#### Prerequisite
[`Rust`](https://www.rust-lang.org/learn/get-started) is needed to build the hermes library.
#### Setup
1. Clone the repo.
2. Update the hermes-protocol submodule (`git submodule update`)
3. Build the shared libraries. (`cd hermes-protocol; cargo build -p hermes-mqtt-ffi -p hermes-ffi-test; cd ..`)
#### Tasks
Development tasks are defined in the [`sam.cr`](https://github.com/imdrasil/sam.cr) file.
Use `make sam help` to display the list of available tasks.
## Contributing
1. Fork it ()
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request
## Contributors
- [elbywan](https://github.com/elbywan) - creator and maintainer