Skip to content

Commit

Permalink
Implement websocket client class (#14)
Browse files Browse the repository at this point in the history
# Description
Implements a WS communication-based client
Refactor to minimize duplicate code


# Issues
<!-- If this is related to or closes an issue/other PR, please note them
here -->

# Other Notes
- Goes with NeonGeckoCom/neon-hana#20

---------

Co-authored-by: Daniel McKnight <[email protected]>
  • Loading branch information
NeonDaniel and NeonDaniel authored May 22, 2024
1 parent 93755e9 commit 3c11613
Show file tree
Hide file tree
Showing 9 changed files with 371 additions and 33 deletions.
1 change: 1 addition & 0 deletions .github/workflows/publish_test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
with:
version_file: "neon_nodes/version.py"
publish_prerelease: true
update_changelog: true
trigger_os_build:
runs-on: ubuntu-latest
needs: publish_alpha_release
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,19 @@ processing, and presenting a response to the user.
The voice client will start a service that listens for a wake word on the local
system, sends recorded speech to a HANA endpoint for processing, and plays back
the response.

## Websocket Client
The websocket client starts a local listener service and establishes a websocket
connection to a remove HANA server. Compared to the Voice Client, this has
lower latency and allows for asynchronous messages from the HANA server.

## Configuration
This service is configured via `~/.config/neon/neon.yaml`.

```yaml
neon_node:
description: Neon Node # Friendly description of the node
hana_address: https://hana.neonaiservices.com # Hana server HTTP address
hana_username: node_user # Hana node user username
hana_password: node_password # Hana node user password
```
27 changes: 27 additions & 0 deletions neon_nodes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,30 @@
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from mock import Mock
from ovos_utils.log import LOG

class MockTransformers(Mock):
def transform(self, chunk):
return chunk, dict()


def on_ready():
LOG.info("ready")


def on_stopping():
LOG.info("stopping")


def on_error(e="unknown"):
LOG.error(e)


def on_alive():
LOG.debug("alive")


def on_started():
LOG.debug("started")
5 changes: 5 additions & 0 deletions neon_nodes/configuration/system.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
neon_node:
description: Neon Node
# TODO: Stable release address
hana_address: https://hana.neonaibeta.com
hana_username: neon
hana_password: neon
microphone:
module: ovos-microphone-plugin-alsa
listener:
sample_rate: 16000
wake_word: hey_mycroft
stand_up_word: ""
VAD:
Expand Down
42 changes: 11 additions & 31 deletions neon_nodes/voice_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,30 +47,7 @@
from pydub import AudioSegment
from pydub.playback import play


class MockTransformers(Mock):
def transform(self, chunk):
return chunk, dict()


def on_ready():
LOG.info("ready")


def on_stopping():
LOG.info("stopping")


def on_error(e="unknown"):
LOG.error(e)


def on_alive():
LOG.debug("alive")


def on_started():
LOG.debug("started")
from neon_nodes import on_alive, on_error, on_ready, on_started, on_stopping, MockTransformers


class NeonVoiceClient:
Expand All @@ -81,7 +58,9 @@ def __init__(self, bus=None, ready_hook=on_ready, error_hook=on_error,
self.stopping_hook = stopping_hook
alive_hook()
self.config = Configuration()
self._device_data = self.config.get('neon_node', {})
self._node_config = self.config.get('neon_node', {})
self._hana_address = self._node_config.get('hana_address')

LOG.init(self.config.get("logging"))
self.bus = bus or FakeBus()
self.lang = self.config.get('lang') or "en-us"
Expand All @@ -108,7 +87,7 @@ def __init__(self, bus=None, ready_hook=on_ready, error_hook=on_error,
self._error_sound = None

self._network_info = dict()
self._node_data = dict()
self._node_data = {"description": self._node_config.get("description")}

started_hook()
self.run()
Expand Down Expand Up @@ -240,18 +219,21 @@ def get_audio_response(self, audio: bytes):
audio_data = b64encode(audio).decode("utf-8")
transcript = request_backend("neon/get_stt",
{"encoded_audio": audio_data,
"lang_code": self.lang})
"lang_code": self.lang},
server_url=self._hana_address)
transcribed = transcript['transcripts'][0]
LOG.info(transcribed)
response = request_backend("neon/get_response",
{"lang_code": self.lang,
"user_profile": self.user_profile,
"node_data": self.node_data,
"utterance": transcribed})
"utterance": transcribed},
server_url=self._hana_address)
answer = response['answer']
LOG.info(answer)
audio = request_backend("neon/get_tts", {"lang_code": self.lang,
"to_speak": answer})
"to_speak": answer},
server_url=self._hana_address)
audio_bytes = b64decode(audio['encoded_audio'])
play(AudioSegment.from_file(io.BytesIO(audio_bytes), format="wav"))
LOG.info(f"Playback completed")
Expand All @@ -272,6 +254,4 @@ def main(*args, **kwargs):


if __name__ == "__main__":
# environ.setdefault("OVOS_CONFIG_BASE_FOLDER", "neon")
# environ.setdefault("OVOS_CONFIG_FILENAME", "diana.yaml")
main()
Loading

0 comments on commit 3c11613

Please sign in to comment.