Skip to content

Commit

Permalink
feat: add "hey neon" ww, Docker container, extras, CLI option
Browse files Browse the repository at this point in the history
  • Loading branch information
mikejgray committed Dec 19, 2023
1 parent b050ba0 commit 05e45f1
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 7 deletions.
61 changes: 61 additions & 0 deletions .github/workflows/publish_test_websat_build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name: Publish Docker Containers
on:
registry:
type: string
default: ghcr.io

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-websat

jobs:
build_and_publish_docker:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.version }}
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}

- name: Log in to the Container registry
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Get Version
id: version
run: |
VERSION=$(sed "s/a/-a./" <<< $(python setup.py --version))
echo ::set-output name=version::${VERSION}
env:
image_name: ${{ env.IMAGE_NAME }}

- name: Setup QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Extract metadata for base Docker
id: base_meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}},value=${{ steps.version.outputs.version }}
type=ref,event=branch
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile.websat
push: true
tags: ${{ steps.base_meta.outputs.tags }}
labels: ${{ steps.base_meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7
24 changes: 24 additions & 0 deletions Dockerfile.websat
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM python:3.8-slim

LABEL vendor=neon.ai \
ai.neon.name="neon-iris-websat"

ENV OVOS_CONFIG_BASE_FOLDER neon
ENV OVOS_CONFIG_FILENAME neon.yaml
ENV XDG_CONFIG_HOME /config


RUN apt update && \
apt install -y ffmpeg \
&& rm -rf /var/lib/apt/lists/*

ADD . /neon_iris
WORKDIR /neon_iris

RUN pip install wheel && \
pip install .[web_sat]

COPY docker_overlay/ /
EXPOSE 8000

CMD ["iris", "start-websat"]
3 changes: 2 additions & 1 deletion docker_overlay/etc/neon/neon.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ iris:
webui_chatbot_label: Chat History
webui_mic_label: Speak to Neon
webui_text_label: Text with Neon
webui_ws_url: ws://localhost:8000/ws # Override, as this needs to be reachable by the browser
server_address: "0.0.0.0"
server_port: 7860
default_lang: en-us
Expand Down Expand Up @@ -43,4 +44,4 @@ logs:
error:
- pika
warning:
- filelock
- filelock
13 changes: 13 additions & 0 deletions neon_iris/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ def start_gradio():
click.echo("Unable to connect to MQ server")


@neon_iris_cli.command(help="Create a Web Voice Satellite session")
@click.option("--port", "-p", default=8000, help="Port to run on, defaults to 8000")
@click.option("--host", default="0.0.0.0", help="Host to run on, defaults to 0.0.0.0")
def start_websat(port, host):
from neon_iris.web_sat_client import app
_print_config()
try:
import uvicorn
uvicorn.run(app, host=host, port=port)
except OSError:
click.echo("Unable to connect to MQ server")


@neon_iris_cli.command(help="Query Neon Core for supported languages")
def get_languages():
from neon_iris.util import query_neon
Expand Down
4 changes: 2 additions & 2 deletions neon_iris/static/scripts/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ function toggleListeningState() {
const WebSocketHandler = (() => {
let lastActivationTime = 0;
const activationCooldown = 3000; // 3 seconds cooldown
const ws = new WebSocket(`ws://localhost:8000/ws`);
const ws = new WebSocket(WS_URL);
const audio = new Audio("/static/wake.mp3"); // Wakeword acknowledgment sound

ws.onopen = () => {
Expand All @@ -124,7 +124,7 @@ const WebSocketHandler = (() => {
const currentTime = Date.now();
if ("activations" in model_payload) {
if (
model_payload.activations.includes("alexa") &&
model_payload.activations.includes("hey_neon") &&
currentTime - lastActivationTime > activationCooldown
) {
shouldListen = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
<!-- Silero VAD -->
<script src="https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@ricky0123/[email protected]/dist/bundle.min.js"></script>
<!-- Config -->
<script>
const WS_URL = "{{ ws_url }}";
</script>
<!-- AI Code -->
<script src="/static/scripts/websocket.js"></script>
<script src="/static/scripts/audio.js"></script>
Expand Down
Binary file added neon_iris/wakeword_models/hey_neon/hey_neon.onnx
Binary file not shown.
Binary file not shown.
10 changes: 8 additions & 2 deletions neon_iris/web_sat_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,13 @@ def __init__(self, lang: str = None):
LOG.name = "iris"
LOG.init(self.config.get("logs"))
# OpenWW
self.oww_model = Model(inference_framework="tflite")
# TODO: Allow for arbitrary models, or pre-existing OpenWW models
self.oww_model = Model(
wakeword_models=["neon_iris/wakeword_models/hey_neon/hey_neon.tflite"],
inference_framework="tflite",
)
# FastAPI
self.templates = Jinja2Templates(directory="neon_iris/static/templates")
self.templates = Jinja2Templates(directory="neon_iris/templates")
self.build_routes()

def get_lang(self, session_id: str):
Expand Down Expand Up @@ -150,12 +154,14 @@ async def read_root(request: Request):
description = self.config.get("webui_description", "Chat With Neon")
title = self.config.get("webui_title", "Neon AI")
placeholder = self.config.get("webui_input_placeholder", "Ask me something")
ws_url = self.config.get("webui_ws_url", "ws://localhost:8000/ws")

context = {
"request": request,
"title": title,
"description": description,
"placeholder": placeholder,
"ws_url": ws_url
}
return self.templates.TemplateResponse("index.html", context)

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ def get_requirements(requirements_filename: str):
"Programming Language :: Python :: 3",
"Operating System :: OS Independent"
],
python_requires='>=3.6',
python_requires='>=3.8',
install_requires=get_requirements("requirements.txt"),
extras_require={"gradio": get_requirements("gradio.txt")},
extras_require={"gradio": get_requirements("gradio.txt"), "web_sat": get_requirements("web_sat.txt")},
entry_points={
'console_scripts': ['iris=neon_iris.cli:neon_iris_cli']
}
Expand Down

0 comments on commit 05e45f1

Please sign in to comment.