{"id":17680712,"url":"https://github.com/elbywan/hermes-crystal","last_synced_at":"2026-07-11T13:31:52.858Z","repository":{"id":152587965,"uuid":"215719929","full_name":"elbywan/hermes-crystal","owner":"elbywan","description":"Crystal bindings to the Hermes protocol used by the Snips platform. :gem:","archived":false,"fork":false,"pushed_at":"2019-11-17T22:09:51.000Z","size":1541,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-26T13:43:25.885Z","etag":null,"topics":["hermes-protocol","snips"],"latest_commit_sha":null,"homepage":"https://elbywan.github.io/hermes-crystal/","language":"Crystal","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elbywan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-10-17T06:33:25.000Z","updated_at":"2019-11-17T22:09:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"c4cfc4cd-4441-411e-b09b-540e9f492954","html_url":"https://github.com/elbywan/hermes-crystal","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/elbywan/hermes-crystal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fhermes-crystal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fhermes-crystal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fhermes-crystal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fhermes-crystal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elbywan","download_url":"https://codeload.github.com/elbywan/hermes-crystal/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elbywan%2Fhermes-crystal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35364269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-11T02:00:05.354Z","response_time":104,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hermes-protocol","snips"],"created_at":"2024-10-24T09:08:41.043Z","updated_at":"2026-07-11T13:31:52.833Z","avatar_url":"https://github.com/elbywan.png","language":"Crystal","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hermes-crystal\n\n#### A crystal wrapper around the hermes protocol\n\n[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://elbywan.github.io/hermes-crystal/)\n[![GitHub release](https://img.shields.io/github/release/elbywan/hermes-crystal.svg)](https://github.com/elbywan/hermes-crystal/releases)\n[![Build Status](https://travis-ci.org/elbywan/hermes-crystal.svg?branch=master)](https://travis-ci.org/elbywan/hermes-crystal)\n\n## Context\n\nThe `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!\n\n`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.\n\n## Installation\n\n1. Add the dependency to your `shard.yml`:\n\n```yaml\ndependencies:\n  hermes-crystal:\n    github: elbywan/hermes-crystal\n```\n\n2. Run `shards install`\n\nA postinstall script will automagically download the file if your os and architecture is supported.\n\n#### ⚠️ Unsupported platforms / architectures\n\nIf the setup could not infer the library file version, it will attempt to build it from the sources.\nPlease 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.\n\nIf you want to force this behaviour, you can also define the `HERMES_BUILD_FROM_SOURCES` environment variable before running `shards install`.\n\n```sh\nenv HERMES_BUILD_FROM_SOURCES=true shards install\n```\n\n## Usage\n\n```crystal\nrequire \"hermes-crystal\"\n```\n\n### Minimal use case\n\n```crystal\nrequire \"hermes-crystal\"\n\n# Instantiate a Hermes object connecting to the default MQTT address.\nhermes = Hermes.new broker_address: \"localhost:1883\"\n\nhermes.dialog.flow \"myIntent\" do |msg, flow|\n  # Log the message\n  pp msg\n  # End the session and speak\n  \"Received message for intent #{msg.intent.intent_name}\"\nend\n\n# To prevent the process from exiting, if needed, you can use `sleep`.\nsleep\n```\n\n### Expanded use case\n\n#### High level \"flow\" API.\n\n```crystal\nrequire \"hermes-crystal\"\n\nhermes = Hermes.new broker_address: \"localhost:1883\"\n\n# NB: dialog is only one of the available API facades.\ndialog = hermes.dialog\n\n# Using the high level API is strongly recommended for building complex dialog flows.\n\n# Let's register the following dialog paths:\n# A\n# ├── B\n# │   └─ D\n# └── C\n#\n# In plain words, intent 'A' starts the flow, then for the next round possible intents are 'B' or 'C'.\n# If 'B' is the next intent detected, then the following intent must be 'D' and then the flow will end.\n# If 'C' is the next intent, the flow will end.\n\ndialog.flow \"A\" do |_, flow|\n  puts \"Intent A received. Session started.\"\n\n  # At each step of the dialog flow, you have the choice of\n  # registering the next intents, or end the flow by not\n  # registering any continuations.\n\n  # We choose to subscribe to both intent B or C so that the dialog\n  # flow will continue with either one or the other next.\n\n  # Mark intent 'B' as one of the next dialog intents. (A -\u003e B)\n  flow.continue \"B\" do |_, flow|\n    puts \"Intent B received. Session continued.\"\n\n    # Mark intent 'D'. (A -\u003e B -\u003e D)\n    flow.continue \"D\" do |_, flow|\n        puts \"Intent D received.\"\n        \"Finished the session with intent D.\"\n    end\n\n    # Make the TTS say that.\n    \"Continue with D.\"\n  end\n\n  # Mark intent 'C' as one of the next dialog intents. (A -\u003e C)\n  flow.continue \"C\" do |msg, flow|\n      slot_value = msg.slots.try \u0026.[0].value.value\n      puts \"Intent C received.\"\n      \"Finished the session with intent C having value #{slot_value} .\"\n  end\n\n  # A flow function must return a string that is going to be spoken by the TTS.\n  \"Continue with B or C.\"\nend\n```\n\n#### Low level subscriber / publisher API.\n\n```crystal\nrequire \"hermes-crystal\"\n\nhermes = Hermes.new broker_address: \"localhost:1883\"\n\n# NB: dialog is only one of the available API facades.\ndialog = hermes.dialog\n\n# Every API facade can publish and receive data based on a list of events.\n\n# For the purpose of this example, we will only use the dialog facade, and the\n# events related to a dialog session.\n\n# Note that more events are available for each facade.\n\n# You can subscribe to an event triggered when the intent 'some_intent' is detected like this:\ndialog.subscribe_intent \"some_intent\" do |message|\n  # The 'message' argument contain all the data you need to perform an action based on what the user said.\n\n  # For instance, you can grab a slot and its value like this.\n  my_slot = message.slots.try \u0026.find { |slot| slot.slot_name == \"some_slot\" }\n  slot_value = my_slot.try \u0026.value.value\n  puts \"Slot value: #{slot_value}\"\n  # And here is how to grab the intent name.\n  puts \"Received intent: #{message.intent.intent_name}\"\n\n  session_must_be_continued = true\n  # Then, you can either (but not both!):\n  if session_must_be_continued\n    # Continue the current dialogue session.\n    dialog.publish_continue_session({\n      # Session id is the same as the current session.\n      session_id: message.session_id,\n      # This is what is going to be spoken between this and the next session round.\n      text: \"The session lives on…\",\n      # A list of possible intent continuations for the next round.\n      intent_filter: [\"next_intent\"],\n      # Unused by the crystal wrapper.\n      custom_data: nil,\n      # An optional slot filler argument. Unneeded for this example.\n      slot: nil,\n      # If true, then a custom behaviour can be determined if no intents are recognized for the next round of dialogue.\n      send_intent_not_recognized: false\n    })\n  else\n    # End the dialogue session.\n    dialog.publish_end_session({\n      session_id: message.session_id,\n      text: \"The session has ended.\"\n    })\n  end\nend\n\n# You can also unsubscribe to a registered event.\nhandler = uninitialized Void* -\u003e Void\nhandler = dialog.subscribe_intents {\n    # In this case, unsubscribe the first time an intent has been detected.\n    dialog.unsubscribe_intents handler\n    # ...\n}\n# Or process a subscription only once:\ndialog.subscribe_intents once: true {\n  # ...\n}\n```\n\n## Documentation\n\nHosted [here](https://elbywan.github.io/hermes-crystal).\n\n## Development\n\n#### Prerequisite\n\n[`Rust`](https://www.rust-lang.org/learn/get-started) is needed to build the hermes library.\n\n#### Setup\n\n1. Clone the repo.\n2. Update the hermes-protocol submodule (`git submodule update`)\n3. Build the shared libraries. (`cd hermes-protocol; cargo build -p hermes-mqtt-ffi -p hermes-ffi-test; cd ..`)\n\n#### Tasks\n\nDevelopment tasks are defined in the [`sam.cr`](https://github.com/imdrasil/sam.cr) file.\n\nUse `make sam help` to display the list of available tasks.\n\n## Contributing\n\n1. Fork it (\u003chttps://github.com/elbywan/hermes-crystal/fork\u003e)\n2. Create your feature branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am 'Add some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5. Create a new Pull Request\n\n## Contributors\n\n- [elbywan](https://github.com/elbywan) - creator and maintainer\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felbywan%2Fhermes-crystal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felbywan%2Fhermes-crystal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felbywan%2Fhermes-crystal/lists"}