agentrelay-cli 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,20 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ - run: uv python install ${{ matrix.python-version }}
19
+ - run: uv sync --python ${{ matrix.python-version }}
20
+ - run: uv run pytest tests/ -v
@@ -0,0 +1,68 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish-agentrelay-cli:
13
+ runs-on: ubuntu-latest
14
+ environment: pypi
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: astral-sh/setup-uv@v5
18
+ - run: uv build
19
+ - uses: pypa/gh-action-pypi-publish@release/v1
20
+
21
+ publish-claude-relay-compat:
22
+ runs-on: ubuntu-latest
23
+ needs: publish-agentrelay-cli
24
+ environment: pypi
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: astral-sh/setup-uv@v5
28
+ - name: Build compatibility package
29
+ run: |
30
+ python - <<'PY'
31
+ import pathlib
32
+ import tomllib
33
+
34
+ root = pathlib.Path(".")
35
+ version = tomllib.loads((root / "pyproject.toml").read_text())["project"]["version"]
36
+
37
+ compat_root = root / "compat" / "claude-relay"
38
+ pkg_dir = compat_root / "src" / "claude_relay_compat"
39
+ pkg_dir.mkdir(parents=True, exist_ok=True)
40
+ (pkg_dir / "__init__.py").write_text('__all__ = ["__version__"]\n__version__ = "' + version + '"\n')
41
+
42
+ (compat_root / "README.md").write_text(
43
+ "# claude-relay (compatibility package)\n\n"
44
+ "Deprecated compatibility package. Install `agentrelay-cli` for canonical updates.\n"
45
+ )
46
+
47
+ (compat_root / "pyproject.toml").write_text(
48
+ f'''[project]
49
+ name = "claude-relay"
50
+ version = "{version}"
51
+ description = "Compatibility package; use agentrelay-cli"
52
+ readme = "README.md"
53
+ requires-python = ">=3.10"
54
+ dependencies = ["agentrelay-cli=={version}"]
55
+
56
+ [build-system]
57
+ requires = ["hatchling"]
58
+ build-backend = "hatchling.build"
59
+
60
+ [tool.hatch.build.targets.wheel]
61
+ packages = ["src/claude_relay_compat"]
62
+ '''
63
+ )
64
+ PY
65
+ uv build compat/claude-relay --out-dir compat/claude-relay/dist
66
+ - uses: pypa/gh-action-pypi-publish@release/v1
67
+ with:
68
+ packages-dir: compat/claude-relay/dist
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ .pytest_cache/
5
+ dist/
6
+ CLAUDE.md
7
+ *.egg-info/
8
+ uv.lock
@@ -0,0 +1,111 @@
1
+ # AGENTS.md — Agent Relay
2
+
3
+ ## What this is
4
+
5
+ Drop-in OpenAI & Anthropic API-compatible server that routes requests through `claude -p` CLI.
6
+
7
+ ## Quick setup
8
+
9
+ **Prerequisites:** `claude` CLI installed and on PATH, Python 3.10+, `uv`
10
+
11
+ ```bash
12
+ # Install and run
13
+ uvx agent-relay serve
14
+
15
+ # Or from source
16
+ git clone https://github.com/npow/claude-relay.git
17
+ cd claude-relay && uv sync && uv run agent-relay serve
18
+ ```
19
+
20
+ Default: `http://0.0.0.0:8082`. Override with `--host` / `--port`.
21
+
22
+ ## Configuring clients
23
+
24
+ **OpenAI SDK (Python):**
25
+ ```python
26
+ from openai import OpenAI
27
+ client = OpenAI(base_url="http://localhost:8082/v1", api_key="unused")
28
+ response = client.chat.completions.create(
29
+ model="sonnet", messages=[{"role": "user", "content": "Hello"}], stream=True
30
+ )
31
+ ```
32
+
33
+ **Anthropic SDK (Python):**
34
+ ```python
35
+ from anthropic import Anthropic
36
+ client = Anthropic(base_url="http://localhost:8082", api_key="unused")
37
+ response = client.messages.create(
38
+ model="sonnet", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}]
39
+ )
40
+ ```
41
+
42
+ **LangChain:**
43
+ ```python
44
+ from langchain_openai import ChatOpenAI
45
+ llm = ChatOpenAI(base_url="http://localhost:8082/v1", api_key="unused", model="sonnet")
46
+ ```
47
+
48
+ **curl:**
49
+ ```bash
50
+ curl http://localhost:8082/v1/chat/completions \
51
+ -H "Content-Type: application/json" \
52
+ -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}]}'
53
+ ```
54
+
55
+ ## API endpoints
56
+
57
+ | Method | Path | Description |
58
+ |--------|------|-------------|
59
+ | GET | `/health` | Server + CLI status |
60
+ | GET | `/v1/models` | List available models |
61
+ | POST | `/v1/chat/completions` | OpenAI-compatible chat |
62
+ | POST | `/v1/messages` | Anthropic-compatible messages |
63
+
64
+ All endpoints also available without `/v1` prefix (e.g. `/models`, `/chat/completions`, `/messages`).
65
+
66
+ ## Models
67
+
68
+ | Model | Notes |
69
+ |-------|-------|
70
+ | `opus` | Most capable |
71
+ | `sonnet` | **Default** if omitted |
72
+ | `haiku` | Fastest |
73
+
74
+ Passed directly to `claude --model`.
75
+
76
+ ## Ignored parameters
77
+
78
+ These are silently discarded — Claude Code CLI does not expose them:
79
+
80
+ `temperature`, `max_tokens`, `top_p`, `top_k`, `n`, `tools`, `tool_choice`, `functions`, `function_call`, `response_format`
81
+
82
+ Image/audio content blocks are stripped to text-only.
83
+
84
+ ## Troubleshooting
85
+
86
+ | Issue | Fix |
87
+ |-------|-----|
88
+ | Health check fails | `curl http://localhost:8082/health` — verify `claude` is on PATH |
89
+ | `claude` not found | Ensure CLI installed: `which claude`. Restart shell if just installed |
90
+ | Slow responses (~2-3s overhead) | Expected — each request spawns a `claude -p` subprocess |
91
+ | Images/audio ignored | Only text content is extracted from multimodal messages |
92
+ | No tool calling | Claude Code uses its own tools internally; tool parameters are ignored |
93
+ | CORS errors | CORS is enabled for all origins by default |
94
+
95
+ ## Development
96
+
97
+ **Source layout:**
98
+ ```
99
+ src/claude_relay/
100
+ ├── __init__.py # Version
101
+ ├── __main__.py # CLI entry point (argparse)
102
+ └── server.py # FastAPI app, all endpoints and logic
103
+ tests/
104
+ └── test_server.py # Unit + integration tests
105
+ ```
106
+
107
+ **Run tests:**
108
+ ```bash
109
+ uv sync
110
+ uv run pytest tests/ -v
111
+ ```
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 npow
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,233 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentrelay-cli
3
+ Version: 0.5.1
4
+ Summary: OpenAI- and Anthropic-compatible API server that routes through agent CLIs
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: fastapi>=0.115
9
+ Requires-Dist: uvicorn>=0.34
10
+ Description-Content-Type: text/markdown
11
+
12
+ # agent-relay
13
+
14
+ [![CI](https://github.com/npow/claude-relay/actions/workflows/ci.yml/badge.svg)](https://github.com/npow/claude-relay/actions/workflows/ci.yml)
15
+ [![PyPI](https://img.shields.io/pypi/v/agentrelay-cli)](https://pypi.org/project/agentrelay-cli/)
16
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
17
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
18
+
19
+ Drop-in OpenAI **and Anthropic** API server that routes through agent CLIs (currently [Claude Code](https://docs.anthropic.com/en/docs/claude-code)).
20
+
21
+ > Compatibility note: `claude-relay` remains available as a compatibility package/command alias.
22
+
23
+ ## Why
24
+
25
+ You have tools that speak the OpenAI or Anthropic API. You have Claude Code with its tools, MCP servers, and agentic capabilities. **agent-relay** bridges the two — point any compatible client at it and every request flows through `claude -p` under the hood.
26
+
27
+ - **Use Claude Code from any OpenAI or Anthropic client** — Cursor, Continue, aider, LangChain, custom scripts
28
+ - **Keep Claude Code's superpowers** — tool use, MCP servers, file access, shell execution
29
+ - **Zero config** — if `claude` works on your machine, so does this
30
+ - **Real token usage** — reports actual token counts from Claude (not zeros)
31
+ - **Token-level streaming** — uses `--include-partial-messages` for true real-time deltas
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ # With uv (recommended)
37
+ uvx agent-relay serve
38
+
39
+ # Or install globally
40
+ uv tool install agentrelay-cli
41
+ agent-relay serve
42
+
43
+ # Or from source
44
+ git clone https://github.com/npow/claude-relay.git
45
+ cd claude-relay
46
+ uv sync
47
+ uv run agent-relay serve
48
+ ```
49
+
50
+ ## Quick start
51
+
52
+ ```bash
53
+ agent-relay serve
54
+ # Server starts on http://localhost:18082
55
+ ```
56
+
57
+ ### Run as background service (macOS)
58
+
59
+ ```bash
60
+ # Install and auto-start on login
61
+ agent-relay service install
62
+ ```
63
+
64
+ The installer will offer to add these to your `~/.zshrc` (or `~/.bashrc`) so every SDK and agent picks up the relay automatically:
65
+
66
+ ```bash
67
+ export ANTHROPIC_BASE_URL="http://127.0.0.1:18082"
68
+ export OPENAI_BASE_URL="http://127.0.0.1:18082/v1"
69
+ ```
70
+
71
+ ```bash
72
+ # Check status
73
+ agent-relay service status
74
+
75
+ # Update
76
+ uv tool upgrade agentrelay-cli
77
+ agent-relay service restart
78
+
79
+ # Stop and remove
80
+ agent-relay service uninstall
81
+ ```
82
+
83
+ Point any OpenAI-compatible client at it:
84
+
85
+ ```python
86
+ from openai import OpenAI
87
+
88
+ client = OpenAI(base_url="http://localhost:18082/v1", api_key="unused")
89
+
90
+ # Streaming
91
+ for chunk in client.chat.completions.create(
92
+ model="sonnet",
93
+ messages=[{"role": "user", "content": "Hello!"}],
94
+ stream=True,
95
+ ):
96
+ print(chunk.choices[0].delta.content or "", end="")
97
+
98
+ # Non-streaming
99
+ resp = client.chat.completions.create(
100
+ model="sonnet",
101
+ messages=[{"role": "user", "content": "Hello!"}],
102
+ )
103
+ print(resp.choices[0].message.content)
104
+ ```
105
+
106
+ ### Anthropic SDK
107
+
108
+ ```python
109
+ import anthropic
110
+
111
+ # Just set the base URL — the SDK reads ANTHROPIC_BASE_URL automatically
112
+ # export ANTHROPIC_BASE_URL=http://localhost:18082
113
+ client = anthropic.Anthropic(base_url="http://localhost:18082")
114
+
115
+ # Streaming
116
+ with client.messages.stream(
117
+ model="sonnet",
118
+ max_tokens=1024,
119
+ messages=[{"role": "user", "content": "Hello!"}],
120
+ ) as stream:
121
+ for text in stream.text_stream:
122
+ print(text, end="")
123
+
124
+ # Non-streaming
125
+ resp = client.messages.create(
126
+ model="sonnet",
127
+ max_tokens=1024,
128
+ messages=[{"role": "user", "content": "Hello!"}],
129
+ )
130
+ print(resp.content[0].text)
131
+ ```
132
+
133
+ ### LangChain
134
+
135
+ ```python
136
+ from langchain_anthropic import ChatAnthropic
137
+
138
+ # export ANTHROPIC_BASE_URL=http://localhost:18082
139
+ llm = ChatAnthropic(model="sonnet")
140
+ print(llm.invoke("Hello!").content)
141
+ ```
142
+
143
+ ### curl
144
+
145
+ ```bash
146
+ # OpenAI format
147
+ curl http://localhost:18082/v1/chat/completions \
148
+ -H "Content-Type: application/json" \
149
+ -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}],"stream":true}'
150
+
151
+ # OpenAI Responses format
152
+ curl http://localhost:18082/v1/responses \
153
+ -H "Content-Type: application/json" \
154
+ -d '{"model":"sonnet","input":"Hello"}'
155
+
156
+ # Anthropic format
157
+ curl http://localhost:18082/v1/messages \
158
+ -H "Content-Type: application/json" \
159
+ -d '{"model":"sonnet","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'
160
+ ```
161
+
162
+ ## Configuration
163
+
164
+ ```
165
+ agent-relay serve [--host HOST] [--port PORT]
166
+ ```
167
+
168
+ | Flag | Default | Description |
169
+ |---|---|---|
170
+ | `--host` | `0.0.0.0` | Bind address |
171
+ | `--port` | `18082` | Bind port |
172
+
173
+ ## API
174
+
175
+ | Endpoint | Method | Description |
176
+ |---|---|---|
177
+ | `/v1/chat/completions` | POST | Chat completions (OpenAI-compatible) |
178
+ | `/v1/responses` | POST | Responses API (OpenAI-compatible) |
179
+ | `/v1/messages` | POST | Messages (Anthropic-compatible) |
180
+ | `/v1/models` | GET | List available models |
181
+ | `/health` | GET | Server and CLI status |
182
+
183
+ All endpoints also work without the `/v1` prefix. CORS is enabled for all origins.
184
+
185
+ ### Supported features
186
+
187
+ | Feature | Status |
188
+ |---|---|
189
+ | Streaming (SSE) | Yes |
190
+ | System messages | Yes (via `--system-prompt`) |
191
+ | Multi-turn conversations | Yes |
192
+ | Multimodal (text parts) | Yes |
193
+ | Model selection | Yes |
194
+ | Token usage reporting | Yes |
195
+ | CORS | Yes |
196
+
197
+ ### Models
198
+
199
+ Pass any model name — it goes directly to `claude --model`:
200
+
201
+ | Model | Description |
202
+ |---|---|
203
+ | `opus` | Most capable |
204
+ | `sonnet` | Balanced (default) |
205
+ | `haiku` | Fastest |
206
+
207
+ ## Limitations
208
+
209
+ - `temperature`, `max_tokens`, `top_p`, and other sampling parameters are ignored (Claude Code CLI does not expose them)
210
+ - No tool/function calling passthrough (Claude Code uses its own tools internally, but they aren't exposed via the OpenAI tool-calling protocol)
211
+ - Each request spawns a new `claude` process (~2-3s overhead on top of API latency)
212
+ - No image/audio content forwarding — only text parts of multimodal messages are extracted
213
+
214
+ ## How it works
215
+
216
+ ```
217
+ OpenAI client ─┐
218
+ ├→ claude-relay → claude -p → Anthropic API
219
+ Anthropic client ─┘ (FastAPI) (stream-json)
220
+ ```
221
+
222
+ Each request spawns a `claude -p` process with `--output-format stream-json --include-partial-messages`. The proxy translates between the OpenAI or Anthropic wire format and Claude Code's streaming JSON protocol. Requests are stateless — no conversation history bleeds between calls.
223
+
224
+ ## Development
225
+
226
+ ```bash
227
+ uv sync
228
+ uv run pytest tests/ -v
229
+ ```
230
+
231
+ ## License
232
+
233
+ [MIT](LICENSE)
@@ -0,0 +1,222 @@
1
+ # agent-relay
2
+
3
+ [![CI](https://github.com/npow/claude-relay/actions/workflows/ci.yml/badge.svg)](https://github.com/npow/claude-relay/actions/workflows/ci.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/agentrelay-cli)](https://pypi.org/project/agentrelay-cli/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
7
+
8
+ Drop-in OpenAI **and Anthropic** API server that routes through agent CLIs (currently [Claude Code](https://docs.anthropic.com/en/docs/claude-code)).
9
+
10
+ > Compatibility note: `claude-relay` remains available as a compatibility package/command alias.
11
+
12
+ ## Why
13
+
14
+ You have tools that speak the OpenAI or Anthropic API. You have Claude Code with its tools, MCP servers, and agentic capabilities. **agent-relay** bridges the two — point any compatible client at it and every request flows through `claude -p` under the hood.
15
+
16
+ - **Use Claude Code from any OpenAI or Anthropic client** — Cursor, Continue, aider, LangChain, custom scripts
17
+ - **Keep Claude Code's superpowers** — tool use, MCP servers, file access, shell execution
18
+ - **Zero config** — if `claude` works on your machine, so does this
19
+ - **Real token usage** — reports actual token counts from Claude (not zeros)
20
+ - **Token-level streaming** — uses `--include-partial-messages` for true real-time deltas
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ # With uv (recommended)
26
+ uvx agent-relay serve
27
+
28
+ # Or install globally
29
+ uv tool install agentrelay-cli
30
+ agent-relay serve
31
+
32
+ # Or from source
33
+ git clone https://github.com/npow/claude-relay.git
34
+ cd claude-relay
35
+ uv sync
36
+ uv run agent-relay serve
37
+ ```
38
+
39
+ ## Quick start
40
+
41
+ ```bash
42
+ agent-relay serve
43
+ # Server starts on http://localhost:18082
44
+ ```
45
+
46
+ ### Run as background service (macOS)
47
+
48
+ ```bash
49
+ # Install and auto-start on login
50
+ agent-relay service install
51
+ ```
52
+
53
+ The installer will offer to add these to your `~/.zshrc` (or `~/.bashrc`) so every SDK and agent picks up the relay automatically:
54
+
55
+ ```bash
56
+ export ANTHROPIC_BASE_URL="http://127.0.0.1:18082"
57
+ export OPENAI_BASE_URL="http://127.0.0.1:18082/v1"
58
+ ```
59
+
60
+ ```bash
61
+ # Check status
62
+ agent-relay service status
63
+
64
+ # Update
65
+ uv tool upgrade agentrelay-cli
66
+ agent-relay service restart
67
+
68
+ # Stop and remove
69
+ agent-relay service uninstall
70
+ ```
71
+
72
+ Point any OpenAI-compatible client at it:
73
+
74
+ ```python
75
+ from openai import OpenAI
76
+
77
+ client = OpenAI(base_url="http://localhost:18082/v1", api_key="unused")
78
+
79
+ # Streaming
80
+ for chunk in client.chat.completions.create(
81
+ model="sonnet",
82
+ messages=[{"role": "user", "content": "Hello!"}],
83
+ stream=True,
84
+ ):
85
+ print(chunk.choices[0].delta.content or "", end="")
86
+
87
+ # Non-streaming
88
+ resp = client.chat.completions.create(
89
+ model="sonnet",
90
+ messages=[{"role": "user", "content": "Hello!"}],
91
+ )
92
+ print(resp.choices[0].message.content)
93
+ ```
94
+
95
+ ### Anthropic SDK
96
+
97
+ ```python
98
+ import anthropic
99
+
100
+ # Just set the base URL — the SDK reads ANTHROPIC_BASE_URL automatically
101
+ # export ANTHROPIC_BASE_URL=http://localhost:18082
102
+ client = anthropic.Anthropic(base_url="http://localhost:18082")
103
+
104
+ # Streaming
105
+ with client.messages.stream(
106
+ model="sonnet",
107
+ max_tokens=1024,
108
+ messages=[{"role": "user", "content": "Hello!"}],
109
+ ) as stream:
110
+ for text in stream.text_stream:
111
+ print(text, end="")
112
+
113
+ # Non-streaming
114
+ resp = client.messages.create(
115
+ model="sonnet",
116
+ max_tokens=1024,
117
+ messages=[{"role": "user", "content": "Hello!"}],
118
+ )
119
+ print(resp.content[0].text)
120
+ ```
121
+
122
+ ### LangChain
123
+
124
+ ```python
125
+ from langchain_anthropic import ChatAnthropic
126
+
127
+ # export ANTHROPIC_BASE_URL=http://localhost:18082
128
+ llm = ChatAnthropic(model="sonnet")
129
+ print(llm.invoke("Hello!").content)
130
+ ```
131
+
132
+ ### curl
133
+
134
+ ```bash
135
+ # OpenAI format
136
+ curl http://localhost:18082/v1/chat/completions \
137
+ -H "Content-Type: application/json" \
138
+ -d '{"model":"sonnet","messages":[{"role":"user","content":"Hello"}],"stream":true}'
139
+
140
+ # OpenAI Responses format
141
+ curl http://localhost:18082/v1/responses \
142
+ -H "Content-Type: application/json" \
143
+ -d '{"model":"sonnet","input":"Hello"}'
144
+
145
+ # Anthropic format
146
+ curl http://localhost:18082/v1/messages \
147
+ -H "Content-Type: application/json" \
148
+ -d '{"model":"sonnet","max_tokens":1024,"messages":[{"role":"user","content":"Hello"}]}'
149
+ ```
150
+
151
+ ## Configuration
152
+
153
+ ```
154
+ agent-relay serve [--host HOST] [--port PORT]
155
+ ```
156
+
157
+ | Flag | Default | Description |
158
+ |---|---|---|
159
+ | `--host` | `0.0.0.0` | Bind address |
160
+ | `--port` | `18082` | Bind port |
161
+
162
+ ## API
163
+
164
+ | Endpoint | Method | Description |
165
+ |---|---|---|
166
+ | `/v1/chat/completions` | POST | Chat completions (OpenAI-compatible) |
167
+ | `/v1/responses` | POST | Responses API (OpenAI-compatible) |
168
+ | `/v1/messages` | POST | Messages (Anthropic-compatible) |
169
+ | `/v1/models` | GET | List available models |
170
+ | `/health` | GET | Server and CLI status |
171
+
172
+ All endpoints also work without the `/v1` prefix. CORS is enabled for all origins.
173
+
174
+ ### Supported features
175
+
176
+ | Feature | Status |
177
+ |---|---|
178
+ | Streaming (SSE) | Yes |
179
+ | System messages | Yes (via `--system-prompt`) |
180
+ | Multi-turn conversations | Yes |
181
+ | Multimodal (text parts) | Yes |
182
+ | Model selection | Yes |
183
+ | Token usage reporting | Yes |
184
+ | CORS | Yes |
185
+
186
+ ### Models
187
+
188
+ Pass any model name — it goes directly to `claude --model`:
189
+
190
+ | Model | Description |
191
+ |---|---|
192
+ | `opus` | Most capable |
193
+ | `sonnet` | Balanced (default) |
194
+ | `haiku` | Fastest |
195
+
196
+ ## Limitations
197
+
198
+ - `temperature`, `max_tokens`, `top_p`, and other sampling parameters are ignored (Claude Code CLI does not expose them)
199
+ - No tool/function calling passthrough (Claude Code uses its own tools internally, but they aren't exposed via the OpenAI tool-calling protocol)
200
+ - Each request spawns a new `claude` process (~2-3s overhead on top of API latency)
201
+ - No image/audio content forwarding — only text parts of multimodal messages are extracted
202
+
203
+ ## How it works
204
+
205
+ ```
206
+ OpenAI client ─┐
207
+ ├→ claude-relay → claude -p → Anthropic API
208
+ Anthropic client ─┘ (FastAPI) (stream-json)
209
+ ```
210
+
211
+ Each request spawns a `claude -p` process with `--output-format stream-json --include-partial-messages`. The proxy translates between the OpenAI or Anthropic wire format and Claude Code's streaming JSON protocol. Requests are stateless — no conversation history bleeds between calls.
212
+
213
+ ## Development
214
+
215
+ ```bash
216
+ uv sync
217
+ uv run pytest tests/ -v
218
+ ```
219
+
220
+ ## License
221
+
222
+ [MIT](LICENSE)