Agent guide · rapid-mlx 0.16.2 · ← Back to README

OpenHands

OpenHands CLI runs an agent with local tools. Its 1.x CLI can run without Docker; the full GUI server uses Docker. rapid-mlx supplies its model through OpenHands' OpenAI-compatible LiteLLM transport.

Quick start

Needs: Python 3.12+, uv, and a server (rapid-mlx serve qwen3.5-4b-4bit).

$ export LLM_BASE_URL=http://localhost:8000/v1
$ export LLM_API_KEY=not-needed
$ export LLM_MODEL=openai/qwen3.5-4b-4bit
$ openhands --override-with-envs --headless --always-approve -t "List this directory"

--override-with-envs is required: OpenHands 1.x otherwise ignores the exported values. The openai/ prefix selects LiteLLM's OpenAI-compatible transport. These explicit exports work with the current CLI.

Wire: LiteLLM → /v1/chat/completions · Setup: env vars LLM_BASE_URL / LLM_API_KEY / LLM_MODEL · Matrix cell (pinned OpenHands 0.9.0 Docker image): ✅ ✅ ✅ XFAIL (Qwen3.6 / Gemma 4 / DeepSeek pass; gpt-oss is XFAIL (format) — its native harmony output never emits the <execute_bash> text-action tags CodeActAgent parses, an upstream OpenHands gap #15167).

Install

$ uv tool install openhands --python 3.12
$ openhands --version

Config

Export these values for the model the server reports. The agents openhands --setup command prints a bare model ID; add openai/ when exporting LLM_MODEL. OpenHands' saved CLI settings live at ~/.openhands/agent_settings.json:

$ export LLM_BASE_URL=http://localhost:8000/v1
$ export LLM_API_KEY=not-needed
$ export LLM_MODEL=openai/qwen3.5-4b-4bit

Run

$ rapid-mlx serve qwen3.5-4b-4bit
$ LLM_BASE_URL=http://localhost:8000/v1 \
  LLM_API_KEY=not-needed \
  LLM_MODEL=openai/qwen3.5-4b-4bit \
  openhands --override-with-envs --headless --always-approve -t "List this directory"

Recommended aliases (per rapid-mlx agents openhands): qwen3.5-9b-4bit, qwen3.6-35b-4bit, qwen3.5-4b-4bit.

Docker E2E harness

The integration test tests/integrations/test_openhands.sh drives the pinned OpenHands 0.9.0 app + runtime images against a running rapid-mlx serve. These historical cells do not verify the current 1.x CLI:

# pinned by manifest-list digest so a moved tag can't silently swap the image
OPENHANDS_IMAGE="ghcr.io/all-hands-ai/openhands:0.9.0@sha256:d4b028…"
OPENHANDS_RUNTIME_IMAGE="ghcr.io/all-hands-ai/runtime:od_v0.9.0_…"

$ docker run --rm \
    -e SANDBOX_CONTAINER_IMAGE="$OPENHANDS_RUNTIME_IMAGE" \
    -e LLM_BASE_URL="http://host.docker.internal:8000/v1" \
    -e LLM_MODEL="openai/$MODEL" \
    -e LLM_API_KEY="rapidmlx" \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v "$WORKDIR:/opt/workspace_base" \
    --add-host host.docker.internal:host-gateway \
    "$OPENHANDS_IMAGE" \
    python -m openhands.core.main \
      -i 10 -d /opt/workspace_base \
      -t "Fix the bug in add.py — this function should add, not subtract."

When the LLM is running on the same Mac as Docker Desktop, localhost inside the container refers to the container itself. The harness rewrites the URL host to host.docker.internal so the container can reach the rapid-mlx server. Any non-loopback host (remote-serve node, RFC1918 IP, DNS name) is preserved as-is. To accept the container's request, bind the server on a reachable interface and include host.docker.internal (or the chosen remote name or IP) in --trusted-hosts. Include loopback names too if local clients connect, and use an API key.

Gotchas

Empirical

The 2026-07-07 pilot ran the harness across all four Tier-1 families with the pinned image pair. Family × wall-time (single cell, cached-image path): Qwen 3.5-4B-4bit 32.14 s (2 CodeAct steps: read → edit_file_by_replace → finish), Gemma-4-31B-4bit 47.87 s, DeepSeek R1-Distill-32B-4bit 72.08 s (long analysis-channel CoT before the edit action, still one-shot), gpt-oss-20B-MXFP4-Q8 XFAIL (harmony output emits no <execute_bash> text-action tag for CodeActAgent to parse — upstream OpenHands #15167, see the gotcha above). The three tool-emitting families satisfied the AST whitelist on add.py; gpt-oss stays strict-xfail.

See also