Skip to content

Commit c516686

Browse files
authored
feat: add Anthropic Messages API and Google Gemini API endpoints (#40)
* feat: expand tests, add ESLint, document env vars - Add 17 new integration tests: CORS edge cases (disallowed origins, no-origin header, OPTIONS for disallowed origin), auth (401/pass-through), and error handling (400/502/404) for /v1/chat/completions Closes #14, closes #16 - Add ESLint with flat config, npm run lint script, and Lint job in CI Closes #15 - Improve README with quickstart section, npm install instructions, and corrected package name; add type column to env vars table Closes #17 * feat: implement SSE streaming and support all opencode providers - Implement streaming for POST /v1/chat/completions (issue #11): subscribe to opencode event stream, pipe message.part.updated deltas as SSE chat.completion.chunk events, finish on session.idle - Implement streaming for POST /v1/responses (issue #11): emit response.created / output_text.delta / response.completed events - Fix provider-agnostic system prompt hint (issue #12): remove 'OpenAI-compatible' wording so non-OpenAI models are not confused - Add TextEncoder and ReadableStream to ESLint globals - Add streaming integration tests (happy path, unknown model, session.error) * feat: refactor SSE queue, expand test coverage, fix package metadata - Extract createSseQueue() helper, eliminating duplicated SSE queue pattern in /v1/chat/completions and /v1/responses streaming branches (closes #34) - Add tests for GET /v1/models happy path, empty providers, and error path (closes #33) - Add tests for POST /v1/responses: happy path, validation, streaming, session.error (closes #32) - Fix package.json description to be provider-agnostic (closes #35) - Add engines field declaring bun >=1.0.0 requirement (closes #35) - Line coverage: 55% -> 89%, function coverage: 83% -> 94% * feat: add Anthropic Messages API and Google Gemini API endpoints - POST /v1/messages — Anthropic Messages API with streaming (SSE) - POST /v1beta/models/:model:generateContent — Gemini non-streaming - POST /v1beta/models/:model:streamGenerateContent — Gemini NDJSON streaming - New helpers: normalizeAnthropicMessages, normalizeGeminiContents, extractGeminiSystemInstruction, mapFinishReasonToAnthropic/Gemini - 35 new tests (77 -> 112 total, all passing) - Update README to document all supported API formats Closes #38, #39
1 parent 29c798d commit c516686

3 files changed

Lines changed: 996 additions & 7 deletions

File tree

README.md

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# opencode-llm-proxy
22

3-
An [OpenCode](https://opencode.ai) plugin that starts a local OpenAI-compatible HTTP server backed by your OpenCode providers.
3+
An [OpenCode](https://opencode.ai) plugin that starts a local HTTP server backed by your OpenCode providers, with support for multiple LLM API formats:
44

5-
Any tool or application that speaks the OpenAI Chat Completions or Responses API can use it — including LangChain, custom scripts, local frontends, etc.
5+
- **OpenAI** Chat Completions (`POST /v1/chat/completions`) and Responses (`POST /v1/responses`)
6+
- **Anthropic** Messages API (`POST /v1/messages`)
7+
- **Google Gemini** API (`POST /v1beta/models/:model:generateContent`)
8+
9+
Any tool or SDK that targets one of these APIs can point at the proxy without code changes.
610

711
## Quickstart
812

@@ -92,7 +96,7 @@ curl http://127.0.0.1:4010/v1/models
9296

9397
Returns all models from all providers configured in your OpenCode setup (e.g. `github-copilot/claude-sonnet-4.6`, `ollama/qwen3.5:9b`, etc.).
9498

95-
### Chat completions
99+
### OpenAI Chat Completions
96100

97101
```bash
98102
curl http://127.0.0.1:4010/v1/chat/completions \
@@ -105,7 +109,7 @@ curl http://127.0.0.1:4010/v1/chat/completions \
105109
}'
106110
```
107111

108-
Use the fully-qualified `provider/model` ID from `GET /v1/models`.
112+
Use the fully-qualified `provider/model` ID from `GET /v1/models`. Supports `"stream": true` for SSE streaming.
109113

110114
### OpenAI Responses API
111115

@@ -118,6 +122,69 @@ curl http://127.0.0.1:4010/v1/responses \
118122
}'
119123
```
120124

125+
Supports `"stream": true` for SSE streaming.
126+
127+
### Anthropic Messages API
128+
129+
Point the Anthropic SDK (or any client) at this proxy:
130+
131+
```bash
132+
curl http://127.0.0.1:4010/v1/messages \
133+
-H "Content-Type: application/json" \
134+
-d '{
135+
"model": "anthropic/claude-3-5-sonnet",
136+
"max_tokens": 1024,
137+
"system": "You are a helpful assistant.",
138+
"messages": [{"role": "user", "content": "Hello!"}]
139+
}'
140+
```
141+
142+
Supports `"stream": true` for SSE streaming with standard Anthropic streaming events (`message_start`, `content_block_delta`, `message_stop`, etc.).
143+
144+
To point the official Anthropic SDK at this proxy:
145+
146+
```js
147+
import Anthropic from "@anthropic-ai/sdk"
148+
149+
const client = new Anthropic({
150+
baseURL: "http://127.0.0.1:4010",
151+
apiKey: "unused", // or your OPENCODE_LLM_PROXY_TOKEN
152+
})
153+
```
154+
155+
### Google Gemini API
156+
157+
```bash
158+
# Non-streaming
159+
curl http://127.0.0.1:4010/v1beta/models/google/gemini-2.0-flash:generateContent \
160+
-H "Content-Type: application/json" \
161+
-d '{
162+
"contents": [{"role": "user", "parts": [{"text": "Hello!"}]}]
163+
}'
164+
165+
# Streaming (newline-delimited JSON)
166+
curl http://127.0.0.1:4010/v1beta/models/google/gemini-2.0-flash:streamGenerateContent \
167+
-H "Content-Type: application/json" \
168+
-d '{
169+
"contents": [{"role": "user", "parts": [{"text": "Hello!"}]}]
170+
}'
171+
```
172+
173+
The model name in the URL path is resolved the same way as other endpoints (use `provider/model` or a bare model ID if unambiguous).
174+
175+
To point the Google Generative AI SDK at this proxy, set the `baseUrl` option to `http://127.0.0.1:4010`.
176+
177+
## Selecting a provider
178+
179+
All endpoints accept an optional `x-opencode-provider` header to force a specific provider when the model ID is ambiguous:
180+
181+
```bash
182+
curl http://127.0.0.1:4010/v1/chat/completions \
183+
-H "x-opencode-provider: anthropic" \
184+
-H "Content-Type: application/json" \
185+
-d '{"model": "claude-3-5-sonnet", "messages": [...]}'
186+
```
187+
121188
## Configuration
122189

