- Hapus src/lib/ipv6-pool.ts dan test
- Hapus fetchViaCurl() dari fetch-utils.ts
- Hapus parameter ipv6Source dari router, ai-proxy, anthropic-proxy
- Hapus ipv6Only: false dari Bun.serve(), default bind ke 0.0.0.0
- Hapus dokumentasi IPv6 dari CLAUDE.md dan README.md
- SSRF protection untuk IPv6 private/loopback tetap ada
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DeepSeek models mengembalikan tool calls dalam format DSML (DeepSeek
Markup Language) di dalam text content, bukan sebagai JSON structured
tool_calls. Ini menyebabkan tool calling gagal di Claude Code.
Perubahan:
- Buat src/lib/dsml-parser.ts: parser DSML berbasis regex dengan dukungan
streaming (DSMLAccumulator), deteksi teks sebelum/sesudah DSML, dan
parsing parameter JSON
- Forward tools/tool_choice dari client ke backend di Anthropic & OpenAI paths
- Konversi DSML ke tool_use content blocks (Anthropic) atau tool_calls array
(OpenAI) di response non-streaming
- Handle DSML di streaming: buffer text deltas, deteksi di akhir stream,
emit tool_use/tool_calls events yang sesuai
- Handle tool_result blocks dari Anthropic format di assistant messages
- Tambah 29 tests untuk parser dan 18 tests untuk anthropic-proxy
Fix: #285 tests pass, 0 fail
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Two fixes for deepseek-v4-flash-free via Anthropic protocol:
1. **Non-streaming**: Apply config.adaptResponse to unwrap double-nested opencode.ai JSON response before backendToAnthropicResponse
2. **Streaming**: Extract delta.reasoning_content from DeepSeek SSE chunks wrapped in <thinking> tags
All tests pass, verified locally with both streaming and non-streaming.
- Fix type mappings in OpenAIRequest and BackendConfig to handle structured message parts (cache-control).
- Stop adaptRequest crashing when message contents are objects or arrays.
- Forward stream, top_k, and stop_sequences to adaptRequest.
- Forward anthropic-version header to backend endpoints during adaptation.
- Parse structured JSON error payloads from upstream before returning generic "Upstream rejected request" error.
- Create .github/workflows/cloudflare.yml to auto-deploy on master branch pushes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Auto-default anthropic-version to 2023-06-01 for prompt caching
- Forward cache headers (x-cache, cf-cache-status, age, etc.) from backend
- Add features: ["prompt_caching"] to /v1/models response
- Extract and report actual usage tokens in streaming message_delta
- Track output char count for token estimation when backend omits it
- Cache-aware usage reporting: cache_creation_input_tokens, cache_read_input_tokens
- Comprehensive tests for cache_control preservation in content blocks & system
- Add anthropicPassthrough flag to BackendConfig for native Anthropic backends
- Preserve cache_control on content blocks and system prompt (keep structured)
- Extract and forward anthropic-version header from client to backend
- Report actual token usage from backend response (input_tokens, output_tokens)
- Support native Anthropic passthrough (no translation) for compatible backends
- Wire anthropic-version through all entry points: index.ts, router.ts, worker.ts
- Fix streaming timeout: use AbortController for connection-only timeout
instead of AbortSignal.timeout() that kills active SSE streams
- Fix fetchViaCurl: stream body via ReadableStream instead of buffering
entire response in memory
- Fix JWT/aichat race condition: add Promise dedup to prevent concurrent
bootstrap calls (10 requests = 1 bootstrap, not 10)
- Fix ACTIVE_READERS memory leak: auto-remove readers on stream completion
- Fix WebSocket backpressure: log warning when client buffer exceeds 1MB
- Add SSE heartbeat/keepalive: send ': keepalive' every 15s to prevent
LB/proxy timeout during AI thinking
- Fix SSELineBuffer: graceful overflow handling (warn + discard instead
of throwing error that crashes stream)
- Fix transformStream tight loop: yield to event loop after each chunk
to prevent starvation
- Fix fetchViaCurl process cleanup: use SIGKILL + proper timeout cleanup
- Add retry on 502/504: retry transient server errors before returning
to caller (both fetchWithRetry and fetchWithSessionRetry)
Add SessionProxyPool for per-session sticky proxy allocation with
load-balanced least-used selection and auto-rotation on failure.
Introduce fetchWithSessionRetry for transparent retry with proxy
rotation. Wire into AI proxy handlers (OpenAI + Anthropic) with
stream lifecycle cleanup.
Co-Authored-By: Claude <noreply@anthropic.com>
Introduce a centralized `fetch-utils.ts` to handle retry logic with proxy fallback, SSE line buffering to prevent chunk-boundary corruption, and graceful shutdown via active reader tracking.
Key changes:
- Add `fetchWithRetry` for automatic direct-to-proxy failover.
- Implement `SSELineBuffer` to ensure reliable parsing of split SSE chunks.
- Add `createStreamBodyLimiter` to enforce payload limits on streaming requests.
- Refactor `ProxyPool` to decouple failure marking from rotation.
- Standardize CORS handling and environment variable configuration.
- Clean up documentation and remove obsolete skill files.
Standardize comment separators and arrow usage in documentation headers.
Additionally, introduce `accumulateSSEText` and `extractTextFromSSE` to
provide robust parsing for various SSE stream formats, including
Claude Code and OpenAI-compatible deltas.
Refactor the backend-to-Anthropic SSE transformation to use a more robust
state machine approach. This replaces manual string building with a
structured phase-based system (`init`, `block`, `done`) to better
manage the Anthropic streaming protocol, including `message_start` and
`content_block_delta` events.
- Extract `formatContentBlockDelta` helper for consistent event formatting
- Implement phase-based state machine in `transformAnthropicStream`
- Delegate `[DONE]` handling to the stream transformer instead of
manually emitting `message_stop`
- Improve reliability of message ID generation and event sequencing
Remove the ANTHROPIC_MODEL_MAP layer — /v1/messages now uses the
same model names as /v1/chat/completions (deepseek-v4-flash-free,
gpt-5.4-mini-no-login, deepseek/deepseek-v4-flash). This way users
send the original model name and it routes to the correct backend.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use anthReq.model instead of backendModel so the backend receives
the user-sent model name (e.g. claude-sonnet-4) rather than the
internal mapped name.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Response handler already calls markFailed() on non-2xx and network
errors (which rotates the proxy). The preamble at attempt >= 2 was
calling markFailed() again, double-rotating and skipping a proxy.
Changed preamble to use rotate() instead of markFailed().
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously only retried on network errors (fetch exceptions). Now also
rotates to next proxy when upstream returns non-2xx (429 rate limit,
5xx, etc). Applies to both OpenAI and Anthropic endpoints.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Create src/lib/anthropic-proxy.ts: accepts Anthropic Messages API
format (POST /v1/messages) and routes to the same backend providers
- Anthropic model names (claude-sonnet-4, claude-3-haiku, claude-opus-4)
map to backend models with full request/response translation
- Streaming (SSE) via Anthropic protocol: message_start,
content_block_delta, message_stop events
- Add /v1/messages route to server with CORS and proxy pool fallback
- Reuses MODEL_ROUTES from ai-proxy.ts for consistent backend routing
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>