Skip to content

Latest commit

 

History

History
389 lines (290 loc) · 18.7 KB

File metadata and controls

389 lines (290 loc) · 18.7 KB

Chat2API

Chat2API Logo

版本 1.4.0 GPL-3.0 许可证 Electron 33+ React 18 TypeScript 5 macOS、Windows 和 Linux

English | 官网 | 文档

Chat2API 是一个跨平台桌面应用和无 Electron 服务端。它将基于 Web 的 AI 服务商账户统一接入本地 OpenAI 兼容网关,配置一次后即可连接 OpenAI SDK、编程代理、桌面客户端和内部工具。

Chat2API 仪表盘

主要功能

  • OpenAI 兼容网关:提供 /v1/chat/completions、/v1/responses、旧版 /v1/completions、模型列表、SSE 流式输出、API Key 认证和多模态消息处理;同时在 /v1beta 下提供 Gemini 兼容的生成和文件接口。
  • 服务商与账户管理:支持一个服务商配置多个账户,验证凭证、映射客户端模型名、固定首选服务商或账户,并按轮询、填充优先或故障转移策略路由。
  • 工具调用与推理兼容:在上游支持的范围内统一函数/自定义工具调用、工具结果续接、推理内容、联网搜索、深度研究和服务商专属思考模式。
  • 长请求控制:提供上下文压缩、请求和流式超时、队列准入、连接保活、有限重试,以及 Qwen 会话和响应恢复。
  • 桌面与服务端部署:在 macOS、Windows、Linux 上使用 Electron 界面,也可以在 Docker 中运行 Koa 代理和浏览器管理端。
  • 运维界面:仪表盘统计、请求日志、模型同步、API Key、代理设置、主题、系统托盘,以及中英文界面。
  • 客户端桥接:支持 Codex CLI Responses 接口。

支持的服务商

当前内置服务商及模型如下:

服务商 认证方式 内置模型
DeepSeek User Token deepseek-v4-flash、deepseek-v4-pro
GLM Refresh Token GLM-5.1
Kimi JWT / Web Token Kimi-K2.6、Kimi-K3
MiniMax JWT MiniMax-M2.7
Mimo 浏览器 Cookie MiMo-V2.5-Pro、MiMo-V2.5、MiMo-V2-Flash
Perplexity Session Cookie Auto
Qwen(国内版) SSO Ticket Qwen3.6、Qwen3.7-Max、Qwen3.5-Flash、Qwen3-Max、Qwen3-Max-Thinking-Preview、Qwen3-Coder
Qwen AI(国际版) JWT,可选 Cookie 和登录凭证 Qwen3.8-Max、Qwen3.8-Max_Fast、Qwen3.8-Max_Auto、Qwen3.8-Max_Thinking、Qwen3.7-Plus、Qwen3.7-Max
Z.ai JWT GLM-5.1、GLM-5-Turbo、GLM-5V-Turbo、GLM-5、GLM-4.7

服务商可用性和模型名称由上游 Web 应用决定,可能随时变化。凭证获取、适配差异和模型映射请查看服务商说明。

安装

下载桌面版本

有可用发行版时,请从 GitHub Releases 下载。源码镜像位于 pyf-feifei/Chat2API:

平台 安装包
macOS Apple Silicon Chat2API-<version>-mac-arm64.dmg
macOS Intel Chat2API-<version>-mac-x64.dmg
Windows Chat2API-<version>-x64-setup.exe 或便携版
Linux Chat2API-<version>-x64.AppImage、.deb 或 .tar.gz

从源码运行

环境要求:Node.js 18+、npm 和 Git。Docker 镜像使用 Node.js 22。

git clone https://github.com/pyf-feifei/Chat2API.git
cd Chat2API
npm install
npm run dev:win       # Windows
npm run dev           # macOS/Linux

构建生产版本:

npm run build
npm run build:mac
npm run build:win
npm run build:linux
npm run build:all

Docker 服务端

Docker 镜像运行 Koa 代理和浏览器管理端,数据保存在 /data,默认监听 8080:

docker build -t chat2api:server .
docker run -d --name chat2api \
  -p 8080:8080 \
  -v chat2api-data:/data \
  -e CHAT2API_HOST=0.0.0.0 \
  -e CHAT2API_PORT=8080 \
  -e CHAT2API_ENABLE_MANAGEMENT_API=true \
  -e CHAT2API_MANAGEMENT_SECRET=change-me \
  chat2api:server

打开 http://localhost:8080/admin/,使用管理密钥登录。完整的 Docker 部署指南 介绍了 Compose、浏览器辅助导入账户、存储加密、Qwen 会话修复和运行参数调优。

快速开始

  1. 启动 Chat2API,或启动 Docker 服务端。
  2. 打开服务商页面,添加内置服务商并填写其 Web 凭证。凭证保存在本地,请勿提交到代码仓库。
  3. 打开代理设置,选择端口和路由策略,然后启动代理。
  4. 将 OpenAI 兼容客户端的地址设为 http://127.0.0.1:8080/v1。

Python OpenAI SDK 示例:

from openai import OpenAI

client = OpenAI(
    api_key="your-chat2api-key",
    base_url="http://127.0.0.1:8080/v1",
)

response = client.chat.completions.create(
    model="deepseek-v4-flash",
    messages=[{"role": "user", "content": "你好,Chat2API!"}],
)

print(response.choices[0].message.content)

Codex CLI 请使用 Responses 接口并参考 docs/codex.md。

网络出口:让服务商流量绕过本地代理

Chat2API 必须通过你真实的网络路径访问服务商 API,而不是走本地 HTTP/SOCKS 代理。一旦走错,会出现一整类看起来像“账号问题”或“内容问题”、实则是出口问题的 上游故障。

这不是假设。2026-09-25,一台开启了 Clash Verge 且设置了 HTTP_PROXY/HTTPS_PROXY=http://127.0.0.1:7897 的 Windows 主机,把所有服务商 请求都发到了一个托管在 195.242.178.82 的 hysteria2 节点上——而那台机器正是运行 生产环境的服务器。于是 Qwen 看到的是一个共享的美国机房出口,而不是家宽 IP,阿里云 WAF 随之返回 bxpunish / RGV587 风控判定(qwen_ai_content_verdict、 “egress-IP flag”)。家宽 IP 从未被封,它只是压根没被用上。

自动防护

src/main/proxy/egressPolicy.ts 在 Electron 主进程与无头服务端中均先于任何网络模块 执行,它会把服务商域名追加到 NO_PROXY/no_proxy。之所以能立即生效,是因为 axios 使用的解析库 proxy-from-env 会在每次请求时读取 process.env,因此对已经创建 好的 axios 实例同样生效。你无需做任何配置。

默认直连的域名:.qwen.ai、.qianwen.com、.aliyuncs.com、.alibabacloud.com、 .alicdn.com,以及 localhost 和 127.0.0.1。

变量 作用
CHAT2API_EGRESS_DIRECT=off 关闭该策略(服务商流量重新走代理)
CHAT2API_EGRESS_DIRECT=a.com,b.com 替换内置列表
CHAT2API_EGRESS_DIRECT_EXTRA=c.com 追加到内置列表

自行验证出口

# 策略生效后应用会走的路径
node -e "const p=require('proxy-from-env');console.log(p.getProxyForUrl('https://chat.qwen.ai/api/v1/chat')||'DIRECT')"

# 绕过所有代理后的真实网络出口
curl -s --noproxy '*' https://myip.ipip.net

第一个命令输出 DIRECT 是预期结果。注意:IP 回显服务只能说明访问它自己走的是哪条路。 在 Clash 这类按规则分流的代理下,ipinfo.io(MATCH,PROXY)、api.ipify.org(代理规则集) 显示的是节点 IP,即使服务商域名全部直连也一样。这个读数已经不止一次把诊断带偏。 要判断某个服务商域名的出口,请看 Clash 对该域名命中的规则(Clash Verge → 连接), 或者用和服务商同一条规则路径的回显服务对比(国内服务商用 myip.ipip.net,走 DIRECT)。如果 --noproxy 那条命令返回机房 AS(如 AS7488),说明 Chat2API 之下还有东西在代理全部流量。(AS4837 属于正常家宽运营商。)

Clash Verge / Mihomo

应用层策略管不到浏览器。如果同一账号在浏览器和 Chat2API 上使用不同出口,上游会看到 账号在两个 IP 之间“跳动”,这在风控看来极像账号被盗。请在订阅覆写里把相同域名加为 DIRECT,且必须放在 MATCH,PROXY 之前:

prepend:
  - DOMAIN-SUFFIX,qwen.ai,DIRECT
  - DOMAIN-SUFFIX,qianwen.com,DIRECT
  - DOMAIN-SUFFIX,aliyuncs.com,DIRECT
  - DOMAIN-SUFFIX,alibabacloud.com,DIRECT
  - DOMAIN-SUFFIX,alicdn.com,DIRECT

在 Clash Verge 中,该文件位于 %APPDATA%/io.github.clash-verge-rev.clash-verge-rev/profiles/<uid>.yaml。 改完后需在 UI 里重载订阅;核心以服务方式运行,无管理员权限的 shell 无法重启它。

Docker 注意:即使容器内没有 HTTP_PROXY,Docker Desktop 也会把容器流量 走 Windows 系统代理(http.docker.internal:3128),所以容器内设 NO_PROXY 没用。 但这些流量确实会进入 Clash,由上面的规则按域名分流。2026-09-30 在系统代理开启时 从容器内实测:上面这些服务商域名走 DIRECT,出口是家宽 IP;国外域名命中 代理规则,走节点。规则只管它列出的域名,所以要确认服务商页面会访问的域名都被覆盖 (例如 Z.ai 还会访问 chatglm.cn,可在“连接”里确认)。如果想让容器完全不经过 Clash,见下一节。

完整配置、验证与排障:docs/network-egress.md。

容器:先查 Docker 的 VM 级代理

如果容器的每个请求都返回阿里云 WAF 挑战页(aliyun_waf_aa),而同一账号在 浏览器里正常,那原因几乎一定是 Docker Desktop 的代理,而不是服务商:

docker info | grep -A2 "^ *Proxy"     # 会打印 http.docker.internal:3128 吗?

如果打印了,说明 Docker 把所有容器流量都送进了这个代理,也就是 Clash。之后每个域名 走哪里由它命中的 Clash 规则决定:DIRECT 规则保持家宽 IP,落到 MATCH,PROXY 的走节点。 所以先确认每个服务商域名都有 DIRECT 规则(见上文)。如果想让容器完全不经过 Clash: 关掉 Windows 系统代理,并把 Docker Desktop 的代理模式改为 manual 且不填地址(存于 %APPDATA%\Docker\marlin.dat,把 "proxyHTTPMode":{...,"Value":"system"} 改为 "manual"),然后重启 Docker Desktop。

容器内部无法覆盖它——HTTP_PROXY、NO_PROXY=*、--network host 全部无效。

完整步骤,以及“所有 Webshare key 同时报 401”其实是 DNS 污染而非 key 失效的情形, 见 docs/network-egress.md §8.5。

不要在工作机上跑全量账号池

服务商的限流维度是出口 IP,不是账号。无论账号从何而来,单一 IP(尤其是共享机房 IP)上驱动约 340 个账号,本身就是一个异常形态。请把完整账号池留在生产服务器上, 本地只用一个账号做功能验证。

一旦出现风控判定,Chat2API 会自动兜底。bxpunish / RGV587 判定由出口路径决定, 而非某个特定请求体,所以除了按请求指纹的熔断之外,还有一个进程级出口熔断: 当 CHAT2API_QWEN_AI_EGRESS_CIRCUIT_WINDOW_MS(默认 5 分钟)窗口内有 CHAT2API_QWEN_AI_EGRESS_CIRCUIT_THRESHOLD(默认 12)个不同请求体被判风控时, 所有新的 Qwen AI 流量会直接以 503 qwen_ai_risk_circuit_open 和 Retry-After 拒绝,在消耗下一个账号之前就停住。只要有一次上游成功响应即自动关闭,因此修好路由 后能立即恢复,而不必等完整个冷却期。

变量 默认值 作用
CHAT2API_QWEN_AI_EGRESS_CIRCUIT_THRESHOLD 12 多少个不同请求体被拒后停住整个出口;设为 0 则首次判定即停
CHAT2API_QWEN_AI_EGRESS_CIRCUIT_COOLDOWN_MS 180000 出口停住的时长
CHAT2API_QWEN_AI_EGRESS_CIRCUIT_WINDOW_MS 300000 统计风控判定的时间窗口

风控判定不一定和出口有关。如果同一个请求指纹在所有账号上都被拒、而其他请求正常,说明 上游判的是这段对话内容,换账号或换 IP 都没用。详见 AGENTS.md("A content verdict is not always an egress or account problem")。

必须配置存储加密密钥

账号凭据在落盘时会被加密。密钥必须在所有共用同一份数据文件的实例之间保持一致, 并且必须真正传到进程里。

CHAT2API_STORAGE_ENCRYPTION_KEY=change-this-to-a-long-random-secret

选一次值,固定写在 .env 里,桌面端和所有共用该存储的 Docker 部署都用同一个值。

# 确认密钥已进入进程
docker exec chat2api printenv CHAT2API_STORAGE_ENCRYPTION_KEY

容器必须用 docker compose up -d 启动,不要用 docker run。 用 docker run 创建的容器没有 compose 标签,docker compose up 会拒绝接管它, .env 也就永远不会被注入——进程会在没有密钥的情况下静默运行。

如果密钥缺失或与数据不匹配

不会抛任何错。运行时会原样返回 c2a:v1:… 密文,于是每个账号都被当成"没有会话", 修复队列每 25 秒对 339 个账号发起 signin,上游返回 401 email not found, 拒绝风暴打开风控闸门(300s → 600s → 1200s → 2400s),最终包括普通聊天在内的所有 请求都返回 403 qwen_ai_token_refresh_gated。它表现为一次风控故障,实际上是配置错误。

现在启动自检会直接拦住它:

[CredentialSelfCheck] Credential data is encrypted (c2a:v1:…) but encryption is
not available. … Set CHAT2API_STORAGE_ENCRYPTION_KEY … and recreate the instance
so the variable actually reaches the process (a container started with `docker run`
never reads .env).
[Store] Initialization aborted: Credential data is encrypted but
CHAT2API_STORAGE_ENCRYPTION_KEY is not usable

修好后必须重建容器(环境变量只在进程启动时读取一次,docker restart 不够):

docker compose up -d --force-recreate

区分它和真实风控最快的办法——对比两个环境的 session repair 启动行:

已损坏:  [QwenAI Session Repair] started ready=0 pending=339
健康:    [QwenAI Session Repair] started ready=339 pending=0

ready=0 pending=339 说明凭据读不出来,而不是上游在拦你。只有存储确实是明文时, 才用 CHAT2API_CREDENTIAL_SELF_CHECK=off 绕过该自检。

本地与生产部署注意事项

在同一台机器上让桌面端和 Docker 服务端共用同一批账号,需要多加注意。

关注点 桌面端(工作机) Docker(生产)
建议账号数量 1–3 个,仅做功能验证 完整账号池
出口 家宽 IP,不走代理 固定服务器 IP,不走代理
禁止 驱动生产账号池,或从本机做压测 —
  • 不要让本地实例和生产容器在同时运行时指向同一个 data.json//data 卷。 双方会互相覆盖 status/errorMessage 字段,且各自的修复队列会与对方的判定相互 干扰。
  • 不要在工作机上做压测或长时间浸泡测试。上游限流是按出口 IP 计的,本机压测损害的 是生产账号池,而不是在度量代码。
  • 不要在容器运行时直接改数据卷。请先停容器、改完再启动;否则内存态会在下一次保存时 把你的修改覆盖掉。动手前先备份:
    docker exec chat2api cat /data/data.json > data.json.backup
  • 桌面端会自动应用直连策略;若你在同样配置了代理的主机上跑 Docker 镜像,无头服务端会 应用同一策略。只有当你确实希望该路径经过代理时,才设置 CHAT2API_EGRESS_DIRECT=off。Docker Desktop 主机请按上文修正 VM 级代理;Clash 规则 管不到容器流量。

两种长得一样但根因无关的故障,详见 docs/network-egress.md:

现象 根因
出口显示机房 AS 本地代理被 Docker Desktop 继承
所有请求 403、账号"假死" 存储加密密钥缺失/不匹配

另见 2026-09-25 全池故障的复盘: docs/diag-2026-09-25-qwen-egress.md。

截图

仪表盘 服务商
仪表盘 服务商
代理设置 API Key
代理设置 API Key
模型管理 会话管理
模型管理 会话管理

配置与数据

桌面版数据保存在 ~/.chat2api/,Docker 版数据保存在挂载的 /data 卷中。

路径 内容
data.json 全部数据:服务商、账号(凭证加密)、配置、会话、统计
qwen-ai-file-cache.json、compression-archive.json 上传文件缓存和上下文压缩归档
logs/、request-logs/、responses/ 应用日志、请求日志、Responses 状态

服务端设置了 CHAT2API_DATA_DIR 时以它为准。

服务端支持主机/端口、管理 API、API Key、存储加密、负载均衡、请求超时和服务商专属参数。可从 docs/docker.md 中的示例开始配置。

参与贡献

欢迎提交 Issue、服务商适配、测试和文档改进。进行较大的适配器改动前,请先阅读现有服务商说明并创建 Issue 讨论。

npm install
npm run build
npm run build:server

测试套件(tests/)不在仓库里,只存在于维护者的本地工作副本中,所以 npm run test:* 脚本需要本地有 tests/ 目录才能运行。

许可证

Chat2API 使用 GNU General Public License v3.0 发布。

致谢

Electron、React、TypeScript、Tailwind CSS、Zustand 和 Koa。