123190
All configuration is done through environment variables. No configuration file is needed.
@@ -149,15 +216,14 @@ curl http://<your-ip>:4010/v1/models \
149216

150217
## How it works
151218

152-
The plugin hooks into OpenCode at startup and spawns a Bun HTTP server. Incoming OpenAI-format requests are translated into OpenCode SDK calls (`client.session.create` + `client.session.prompt`), routed through whichever provider/model is requested, and the response is returned in OpenAI format.
219+
The plugin hooks into OpenCode at startup and spawns a Bun HTTP server. Incoming requests (in OpenAI, Anthropic, or Gemini format) are translated into OpenCode SDK calls (`client.session.create` + `client.session.prompt`), routed through whichever provider/model is requested, and the response is returned in the matching API format.
153220

154221
Each request creates a temporary OpenCode session, so prompts and responses appear in the OpenCode session list.
155222

156223
## Limitations
157224

158-
- Streaming (`"stream": true`) is not yet implemented — requests will return a 400 error.
159225
- Tool/function calling is not forwarded; all built-in OpenCode tools are disabled for proxy sessions.
160-
- The proxy only handles `POST /v1/chat/completions` and `POST /v1/responses`. Other OpenAI endpoints are not implemented.
226+
- Only text content is handled; image and file inputs are ignored.
161227

162228
## License
163229

0 commit comments

Comments
 (0)