Disassembled Assembly-CSharp.dll:
* get_enableOfflineAI calls ReadConfigVariable('enableOfflineAI', ...)
* ReadConfigVariable does File.ReadAllLines(GameFolder() + '/config.init')
* GameFolder() = UnityEngine.Application.dataPath, which is the dir
containing Robot.exe (parent of Robot_Data/), NOT Robot_Data/ itself
The Patreon SELF HOST mod.zip puts the file at <install>/Robot_Data/, but
the binary actually reads <install>/config.init. Move it (or copy it to
both, the binary only looks at the parent).
Verified strings:
* US@0x1aeb1 = 'Reading '
* US@0x1aec3 = ' from '
* US@0x1aed1 = '/config.init'
* MemberRef row 2302 = UnityEngine.Application.get_dataPath
OmniVoice ↔ Chatterbox Bridge
A small FastAPI adapter that exposes a Chatterbox-TTS-Server-shaped REST API
on http://127.0.0.1:8000 and proxies every request to a HASS OmniVoice
daemon on your LAN.
It exists so that MyRobot's in-game AI Setup → Text to Speech → Self Host
mode can drive an OmniVoice TTS endpoint without installing the real
Chatterbox Python stack (which is heavy and needs a CUDA-capable GPU).
MyRobot ──http://127.0.0.1:8000──▶ bridge (this repo)
│
▼
http://192.168.1.106:7860 (OmniVoice)
Table of contents
What this is
- A Python service (
server.py) using FastAPI + Uvicorn. - It listens on
127.0.0.1:8000and answers three Chatterbox-shaped endpoints:GET /→ healthGET /api/voices→ voice listPOST /v1/audio/speech→ synthesize to WAVPOST /v1/audio/speech/stream→ synthesize to raw PCM s16le
- It translates each request into the OmniVoice API and ships the WAV back.
- The bearer token is read from
OMNIVOICE_API_KEY(env or--api-key-file) and never appears in logs or responses.
What this is not
- It does not generate text. You still need Ollama (or KoboldCPP) for that. MyRobot talks to Ollama directly.
- It does not run the actual Chatterbox model. There is no model loaded in this process; everything is forwarded to OmniVoice.
- It does not require Steam-side changes. MyRobot's
Self Hostoption is already compiled into the game; the bridge just speaks the dialect of API the game expects.
Install
1. Get the code
git clone https://git.autisme.monster/juli/omnivoice-chatterbox-bridge.git
cd omnivoice-chatterbox-bridge
2. Create a venv and install
python3 -m venv .venv
.venv/bin/pip install -e .
Requirements: Python ≥ 3.10. Dependencies (pulled in automatically):
fastapiuvicornhttpxomnivoice-agent-tts(the official client, kept around for direct CLI use)
3. Put your OmniVoice bearer token somewhere safe
mkdir -p ~/.config/omnivoice-chatterbox-bridge
umask 077
nano ~/.config/omnivoice-chatterbox-bridge/api.key # paste the key, save
chmod 600 ~/.config/omnivoice-chatterbox-bridge/api.key
4. (Optional) Install the systemd user service
The repo ships a unit at contrib/systemd/omnivoice-bridge.service. Edit
the ExecStart=, WorkingDirectory=, and Environment=OMNIVOICE_URL= lines
to match your setup, then:
mkdir -p ~/.config/systemd/user
cp contrib/systemd/omnivoice-chatterbox-bridge.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now omnivoice-chatterbox-bridge.service
systemctl --user status omnivoice-bridge.service
The bundled service is hard-coded to the current author's paths and
OMNIVOICE_URL=http://192.168.1.106:7860. Treat it as a starting point, not a drop-in.
Run
Foreground (handy for debugging):
OMNIVOICE_API_KEY=$(cat ~/.config/omnivoice-chatterbox-bridge/api.key) \
./.venv/bin/python server.py \
--host 127.0.0.1 \
--port 8000
Or via the bundled CLI tool that ships with omnivoice-agent-tts:
source .venv/bin/activate
omnivoice-tts health --json
omnivoice-tts voices --json
omnivoice-tts speak --voice sparkle -o /tmp/hello.wav 'Hi from the bridge.'
Environment variables
| Variable | Default | Purpose |
|---|---|---|
OMNIVOICE_URL |
http://192.168.1.106:7860 |
Upstream OmniVoice base URL |
OMNIVOICE_API_KEY |
(unset) | Bearer token for the upstream |
OMNIVOICE_VOICE |
sparkle |
Default voice when the game sends default |
OMNIVOICE_LANGUAGE |
en |
Default language code |
OMNIVOICE_TIMEOUT |
60 |
Per-request timeout in seconds |
BRIDGE_LOG_LEVEL |
info |
Uvicorn log level |
The API key can also be supplied at runtime via --api-key-file; the file
must be mode 600 and contain only the key.
Verify it works
curl -s http://127.0.0.1:8000/ # → {"status":"ok",...}
curl -s http://127.0.0.1:8000/api/voices # → {"voices":[...]}
curl -s -X POST http://127.0.0.1:8000/v1/audio/speech \
-H 'Content-Type: application/json' \
-d '{"input":"Hello robot.","voice":"sparkle"}' \
-o /tmp/test.wav
file /tmp/test.wav
# → RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, mono ...
Use in MyRobot
The game already ships the Self Host option (no Patreon mod needed for the
option to appear — that mod only adds extra polish).
Before first launch: stop the game from trying to install Ollama via Wine
MyRobot's AutoSelfHost feature ships with a copy of the Ollama Windows
installer and will happily run it under Proton if it cannot find Ollama
already running — even when you have native Ollama listening on
localhost:11434. The fix is three-fold:
-
Install the official
enableOfflineAIconfig flip. The dev ships a 20-byte mod on Patreon that just sets one flag. The game readsGameFolder() + "/config.init"at startup —GameFolder()isUnityEngine.Application.dataPath, which is the directory containingRobot.exe(the parent ofRobot_Data/, NOTRobot_Data/itself). A copy is bundled atcontrib/self-host-mod/:cp contrib/self-host-mod/config.init \ ~/.local/share/Steam/steamapps/common/MyRobot/config.initThe game uses
ReadConfigVariable("enableOfflineAI", "enableOfflineAI") == "true"to gate the offline AI flow (verified viaAssembly-CSharp.dlldisassembly —get_enableOfflineAIcallsFile.ReadAllLines(<Application.dataPath>/config.init)and checks the value). -
Hide the Wine Ollama so the game can't launch it.
cd ~/.local/share/Steam/steamapps/compatdata/<appid>/pfx/drive_c/users/steamuser/AppData/Local/Programs/Ollama mv ollama.exe ollama.exe.disabled-by-bridge mv 'ollama app.exe' 'ollama app.exe.disabled-by-bridge' mv unins000.exe unins000.exe.disabled-by-bridgeRestore later by renaming them back.
-
Mark setup steps complete in the Wine registry so the game's first-run wizard doesn't try to re-install anything:
contrib/write_myrobot_prefs.py \ ~/.local/share/Steam/steamapps/compatdata/<appid>/pfx/user.reg \ PreferredLLMService=0 \ PreferredLLMPort=11434 \ OverrideOllamaModel=0 \ OverrideKoboldModel=0 \ OverrideKoboldModelParams=0 \ TTSDepsInstalled=1 \ OllamaDepsInstalled=1 \ PipPackagesInstalled=1 \ PipToolsUpgraded=1 \ CondaTOSAccepted=1 \ PreferredTTSPort=8000 \ 'PreferredTTSServer="http://127.0.0.1:8000"'The script computes the djb2-xor hash Unity uses for PlayerPrefs keys on Windows and writes them in Wine's
hex:/dword:format. Back upuser.regfirst.
After this, the game should treat Ollama and TTS as already installed and
just connect to whatever is listening on 127.0.0.1:11434 (your native
Ollama) and 127.0.0.1:8000 (this bridge).
TTS — point at the bridge
-
Launch the game.
-
Press Esc to pause.
-
Open AI Setup from the pause menu.
-
Select the Text to Speech tab.
-
There is a Provider / Service dropdown. Pick Self Host. (If you don't see it, the dropdown is greyed out: in the pause menu open the Settings screen and look for the AI Setup tab there as well — on some builds the path is Pause → Settings → AI Setup → Text to Speech.)
-
A Server Address input appears. Type:
http://127.0.0.1:8000The field validates on Enter — if it turns green the bridge is reachable, red means wrong URL or the bridge isn't running.
-
Hit Save / close the menu. The game should now speak through OmniVoice.
If the bridge was started via the systemd user unit, it survives logout and reboots (after your first graphical login).
LLM — point at a remote Ollama
MyRobot's compiled DLL only stores an LLM port in PlayerPrefs; the
host is hard-coded to localhost. So when the game asks Ollama for text
generation it always talks to http://localhost:<port>.
If your Ollama lives on another machine (e.g. 192.168.1.106) you have two
options. Pick whichever fits.
Option A — type the full URL in-game
The AI Setup → Text Generation panel has its own Server Address field
that overrides the hard-coded localhost. After picking Self Host:
Server Address: http://192.168.1.106:11434
The game will talk straight to the remote Ollama. If the field is missing, update the game — the option was added in a recent build.
Option B — forward localhost:11434 to the remote Ollama
Run a tiny TCP forwarder that binds 127.0.0.1:11434 and proxies to the
remote box. This works on every MyRobot build because the game's hard-coded
localhost now actually reaches the remote Ollama.
sudo systemctl stop ollama # free port 11434 if local Ollama runs
socat TCP-LISTEN:11434,bind=127.0.0.1,reuseaddr,fork \
TCP:192.168.1.106:11434
For a permanent setup, copy contrib/systemd/ollama-forward.service to
~/.config/systemd/user/, edit the target host, and enable --now it.
Trade-off: Option A is one fewer moving part but requires the in-game field to exist. Option B is bulletproof but assumes you can stop your local Ollama (or aren't running one).
Tests
PYTHONPATH=. ./.venv/bin/python -m unittest discover -s tests -v
The suite covers health, voice passthrough, payload translation, default-voice remap, bearer-token forwarding, empty-input rejection, upstream 401 handling, and the streaming endpoint.
Security
- The OmniVoice bearer token is read from
OMNIVOICE_API_KEYor--api-key-file. It is never echoed in responses, logs, or errors. - Default bind address is
127.0.0.1. Do not bind to0.0.0.0unless the host is on a network you trust — anyone who can reach the bridge can ask OmniVoice to read arbitrary text aloud. - The bridge does not write to disk (no model downloads, no temp files, no caches).
- Treat any text handed to the bridge from external sources as untrusted agent output.
Troubleshooting
| Symptom | Likely cause |
|---|---|
GET / returns connection refused |
Bridge not running. systemctl --user status omnivoice-bridge |
/api/voices returns HTTP 502 with "omnivoice unreachable" |
OmniVoice daemon down or wrong host/port |
/v1/audio/speech returns HTTP 502 with "rejected api key" |
OMNIVOICE_API_KEY missing or wrong |
| MyRobot shows the Server Address field in red | Bridge not reachable from inside Proton. curl http://127.0.0.1:8000/ from a normal terminal first; if that works, the in-game field should turn green too. |
| Bridge works from terminal but MyRobot still speaks online | The game's Provider dropdown is still on Patreon / Steam / Player2, not Self Host. |
| Game says "Ollama not found" | For Option B: the forwarder isn't running, or local Ollama is still bound to 11434 (ss -tlnp | grep 11434). |
| 401 from OmniVoice despite a key | Token may have whitespace or wrong line endings. od -c ~/.config/omnivoice-chatterbox-bridge/api.key | head |