Skip to content

Repository files navigation

qwen2api

自建 Qwen(chat.qwen.ai)账号池网关:自研打码注册(Python Worker) + Fastify/TS 协议转换与账户体系。

对外协议目标:

  • OpenAI Chat Completions:POST /v1/chat/completions(含 stream SSE)
  • OpenAI Responses:POST /v1/responses,包含非流式响应和标准 typed SSE 事件
  • GET /v1/models:实时同步 Qwen Web 当前模型名称、能力和思考模式

内部把请求转到 Qwen Web Chat API(Bearer JWT),并做:

  • 账号入库(SQLite)
  • 保活 / 自动重新授权(并发与冷却可配)
  • 会话粘性(X-Session-Id / user)
  • 会话上下文压缩(gzip)
  • 账号轮询(sticky_lru / round_robin / random)
  • 单账号串行请求队列、最小请求间隔和一次有界 429 退避重试
  • 图片、音频、视频和文档上传到 Qwen 文件接口

架构

Client (OpenAI SDK)
    │  /v1/chat/completions
    ▼
Fastify API (Node/TS)  ──账号池/粘性/保活/reauth──► SQLite
    │
    ├─ Qwen Web API (chat.qwen.ai)  协议转换
    │
    └─ Register Worker (Python)  自研滑块注册(不付费打码)

本地开发

1. Python Worker(打码注册)

pip install -r requirements.txt
playwright install chromium
# 本机调试可用有头:HEADLESS=0
uvicorn qwen_register.worker_app:app --host 0.0.0.0 --port 8091

2. Fastify API

cd apps/api
npm install
npm run dev

3. 注册并入库

curl -X POST http://127.0.0.1:8080/v1/accounts/register \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"max_attempts\":5,\"headless\":false}"

4. OpenAI 兼容调用

curl http://127.0.0.1:8080/v1/chat/completions \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Session-Id: my-session-1" \
  -d '{
    "model": "qwen3.7-plus",
    "messages": [{"role":"user","content":"你好"}],
    "stream": false,
    "reasoning_effort": "high",
    "web_search": true
  }'

Python:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8080/v1", api_key="your-key")
print(client.chat.completions.create(
    model="qwen3.7-plus",
    messages=[{"role":"user","content":"hi"}],
    reasoning_effort="high",
    extra_body={"web_search": True},
))

模型同步与能力信息

GET /v1/models 每次请求都会通过账号池查询 Qwen 上游 /api/v2/models/。网关会解析上游当前的 data.data 结构,不缓存、不补充静态模型名。上游返回空列表时原样返回空列表;上游查询失败时明确返回错误,避免客户端把过期模型误认为仍然可用。

每个模型除 OpenAI 标准字段外,还会返回以下扩展字段:

字段 含义
name Qwen 页面展示名称,例如 Qwen3.7-Plus
capabilities 上游能力开关,例如 thinking、search、vision
context_window 上游声明的最大上下文长度
max_output_tokens 上游声明的生成或摘要上限
thinking_modes 可用模式,取值为 Auto、Thinking、Fast
default_thinking_mode 根据模型元数据推导的默认模式
auto_search 模型是否声明自动搜索或搜索能力
qwen Qwen 原生思考能力等级、思考格式、模态和聊天类型

客户端应先调用 /v1/models,再使用返回对象的 id 发起生成请求。上游成功返回模型时,网关不会额外注入 QWEN_DEFAULT_MODEL。

思考模式

Chat Completions 和 Responses 都支持以下输入方式:

输入 Qwen 上游结果
reasoning_effort: "none" 或 "minimal" thinking_mode: "Fast"
reasoning_effort: "low"、"medium"、"high" 或 "xhigh" thinking_mode: "Thinking"
reasoning_effort: "auto" thinking_mode: "Auto"
reasoning: {"effort":"high"} thinking_mode: "Thinking"
thinking_mode: "Auto"、"Thinking" 或 "Fast" 直接使用指定模式
enable_thinking: true thinking_mode: "Thinking"
enable_thinking: false thinking_mode: "Fast"
thinking_budget: 4096 写入 Qwen feature_config.thinking_budget

没有请求级参数时使用 QWEN_DEFAULT_THINKING_MODE,默认值为 Auto。所有用户消息都会带上 Qwen 当前前端使用的完整配置:thinking_enabled、output_schema、research_mode、auto_thinking、thinking_mode、thinking_format 和 auto_search。

网页搜索

网页搜索默认开启。以下任一请求级字段可以显式控制它:

{
  "web_search": false
}
{
  "enable_search": false
}
{
  "search": false
}

Responses API 或 Chat Completions 请求中出现 {"type":"web_search"}、{"type":"web_search_preview"} 或 {"type":"search"} 工具时,也会启用上游 feature_config.auto_search。请求没有显式设置时使用 QWEN_DEFAULT_WEB_SEARCH,默认值为 1。

多模态输入

网关先调用 Qwen /api/v2/files/getstsToken,再使用临时 STS 凭据把文件上传到 Qwen 指定的 OSS 路径。文档会继续调用 /api/v2/files/parse 并轮询 /api/v2/files/parse/status;完成后才把 Qwen 文件对象放入聊天消息。

