# DS2API [![License](https://img.shields.io/github/license/CJackHwang/ds2api.svg)](LICENSE) ![Stars](https://img.shields.io/github/stars/CJackHwang/ds2api.svg) ![Forks](https://img.shields.io/github/forks/CJackHwang/ds2api.svg) [![Version](https://img.shields.io/badge/version-1.6.11-blue.svg)](version.txt) [![Docker](https://img.shields.io/badge/docker-ready-blue.svg)](DEPLOY.en.md) Language: [δΈ­ζ–‡](README.MD) | [English](README.en.md) DS2API converts DeepSeek Web chat capability into OpenAI-compatible and Claude-compatible APIs. The backend is a **pure Go implementation**, with a React WebUI admin panel (source in `webui/`, build output auto-generated to `static/admin` during deployment). ## Architecture Overview ```mermaid flowchart LR Client["πŸ–₯️ Clients\n(OpenAI / Claude compat)"] subgraph DS2API["DS2API Service"] direction TB CORS["CORS Middleware"] Auth["πŸ” Auth Middleware"] subgraph Adapters["Adapter Layer"] OA["OpenAI Adapter\n/v1/*"] CA["Claude Adapter\n/anthropic/*"] end subgraph Support["Support Modules"] Pool["πŸ“¦ Account Pool / Queue"] PoW["βš™οΈ PoW WASM\n(wazero)"] end Admin["πŸ› οΈ Admin API\n/admin/*"] WebUI["🌐 WebUI\n(/admin)"] end DS["☁️ DeepSeek API"] Client -- "Request" --> CORS --> Auth Auth --> OA & CA OA & CA -- "Call" --> DS Auth --> Admin OA & CA -. "Rotate accounts" .-> Pool OA & CA -. "Compute PoW" .-> PoW DS -- "Response" --> Client ``` - **Backend**: Go (`cmd/ds2api/`, `api/`, `internal/`), no Python runtime - **Frontend**: React admin panel (`webui/`), served as static build at runtime - **Deployment**: local run, Docker, Vercel serverless, Linux systemd ## Key Capabilities | Capability | Details | | --- | --- | | OpenAI compatible | `GET /v1/models`, `POST /v1/chat/completions` (stream/non-stream) | | Claude compatible | `GET /anthropic/v1/models`, `POST /anthropic/v1/messages`, `POST /anthropic/v1/messages/count_tokens` | | Multi-account rotation | Auto token refresh, email/mobile dual login | | Concurrency control | Per-account in-flight limit + waiting queue, dynamic recommended concurrency | | DeepSeek PoW | WASM solving via `wazero`, no external Node.js dependency | | Tool Calling | Anti-leak handling: auto buffer, detect, structured output | | Admin API | Config management, account testing/batch test, import/export, Vercel sync | | WebUI Admin Panel | SPA at `/admin` (bilingual Chinese/English, dark mode) | | Health Probes | `GET /healthz` (liveness), `GET /readyz` (readiness) | ## Model Support ### OpenAI Endpoint | Model | thinking | search | | --- | --- | --- | | `deepseek-chat` | ❌ | ❌ | | `deepseek-reasoner` | βœ… | ❌ | | `deepseek-chat-search` | ❌ | βœ… | | `deepseek-reasoner-search` | βœ… | βœ… | ### Claude Endpoint | Model | Default Mapping | | --- | --- | | `claude-sonnet-4-5` | `deepseek-chat` | | `claude-haiku-4-5` (compatible with `claude-3-5-haiku-latest`) | `deepseek-chat` | | `claude-opus-4-6` | `deepseek-reasoner` | Override mapping via `claude_mapping` or `claude_model_mapping` in config. In addition, `/anthropic/v1/models` now includes historical Claude 1.x/2.x/3.x/4.x IDs and common aliases for legacy client compatibility. ## Quick Start ### Option 1: Local Run **Prerequisites**: Go 1.24+, Node.js 20+ (only if building WebUI locally) ```bash # 1. Clone git clone https://github.com/CJackHwang/ds2api.git cd ds2api # 2. Configure cp config.example.json config.json # Edit config.json with your DeepSeek account info and API keys # 3. Start go run ./cmd/ds2api ``` Default URL: `http://localhost:5001` > **WebUI auto-build**: On first local startup, if `static/admin` is missing, DS2API will auto-run `npm install && npm run build` (requires Node.js). You can also build manually: `./scripts/build-webui.sh` ### Option 2: Docker ```bash # 1. Configure environment cp .env.example .env # Edit .env # 2. Start docker-compose up -d # 3. View logs docker-compose logs -f ``` Rebuild after updates: `docker-compose up -d --build` ### Option 3: Vercel 1. Fork this repo to your GitHub account 2. Import the project on Vercel 3. Set environment variables (minimum: `DS2API_ADMIN_KEY` and `DS2API_CONFIG_JSON`) 4. Deploy > **Streaming note**: `/v1/chat/completions` on Vercel is routed to `api/chat-stream.js` (Node Runtime) for real-time SSE. Auth, account selection, and session/PoW preparation are still handled by the Go internal prepare endpoint; streaming output (including `tools`) is assembled on Node with Go-aligned anti-leak handling. For detailed deployment instructions, see the [Deployment Guide](DEPLOY.en.md). ### Option 4: Download Release Binaries GitHub Actions automatically builds multi-platform archives on each Release: ```bash # After downloading the archive for your platform tar -xzf ds2api_v1.7.0_linux_amd64.tar.gz cd ds2api_v1.7.0_linux_amd64 cp config.example.json config.json # Edit config.json ./ds2api ``` ### Option 5: OpenCode CLI 1. Copy the example config: ```bash cp opencode.json.example opencode.json ``` 2. Edit `opencode.json`: - Set `baseURL` to your DS2API endpoint (for example, `https://your-domain.com/v1`) - Set `apiKey` to your DS2API key (from `config.keys`) 3. Start OpenCode CLI in the project directory (run `opencode` using your installed method). > Recommended: use the OpenAI-compatible path (`/v1/*`) via `@ai-sdk/openai-compatible` as shown in the example. ## Configuration ### `config.json` Example ```json { "keys": ["your-api-key-1", "your-api-key-2"], "accounts": [ { "email": "user@example.com", "password": "your-password", "token": "" }, { "mobile": "12345678901", "password": "your-password", "token": "" } ], "claude_model_mapping": { "fast": "deepseek-chat", "slow": "deepseek-reasoner" } } ``` - `keys`: API access keys; clients authenticate via `Authorization: Bearer ` - `accounts`: DeepSeek account list, supports `email` or `mobile` login - `token`: Leave empty for auto-login on first request; or pre-fill an existing token - `claude_model_mapping`: Maps `fast`/`slow` suffixes to corresponding DeepSeek models ### Environment Variables | Variable | Purpose | Default | | --- | --- | --- | | `PORT` | Service port | `5001` | | `LOG_LEVEL` | Log level | `INFO` (`DEBUG`/`WARN`/`ERROR`) | | `DS2API_ADMIN_KEY` | Admin login key | `admin` | | `DS2API_JWT_SECRET` | Admin JWT signing secret | Same as `DS2API_ADMIN_KEY` | | `DS2API_JWT_EXPIRE_HOURS` | Admin JWT TTL in hours | `24` | | `DS2API_CONFIG_PATH` | Config file path | `config.json` | | `DS2API_CONFIG_JSON` | Inline config (JSON or Base64) | β€” | | `DS2API_WASM_PATH` | PoW WASM file path | Auto-detect | | `DS2API_STATIC_ADMIN_DIR` | Admin static assets dir | `static/admin` | | `DS2API_AUTO_BUILD_WEBUI` | Auto-build WebUI on startup | Enabled locally, disabled on Vercel | | `DS2API_ACCOUNT_MAX_INFLIGHT` | Max in-flight requests per account | `2` | | `DS2API_ACCOUNT_CONCURRENCY` | Alias (legacy compat) | β€” | | `DS2API_ACCOUNT_MAX_QUEUE` | Waiting queue limit | `recommended_concurrency` | | `DS2API_ACCOUNT_QUEUE_SIZE` | Alias (legacy compat) | β€” | | `DS2API_VERCEL_INTERNAL_SECRET` | Vercel hybrid streaming internal auth | Falls back to `DS2API_ADMIN_KEY` | | `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | Stream lease TTL seconds | `900` | | `VERCEL_TOKEN` | Vercel sync token | β€” | | `VERCEL_PROJECT_ID` | Vercel project ID | β€” | | `VERCEL_TEAM_ID` | Vercel team ID | β€” | | `DS2API_VERCEL_PROTECTION_BYPASS` | Vercel deployment protection bypass for internal Nodeβ†’Go calls | β€” | ## Authentication Modes For business endpoints (`/v1/*`, `/anthropic/*`), DS2API supports two modes: | Mode | Description | | --- | --- | | **Managed account** | Use a key from `config.keys` via `Authorization: Bearer ...` or `x-api-key`; DS2API auto-selects an account | | **Direct token** | If the token is not in `config.keys`, DS2API treats it as a DeepSeek token directly | Optional header `X-Ds2-Target-Account`: Pin a specific managed account (value is email or mobile). ## Concurrency Model ``` Per-account inflight = DS2API_ACCOUNT_MAX_INFLIGHT (default 2) Recommended concurrency = account_count Γ— per_account_inflight Queue limit = DS2API_ACCOUNT_MAX_QUEUE (default = recommended concurrency) 429 threshold = inflight + queue β‰ˆ account_count Γ— 4 ``` - When inflight slots are full, requests enter a waiting queue β€” **no immediate 429** - 429 is returned only when total load exceeds inflight + queue capacity - `GET /admin/queue/status` returns real-time concurrency state ## Tool Call Adaptation When `tools` is present in the request, DS2API performs anti-leak handling: 1. With `stream=true`, DS2API **buffers** text deltas first 2. If a tool call is detected β†’ only structured `tool_calls` are emitted, raw JSON is not leaked 3. If no tool call β†’ buffered text is emitted at once 4. Parser supports mixed text, fenced JSON, and `function.arguments` payloads ## Project Structure ```text ds2api/ β”œβ”€β”€ cmd/ β”‚ β”œβ”€β”€ ds2api/ # Local / container entrypoint β”‚ └── ds2api-tests/ # End-to-end testsuite entrypoint β”œβ”€β”€ api/ β”‚ β”œβ”€β”€ index.go # Vercel Serverless Go entry β”‚ β”œβ”€β”€ chat-stream.js # Vercel Node.js stream relay β”‚ └── helpers/ # Node.js helper modules β”œβ”€β”€ internal/ β”‚ β”œβ”€β”€ account/ # Account pool and concurrency queue β”‚ β”œβ”€β”€ adapter/ β”‚ β”‚ β”œβ”€β”€ openai/ # OpenAI adapter (incl. tool call parsing, Vercel stream prepare/release) β”‚ β”‚ └── claude/ # Claude adapter β”‚ β”œβ”€β”€ admin/ # Admin API handlers β”‚ β”œβ”€β”€ auth/ # Auth and JWT β”‚ β”œβ”€β”€ config/ # Config loading and hot-reload β”‚ β”œβ”€β”€ deepseek/ # DeepSeek API client, PoW WASM β”‚ β”œβ”€β”€ server/ # HTTP routing and middleware (chi router) β”‚ β”œβ”€β”€ sse/ # SSE parsing utilities β”‚ β”œβ”€β”€ util/ # Common utilities β”‚ └── webui/ # WebUI static file serving and auto-build β”œβ”€β”€ webui/ # React WebUI source (Vite + Tailwind) β”‚ └── src/ β”‚ β”œβ”€β”€ components/ # AccountManager / ApiTester / BatchImport / VercelSync / Login / LandingPage β”‚ └── locales/ # Language packs (zh.json / en.json) β”œβ”€β”€ scripts/ β”‚ β”œβ”€β”€ build-webui.sh # Manual WebUI build script β”‚ └── testsuite/ # Testsuite runner scripts β”œβ”€β”€ static/admin/ # WebUI build output (not committed to Git) β”œβ”€β”€ .github/ β”‚ β”œβ”€β”€ workflows/ # GitHub Actions (Release artifact automation) β”‚ β”œβ”€β”€ ISSUE_TEMPLATE/ # Issue templates β”‚ └── PULL_REQUEST_TEMPLATE.md β”œβ”€β”€ config.example.json # Config file template β”œβ”€β”€ .env.example # Environment variable template β”œβ”€β”€ Dockerfile # Multi-stage build (WebUI + Go) β”œβ”€β”€ docker-compose.yml # Production Docker Compose β”œβ”€β”€ docker-compose.dev.yml # Development Docker Compose β”œβ”€β”€ vercel.json # Vercel routing and build config β”œβ”€β”€ go.mod / go.sum # Go module dependencies └── version.txt # Version number ``` ## Documentation Index | Document | Description | | --- | --- | | [API.md](API.md) / [API.en.md](API.en.md) | API reference with request/response examples | | [DEPLOY.md](DEPLOY.md) / [DEPLOY.en.md](DEPLOY.en.md) | Deployment guide (local/Docker/Vercel/systemd) | | [CONTRIBUTING.md](CONTRIBUTING.md) / [CONTRIBUTING.en.md](CONTRIBUTING.en.md) | Contributing guide | | [TESTING.md](TESTING.md) | Testsuite guide | ## Testing ```bash # Unit tests go test ./... # One-command live end-to-end tests (real accounts, full request/response logs) ./scripts/testsuite/run-live.sh # Or with custom flags go run ./cmd/ds2api-tests \ --config config.json \ --admin-key admin \ --out artifacts/testsuite \ --timeout 120 \ --retries 2 ``` ## Release Artifact Automation (GitHub Actions) Workflow: `.github/workflows/release-artifacts.yml` - **Trigger**: only on GitHub Release `published` (normal pushes do not trigger builds) - **Outputs**: multi-platform archives (`linux/amd64`, `linux/arm64`, `darwin/amd64`, `darwin/arm64`, `windows/amd64`) + `sha256sums.txt` - **Each archive includes**: `ds2api` executable, `static/admin`, WASM file, config template, README, LICENSE ## Disclaimer This project is built through reverse engineering and is provided for learning and research only. Stability is not guaranteed. Do not use it in scenarios that violate terms of service or laws.