relayai-gateway
OpenAI and Anthropic compatible API, with tool calling, for the models in opencode.
Install it, and every tool that speaks the OpenAI or Anthropic API (the openai SDK, LangChain, Open WebUI, Claude Code, your own scripts) can use the models you have in opencode, including opencode's free models. relayai finds opencode, runs opencode serve in the background for you, and translates between the APIs.
POST /v1/chat/completions(OpenAI) andPOST /v1/messages(Anthropic), streaming included- Tool calling on both, so agent harnesses like Claude Code work
- Models are read live from opencode, nothing is hardcoded
- A CLI (
relayai), a server, and a Python SDK in one package with no dependencies - The same tool exists for Node:
npm install -g relayai-gateway
pip install relayai-gateway
relayai chat "Explain DNS in one sentence"
Contents
- Requirements
- Quick start
- Use it from any OpenAI or Anthropic client
- Full API reference
- Claude Code
- Python SDK
- CLI reference
- Configuration
- How it works
- Which models work with tools
- Limits
- Security
- Troubleshooting
Requirements
-
Python 3.8 or newer
-
opencode (1.x or 2.x). If you do not have it:
relayai install-opencode # runs: npm install -g @opencode/cli (asks first)
or install it yourself as described at https://opencode.ai/docs.
-
At least one model in opencode. opencode's free models work out of the box; to add your own providers (OpenRouter, Anthropic, OpenAI, Groq, ...) run
opencode auth login.
Check everything with:
relayai doctor --test
Quick start
relayai start # opencode serve + gateway in the background
relayai models # what you can use
relayai chat "Hello" # one question
relayai chat # interactive chat (/reset clears it)
relayai status # URLs and API key
relayai stop # stops the gateway and the opencode serve it started
relayai serve does the same as start but in the foreground, with request logs; Ctrl+C stops everything.
The gateway listens on http://127.0.0.1:8000. Its API key is created once and stored in ~/.relayai/api_key; relayai status shows it.
Commands like chat and models use the background gateway when it runs. Without it they start a private one for the duration of the command, which takes a few seconds longer.
Use it from any OpenAI or Anthropic client
relayai env prints the variables for your shell:
eval "$(relayai env)" # bash / zsh
relayai env | Invoke-Expression # PowerShell
| Client type | Base URL | API key | Model |
|---|---|---|---|
| OpenAI (SDKs, LangChain, Open WebUI, ...) | http://127.0.0.1:8000/v1 |
from relayai status |
default or any id from relayai models |
| Anthropic (SDKs, Claude Code) | http://127.0.0.1:8000 |
same | same |
curl:
curl http://127.0.0.1:8000/v1/chat/completions \
-H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" \
-d '{"model": "default", "messages": [{"role": "user", "content": "Say hi"}]}'
The official openai package:
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="rai-...")
reply = client.chat.completions.create(
model="opencode/big-pickle",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)
Tools work the normal OpenAI way (tools=[...], then tool_calls in the reply, then a role: "tool" message with the result).
Full API reference
Every endpoint, request field, streaming event, error code and client integration (OpenAI and Anthropic SDKs for Python and Node, LangChain, Vercel AI SDK, Open WebUI, Claude Code), plus HTTPS, network access and running on boot, is documented in the API reference.
Claude Code
relayai claude # interactive, through the gateway
relayai claude -m opencode/big-pickle # pick the model
relayai claude -- -p "list the files here" # anything after -- goes to claude
This sets ANTHROPIC_BASE_URL, ANTHROPIC_AUTH_TOKEN and the model variables for that one Claude Code process only; your normal claude is untouched. Pick a model from the tools table, because Claude Code depends on tool calling.
Python SDK
from relayai import RelayAI
with RelayAI() as ai: # finds/starts opencode and a private gateway
print(ai.chat("Hello")) # -> str
for piece in ai.stream("Tell me a joke"):
print(piece, end="", flush=True)
print(ai.models()) # -> ["opencode/big-pickle", ...]
RelayAI reuses the background gateway from relayai start when it runs (pass reuse=False to always run a private one). On exit it stops whatever it started.
Options are keywords: RelayAI(model="opencode/big-pickle", timeout=120, opencode_url="http://127.0.0.1:4096", opencode_password="..."). Every field of Config works.
Conversations, system prompt, extra parameters
ai.chat([
{"role": "user", "content": "My name is Asha."},
{"role": "assistant", "content": "Nice to meet you, Asha."},
{"role": "user", "content": "What is my name?"},
], system="Answer in one word.", model="opencode/big-pickle")
Tool calling
weather = {"type": "function", "function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
}}
messages = [{"role": "user", "content": "Weather in Paris?"}]
reply = ai.complete(messages=messages, tools=[weather]) # raw OpenAI response dict
call = reply["choices"][0]["message"]["tool_calls"][0]
messages += [reply["choices"][0]["message"],
{"role": "tool", "tool_call_id": call["id"], "content": "18C, sunny"}]
print(ai.chat(messages, tools=[weather]))
Anthropic format: ai.messages(messages=[...], tools=[...]) returns the Anthropic response dict.
Plug the official SDKs in: ai.base_url (OpenAI, ends in /v1), ai.anthropic_base_url and ai.api_key.
Errors: every failure raises relayai.ApiError with .status (HTTP status), .message, .kind and .code.
Lower level pieces
| Class | Use it to |
|---|---|
Client(base_url, api_key) |
talk to a gateway that is already running somewhere |
Gateway(Config(...)) |
embed the server in your own program (.start(), .close(), .base_url) |
OpencodeManager |
find, install, start and stop opencode serve yourself |
OpencodeClient(url, password=...) |
call opencode's own API (1.x and 2.x) |
from relayai import Config, Gateway
with Gateway(Config.from_env(api_key="secret", port=9000)) as gw:
print(gw.base_url) # http://127.0.0.1:9000/v1, serving in a background thread
input("Enter to stop")
CLI reference
| Command | What it does |
|---|---|
relayai serve |
run the gateway in this terminal (--keep-opencode leaves opencode running on exit) |
relayai start |
run it in the background (--wait seconds to wait for it) |
relayai stop |
stop the background gateway and the opencode serve relayai started (--keep-opencode) |
relayai status [--json] |
what is running, URLs and API key |
relayai models [--json] |
models opencode can use |
relayai chat [PROMPT] |
answer one prompt (also from stdin), or chat interactively; -s system prompt, --no-stream |
relayai env [--shell bash|powershell|cmd|fish] |
print OPENAI_* and ANTHROPIC_* variables |
relayai claude [-- ARGS] |
run Claude Code through the gateway |
relayai doctor [--test] |
check the setup; --test sends one real request |
relayai install-opencode [-y] [--force] |
install opencode with npm |
Every command accepts the options in the next section. relayai <command> --help shows them.
Configuration
Command line flags win over environment variables, which win over defaults.
| Flag | Variable | Default | Meaning |
|---|---|---|---|
--host |
RELAYAI_HOST |
127.0.0.1 |
interface to listen on; 0.0.0.0 exposes it on the network |
--port |
RELAYAI_PORT |
8000 |
gateway port |
--api-key |
RELAYAI_API_KEY |
stored in ~/.relayai/api_key |
key clients must send |
-m, --model |
RELAYAI_MODEL |
opencode's default | model behind default, as provider/model |
--no-strict-models |
RELAYAI_STRICT_MODELS=0 |
strict | unknown model names fall back to the default instead of a 404 (the Anthropic endpoint always falls back, because Claude Code sends Claude model names) |
--timeout |
RELAYAI_TIMEOUT |
600 |
seconds per answer |
--max-concurrency |
RELAYAI_MAX_CONCURRENCY |
4 |
answers generated at once |
RELAYAI_QUEUE_WAIT |
30 |
seconds to wait for a free slot before a 429 | |
RELAYAI_MODEL_CACHE |
30 |
seconds the model list is cached | |
--opencode-url |
OPENCODE_URL |
managed by relayai | use an opencode serve you run yourself |
--opencode-password |
OPENCODE_SERVER_PASSWORD |
its password (opencode 2.x always has one) | |
OPENCODE_SERVER_USERNAME |
opencode |
its username | |
--opencode-port |
RELAYAI_OPENCODE_PORT |
4096 |
preferred port for the managed opencode serve (the next free one is used if taken) |
--opencode-bin |
RELAYAI_OPENCODE_BIN |
found on PATH | path to the opencode executable |
RELAYAI_HOME |
~/.relayai |
where state, logs and the key live |
In code: Config.from_env(port=9000, model="opencode/big-pickle").
How it works
opencode management. Unless you pass --opencode-url, relayai looks for the opencode executable, starts opencode serve on 127.0.0.1 with a freshly generated password, and keeps it in ~/.relayai/workspace so it never works inside your projects. Its details go in ~/.relayai/opencode.json, so later commands (and the Node package) reuse it. relayai stop, serve on exit and the SDK on close stop it again if they started it.
Requests. Every API request becomes one short-lived opencode session that is deleted afterwards. The OpenAI and Anthropic APIs are stateless, so the whole conversation is folded into one prompt. opencode's own tools (shell, file editing) never run.
Tool calling is emulated: your tools are described in the prompt, the model answers with <tool_call>{"name": ..., "arguments": ...}</tool_call> blocks, and relayai turns those into OpenAI tool_calls or Anthropic tool_use. Your program runs the tools, exactly as with a native API.
Files in ~/.relayai: api_key, gateway.json and opencode.json (what is running), gateway.log and opencode.log, and workspace/.
Which models work with tools
Plain chat works with every model opencode can reach. Emulated tool calling depends on the model following the <tool_call> format. Tested on 2026-11-11 with opencode 2.0.31's free models:
| Model | Chat | Tools (OpenAI and Anthropic) |
|---|---|---|
opencode/big-pickle |
yes | yes |
opencode/longcat-2.5-free |
yes | yes |
opencode/minimax-m3.1-flash-free |
yes | yes |
opencode/deepseek-v4.1-flash-free, opencode/inkling-2-small-free |
yes | partly (one endpoint) |
glm-5.3-flash-free, flint-40b-free, iceman-free, beam-free, nemotron-3.5-lightning-free, mimo-v2.6-flash-free |
yes | no |
The models marked "no" try to use opencode's own built-in tools instead of writing text, and the reply comes back empty ("The model returned no text"). Strong paid models generally follow the format well. Free model line-ups change often, so test yours with a small tools request before relying on it.
Limits
- Streaming arrives in one piece. opencode returns the finished answer, so
stream: truesends it in chunks once it is ready (with keepalives meanwhile), not token by token. - Text only. Images and documents are replaced by a placeholder.
- Token counts are estimates on opencode 2.x (about 4 characters per token); opencode 1.x reports real counts.
- Endpoints: chat completions, messages, models, count_tokens (an estimate). No embeddings, no Responses API, no images.
- Free models are rate limited by opencode; bursts of parallel requests get "Free usage limit reached" for a while.
Security
- The gateway listens on
127.0.0.1only.--host 0.0.0.0exposes it to your network; anyone with the key can then spend your providers' quota, so use a long key. - Every API request needs the key (
Authorization: Bearer ...orx-api-key)./healthis the only open endpoint. - The key and the opencode password are stored in
~/.relayaiwith owner-only permissions on Linux and macOS.
Troubleshooting
| Message | Meaning and fix |
|---|---|
opencode is not installed |
relayai install-opencode, or --opencode-bin PATH |
opencode: Free usage limit reached (HTTP 503) |
opencode's free tier is rate limited; wait a little, send fewer parallel requests, or connect your own provider with opencode auth login |
The model returned no text |
the model tried opencode's built-in tools; pick a model from the tools table |
opencode rejected the login |
with --opencode-url, pass the matching --opencode-password |
cannot listen on 127.0.0.1:8000 |
another program uses the port; --port 8100 |
The model 'x' is not available (404) |
run relayai models and use one of those ids, or default |
Logs: ~/.relayai/gateway.log (background gateway) and ~/.relayai/opencode.log.
Changelog and contributing
Versions are date based (YYYY.M.D, the release day). See the changelog, contributing guide and code of conduct.
License
MIT, by Mithun Gowda B. Source: github.com/mithun50/relayai-gateway
Metadata
Release files for relayai-gateway 2026.11.12
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| relayai_gateway-2026.11.12.tar.gz | 41.7 kB | With provenance | Details for relayai_gateway-2026.11.12.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| relayai_gateway-2026.11.12-py3-none-any.whl | Python 3 | none | any | Details for relayai_gateway-2026.11.12-py3-none-any.whl |
Total release size: 80.9 kB
Release files / relayai_gateway-2026.11.12.tar.gz
| Download URL | relayai_gateway-2026.11.12.tar.gz |
|---|---|
| Size | 41.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
43b9257c314792629c06966e29ba8eaa3ea3f02df9186933b9b43f71aecf44ca
|
|
BLAKE2b-256 checksum How to use checksums |
956f8e68db6425494df4ab7c8a9a93acbad0192f0e36d63ce473f60b5d312a41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logRelease files / relayai_gateway-2026.11.12-py3-none-any.whl
| Download URL | relayai_gateway-2026.11.12-py3-none-any.whl |
|---|---|
| Size | 39.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bcd678517fc49be36a7d202baf76d69ffc475570b43182846ec1c01e6589c742
|
|
BLAKE2b-256 checksum How to use checksums |
7e450aa57ebef5322da0d4e14c7d473923e30c665784bb438d663e12da19ab45
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logSecurity for relayai-gateway 2026.11.12
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for relayai_gateway-2026.11.12.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Nov 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.