支持的 OpenAI 输入块如下:

协议 输入块
Chat Completions text、image_url、input_audio、video_url、file
Responses input_text、input_image、input_audio、input_video、input_file

文件来源可以是 HTTPS URL、data: URL,或相应字段中的 Base64 数据。远程 URL 会解析 DNS,并在每次 HTTP 重定向后重新检查目标地址;回环、链路本地、私网、CGNAT 和带 URL 凭据的地址会被拒绝。默认单文件上限为 100 MiB,请求体上限为 140 MiB。Qwen 模型是否接受具体模态应以 /v1/models 返回的 capabilities 和 qwen.modality 为准。

当前没有实现 OpenAI Files API,因此仅给出 file_id、而不提供 URL 或数据的输入会返回 HTTP 400,不会静默丢弃附件。

会话粘连、上下文缓存与轮询

客户端应在连续请求中保持同一个 X-Session-Id。未提供该请求头时,网关依次使用 user 的 SHA-256 摘要或新 UUID,并在响应头中返回实际会话 ID。

每个会话保存以下状态:

状态 作用
account_id 会话固定使用同一 Qwen 账号
qwen_chat_id 复用 Qwen 上游会话
last_message_id 作为下一轮 Qwen parent_id
context_blob gzip + Base64URL 压缩的最近 40 条消息
expires_at 会话粘连过期时间

当 Qwen 父消息链完整时,网关只发送最新用户消息和 parent_id;父链缺失或账号需要重绑时,网关清空旧 Qwen ID,并用本地压缩上下文重建请求。不同会话按 sticky_lru、round_robin 或 random 选择账号。同一个会话在服务内严格串行,同一个账号也只有一个真正的 Qwen 请求在途;其他请求在内存队列等待。数据库 inflight 只统计已经离开队列并访问 Qwen 的请求,进程启动时会复位异常退出遗留的租约。

协议边界

能够可靠转换的 Chat Completions 参数包括 model、messages、stream、temperature、top_p、max_tokens、max_completion_tokens、stream_options.include_usage、思考扩展字段、网页搜索字段和受支持的多模态内容。

能够可靠转换的 Responses 参数包括 model、input、instructions、stream、temperature、top_p、max_output_tokens、reasoning、网页搜索字段和受支持的多模态内容。流式返回包含 response.created、response.in_progress、response.output_item.added、response.content_part.added、response.output_text.delta、各级 done 事件以及 response.completed;失败使用 response.failed,不发送 Chat Completions 的 [DONE]。

Qwen Web 没有等价能力的参数会返回 HTTP 400,包括函数或自定义工具调用、JSON Schema 结构化输出、音频输出、多个候选结果、stored Responses、previous_response_id、conversation、background mode、logprobs、penalties、prediction、seed 和 stop。这样客户端不会误以为参数已经生效。

管理 API

浏览器管理控制台位于 /admin。它使用独立管理员账号密码和 HttpOnly Cookie 登录,不要求浏览器持有终端 API key。控制台采用与 MiMo2API 一致的运维工作台布局,包含服务概览、账号池、动态模型、注册机、可编辑运行配置和密钥管理。模型页可以立即重新同步上游;运行配置与注册参数经过范围校验后写入 SQLite 并在进程内生效;注册任务在后台串行运行,可轮询进度和停止后续账号。每个新账号在注册开始时生成独立高强度随机密码,结果随账号写入 SQLite 并仅供系统生命周期使用;账号密码和 token 不在管理页面或管理接口回显。

密钥页只管理终端 API key 和系统级密钥。所有密钥默认隐藏,点击对应的小眼睛按钮可显示完整明文。

普通 OpenAI 与账号管理 API 始终要求 Authorization: Bearer <API key>。启动时的 API_KEY 会作为 Bootstrap API key 写入 SQLite;控制台生成的其他 key 同样以明文写入 api_keys 表。撤销后新请求立即返回 HTTP 401。Bootstrap key 由 K8s Secret 管理,不能在控制台撤销。

方法 路径 说明
GET /health 健康检查 + worker
GET /v1/accounts 账号列表
POST /v1/accounts/register 调 Python Worker 注册并入库
POST /v1/accounts/import 手工导入 email/password/token
POST /v1/accounts/:id/reauth 重新授权(signin)
POST /v1/keepalive/run 立即跑一轮保活
GET /v1/models OpenAI models
POST /v1/chat/completions OpenAI chat
POST /v1/responses OpenAI responses 子集

