OpenHands

OpenHands is an open-source autonomous software agent. It reaches models through LiteLLM, so any OpenAI-compatible endpoint works once the model name carries the right prefix.

Verified against the OpenHands CLI 1.16.0. The config below is re-run against that release every week, including a tool call round trip. Upstream has stopped maintaining the standalone CLI; it still runs, and it is the only OpenHands entry point with a headless mode. The OpenHands web app takes the same three values (see Web app below).

Install

The CLI needs Python 3.12 or later:

Bash
uv tool install openhands==1.16.0 --python 3.12

Configure

Three environment variables describe the model:

Bash
export LLM_MODEL=openai/auto/mistralai/mistral-large-2512
export LLM_BASE_URL=https://api.lowrouter.ai/v1
export LLM_API_KEY=sk-lr-...

The CLI ignores them unless you pass --override-with-envs. Without the flag it reads the model from its saved settings in ~/.openhands/, and the variables have no effect. The override is not written back to those settings.

Bash
openhands --headless --override-with-envs -t "Summarise what this repository does"

--headless runs without the terminal UI and approves every action the agent takes, shell commands included. Run it in a directory, or a container, where that is acceptable. Drop --headless for the interactive UI.

Two details in the variables are easy to get wrong:

  • LLM_MODEL starts with openai/. That prefix tells LiteLLM to use its OpenAI-compatible client; LiteLLM strips it, and LowRouter receives auto/mistralai/mistral-large-2512 unchanged. Without the prefix LiteLLM does not recognise the provider and no request is sent.
  • LLM_BASE_URL ends in /v1. LiteLLM appends only /chat/completions, so the bare origin gives a 404.

Neither mistake shows up in the exit code. Headless mode exits 0 and prints its own system prompt as the “last message sent by the agent” when the model call failed. If the summary shows a system prompt, check the two variables above.

Picking a model

OpenHands works through function calling for every step: reading files, editing them, running commands. Pick a model with the function-calling tag. The model browser filtered to them lists every one.

Tasks run for many turns and resend the whole conversation on each one, so input tokens dominate the bill. The example uses auto/mistralai/mistral-large-2512, served from the EU. Models we have run through OpenHands’ tool loop:

  • auto/qwen/qwen3-coder-30b-a3b-instruct, a coding model served from the EU (128k context).
  • auto/anthropic/claude-haiku-4.5, a cheaper Anthropic model (200k context).

OpenHands sends model IDs containing gpt-5 or gpt-6 to the Responses API (/v1/responses) instead of Chat Completions. LowRouter serves both, so those models work without extra settings.

Cost tracking

OpenHands’ Conversation Metrics shows a per-conversation cost. On LowRouter the number comes from the response itself, so auto/… and alias/… models report it like any other:

  • Non-streaming calls carry an x-litellm-response-cost response header — the figure OpenHands’ metrics read. It is in USD and informational; your invoice is in your billing currency, and the x-lowrouter-response-cost + x-lowrouter-response-cost-currency headers carry that billed figure.
  • Streaming calls cannot carry a late-computed header, so their cost rides the final chunk’s usage.cost field, which LiteLLM forwards.
  • Tracing setups read the same figures as OpenTelemetry span attributes (gen_ai.usage.cost in USD — but only when a stored EUR/USD rate exists — lowrouter.cost.amount + lowrouter.cost.currency in the billing currency).

Web app

The OpenHands web app sets the same values in its LLM settings under Advanced: custom model openai/auto/mistralai/mistral-large-2512, base URL https://api.lowrouter.ai/v1, and your API key. The web app has no headless mode, so this path is documented but not re-run by the weekly check.

  • Use a dedicated key. An autonomous agent left running overnight can spend a lot, and a key of its own keeps that spend attributable and revocable without touching anything else.
  • Cap the run. OpenHands stops a conversation after max_iterations steps, 500 by default; lower it in the saved settings for unattended tasks.
  • Keep it in a container or a throwaway checkout when using --headless, which approves every command.

Troubleshooting

  • The summary shows the system prompt, and nothing happened: LLM_MODEL is missing the openai/ prefix, or LLM_BASE_URL is missing /v1.
  • The CLI uses a different model from the one you exported: --override-with-envs is missing, so the saved settings won.
  • Tool calls fail or the agent loops without acting: check that the model supports function calling on its page in the model browser.