docs: make English docs canonical, drop bilingual Chinese variants
Replace the Chinese README/API/ARCHITECTURE/CONTRIBUTING/DEPLOY with their English versions and remove the now-redundant .en.md files. Strip the language switchers and rewrite all .en.md links to the canonical .md paths; update the release artifact list accordingly.
This commit is contained in:
@@ -79,7 +79,7 @@ jobs:
|
||||
CGO_ENABLED=0 GOOS="${GOOS}" GOARCH="${GOARCH}" \
|
||||
go build -trimpath -ldflags="-s -w -X ds2api/internal/version.BuildVersion=${BUILD_VERSION}" -o "${STAGE}/${BIN}" ./cmd/ds2api
|
||||
|
||||
cp config.example.json .env.example LICENSE README.MD README.en.md "${STAGE}/"
|
||||
cp config.example.json .env.example LICENSE README.MD "${STAGE}/"
|
||||
cp -R static/admin "${STAGE}/static/admin"
|
||||
|
||||
if [ "${GOOS}" = "windows" ]; then
|
||||
|
||||
@@ -270,7 +270,7 @@ If you change config shape, check:
|
||||
- `docs/prompt-compatibility.md` is the source-of-truth document for the “API -> pure-text web-chat context” compatibility flow.
|
||||
- If a change affects message normalization, tool prompt injection, prompt-visible tool history, file/reference handling, history split, or completion payload assembly, update `docs/prompt-compatibility.md` in the same change.
|
||||
- If a change affects route layout, module boundaries, or major flow descriptions, update `docs/ARCHITECTURE.md`.
|
||||
- If a change affects external request or response behavior, update `API.md` and `API.en.md`.
|
||||
- If a change affects external request or response behavior, update `API.md`.
|
||||
- If a change affects deployment behavior, Vercel/Docker entrypoints, or required environment variables, update `docs/DEPLOY.md`.
|
||||
- If a change affects developer workflow or verification steps, update `docs/TESTING.md` and/or `docs/CONTRIBUTING.md`.
|
||||
|
||||
|
||||
@@ -12,50 +12,47 @@
|
||||
[](https://zeabur.com/templates/L4CFHP)
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/CJackHwang/ds2api)
|
||||
|
||||
语言 / Language: [中文](README.MD) | [English](README.en.md)
|
||||
|
||||
将 DeepSeek Web 对话能力转换为 OpenAI、Claude 与 Gemini 兼容 API。后端为 **Go 全量实现**,前端为 React WebUI 管理台(源码在 `webui/`,部署时自动构建到 `static/admin`)。
|
||||
DS2API converts DeepSeek Web chat capability into OpenAI-compatible, Claude-compatible, and Gemini-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).
|
||||
|
||||
文档入口:[文档导航](docs/README.md) / [架构说明](docs/ARCHITECTURE.md) / [接口文档](API.md)
|
||||
Documentation entry: [Docs Index](docs/README.md) / [Architecture](docs/ARCHITECTURE.md) / [API Reference](API.md)
|
||||
|
||||
【感谢Linux.do社区及GitHub社区各位开发者对项目的支持与贡献】
|
||||
|
||||
> **重要免责声明**
|
||||
> **Important Disclaimer**
|
||||
>
|
||||
> 本仓库仅供学习、研究、个人实验和内部验证使用,不提供任何形式的商业授权、适用性保证或结果保证。
|
||||
> This repository is provided for learning, research, personal experimentation, and internal validation only. It does not grant any commercial authorization and comes with no warranty of fitness, stability, or results.
|
||||
>
|
||||
> 作者及仓库维护者不对因使用、修改、分发、部署或依赖本项目而产生的任何直接或间接损失、账号封禁、数据丢失、法律风险或第三方索赔负责。
|
||||
> The author and repository maintainers are not responsible for any direct or indirect loss, account suspension, data loss, legal risk, or third-party claims arising from use, modification, distribution, deployment, or reliance on this project.
|
||||
>
|
||||
> 请勿将本项目用于违反服务条款、协议、法律法规或平台规则的场景。商业使用前请自行确认 `LICENSE`、相关协议以及你是否获得了作者的书面许可。
|
||||
> Do not use this project in ways that violate service terms, agreements, laws, or platform rules. Before any commercial use, review the `LICENSE`, the relevant terms, and confirm that you have the author's written permission.
|
||||
|
||||
## 架构概览(摘要)
|
||||
## Architecture Overview (Summary)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Client["🖥️ 客户端 / SDK\n(OpenAI / Claude / Gemini)"]
|
||||
Client["🖥️ Clients / SDKs\n(OpenAI / Claude / Gemini)"]
|
||||
Upstream["☁️ DeepSeek API"]
|
||||
|
||||
subgraph DS2API["DS2API 4.x(模块化 HTTP surface + PromptCompat 内核)"]
|
||||
Router["chi Router + 中间件\n(RequestID / RealIP / Logger / Recoverer / CORS)"]
|
||||
subgraph DS2API["DS2API 4.x (Modular HTTP Surface + PromptCompat Core)"]
|
||||
Router["chi Router + Middleware\n(RequestID / RealIP / Logger / Recoverer / CORS)"]
|
||||
|
||||
subgraph HTTP["HTTP API surface"]
|
||||
subgraph HTTP["HTTP API Surface"]
|
||||
OA["OpenAI\nchat / responses / files / embeddings"]
|
||||
CA["Claude\n/anthropic/* + /v1/messages"]
|
||||
GA["Gemini\n/v1beta/models/* + /v1/models/*"]
|
||||
Admin["Admin API\n资源子包"]
|
||||
WebUI["WebUI\n/admin(静态托管)"]
|
||||
Admin["Admin API\nresource packages"]
|
||||
WebUI["WebUI\n/admin (static hosting)"]
|
||||
Vercel["Vercel Node Stream\n/v1/chat/completions"]
|
||||
end
|
||||
|
||||
subgraph Runtime["运行时核心能力"]
|
||||
Compat["PromptCompat\n(API -> 网页纯文本上下文)"]
|
||||
Chat["Chat / Responses Runtime\n(统一工具调用与流式语义)"]
|
||||
subgraph Runtime["Runtime + Core Capabilities"]
|
||||
Compat["PromptCompat\n(API -> web-chat plain text context)"]
|
||||
Chat["Chat / Responses Runtime\n(unified tools + stream semantics)"]
|
||||
Auth["Auth Resolver\n(API key / bearer / x-goog-api-key)"]
|
||||
Pool["Account Pool + Queue\n(并发槽位 + 等待队列)"]
|
||||
DSClient["DeepSeek Client\n(Session / Auth / Completion / Files)"]
|
||||
Pow["PoW 实现\n(纯 Go)"]
|
||||
Tool["Tool Sieve\n(Go/Node 语义对齐)"]
|
||||
History["History Split\n(长历史文件化)"]
|
||||
Pool["Account Pool + Queue\n(in-flight slots + wait queue)"]
|
||||
DSClient["DeepSeek Client\n(session / auth / completion / files)"]
|
||||
Pow["PoW Solver\n(Pure Go)"]
|
||||
Tool["Tool Sieve\n(Go/Node semantic parity)"]
|
||||
History["History Split\n(long history as files)"]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -68,13 +65,13 @@ flowchart LR
|
||||
OA --> Compat
|
||||
CA & GA --> Compat
|
||||
Compat --> Chat
|
||||
Compat -.长历史.-> History
|
||||
Compat -.long history.-> History
|
||||
Vercel -.Go prepare.-> Chat
|
||||
Vercel -.Node SSE.-> Tool
|
||||
Chat --> Auth
|
||||
Chat -.账号轮询.-> Pool
|
||||
Chat -.工具调用解析.-> Tool
|
||||
Chat -.PoW 计算.-> Pow
|
||||
Chat -.account rotation.-> Pool
|
||||
Chat -.tool-call parsing.-> Tool
|
||||
Chat -.PoW solving.-> Pow
|
||||
Auth --> DSClient
|
||||
DSClient --> Upstream
|
||||
Upstream --> DSClient
|
||||
@@ -82,250 +79,248 @@ flowchart LR
|
||||
Vercel --> Client
|
||||
```
|
||||
|
||||
详细架构拆分与目录职责见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。
|
||||
For the full module-by-module architecture and directory responsibilities, see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
||||
|
||||
- **后端**:Go(`cmd/ds2api/`、`api/`、`internal/`),不依赖 Python 运行时
|
||||
- **前端**:React 管理台(`webui/`),运行时托管静态构建产物
|
||||
- **部署**:本地运行、Docker、Vercel Serverless、Linux systemd
|
||||
- **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 兼容 | `GET /v1/models`、`GET /v1/models/{id}`、`POST /v1/chat/completions`、`POST /v1/responses`、`GET /v1/responses/{response_id}`、`POST /v1/embeddings`、`POST /v1/files` |
|
||||
| Claude 兼容 | `GET /anthropic/v1/models`、`POST /anthropic/v1/messages`、`POST /anthropic/v1/messages/count_tokens`(及快捷路径 `/v1/messages`、`/messages`) |
|
||||
| Gemini 兼容 | `POST /v1beta/models/{model}:generateContent`、`POST /v1beta/models/{model}:streamGenerateContent`(及 `/v1/models/{model}:*` 路径) |
|
||||
| 统一 CORS 兼容 | `/v1/*`、`/anthropic/*`、`/v1beta/models/*`、`/admin/*` 统一走同一套 CORS 策略;Vercel 上 `/v1/chat/completions` 的 Node Runtime 也对齐相同放行规则,尽量减少第三方预检请求头限制 |
|
||||
| 多账号轮询 | 自动 token 刷新、邮箱/手机号双登录方式 |
|
||||
| 并发队列控制 | 每账号 in-flight 上限 + 等待队列,动态计算建议并发值 |
|
||||
| DeepSeek PoW | 纯 Go 高性能实现(DeepSeekHashV1),毫秒级响应 |
|
||||
| Tool Calling | 防泄漏处理:非代码块高置信特征识别、`delta.tool_calls` 早发、结构化增量输出 |
|
||||
| Admin API | 配置管理、运行时设置热更新、代理管理、账号测试 / 批量测试、会话清理、导入导出、Vercel 同步、版本检查 |
|
||||
| WebUI 管理台 | `/admin` 单页应用(中英文双语、深色模式,支持查看服务器端对话记录) |
|
||||
| 运维探针 | `GET /healthz`(存活)、`GET /readyz`(就绪) |
|
||||
| OpenAI compatible | `GET /v1/models`, `GET /v1/models/{id}`, `POST /v1/chat/completions`, `POST /v1/responses`, `GET /v1/responses/{response_id}`, `POST /v1/embeddings`, `POST /v1/files` |
|
||||
| Claude compatible | `GET /anthropic/v1/models`, `POST /anthropic/v1/messages`, `POST /anthropic/v1/messages/count_tokens` (plus shortcut paths `/v1/messages`, `/messages`) |
|
||||
| Gemini compatible | `POST /v1beta/models/{model}:generateContent`, `POST /v1beta/models/{model}:streamGenerateContent` (plus `/v1/models/{model}:*` paths) |
|
||||
| Unified CORS compatibility | `/v1/*`, `/anthropic/*`, `/v1beta/models/*`, and `/admin/*` share one CORS policy; on Vercel, the Node Runtime for `/v1/chat/completions` mirrors the same relaxed preflight behavior for third-party clients |
|
||||
| Multi-account rotation | Auto token refresh, email/mobile dual login |
|
||||
| Concurrency control | Per-account in-flight limit + waiting queue, dynamic recommended concurrency |
|
||||
| DeepSeek PoW | Pure Go high-performance solver (DeepSeekHashV1), ms-level response |
|
||||
| Tool Calling | Anti-leak handling: non-code-block feature match, early `delta.tool_calls`, structured incremental output |
|
||||
| Admin API | Config management, runtime settings hot-reload, proxy management, account testing/batch test, session cleanup, import/export, Vercel sync, version check |
|
||||
| WebUI Admin Panel | SPA at `/admin` (bilingual Chinese/English, dark mode, with server-side conversation history) |
|
||||
| Health Probes | `GET /healthz` (liveness), `GET /readyz` (readiness) |
|
||||
|
||||
## 平台兼容矩阵
|
||||
## Platform Compatibility Matrix
|
||||
|
||||
| 级别 | 平台 | 当前状态 |
|
||||
| Tier | Platform | Status |
|
||||
| --- | --- | --- |
|
||||
| P0 | Codex CLI/SDK(`wire_api=chat` / `wire_api=responses`) | ✅ |
|
||||
| P0 | OpenAI SDK(JS/Python,chat + responses) | ✅ |
|
||||
| P0 | Vercel AI SDK(openai-compatible) | ✅ |
|
||||
| P0 | Anthropic SDK(messages) | ✅ |
|
||||
| P0 | Google Gemini SDK(generateContent) | ✅ |
|
||||
| P1 | LangChain / LlamaIndex / OpenWebUI(OpenAI 兼容接入) | ✅ |
|
||||
| P0 | Codex CLI/SDK (`wire_api=chat` / `wire_api=responses`) | ✅ |
|
||||
| P0 | OpenAI SDK (JS/Python, chat + responses) | ✅ |
|
||||
| P0 | Vercel AI SDK (openai-compatible) | ✅ |
|
||||
| P0 | Anthropic SDK (messages) | ✅ |
|
||||
| P0 | Google Gemini SDK (generateContent) | ✅ |
|
||||
| P1 | LangChain / LlamaIndex / OpenWebUI (OpenAI-compatible integration) | ✅ |
|
||||
|
||||
## 模型支持
|
||||
## Model Support
|
||||
|
||||
### OpenAI 接口(`GET /v1/models`)
|
||||
### OpenAI Endpoint (`GET /v1/models`)
|
||||
|
||||
| 模型类型 | 模型 ID | thinking | search |
|
||||
| Family | Model ID | thinking | search |
|
||||
| --- | --- | --- | --- |
|
||||
| default | `deepseek-v4-flash` | 默认开启,可由请求参数控制 | ❌ |
|
||||
| expert | `deepseek-v4-pro` | 默认开启,可由请求参数控制 | ❌ |
|
||||
| default | `deepseek-v4-flash-search` | 默认开启,可由请求参数控制 | ✅ |
|
||||
| expert | `deepseek-v4-pro-search` | 默认开启,可由请求参数控制 | ✅ |
|
||||
| vision | `deepseek-v4-vision` | 默认开启,可由请求参数控制 | ❌ |
|
||||
| vision | `deepseek-v4-vision-search` | 默认开启,可由请求参数控制 | ✅ |
|
||||
| default | `deepseek-v4-flash` | enabled by default, request-controlled | ❌ |
|
||||
| expert | `deepseek-v4-pro` | enabled by default, request-controlled | ❌ |
|
||||
| default | `deepseek-v4-flash-search` | enabled by default, request-controlled | ✅ |
|
||||
| expert | `deepseek-v4-pro-search` | enabled by default, request-controlled | ✅ |
|
||||
| vision | `deepseek-v4-vision` | enabled by default, request-controlled | ❌ |
|
||||
| vision | `deepseek-v4-vision-search` | enabled by default, request-controlled | ✅ |
|
||||
|
||||
除原生模型外,也支持常见 alias 输入(如 `gpt-4.1`、`gpt-5`、`gpt-5-codex`、`o3`、`claude-*`、`gemini-*` 等),但 `/v1/models` 返回的是规范化后的 DeepSeek 原生模型 ID。完整 alias 行为以 [API.md](API.md#模型-alias-解析策略) 和 `config.example.json` 为准。
|
||||
Besides native IDs, DS2API also accepts common aliases as input (for example `gpt-4.1`, `gpt-5`, `gpt-5-codex`, `o3`, `claude-*`, `gemini-*`), but `/v1/models` returns normalized DeepSeek native model IDs. The complete alias behavior is documented in [API.md](API.md#model-alias-resolution) and `config.example.json`.
|
||||
|
||||
### Claude 接口(`GET /anthropic/v1/models`)
|
||||
### Claude Endpoint (`GET /anthropic/v1/models`)
|
||||
|
||||
| 当前常用模型 | 默认映射 |
|
||||
| Current common model | Default Mapping |
|
||||
| --- | --- |
|
||||
| `claude-sonnet-4-6` | `deepseek-v4-flash` |
|
||||
| `claude-haiku-4-5`(兼容 `claude-3-5-haiku-latest`) | `deepseek-v4-flash` |
|
||||
| `claude-haiku-4-5` (compatible with `claude-3-5-haiku-latest`) | `deepseek-v4-flash` |
|
||||
| `claude-opus-4-6` | `deepseek-v4-pro` |
|
||||
|
||||
可通过配置中的 `model_aliases` 覆盖映射关系。
|
||||
`/anthropic/v1/models` 除上述主别名外,还会返回 Claude 4.x snapshots、3.x 历史模型 ID 与常见 alias,便于旧客户端直接兼容。
|
||||
Override mapping via the global `model_aliases` config.
|
||||
Besides the primary aliases above, `/anthropic/v1/models` also returns Claude 4.x snapshots plus historical 3.x IDs and common aliases for legacy client compatibility.
|
||||
|
||||
#### Claude Code 接入避坑(实测)
|
||||
#### Claude Code integration pitfalls (validated)
|
||||
|
||||
- `ANTHROPIC_BASE_URL` 推荐直接指向 DS2API 根地址(例如 `http://127.0.0.1:5001`),Claude Code 会请求 `/v1/messages?beta=true`。
|
||||
- `ANTHROPIC_API_KEY` 需要与 `config.json` 中 `keys` 一致;建议同时保留常规 key 与 `sk-ant-*` 形态 key,兼容不同客户端校验习惯。
|
||||
- 若系统设置了代理,建议对 DS2API 地址配置 `NO_PROXY=127.0.0.1,localhost,<你的主机IP>`,避免本地回环请求被代理拦截。
|
||||
- 如遇“工具调用输出成文本、未执行”问题,请优先检查模型输出是否为当前唯一受支持的 XML 工具块:`<tool_calls><invoke name="..."><parameter name="...">...`,而不是旧式 `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`、`<function_call>`、`tool_use` 或纯 JSON `tool_calls` 片段。
|
||||
- Set `ANTHROPIC_BASE_URL` to the DS2API root URL (for example `http://127.0.0.1:5001`). Claude Code sends requests to `/v1/messages?beta=true`.
|
||||
- `ANTHROPIC_API_KEY` must match an entry in `keys` from `config.json`. Keeping both a regular key and an `sk-ant-*` style key improves client compatibility.
|
||||
- If your environment has proxy variables, set `NO_PROXY=127.0.0.1,localhost,<your_host_ip>` for DS2API to avoid proxy interception of local traffic.
|
||||
- If tool calls are rendered as plain text and not executed, first verify the model output uses the only supported XML block: `<tool_calls><invoke name="..."><parameter name="...">...`, not legacy `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`, `<function_call>`, `tool_use`, or standalone JSON `tool_calls`.
|
||||
|
||||
### Gemini 接口
|
||||
### Gemini Endpoint
|
||||
|
||||
Gemini 适配器将模型名通过 `model_aliases` 或内置规则映射到 DeepSeek 原生模型,支持 `generateContent` 和 `streamGenerateContent` 两种调用方式,并完整支持 Tool Calling(`functionDeclarations` → `functionCall` 输出)。
|
||||
The Gemini adapter maps model names to DeepSeek native models via `model_aliases` or built-in heuristics, supporting both `generateContent` and `streamGenerateContent` call patterns with full Tool Calling support (`functionDeclarations` → `functionCall` output).
|
||||
|
||||
## 快速开始
|
||||
## Quick Start
|
||||
|
||||
### 部署方式优先级建议
|
||||
### Recommended deployment priority
|
||||
|
||||
推荐按以下顺序选择部署方式:
|
||||
Recommended order when choosing a deployment method:
|
||||
|
||||
1. **下载 Release 构建包运行**:最省事,产物已编译完成,最适合大多数用户。
|
||||
2. **Docker / GHCR 镜像部署**:适合需要容器化、编排或云环境部署。
|
||||
3. **Vercel 部署**:适合已有 Vercel 环境且接受其平台约束的场景。
|
||||
4. **本地源码运行 / 自行编译**:适合开发、调试或需要自行修改代码的场景。
|
||||
1. **Download and run release binaries**: the easiest path for most users because the artifacts are already built.
|
||||
2. **Docker / GHCR image deployment**: suitable for containerized, orchestrated, or cloud environments.
|
||||
3. **Vercel deployment**: suitable if you already use Vercel and accept its platform constraints.
|
||||
4. **Run from source / build locally**: suitable for development, debugging, or when you need to modify the code yourself.
|
||||
|
||||
### 通用第一步(所有部署方式)
|
||||
### Universal First Step (all deployment modes)
|
||||
|
||||
把 `config.json` 作为唯一配置源(推荐做法):
|
||||
Use `config.json` as the single source of truth (recommended):
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
后续部署建议:
|
||||
- 本地运行:直接读取 `config.json`
|
||||
- Docker / Vercel:由 `config.json` 生成 `DS2API_CONFIG_JSON`(Base64)注入环境变量,也可以直接写原始 JSON
|
||||
Recommended per deployment mode:
|
||||
- Local run: read `config.json` directly
|
||||
- Docker / Vercel: generate Base64 from `config.json` and inject as `DS2API_CONFIG_JSON`, or paste raw JSON directly
|
||||
|
||||
WebUI 管理台里的“全量配置模板”也直接复用同一份 `config.example.json`,所以更新示例文件后,前端模板会自动保持一致。
|
||||
The WebUI admin panel’s “Full configuration template” is loaded from the same `config.example.json`, so updating that file keeps the frontend template in sync.
|
||||
|
||||
### 方式一:下载 Release 构建包
|
||||
### Option 1: Download Release Binaries
|
||||
|
||||
每次发布 Release 时,GitHub Actions 会自动构建多平台二进制包:
|
||||
GitHub Actions automatically builds multi-platform archives on each Release:
|
||||
|
||||
```bash
|
||||
# 下载对应平台的压缩包后
|
||||
# After downloading the archive for your platform
|
||||
tar -xzf ds2api_<tag>_linux_amd64.tar.gz
|
||||
cd ds2api_<tag>_linux_amd64
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
./ds2api
|
||||
```
|
||||
|
||||
### 方式二:Docker 运行
|
||||
### Option 2: Docker / GHCR
|
||||
|
||||
```bash
|
||||
# 1. 准备环境变量和配置文件
|
||||
# Pull prebuilt image
|
||||
docker pull ghcr.io/cjackhwang/ds2api:latest
|
||||
|
||||
# Or run a pinned version
|
||||
# docker pull ghcr.io/cjackhwang/ds2api:v3.0.0
|
||||
|
||||
# Prepare env file and config file
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# 2. 编辑 .env(至少设置 DS2API_ADMIN_KEY;如需修改宿主机端口,可额外设置 DS2API_HOST_PORT)
|
||||
# DS2API_ADMIN_KEY=请替换为强密码
|
||||
|
||||
# 3. 启动
|
||||
# Start with compose
|
||||
docker-compose up -d
|
||||
|
||||
# 4. 查看日志
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
默认 `docker-compose.yml` 会把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请设置 `DS2API_HOST_PORT=5001`(或者手动调整 `ports` 配置)。
|
||||
The default `docker-compose.yml` uses `ghcr.io/cjackhwang/ds2api:latest` and maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
更新镜像:`docker-compose up -d --build`
|
||||
Rebuild after updates: `docker-compose up -d --build`
|
||||
|
||||
#### Zeabur 一键部署(Dockerfile)
|
||||
#### Zeabur One-Click (Dockerfile)
|
||||
|
||||
1. 点击上方 “Deploy on Zeabur” 按钮,一键部署。
|
||||
2. 部署完成后访问 `/admin`,使用 Zeabur 环境变量/模板指引中的 `DS2API_ADMIN_KEY` 登录。
|
||||
3. 在管理台导入/编辑配置(会写入并持久化到 `/data/config.json`)。
|
||||
1. Click the “Deploy on Zeabur” button above to deploy.
|
||||
2. After deployment, open `/admin` and login with `DS2API_ADMIN_KEY` shown in Zeabur env/template instructions.
|
||||
3. Import / edit config in Admin UI (it will be written and persisted to `/data/config.json`).
|
||||
|
||||
说明:Zeabur 使用仓库内 `Dockerfile` 直接构建时,不需要额外传入 `BUILD_VERSION`;镜像会优先读取该构建参数,未提供时自动回退到仓库根目录的 `VERSION` 文件。
|
||||
Note: when Zeabur builds directly from the repo `Dockerfile`, you do not need to pass `BUILD_VERSION`. The image prefers that build arg when provided, and automatically falls back to the repo-root `VERSION` file when it is absent.
|
||||
|
||||
### 方式三:Vercel 部署
|
||||
### Option 3: Vercel
|
||||
|
||||
1. Fork 仓库到自己的 GitHub
|
||||
2. 在 Vercel 上导入项目
|
||||
3. 配置环境变量(最少设置 `DS2API_ADMIN_KEY`;推荐同时设置 `DS2API_CONFIG_JSON`)
|
||||
4. 部署
|
||||
1. Fork this repo to your GitHub account
|
||||
2. Import the project on Vercel
|
||||
3. Set environment variables (minimum: `DS2API_ADMIN_KEY`; recommended to also set `DS2API_CONFIG_JSON`)
|
||||
4. Deploy
|
||||
|
||||
建议先在仓库目录复制模板并填写:
|
||||
Recommended first step in repo root:
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
推荐:先本地把 `config.json` 转成 Base64,再粘贴到 `DS2API_CONFIG_JSON`,避免 JSON 格式错误:
|
||||
Recommended: convert `config.json` to Base64 locally, then paste into `DS2API_CONFIG_JSON` to avoid JSON formatting mistakes:
|
||||
|
||||
```bash
|
||||
base64 < config.json | tr -d '\n'
|
||||
```
|
||||
|
||||
> **流式说明**:`/v1/chat/completions` 在 Vercel 上默认走 `api/chat-stream.js`(Node Runtime)以保证实时 SSE。鉴权、账号选择、会话/PoW 准备仍由 Go 内部 prepare 接口完成;流式响应(含 `tools`)在 Node 侧执行与 Go 对齐的输出组装与防泄漏处理。虽然这里只有 OpenAI chat 流式走 Node,但 CORS 放行策略仍与 Go 主路由保持一致,统一覆盖第三方客户端预检场景。
|
||||
> **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. This is the only interface family currently routed through Node, and its CORS allow behavior is kept aligned with the Go router so third-party preflight handling stays unified.
|
||||
|
||||
详细部署说明请参阅 [部署指南](docs/DEPLOY.md)。
|
||||
For detailed deployment instructions, see the [Deployment Guide](docs/DEPLOY.md).
|
||||
|
||||
### 方式四:本地源码运行
|
||||
### Option 4: Local Run
|
||||
|
||||
**前置要求**:Go 1.26+,Node.js `20.19+` 或 `22.12+`(仅在需要构建 WebUI 时)
|
||||
**Prerequisites**: Go 1.26+, Node.js `20.19+` or `22.12+` (only if building WebUI locally)
|
||||
|
||||
```bash
|
||||
# 1. 克隆仓库
|
||||
# 1. Clone
|
||||
git clone https://github.com/CJackHwang/ds2api.git
|
||||
cd ds2api
|
||||
|
||||
# 2. 配置
|
||||
# 2. Configure
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json,填入你的 DeepSeek 账号信息和 API key
|
||||
# Edit config.json with your DeepSeek account info and API keys
|
||||
|
||||
# 3. 启动
|
||||
# 3. Start
|
||||
go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
默认本地访问地址:`http://127.0.0.1:5001`
|
||||
Default local URL: `http://127.0.0.1:5001`
|
||||
|
||||
服务实际绑定:`0.0.0.0:5001`,因此同一局域网设备通常也可以通过你的内网 IP 访问。
|
||||
The server actually binds to `0.0.0.0:5001`, so devices on the same LAN can usually reach it through your private IP as well.
|
||||
|
||||
> **WebUI 自动构建**:本地首次启动时,若 `static/admin` 不存在,会自动尝试执行 `npm ci`(仅在缺少依赖时)和 `npm run build -- --outDir static/admin --emptyOutDir`(需要本机有 Node.js)。你也可以手动构建:`./scripts/build-webui.sh`
|
||||
> **WebUI auto-build**: On first local startup, if `static/admin` is missing, DS2API will auto-run `npm ci` (only when dependencies are missing) and `npm run build -- --outDir static/admin --emptyOutDir` (requires Node.js). You can also build manually: `./scripts/build-webui.sh`
|
||||
|
||||
## 配置说明
|
||||
## Configuration
|
||||
|
||||
`README` 只保留快速入口,完整字段请以 [config.example.json](config.example.json) 为模板,并参考 [部署指南](docs/DEPLOY.md#0-前置要求) 与 [API 配置最佳实践](API.md#配置最佳实践)。
|
||||
`README` keeps only the onboarding path. Use [config.example.json](config.example.json) as the field template, and see the [deployment guide](docs/DEPLOY.md#0-prerequisites) plus [API configuration notes](API.md#configuration-best-practice) for full details.
|
||||
|
||||
常用字段:
|
||||
Common fields:
|
||||
|
||||
- `keys` / `api_keys`:客户端访问密钥,`api_keys` 支持 `name` 与 `remark` 元信息,`keys` 继续兼容。
|
||||
- `accounts`:DeepSeek 托管账号,支持 `email` 或 `mobile` 登录,可配置代理、名称和备注。
|
||||
- `model_aliases`:OpenAI / Claude / Gemini 共用的模型 alias 映射。
|
||||
- `runtime`:账号并发、队列、token 刷新、账号健康冷却、上游文件上传开关,以及本地后台禁言扫描间隔;其中 `account_mute_scan_interval_seconds` 只随配置/重启生效,不通过 Admin Settings 热更新。
|
||||
- `auto_delete.mode`:请求结束后的远端会话清理策略,支持 `none` / `single` / `all`。
|
||||
- `history_split`:多轮历史拆分策略,已全局强制开启;可调整触发阈值,避免长历史全部内联进 prompt。
|
||||
- `keys` / `api_keys`: client API keys; `api_keys` adds `name` and `remark` metadata while `keys` remains compatible.
|
||||
- `accounts`: managed DeepSeek accounts, supporting `email` or `mobile` login plus proxy/name/remark metadata.
|
||||
- `model_aliases`: one shared alias map for OpenAI / Claude / Gemini model names.
|
||||
- `runtime`: account concurrency, queueing, token refresh, account health cooldowns, and upstream file-upload policy, hot-reloadable via Admin Settings.
|
||||
- `auto_delete.mode`: remote session cleanup after each request, supporting `none` / `single` / `all`.
|
||||
- `history_split`: multi-turn history split policy, now forced on globally; tune its trigger threshold to avoid inlining all long history into the prompt.
|
||||
|
||||
环境变量完整列表见 [部署指南](docs/DEPLOY.md),接口鉴权规则见 [API.md](API.md#鉴权规则)。
|
||||
For the full environment variable list, see [docs/DEPLOY.md](docs/DEPLOY.md). For auth behavior, see [API.md](API.md#authentication).
|
||||
|
||||
## 鉴权模式
|
||||
## Authentication Modes
|
||||
|
||||
调用业务接口(`/v1/*`、`/anthropic/*`、Gemini 路由)时支持两种模式:
|
||||
For business endpoints (`/v1/*`, `/anthropic/*`, Gemini routes), DS2API supports two modes:
|
||||
|
||||
| 模式 | 说明 |
|
||||
| Mode | Description |
|
||||
| --- | --- |
|
||||
| **托管账号模式** | `Bearer` 或 `x-api-key` 传入 `config.keys` 中的 key,由服务自动轮询选择账号 |
|
||||
| **直通 token 模式** | 传入 token 不在 `config.keys` 中时,直接作为 DeepSeek token 使用 |
|
||||
| **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 |
|
||||
|
||||
可选请求头 `X-Ds2-Target-Account`:指定使用某个托管账号(值为 email 或 mobile)。
|
||||
Gemini 路由还可以使用 `x-goog-api-key`,或在没有认证头时使用 `?key=` / `?api_key=` 作为调用方凭据。
|
||||
Optional header `X-Ds2-Target-Account`: Pin a specific managed account (value is email or mobile).
|
||||
Gemini routes also accept `x-goog-api-key`, or `?key=` / `?api_key=` when no auth header is present.
|
||||
|
||||
## 并发模型
|
||||
## Concurrency Model
|
||||
|
||||
```
|
||||
每账号可用并发 = DS2API_ACCOUNT_MAX_INFLIGHT(默认 2)
|
||||
建议并发值 = 账号数量 × 每账号并发上限
|
||||
等待队列上限 = DS2API_ACCOUNT_MAX_QUEUE(默认 = 建议并发值)
|
||||
429 阈值 = in-flight + 等待队列 ≈ 账号数量 × 4
|
||||
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
|
||||
```
|
||||
|
||||
- 当 in-flight 槽位满时,请求进入等待队列,**不会立即 429**
|
||||
- 超出总承载上限后才返回 `429 Too Many Requests`
|
||||
- `GET /admin/queue/status` 返回实时并发状态
|
||||
- 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 适配
|
||||
## Tool Call Adaptation
|
||||
|
||||
当请求中带 `tools` 时,DS2API 会做防泄漏处理与结构化转译:
|
||||
When `tools` is present in the request, DS2API performs anti-leak handling:
|
||||
|
||||
1. 只在**非代码块上下文**启用执行型 toolcall 识别(代码块示例默认不触发)
|
||||
2. 解析层当前只把 canonical XML 工具块视为可执行调用:`<tool_calls>` → `<invoke name="...">` → `<parameter name="...">`;旧式 `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`、`<function_call>`、`tool_use` / antml 变体与纯 JSON `tool_calls` 片段都会按普通文本处理
|
||||
3. `responses` 流式严格使用官方 item 生命周期事件(`response.output_item.*`、`response.content_part.*`、`response.function_call_arguments.*`)
|
||||
4. `responses` 支持并执行 `tool_choice`(`auto`/`none`/`required`/强制函数);`required` 违规时非流式返回 `422`,流式返回 `response.failed`
|
||||
5. 客户端请求哪种协议,就按该协议返回工具调用(OpenAI/Claude/Gemini 各自原生结构);模型侧优先约束输出规范 XML,再由兼容层转译
|
||||
1. Toolcall feature matching is enabled only in **non-code-block context** (fenced examples are ignored)
|
||||
2. The parser now treats only the canonical XML wrapper as executable tool-calling syntax: `<tool_calls>` → `<invoke name="...">` → `<parameter name="...">`; legacy `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`, `<function_call>`, `tool_use`, antml variants, and standalone JSON `tool_calls` payloads are treated as plain text
|
||||
3. `responses` streaming strictly uses official item lifecycle events (`response.output_item.*`, `response.content_part.*`, `response.function_call_arguments.*`)
|
||||
4. `responses` supports and enforces `tool_choice` (`auto`/`none`/`required`/forced function); `required` violations return `422` for non-stream and `response.failed` for stream
|
||||
5. The output protocol follows the client request (OpenAI / Claude / Gemini native shapes); model-side prompting can prefer XML, and the compatibility layer handles the protocol-specific translation
|
||||
|
||||
> 说明:当前版本在 parser 层仍以“尽量解析成功”为优先,未启用基于 allow-list 的工具名硬拒绝。
|
||||
>
|
||||
> 想评估“把工具调用封装成 XML 再输入模型”的方案,可参考:`docs/toolcall-semantics.md`。
|
||||
> Note: the current parser still prioritizes “parse successfully whenever possible”; hard allow-list rejection for undeclared tool names is not enabled yet.
|
||||
|
||||
## 本地开发抓包工具
|
||||
## Local Dev Packet Capture
|
||||
|
||||
用于定位「responses 思考流/工具调用」等问题。开启后会自动记录最近 N 条 DeepSeek 对话上游请求体与响应体(默认 20 条,超出自动淘汰;单条响应体默认最多记录 5 MB)。
|
||||
This is for debugging issues such as Responses reasoning streaming and tool-call handoff. When enabled, DS2API stores the latest N DeepSeek conversation payload pairs (request body + upstream response body), defaulting to 20 entries with auto-eviction; each response body is capped at 5 MB by default.
|
||||
|
||||
启用示例:
|
||||
Enable example:
|
||||
|
||||
```bash
|
||||
DS2API_DEV_PACKET_CAPTURE=true \
|
||||
@@ -333,63 +328,63 @@ DS2API_DEV_PACKET_CAPTURE_LIMIT=20 \
|
||||
go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
查询/清空(需 Admin JWT):
|
||||
Inspect/clear (Admin JWT required):
|
||||
|
||||
- `GET /admin/dev/captures`:查看抓包列表(最新在前)
|
||||
- `DELETE /admin/dev/captures`:清空抓包
|
||||
- `GET /admin/dev/raw-samples/query?q=关键词&limit=20`:按问题关键词查询当前内存抓包,并按 `chat_session_id` 归并 `completion + continue` 链
|
||||
- `POST /admin/dev/raw-samples/save`:把命中的某条抓包链保存为 `tests/raw_stream_samples/<sample-id>/` 回放样本
|
||||
- `GET /admin/dev/captures`: list captured items (newest first)
|
||||
- `DELETE /admin/dev/captures`: clear captured items
|
||||
- `GET /admin/dev/raw-samples/query?q=keyword&limit=20`: search current in-memory captures by prompt keyword and group `completion + continue` by `chat_session_id`
|
||||
- `POST /admin/dev/raw-samples/save`: persist a selected capture chain as `tests/raw_stream_samples/<sample-id>/`
|
||||
|
||||
返回字段包含:
|
||||
Response fields include:
|
||||
|
||||
- `request_body`:发送给 DeepSeek 的完整请求体
|
||||
- `response_body`:上游返回的原始流式内容拼接文本
|
||||
- `response_truncated`:是否触发单条大小截断
|
||||
- `request_body`: full payload sent to DeepSeek
|
||||
- `response_body`: concatenated raw upstream stream body text
|
||||
- `response_truncated`: whether body-size truncation happened
|
||||
|
||||
保存接口支持用 `query`、`chain_key` 或 `capture_id` 选中目标。例如:
|
||||
The save endpoint can target a chain by `query`, `chain_key`, or `capture_id`. Example:
|
||||
|
||||
```json
|
||||
{"query":"广州天气","sample_id":"gz-weather-from-memory"}
|
||||
{"query":"Guangzhou weather","sample_id":"gz-weather-from-memory"}
|
||||
```
|
||||
|
||||
## 文档索引
|
||||
## Documentation Index
|
||||
|
||||
| 文档 | 说明 |
|
||||
| Document | Description |
|
||||
| --- | --- |
|
||||
| [API.md](API.md) / [API.en.md](API.en.md) | API 接口文档(含请求/响应示例) |
|
||||
| [DEPLOY.md](docs/DEPLOY.md) / [DEPLOY.en.md](docs/DEPLOY.en.md) | 部署指南(本地/Docker/Vercel/systemd) |
|
||||
| [CONTRIBUTING.md](docs/CONTRIBUTING.md) / [CONTRIBUTING.en.md](docs/CONTRIBUTING.en.md) | 贡献指南 |
|
||||
| [TESTING.md](docs/TESTING.md) | 测试集使用指南 |
|
||||
| [API.md](API.md) | API reference with request/response examples |
|
||||
| [DEPLOY.md](docs/DEPLOY.md) | Deployment guide (local/Docker/Vercel/systemd) |
|
||||
| [CONTRIBUTING.md](docs/CONTRIBUTING.md) | Contributing guide |
|
||||
| [TESTING.md](docs/TESTING.md) | Testsuite guide |
|
||||
|
||||
## 测试
|
||||
## Testing
|
||||
|
||||
详细测试指南请参阅 [docs/TESTING.md](docs/TESTING.md)。
|
||||
For the full testing guide, see [docs/TESTING.md](docs/TESTING.md).
|
||||
|
||||
### 快速测试命令
|
||||
Quick commands:
|
||||
|
||||
```bash
|
||||
# 本地 PR 门禁
|
||||
# Local PR gates
|
||||
./scripts/lint.sh
|
||||
./tests/scripts/check-refactor-line-gate.sh
|
||||
./tests/scripts/run-unit-all.sh
|
||||
npm run build --prefix webui
|
||||
|
||||
# 端到端全链路测试(真实账号,生成完整请求/响应日志)
|
||||
# Live end-to-end tests (real accounts, full request/response logs)
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
## Release 自动构建(GitHub Actions)
|
||||
## Release Artifact Automation (GitHub Actions)
|
||||
|
||||
工作流文件:`.github/workflows/release-artifacts.yml`
|
||||
Workflow: `.github/workflows/release-artifacts.yml`
|
||||
|
||||
- **触发条件**:仅在 GitHub Release `published` 时触发(普通 push 不会触发)
|
||||
- **构建产物**:多平台二进制包(`linux/amd64`、`linux/arm64`、`darwin/amd64`、`darwin/arm64`、`windows/amd64`)+ `sha256sums.txt`
|
||||
- **容器镜像发布**:仅推送到 GHCR(`ghcr.io/cjackhwang/ds2api`)
|
||||
- **每个压缩包包含**:`ds2api` 可执行文件、`static/admin`、WASM 文件(同时支持内置 fallback)、`config.example.json` 配置示例、README、LICENSE
|
||||
- **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`
|
||||
- **Container publishing**: GHCR only (`ghcr.io/cjackhwang/ds2api`)
|
||||
- **Each archive includes**: `ds2api` executable, `static/admin`, WASM file (with embedded fallback support), `config.example.json`-based config template, README, LICENSE
|
||||
|
||||
## 免责声明
|
||||
## Disclaimer
|
||||
|
||||
本项目基于逆向方式实现,仅供学习、研究、个人实验和内部验证使用,不提供任何商业授权、稳定性保证或可用性保证。
|
||||
作者及仓库维护者不对因使用、修改、分发、部署或依赖本项目而产生的任何直接或间接损失、账号封禁、数据丢失、法律风险或第三方索赔负责。
|
||||
This project is built through reverse engineering and is provided for learning, research, personal experimentation, and internal validation only. No commercial authorization is granted, and no warranty of stability, fitness, or results is provided.
|
||||
The author and repository maintainers are not responsible for any direct or indirect loss, account suspension, data loss, legal risk, or third-party claims arising from use, modification, distribution, deployment, or reliance on this project.
|
||||
|
||||
请勿将本项目用于违反服务条款、协议、法律法规或平台规则的场景。商业使用前请自行确认 `LICENSE`、相关协议以及你是否获得了作者的书面许可。
|
||||
Do not use this project in ways that violate service terms, agreements, laws, or platform rules. Before any commercial use, review the `LICENSE`, the relevant terms, and confirm that you have the author's written permission.
|
||||
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
<p align="center">
|
||||
<img src="webui/public/ds2api-favicon.svg" width="128" height="128" alt="DS2API icon" />
|
||||
</p>
|
||||
|
||||
# DS2API
|
||||
|
||||
[](LICENSE)
|
||||

|
||||

|
||||
[](https://github.com/CJackHwang/ds2api/releases)
|
||||
[](docs/DEPLOY.en.md)
|
||||
[](https://zeabur.com/templates/L4CFHP)
|
||||
[](https://vercel.com/new/clone?repository-url=https://github.com/CJackHwang/ds2api)
|
||||
|
||||
Language: [中文](README.MD) | [English](README.en.md)
|
||||
|
||||
DS2API converts DeepSeek Web chat capability into OpenAI-compatible, Claude-compatible, and Gemini-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).
|
||||
|
||||
Documentation entry: [Docs Index](docs/README.md) / [Architecture](docs/ARCHITECTURE.en.md) / [API Reference](API.en.md)
|
||||
|
||||
> **Important Disclaimer**
|
||||
>
|
||||
> This repository is provided for learning, research, personal experimentation, and internal validation only. It does not grant any commercial authorization and comes with no warranty of fitness, stability, or results.
|
||||
>
|
||||
> The author and repository maintainers are not responsible for any direct or indirect loss, account suspension, data loss, legal risk, or third-party claims arising from use, modification, distribution, deployment, or reliance on this project.
|
||||
>
|
||||
> Do not use this project in ways that violate service terms, agreements, laws, or platform rules. Before any commercial use, review the `LICENSE`, the relevant terms, and confirm that you have the author's written permission.
|
||||
|
||||
## Architecture Overview (Summary)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Client["🖥️ Clients / SDKs\n(OpenAI / Claude / Gemini)"]
|
||||
Upstream["☁️ DeepSeek API"]
|
||||
|
||||
subgraph DS2API["DS2API 4.x (Modular HTTP Surface + PromptCompat Core)"]
|
||||
Router["chi Router + Middleware\n(RequestID / RealIP / Logger / Recoverer / CORS)"]
|
||||
|
||||
subgraph HTTP["HTTP API Surface"]
|
||||
OA["OpenAI\nchat / responses / files / embeddings"]
|
||||
CA["Claude\n/anthropic/* + /v1/messages"]
|
||||
GA["Gemini\n/v1beta/models/* + /v1/models/*"]
|
||||
Admin["Admin API\nresource packages"]
|
||||
WebUI["WebUI\n/admin (static hosting)"]
|
||||
Vercel["Vercel Node Stream\n/v1/chat/completions"]
|
||||
end
|
||||
|
||||
subgraph Runtime["Runtime + Core Capabilities"]
|
||||
Compat["PromptCompat\n(API -> web-chat plain text context)"]
|
||||
Chat["Chat / Responses Runtime\n(unified tools + stream semantics)"]
|
||||
Auth["Auth Resolver\n(API key / bearer / x-goog-api-key)"]
|
||||
Pool["Account Pool + Queue\n(in-flight slots + wait queue)"]
|
||||
DSClient["DeepSeek Client\n(session / auth / completion / files)"]
|
||||
Pow["PoW Solver\n(Pure Go)"]
|
||||
Tool["Tool Sieve\n(Go/Node semantic parity)"]
|
||||
History["History Split\n(long history as files)"]
|
||||
end
|
||||
end
|
||||
|
||||
Client --> Router
|
||||
Router --> OA & CA & GA
|
||||
Router --> Admin
|
||||
Router --> WebUI
|
||||
Router --> Vercel
|
||||
|
||||
OA --> Compat
|
||||
CA & GA --> Compat
|
||||
Compat --> Chat
|
||||
Compat -.long history.-> History
|
||||
Vercel -.Go prepare.-> Chat
|
||||
Vercel -.Node SSE.-> Tool
|
||||
Chat --> Auth
|
||||
Chat -.account rotation.-> Pool
|
||||
Chat -.tool-call parsing.-> Tool
|
||||
Chat -.PoW solving.-> Pow
|
||||
Auth --> DSClient
|
||||
DSClient --> Upstream
|
||||
Upstream --> DSClient
|
||||
Chat --> Client
|
||||
Vercel --> Client
|
||||
```
|
||||
|
||||
For the full module-by-module architecture and directory responsibilities, see [docs/ARCHITECTURE.en.md](docs/ARCHITECTURE.en.md).
|
||||
|
||||
- **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`, `GET /v1/models/{id}`, `POST /v1/chat/completions`, `POST /v1/responses`, `GET /v1/responses/{response_id}`, `POST /v1/embeddings`, `POST /v1/files` |
|
||||
| Claude compatible | `GET /anthropic/v1/models`, `POST /anthropic/v1/messages`, `POST /anthropic/v1/messages/count_tokens` (plus shortcut paths `/v1/messages`, `/messages`) |
|
||||
| Gemini compatible | `POST /v1beta/models/{model}:generateContent`, `POST /v1beta/models/{model}:streamGenerateContent` (plus `/v1/models/{model}:*` paths) |
|
||||
| Unified CORS compatibility | `/v1/*`, `/anthropic/*`, `/v1beta/models/*`, and `/admin/*` share one CORS policy; on Vercel, the Node Runtime for `/v1/chat/completions` mirrors the same relaxed preflight behavior for third-party clients |
|
||||
| Multi-account rotation | Auto token refresh, email/mobile dual login |
|
||||
| Concurrency control | Per-account in-flight limit + waiting queue, dynamic recommended concurrency |
|
||||
| DeepSeek PoW | Pure Go high-performance solver (DeepSeekHashV1), ms-level response |
|
||||
| Tool Calling | Anti-leak handling: non-code-block feature match, early `delta.tool_calls`, structured incremental output |
|
||||
| Admin API | Config management, runtime settings hot-reload, proxy management, account testing/batch test, session cleanup, import/export, Vercel sync, version check |
|
||||
| WebUI Admin Panel | SPA at `/admin` (bilingual Chinese/English, dark mode, with server-side conversation history) |
|
||||
| Health Probes | `GET /healthz` (liveness), `GET /readyz` (readiness) |
|
||||
|
||||
## Platform Compatibility Matrix
|
||||
|
||||
| Tier | Platform | Status |
|
||||
| --- | --- | --- |
|
||||
| P0 | Codex CLI/SDK (`wire_api=chat` / `wire_api=responses`) | ✅ |
|
||||
| P0 | OpenAI SDK (JS/Python, chat + responses) | ✅ |
|
||||
| P0 | Vercel AI SDK (openai-compatible) | ✅ |
|
||||
| P0 | Anthropic SDK (messages) | ✅ |
|
||||
| P0 | Google Gemini SDK (generateContent) | ✅ |
|
||||
| P1 | LangChain / LlamaIndex / OpenWebUI (OpenAI-compatible integration) | ✅ |
|
||||
|
||||
## Model Support
|
||||
|
||||
### OpenAI Endpoint (`GET /v1/models`)
|
||||
|
||||
| Family | Model ID | thinking | search |
|
||||
| --- | --- | --- | --- |
|
||||
| default | `deepseek-v4-flash` | enabled by default, request-controlled | ❌ |
|
||||
| expert | `deepseek-v4-pro` | enabled by default, request-controlled | ❌ |
|
||||
| default | `deepseek-v4-flash-search` | enabled by default, request-controlled | ✅ |
|
||||
| expert | `deepseek-v4-pro-search` | enabled by default, request-controlled | ✅ |
|
||||
| vision | `deepseek-v4-vision` | enabled by default, request-controlled | ❌ |
|
||||
| vision | `deepseek-v4-vision-search` | enabled by default, request-controlled | ✅ |
|
||||
|
||||
Besides native IDs, DS2API also accepts common aliases as input (for example `gpt-4.1`, `gpt-5`, `gpt-5-codex`, `o3`, `claude-*`, `gemini-*`), but `/v1/models` returns normalized DeepSeek native model IDs. The complete alias behavior is documented in [API.en.md](API.en.md#model-alias-resolution) and `config.example.json`.
|
||||
|
||||
### Claude Endpoint (`GET /anthropic/v1/models`)
|
||||
|
||||
| Current common model | Default Mapping |
|
||||
| --- | --- |
|
||||
| `claude-sonnet-4-6` | `deepseek-v4-flash` |
|
||||
| `claude-haiku-4-5` (compatible with `claude-3-5-haiku-latest`) | `deepseek-v4-flash` |
|
||||
| `claude-opus-4-6` | `deepseek-v4-pro` |
|
||||
|
||||
Override mapping via the global `model_aliases` config.
|
||||
Besides the primary aliases above, `/anthropic/v1/models` also returns Claude 4.x snapshots plus historical 3.x IDs and common aliases for legacy client compatibility.
|
||||
|
||||
#### Claude Code integration pitfalls (validated)
|
||||
|
||||
- Set `ANTHROPIC_BASE_URL` to the DS2API root URL (for example `http://127.0.0.1:5001`). Claude Code sends requests to `/v1/messages?beta=true`.
|
||||
- `ANTHROPIC_API_KEY` must match an entry in `keys` from `config.json`. Keeping both a regular key and an `sk-ant-*` style key improves client compatibility.
|
||||
- If your environment has proxy variables, set `NO_PROXY=127.0.0.1,localhost,<your_host_ip>` for DS2API to avoid proxy interception of local traffic.
|
||||
- If tool calls are rendered as plain text and not executed, first verify the model output uses the only supported XML block: `<tool_calls><invoke name="..."><parameter name="...">...`, not legacy `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`, `<function_call>`, `tool_use`, or standalone JSON `tool_calls`.
|
||||
|
||||
### Gemini Endpoint
|
||||
|
||||
The Gemini adapter maps model names to DeepSeek native models via `model_aliases` or built-in heuristics, supporting both `generateContent` and `streamGenerateContent` call patterns with full Tool Calling support (`functionDeclarations` → `functionCall` output).
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Recommended deployment priority
|
||||
|
||||
Recommended order when choosing a deployment method:
|
||||
|
||||
1. **Download and run release binaries**: the easiest path for most users because the artifacts are already built.
|
||||
2. **Docker / GHCR image deployment**: suitable for containerized, orchestrated, or cloud environments.
|
||||
3. **Vercel deployment**: suitable if you already use Vercel and accept its platform constraints.
|
||||
4. **Run from source / build locally**: suitable for development, debugging, or when you need to modify the code yourself.
|
||||
|
||||
### Universal First Step (all deployment modes)
|
||||
|
||||
Use `config.json` as the single source of truth (recommended):
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
Recommended per deployment mode:
|
||||
- Local run: read `config.json` directly
|
||||
- Docker / Vercel: generate Base64 from `config.json` and inject as `DS2API_CONFIG_JSON`, or paste raw JSON directly
|
||||
|
||||
The WebUI admin panel’s “Full configuration template” is loaded from the same `config.example.json`, so updating that file keeps the frontend template in sync.
|
||||
|
||||
### Option 1: Download Release Binaries
|
||||
|
||||
GitHub Actions automatically builds multi-platform archives on each Release:
|
||||
|
||||
```bash
|
||||
# After downloading the archive for your platform
|
||||
tar -xzf ds2api_<tag>_linux_amd64.tar.gz
|
||||
cd ds2api_<tag>_linux_amd64
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
./ds2api
|
||||
```
|
||||
|
||||
### Option 2: Docker / GHCR
|
||||
|
||||
```bash
|
||||
# Pull prebuilt image
|
||||
docker pull ghcr.io/cjackhwang/ds2api:latest
|
||||
|
||||
# Or run a pinned version
|
||||
# docker pull ghcr.io/cjackhwang/ds2api:v3.0.0
|
||||
|
||||
# Prepare env file and config file
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# Start with compose
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
The default `docker-compose.yml` uses `ghcr.io/cjackhwang/ds2api:latest` and maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
Rebuild after updates: `docker-compose up -d --build`
|
||||
|
||||
#### Zeabur One-Click (Dockerfile)
|
||||
|
||||
1. Click the “Deploy on Zeabur” button above to deploy.
|
||||
2. After deployment, open `/admin` and login with `DS2API_ADMIN_KEY` shown in Zeabur env/template instructions.
|
||||
3. Import / edit config in Admin UI (it will be written and persisted to `/data/config.json`).
|
||||
|
||||
Note: when Zeabur builds directly from the repo `Dockerfile`, you do not need to pass `BUILD_VERSION`. The image prefers that build arg when provided, and automatically falls back to the repo-root `VERSION` file when it is absent.
|
||||
|
||||
### 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`; recommended to also set `DS2API_CONFIG_JSON`)
|
||||
4. Deploy
|
||||
|
||||
Recommended first step in repo root:
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
Recommended: convert `config.json` to Base64 locally, then paste into `DS2API_CONFIG_JSON` to avoid JSON formatting mistakes:
|
||||
|
||||
```bash
|
||||
base64 < config.json | tr -d '\n'
|
||||
```
|
||||
|
||||
> **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. This is the only interface family currently routed through Node, and its CORS allow behavior is kept aligned with the Go router so third-party preflight handling stays unified.
|
||||
|
||||
For detailed deployment instructions, see the [Deployment Guide](docs/DEPLOY.en.md).
|
||||
|
||||
### Option 4: Local Run
|
||||
|
||||
**Prerequisites**: Go 1.26+, Node.js `20.19+` or `22.12+` (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 local URL: `http://127.0.0.1:5001`
|
||||
|
||||
The server actually binds to `0.0.0.0:5001`, so devices on the same LAN can usually reach it through your private IP as well.
|
||||
|
||||
> **WebUI auto-build**: On first local startup, if `static/admin` is missing, DS2API will auto-run `npm ci` (only when dependencies are missing) and `npm run build -- --outDir static/admin --emptyOutDir` (requires Node.js). You can also build manually: `./scripts/build-webui.sh`
|
||||
|
||||
## Configuration
|
||||
|
||||
`README` keeps only the onboarding path. Use [config.example.json](config.example.json) as the field template, and see the [deployment guide](docs/DEPLOY.en.md#0-prerequisites) plus [API configuration notes](API.en.md#configuration-best-practice) for full details.
|
||||
|
||||
Common fields:
|
||||
|
||||
- `keys` / `api_keys`: client API keys; `api_keys` adds `name` and `remark` metadata while `keys` remains compatible.
|
||||
- `accounts`: managed DeepSeek accounts, supporting `email` or `mobile` login plus proxy/name/remark metadata.
|
||||
- `model_aliases`: one shared alias map for OpenAI / Claude / Gemini model names.
|
||||
- `runtime`: account concurrency, queueing, token refresh, account health cooldowns, and upstream file-upload policy, hot-reloadable via Admin Settings.
|
||||
- `auto_delete.mode`: remote session cleanup after each request, supporting `none` / `single` / `all`.
|
||||
- `history_split`: multi-turn history split policy, now forced on globally; tune its trigger threshold to avoid inlining all long history into the prompt.
|
||||
|
||||
For the full environment variable list, see [docs/DEPLOY.en.md](docs/DEPLOY.en.md). For auth behavior, see [API.en.md](API.en.md#authentication).
|
||||
|
||||
## Authentication Modes
|
||||
|
||||
For business endpoints (`/v1/*`, `/anthropic/*`, Gemini routes), 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).
|
||||
Gemini routes also accept `x-goog-api-key`, or `?key=` / `?api_key=` when no auth header is present.
|
||||
|
||||
## 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. Toolcall feature matching is enabled only in **non-code-block context** (fenced examples are ignored)
|
||||
2. The parser now treats only the canonical XML wrapper as executable tool-calling syntax: `<tool_calls>` → `<invoke name="...">` → `<parameter name="...">`; legacy `<tools>` / `<tool_call>` / `<tool_name>` / `<param>`, `<function_call>`, `tool_use`, antml variants, and standalone JSON `tool_calls` payloads are treated as plain text
|
||||
3. `responses` streaming strictly uses official item lifecycle events (`response.output_item.*`, `response.content_part.*`, `response.function_call_arguments.*`)
|
||||
4. `responses` supports and enforces `tool_choice` (`auto`/`none`/`required`/forced function); `required` violations return `422` for non-stream and `response.failed` for stream
|
||||
5. The output protocol follows the client request (OpenAI / Claude / Gemini native shapes); model-side prompting can prefer XML, and the compatibility layer handles the protocol-specific translation
|
||||
|
||||
> Note: the current parser still prioritizes “parse successfully whenever possible”; hard allow-list rejection for undeclared tool names is not enabled yet.
|
||||
|
||||
## Local Dev Packet Capture
|
||||
|
||||
This is for debugging issues such as Responses reasoning streaming and tool-call handoff. When enabled, DS2API stores the latest N DeepSeek conversation payload pairs (request body + upstream response body), defaulting to 20 entries with auto-eviction; each response body is capped at 5 MB by default.
|
||||
|
||||
Enable example:
|
||||
|
||||
```bash
|
||||
DS2API_DEV_PACKET_CAPTURE=true \
|
||||
DS2API_DEV_PACKET_CAPTURE_LIMIT=20 \
|
||||
go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
Inspect/clear (Admin JWT required):
|
||||
|
||||
- `GET /admin/dev/captures`: list captured items (newest first)
|
||||
- `DELETE /admin/dev/captures`: clear captured items
|
||||
- `GET /admin/dev/raw-samples/query?q=keyword&limit=20`: search current in-memory captures by prompt keyword and group `completion + continue` by `chat_session_id`
|
||||
- `POST /admin/dev/raw-samples/save`: persist a selected capture chain as `tests/raw_stream_samples/<sample-id>/`
|
||||
|
||||
Response fields include:
|
||||
|
||||
- `request_body`: full payload sent to DeepSeek
|
||||
- `response_body`: concatenated raw upstream stream body text
|
||||
- `response_truncated`: whether body-size truncation happened
|
||||
|
||||
The save endpoint can target a chain by `query`, `chain_key`, or `capture_id`. Example:
|
||||
|
||||
```json
|
||||
{"query":"Guangzhou weather","sample_id":"gz-weather-from-memory"}
|
||||
```
|
||||
|
||||
## Documentation Index
|
||||
|
||||
| Document | Description |
|
||||
| --- | --- |
|
||||
| [API.md](API.md) / [API.en.md](API.en.md) | API reference with request/response examples |
|
||||
| [DEPLOY.md](docs/DEPLOY.md) / [DEPLOY.en.md](docs/DEPLOY.en.md) | Deployment guide (local/Docker/Vercel/systemd) |
|
||||
| [CONTRIBUTING.md](docs/CONTRIBUTING.md) / [CONTRIBUTING.en.md](docs/CONTRIBUTING.en.md) | Contributing guide |
|
||||
| [TESTING.md](docs/TESTING.md) | Testsuite guide |
|
||||
|
||||
## Testing
|
||||
|
||||
For the full testing guide, see [docs/TESTING.md](docs/TESTING.md).
|
||||
|
||||
Quick commands:
|
||||
|
||||
```bash
|
||||
# Local PR gates
|
||||
./scripts/lint.sh
|
||||
./tests/scripts/check-refactor-line-gate.sh
|
||||
./tests/scripts/run-unit-all.sh
|
||||
npm run build --prefix webui
|
||||
|
||||
# Live end-to-end tests (real accounts, full request/response logs)
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
## 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`
|
||||
- **Container publishing**: GHCR only (`ghcr.io/cjackhwang/ds2api`)
|
||||
- **Each archive includes**: `ds2api` executable, `static/admin`, WASM file (with embedded fallback support), `config.example.json`-based config template, README, LICENSE
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is built through reverse engineering and is provided for learning, research, personal experimentation, and internal validation only. No commercial authorization is granted, and no warranty of stability, fitness, or results is provided.
|
||||
The author and repository maintainers are not responsible for any direct or indirect loss, account suspension, data loss, legal risk, or third-party claims arising from use, modification, distribution, deployment, or reliance on this project.
|
||||
|
||||
Do not use this project in ways that violate service terms, agreements, laws, or platform rules. Before any commercial use, review the `LICENSE`, the relevant terms, and confirm that you have the author's written permission.
|
||||
@@ -1,196 +0,0 @@
|
||||
# DS2API Architecture & Project Layout
|
||||
|
||||
Language: [中文](ARCHITECTURE.md) | [English](ARCHITECTURE.en.md)
|
||||
|
||||
> This file is the single architecture source for directory layout, module boundaries, and execution flow.
|
||||
|
||||
## 1. Top-level Layout (core directories)
|
||||
|
||||
> Notes: this lists the main business directories (excluding metadata/dependency dirs such as `.git/` and `webui/node_modules/`), with each folder annotated by purpose. Newly added directories should be verified from the code tree rather than treated as a per-file inventory here.
|
||||
|
||||
```text
|
||||
ds2api/
|
||||
├── .github/ # GitHub collaboration and CI config
|
||||
│ ├── ISSUE_TEMPLATE/ # Issue templates
|
||||
│ └── workflows/ # GitHub Actions workflows
|
||||
├── api/ # Serverless entrypoints (Vercel Go/Node)
|
||||
├── app/ # Application-level handler assembly
|
||||
├── cmd/ # Executable entrypoints
|
||||
│ ├── ds2api/ # Main service bootstrap
|
||||
│ └── ds2api-tests/ # E2E testsuite CLI bootstrap
|
||||
├── docs/ # Project documentation
|
||||
├── internal/ # Core implementation (non-public packages)
|
||||
│ ├── account/ # Account pool, inflight slots, waiting queue
|
||||
│ ├── auth/ # Auth/JWT/credential resolution
|
||||
│ ├── chathistory/ # Server-side conversation history storage/query
|
||||
│ ├── claudeconv/ # Claude message conversion helpers
|
||||
│ ├── compat/ # Compatibility and regression helpers
|
||||
│ ├── config/ # Config loading/validation/hot reload
|
||||
│ ├── deepseek/ # DeepSeek upstream client/protocol/transport
|
||||
│ │ ├── client/ # Login/session/completion/upload/delete calls
|
||||
│ │ ├── protocol/ # DeepSeek URLs, constants, skip path/pattern
|
||||
│ │ └── transport/ # DeepSeek transport details
|
||||
│ ├── devcapture/ # Dev capture and troubleshooting
|
||||
│ ├── format/ # Response formatting layer
|
||||
│ │ ├── claude/ # Claude output formatting
|
||||
│ │ └── openai/ # OpenAI output formatting
|
||||
│ ├── httpapi/ # HTTP surfaces: OpenAI/Claude/Gemini/Admin
|
||||
│ │ ├── admin/ # Admin API root assembly and resource packages
|
||||
│ │ ├── claude/ # Claude HTTP protocol adapter
|
||||
│ │ ├── gemini/ # Gemini HTTP protocol adapter
|
||||
│ │ └── openai/ # OpenAI HTTP surface
|
||||
│ │ ├── chat/ # Chat Completions execution entrypoint
|
||||
│ │ ├── responses/ # Responses API and response store
|
||||
│ │ ├── files/ # Files API and inline-file preprocessing
|
||||
│ │ ├── embeddings/ # Embeddings API
|
||||
│ │ ├── history/ # OpenAI history split
|
||||
│ │ └── shared/ # OpenAI HTTP errors/models/tool formatting
|
||||
│ ├── js/ # Node runtime related logic
|
||||
│ │ ├── chat-stream/ # Node streaming bridge
|
||||
│ │ ├── helpers/ # JS helper modules
|
||||
│ │ │ └── stream-tool-sieve/ # JS implementation of tool sieve
|
||||
│ │ └── shared/ # Shared semantics between Go/Node
|
||||
│ ├── prompt/ # Prompt composition
|
||||
│ ├── promptcompat/ # API request -> DeepSeek web-chat plain-text compatibility
|
||||
│ ├── rawsample/ # Raw sample read/write and management
|
||||
│ ├── server/ # Router and middleware assembly
|
||||
│ │ └── data/ # Router/runtime helper data
|
||||
│ ├── sse/ # SSE parsing utilities
|
||||
│ ├── stream/ # Unified stream consumption engine
|
||||
│ ├── testsuite/ # Testsuite execution framework
|
||||
│ ├── textclean/ # Text cleanup
|
||||
│ ├── toolcall/ # Tool-call parsing and repair
|
||||
│ ├── toolstream/ # Go streaming tool-call anti-leak and delta detection
|
||||
│ ├── translatorcliproxy/ # Cross-protocol translation bridge
|
||||
│ ├── util/ # Shared utility helpers
|
||||
│ ├── version/ # Version query/compare
|
||||
│ └── webui/ # WebUI static hosting logic
|
||||
├── plans/ # Stage plans and manual QA records
|
||||
├── pow/ # PoW standalone implementation + benchmarks
|
||||
├── scripts/ # Build/release helper scripts
|
||||
├── tests/ # Test assets and scripts
|
||||
│ ├── compat/ # Compatibility fixtures + expected outputs
|
||||
│ │ ├── expected/ # Expected output samples
|
||||
│ │ └── fixtures/ # Fixture inputs
|
||||
│ │ ├── sse_chunks/ # SSE chunk fixtures
|
||||
│ │ └── toolcalls/ # Tool-call fixtures
|
||||
│ ├── node/ # Node unit tests
|
||||
│ ├── raw_stream_samples/ # Upstream raw SSE samples
|
||||
│ │ ├── content-filter-trigger-20260405-jwt3/ # Content-filter terminal sample
|
||||
│ │ ├── continue-thinking-snapshot-replay-20260405/ # Continue-thinking sample
|
||||
│ │ ├── guangzhou-weather-reasoner-search-20260404/ # Search/reference sample
|
||||
│ │ ├── markdown-format-example-20260405/ # Markdown sample
|
||||
│ │ └── markdown-format-example-20260405-spacefix/ # Space-fix sample
|
||||
│ ├── scripts/ # Test entry scripts
|
||||
│ └── tools/ # Testing helper tools
|
||||
└── webui/ # React admin console source
|
||||
├── public/ # Static assets
|
||||
└── src/ # Frontend source code
|
||||
├── app/ # Routing/state scaffolding
|
||||
├── components/ # Shared UI components
|
||||
├── features/ # Feature modules
|
||||
│ ├── account/ # Account management page
|
||||
│ ├── apiTester/ # API tester page
|
||||
│ ├── settings/ # Settings page
|
||||
│ └── vercel/ # Vercel sync page
|
||||
├── layout/ # Layout components
|
||||
├── locales/ # i18n strings
|
||||
└── utils/ # Frontend utilities
|
||||
```
|
||||
|
||||
## 2. Primary Request Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
C[Client / SDK] --> R[internal/server/router.go]
|
||||
|
||||
subgraph HTTP[HTTP API surface]
|
||||
OA[internal/httpapi/openai]
|
||||
CHAT[openai/chat]
|
||||
RESP[openai/responses]
|
||||
FILES[openai/files + embeddings]
|
||||
CA[internal/httpapi/claude]
|
||||
GA[internal/httpapi/gemini]
|
||||
AD[internal/httpapi/admin/*]
|
||||
WEB[internal/webui static admin]
|
||||
end
|
||||
|
||||
subgraph COMPAT[Prompt compatibility core]
|
||||
PC[internal/promptcompat]
|
||||
PROMPT[internal/prompt]
|
||||
HIST[internal/httpapi/openai/history]
|
||||
end
|
||||
|
||||
subgraph RUNTIME[Shared runtime]
|
||||
AUTH[internal/auth]
|
||||
POOL[internal/account queue + concurrency]
|
||||
STREAM[internal/stream + internal/sse]
|
||||
TOOL[internal/toolcall + internal/toolstream]
|
||||
DS[internal/deepseek/client]
|
||||
POW[pow + internal/deepseek/protocol]
|
||||
end
|
||||
|
||||
subgraph NODE[Vercel Node Runtime]
|
||||
NCS[api/chat-stream.js]
|
||||
JS[internal/js/chat-stream + stream-tool-sieve]
|
||||
end
|
||||
|
||||
R --> OA --> CHAT
|
||||
OA --> RESP
|
||||
OA --> FILES
|
||||
R --> CA
|
||||
R --> GA
|
||||
R --> AD
|
||||
R --> WEB
|
||||
R -.Vercel stream.-> NCS
|
||||
|
||||
CA --> PC
|
||||
GA --> PC
|
||||
CHAT --> PC
|
||||
RESP --> PC
|
||||
PC --> PROMPT
|
||||
PC -.long history.-> HIST
|
||||
PC --> AUTH
|
||||
|
||||
NCS -.Go prepare/release.-> CHAT
|
||||
NCS --> JS
|
||||
JS --> TOOL
|
||||
|
||||
AUTH --> POOL
|
||||
CHAT --> STREAM
|
||||
RESP --> STREAM
|
||||
STREAM --> TOOL
|
||||
POOL --> DS
|
||||
DS --> POW
|
||||
DS --> U[DeepSeek upstream]
|
||||
```
|
||||
|
||||
## 3. Responsibilities in `internal/`
|
||||
|
||||
- `internal/server`: router tree + middlewares (health, protocol routes, Admin/WebUI).
|
||||
- `internal/httpapi/openai/*`: OpenAI HTTP surface split into chat, responses, files, embeddings, history, and shared packages; chat/responses share the promptcompat, stream, and toolcall semantics.
|
||||
- `internal/httpapi/{claude,gemini}`: protocol wrappers that normalize into the same prompt compatibility semantics without duplicating upstream execution.
|
||||
- `internal/promptcompat`: compatibility core for turning OpenAI/Claude/Gemini requests into DeepSeek web-chat plain-text context.
|
||||
- `internal/translatorcliproxy`: structure translation between Claude/Gemini and OpenAI.
|
||||
- `internal/deepseek/{client,protocol,transport}`: upstream requests, sessions, PoW adaptation, protocol constants, and transport details.
|
||||
- `internal/js/chat-stream` + `api/chat-stream.js`: Vercel Node streaming bridge; Go prepare/release owns auth, account lease, and completion payload assembly, while Node relays real-time SSE with Go-aligned finalization and tool sieve semantics.
|
||||
- `internal/stream` + `internal/sse`: Go stream parsing and incremental assembly.
|
||||
- `internal/toolcall` + `internal/toolstream`: canonical XML tool-call parsing + anti-leak sieve (the only executable format is `<tool_calls>` / `<invoke name="...">` / `<parameter name="...">`).
|
||||
- `internal/httpapi/admin/*`: Admin API root assembly plus auth/accounts/config/settings/proxies/rawsamples/vercel/history/devcapture/version resource packages.
|
||||
- `internal/chathistory`: server-side conversation history persistence, pagination, detail lookup, and retention policy.
|
||||
- `internal/config`: config loading/validation + runtime settings hot-reload.
|
||||
- `internal/account`: managed account pool, inflight slots, waiting queue, and account health weight/cooldown scheduling.
|
||||
|
||||
## 4. WebUI Runtime Relation
|
||||
|
||||
- `webui/` stores frontend source (Vite + React).
|
||||
- Runtime serves static output from `static/admin`.
|
||||
- On first local startup, if `static/admin` is missing, DS2API may auto-build it (Node.js required).
|
||||
|
||||
## 5. Documentation Split Strategy
|
||||
|
||||
- Onboarding & quick start: `README.MD` / `README.en.md`
|
||||
- Architecture & layout: `docs/ARCHITECTURE*.md` (this file)
|
||||
- API contracts: `API.md` / `API.en.md`
|
||||
- Deployment/testing/contributing: `docs/DEPLOY*`, `docs/TESTING.md`, `docs/CONTRIBUTING*`
|
||||
- Deep topics: `docs/toolcall-semantics.md`, `docs/DeepSeekSSE行为结构说明-2026-04-05.md`
|
||||
+113
-114
@@ -1,104 +1,103 @@
|
||||
# DS2API 架构与项目结构说明
|
||||
# DS2API Architecture & Project Layout
|
||||
|
||||
语言 / Language: [中文](ARCHITECTURE.md) | [English](ARCHITECTURE.en.md)
|
||||
|
||||
> 本文档用于集中维护“代码目录结构 + 模块边界 + 主链路调用关系”。
|
||||
> This file is the single architecture source for directory layout, module boundaries, and execution flow.
|
||||
|
||||
## 1. 顶层目录结构(核心目录)
|
||||
## 1. Top-level Layout (core directories)
|
||||
|
||||
> 说明:以下为仓库内主要业务目录(排除 `.git/` 与 `webui/node_modules/` 这类依赖/元数据目录),并标注每个文件夹作用。新增目录以代码为准,不要求在本文做逐文件展开。
|
||||
> Notes: this lists the main business directories (excluding metadata/dependency dirs such as `.git/` and `webui/node_modules/`), with each folder annotated by purpose. Newly added directories should be verified from the code tree rather than treated as a per-file inventory here.
|
||||
|
||||
```text
|
||||
ds2api/
|
||||
├── .github/ # GitHub 协作与 CI 配置
|
||||
│ ├── ISSUE_TEMPLATE/ # Issue 模板
|
||||
│ └── workflows/ # GitHub Actions 工作流
|
||||
├── api/ # Serverless 入口(Vercel Go/Node)
|
||||
├── app/ # 应用级 handler 装配层
|
||||
├── cmd/ # 可执行程序入口
|
||||
│ ├── ds2api/ # 主服务启动入口
|
||||
│ └── ds2api-tests/ # E2E 测试集 CLI 入口
|
||||
├── docs/ # 项目文档目录
|
||||
├── internal/ # 核心业务实现(不对外暴露)
|
||||
│ ├── account/ # 账号池、并发槽位、等待队列
|
||||
│ ├── auth/ # 鉴权/JWT/凭证解析
|
||||
│ ├── chathistory/ # 服务器端对话记录存储与查询
|
||||
│ ├── claudeconv/ # Claude 消息格式转换工具
|
||||
│ ├── compat/ # 兼容性辅助与回归支持
|
||||
│ ├── config/ # 配置加载、校验、热更新
|
||||
│ ├── deepseek/ # DeepSeek 上游 client/protocol/transport
|
||||
│ │ ├── client/ # 登录、会话、completion、上传/删除等上游调用
|
||||
│ │ ├── protocol/ # DeepSeek URL、常量、skip path/pattern
|
||||
│ │ └── transport/ # DeepSeek 传输层细节
|
||||
│ ├── devcapture/ # 开发抓包与调试采集
|
||||
│ ├── format/ # 响应格式化层
|
||||
│ │ ├── claude/ # Claude 输出格式化
|
||||
│ │ └── openai/ # OpenAI 输出格式化
|
||||
│ ├── httpapi/ # HTTP surface:OpenAI/Claude/Gemini/Admin
|
||||
│ │ ├── admin/ # Admin API 根装配与资源子包
|
||||
│ │ ├── claude/ # Claude HTTP 协议适配
|
||||
│ │ ├── gemini/ # Gemini HTTP 协议适配
|
||||
├── .github/ # GitHub collaboration and CI config
|
||||
│ ├── ISSUE_TEMPLATE/ # Issue templates
|
||||
│ └── workflows/ # GitHub Actions workflows
|
||||
├── api/ # Serverless entrypoints (Vercel Go/Node)
|
||||
├── app/ # Application-level handler assembly
|
||||
├── cmd/ # Executable entrypoints
|
||||
│ ├── ds2api/ # Main service bootstrap
|
||||
│ └── ds2api-tests/ # E2E testsuite CLI bootstrap
|
||||
├── docs/ # Project documentation
|
||||
├── internal/ # Core implementation (non-public packages)
|
||||
│ ├── account/ # Account pool, inflight slots, waiting queue
|
||||
│ ├── auth/ # Auth/JWT/credential resolution
|
||||
│ ├── chathistory/ # Server-side conversation history storage/query
|
||||
│ ├── claudeconv/ # Claude message conversion helpers
|
||||
│ ├── compat/ # Compatibility and regression helpers
|
||||
│ ├── config/ # Config loading/validation/hot reload
|
||||
│ ├── deepseek/ # DeepSeek upstream client/protocol/transport
|
||||
│ │ ├── client/ # Login/session/completion/upload/delete calls
|
||||
│ │ ├── protocol/ # DeepSeek URLs, constants, skip path/pattern
|
||||
│ │ └── transport/ # DeepSeek transport details
|
||||
│ ├── devcapture/ # Dev capture and troubleshooting
|
||||
│ ├── format/ # Response formatting layer
|
||||
│ │ ├── claude/ # Claude output formatting
|
||||
│ │ └── openai/ # OpenAI output formatting
|
||||
│ ├── httpapi/ # HTTP surfaces: OpenAI/Claude/Gemini/Admin
|
||||
│ │ ├── admin/ # Admin API root assembly and resource packages
|
||||
│ │ ├── claude/ # Claude HTTP protocol adapter
|
||||
│ │ ├── gemini/ # Gemini HTTP protocol adapter
|
||||
│ │ └── openai/ # OpenAI HTTP surface
|
||||
│ │ ├── chat/ # Chat Completions 执行入口
|
||||
│ │ ├── responses/ # Responses API 与 response store
|
||||
│ │ ├── files/ # Files API 与 inline file 预处理
|
||||
│ │ ├── chat/ # Chat Completions execution entrypoint
|
||||
│ │ ├── responses/ # Responses API and response store
|
||||
│ │ ├── files/ # Files API and inline-file preprocessing
|
||||
│ │ ├── embeddings/ # Embeddings API
|
||||
│ │ ├── history/ # OpenAI history split
|
||||
│ │ └── shared/ # OpenAI HTTP 公共错误/模型/工具格式
|
||||
│ ├── js/ # Node Runtime 相关逻辑
|
||||
│ │ ├── chat-stream/ # Node 流式输出桥接
|
||||
│ │ ├── helpers/ # JS 辅助函数
|
||||
│ │ │ └── stream-tool-sieve/ # Tool sieve JS 实现
|
||||
│ │ └── shared/ # Go/Node 共用语义片段
|
||||
│ ├── prompt/ # Prompt 组装
|
||||
│ ├── promptcompat/ # API 请求到 DeepSeek 网页纯文本上下文兼容层
|
||||
│ ├── rawsample/ # raw sample 读写与管理
|
||||
│ ├── server/ # 路由与中间件装配
|
||||
│ │ └── data/ # 路由/运行时辅助数据
|
||||
│ ├── sse/ # SSE 解析工具
|
||||
│ ├── stream/ # 统一流式消费引擎
|
||||
│ ├── testsuite/ # 测试集执行框架
|
||||
│ ├── textclean/ # 文本清洗
|
||||
│ ├── toolcall/ # 工具调用解析与修复
|
||||
│ ├── toolstream/ # Go 流式 tool call 防泄漏与增量检测
|
||||
│ ├── translatorcliproxy/ # 多协议互转桥
|
||||
│ ├── util/ # 通用工具函数
|
||||
│ ├── version/ # 版本查询/比较
|
||||
│ └── webui/ # WebUI 静态托管相关逻辑
|
||||
├── plans/ # 阶段计划与人工验收记录
|
||||
├── pow/ # PoW 独立实现与基准
|
||||
├── scripts/ # 构建/发布/辅助脚本
|
||||
├── tests/ # 测试资源与脚本
|
||||
│ ├── compat/ # 兼容性夹具与期望输出
|
||||
│ │ ├── expected/ # 预期结果样本
|
||||
│ │ └── fixtures/ # 测试输入夹具
|
||||
│ │ ├── sse_chunks/ # SSE chunk 夹具
|
||||
│ │ └── toolcalls/ # toolcall 夹具
|
||||
│ ├── node/ # Node 单元测试
|
||||
│ ├── raw_stream_samples/ # 上游原始 SSE 样本
|
||||
│ │ ├── content-filter-trigger-20260405-jwt3/ # 风控终态样本
|
||||
│ │ ├── continue-thinking-snapshot-replay-20260405/ # continue 样本
|
||||
│ │ ├── guangzhou-weather-reasoner-search-20260404/ # 搜索+引用样本
|
||||
│ │ ├── markdown-format-example-20260405/ # Markdown 样本
|
||||
│ │ └── markdown-format-example-20260405-spacefix/ # 空格修复样本
|
||||
│ ├── scripts/ # 测试脚本入口
|
||||
│ └── tools/ # 测试辅助工具
|
||||
└── webui/ # React 管理台源码
|
||||
├── public/ # 静态资源
|
||||
└── src/ # 前端源码
|
||||
├── app/ # 路由/状态框架
|
||||
├── components/ # 共享组件
|
||||
├── features/ # 功能模块
|
||||
│ ├── account/ # 账号管理页面
|
||||
│ ├── apiTester/ # API 测试页面
|
||||
│ ├── settings/ # 设置页面
|
||||
│ └── vercel/ # Vercel 同步页面
|
||||
├── layout/ # 布局组件
|
||||
├── locales/ # 国际化文案
|
||||
└── utils/ # 前端工具函数
|
||||
│ │ └── shared/ # OpenAI HTTP errors/models/tool formatting
|
||||
│ ├── js/ # Node runtime related logic
|
||||
│ │ ├── chat-stream/ # Node streaming bridge
|
||||
│ │ ├── helpers/ # JS helper modules
|
||||
│ │ │ └── stream-tool-sieve/ # JS implementation of tool sieve
|
||||
│ │ └── shared/ # Shared semantics between Go/Node
|
||||
│ ├── prompt/ # Prompt composition
|
||||
│ ├── promptcompat/ # API request -> DeepSeek web-chat plain-text compatibility
|
||||
│ ├── rawsample/ # Raw sample read/write and management
|
||||
│ ├── server/ # Router and middleware assembly
|
||||
│ │ └── data/ # Router/runtime helper data
|
||||
│ ├── sse/ # SSE parsing utilities
|
||||
│ ├── stream/ # Unified stream consumption engine
|
||||
│ ├── testsuite/ # Testsuite execution framework
|
||||
│ ├── textclean/ # Text cleanup
|
||||
│ ├── toolcall/ # Tool-call parsing and repair
|
||||
│ ├── toolstream/ # Go streaming tool-call anti-leak and delta detection
|
||||
│ ├── translatorcliproxy/ # Cross-protocol translation bridge
|
||||
│ ├── util/ # Shared utility helpers
|
||||
│ ├── version/ # Version query/compare
|
||||
│ └── webui/ # WebUI static hosting logic
|
||||
├── plans/ # Stage plans and manual QA records
|
||||
├── pow/ # PoW standalone implementation + benchmarks
|
||||
├── scripts/ # Build/release helper scripts
|
||||
├── tests/ # Test assets and scripts
|
||||
│ ├── compat/ # Compatibility fixtures + expected outputs
|
||||
│ │ ├── expected/ # Expected output samples
|
||||
│ │ └── fixtures/ # Fixture inputs
|
||||
│ │ ├── sse_chunks/ # SSE chunk fixtures
|
||||
│ │ └── toolcalls/ # Tool-call fixtures
|
||||
│ ├── node/ # Node unit tests
|
||||
│ ├── raw_stream_samples/ # Upstream raw SSE samples
|
||||
│ │ ├── content-filter-trigger-20260405-jwt3/ # Content-filter terminal sample
|
||||
│ │ ├── continue-thinking-snapshot-replay-20260405/ # Continue-thinking sample
|
||||
│ │ ├── guangzhou-weather-reasoner-search-20260404/ # Search/reference sample
|
||||
│ │ ├── markdown-format-example-20260405/ # Markdown sample
|
||||
│ │ └── markdown-format-example-20260405-spacefix/ # Space-fix sample
|
||||
│ ├── scripts/ # Test entry scripts
|
||||
│ └── tools/ # Testing helper tools
|
||||
└── webui/ # React admin console source
|
||||
├── public/ # Static assets
|
||||
└── src/ # Frontend source code
|
||||
├── app/ # Routing/state scaffolding
|
||||
├── components/ # Shared UI components
|
||||
├── features/ # Feature modules
|
||||
│ ├── account/ # Account management page
|
||||
│ ├── apiTester/ # API tester page
|
||||
│ ├── settings/ # Settings page
|
||||
│ └── vercel/ # Vercel sync page
|
||||
├── layout/ # Layout components
|
||||
├── locales/ # i18n strings
|
||||
└── utils/ # Frontend utilities
|
||||
```
|
||||
|
||||
## 2. 请求主链路
|
||||
## 2. Primary Request Flow
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
@@ -149,7 +148,7 @@ flowchart LR
|
||||
CHAT --> PC
|
||||
RESP --> PC
|
||||
PC --> PROMPT
|
||||
PC -.长历史.-> HIST
|
||||
PC -.long history.-> HIST
|
||||
PC --> AUTH
|
||||
|
||||
NCS -.Go prepare/release.-> CHAT
|
||||
@@ -165,32 +164,32 @@ flowchart LR
|
||||
DS --> U[DeepSeek upstream]
|
||||
```
|
||||
|
||||
## 3. internal/ 子模块职责
|
||||
## 3. Responsibilities in `internal/`
|
||||
|
||||
- `internal/server`:路由树和中间件挂载(健康检查、协议入口、Admin/WebUI)。
|
||||
- `internal/httpapi/openai/*`:OpenAI HTTP surface,按 chat、responses、files、embeddings、history、shared 拆分;chat/responses 共享 promptcompat、stream、toolcall 等核心语义。
|
||||
- `internal/httpapi/{claude,gemini}`:协议输入输出适配,归一到同一套 prompt compatibility 语义,不重复实现上游调用逻辑。
|
||||
- `internal/promptcompat`:OpenAI/Claude/Gemini 请求到 DeepSeek 网页纯文本上下文的兼容内核。
|
||||
- `internal/translatorcliproxy`:Claude/Gemini 与 OpenAI 结构互转。
|
||||
- `internal/deepseek/{client,protocol,transport}`:上游请求、会话、PoW 适配、协议常量与传输层。
|
||||
- `internal/js/chat-stream` + `api/chat-stream.js`:Vercel Node 流式桥;Go prepare/release 管理鉴权、账号租约和 completion payload,Node 侧负责实时 SSE 转发并保持 Go 对齐的终结态和 tool sieve 语义。
|
||||
- `internal/stream` + `internal/sse`:Go 流式解析与增量处理。
|
||||
- `internal/toolcall` + `internal/toolstream`:canonical XML 工具调用解析与防泄漏筛分(唯一可执行格式:`<tool_calls>` / `<invoke name="...">` / `<parameter name="...">`)。
|
||||
- `internal/httpapi/admin/*`:Admin API 根装配与 auth/accounts/config/settings/proxies/rawsamples/vercel/history/devcapture/version 等资源子包。
|
||||
- `internal/chathistory`:服务器端对话记录持久化、分页、单条详情和保留策略。
|
||||
- `internal/config`:配置加载、校验、运行时 settings 热更新。
|
||||
- `internal/account`:托管账号池、并发槽位、等待队列、账号健康降权与冷却调度。
|
||||
- `internal/server`: router tree + middlewares (health, protocol routes, Admin/WebUI).
|
||||
- `internal/httpapi/openai/*`: OpenAI HTTP surface split into chat, responses, files, embeddings, history, and shared packages; chat/responses share the promptcompat, stream, and toolcall semantics.
|
||||
- `internal/httpapi/{claude,gemini}`: protocol wrappers that normalize into the same prompt compatibility semantics without duplicating upstream execution.
|
||||
- `internal/promptcompat`: compatibility core for turning OpenAI/Claude/Gemini requests into DeepSeek web-chat plain-text context.
|
||||
- `internal/translatorcliproxy`: structure translation between Claude/Gemini and OpenAI.
|
||||
- `internal/deepseek/{client,protocol,transport}`: upstream requests, sessions, PoW adaptation, protocol constants, and transport details.
|
||||
- `internal/js/chat-stream` + `api/chat-stream.js`: Vercel Node streaming bridge; Go prepare/release owns auth, account lease, and completion payload assembly, while Node relays real-time SSE with Go-aligned finalization and tool sieve semantics.
|
||||
- `internal/stream` + `internal/sse`: Go stream parsing and incremental assembly.
|
||||
- `internal/toolcall` + `internal/toolstream`: canonical XML tool-call parsing + anti-leak sieve (the only executable format is `<tool_calls>` / `<invoke name="...">` / `<parameter name="...">`).
|
||||
- `internal/httpapi/admin/*`: Admin API root assembly plus auth/accounts/config/settings/proxies/rawsamples/vercel/history/devcapture/version resource packages.
|
||||
- `internal/chathistory`: server-side conversation history persistence, pagination, detail lookup, and retention policy.
|
||||
- `internal/config`: config loading/validation + runtime settings hot-reload.
|
||||
- `internal/account`: managed account pool, inflight slots, waiting queue, and account health weight/cooldown scheduling.
|
||||
|
||||
## 4. WebUI 与运行时关系
|
||||
## 4. WebUI Runtime Relation
|
||||
|
||||
- `webui/` 是前端源码(Vite + React)。
|
||||
- 运行时托管目录是 `static/admin`(构建产物)。
|
||||
- 本地首次启动若 `static/admin` 缺失,会尝试自动构建(依赖 Node.js)。
|
||||
- `webui/` stores frontend source (Vite + React).
|
||||
- Runtime serves static output from `static/admin`.
|
||||
- On first local startup, if `static/admin` is missing, DS2API may auto-build it (Node.js required).
|
||||
|
||||
## 5. 文档拆分策略
|
||||
## 5. Documentation Split Strategy
|
||||
|
||||
- 总览与快速开始:`README.MD` / `README.en.md`
|
||||
- 架构与目录:`docs/ARCHITECTURE*.md`(本文件)
|
||||
- 接口协议:`API.md` / `API.en.md`
|
||||
- 部署、测试、贡献:`docs/DEPLOY*`、`docs/TESTING.md`、`docs/CONTRIBUTING*`
|
||||
- 专题:`docs/toolcall-semantics.md`、`docs/DeepSeekSSE行为结构说明-2026-04-05.md`
|
||||
- Onboarding & quick start: `README.MD`
|
||||
- Architecture & layout: `docs/ARCHITECTURE*.md` (this file)
|
||||
- API contracts: `API.md`
|
||||
- Deployment/testing/contributing: `docs/DEPLOY*`, `docs/TESTING.md`, `docs/CONTRIBUTING*`
|
||||
- Deep topics: `docs/toolcall-semantics.md`, `docs/DeepSeekSSE行为结构说明-2026-04-05.md`
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
# Contributing Guide
|
||||
|
||||
Language: [中文](CONTRIBUTING.md) | [English](CONTRIBUTING.en.md)
|
||||
|
||||
Thanks for your interest in contributing to DS2API!
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.26+
|
||||
- Node.js `20.19+` or `22.12+` (for WebUI development)
|
||||
- npm (bundled with Node.js)
|
||||
|
||||
### Backend Development
|
||||
|
||||
```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 test accounts
|
||||
|
||||
# 3. Run backend
|
||||
go run ./cmd/ds2api
|
||||
# Local access: http://127.0.0.1:5001
|
||||
# Actual bind: 0.0.0.0:5001, so LAN access is available via your private IP
|
||||
```
|
||||
|
||||
### Frontend Development (WebUI)
|
||||
|
||||
```bash
|
||||
# 1. Navigate to WebUI directory
|
||||
cd webui
|
||||
|
||||
# 2. Install dependencies
|
||||
npm install
|
||||
|
||||
# 3. Start dev server (hot reload)
|
||||
npm run dev
|
||||
# Default: http://localhost:5173, auto-proxies API to backend
|
||||
# host: 0.0.0.0 is not configured, so LAN access is not enabled by default
|
||||
```
|
||||
|
||||
WebUI tech stack:
|
||||
- React + Vite
|
||||
- Tailwind CSS
|
||||
- Bilingual language packs: `webui/src/locales/zh.json` / `en.json`
|
||||
|
||||
### Docker Development
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
## Code Standards
|
||||
|
||||
| Language | Standards |
|
||||
| --- | --- |
|
||||
| **Go** | Run `gofmt -w` after editing Go files; before committing, run `./scripts/lint.sh` (format check + golangci-lint) |
|
||||
| **JavaScript/React** | Follow existing project style (functional components) |
|
||||
| **Commit messages** | Use semantic prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `style:`, `perf:`, `chore:` |
|
||||
|
||||
Do not silently ignore cleanup errors from I/O-style calls such as `Close`, `Flush`, or `Sync`; return them when possible, otherwise log them explicitly.
|
||||
|
||||
## Submitting a PR
|
||||
|
||||
1. Fork the repo
|
||||
2. Create a branch (e.g. `feature/xxx` or `fix/xxx`)
|
||||
3. Commit changes
|
||||
4. Push your branch
|
||||
5. Open a Pull Request
|
||||
|
||||
> 💡 If you modify files under `webui/`, no manual build is needed — CI handles it automatically.
|
||||
> If you want to verify the generated `static/admin/` assets locally, you can still run `./scripts/build-webui.sh`.
|
||||
|
||||
## Build WebUI
|
||||
|
||||
Manually build WebUI to `static/admin/`:
|
||||
|
||||
```bash
|
||||
./scripts/build-webui.sh
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# Local PR gates (kept aligned with the quality-gates workflow)
|
||||
./scripts/lint.sh
|
||||
./tests/scripts/check-refactor-line-gate.sh
|
||||
./tests/scripts/run-unit-all.sh
|
||||
npm run build --prefix webui
|
||||
|
||||
# End-to-end live tests (real accounts; recommended for releases or high-risk changes)
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
## Upstream Fork Monitoring
|
||||
|
||||
The local review refs, feature themes, and current recommendations for active forks are tracked in [Upstream fork monitoring notes](./upstream-forks.md). Before importing code from an external fork, refresh those refs and port the useful behavior in small feature slices instead of merging an entire fork branch.
|
||||
|
||||
## Project Structure
|
||||
|
||||
To avoid documentation drift, directory layout and module responsibilities were moved to:
|
||||
|
||||
- [docs/ARCHITECTURE.en.md](./ARCHITECTURE.en.md)
|
||||
- [docs/README.md](./README.md)
|
||||
|
||||
Before contributing, review the architecture doc sections for request flow and `internal/` module boundaries.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Please use [GitHub Issues](https://github.com/CJackHwang/ds2api/issues) and include:
|
||||
|
||||
- Steps to reproduce
|
||||
- Relevant log output
|
||||
- Environment info (OS, Go version, deployment method)
|
||||
+51
-52
@@ -1,119 +1,118 @@
|
||||
# 贡献指南
|
||||
# Contributing Guide
|
||||
|
||||
语言 / Language: [中文](CONTRIBUTING.md) | [English](CONTRIBUTING.en.md)
|
||||
|
||||
感谢你对 DS2API 的关注与贡献!
|
||||
Thanks for your interest in contributing to DS2API!
|
||||
|
||||
## 开发环境设置
|
||||
## Development Setup
|
||||
|
||||
### 前置要求
|
||||
### Prerequisites
|
||||
|
||||
- Go 1.26+
|
||||
- Node.js `20.19+` 或 `22.12+`(WebUI 开发时)
|
||||
- npm(随 Node.js 提供)
|
||||
- Node.js `20.19+` or `22.12+` (for WebUI development)
|
||||
- npm (bundled with Node.js)
|
||||
|
||||
### 后端开发
|
||||
### Backend Development
|
||||
|
||||
```bash
|
||||
# 1. 克隆仓库
|
||||
# 1. Clone
|
||||
git clone https://github.com/CJackHwang/ds2api.git
|
||||
cd ds2api
|
||||
|
||||
# 2. 配置
|
||||
# 2. Configure
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json,填入测试账号
|
||||
# Edit config.json with test accounts
|
||||
|
||||
# 3. 启动后端
|
||||
# 3. Run backend
|
||||
go run ./cmd/ds2api
|
||||
# 本地访问 http://127.0.0.1:5001
|
||||
# 实际绑定 0.0.0.0:5001,可通过局域网 IP 访问
|
||||
# Local access: http://127.0.0.1:5001
|
||||
# Actual bind: 0.0.0.0:5001, so LAN access is available via your private IP
|
||||
```
|
||||
|
||||
### 前端开发(WebUI)
|
||||
### Frontend Development (WebUI)
|
||||
|
||||
```bash
|
||||
# 1. 进入 WebUI 目录
|
||||
# 1. Navigate to WebUI directory
|
||||
cd webui
|
||||
|
||||
# 2. 安装依赖
|
||||
# 2. Install dependencies
|
||||
npm install
|
||||
|
||||
# 3. 启动开发服务器(热更新)
|
||||
# 3. Start dev server (hot reload)
|
||||
npm run dev
|
||||
# 默认监听 http://localhost:5173,自动代理 API 到后端
|
||||
# 当前未配置 host: 0.0.0.0,因此默认不对局域网开放
|
||||
# Default: http://localhost:5173, auto-proxies API to backend
|
||||
# host: 0.0.0.0 is not configured, so LAN access is not enabled by default
|
||||
```
|
||||
|
||||
WebUI 技术栈:
|
||||
WebUI tech stack:
|
||||
- React + Vite
|
||||
- Tailwind CSS
|
||||
- 中英文语言包:`webui/src/locales/zh.json` / `en.json`
|
||||
- Bilingual language packs: `webui/src/locales/zh.json` / `en.json`
|
||||
|
||||
### Docker 开发环境
|
||||
### Docker Development
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
## 代码规范
|
||||
## Code Standards
|
||||
|
||||
| 语言 | 规范 |
|
||||
| Language | Standards |
|
||||
| --- | --- |
|
||||
| **Go** | 修改 Go 文件后运行 `gofmt -w`;提交前运行 `./scripts/lint.sh`(包含格式化检查和 golangci-lint) |
|
||||
| **JavaScript/React** | 保持现有代码风格(函数组件) |
|
||||
| **提交信息** | 使用语义化前缀:`feat:`、`fix:`、`docs:`、`refactor:`、`style:`、`perf:`、`chore:` |
|
||||
| **Go** | Run `gofmt -w` after editing Go files; before committing, run `./scripts/lint.sh` (format check + golangci-lint) |
|
||||
| **JavaScript/React** | Follow existing project style (functional components) |
|
||||
| **Commit messages** | Use semantic prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `style:`, `perf:`, `chore:` |
|
||||
|
||||
I/O 类清理调用(如 `Close`、`Flush`、`Sync`)的错误不要直接忽略;无法向上返回时请显式记录日志。
|
||||
Do not silently ignore cleanup errors from I/O-style calls such as `Close`, `Flush`, or `Sync`; return them when possible, otherwise log them explicitly.
|
||||
|
||||
## 提交 PR
|
||||
## Submitting a PR
|
||||
|
||||
1. Fork 仓库
|
||||
2. 创建分支(如 `feature/xxx` 或 `fix/xxx`)
|
||||
3. 提交更改
|
||||
4. 推送分支
|
||||
5. 发起 Pull Request
|
||||
1. Fork the repo
|
||||
2. Create a branch (e.g. `feature/xxx` or `fix/xxx`)
|
||||
3. Commit changes
|
||||
4. Push your branch
|
||||
5. Open a Pull Request
|
||||
|
||||
> 💡 如果修改了 `webui/` 目录下的文件,无需手动构建——CI 会自动处理。
|
||||
> 但如果你本地想验证 `static/admin/` 产物,还是可以手动运行 `./scripts/build-webui.sh`。
|
||||
> 💡 If you modify files under `webui/`, no manual build is needed — CI handles it automatically.
|
||||
> If you want to verify the generated `static/admin/` assets locally, you can still run `./scripts/build-webui.sh`.
|
||||
|
||||
## WebUI 构建
|
||||
## Build WebUI
|
||||
|
||||
手动构建 WebUI 到 `static/admin/`:
|
||||
Manually build WebUI to `static/admin/`:
|
||||
|
||||
```bash
|
||||
./scripts/build-webui.sh
|
||||
```
|
||||
|
||||
## 运行测试
|
||||
## Running Tests
|
||||
|
||||
```bash
|
||||
# PR 本地门禁(与 quality-gates 工作流保持一致)
|
||||
# Local PR gates (kept aligned with the quality-gates workflow)
|
||||
./scripts/lint.sh
|
||||
./tests/scripts/check-refactor-line-gate.sh
|
||||
./tests/scripts/run-unit-all.sh
|
||||
npm run build --prefix webui
|
||||
|
||||
# 端到端全链路测试(真实账号,发布或高风险改动时建议执行)
|
||||
# End-to-end live tests (real accounts; recommended for releases or high-risk changes)
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
## 上游 Fork 监控
|
||||
## Upstream Fork Monitoring
|
||||
|
||||
活跃 fork 的本地 review refs、功能主题和当前处理建议维护在 [上游 Fork 监控清单](./upstream-forks.md)。从外部 fork 吸收实现前,先刷新该清单中的 ref,再按功能块做最小化 cherry-pick 或重写,避免整分支合并带入无关架构变化。
|
||||
The local review refs, feature themes, and current recommendations for active forks are tracked in [Upstream fork monitoring notes](./upstream-forks.md). Before importing code from an external fork, refresh those refs and port the useful behavior in small feature slices instead of merging an entire fork branch.
|
||||
|
||||
## 项目结构
|
||||
## Project Structure
|
||||
|
||||
为避免与其他文档重复维护,目录结构与模块职责已迁移到:
|
||||
To avoid documentation drift, directory layout and module responsibilities were moved to:
|
||||
|
||||
- [docs/ARCHITECTURE.md](./ARCHITECTURE.md)
|
||||
- [docs/README.md](./README.md)
|
||||
|
||||
贡献前建议先阅读架构文档中的“请求主链路”和 `internal/` 模块职责,再定位改动范围。
|
||||
Before contributing, review the architecture doc sections for request flow and `internal/` module boundaries.
|
||||
|
||||
## 问题反馈
|
||||
## Reporting Issues
|
||||
|
||||
请使用 [GitHub Issues](https://github.com/CJackHwang/ds2api/issues) 并附上:
|
||||
Please use [GitHub Issues](https://github.com/CJackHwang/ds2api/issues) and include:
|
||||
|
||||
- 复现步骤
|
||||
- 相关日志输出
|
||||
- 运行环境信息(OS、Go 版本、部署方式)
|
||||
- Steps to reproduce
|
||||
- Relevant log output
|
||||
- Environment info (OS, Go version, deployment method)
|
||||
|
||||
@@ -1,584 +0,0 @@
|
||||
# DS2API Deployment Guide
|
||||
|
||||
Language: [中文](DEPLOY.md) | [English](DEPLOY.en.md)
|
||||
|
||||
This guide covers all deployment methods for the current Go-based codebase.
|
||||
|
||||
Doc map: [Index](./README.md) | [Architecture](./ARCHITECTURE.en.md) | [API](../API.en.md) | [Testing](./TESTING.md)
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Recommended deployment priority](#recommended-deployment-priority)
|
||||
- [Prerequisites](#0-prerequisites)
|
||||
- [1. Download Release Binaries](#1-download-release-binaries)
|
||||
- [2. Docker / GHCR Deployment](#2-docker--ghcr-deployment)
|
||||
- [3. Vercel Deployment](#3-vercel-deployment)
|
||||
- [4. Local Run from Source](#4-local-run-from-source)
|
||||
- [5. Reverse Proxy (Nginx)](#5-reverse-proxy-nginx)
|
||||
- [6. Linux systemd Service](#6-linux-systemd-service)
|
||||
- [7. Post-Deploy Checks](#7-post-deploy-checks)
|
||||
- [8. Pre-Release Local Regression](#8-pre-release-local-regression)
|
||||
|
||||
---
|
||||
|
||||
## Recommended deployment priority
|
||||
|
||||
Recommended order when choosing a deployment method:
|
||||
|
||||
1. **Download and run release binaries**: the easiest path for most users because the artifacts are already built.
|
||||
2. **Docker / GHCR image deployment**: suitable for containerized, orchestrated, or cloud environments.
|
||||
3. **Vercel deployment**: suitable if you already use Vercel and accept its platform constraints.
|
||||
4. **Run from source / build locally**: suitable for development, debugging, or when you need to modify the code yourself.
|
||||
|
||||
---
|
||||
|
||||
## 0. Prerequisites
|
||||
|
||||
| Dependency | Minimum Version | Notes |
|
||||
| --- | --- | --- |
|
||||
| Go | 1.26+ | Build backend |
|
||||
| Node.js | `20.19+` or `22.12+` | Only needed to build WebUI locally |
|
||||
| npm | Bundled with Node.js | Install WebUI dependencies |
|
||||
|
||||
Config source (choose one):
|
||||
|
||||
- **File**: `config.json` (recommended for local/Docker)
|
||||
- **Environment variable**: `DS2API_CONFIG_JSON` (recommended for Vercel; supports raw JSON or Base64)
|
||||
|
||||
Unified recommendation (best practice):
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
Use `config.json` as the single source of truth:
|
||||
- Local run: read `config.json` directly
|
||||
- Docker / Vercel: generate `DS2API_CONFIG_JSON` (Base64) from `config.json` and inject it
|
||||
|
||||
---
|
||||
|
||||
## 1. Download Release Binaries
|
||||
|
||||
Built-in GitHub Actions workflow: `.github/workflows/release-artifacts.yml`
|
||||
|
||||
- **Trigger**: only on Release `published` (no build on normal push)
|
||||
- **Outputs**: multi-platform binary archives + `sha256sums.txt`
|
||||
- **Container publishing**: GHCR only (`ghcr.io/cjackhwang/ds2api`)
|
||||
|
||||
In addition, `.github/workflows/ghcr.yml` automatically builds and pushes multi-arch GHCR images on `main` pushes, `v*` tag pushes, or manual dispatch. Tags include the branch name, version tag, `sha-*`, and `latest` for the default branch.
|
||||
|
||||
| Platform | Architecture | Format |
|
||||
| --- | --- | --- |
|
||||
| Linux | amd64, arm64 | `.tar.gz` |
|
||||
| macOS | amd64, arm64 | `.tar.gz` |
|
||||
| Windows | amd64 | `.zip` |
|
||||
|
||||
Each archive includes:
|
||||
|
||||
- `ds2api` executable (`ds2api.exe` on Windows)
|
||||
- `static/admin/` (built WebUI assets)
|
||||
- `config.example.json`, `.env.example`
|
||||
- `README.MD`, `README.en.md`, `LICENSE`
|
||||
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# 1. Download the archive for your platform
|
||||
# 2. Extract
|
||||
tar -xzf ds2api_<tag>_linux_amd64.tar.gz
|
||||
cd ds2api_<tag>_linux_amd64
|
||||
|
||||
# 3. Configure
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
|
||||
# 4. Start
|
||||
./ds2api
|
||||
```
|
||||
|
||||
### Maintainer Release Flow
|
||||
|
||||
1. Create and publish a GitHub Release (with tag, for example `vX.Y.Z`)
|
||||
2. Wait for the `Release Artifacts` workflow to complete
|
||||
3. Download the matching archive from Release Assets
|
||||
|
||||
---
|
||||
|
||||
## 2. Docker / GHCR Deployment
|
||||
|
||||
### 2.1 Basic Steps
|
||||
|
||||
```bash
|
||||
# Pull prebuilt image
|
||||
docker pull ghcr.io/cjackhwang/ds2api:latest
|
||||
|
||||
# Copy env template and config file
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# Edit .env and set at least:
|
||||
# DS2API_ADMIN_KEY=your-admin-key
|
||||
# Optionally set the host port:
|
||||
# DS2API_HOST_PORT=6011
|
||||
|
||||
# Start
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
The default `docker-compose.yml` directly uses `ghcr.io/cjackhwang/ds2api:latest` and maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
If you want a pinned version instead of `latest`, you can also pull a specific tag directly:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/cjackhwang/ds2api:v3.0.0
|
||||
```
|
||||
|
||||
### 2.2 Update
|
||||
|
||||
```bash
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
### 2.3 Docker Architecture
|
||||
|
||||
The `Dockerfile` now provides two image paths:
|
||||
|
||||
1. **Default local/dev path (`runtime-from-source`)**: a three-stage build (WebUI build + Go build + runtime).
|
||||
2. **Release path (`runtime-from-dist`)**: the release workflow first creates tag-named release archives, then copies the Linux bundles to `dist/docker-input/linux_amd64.tar.gz` / `linux_arm64.tar.gz`; Docker consumes those prepared inputs directly, without rerunning `npm build`/`go build`.
|
||||
|
||||
The release path keeps Docker images aligned with release archives and reduces duplicate build work.
|
||||
|
||||
Container entry command: `/usr/local/bin/ds2api`, default exposed port: `5001`.
|
||||
|
||||
### 2.4 Development Mode
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
Development features:
|
||||
- Source code mounted (live changes)
|
||||
- `LOG_LEVEL=DEBUG`
|
||||
- No auto-restart
|
||||
|
||||
### 2.5 Health Check
|
||||
|
||||
Docker Compose includes a built-in health check:
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
test: ["CMD", "/usr/local/bin/busybox", "wget", "-qO-", "http://localhost:${PORT:-5001}/healthz"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### 2.6 Docker Troubleshooting
|
||||
|
||||
If container logs look normal but the admin panel is unreachable, check these first:
|
||||
|
||||
1. **Port alignment**: when `PORT` is not `5001`, use the same port in your URL (for example `http://localhost:8080/admin`).
|
||||
2. **WebUI assets in dev compose**: `docker-compose.dev.yml` runs `go run` in a dev image and does not auto-install Node.js inside the container; if `static/admin` is missing in your repo, `/admin` will return 404. Build once on host: `./scripts/build-webui.sh`.
|
||||
|
||||
### 2.7 Zeabur One-Click (Dockerfile)
|
||||
|
||||
This repo includes a `zeabur.yaml` template for one-click deployment on Zeabur:
|
||||
|
||||
[](https://zeabur.com/templates/L4CFHP)
|
||||
|
||||
Notes:
|
||||
|
||||
- **Port**: DS2API listens on `5001` by default; the template sets `PORT=5001`.
|
||||
- **Persistent config**: the template mounts `/data` and sets `DS2API_CONFIG_PATH=/data/config.json`. After importing config in Admin UI, it will be written and persisted to this path.
|
||||
- **Build version**: Zeabur / regular `docker build` does not require `BUILD_VERSION` by default. The image prefers that build arg when provided, and automatically falls back to the repo-root `VERSION` file when it is absent.
|
||||
- **First login**: after deployment, open `/admin` and login with `DS2API_ADMIN_KEY` shown in Zeabur env/template instructions (recommended: rotate to a strong secret after first login).
|
||||
|
||||
---
|
||||
|
||||
## 3. Vercel Deployment
|
||||
|
||||
### 3.1 Steps
|
||||
|
||||
1. **Fork** the repo to your GitHub account
|
||||
2. **Import** the project on Vercel
|
||||
3. **Set environment variables** (minimum required: one variable):
|
||||
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| `DS2API_ADMIN_KEY` | Admin key (required) |
|
||||
| `DS2API_CONFIG_JSON` | Config content, raw JSON or Base64 (optional, recommended) |
|
||||
|
||||
4. **Deploy**
|
||||
|
||||
### 3.1.1 Recommended Input (avoid `DS2API_CONFIG_JSON` mistakes)
|
||||
|
||||
If you prefer faster one-click bootstrap, you can leave `DS2API_CONFIG_JSON` empty first, then open `/admin` after deployment, import config, and sync it back to Vercel env vars from the "Vercel Sync" page.
|
||||
|
||||
Recommended: in repo root, copy the template first and fill your real accounts:
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
Do not hand-edit large JSON directly in Vercel. Generate Base64 locally and paste it:
|
||||
|
||||
```bash
|
||||
# Run in repo root
|
||||
DS2API_CONFIG_JSON="$(base64 < config.json | tr -d '\n')"
|
||||
echo "$DS2API_CONFIG_JSON"
|
||||
```
|
||||
|
||||
If you choose to preconfigure before first deploy, set these vars in Vercel Project Settings -> Environment Variables:
|
||||
|
||||
```text
|
||||
DS2API_ADMIN_KEY=replace-with-a-strong-secret
|
||||
DS2API_CONFIG_JSON=<the single-line Base64 output above>
|
||||
```
|
||||
|
||||
Optional but recommended (for WebUI one-click Vercel sync):
|
||||
|
||||
```text
|
||||
VERCEL_TOKEN=your-vercel-token
|
||||
VERCEL_PROJECT_ID=prj_xxxxxxxxxxxx
|
||||
VERCEL_TEAM_ID=team_xxxxxxxxxxxx # optional for personal accounts
|
||||
```
|
||||
|
||||
### 3.2 Optional Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `DS2API_ACCOUNT_MAX_INFLIGHT` | Per-account inflight limit | `2` |
|
||||
| `DS2API_ACCOUNT_MAX_QUEUE` | Waiting queue limit | `recommended_concurrency` |
|
||||
| `DS2API_GLOBAL_MAX_INFLIGHT` | Global inflight limit | `recommended_concurrency` |
|
||||
| `DS2API_ENV_WRITEBACK` | When `DS2API_CONFIG_JSON` is present, auto-write to `DS2API_CONFIG_PATH` and switch to file-backed mode after success (`1/true/yes/on`) | Disabled |
|
||||
| `DS2API_VERCEL_INTERNAL_SECRET` | Hybrid streaming internal auth | Falls back to `DS2API_ADMIN_KEY` |
|
||||
| `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | Stream lease TTL | `900` |
|
||||
| `DS2API_RAW_STREAM_SAMPLE_ROOT` | Raw stream sample root for saving/reading samples | `tests/raw_stream_samples` |
|
||||
| `VERCEL_TOKEN` | Vercel sync token | — |
|
||||
| `VERCEL_PROJECT_ID` | Vercel project ID | — |
|
||||
| `VERCEL_TEAM_ID` | Vercel team ID | — |
|
||||
| `DS2API_VERCEL_PROTECTION_BYPASS` | Deployment protection bypass for internal Node→Go calls | — |
|
||||
|
||||
### 3.4 Vercel Architecture
|
||||
|
||||
```text
|
||||
Request ──────┐
|
||||
│
|
||||
▼
|
||||
vercel.json routing
|
||||
│
|
||||
┌─────┴─────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
api/index.go api/chat-stream.js
|
||||
(Go Runtime) (Node Runtime)
|
||||
```
|
||||
|
||||
- **Go entry**: `api/index.go` (Serverless Go)
|
||||
- **Stream entry**: `api/chat-stream.js` (Node Runtime for real-time SSE)
|
||||
- **Routing**: `vercel.json`
|
||||
- **Build command**: `npm ci --prefix webui && npm run build --prefix webui` (automatic)
|
||||
|
||||
#### Streaming Pipeline
|
||||
|
||||
Vercel Go Runtime applies platform-level response buffering, so this project uses a hybrid "**Go prepare + Node stream**" path on Vercel:
|
||||
|
||||
1. `api/chat-stream.js` receives `/v1/chat/completions` request
|
||||
2. Node calls Go internal prepare endpoint (`?__stream_prepare=1`) for session ID, PoW, token
|
||||
3. Go prepare creates a stream lease, locking the account
|
||||
4. Node connects directly to DeepSeek upstream, relays SSE in real-time to client (including OpenAI chunk framing and tools anti-leak sieve)
|
||||
5. After stream ends, Node calls Go release endpoint (`?__stream_release=1`) to free the account
|
||||
|
||||
> This adaptation is **Vercel-only**; local and Docker remain pure Go.
|
||||
|
||||
#### Non-Stream Fallback and Tool Call Handling
|
||||
|
||||
- `api/chat-stream.js` falls back to Go entry (`?__go=1`) for non-stream requests only
|
||||
- Streaming requests (including requests with `tools`) stay on the Node path and use Go-aligned tool-call anti-leak handling
|
||||
- The Node stream path also mirrors Go finalization semantics: empty visible output returns the same shaped error SSE, and empty `content_filter` returns a `content_filter` error
|
||||
- WebUI non-stream test calls `?__go=1` directly to avoid Node hop timeout on long requests
|
||||
|
||||
#### Function Duration
|
||||
|
||||
`vercel.json` sets `maxDuration: 300` for both `api/chat-stream.js` and `api/index.go` (subject to your Vercel plan limits).
|
||||
|
||||
### 3.5 Vercel Troubleshooting
|
||||
|
||||
#### Go Build Failure
|
||||
|
||||
```text
|
||||
Error: Command failed: go build -ldflags -s -w -o .../bootstrap ...
|
||||
```
|
||||
|
||||
**Cause**: Invalid Go build flag settings in Vercel (`-ldflags` not passed as a single argument).
|
||||
|
||||
**Fix**:
|
||||
|
||||
1. Open Vercel Project Settings → Build and Development Settings
|
||||
2. **Clear** custom Go Build Flags / Build Command (recommended)
|
||||
3. If ldflags must be used, set `-ldflags="-s -w"` (ensure it's one argument)
|
||||
4. Verify `go.mod` uses a supported version (currently `go 1.26.0`)
|
||||
5. Redeploy (recommended: clear cache)
|
||||
|
||||
#### Internal Package Import Error
|
||||
|
||||
```text
|
||||
use of internal package ds2api/internal/server not allowed
|
||||
```
|
||||
|
||||
**Cause**: Vercel Go entrypoint directly imports `internal/...`.
|
||||
|
||||
**Fix**: This repo uses a public bridge package: `api/index.go` → `ds2api/app` → `internal/server`.
|
||||
|
||||
#### Output Directory Error
|
||||
|
||||
```text
|
||||
No Output Directory named "public" found after the Build completed.
|
||||
```
|
||||
|
||||
**Fix**: This repo uses `static` as output directory (`"outputDirectory": "static"` in `vercel.json`). If you manually changed Output Directory in Project Settings, set it to `static` or clear it.
|
||||
|
||||
#### Deployment Protection Blocking
|
||||
|
||||
If API responses return Vercel HTML `Authentication Required`:
|
||||
|
||||
- **Option A**: Disable Deployment Protection for that environment (recommended for public APIs)
|
||||
- **Option B**: Add `x-vercel-protection-bypass` header to requests
|
||||
- **Option C**: Set `VERCEL_AUTOMATION_BYPASS_SECRET` (or `DS2API_VERCEL_PROTECTION_BYPASS`) for internal Node→Go calls
|
||||
|
||||
### 3.6 Build Artifacts Not Committed
|
||||
|
||||
- `static/admin` directory is not in Git
|
||||
- Vercel / Docker automatically generate WebUI assets during build
|
||||
|
||||
---
|
||||
|
||||
## 4. Local Run from Source
|
||||
|
||||
### 4.1 Basic Steps
|
||||
|
||||
```bash
|
||||
# Clone
|
||||
git clone https://github.com/CJackHwang/ds2api.git
|
||||
cd ds2api
|
||||
|
||||
# Copy and edit config
|
||||
cp config.example.json config.json
|
||||
# Open config.json and fill in:
|
||||
# - keys: your API access keys
|
||||
# - accounts: DeepSeek accounts (email or mobile + password)
|
||||
|
||||
# Start
|
||||
go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
Default local access URL: `http://127.0.0.1:5001`; the server actually binds to `0.0.0.0:5001` (override with `PORT`).
|
||||
|
||||
### 4.2 WebUI Build
|
||||
|
||||
On first local startup, if `static/admin/` is missing, DS2API will automatically attempt to build the WebUI (requires Node.js/npm; when dependencies are missing it runs `npm ci` first, then `npm run build -- --outDir static/admin --emptyOutDir`).
|
||||
|
||||
Manual build:
|
||||
|
||||
```bash
|
||||
./scripts/build-webui.sh
|
||||
```
|
||||
|
||||
Or step by step:
|
||||
|
||||
```bash
|
||||
cd webui
|
||||
npm install
|
||||
npm run build
|
||||
# Output goes to static/admin/
|
||||
```
|
||||
|
||||
Control auto-build via environment variable:
|
||||
|
||||
```bash
|
||||
# Disable auto-build
|
||||
DS2API_AUTO_BUILD_WEBUI=false go run ./cmd/ds2api
|
||||
|
||||
# Force enable auto-build
|
||||
DS2API_AUTO_BUILD_WEBUI=true go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
### 4.3 Compile to Binary
|
||||
|
||||
```bash
|
||||
go build -o ds2api ./cmd/ds2api
|
||||
./ds2api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Reverse Proxy (Nginx)
|
||||
|
||||
When deploying behind Nginx, **you must disable buffering** for SSE streaming to work:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
chunked_transfer_encoding on;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
```
|
||||
|
||||
For HTTPS, add SSL at the Nginx layer:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name api.example.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:5001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
chunked_transfer_encoding on;
|
||||
tcp_nodelay on;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Linux systemd Service
|
||||
|
||||
### 6.1 Installation
|
||||
|
||||
```bash
|
||||
# Copy compiled binary and related files to target directory
|
||||
sudo mkdir -p /opt/ds2api
|
||||
sudo cp ds2api config.json /opt/ds2api/
|
||||
sudo cp -r static/admin /opt/ds2api/static/admin
|
||||
```
|
||||
|
||||
### 6.2 Create systemd Service File
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/ds2api.service
|
||||
|
||||
[Unit]
|
||||
Description=DS2API (Go)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/opt/ds2api
|
||||
Environment=PORT=5001
|
||||
Environment=DS2API_CONFIG_PATH=/opt/ds2api/config.json
|
||||
Environment=DS2API_ADMIN_KEY=your-admin-key-here
|
||||
ExecStart=/opt/ds2api/ds2api
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 6.3 Common Commands
|
||||
|
||||
```bash
|
||||
# Reload service config
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# Enable on boot
|
||||
sudo systemctl enable ds2api
|
||||
|
||||
# Start
|
||||
sudo systemctl start ds2api
|
||||
|
||||
# Check status
|
||||
sudo systemctl status ds2api
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u ds2api -f
|
||||
|
||||
# Restart
|
||||
sudo systemctl restart ds2api
|
||||
|
||||
# Stop
|
||||
sudo systemctl stop ds2api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Post-Deploy Checks
|
||||
|
||||
After deployment (any method), verify in order:
|
||||
|
||||
```bash
|
||||
# 1. Liveness probe
|
||||
curl -s http://127.0.0.1:5001/healthz
|
||||
# Expected: {"status":"ok"}
|
||||
|
||||
# 2. Readiness probe
|
||||
curl -s http://127.0.0.1:5001/readyz
|
||||
# Expected: {"status":"ready"}
|
||||
|
||||
# 3. Model list
|
||||
curl -s http://127.0.0.1:5001/v1/models
|
||||
# Expected: {"object":"list","data":[...]}
|
||||
|
||||
# 4. Admin panel (if WebUI is built)
|
||||
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5001/admin
|
||||
# Expected: 200
|
||||
|
||||
# 5. Test API call
|
||||
curl http://127.0.0.1:5001/v1/chat/completions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"hello"}]}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Pre-Release Local Regression
|
||||
|
||||
Run the full live testsuite before release (real account tests):
|
||||
|
||||
```bash
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
With custom flags:
|
||||
|
||||
```bash
|
||||
go run ./cmd/ds2api-tests \
|
||||
--config config.json \
|
||||
--admin-key admin \
|
||||
--out artifacts/testsuite \
|
||||
--timeout 120 \
|
||||
--retries 2
|
||||
```
|
||||
|
||||
The testsuite automatically performs:
|
||||
|
||||
- ✅ Preflight checks (syntax/build/unit tests)
|
||||
- ✅ Isolated config copy startup (no mutation to your original `config.json`)
|
||||
- ✅ Live scenario verification (OpenAI/Claude/Admin/concurrency/toolcall/streaming)
|
||||
- ✅ Full request/response artifact logging for debugging
|
||||
|
||||
For detailed testsuite documentation, see [TESTING.md](TESTING.md). The fixed local PR gates are listed in [TESTING.md](TESTING.md#pr-门禁--pr-gates).
|
||||
+228
-239
@@ -1,175 +1,174 @@
|
||||
# DS2API 部署指南
|
||||
# DS2API Deployment Guide
|
||||
|
||||
语言 / Language: [中文](DEPLOY.md) | [English](DEPLOY.en.md)
|
||||
|
||||
本指南基于当前 Go 代码库,详细说明各种部署方式。
|
||||
This guide covers all deployment methods for the current Go-based codebase.
|
||||
|
||||
本页导航:[文档总索引](./README.md)|[架构说明](./ARCHITECTURE.md)|[接口文档](../API.md)|[测试指南](./TESTING.md)
|
||||
Doc map: [Index](./README.md) | [Architecture](./ARCHITECTURE.md) | [API](../API.md) | [Testing](./TESTING.md)
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
## Table of Contents
|
||||
|
||||
- [部署方式优先级建议](#部署方式优先级建议)
|
||||
- [前置要求](#0-前置要求)
|
||||
- [一、下载 Release 构建包](#一下载-release-构建包)
|
||||
- [二、Docker / GHCR 部署](#二docker--ghcr-部署)
|
||||
- [三、Vercel 部署](#三vercel-部署)
|
||||
- [四、本地源码运行](#四本地源码运行)
|
||||
- [五、反向代理(Nginx)](#五反向代理nginx)
|
||||
- [六、Linux systemd 服务化](#六linux-systemd-服务化)
|
||||
- [七、部署后检查](#七部署后检查)
|
||||
- [八、发布前进行本地回归](#八发布前进行本地回归)
|
||||
- [Recommended deployment priority](#recommended-deployment-priority)
|
||||
- [Prerequisites](#0-prerequisites)
|
||||
- [1. Download Release Binaries](#1-download-release-binaries)
|
||||
- [2. Docker / GHCR Deployment](#2-docker--ghcr-deployment)
|
||||
- [3. Vercel Deployment](#3-vercel-deployment)
|
||||
- [4. Local Run from Source](#4-local-run-from-source)
|
||||
- [5. Reverse Proxy (Nginx)](#5-reverse-proxy-nginx)
|
||||
- [6. Linux systemd Service](#6-linux-systemd-service)
|
||||
- [7. Post-Deploy Checks](#7-post-deploy-checks)
|
||||
- [8. Pre-Release Local Regression](#8-pre-release-local-regression)
|
||||
|
||||
---
|
||||
|
||||
## 部署方式优先级建议
|
||||
## Recommended deployment priority
|
||||
|
||||
推荐按以下顺序选择部署方式:
|
||||
Recommended order when choosing a deployment method:
|
||||
|
||||
1. **下载 Release 构建包运行**:最省事,产物已编译完成,最适合大多数用户。
|
||||
2. **Docker / GHCR 镜像部署**:适合需要容器化、编排或云环境部署。
|
||||
3. **Vercel 部署**:适合已有 Vercel 环境且接受其平台约束的场景。
|
||||
4. **本地源码运行 / 自行编译**:适合开发、调试或需要自行修改代码的场景。
|
||||
1. **Download and run release binaries**: the easiest path for most users because the artifacts are already built.
|
||||
2. **Docker / GHCR image deployment**: suitable for containerized, orchestrated, or cloud environments.
|
||||
3. **Vercel deployment**: suitable if you already use Vercel and accept its platform constraints.
|
||||
4. **Run from source / build locally**: suitable for development, debugging, or when you need to modify the code yourself.
|
||||
|
||||
---
|
||||
|
||||
## 0. 前置要求
|
||||
## 0. Prerequisites
|
||||
|
||||
| 依赖 | 最低版本 | 说明 |
|
||||
| Dependency | Minimum Version | Notes |
|
||||
| --- | --- | --- |
|
||||
| Go | 1.26+ | 编译后端 |
|
||||
| Node.js | `20.19+` 或 `22.12+` | 仅在需要本地构建 WebUI 时 |
|
||||
| npm | 随 Node.js 提供 | 安装 WebUI 依赖 |
|
||||
| Go | 1.26+ | Build backend |
|
||||
| Node.js | `20.19+` or `22.12+` | Only needed to build WebUI locally |
|
||||
| npm | Bundled with Node.js | Install WebUI dependencies |
|
||||
|
||||
配置来源(任选其一):
|
||||
Config source (choose one):
|
||||
|
||||
- **文件方式**:`config.json`(推荐本地/Docker 使用)
|
||||
- **环境变量方式**:`DS2API_CONFIG_JSON`(推荐 Vercel 使用,支持 JSON 字符串或 Base64 编码,也可以直接写原始 JSON)
|
||||
- **File**: `config.json` (recommended for local/Docker)
|
||||
- **Environment variable**: `DS2API_CONFIG_JSON` (recommended for Vercel; supports raw JSON or Base64)
|
||||
|
||||
统一建议(最优实践):
|
||||
Unified recommendation (best practice):
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
建议把 `config.json` 作为唯一配置源:
|
||||
- 本地运行:直接读 `config.json`
|
||||
- Docker / Vercel:从 `config.json` 生成 `DS2API_CONFIG_JSON`(Base64)注入环境变量
|
||||
Use `config.json` as the single source of truth:
|
||||
- Local run: read `config.json` directly
|
||||
- Docker / Vercel: generate `DS2API_CONFIG_JSON` (Base64) from `config.json` and inject it
|
||||
|
||||
---
|
||||
|
||||
## 一、下载 Release 构建包
|
||||
## 1. Download Release Binaries
|
||||
|
||||
仓库内置 GitHub Actions 工作流:`.github/workflows/release-artifacts.yml`
|
||||
Built-in GitHub Actions workflow: `.github/workflows/release-artifacts.yml`
|
||||
|
||||
- **触发条件**:仅在 Release `published` 时触发(普通 push 不会构建)
|
||||
- **构建产物**:多平台二进制压缩包 + `sha256sums.txt`
|
||||
- **容器镜像发布**:仅发布到 GHCR(`ghcr.io/cjackhwang/ds2api`)
|
||||
- **Trigger**: only on Release `published` (no build on normal push)
|
||||
- **Outputs**: multi-platform binary archives + `sha256sums.txt`
|
||||
- **Container publishing**: GHCR only (`ghcr.io/cjackhwang/ds2api`)
|
||||
|
||||
另外,`.github/workflows/ghcr.yml` 会在 `main` push、`v*` tag push 或手动触发时自动构建并推送多架构 GHCR 镜像,tag 包含分支名、版本 tag、`sha-*`,默认分支额外发布 `latest`。
|
||||
In addition, `.github/workflows/ghcr.yml` automatically builds and pushes multi-arch GHCR images on `main` pushes, `v*` tag pushes, or manual dispatch. Tags include the branch name, version tag, `sha-*`, and `latest` for the default branch.
|
||||
|
||||
| 平台 | 架构 | 文件格式 |
|
||||
| Platform | Architecture | Format |
|
||||
| --- | --- | --- |
|
||||
| Linux | amd64, arm64 | `.tar.gz` |
|
||||
| macOS | amd64, arm64 | `.tar.gz` |
|
||||
| Windows | amd64 | `.zip` |
|
||||
|
||||
每个压缩包包含:
|
||||
Each archive includes:
|
||||
|
||||
- `ds2api` 可执行文件(Windows 为 `ds2api.exe`)
|
||||
- `static/admin/`(WebUI 构建产物)
|
||||
- `config.example.json`、`.env.example`
|
||||
- `README.MD`、`README.en.md`、`LICENSE`
|
||||
- `ds2api` executable (`ds2api.exe` on Windows)
|
||||
- `static/admin/` (built WebUI assets)
|
||||
- `config.example.json`, `.env.example`
|
||||
- `README.MD`, `LICENSE`
|
||||
|
||||
### 使用步骤
|
||||
### Usage
|
||||
|
||||
```bash
|
||||
# 1. 下载对应平台的压缩包
|
||||
# 2. 解压
|
||||
# 1. Download the archive for your platform
|
||||
# 2. Extract
|
||||
tar -xzf ds2api_<tag>_linux_amd64.tar.gz
|
||||
cd ds2api_<tag>_linux_amd64
|
||||
|
||||
# 3. 配置
|
||||
# 3. Configure
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
|
||||
# 4. 启动
|
||||
# 4. Start
|
||||
./ds2api
|
||||
```
|
||||
|
||||
### 维护者发布步骤
|
||||
### Maintainer Release Flow
|
||||
|
||||
1. 在 GitHub 创建并发布 Release(带 tag,如 `vX.Y.Z`)
|
||||
2. 等待 Actions 工作流 `Release Artifacts` 完成
|
||||
3. 在 Release 的 Assets 下载对应平台压缩包
|
||||
1. Create and publish a GitHub Release (with tag, for example `vX.Y.Z`)
|
||||
2. Wait for the `Release Artifacts` workflow to complete
|
||||
3. Download the matching archive from Release Assets
|
||||
|
||||
---
|
||||
|
||||
## 二、Docker / GHCR 部署
|
||||
## 2. Docker / GHCR Deployment
|
||||
|
||||
### 2.1 基本步骤
|
||||
### 2.1 Basic Steps
|
||||
|
||||
```bash
|
||||
# 拉取预编译镜像
|
||||
# Pull prebuilt image
|
||||
docker pull ghcr.io/cjackhwang/ds2api:latest
|
||||
|
||||
# 复制环境变量模板和配置文件
|
||||
# Copy env template and config file
|
||||
cp .env.example .env
|
||||
cp config.example.json config.json
|
||||
|
||||
# 编辑 .env(请改成你的强密码),至少设置:
|
||||
# Edit .env and set at least:
|
||||
# DS2API_ADMIN_KEY=your-admin-key
|
||||
# 如需修改宿主机端口,可额外设置:
|
||||
# Optionally set the host port:
|
||||
# DS2API_HOST_PORT=6011
|
||||
|
||||
# 启动
|
||||
# Start
|
||||
docker-compose up -d
|
||||
|
||||
# 查看日志
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
```
|
||||
|
||||
默认 `docker-compose.yml` 直接使用 `ghcr.io/cjackhwang/ds2api:latest`,并把宿主机 `6011` 映射到容器内的 `5001`。如果你希望直接对外暴露 `5001`,请设置 `DS2API_HOST_PORT=5001`(或者手动调整 `ports` 配置)。
|
||||
The default `docker-compose.yml` directly uses `ghcr.io/cjackhwang/ds2api:latest` and maps host port `6011` to container port `5001`. If you want `5001` exposed directly, set `DS2API_HOST_PORT=5001` (or adjust the `ports` mapping).
|
||||
|
||||
如需固定版本,也可以直接拉取指定 tag:
|
||||
If you want a pinned version instead of `latest`, you can also pull a specific tag directly:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/cjackhwang/ds2api:v3.0.0
|
||||
```
|
||||
|
||||
### 2.2 更新
|
||||
### 2.2 Update
|
||||
|
||||
```bash
|
||||
docker-compose up -d --build
|
||||
```
|
||||
|
||||
### 2.3 Docker 架构说明
|
||||
### 2.3 Docker Architecture
|
||||
|
||||
`Dockerfile` 提供两条构建路径:
|
||||
The `Dockerfile` now provides two image paths:
|
||||
|
||||
1. **本地/开发默认路径(`runtime-from-source`)**:三阶段构建(WebUI 构建 + Go 构建 + 运行阶段)。
|
||||
2. **Release 路径(`runtime-from-dist`)**:发布工作流先生成 tag 命名的 Release 压缩包,再把 Linux 产物复制成 `dist/docker-input/linux_amd64.tar.gz` / `linux_arm64.tar.gz`;Docker 构建阶段直接消费这些输入,不再重复执行 `npm build`/`go build`。
|
||||
1. **Default local/dev path (`runtime-from-source`)**: a three-stage build (WebUI build + Go build + runtime).
|
||||
2. **Release path (`runtime-from-dist`)**: the release workflow first creates tag-named release archives, then copies the Linux bundles to `dist/docker-input/linux_amd64.tar.gz` / `linux_arm64.tar.gz`; Docker consumes those prepared inputs directly, without rerunning `npm build`/`go build`.
|
||||
|
||||
Release 路径可确保 Docker 镜像与 release 压缩包使用同一套产物,减少重复构建带来的差异。
|
||||
The release path keeps Docker images aligned with release archives and reduces duplicate build work.
|
||||
|
||||
容器内启动命令:`/usr/local/bin/ds2api`,默认暴露端口 `5001`。
|
||||
Container entry command: `/usr/local/bin/ds2api`, default exposed port: `5001`.
|
||||
|
||||
### 2.4 开发环境
|
||||
### 2.4 Development Mode
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.dev.yml up
|
||||
```
|
||||
|
||||
开发模式特性:
|
||||
- 源代码挂载(修改即生效)
|
||||
Development features:
|
||||
- Source code mounted (live changes)
|
||||
- `LOG_LEVEL=DEBUG`
|
||||
- 不自动重启
|
||||
- No auto-restart
|
||||
|
||||
### 2.5 健康检查
|
||||
### 2.5 Health Check
|
||||
|
||||
Docker Compose 已配置内置健康检查:
|
||||
Docker Compose includes a built-in health check:
|
||||
|
||||
```yaml
|
||||
healthcheck:
|
||||
@@ -180,248 +179,238 @@ healthcheck:
|
||||
start_period: 10s
|
||||
```
|
||||
|
||||
### 2.6 Docker 常见排查
|
||||
### 2.6 Docker Troubleshooting
|
||||
|
||||
如果容器日志正常但面板打不开,优先检查:
|
||||
If container logs look normal but the admin panel is unreachable, check these first:
|
||||
|
||||
1. **端口是否一致**:`PORT` 改成非 `5001` 时,访问地址也要改成对应端口(如 `http://localhost:8080/admin`)。
|
||||
2. **开发 compose 的 WebUI 静态文件**:`docker-compose.dev.yml` 使用 `go run` 开发镜像,不会在容器内自动安装 Node.js;若仓库里没有 `static/admin`,`/admin` 会返回 404。可先在宿主机构建一次:`./scripts/build-webui.sh`。
|
||||
1. **Port alignment**: when `PORT` is not `5001`, use the same port in your URL (for example `http://localhost:8080/admin`).
|
||||
2. **WebUI assets in dev compose**: `docker-compose.dev.yml` runs `go run` in a dev image and does not auto-install Node.js inside the container; if `static/admin` is missing in your repo, `/admin` will return 404. Build once on host: `./scripts/build-webui.sh`.
|
||||
|
||||
### 2.7 Zeabur 一键部署(Dockerfile)
|
||||
### 2.7 Zeabur One-Click (Dockerfile)
|
||||
|
||||
仓库提供 `zeabur.yaml` 模板,可在 Zeabur 上一键部署:
|
||||
This repo includes a `zeabur.yaml` template for one-click deployment on Zeabur:
|
||||
|
||||
[](https://zeabur.com/templates/L4CFHP)
|
||||
|
||||
部署要点:
|
||||
Notes:
|
||||
|
||||
- **端口**:服务默认监听 `5001`,模板会固定设置 `PORT=5001`。
|
||||
- **配置持久化**:模板挂载卷 `/data`,并设置 `DS2API_CONFIG_PATH=/data/config.json`;在管理台导入配置后,会写入并持久化到该路径。
|
||||
- **构建版本号**:Zeabur / 普通 `docker build` 默认不需要传 `BUILD_VERSION`;镜像会优先使用该构建参数,未提供时自动回退到仓库根目录的 `VERSION` 文件。
|
||||
- **首次登录**:部署完成后访问 `/admin`,使用 Zeabur 环境变量/模板指引中的 `DS2API_ADMIN_KEY` 登录(建议首次登录后自行更换为强密码)。
|
||||
- **Port**: DS2API listens on `5001` by default; the template sets `PORT=5001`.
|
||||
- **Persistent config**: the template mounts `/data` and sets `DS2API_CONFIG_PATH=/data/config.json`. After importing config in Admin UI, it will be written and persisted to this path.
|
||||
- **Build version**: Zeabur / regular `docker build` does not require `BUILD_VERSION` by default. The image prefers that build arg when provided, and automatically falls back to the repo-root `VERSION` file when it is absent.
|
||||
- **First login**: after deployment, open `/admin` and login with `DS2API_ADMIN_KEY` shown in Zeabur env/template instructions (recommended: rotate to a strong secret after first login).
|
||||
|
||||
---
|
||||
|
||||
## 三、Vercel 部署
|
||||
## 3. Vercel Deployment
|
||||
|
||||
### 3.1 部署步骤
|
||||
### 3.1 Steps
|
||||
|
||||
1. **Fork 仓库**到你的 GitHub 账号
|
||||
2. **在 Vercel 上导入项目**
|
||||
3. **配置环境变量**(最少只需设置以下一项):
|
||||
1. **Fork** the repo to your GitHub account
|
||||
2. **Import** the project on Vercel
|
||||
3. **Set environment variables** (minimum required: one variable):
|
||||
|
||||
| 变量 | 说明 |
|
||||
| Variable | Description |
|
||||
| --- | --- |
|
||||
| `DS2API_ADMIN_KEY` | 管理密钥(必填) |
|
||||
| `DS2API_CONFIG_JSON` | 配置内容,JSON 字符串或 Base64 编码(可选,建议) |
|
||||
| `DS2API_ADMIN_KEY` | Admin key (required) |
|
||||
| `DS2API_CONFIG_JSON` | Config content, raw JSON or Base64 (optional, recommended) |
|
||||
|
||||
4. **部署**
|
||||
4. **Deploy**
|
||||
|
||||
### 3.1.1 推荐填写方式(避免 `DS2API_CONFIG_JSON` 填错)
|
||||
### 3.1.1 Recommended Input (avoid `DS2API_CONFIG_JSON` mistakes)
|
||||
|
||||
如果你想先完成一键部署,也可以先不填 `DS2API_CONFIG_JSON`,部署后进入 `/admin` 导入配置,再在「Vercel 同步」里写回环境变量。
|
||||
If you prefer faster one-click bootstrap, you can leave `DS2API_CONFIG_JSON` empty first, then open `/admin` after deployment, import config, and sync it back to Vercel env vars from the "Vercel Sync" page.
|
||||
|
||||
建议先在仓库目录复制示例配置,再按实际账号填写:
|
||||
Recommended: in repo root, copy the template first and fill your real accounts:
|
||||
|
||||
```bash
|
||||
cp config.example.json config.json
|
||||
# 编辑 config.json
|
||||
# Edit config.json
|
||||
```
|
||||
|
||||
不要在 Vercel 面板里手写复杂 JSON,建议本地生成 Base64 后粘贴:
|
||||
Do not hand-edit large JSON directly in Vercel. Generate Base64 locally and paste it:
|
||||
|
||||
```bash
|
||||
# 在仓库根目录执行
|
||||
# Run in repo root
|
||||
DS2API_CONFIG_JSON="$(base64 < config.json | tr -d '\n')"
|
||||
echo "$DS2API_CONFIG_JSON"
|
||||
```
|
||||
|
||||
如果你选择在部署前就预置配置,请在 Vercel Project Settings -> Environment Variables 配置:
|
||||
If you choose to preconfigure before first deploy, set these vars in Vercel Project Settings -> Environment Variables:
|
||||
|
||||
```text
|
||||
DS2API_ADMIN_KEY=请替换为强密码
|
||||
DS2API_CONFIG_JSON=上一步生成的一整行 Base64
|
||||
DS2API_ADMIN_KEY=replace-with-a-strong-secret
|
||||
DS2API_CONFIG_JSON=<the single-line Base64 output above>
|
||||
```
|
||||
|
||||
可选但推荐(用于 WebUI 一键同步 Vercel 配置):
|
||||
Optional but recommended (for WebUI one-click Vercel sync):
|
||||
|
||||
```text
|
||||
VERCEL_TOKEN=你的 Vercel Token
|
||||
VERCEL_TOKEN=your-vercel-token
|
||||
VERCEL_PROJECT_ID=prj_xxxxxxxxxxxx
|
||||
VERCEL_TEAM_ID=team_xxxxxxxxxxxx # 个人账号可留空
|
||||
VERCEL_TEAM_ID=team_xxxxxxxxxxxx # optional for personal accounts
|
||||
```
|
||||
|
||||
### 3.2 可选环境变量
|
||||
### 3.2 Optional Environment Variables
|
||||
|
||||
| 变量 | 说明 | 默认值 |
|
||||
| Variable | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `DS2API_ACCOUNT_MAX_INFLIGHT` | 每账号并发上限 | `2` |
|
||||
| `DS2API_ACCOUNT_MAX_QUEUE` | 等待队列上限 | `recommended_concurrency` |
|
||||
| `DS2API_GLOBAL_MAX_INFLIGHT` | 全局并发上限 | `recommended_concurrency` |
|
||||
| `DS2API_ENV_WRITEBACK` | 检测到 `DS2API_CONFIG_JSON` 时自动写入 `DS2API_CONFIG_PATH`,并在成功后转为文件模式(`1/true/yes/on`) | 关闭 |
|
||||
| `DS2API_VERCEL_INTERNAL_SECRET` | 混合流式内部鉴权 | 回退用 `DS2API_ADMIN_KEY` |
|
||||
| `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | 流式 lease TTL | `900` |
|
||||
| `DS2API_RAW_STREAM_SAMPLE_ROOT` | raw stream 样本保存/读取根目录 | `tests/raw_stream_samples` |
|
||||
| `VERCEL_TOKEN` | Vercel 同步 token | — |
|
||||
| `VERCEL_PROJECT_ID` | Vercel 项目 ID | — |
|
||||
| `VERCEL_TEAM_ID` | Vercel 团队 ID | — |
|
||||
| `DS2API_VERCEL_PROTECTION_BYPASS` | 部署保护绕过密钥(内部 Node→Go 调用) | — |
|
||||
| `DS2API_ACCOUNT_MAX_INFLIGHT` | Per-account inflight limit | `2` |
|
||||
| `DS2API_ACCOUNT_MAX_QUEUE` | Waiting queue limit | `recommended_concurrency` |
|
||||
| `DS2API_GLOBAL_MAX_INFLIGHT` | Global inflight limit | `recommended_concurrency` |
|
||||
| `DS2API_ENV_WRITEBACK` | When `DS2API_CONFIG_JSON` is present, auto-write to `DS2API_CONFIG_PATH` and switch to file-backed mode after success (`1/true/yes/on`) | Disabled |
|
||||
| `DS2API_VERCEL_INTERNAL_SECRET` | Hybrid streaming internal auth | Falls back to `DS2API_ADMIN_KEY` |
|
||||
| `DS2API_VERCEL_STREAM_LEASE_TTL_SECONDS` | Stream lease TTL | `900` |
|
||||
| `DS2API_RAW_STREAM_SAMPLE_ROOT` | Raw stream sample root for saving/reading samples | `tests/raw_stream_samples` |
|
||||
| `VERCEL_TOKEN` | Vercel sync token | — |
|
||||
| `VERCEL_PROJECT_ID` | Vercel project ID | — |
|
||||
| `VERCEL_TEAM_ID` | Vercel team ID | — |
|
||||
| `DS2API_VERCEL_PROTECTION_BYPASS` | Deployment protection bypass for internal Node→Go calls | — |
|
||||
|
||||
### 3.3 运行时行为配置(通过 Admin API 设置)
|
||||
|
||||
部分运行时行为无法通过环境变量直接配置,需要在部署后通过 Admin API 设置,例如:
|
||||
|
||||
- **自动删除会话模式** (`auto_delete.mode`):支持 `none` / `single` / `all`,默认为 `none`。可通过 `PUT /admin/settings` 更新。
|
||||
- **每账号并发上限** (`account_max_inflight`):环境变量已支持,但也可通过 Admin API 热更新。
|
||||
- **全局并发上限** (`global_max_inflight`):同上。
|
||||
|
||||
详细说明参见 [API.md](../API.md#admin-接口) 中 `/admin/settings` 部分。
|
||||
|
||||
### 3.4 Vercel 架构说明
|
||||
### 3.4 Vercel Architecture
|
||||
|
||||
```text
|
||||
请求 ─────┐
|
||||
│
|
||||
▼
|
||||
vercel.json 路由规则
|
||||
│
|
||||
┌─────┴─────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
api/index.go api/chat-stream.js
|
||||
(Go Runtime) (Node Runtime)
|
||||
Request ──────┐
|
||||
│
|
||||
▼
|
||||
vercel.json routing
|
||||
│
|
||||
┌─────┴─────┐
|
||||
│ │
|
||||
▼ ▼
|
||||
api/index.go api/chat-stream.js
|
||||
(Go Runtime) (Node Runtime)
|
||||
```
|
||||
|
||||
- **入口文件**:`api/index.go`(Serverless Go)
|
||||
- **流式入口**:`api/chat-stream.js`(Node Runtime,保证实时 SSE)
|
||||
- **路由重写**:`vercel.json`
|
||||
- **构建命令**:`npm ci --prefix webui && npm run build --prefix webui`(自动执行)
|
||||
- **Go entry**: `api/index.go` (Serverless Go)
|
||||
- **Stream entry**: `api/chat-stream.js` (Node Runtime for real-time SSE)
|
||||
- **Routing**: `vercel.json`
|
||||
- **Build command**: `npm ci --prefix webui && npm run build --prefix webui` (automatic)
|
||||
|
||||
#### 流式处理链路
|
||||
#### Streaming Pipeline
|
||||
|
||||
由于 Vercel Go Runtime 存在平台层响应缓冲,本项目在 Vercel 上采用"**Go prepare + Node stream**"的混合链路:
|
||||
Vercel Go Runtime applies platform-level response buffering, so this project uses a hybrid "**Go prepare + Node stream**" path on Vercel:
|
||||
|
||||
1. `api/chat-stream.js` 收到 `/v1/chat/completions` 请求
|
||||
2. Node 调用 Go 内部 prepare 接口(`?__stream_prepare=1`),获取会话 ID、PoW、token 等
|
||||
3. Go prepare 创建 stream lease,锁定账号
|
||||
4. Node 直连 DeepSeek 上游,实时流式转发 SSE 给客户端(含 OpenAI chunk 封装与 tools 防泄漏筛分)
|
||||
5. 流结束后 Node 调用 Go release 接口(`?__stream_release=1`),释放账号
|
||||
1. `api/chat-stream.js` receives `/v1/chat/completions` request
|
||||
2. Node calls Go internal prepare endpoint (`?__stream_prepare=1`) for session ID, PoW, token
|
||||
3. Go prepare creates a stream lease, locking the account
|
||||
4. Node connects directly to DeepSeek upstream, relays SSE in real-time to client (including OpenAI chunk framing and tools anti-leak sieve)
|
||||
5. After stream ends, Node calls Go release endpoint (`?__stream_release=1`) to free the account
|
||||
|
||||
> 该适配**仅在 Vercel 环境生效**;本地与 Docker 仍走纯 Go 链路。
|
||||
> This adaptation is **Vercel-only**; local and Docker remain pure Go.
|
||||
|
||||
#### 非流式回退与 Tool Call 处理
|
||||
#### Non-Stream Fallback and Tool Call Handling
|
||||
|
||||
- `api/chat-stream.js` 仅对非流式请求回退到 Go 入口(`?__go=1`)
|
||||
- 流式请求(包括带 `tools`)走 Node 路径,并执行与 Go 对齐的 tool-call 防泄漏处理
|
||||
- Node 流式路径同时对齐 Go 的终结态语义:空可见输出会返回同形状错误 SSE,空 `content_filter` 会返回 `content_filter` 错误
|
||||
- WebUI 的"非流式测试"直接请求 `?__go=1`,避免 Node 中转造成长请求超时
|
||||
- `api/chat-stream.js` falls back to Go entry (`?__go=1`) for non-stream requests only
|
||||
- Streaming requests (including requests with `tools`) stay on the Node path and use Go-aligned tool-call anti-leak handling
|
||||
- The Node stream path also mirrors Go finalization semantics: empty visible output returns the same shaped error SSE, and empty `content_filter` returns a `content_filter` error
|
||||
- WebUI non-stream test calls `?__go=1` directly to avoid Node hop timeout on long requests
|
||||
|
||||
#### 函数时长
|
||||
#### Function Duration
|
||||
|
||||
`vercel.json` 已将 `api/chat-stream.js` 与 `api/index.go` 的 `maxDuration` 设为 `300`(受 Vercel 套餐上限约束)。
|
||||
`vercel.json` sets `maxDuration: 300` for both `api/chat-stream.js` and `api/index.go` (subject to your Vercel plan limits).
|
||||
|
||||
### 3.5 Vercel 常见报错排查
|
||||
### 3.5 Vercel Troubleshooting
|
||||
|
||||
#### Go 构建失败
|
||||
#### Go Build Failure
|
||||
|
||||
```text
|
||||
Error: Command failed: go build -ldflags -s -w -o .../bootstrap ...
|
||||
```
|
||||
|
||||
**原因**:Vercel 项目的 Go 构建参数配置不正确(`-ldflags` 没有作为一个整体字符串传递)。
|
||||
**Cause**: Invalid Go build flag settings in Vercel (`-ldflags` not passed as a single argument).
|
||||
|
||||
**解决**:
|
||||
**Fix**:
|
||||
|
||||
1. 进入 Vercel Project Settings → Build and Development Settings
|
||||
2. **清空**自定义 Go Build Flags / Build Command(推荐)
|
||||
3. 若必须设置 ldflags,使用 `-ldflags="-s -w"`(保证它是一个参数)
|
||||
4. 确认仓库 `go.mod` 为受支持版本(当前为 `go 1.26.0`)
|
||||
5. 重新部署(建议清缓存后 Redeploy)
|
||||
1. Open Vercel Project Settings → Build and Development Settings
|
||||
2. **Clear** custom Go Build Flags / Build Command (recommended)
|
||||
3. If ldflags must be used, set `-ldflags="-s -w"` (ensure it's one argument)
|
||||
4. Verify `go.mod` uses a supported version (currently `go 1.26.0`)
|
||||
5. Redeploy (recommended: clear cache)
|
||||
|
||||
#### Internal 包导入错误
|
||||
#### Internal Package Import Error
|
||||
|
||||
```text
|
||||
use of internal package ds2api/internal/server not allowed
|
||||
```
|
||||
|
||||
**原因**:Vercel Go 入口文件直接 `import internal/...`。
|
||||
**Cause**: Vercel Go entrypoint directly imports `internal/...`.
|
||||
|
||||
**解决**:当前仓库已通过公开桥接包 `app` 解决:`api/index.go` → `ds2api/app` → `internal/server`。
|
||||
**Fix**: This repo uses a public bridge package: `api/index.go` → `ds2api/app` → `internal/server`.
|
||||
|
||||
#### 输出目录错误
|
||||
#### Output Directory Error
|
||||
|
||||
```text
|
||||
No Output Directory named "public" found after the Build completed.
|
||||
```
|
||||
|
||||
**解决**:当前仓库使用 `static` 作为输出目录(`vercel.json` 中 `"outputDirectory": "static"`)。若你在项目设置里手动改过 Output Directory,请设为 `static` 或清空让仓库配置生效。
|
||||
**Fix**: This repo uses `static` as output directory (`"outputDirectory": "static"` in `vercel.json`). If you manually changed Output Directory in Project Settings, set it to `static` or clear it.
|
||||
|
||||
#### 部署保护拦截
|
||||
#### Deployment Protection Blocking
|
||||
|
||||
如果接口返回 Vercel HTML 页面 `Authentication Required`:
|
||||
If API responses return Vercel HTML `Authentication Required`:
|
||||
|
||||
- **方案 A**:关闭该部署/环境的 Deployment Protection(推荐用于公开 API)
|
||||
- **方案 B**:请求中添加 `x-vercel-protection-bypass` 头
|
||||
- **方案 C**:设置 `VERCEL_AUTOMATION_BYPASS_SECRET`(或 `DS2API_VERCEL_PROTECTION_BYPASS`),仅影响内部 Node→Go 调用
|
||||
- **Option A**: Disable Deployment Protection for that environment (recommended for public APIs)
|
||||
- **Option B**: Add `x-vercel-protection-bypass` header to requests
|
||||
- **Option C**: Set `VERCEL_AUTOMATION_BYPASS_SECRET` (or `DS2API_VERCEL_PROTECTION_BYPASS`) for internal Node→Go calls
|
||||
|
||||
### 3.6 仓库不提交构建产物
|
||||
### 3.6 Build Artifacts Not Committed
|
||||
|
||||
- `static/admin` 目录不在 Git 中
|
||||
- Vercel / Docker 构建阶段自动生成 WebUI 静态文件
|
||||
- `static/admin` directory is not in Git
|
||||
- Vercel / Docker automatically generate WebUI assets during build
|
||||
|
||||
---
|
||||
|
||||
## 四、本地源码运行
|
||||
## 4. Local Run from Source
|
||||
|
||||
### 4.1 基本步骤
|
||||
### 4.1 Basic Steps
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
# Clone
|
||||
git clone https://github.com/CJackHwang/ds2api.git
|
||||
cd ds2api
|
||||
|
||||
# 复制并编辑配置
|
||||
# Copy and edit config
|
||||
cp config.example.json config.json
|
||||
# 使用你喜欢的编辑器打开 config.json,填入:
|
||||
# - keys: 你的 API 访问密钥
|
||||
# - accounts: DeepSeek 账号(email 或 mobile + password)
|
||||
# Open config.json and fill in:
|
||||
# - keys: your API access keys
|
||||
# - accounts: DeepSeek accounts (email or mobile + password)
|
||||
|
||||
# 启动服务
|
||||
# Start
|
||||
go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
默认本地访问地址是 `http://127.0.0.1:5001`;服务实际绑定 `0.0.0.0:5001`,可通过 `PORT` 环境变量覆盖。
|
||||
Default local access URL: `http://127.0.0.1:5001`; the server actually binds to `0.0.0.0:5001` (override with `PORT`).
|
||||
|
||||
### 4.2 WebUI 构建
|
||||
### 4.2 WebUI Build
|
||||
|
||||
本地首次启动时,若 `static/admin/` 不存在,服务会自动尝试构建 WebUI(需要 Node.js/npm;缺依赖时会先执行 `npm ci`,再执行 `npm run build -- --outDir static/admin --emptyOutDir`)。
|
||||
On first local startup, if `static/admin/` is missing, DS2API will automatically attempt to build the WebUI (requires Node.js/npm; when dependencies are missing it runs `npm ci` first, then `npm run build -- --outDir static/admin --emptyOutDir`).
|
||||
|
||||
你也可以手动构建:
|
||||
Manual build:
|
||||
|
||||
```bash
|
||||
./scripts/build-webui.sh
|
||||
```
|
||||
|
||||
或手动执行:
|
||||
Or step by step:
|
||||
|
||||
```bash
|
||||
cd webui
|
||||
npm install
|
||||
npm run build
|
||||
# 产物输出到 static/admin/
|
||||
# Output goes to static/admin/
|
||||
```
|
||||
|
||||
通过环境变量控制自动构建行为:
|
||||
Control auto-build via environment variable:
|
||||
|
||||
```bash
|
||||
# 强制关闭自动构建
|
||||
# Disable auto-build
|
||||
DS2API_AUTO_BUILD_WEBUI=false go run ./cmd/ds2api
|
||||
|
||||
# 强制开启自动构建
|
||||
# Force enable auto-build
|
||||
DS2API_AUTO_BUILD_WEBUI=true go run ./cmd/ds2api
|
||||
```
|
||||
|
||||
### 4.3 编译为二进制文件
|
||||
### 4.3 Compile to Binary
|
||||
|
||||
```bash
|
||||
go build -o ds2api ./cmd/ds2api
|
||||
@@ -430,9 +419,9 @@ go build -o ds2api ./cmd/ds2api
|
||||
|
||||
---
|
||||
|
||||
## 五、反向代理(Nginx)
|
||||
## 5. Reverse Proxy (Nginx)
|
||||
|
||||
如果在 Nginx 后部署,**必须关闭缓冲**以保证 SSE 流式响应正常工作:
|
||||
When deploying behind Nginx, **you must disable buffering** for SSE streaming to work:
|
||||
|
||||
```nginx
|
||||
location / {
|
||||
@@ -446,7 +435,7 @@ location / {
|
||||
}
|
||||
```
|
||||
|
||||
如果需要 HTTPS,可以在 Nginx 层配置 SSL 证书:
|
||||
For HTTPS, add SSL at the Nginx layer:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
@@ -474,18 +463,18 @@ server {
|
||||
|
||||
---
|
||||
|
||||
## 六、Linux systemd 服务化
|
||||
## 6. Linux systemd Service
|
||||
|
||||
### 6.1 安装
|
||||
### 6.1 Installation
|
||||
|
||||
```bash
|
||||
# 将编译好的二进制文件和相关文件复制到目标目录
|
||||
# Copy compiled binary and related files to target directory
|
||||
sudo mkdir -p /opt/ds2api
|
||||
sudo cp ds2api config.json /opt/ds2api/
|
||||
sudo cp -r static/admin /opt/ds2api/static/admin
|
||||
```
|
||||
|
||||
### 6.2 创建 systemd 服务文件
|
||||
### 6.2 Create systemd Service File
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/ds2api.service
|
||||
@@ -508,55 +497,55 @@ RestartSec=5
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### 6.3 常用命令
|
||||
### 6.3 Common Commands
|
||||
|
||||
```bash
|
||||
# 加载服务配置
|
||||
# Reload service config
|
||||
sudo systemctl daemon-reload
|
||||
|
||||
# 设置开机自启
|
||||
# Enable on boot
|
||||
sudo systemctl enable ds2api
|
||||
|
||||
# 启动服务
|
||||
# Start
|
||||
sudo systemctl start ds2api
|
||||
|
||||
# 查看状态
|
||||
# Check status
|
||||
sudo systemctl status ds2api
|
||||
|
||||
# 查看日志
|
||||
# View logs
|
||||
sudo journalctl -u ds2api -f
|
||||
|
||||
# 重启服务
|
||||
# Restart
|
||||
sudo systemctl restart ds2api
|
||||
|
||||
# 停止服务
|
||||
# Stop
|
||||
sudo systemctl stop ds2api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、部署后检查
|
||||
## 7. Post-Deploy Checks
|
||||
|
||||
无论使用哪种部署方式,启动后建议依次检查:
|
||||
After deployment (any method), verify in order:
|
||||
|
||||
```bash
|
||||
# 1. 存活探针
|
||||
# 1. Liveness probe
|
||||
curl -s http://127.0.0.1:5001/healthz
|
||||
# 预期: {"status":"ok"}
|
||||
# Expected: {"status":"ok"}
|
||||
|
||||
# 2. 就绪探针
|
||||
# 2. Readiness probe
|
||||
curl -s http://127.0.0.1:5001/readyz
|
||||
# 预期: {"status":"ready"}
|
||||
# Expected: {"status":"ready"}
|
||||
|
||||
# 3. 模型列表
|
||||
# 3. Model list
|
||||
curl -s http://127.0.0.1:5001/v1/models
|
||||
# 预期: {"object":"list","data":[...]}
|
||||
# Expected: {"object":"list","data":[...]}
|
||||
|
||||
# 4. 管理台页面(如果已构建 WebUI)
|
||||
# 4. Admin panel (if WebUI is built)
|
||||
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:5001/admin
|
||||
# 预期: 200
|
||||
# Expected: 200
|
||||
|
||||
# 5. 测试 API 调用
|
||||
# 5. Test API call
|
||||
curl http://127.0.0.1:5001/v1/chat/completions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-H "Content-Type: application/json" \
|
||||
@@ -565,15 +554,15 @@ curl http://127.0.0.1:5001/v1/chat/completions \
|
||||
|
||||
---
|
||||
|
||||
## 八、发布前进行本地回归
|
||||
## 8. Pre-Release Local Regression
|
||||
|
||||
建议在发布前执行完整的端到端测试集(使用真实账号):
|
||||
Run the full live testsuite before release (real account tests):
|
||||
|
||||
```bash
|
||||
./tests/scripts/run-live.sh
|
||||
```
|
||||
|
||||
可自定义参数:
|
||||
With custom flags:
|
||||
|
||||
```bash
|
||||
go run ./cmd/ds2api-tests \
|
||||
@@ -584,11 +573,11 @@ go run ./cmd/ds2api-tests \
|
||||
--retries 2
|
||||
```
|
||||
|
||||
测试集自动执行内容:
|
||||
The testsuite automatically performs:
|
||||
|
||||
- ✅ 语法/构建/单测 preflight
|
||||
- ✅ 隔离副本配置启动服务(不污染原始 `config.json`)
|
||||
- ✅ 真实调用场景验证(OpenAI/Claude/Admin/并发/toolcall/流式)
|
||||
- ✅ 全量请求与响应日志落盘(用于故障复盘)
|
||||
- ✅ Preflight checks (syntax/build/unit tests)
|
||||
- ✅ Isolated config copy startup (no mutation to your original `config.json`)
|
||||
- ✅ Live scenario verification (OpenAI/Claude/Admin/concurrency/toolcall/streaming)
|
||||
- ✅ Full request/response artifact logging for debugging
|
||||
|
||||
详细测试集说明参阅 [TESTING.md](TESTING.md)。PR 前的固定本地门禁以 [TESTING.md](TESTING.md#pr-门禁--pr-gates) 为准。
|
||||
For detailed testsuite documentation, see [TESTING.md](TESTING.md). The fixed local PR gates are listed in [TESTING.md](TESTING.md#pr-门禁--pr-gates).
|
||||
|
||||
+7
-7
@@ -23,7 +23,7 @@
|
||||
### 文档维护约定
|
||||
|
||||
- 文档更新必须以实际代码实现为依据:总路由装配看 `internal/server/router.go`,协议/resource 路由看 `internal/httpapi/*/**/routes.go` 与 `internal/httpapi/admin/handler.go`,配置默认值看 `internal/config/*`,模型/alias 看 `internal/config/models.go`,prompt 兼容链路看 `docs/prompt-compatibility.md` 列出的代码入口。
|
||||
- `README.MD` / `README.en.md`:面向首次接触用户,保留“是什么 + 怎么快速跑起来”。
|
||||
- `README.MD`:面向首次接触用户,保留“是什么 + 怎么快速跑起来”。
|
||||
- `docs/ARCHITECTURE*.md`:面向开发者,集中维护项目结构、模块职责与调用链。
|
||||
- `API*.md`:面向客户端接入者,聚焦接口行为、鉴权和示例。
|
||||
- `docs/prompt-compatibility.md`:面向维护者,集中维护“API -> 网页对话纯文本上下文”的统一兼容语义;相关行为修改时必须同步更新。
|
||||
@@ -37,12 +37,12 @@ To reduce maintenance drift, docs are split into an “entry doc + topical docs
|
||||
|
||||
Recommended reading order:
|
||||
|
||||
1. [Project overview (README)](../README.en.md)
|
||||
2. [Architecture and project layout](./ARCHITECTURE.en.md)
|
||||
3. [API reference](../API.en.md)
|
||||
4. [Deployment guide](./DEPLOY.en.md)
|
||||
1. [Project overview (README)](../README.MD)
|
||||
2. [Architecture and project layout](./ARCHITECTURE.md)
|
||||
3. [API reference](../API.md)
|
||||
4. [Deployment guide](./DEPLOY.md)
|
||||
5. [Testing guide](./TESTING.md)
|
||||
6. [Contributing guide](./CONTRIBUTING.en.md)
|
||||
6. [Contributing guide](./CONTRIBUTING.md)
|
||||
|
||||
### Topical docs
|
||||
|
||||
@@ -54,7 +54,7 @@ Recommended reading order:
|
||||
### Maintenance conventions
|
||||
|
||||
- Documentation updates must be grounded in the actual implementation: root routing lives in `internal/server/router.go`, protocol/resource routes live in `internal/httpapi/*/**/routes.go` and `internal/httpapi/admin/handler.go`, config defaults in `internal/config/*`, models/aliases in `internal/config/models.go`, and the prompt compatibility pipeline in the code entrypoints listed by `docs/prompt-compatibility.md`.
|
||||
- `README.MD` / `README.en.md`: onboarding-oriented (“what + quick start”).
|
||||
- `README.MD`: onboarding-oriented (“what + quick start”).
|
||||
- `docs/ARCHITECTURE*.md`: developer-oriented source of truth for module boundaries and execution flow.
|
||||
- `API*.md`: integration-oriented behavior/contracts.
|
||||
- `docs/prompt-compatibility.md`: maintainer-oriented source of truth for the “API -> pure-text web-chat context” compatibility flow; update it whenever related behavior changes.
|
||||
|
||||
@@ -397,7 +397,7 @@ history split 现在全局强制开启;旧配置中的 `history_split.enabled=
|
||||
如果外部接口行为也变了,还应同步检查:
|
||||
|
||||
- [API.md](../API.md)
|
||||
- [API.en.md](../API.en.md)
|
||||
- [API.md](../API.md)
|
||||
- [docs/toolcall-semantics.md](./toolcall-semantics.md)
|
||||
|
||||
原则是:
|
||||
|
||||
Reference in New Issue
Block a user