Skip to content

Commit 9d30eaf

Browse files
authored
Merge pull request #8 from second-state/docker
Dockerfile echokit-server container & Action for release binary
2 parents 490e3cd + a541a58 commit 9d30eaf

File tree

9 files changed

+490
-0
lines changed

9 files changed

+490
-0
lines changed

.github/workflows/release.yml

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
name: Build and Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
build-x86_64:
13+
name: Build (x86_64)
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Install base dependencies
17+
shell: bash
18+
run: |
19+
set -euo pipefail
20+
export DEBIAN_FRONTEND=noninteractive
21+
sudo apt-get update
22+
sudo apt-get install -y --no-install-recommends \
23+
ca-certificates \
24+
pkg-config \
25+
libssl-dev \
26+
build-essential \
27+
git \
28+
curl \
29+
unzip
30+
31+
- name: Check out repository
32+
uses: actions/checkout@v4
33+
34+
- name: Set up Rust toolchain
35+
uses: dtolnay/rust-toolchain@stable
36+
37+
- name: Add Rust target
38+
run: rustup target add x86_64-unknown-linux-gnu
39+
40+
- name: Build binary
41+
run: cargo build --release --target x86_64-unknown-linux-gnu
42+
43+
- name: Package release artifact
44+
id: package
45+
shell: bash
46+
run: |
47+
set -euo pipefail
48+
binary_path="target/x86_64-unknown-linux-gnu/release/echokit_server"
49+
if [ ! -f "$binary_path" ]; then
50+
echo "Release binary not found at $binary_path" >&2
51+
exit 1
52+
fi
53+
54+
archive_name="echokit_server-${{ github.ref_name }}-x86_64-unknown-linux-gnu.tar.gz"
55+
mkdir -p dist
56+
cp "$binary_path" dist/echokit_server
57+
chmod +x dist/echokit_server
58+
tar -C dist -czf "$archive_name" echokit_server
59+
rm dist/echokit_server
60+
61+
echo "archive=$archive_name" >> "$GITHUB_OUTPUT"
62+
63+
- name: Upload artifact
64+
uses: actions/upload-artifact@v4
65+
with:
66+
name: echokit_server-x86_64-unknown-linux-gnu
67+
path: ${{ steps.package.outputs.archive }}
68+
69+
build-aarch64:
70+
name: Build (aarch64)
71+
runs-on: ubuntu-latest
72+
container:
73+
image: debian:bookworm-slim
74+
steps:
75+
- name: Install base dependencies
76+
shell: bash
77+
run: |
78+
set -euo pipefail
79+
export DEBIAN_FRONTEND=noninteractive
80+
dpkg --add-architecture arm64
81+
apt-get update
82+
apt-get install -y --no-install-recommends \
83+
ca-certificates \
84+
pkg-config \
85+
libssl-dev \
86+
libssl-dev:arm64 \
87+
gcc-aarch64-linux-gnu \
88+
libc6-dev-arm64-cross \
89+
build-essential \
90+
git \
91+
curl \
92+
unzip
93+
94+
- name: Check out repository
95+
uses: actions/checkout@v4
96+
97+
- name: Set up Rust toolchain
98+
uses: dtolnay/rust-toolchain@stable
99+
100+
- name: Add Rust target
101+
run: rustup target add aarch64-unknown-linux-gnu
102+
103+
- name: Build binary
104+
env:
105+
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
106+
AR_aarch64_unknown_linux_gnu: aarch64-linux-gnu-ar
107+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
108+
PKG_CONFIG_ALLOW_CROSS: "1"
109+
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
110+
run: cargo build --release --target aarch64-unknown-linux-gnu
111+
112+
- name: Package release artifact
113+
id: package
114+
shell: bash
115+
run: |
116+
set -euo pipefail
117+
binary_path="target/aarch64-unknown-linux-gnu/release/echokit_server"
118+
if [ ! -f "$binary_path" ]; then
119+
echo "Release binary not found at $binary_path" >&2
120+
exit 1
121+
fi
122+
123+
archive_name="echokit_server-${{ github.ref_name }}-aarch64-unknown-linux-gnu.tar.gz"
124+
mkdir -p dist
125+
cp "$binary_path" dist/echokit_server
126+
chmod +x dist/echokit_server
127+
tar -C dist -czf "$archive_name" echokit_server
128+
rm dist/echokit_server
129+
130+
echo "archive=$archive_name" >> "$GITHUB_OUTPUT"
131+
132+
- name: Upload artifact
133+
uses: actions/upload-artifact@v4
134+
with:
135+
name: echokit_server-aarch64-unknown-linux-gnu
136+
path: ${{ steps.package.outputs.archive }}
137+
138+
release:
139+
name: Publish release
140+
runs-on: ubuntu-latest
141+
needs:
142+
- build-x86_64
143+
- build-aarch64
144+
steps:
145+
- name: Download artifacts
146+
uses: actions/download-artifact@v4
147+
with:
148+
pattern: echokit_server-*
149+
path: artifacts
150+
merge-multiple: true
151+
152+
- name: Prepare release files
153+
id: prepare
154+
shell: bash
155+
run: |
156+
set -euo pipefail
157+
mapfile -t archives < <(find artifacts -name '*.tar.gz' -type f)
158+
if [ "${#archives[@]}" -eq 0 ]; then
159+
echo "No release archives found" >&2
160+
exit 1
161+
fi
162+
{
163+
echo "files<<EOF"
164+
for archive in "${archives[@]}"; do
165+
echo "$archive"
166+
done
167+
echo "EOF"
168+
} >> "$GITHUB_OUTPUT"
169+
170+
- name: Publish GitHub release
171+
uses: softprops/action-gh-release@v1
172+
with:
173+
tag_name: ${{ github.ref_name }}
174+
name: Echokit Server ${{ github.ref_name }}
175+
files: ${{ steps.prepare.outputs.files }}
176+
env:
177+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

docker/server-vad/Dockerfile

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# syntax=docker/dockerfile:1.6
2+
3+
FROM debian:bookworm-slim
4+
5+
WORKDIR /app
6+
7+
ARG TARGETPLATFORM
8+
ARG ECHOKIT_VERSION=0.1.0
9+
ARG VAD_VERSION=0.1.0
10+
11+
RUN set -eux; \
12+
apt-get update; \
13+
apt-get install -y --no-install-recommends \
14+
bash \
15+
ca-certificates \
16+
curl \
17+
unzip \
18+
libgomp1 \
19+
libssl3; \
20+
if [ "${TARGETPLATFORM}" = "linux/arm64" ]; then \
21+
apt-get install -y --no-install-recommends \
22+
libopenblas-dev \
23+
libgomp1; \
24+
fi; \
25+
rm -rf /var/lib/apt/lists/*
26+
27+
RUN set -eux; \
28+
case "${TARGETPLATFORM}" in \
29+
"linux/amd64") \
30+
curl -fsSL "https://download.pytorch.org/libtorch/cpu/libtorch-cxx11-abi-shared-with-deps-2.4.0%2Bcpu.zip" -o /tmp/libtorch.zip; \
31+
unzip /tmp/libtorch.zip -d /usr/local/lib; \
32+
rm /tmp/libtorch.zip; \
33+
;; \
34+
"linux/arm64") \
35+
curl -fsSL "https://github.com/second-state/libtorch-releases/releases/download/v2.4.0/libtorch-cxx11-abi-aarch64-2.4.0.tar.gz" -o /tmp/libtorch.tar.gz; \
36+
tar -xzf /tmp/libtorch.tar.gz -C /usr/local/lib; \
37+
mv /usr/local/lib/torch /usr/local/lib/libtorch; \
38+
rm /tmp/libtorch.tar.gz; \
39+
;; \
40+
*) echo "Unsupported TARGETPLATFORM: ${TARGETPLATFORM}" >&2; exit 1 ;; \
41+
esac; \
42+
if [ -f /usr/local/lib/libtorch/lib/libnvrtc-builtins.so ]; then \
43+
ln -sf /usr/local/lib/libtorch/lib/libnvrtc-builtins.so /usr/local/lib/libtorch/lib/libnvrtc-builtins.so.12.4; \
44+
fi
45+
46+
RUN set -eux; \
47+
case "${TARGETPLATFORM}" in \
48+
"linux/amd64") ARCH_SUFFIX="x86_64-unknown-linux-gnu" ;; \
49+
"linux/arm64") ARCH_SUFFIX="aarch64-unknown-linux-gnu" ;; \
50+
*) echo "Unsupported TARGETPLATFORM: ${TARGETPLATFORM}" >&2; exit 1 ;; \
51+
esac; \
52+
tmpdir="$(mktemp -d)"; \
53+
archive="echokit_server-v${ECHOKIT_VERSION}-${ARCH_SUFFIX}.tar.gz"; \
54+
url="https://github.com/second-state/echokit_server/releases/download/v${ECHOKIT_VERSION}/${archive}"; \
55+
curl -fsSL -o /tmp/echokit_server.tar.gz "${url}"; \
56+
tar -xzf /tmp/echokit_server.tar.gz -C "$tmpdir"; \
57+
bin_path="$(find "$tmpdir" -type f -name 'echokit_server' -print -quit)"; \
58+
test -n "$bin_path"; \
59+
install -m 0755 "$bin_path" /usr/local/bin/echokit_server; \
60+
rm -rf "$tmpdir" /tmp/echokit_server.tar.gz
61+
62+
RUN set -eux; \
63+
case "${TARGETPLATFORM}" in \
64+
"linux/amd64") ARCH_SUFFIX="x86_64-unknown-linux-gnu" ;; \
65+
"linux/arm64") ARCH_SUFFIX="aarch64-unknown-linux-gnu" ;; \
66+
*) echo "Unsupported TARGETPLATFORM: ${TARGETPLATFORM}" >&2; exit 1 ;; \
67+
esac; \
68+
tmpdir="$(mktemp -d)"; \
69+
archive="silero_vad_server-v${VAD_VERSION}-${ARCH_SUFFIX}.tar.gz"; \
70+
url="https://github.com/second-state/silero_vad_server/releases/download/v${VAD_VERSION}/${archive}"; \
71+
curl -fsSL -o /tmp/silero_vad_server.tar.gz "${url}"; \
72+
tar -xzf /tmp/silero_vad_server.tar.gz -C "$tmpdir"; \
73+
vad_bin="$(find "$tmpdir" -type f -name 'silero_vad_server' -print -quit)"; \
74+
test -n "$vad_bin"; \
75+
install -m 0755 "$vad_bin" /usr/local/bin/silero_vad_server; \
76+
rm -rf "$tmpdir" /tmp/silero_vad_server.tar.gz
77+
78+
RUN curl -fsSL "https://github.com/second-state/silero_vad_server/raw/refs/tags/v${VAD_VERSION}/silero_vad.jit" -o /app/silero_vad.jit
79+
80+
ENV LD_LIBRARY_PATH=/usr/local/lib/libtorch/lib:$LD_LIBRARY_PATH
81+
ENV LIBTORCH=/usr/local/lib/libtorch
82+
83+
COPY config.toml .
84+
COPY hello.wav .
85+
86+
RUN cat <<'EOF' > /usr/local/bin/start_servers.sh \
87+
&& chmod +x /usr/local/bin/start_servers.sh
88+
#!/usr/bin/env bash
89+
set -euo pipefail
90+
91+
vad_pid=""
92+
server_pid=""
93+
94+
terminate() {
95+
if [[ -n "$vad_pid" ]]; then
96+
kill -TERM "$vad_pid" 2>/dev/null || true
97+
fi
98+
if [[ -n "$server_pid" ]]; then
99+
kill -TERM "$server_pid" 2>/dev/null || true
100+
fi
101+
}
102+
103+
silero_vad_server &
104+
vad_pid=$!
105+
106+
echokit_server config.toml &
107+
server_pid=$!
108+
109+
trap terminate SIGINT SIGTERM EXIT
110+
111+
wait -n "$vad_pid" "$server_pid" || true
112+
terminate
113+
wait || true
114+
EOF
115+
116+
ENV RUST_LOG=info
117+
118+
CMD ["/usr/local/bin/start_servers.sh"]

docker/server-vad/README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# echokit-server + Silero VAD Docker Image
2+
3+
This directory provides a single-stage runtime image that launches both `echokit_server` and `silero_vad_server` inside the same container.
4+
5+
- **Runtime image** (`debian:bookworm-slim`): installs the runtime dependencies (adding `libopenblas` on arm64), selects the appropriate `libtorch` archive for the target architecture, and downloads the `v0.1.0` release binaries for `echokit_server`, `silero_vad_server`, and the `silero_vad.jit` model.
6+
- **Supervisor script**: `/usr/local/bin/start_servers.sh` starts both services, relays signals, and keeps the container alive while either process is running.
7+
8+
## Run
9+
10+
Expose the application ports and mount your configuration plus a writable recordings directory:
11+
12+
```sh
13+
docker run --rm \
14+
-p 8080:8080 \
15+
-v $(pwd)/config.toml:/app/config.toml \
16+
-v $(pwd)/record:/app/record \
17+
secondstate/echokit:latest-server-vad
18+
```
19+
20+
Mount your `config.toml` directly into `/app/config.toml`. If you need to override additional assets such as `silero_vad.jit`, mount each file individually alongside the config. The servers write generated artifacts to `/app/record`, so ensure the local `record` directory exists and is writable. The container sets `RUST_LOG=info` and runs `start_servers.sh` by default, keeping both services available on ports `8080` and `8000` inside the container.
21+
22+
The VAD server listens on port `8000` internally. Choose one of the following so `echokit_server` talks to it correctly without publishing the VAD port to the host:
23+
24+
1. Update your mounted `config.toml` so `vad_url` and `vad_realtime_url` point to `http://localhost:8000` / `ws://localhost:8000`.
25+
2. Keep the default config (`9093`) and add `-e VAD_LISTEN=9093` to the `docker run` command so the VAD server binds that port inside the container.
26+
27+
## Build
28+
29+
```sh
30+
docker build -t secondstate/echokit:latest-server-vad .
31+
```
32+
33+
The build automatically detects `TARGETPLATFORM` and pulls the matching release artifacts (x86_64 or arm64). Override the downloaded releases by supplying `--build-arg ECHOKIT_VERSION=<version>` or `--build-arg VAD_VERSION=<version>` if you want a different tag.
34+
35+
## Multi-platform build
36+
37+
Use Buildx to produce and publish a multi-arch manifest in one command. BuildKit injects `TARGETPLATFORM` (`linux/amd64`, `linux/arm64`, etc.), so you do not need to set them manually.
38+
39+
```sh
40+
docker buildx build \
41+
--platform linux/amd64,linux/arm64 \
42+
--build-arg ECHOKIT_VERSION=0.1.0 \
43+
--build-arg VAD_VERSION=0.1.0 \
44+
-t secondstate/echokit:latest-server-vad \
45+
.
46+
```
47+
48+
Adjust the build arguments as needed; omit them to fall back to the defaults baked into the Dockerfile.
49+
50+
## Publish
51+
52+
```sh
53+
docker login
54+
docker push secondstate/echokit:latest-server-vad
55+
```

docker/server-vad/config.toml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
addr = "0.0.0.0:8080"
2+
hello_wav = "hello.wav"
3+
4+
[tts]
5+
platform = "Groq"
6+
model = "playai-tts"
7+
api_key = "gsk_ABCD"
8+
voice = "Fritz-PlayAI"
9+
10+
[asr]
11+
url = "https://api.groq.com/openai/v1/audio/transcriptions"
12+
api_key = "gsk_ABCD"
13+
model = "whisper-large-v3"
14+
lang = "en"
15+
prompt = "Hello\n你好\n(noise)\n(bgm)\n(silence)\n"
16+
# Requires a local Silero VAD server: https://github.com/second-state/silero_vad_server
17+
# Default port is 8000, but some setups may use 9093. Update the port below if needed.
18+
vad_realtime_url = "ws://localhost:8000/v1/audio/realtime_vad"
19+
20+
[llm]
21+
llm_chat_url = "https://api.groq.com/openai/v1/chat/completions"
22+
api_key = "gsk_ABCD"
23+
model = "openai/gpt-oss-20b"
24+
history = 5
25+
26+
[[llm.sys_prompts]]
27+
role = "system"
28+
content = """
29+
You are a helpful assistant. Answer truthfully and concisely.
30+
"""

docker/server-vad/hello.wav

16.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)