- Add request/response converters (OpenAI <-> Anthropic formats) - Implement SSE streaming conversion (Anthropic events -> OpenAI SSE) - Add /v1/models endpoint with Claude model list - Add /v1/chat/completions endpoint with streaming and non-streaming support - Fix context key type matching bug (sessionIDKey) - Configurable upstream URL via config.yaml - Mimic claude-code CLI headers for upstream requests
68 lines
3.6 KiB
Markdown
68 lines
3.6 KiB
Markdown
# proxx Implementation Plan
|
|
|
|
## Goal
|
|
A working Go binary that proxies OpenAI-format requests to an Anthropic endpoint, with full streaming and non-streaming support.
|
|
|
|
## Approach
|
|
Straightforward per the spec. Go 1.21+ with only `gopkg.in/yaml.v3` as external dependency. Standard library `net/http` for everything HTTP-related.
|
|
|
|
## Steps
|
|
|
|
### Phase 1: Project Scaffolding
|
|
- [ ] Initialize `go mod init github.com/penal-colony/proxx`
|
|
- [ ] Create directory structure: `main.go`, `handler.go`, `converter.go`, `types.go`, `streaming.go`, `config.yaml`
|
|
- [ ] Add `gopkg.in/yaml.v3` dependency
|
|
|
|
### Phase 2: Types (`types.go`)
|
|
- [ ] OpenAI request/response structs (`ChatCompletionRequest`, `ChatCompletionResponse`, `Message`, `Choice`, `Usage`)
|
|
- [ ] Anthropic request/response structs (`AnthropicRequest`, `AnthropicResponse`, `ContentBlock`, `Message`, `Usage`)
|
|
- [ ] Tool/function conversion types
|
|
- [ ] Streaming event structs (Anthropic SSE + OpenAI SSE)
|
|
|
|
### Phase 3: Converter (`converter.go`)
|
|
- [ ] `convertRequest()` — map OpenAI req to Anthropic req (model, messages, system, tools, temperature, top_p, max_tokens, stop_sequences)
|
|
- [ ] `extractSystemMessage()` — pull role=system out of messages into top-level system field
|
|
- [ ] `convertTools()` — map OpenAI function tools to Anthropic tool format
|
|
- [ ] `convertResponse()` — map Anthropic non-streaming response to OpenAI format
|
|
- [ ] `mapStopReason()` — end_turn→stop, tool_use→tool_calls, max_tokens→length
|
|
|
|
### Phase 4: Streaming (`streaming.go`)
|
|
- [ ] `StreamConverter` struct holding accumulated state
|
|
- [ ] `HandleAnthropicStream()` — parse SSE events, convert to OpenAI SSE format
|
|
- [ ] Handle: `message_start`, `content_block_start`, `content_block_delta`, `content_block_stop`, `message_delta`, `message_stop`
|
|
- [ ] Map: role, content deltas, tool_use deltas, finish_reason
|
|
- [ ] Send `data: [DONE]` on stream end
|
|
|
|
### Phase 5: HTTP Handlers (`handler.go`)
|
|
- [ ] `handleModels()` — static list of Claude models in OpenAI format
|
|
- [ ] `handleChatCompletions()` — routing: detect streaming vs non-streaming, call upstream, return converted response
|
|
- [ ] Extract `Authorization: Bearer` → forward as `x-api-key`
|
|
- [ ] Set Claude-Code mimicry headers on upstream requests
|
|
- [ ] Error handling: 400 for bad requests, 502 for upstream failures
|
|
|
|
### Phase 6: Entry Point (`main.go`)
|
|
- [ ] Load `config.yaml` via `gopkg.in/yaml.v3`
|
|
- [ ] Register handlers on `/v1/chat/completions` and `/v1/models`
|
|
- [ ] Start HTTP server on configured port
|
|
- [ ] Generate random `X-Claude-Code-Session-Id` UUID at startup
|
|
|
|
### Phase 7: Config
|
|
- [ ] Create `config.yaml` with defaults (port 8080, upstream_url)
|
|
|
|
### Phase 8: Testing
|
|
- [ ] Unit tests for `converter.go` (pure logic, no HTTP)
|
|
- [ ] Unit tests for `streaming.go` (test SSE event conversion)
|
|
- [ ] Integration test with mock upstream
|
|
|
|
## Risks
|
|
- **SSE parsing**: Anthropic uses SSE format, need to handle `data:` lines correctly. Risk: low, well-documented format.
|
|
- **Tool calling conversion**: Complex nested structure mapping. Risk: medium — need to verify edge cases.
|
|
- **Streaming state machine**: Accumulating partial tool_use blocks across multiple events. Risk: medium — test with actual stream.
|
|
|
|
## Definition of Done
|
|
- [ ] `go build` produces a binary with no errors
|
|
- [ ] Unit tests pass for converter and streaming logic
|
|
- [ ] Binary starts, loads config, listens on port
|
|
- [ ] `/v1/models` returns Claude model list in OpenAI format
|
|
- [ ] Non-streaming `/v1/chat/completions` round-trips correctly through Anthropic upstream
|
|
- [ ] Streaming `/v1/chat/completions` produces valid OpenAI SSE output
|