Add OpenAI-to-Anthropic proxy design spec
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
4fb87b75a8
2 changed files with 110 additions and 0 deletions
|
|
@ -0,0 +1,108 @@
|
|||
# OpenAI-to-Anthropic Proxy Design (proxx)
|
||||
|
||||
## Purpose
|
||||
|
||||
A Go proxy that exposes OpenAI-compatible API endpoints, converts requests to Anthropic format, and forwards them to a configurable upstream Anthropic endpoint. The proxy mimics claude-code (the CLI tool) so upstream sees requests as originating from claude-code.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Client (OpenAI format) --> proxx (Go :8080) --> Upstream Anthropic endpoint
|
||||
e.g. https://api.z.ai/api/anthropic
|
||||
```
|
||||
|
||||
Single binary using Go standard library `net/http`. Only external dependency: `gopkg.in/yaml.v3` for config parsing.
|
||||
|
||||
## Configuration
|
||||
|
||||
Config loaded from `config.yaml` in the working directory:
|
||||
|
||||
```yaml
|
||||
port: 8080
|
||||
upstream_url: "https://api.z.ai/api/anthropic"
|
||||
```
|
||||
|
||||
## Endpoints
|
||||
|
||||
### POST /v1/chat/completions
|
||||
|
||||
Accepts OpenAI chat completion format. Converts to Anthropic `POST /v1/messages`. Supports both streaming and non-streaming modes.
|
||||
|
||||
### GET /v1/models
|
||||
|
||||
Returns a static list of Claude models in OpenAI `/v1/models` response format. No upstream call needed.
|
||||
|
||||
## Authentication
|
||||
|
||||
The API key is extracted from the incoming request's `Authorization: Bearer <key>` header and forwarded to the upstream as the `x-api-key` header.
|
||||
|
||||
## Claude-Code Mimicry
|
||||
|
||||
The proxy sets these headers on all upstream requests:
|
||||
|
||||
| Header | Value |
|
||||
|---|---|
|
||||
| `User-Agent` | `claude-cli/1.0.18 (pro, cli)` |
|
||||
| `x-api-key` | From incoming Bearer token |
|
||||
| `x-app` | `cli` |
|
||||
| `anthropic-version` | `2023-06-01` |
|
||||
| `anthropic-beta` | `interleaved-thinking-2025-05-14,prompt-caching-scope-2026-01-05,context-management-2025-06-27` |
|
||||
| `X-Claude-Code-Session-Id` | Random UUID generated at startup |
|
||||
| `content-type` | `application/json` |
|
||||
|
||||
## Request Conversion (OpenAI -> Anthropic)
|
||||
|
||||
### Fields mapped directly
|
||||
- `model` -> `model`
|
||||
- `temperature` -> `temperature`
|
||||
- `max_tokens` -> `max_tokens` (default 8192 if omitted)
|
||||
- `stream` -> `stream`
|
||||
- `top_p` -> `top_p`
|
||||
- `stop` -> `stop_sequences`
|
||||
|
||||
### Messages
|
||||
- OpenAI `messages[]` with `role` and `content` mapped to Anthropic `messages[]`
|
||||
- `system` message (role="system") extracted to top-level `system` field
|
||||
- String content mapped directly; array content (text+image parts) mapped to Anthropic content block format
|
||||
|
||||
### Tools
|
||||
- OpenAI function tools converted to Anthropic tool format (`name`, `description`, `input_schema`)
|
||||
- `tool_choice` mapped: `auto`/`none`/`required` + specific tool selection
|
||||
|
||||
## Response Conversion (Anthropic -> OpenAI)
|
||||
|
||||
### Non-streaming
|
||||
- `content[0].text` -> `choices[0].message.content`
|
||||
- `content[].type == "tool_use"` -> `choices[0].message.tool_calls[]`
|
||||
- `usage.input_tokens` -> `usage.prompt_tokens`
|
||||
- `usage.output_tokens` -> `usage.completion_tokens`
|
||||
- `stop_reason` mapped: `end_turn` -> `stop`, `tool_use` -> `tool_calls`, `max_tokens` -> `length`
|
||||
|
||||
### Streaming
|
||||
- Anthropic SSE events (`message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`) converted to OpenAI SSE format (`role`, `content` deltas, `tool_calls` deltas, `finish_reason`)
|
||||
- Final `data: [DONE]` sent after stream ends
|
||||
|
||||
### Error Responses
|
||||
- Upstream errors converted to OpenAI error format: `{"error": {"message": "...", "type": "...", "code": "..."}}`
|
||||
- Connection failures: HTTP 502
|
||||
- Malformed requests: HTTP 400
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
proxx/
|
||||
├── main.go # Entry point, config loading, HTTP server setup
|
||||
├── handler.go # HTTP handlers for /v1/chat/completions and /v1/models
|
||||
├── converter.go # OpenAI <-> Anthropic format conversion logic
|
||||
├── types.go # All request/response struct types
|
||||
├── streaming.go # SSE streaming conversion (Anthropic -> OpenAI)
|
||||
├── config.yaml # Default configuration file
|
||||
├── go.mod
|
||||
└── go.sum
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
- Unit tests for the converter functions (pure logic, no HTTP)
|
||||
- Integration test with a mock upstream server to verify end-to-end flow
|
||||
- Streaming test to verify SSE event conversion
|
||||
Loading…
Add table
Add a link
Reference in a new issue