Codebuff Freebuff 的 OpenAI-compatible API
| 端点 | 协议 | 认证方式 |
|---|---|---|
POST /v1/chat/completions |
OpenAI Chat | Authorization: Bearer <key> |
POST /v1/responses |
OpenAI Responses | Authorization: Bearer <key> |
POST /v1/messages |
Anthropic | x-api-key: <key> |
GET /v1/models |
通用 | — |
POST /v1/models/refresh |
通用 | Authorization: Bearer <key> |
GET /healthz |
通用 | — |
模型名支持简写(如 deepseek-v4-pro 等同于 deepseek/deepseek-v4-pro)。
无需安装 Freebuff / Codebuff CLI,可以直接打开公开页面自动获取 token:
https://freebuff.071129.xyz/
使用方式:
- 打开上面的地址
- 选择 Freebuff
- 点击“开始认证”,在跳转页面完成授权
- 回到页面复制展示的 token
- 将复制结果写入本项目
.env
示例:
FREEBUFF_TOKEN=你的 Freebuff Bearer token多账号可用英文逗号分隔,每个 token 对应一个独立账号。请求逐请求轮换 (round-robin):每次都切换到下一个空闲账号,并发请求均匀分摊到不同 账号,避免单个账号的全局 active free session 被并发切模型请求互相覆盖:
FREEBUFF_TOKEN=token-a,token-b,token-c429 限流自动冷却,按 (账号, 模型) 隔离:
- 某账号对某模型触发 429 后,该 (账号, 模型) 进入冷却窗口(直到上游
resetAt过期),冷却期间该模型的请求自动轮换到下一个可用账号, 不再打同一个被限流的账号 - 冷却只影响该 (账号, 模型) 组合——同账号的其他模型不受影响,照常 使用该账号
- 如果所有账号都在冷却同一个模型,请求立即返回 429(带上游原始限流 信息),而不是空等冷却结束
观测方式:每条请求会打印 pool reserved account index=x/n model=...
日志,可直接看到请求落在了哪个账号。无网络的轮换/故障转移演示脚本:
$env:FREEBUFF_TOKEN='token-a,token-b' ; python tool/demo_pool_rotation.py复制 .env.example 为 .env,然后填写上游 token:
Copy-Item .env.example .env.env 示例:
FREEBUFF_TOKEN=你的 Freebuff Bearer token
FREEBUFF_API_KEY=本地 OpenAI API key,可留空
FREEBUFF_AD_PROVIDERS=gravity,carbon
FREEBUFF_PROXY_ENABLED=false
FREEBUFF_PROXY_URL=
FREEBUFF_DEBUG=false
FREEBUFF_LOG_LEVEL=INFO
FREEBUFF_LOG_BODY_CHARS=2000
FREEBUFF_LOG_COLOR=true
FREEBUFF_HOST=0.0.0.0
FREEBUFF_PORT=8000
FREEBUFF_MODELS_REFRESH_SECONDS=3600
FREEBUFF_MODELS_CACHE_PATH=freebuff_models.json默认不启用代理,所有上游请求直连,且不会读取系统 HTTP_PROXY / HTTPS_PROXY。
需要让所有上游请求经过代理时,在 .env 中开启:
FREEBUFF_PROXY_ENABLED=true
FREEBUFF_PROXY_URL=http://127.0.0.1:7890支持 HTTP 和 SOCKS 代理,例如:
FREEBUFF_PROXY_URL=http://127.0.0.1:7890
FREEBUFF_PROXY_URL=socks5://127.0.0.1:1080
FREEBUFF_PROXY_URL=socks5h://127.0.0.1:1080模型来源:除 3 个 Gemini 别名外,模型列表在服务启动时从上游自动发现,
并默认每小时自动刷新(FREEBUFF_MODELS_REFRESH_SECONDS,设为 0 关闭),
也可以调用 POST /v1/models/refresh 手动立即刷新。多账号时会把所有账号
发现的模型按模型 id 去重合并(并集):某模型只在一个账号的免费配额里
可见也会被列出,轮换到该账号即可使用;单个账号限流/异常只会少贡献它
自己的模型,不会丢掉其他账号的模型。上游新增模型若无法直接映射到
agent,会自动按显示名 / provider 路径继承已知映射(日志会打印
auto-corrected agent mapping)。
模型持久化:每次刷新成功后,动态模型列表和已学习的 agent 映射会原子写入
FREEBUFF_MODELS_CACHE_PATH(默认 freebuff_models.json,设空字符串关闭)。
服务启动时先加载磁盘缓存再刷新——即使上游暂时不可用,重启后也能立刻用
上次的模型列表,不会塌缩成只有 3 个 Gemini;刷新成功后会覆盖缓存,刷新失败
则保留旧缓存。Docker 部署时把该文件放到挂载卷上即可跨容器重启/重建保留
(见下方 Docker 部署)。
硬编码的 Gemini 别名(实际路由到 mimo/mimo-v2.5):
google/gemini-2.5-flash-litegoogle/gemini-3.1-flash-lite-previewgoogle/gemini-3.1-pro-preview
另外硬编码保留两个旧模型 id:deepseek/deepseek-v4-flash 和
mimo/mimo-v2.5。即使账号当前配额(rateLimitsByModel)不再下发它们,它们
也会一直出现在 /v1/models 中;请求仍走正常会话流程,账号若无法提供则原样透传
上游错误(如 409 区域/会话限制、429 限流),而不是本地报 “Unsupported model”。
调试空返回或上游异常时:
FREEBUFF_DEBUG=true
FREEBUFF_LOG_LEVEL=DEBUG
FREEBUFF_LOG_BODY_CHARS=0查看上游实际返回的模型、并集合并结果与自动纠正映射:
python tool/check_models.pyuv sync
uv run freebuff2api或:
python -m pip install -e .
python main.py# docker-compose.yml
services:
freebuff2api:
image: yflwz/freebuff2api:latest
container_name: freebuff2api
restart: always
ports:
- "8000:8000"
environment:
# 模型缓存写到命名卷,容器重建/重启后模型列表不丢
- FREEBUFF_MODELS_CACHE_PATH=/data/freebuff_models.json
volumes:
- ./.env:/app/.env
- freebuff-models:/data
volumes:
freebuff-models:启动:
docker compose up -d推送 main 分支或打 v* tag 时自动构建并推送到 Docker Hub。
构建后、推送前会先跑一个模型持久化冒烟测试(docker-compose.smoke.yml):
种子一个假模型缓存并用无效 token 启动容器(上游发现必然失败),断言
GET /v1/models 仍返回缓存中的模型(而不是塌缩成 3 个 Gemini),验证通过后
才把镜像推送到 Docker Hub。
在仓库 Secrets 添加:
| Secret | 说明 |
|---|---|
DOCKER_USERNAME |
Docker Hub 用户名 |
DOCKER_PASSWORD |
Docker Hub 密码或 token |
curl http://127.0.0.1:8000/v1/chat/completions `
-H "Authorization: Bearer $env:FREEBUFF_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "deepseek/deepseek-v4-pro",
"messages": [{"role": "user", "content": "你好"}],
"stream": false
}'流式:
curl -N http://127.0.0.1:8000/v1/chat/completions `
-H "Authorization: Bearer $env:FREEBUFF_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "deepseek/deepseek-v4-pro",
"messages": [{"role": "user", "content": "写一个 Python 快排"}],
"stream": true
}'from openai import OpenAI
client = OpenAI(api_key="你的 FREEBUFF_API_KEY", base_url="http://127.0.0.1:8000")
# 非流式
resp = client.responses.create(
model="deepseek/deepseek-v4-pro",
input="Hello",
instructions="You are helpful.",
)
print(resp.output[0].content[0].text)
# 流式
for event in client.responses.stream(
model="deepseek-v4-pro",
input="Count to 3",
):
if event.type == "response.output_text.delta":
print(event.delta, end="")支持的 input 项:
type: "message"(任意角色;字符串或文本块列表都会被规范化)type: "function_call"→ 转成带tool_calls的 assistant 消息type: "function_call_output"→ 转成带tool_call_id的 tool 消息- 其他未知类型会在请求体中静默跳过
流式事件类型(与非流式响应中的 output 一一对应):
response.created/response.in_progress/response.completedresponse.output_item.added/response.output_item.donemessage项:附带response.content_part.added/response.output_text.delta/response.output_text.done/response.content_part.donereasoning项:附带response.reasoning_text.delta/response.reasoning_text.donefunction_call项:附带response.function_call_arguments.delta/response.function_call_arguments.done
工具过滤:由于上游只接受 type: "function",请求里携带的 type: "custom" 等非函数工具会在转发前自动剔除,避免上游返回 tools[N]: unknown variant custom 的 400 错误。
from anthropic import Anthropic
client = Anthropic(
api_key="你的 FREEBUFF_API_KEY",
base_url="http://127.0.0.1:8000",
)
# 非流式
msg = client.messages.create(
model="deepseek/deepseek-v4-pro",
max_tokens=1024,
system="You are a helpful assistant.",
messages=[{"role": "user", "content": "你好"}],
)
print(msg.content[0].text)
# 流式
with client.messages.stream(
model="deepseek-v4-pro",
max_tokens=1024,
messages=[{"role": "user", "content": "数到3"}],
) as stream:
for text in stream.text_stream:
print(text, end="")