管理员接口还提供 /admin/api/models/refresh、/admin/api/runtime 和 /admin/api/registration/*,分别用于上游模型刷新、SQLite 热配置和后台注册任务。管理员写操作同时要求登录 Cookie 与 CSRF token。

Qwen Baxia 动态请求头

bx-ua、bx-umidtoken 和 bx-v 不是静态配置。Python Worker 保持一个与 Qwen Web 相同 User-Agent 的 Playwright 浏览器,加载 Qwen 当前页面和 AWSC/Baxia 模块。API 每次准备访问 Qwen 上游时,都会把本次目标 URL、HTTP 方法和请求体发送到 Worker;Worker 在页面内发起一条被本地路由立即中止的探测请求,从请求发出瞬间提取本次新生成的风控头及当前前端版本,再交给 API 的真实请求。任何一次生成失败都会使该上游请求明确失败,不会复用旧值或回退到固定环境变量。

关键环境变量

变量 默认 说明
API_KEY 无 启动时写入 SQLite 的 Bootstrap API key;普通 API 没有有效 key 时一律拒绝
ADMIN_USERNAME admin /admin 管理员账号
ADMIN_PASSWORD 无 /admin 管理员密码;生产必须由 Secret 注入
ADMIN_SESSION_TTL_MS 28800000 管理员登录会话有效期
ADMIN_COOKIE_SECURE 1 管理 Cookie 是否只允许 HTTPS;本地 HTTP 调试设为 0
REGISTER_WORKER_URL http://127.0.0.1:8091 打码 Worker
WORKER_TOKEN 空 Worker 共享密钥
REGISTER_HEADLESS 0 注册 Worker 是否使用无头浏览器;容器默认通过 Xvfb 使用有界面模式以降低 WAF 指纹异常
REGISTER_BROWSER camoufox 注册浏览器后端;Camoufox 启动失败时自动回退 Patchright
REGISTER_USE_PROXY 1 注册时是否启用 Worker 代理池,默认开启
REGISTER_MAX_ATTEMPTS 5 单账号内部最大注册尝试次数
REGISTER_BETWEEN_ATTEMPTS_SEC 8 单账号内部重试间隔秒数
REGISTER_BATCH_COUNT 1 后台注册任务默认账号数量
REGISTER_BATCH_INTERVAL_SEC 60 批次中两个账号之间的间隔秒数
KEEPALIVE_INTERVAL_MS 600000 保活周期
KEEPALIVE_CONCURRENCY 3 保活并发
REAUTH_CONCURRENCY 2 重新授权并发
REAUTH_COOLDOWN_MS 60000 单账号 reauth 冷却
ACCOUNT_PICK_STRATEGY sticky_lru 轮询策略
MAX_INFLIGHT_PER_ACCOUNT 1 单账号真正访问 Qwen 的最大在途请求数
QWEN_ACCOUNT_REQUEST_INTERVAL_MS 1000 同一账号两次上游请求之间的最小间隔
QWEN_RATE_LIMIT_RETRY_MS 3000 上游 429 后唯一一次重试前的等待时间
QWEN_MAX_UPLOAD_BYTES 104857600 单个多模态文件最大字节数
BODY_LIMIT_BYTES 146800640 Fastify 请求体最大字节数
SESSION_COMPRESS 1 会话上下文 gzip
AUTO_REAUTH 1 401 自动登录刷新
QWEN_DEFAULT_MODEL qwen3.7-plus 生成请求未指定 model 时使用的默认模型,可在管理台热更新
QWEN_DEFAULT_WEB_SEARCH 1 请求未指定时默认启用网页搜索
QWEN_DEFAULT_THINKING_MODE Auto 请求未指定时的思考模式,可选 Auto、Thinking、Fast

GitHub Actions 与 Oracle K8s

生产环境不在本机构建镜像。.github/workflows/build-and-deploy.yml 在每次 main 推送后完成以下步骤:

  1. 安装 Node 22 依赖,执行单元测试、TypeScript 类型检查、编译和 Python 语法检查。
  2. 使用 GitHub Actions Buildx 构建 linux/arm64 API 和 Worker 镜像。
  3. 推送 speedproxy/qwen2api:api-sha-<commit> 与 worker-sha-<commit>。
  4. 使用只对 GitOps 仓库有写权限的 deploy key 更新 qwen2api/oracle/kustomization.yaml。
  5. ArgoCD 自动同步到 Oracle K8s,并使用 Recreate 保证 SQLite PVC 单写。

源仓库需要三个 Actions Secret:DOCKERHUB_USERNAME、DOCKERHUB_TOKEN、GITOPS_DEPLOY_KEY。K8s 运行时 Secret qwen2api-secrets 和拉取 Secret qwen2api-dockerhub 不进入 Git。生产入口为 https://qwen2api.mnnu.eu.org/v1,长流式直连入口为 https://qwen2api-direct.mnnu.eu.org/v1。

协议转换说明

客户端 (OpenAI) 网关 上游 (Qwen Web)
messages[] 转为 Qwen 2.1 完整消息对象 /api/v2/chat/completions?chat_id=... messages
stream: true SSE 重打包 Qwen SSE / 增量字段
/v1/responses input 转为 messages;返回 Responses typed events 同上 chat 路径
X-Session-Id 粘账号 + qwen chat_id chats/new + 复用 chat_id
图片/音频/视频/文档 STS 上传、文档解析、Qwen files 对象 /api/v2/files/* + OSS
思考参数 统一映射 message.feature_config.thinking_*
搜索参数 默认开启,可按请求关闭 message.feature_config.auto_search

上游字段若随前端版本变化,主要改 apps/api/src/qwenChat.ts。

About

OpenAI-compatible gateway for Qwen Web with multimodal input and session stickiness

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages