Skip to content

Repository files navigation

FreeLLMAPI Worker (fllmapi-worker)

FreeLLMAPI 的 Cloudflare Worker 精简版:一个运行在边缘的 OpenAI 兼容 LLM 代理,聚合多个免费 provider,带简单 fallback 链。

独立仓库:从 freellmapi-worker monorepo 的 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 settings max_consecutive_upstream_fails)设 >0 时,同一请求链连续失败达阈值即截断返回 503,不再耗尽全部候选
  • 全冷却/未配置的响应:全部候选冷却 → 429 rate_limit_exceeded + retryAtMs(最早到期时刻)+ Retry-After,客户端可据此等待重试;一个 provider 都没配置 → 503 no_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 部署(云端一键)

推送后,在 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,默认中文):

  1. 管理 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
  2. 添加 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 填该平台支持的模型

免费额度限制(2026-09 现状)

指标 免费计划 本 Worker 的消耗
请求数 100K/天 1 请求 = 1 次上游调用(fallback 时 +1)
CPU 时间 10ms/请求 解析 + 透传,远低于限制
D1 行读 5M/天 配置加载(缓存后为 0)
D1 行写 100K/天 仅配置页操作
上游超时 125s(Proxy Read Timeout) 超过返回 524,客户端需重试

免责声明与风险

责任说明

  • 本仓库是公开的个人项目,按 AS-IS 原样提供,不含任何明示或默示担保(包括但不限于适销性、特定用途适用性、不侵权)。作者不对使用本软件产生的任何直接、间接、偶然或后果性损害负责。
  • 请在阅读、理解并接受上述条款后使用。

知情风险

  1. 上游免费额度随时可能变化:各 provider 的免费 tier 可能随时收紧、限流或下线。本 Worker 不保证任何 provider 的可用性、稳定性与响应速度;相关状态异常导致的失败(429/5xx/超时)由 fallback 链尽力缓解,但不保证全部成功。
  2. 请遵守上游服务条款与适用法律、仅用于合法用途:本项目聚合多个第三方模型 API,使用者须自行确保其对上游服务的调用不违反各 provider 的服务条款,不用于任何违法或不道德的场景。
  3. 内容自行负责:本 Worker 仅做透传与路由,不审查、不存储、不追踪经由此代理的对话内容。请求的内容是否合规由调用方自行负责。
  4. 密钥保管自负:provider API key 加密存储于 Cloudflare D1,但整体安全性取决于你自己的 Cloudflare 账号、D1 权限与 ENCRYPTION_KEY 的保管。泄露或滥用密钥造成的后果由使用者承担。
  5. Workers 免费计划限制由 Cloudflare 制定:100K 请求/天、D1 行读写等配额由 Cloudflare 官方决定并可能调整。超出免费配额可能导致请求失败或产生费用;本项目的"免费额度设计"只是尽力克制消耗,不构成承诺。
  6. 无 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 test

目录结构

src/
├── 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)

About

freellmapi for worker

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages