FreeLLMAPI 的 Cloudflare Worker 精简版:一个运行在边缘的 OpenAI 兼容 LLM 代理,聚合多个免费 provider,带简单 fallback 链。
独立仓库:从
freellmapi-workermonorepo 的worker/目录拆出。Cloudflare 侧 worker 名与 D1 库名沿用freellmapi-worker/freellmapi-worker-db。
与原版差异:
| 能力 | 原版 server | Worker 精简版 |
|---|---|---|
| API 表面 | 全部(chat/embeddings/images/audio/responses/…) | 仅 /v1/chat/completions(含 SSE 流式) |
| 存储 | SQLite(better-sqlite3) | D1(密钥加密格式兼容,可迁移) |
| 路由 | 六策略评分路由 + 持久化速率限制 | 简单 fallback 链(priority 排序,429/5xx/网络错误切换 + 内存冷却) |
| 管理界面 | React dashboard(60 语言) | 单页极简配置页 |
| 出站代理 | SOCKS/HTTP 代理、fetch-relay | 无(Worker 直连上游) |
/v1/chat/completions
→ 认证(Bearer API_TOKEN,常量时间比较)
→ 解析 body(zod)
→ 配置缓存(Worker 实例内存,admin API 修改后失效)
→ resolveChain:按 priority 排序,过滤支持目标模型且未在冷却中的 provider
→ fallback 链:逐个尝试,429/5xx/网络错误切换到下一个
→ 非流式:JSON 响应 + X-Routed-Via 头
→ 流式:SSE 透传(首 chunk 拿到后才开始发送,之前的失败可 fallback)
高可用(fallback 循环):
- 内存冷却(circuit breaker):provider 失败后进入冷却,冷却期间跳过,避免反复打挂掉的 provider 浪费时间。
- 401 无效 key → 5min 启发式;402 余额耗尽 → 24h(credit);403 tier 问题 → 24h(tier)。credit/tier 由上游/运营商决定,不参与探测
- 429 带
Retry-After(>90s)→ 权威冷却,按上游自述时长;消息含 daily/allocation/quota → 冷却到下一个 UTC 午夜(免费额度重置)。带短时重试提示(消息里 "in 20 seconds" 等或Retry-After≤90s)的不算日额度,走瞬态阶梯自愈——避免把几分钟内恢复的限流误锁到午夜 - 其他 429 → 90s 起步的瞬态阶梯:1h 内第 2 次 2min、≥3 次 10min(封顶)
- 5xx / 网络错误 / 超时 → 90s 瞬态;模型 404(下架)→ 按 provider+model 15min 窗口计数,3 次才 bench 10min,不株连同 provider 其他模型
- 冷却只允许延长、绝不缩短;成功响应立即清除;admin 修改 provider(如换 key)立即解除(冷却与速度记忆一并作废)
- 冷却为 Worker 实例内存态(isolate 间不共享):admin 的清除只作用于其命中实例,chat 请求可能落在别的仍带锁的实例上——重新部署会回收所有实例、清空全部冷却
- 全冷却 429 响应带明细:
cooldowns: [{providerId, name, source, remainingMs, lastStatus, lastMessage}],消息里逐家列出原因与剩余时间;配置页状态栏出现"清除冷却"按钮(POST /api/admin/cooldowns/clear,同样仅作用于当前实例)
- half-open 探测(自愈):全部候选都在冷却时,把该请求当探测打到启发式冷却中、冷却过半的 provider:成功即当正常请求返回并解除该 provider 冷却;失败按 30s×2ⁿ 指数退避(封顶 15min)推迟下一次探测,不延长冷却、不推高瞬态阶梯。探测超时钳到 5s
- 连败熔断:
MAX_CONSECUTIVE_UPSTREAM_FAILS(或 D1 settingsmax_consecutive_upstream_fails)设 >0 时,同一请求链连续失败达阈值即截断返回 503,不再耗尽全部候选 - 全冷却/未配置的响应:全部候选冷却 → 429
rate_limit_exceeded+retryAtMs(最早到期时刻)+Retry-After,客户端可据此等待重试;一个 provider 都没配置 → 503no_providers_configured(不再误报为模型不存在) - 清除并验证:配置页冷却徽章旁可一键「清除并验证」——清空冷却后对每个之前冷却中的 provider 发一次最小探测(5s 超时)确认上游是否恢复,探测失败的 provider 按正常
noteFailure语义自动重新进入冷却,避免上游未恢复时被反复请求击中 - 总时间预算:fallback 循环不超过 45s(原版默认值),避免 N 个 provider 每个等满超时
- 单候选超时上限:未显式配置
timeout_ms的 provider,单次尝试最多 15s 就会被切走;显式配置了timeout_ms的 provider 尊重其值(慢推理模型可用它豁免,但仍受 45s 总预算约束) - 速度/成功率记忆(Worker 实例内存,含 EMA):同 provider 内,实测快而稳的模型优先尝试;只失败过的模型垫底;每次配置页改动后自动作废。无任何数据时按配置顺序
- 流式边界:首 chunk 之前的失败(429/5xx/网络/空流)可 fallback;流开始后不可 fallback(SSE 已发给客户端)——这是流式协议的固有限制
免费额度设计(严格控制在 Workers Free 计划内):
- 热路径 0 次 D1 查询:providers + settings 缓存在 Worker 实例内存,admin API 修改后失效重载
- 每次请求 1 次 D1 读(仅配置加载,且被缓存吸收)——远低于 5M 行读/天
- 无 TransformStream 处理:SSE 直接透传,不统计 token、不修改内容
- 无持久化速率统计:不写 D1 日志
- 100K 请求/天免费额度内,D1 行读/写均远低于限额
前置:Node >=20.18,已安装 wrangler 并登录(npx wrangler login)。
npm install
ADMIN_TOKEN=xxx API_TOKEN=xxx npm run deploy:oneclick脚本自动完成:检查登录 → 创建 D1 数据库(若 wrangler.jsonc 中 database_id 还是占位符)→ 设置 secrets → 应用迁移 → 部署。
必填环境变量:
ADMIN_TOKEN— 配置页登录用API_TOKEN—/v1/chat/completions客户端 token
可选环境变量:ENCRYPTION_KEY(64 位 hex;不填则自动生成,已存在则复用,更换会导致已存 provider 密钥无法解密)。生成的密钥写入 .dev.vars 供本地开发使用。
另有路由旋钮(均有 D1 settings 表行优先、env 兜底,非法值退回默认):
| 环境变量 | D1 settings 行 | 缺省 | 说明 |
|---|---|---|---|
COOLDOWN_CEILING_MS |
routing_cooldown_ceiling_ms |
24h | 冷却时长上限(仅夹启发式/credit/tier,权威冷却不受限),钳在 [1min, 24h] |
MAX_CONSECUTIVE_UPSTREAM_FAILS |
max_consecutive_upstream_fails |
0(关) | 连败熔断阈值;设 3 即"同一链连续失败 3 次"截断 503 |
D1 覆盖示例:npx wrangler d1 execute freellmapi-worker-db --remote --command "INSERT OR REPLACE INTO settings (key, value) VALUES ('routing_cooldown_ceiling_ms', '3600000')"。
npm install
# 1. 创建 D1 数据库(首次,两种方式任选)
# 方式 A(CLI,推荐):输出直接包含 database_id
npx wrangler d1 create freellmapi-worker-db
# 方式 B(控制台):D1 SQL database → Create database,
# 创建后控制台不显示 database_id,需 `npx wrangler d1 list` 查看 UUID
# 输出里的 database_id 填入 wrangler.jsonc 的 d1_databases[0].database_id
# 2. 设置密钥
npx wrangler secret put ENCRYPTION_KEY # 64 位 hex:node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
npx wrangler secret put ADMIN_TOKEN # 配置页登录用
npx wrangler secret put API_TOKEN # /v1/chat/completions 客户端 token
# 3. 应用迁移
npx wrangler d1 migrations apply freellmapi-worker-db
# 4. 本地开发
npm run dev
# 5. 部署
npm run deploy推送后,在 GitHub 仓库 Actions → Deploy Worker to Cloudflare → Run workflow 手动触发,即完成「测试 → 部署」全流程。仅手动触发,无 push 自动部署。
前置:在仓库 Settings → Secrets and variables → Actions 配置:
| Secret | 必填 | 说明 |
|---|---|---|
CLOUDFLARE_API_TOKEN |
必填 | Cloudflare API token,需 Workers Scripts:Edit + D1:Edit + Account Settings:Read 权限 |
CLOUDFLARE_ACCOUNT_ID |
必填 | Cloudflare 账户 ID |
D1_DATABASE_ID |
必填 | D1 数据库 UUID(CLI 创建直接输出;控制台创建后需 wrangler d1 list 查看) |
ADMIN_TOKEN |
必填 | 配置页登录用 |
API_TOKEN |
必填 | /v1/chat/completions 客户端 token |
ENCRYPTION_KEY |
可选 | 不填则首次部署自动生成并复用 |
工作流流程:安装依赖 → 运行测试 → 校验必填 secrets → 注入 D1 数据库 id → 应用迁移 → 设置 secrets → 部署。
部署后打开 https://<your-worker>.workers.dev/,输入 ADMIN_TOKEN 进入配置页(右上角可切换 中文/English,默认中文):
- 管理 Token:配置页「Token 管理」面板可修改两个 token:
- API Token:客户端调用
/v1/chat/completions用(Authorization: Bearer <token>) - Admin Token:登录本配置页用,修改后当前会话自动切换为新值
- 每个输入框可填新值后保存;API Token 额外提供「随机生成」(UUID)按钮
- token 存 D1
settings表,优先于环境变量 secrets(目标是部署时的初始值)。应急恢复:wrangler d1 execute freellmapi-worker-db --remote --command "DELETE FROM settings WHERE key IN ('api_token','admin_token')"可回退到 secrets
- API Token:客户端调用
- 添加 provider:
- 快速添加(推荐):从预置平台下拉选择(Groq、Google Gemini、OpenRouter、Cerebras、DeepSeek、智谱 GLM、ModelScope、SiliconFlow、Mistral、Cloudflare Workers AI),自动填充 Base URL 与默认模型(可取消勾选),只需粘贴 API key 即可添加;也可点「获取完整模型列表」拉取该平台全部模型后搜索勾选
- 自定义添加:手动填写名称、平台 ID、Base URL、API key、模型列表(每行一个)、优先级(越小越先尝试)、超时
- 列表中的 provider 可编辑(API key 留空则保留旧值)或删除
curl https://<your-worker>.workers.dev/v1/chat/completions \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model": "auto", "messages": [{"role": "user", "content": "Hello"}]}'OpenAI SDK:
from openai import OpenAI
client = OpenAI(base_url="https://<your-worker>.workers.dev/v1", api_key=API_TOKEN)
resp = client.chat.completions.create(model="auto", messages=[{"role": "user", "content": "Hello"}])model传具体模型 id(如llama-3.3-70b-versatile)→ 先路由到声明支持该模型的 provider;全部不可用时降级到其他模型,实际命中的见响应头X-Routed-Via,被降级时另有X-Fallback-From: <原始请求模型>model传auto→ 按 priority 顺序尝试所有 provider 的全部模型(同一家内按实测速度/成功率排序逐个切换,单条链上限 12 个候选)- 429/5xx/网络错误、无效 key(401/403)、余额耗尽(402)、模型下架(404)都会自动换下一个候选;失败的 provider(或仅失败的某个模型)进入内存冷却,冷却期内跳过,在配置页改动该 provider 立即解除(冷却与速度记忆一并作废)
- 全部候选都在冷却 → 429
rate_limit_exceeded(含retryAtMs+Retry-After),不会误报 503;此时若有冷却过半的启发式冷却 provider,该请求会被当作 half-open 探测直接打到它,成功即恢复服务 - 配置改动最多 60 秒内对所有 Worker 实例生效;provider 列表为空时立即生效
原版 SQLite 的 api_keys 表用 AES-256-GCM 加密(encrypted/iv/auth_tag hex 三元组)。
本 Worker 使用相同的算法和存储格式,因此可以用 ENCRYPTION_KEY 解密后重新入库:
-- 原版 → 新库映射(在 D1 中执行)
-- 每个原版 key 对应一个 provider 行,models 填该平台支持的模型| 指标 | 免费计划 | 本 Worker 的消耗 |
|---|---|---|
| 请求数 | 100K/天 | 1 请求 = 1 次上游调用(fallback 时 +1) |
| CPU 时间 | 10ms/请求 | 解析 + 透传,远低于限制 |
| D1 行读 | 5M/天 | 配置加载(缓存后为 0) |
| D1 行写 | 100K/天 | 仅配置页操作 |
| 上游超时 | 125s(Proxy Read Timeout) | 超过返回 524,客户端需重试 |
责任说明
- 本仓库是公开的个人项目,按 AS-IS 原样提供,不含任何明示或默示担保(包括但不限于适销性、特定用途适用性、不侵权)。作者不对使用本软件产生的任何直接、间接、偶然或后果性损害负责。
- 请在阅读、理解并接受上述条款后使用。
知情风险
- 上游免费额度随时可能变化:各 provider 的免费 tier 可能随时收紧、限流或下线。本 Worker 不保证任何 provider 的可用性、稳定性与响应速度;相关状态异常导致的失败(429/5xx/超时)由 fallback 链尽力缓解,但不保证全部成功。
- 请遵守上游服务条款与适用法律、仅用于合法用途:本项目聚合多个第三方模型 API,使用者须自行确保其对上游服务的调用不违反各 provider 的服务条款,不用于任何违法或不道德的场景。
- 内容自行负责:本 Worker 仅做透传与路由,不审查、不存储、不追踪经由此代理的对话内容。请求的内容是否合规由调用方自行负责。
- 密钥保管自负:provider API key 加密存储于 Cloudflare D1,但整体安全性取决于你自己的 Cloudflare 账号、D1 权限与
ENCRYPTION_KEY的保管。泄露或滥用密钥造成的后果由使用者承担。 - Workers 免费计划限制由 Cloudflare 制定:100K 请求/天、D1 行读写等配额由 Cloudflare 官方决定并可能调整。超出免费配额可能导致请求失败或产生费用;本项目的"免费额度设计"只是尽力克制消耗,不构成承诺。
- 无 SLA:本项目没有服务等级协议、没有可用性承诺、也没有官方支持渠道。部署、运维与故障排查责任在部署者自身。
npm install # 首次
cp .dev.vars.example .dev.vars # 本地密钥样版(真实密钥不进 git)
npm run dev # wrangler dev,本地 :8787
npm run db:migrate:local # 本地 D1 迁移
npm run types # 重新生成 worker-configuration.d.ts
npm run lint
npm testsrc/
├── index.ts # Hono 入口,路由挂载
├── env.ts # Env 类型 + 配置加载/缓存(TTL 60s)
├── types.ts # OpenAI wire 类型(自包含,不依赖 monorepo shared)
├── env.test.ts
├── lib/
│ ├── crypto.ts # WebCrypto AES-256-GCM(格式兼容原版)
│ ├── models.ts # 预置模型数据(平台 → 默认模型)
│ ├── platforms.ts # 预置平台列表(名称/Base URL/模型获取端点)
│ └── tokens.ts # token 解析(D1 settings 优先,env 兜底)
├── providers/
│ ├── base.ts # Provider 基类(SSE 解析、超时、错误分类)
│ └── openai-compat.ts # OpenAI 兼容适配器
├── services/
│ └── router.ts # fallback 链 + 内存冷却 + 速度记忆
├── routes/
│ ├── chat.ts # /v1/chat/completions
│ └── admin.ts # 配置页 + 管理 API
└── test-helpers/
└── d1-stub.ts # 测试用 D1 内存 stub
migrations/
└── 20260912143025_init.sql # D1 schema(providers + settings)