Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,7 @@ VectorSnapshots/
.DS_Store
Thumbs.db
desktop.ini

# ---Docker Configs and Volumes ---
vector-data/

126 changes: 126 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
FROM ubuntu:22.04

# Prevent interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive

# 1. Install system dependencies
RUN apt-get update && apt-get install -y \
curl \
git \
python3 \
python3-venv \
python3-pip \
unzip \
build-essential \
cmake \
avahi-daemon \
dbus \
sudo \
zstd \
pkg-config \
libopus-dev \
libopusfile-dev \
&& rm -rf /var/lib/apt/lists/*

# 2. Install Go 1.22.4
ENV GO_VERSION=1.22.4
RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" -o /tmp/go.tar.gz && \
tar -C /usr/local -xzf /tmp/go.tar.gz && \
rm /tmp/go.tar.gz
ENV PATH="/usr/local/go/bin:${PATH}"

# 3. Install Ollama
RUN curl -fsSL https://ollama.com/install.sh | sh

# 4. Set up the working directory and copy project files
WORKDIR /root/vector-pod
COPY . /root/vector-intelligence

# 5. Build Wire-Pod (Chipper)
ENV WIREPOD_REPO="https://github.com/kercre123/wire-pod"
ENV WIREPOD_DIR="/root/vector-pod/wire-pod"
ENV WIREPOD_COMMIT="11e7b22095166ed35765e88a8a10ed3a6ce49d5c"
ENV WHISPER_COMMIT="60cd96acff3a72895cb9ae9cbabe9de21b1e9125"
ENV SDK_COMMIT="62168f3595d67ae0bf24103a9fe1fc5f2eb9b85c"

RUN git clone "$WIREPOD_REPO" "$WIREPOD_DIR" && \
cd "$WIREPOD_DIR" && git checkout -q "$WIREPOD_COMMIT"

# Apply Python-based patches to Wire-Pod
RUN cp /root/vector-intelligence/shared/config/wirepod-intents-en-US.json $WIREPOD_DIR/chipper/intent-data/en-US.json && \
sed -i -E 's|inactiveNumMax := (23\|150\|100\|75)[^\r\n]*|inactiveNumMax := 75 // 1.5s of silence|' $WIREPOD_DIR/chipper/pkg/wirepod/speechrequest/speechrequest.go && \
python3 /root/vector-intelligence/shared/patches/expand-animations.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_cmds.go && \
python3 /root/vector-intelligence/shared/patches/wake-word-grace-period.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_interrupt.go && \
python3 /root/vector-intelligence/shared/patches/add-button-interrupt.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_interrupt.go && \
python3 /root/vector-intelligence/shared/patches/wake-word-mute-during-getimage.py $WIREPOD_DIR && \
python3 /root/vector-intelligence/shared/patches/add-ondemand-face.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_interrupt.go && \
python3 /root/vector-intelligence/shared/patches/remove-photo-countdown.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_cmds.go && \
python3 /root/vector-intelligence/shared/patches/use-builtin-behaviors.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_cmds.go && \
python3 /root/vector-intelligence/shared/patches/prelim-lookatme-then-llm.py $WIREPOD_DIR/chipper/pkg/wirepod/preqs/intent_graph.go && \
python3 /root/vector-intelligence/shared/patches/slow-tts.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_cmds.go && \
python3 /root/vector-intelligence/shared/patches/add-eye-color-cmd.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim_cmds.go && \
python3 /root/vector-intelligence/shared/patches/add-sensor-reactions.py $WIREPOD_DIR && \
python3 /root/vector-intelligence/shared/patches/fix-connection-leak.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/kgsim.go && \
python3 /root/vector-intelligence/shared/patches/fix-saytext-stream-leak.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/bcontrol.go && \
python3 /root/vector-intelligence/shared/patches/fix-name-extraction.py $WIREPOD_DIR/chipper/pkg/wirepod/ttr/intentparam.go && \
python3 /root/vector-intelligence/shared/patches/add-face-probe.py $WIREPOD_DIR && \
python3 /root/vector-intelligence/shared/patches/add-ambient-loop.py $WIREPOD_DIR && \
sed -i 's/vars.APIConfig.Server.EPConfig && //g' $WIREPOD_DIR/chipper/pkg/initwirepod/startserver.go

# Patched vector-go-sdk
RUN git clone "https://github.com/fforchino/vector-go-sdk" "$WIREPOD_DIR/chipper/third_party/vector-go-sdk" && \
cd "$WIREPOD_DIR/chipper/third_party/vector-go-sdk" && git checkout -q "$SDK_COMMIT" && \
python3 /root/vector-intelligence/shared/patches/add-sdk-close.py "$WIREPOD_DIR/chipper/third_party/vector-go-sdk/pkg/vector/vector.go" && \
cd "$WIREPOD_DIR/chipper" && echo "\nreplace github.com/fforchino/vector-go-sdk => ./third_party/vector-go-sdk\n" >> go.mod

# Build Whisper.cpp and chipper-whisper
ARG WHISPER_MODEL="base.en"
ENV WHISPER_MODEL="${WHISPER_MODEL}"
ENV WHISPER_REPO="$WIREPOD_DIR/whisper.cpp"
RUN git clone https://github.com/kercre123/whisper.cpp.git "$WHISPER_REPO" && \
cd "$WHISPER_REPO" && git checkout -q "$WHISPER_COMMIT" && \
cmake -B build_go -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DWHISPER_BUILD_EXAMPLES=OFF -DWHISPER_BUILD_TESTS=OFF && \
cmake --build build_go --config Release -j 4

RUN curl -L -o "$WHISPER_REPO/models/ggml-${WHISPER_MODEL}.bin" "https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${WHISPER_MODEL}.bin"

# Install libwhisper and ggml globally so the executable can find it at runtime without LD_LIBRARY_PATH
RUN cp $WHISPER_REPO/build_go/src/libwhisper.so /usr/local/lib/ && \
cp $WHISPER_REPO/build_go/ggml/src/libggml*.so /usr/local/lib/ && \
ldconfig

RUN cd "$WIREPOD_DIR/chipper" && \
CGO_CFLAGS="-I$WHISPER_REPO/include -I$WHISPER_REPO/ggml/include" \
CGO_LDFLAGS="-L/usr/local/lib -lwhisper -lggml" \
go build -o chipper-whisper ./cmd/experimental/whisper.cpp

# 5b. Cross-compile the mDNS reflector for Windows
# This small .exe runs natively on Windows to bridge mDNS (multicast)
# between the Docker container and the physical LAN — working around
# WSL2's inability to forward multicast traffic.
RUN cd "$WIREPOD_DIR/chipper" && \
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 \
go build -o /root/vector-pod/windows-mdns.exe \
/root/vector-intelligence/shared/mdns-reflector.go


# 6. Set up vector-ai python environment
ENV VECTORAI_DIR="/root/vector-pod/vector-ai"
RUN mkdir -p "$VECTORAI_DIR" && \
cp /root/vector-intelligence/shared/vector-ai/service.py "$VECTORAI_DIR/service.py" && \
cp /root/vector-intelligence/shared/vector-ai/memory.py "$VECTORAI_DIR/memory.py" && \
cp /root/vector-intelligence/shared/vector-ai/requirements.txt "$VECTORAI_DIR/requirements.txt" && \
cp /root/vector-intelligence/shared/supervisor.py "/root/vector-pod/supervisor.py" && \
cp /root/vector-intelligence/shared/vector-ai/.env "$VECTORAI_DIR/.env" && \
cp /root/vector-intelligence/shared/vector-ai/persona.txt "$VECTORAI_DIR/persona.txt"

RUN python3 -m venv "$VECTORAI_DIR/venv" && \
"$VECTORAI_DIR/venv/bin/pip" install --upgrade pip && \
"$VECTORAI_DIR/venv/bin/pip" install -r "$VECTORAI_DIR/requirements.txt"

# Provide entrypoint
COPY docker-entrypoint.sh /root/docker-entrypoint.sh
RUN chmod +x /root/docker-entrypoint.sh

# The entrypoint will start dbus/avahi and then supervisor.py
ENTRYPOINT ["/root/docker-entrypoint.sh"]
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,61 @@ bash apply-wirepod-config.sh # apply the AI config
# pair Vector via the Robots tab
```

### Docker (Windows & Linux)

You can run the entire stack (Wire-Pod, vector-ai, and Ollama) using Docker via the included `docker-compose.yml`.

#### Windows Setup (Docker Desktop)
The most reliable way to run this stack on Windows is using **Docker Desktop** with the WSL2 backend.

**Important:** Docker on Windows (WSL2) cannot forward mDNS multicast traffic
to the physical LAN. The build automatically cross-compiles a tiny
`windows-mdns.exe` that runs natively on Windows and bridges this gap — Vector
discovers the server through it.

1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/) (ensure the WSL2 backend is selected).
2. Build and start:
```bash
docker compose build
docker compose up -d
```
3. On the **very first start**, the container exports `windows-mdns.exe` into
your `./vector-data/` folder. Run it (double-click or from a terminal):
```powershell
.\vector-data\windows-mdns.exe
```
Keep this window open — it advertises `escapepod.local` on your LAN so
Vector can find the Docker container. It also auto-detects Vector's IP
changes and updates `botSdkInfo.json`.
4. **Initial Bot Setup & Authentication Note (Windows Docker):**
Performing initial bot pairing and authentication wizard directly through the web UI in Docker Desktop on Windows has known limitations. The recommended onboarding path on Windows is:
- Download the official native [Wire-Pod for Windows](https://github.com/kercre123/wire-pod).
- Run native Wire-Pod once to complete initial setup and authenticate/pair Vector.
- Copy the generated folders `session-certs`, `certs`, and `jdocs` from `C:\Users\<your-user>\AppData\Roaming\wire-pod` directly into the `./vector-data/` folder of this Docker project.
5. **Cleaning Windows Firewall Rules (Optional):**
On first execution, `windows-mdns.exe` prompts for Windows Defender Firewall permission so it can answer mDNS queries on UDP 5353. If you ever uninstall the project and want to clean up those firewall rules, run the provided script in PowerShell (as Administrator):
```powershell
.\shared\scripts\remove-windows-mdns-rule.ps1
```
Or execute: `Remove-NetFirewallRule -DisplayName "windows-mdns"`

#### Linux Setup
On Linux, Docker can access the host network directly — no mDNS reflector binary needed.
1. In `docker-compose.yml`, uncomment `network_mode: "host"` and comment out the `ports:` block.
2. In host network mode, Docker shares the host's physical network adapter (`eth0`/`wlan0`) directly. The internal `zeroconf` service inside the container advertises `escapepod.local` across your physical LAN natively.
3. Ensure the NVIDIA Container Toolkit is installed so Docker can access your GPU:
- [Official NVIDIA Container Toolkit install guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).

#### Customizing the Whisper Speech Model
By default, the Docker image builds with the English base model (`base.en`). You can change the model size (e.g. `tiny.en`, `base.en`, `small.en`, `medium.en`) when building or running Docker:

- Pass `WHISPER_MODEL` when building or running `docker compose`:
```bash
WHISPER_MODEL=small.en docker compose build --no-cache
WHISPER_MODEL=small.en docker compose up -d
```


---

## Daily operation
Expand Down
34 changes: 34 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
services:
vector-intelligence:
build:
context: .
args:
WHISPER_MODEL: ${WHISPER_MODEL:-base.en}
container_name: vector-intelligence
ports:
- "80:80"
- "443:443"
- "8080:8080"
- "8084:8084"
- "8090:8090"
- "11434:11434"

environment:
- WHISPER_MODEL=${WHISPER_MODEL:-base.en}
- OLLAMA_MAIN_MODEL=${OLLAMA_MAIN_MODEL:-gemma4:e4b}
- OLLAMA_MODEL=${OLLAMA_MAIN_MODEL:-gemma4:e4b}
- OLLAMA_SUMMARY_MODEL=${OLLAMA_SUMMARY_MODEL:-gemma4:e2b}
volumes:
- ollama_data:/root/.ollama
- ./vector-data:/user-data
restart: unless-stopped
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]

volumes:
ollama_data:
106 changes: 106 additions & 0 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash
set -e

echo "Starting dbus and avahi-daemon for mDNS..."
mkdir -p /var/run/dbus

# Disable the supervisor's Python-level mDNS — on Docker/Windows, the native
# windows-mdns.exe handles LAN mDNS. Without this, the supervisor would
# advertise the container's internal 172.x IP, confusing Vector.
export DISABLE_SUPERVISOR_MDNS="true"
export WHISPER_MODEL="${WHISPER_MODEL:-base.en}"

# Remove old PID if restarting container
rm -f /run/dbus/pid
dbus-daemon --system
# avahi-daemon disabled — on Docker/Windows, windows-mdns.exe handles mDNS.
# avahi would broadcast the container's 172.x IP, confusing Vector.
# avahi-daemon -D

# Safely manage user data in the bind mount
mkdir -p /user-data/jdocs

# Provide the Windows mDNS reflector .exe on first run
# (pre-compiled during Docker build for Windows users)
if [ -f /root/vector-pod/windows-mdns.exe ] && [ ! -f /user-data/windows-mdns.exe ]; then
cp /root/vector-pod/windows-mdns.exe /user-data/windows-mdns.exe
echo "Exported windows-mdns.exe to /user-data/ (run it on Windows to bridge mDNS)"
fi


# Vector-AI persona
if [ ! -f /user-data/persona.txt ]; then
cp /root/vector-pod/vector-ai/persona.txt /user-data/persona.txt
fi
rm -f /root/vector-pod/vector-ai/persona.txt
ln -s /user-data/persona.txt /root/vector-pod/vector-ai/persona.txt

# Wire-Pod botSdkInfo.json
if [ -f /root/vector-pod/wire-pod/chipper/jdocs/botSdkInfo.json ] && [ ! -f /user-data/jdocs/botSdkInfo.json ]; then
cp /root/vector-pod/wire-pod/chipper/jdocs/botSdkInfo.json /user-data/jdocs/botSdkInfo.json
fi
rm -rf /root/vector-pod/wire-pod/chipper/jdocs
ln -s /user-data/jdocs /root/vector-pod/wire-pod/chipper/jdocs

# Import certs if provided
if [ -d /user-data/certs ]; then
rm -rf /root/vector-pod/wire-pod/certs /root/vector-pod/wire-pod/chipper/certs
ln -s /user-data/certs /root/vector-pod/wire-pod/certs
ln -s /user-data/certs /root/vector-pod/wire-pod/chipper/certs
fi

# Import old session-certs if provided
if [ -d /user-data/session-certs ]; then
rm -rf /root/vector-pod/wire-pod/chipper/session-certs
ln -s /user-data/session-certs /root/vector-pod/wire-pod/chipper/session-certs
fi

# Import old botConfig.txt if provided
if [ -f /user-data/botConfig.txt ]; then
rm -f /root/vector-pod/wire-pod/chipper/botConfig.txt
ln -s /user-data/botConfig.txt /root/vector-pod/wire-pod/chipper/botConfig.txt
fi

# Persist apiConfig.json (Wire-Pod's main config with setup flags)
if [ ! -f /user-data/apiConfig.json ]; then
if [ -f /root/vector-intelligence/shared/config/wirepod-apiConfig.json ]; then
cp /root/vector-intelligence/shared/config/wirepod-apiConfig.json /user-data/apiConfig.json
elif [ -f /root/vector-pod/wire-pod/chipper/apiConfig.json ]; then
cp /root/vector-pod/wire-pod/chipper/apiConfig.json /user-data/apiConfig.json
fi
fi
rm -f /root/vector-pod/wire-pod/chipper/apiConfig.json
ln -s /user-data/apiConfig.json /root/vector-pod/wire-pod/chipper/apiConfig.json

# Vector-AI memory db
if [ ! -f /user-data/memory.db ]; then
touch /user-data/memory.db
fi
ln -sf /user-data/memory.db /root/vector-pod/vector-ai/memory.db

# Set default values if empty
OLLAMA_MAIN_MODEL=${OLLAMA_MAIN_MODEL:-"gemma3:12b"}
OLLAMA_SUMMARY_MODEL=${OLLAMA_SUMMARY_MODEL:-"llama3.2:3b"}


echo "Ensuring Ollama models ($OLLAMA_MAIN_MODEL, $OLLAMA_SUMMARY_MODEL) are downloaded..."
# Start Ollama temporarily to pull models
ollama serve > /dev/null 2>&1 &
OLLAMA_PID=$!

echo "Waiting for Ollama to start..."
until curl -s http://127.0.0.1:11434 > /dev/null; do
sleep 1
done

echo "Pulling models..."
ollama pull "$OLLAMA_MAIN_MODEL"
ollama pull "$OLLAMA_SUMMARY_MODEL"

# Stop temporary Ollama process so supervisor can manage it
kill $OLLAMA_PID
wait $OLLAMA_PID 2>/dev/null || true

echo "Starting Vector Pod Supervisor..."
cd /root/vector-pod
exec /root/vector-pod/vector-ai/venv/bin/python supervisor.py
8 changes: 5 additions & 3 deletions shared/config/wirepod-apiConfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
"temp": 1.2
},
"STT": {
"provider": "vosk",
"provider": "whisper.cpp",
"language": "en-US"
},
"server": {
"epconfig": true,
"epconfig": false,
"port": "443"
}
},
"hasreadfromenv": false,
"pastinitialsetup": true
}
Loading