juli 07465241dc Fix config.init install path: parent of Robot_Data/, not Robot_Data/
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
2026-07-14 05:19:42 +02:00
2026-07-14 04:28:36 +02:00

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

  1. What this is
  2. What this is not
  3. Install
  4. Run
  5. Use in MyRobot
  6. Tests
  7. Security
  8. Troubleshooting

What this is

  • A Python service (server.py) using FastAPI + Uvicorn.
  • It listens on 127.0.0.1:8000 and answers three Chatterbox-shaped endpoints:
    • GET / → health
    • GET /api/voices → voice list
    • POST /v1/audio/speech → synthesize to WAV
    • POST /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 Host option 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):

  • fastapi
  • uvicorn
  • httpx
  • omnivoice-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:

  1. Install the official enableOfflineAI config flip. The dev ships a 20-byte mod on Patreon that just sets one flag. The game reads GameFolder() + "/config.init" at startup — GameFolder() is UnityEngine.Application.dataPath, which is the directory containing Robot.exe (the parent of Robot_Data/, NOT Robot_Data/ itself). A copy is bundled at contrib/self-host-mod/:

    cp contrib/self-host-mod/config.init \
       ~/.local/share/Steam/steamapps/common/MyRobot/config.init
    

    The game uses ReadConfigVariable("enableOfflineAI", "enableOfflineAI") == "true" to gate the offline AI flow (verified via Assembly-CSharp.dll disassembly — get_enableOfflineAI calls File.ReadAllLines(<Application.dataPath>/config.init) and checks the value).

  2. 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-bridge
    

    Restore later by renaming them back.

  3. 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 up user.reg first.

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

  1. Launch the game.

  2. Press Esc to pause.

  3. Open AI Setup from the pause menu.

  4. Select the Text to Speech tab.

  5. 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.)

  6. A Server Address input appears. Type:

    http://127.0.0.1:8000
    

    The field validates on Enter — if it turns green the bridge is reachable, red means wrong URL or the bridge isn't running.

  7. 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_KEY or --api-key-file. It is never echoed in responses, logs, or errors.
  • Default bind address is 127.0.0.1. Do not bind to 0.0.0.0 unless 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

Description
Chatterbox-TTS-Server-compatible REST adapter that proxies MyRobot TTS calls to a HASS OmniVoice endpoint.
Readme 62 KiB
Languages
Python 100%