Skip to content
 
 

Repository files navigation

freebuff2api

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)。

配置

获取 Token

无需安装 Freebuff / Codebuff CLI,可以直接打开公开页面自动获取 token:

https://freebuff.071129.xyz/

使用方式:

  1. 打开上面的地址
  2. 选择 Freebuff
  3. 点击“开始认证”,在跳转页面完成授权
  4. 回到页面复制展示的 token
  5. 将复制结果写入本项目 .env

示例:

FREEBUFF_TOKEN=你的 Freebuff Bearer token

多账号可用英文逗号分隔,每个 token 对应一个独立账号。请求逐请求轮换 (round-robin):每次都切换到下一个空闲账号,并发请求均匀分摊到不同 账号,避免单个账号的全局 active free session 被并发切模型请求互相覆盖:

FREEBUFF_TOKEN=token-a,token-b,token-c

429 限流自动冷却,按 (账号, 模型) 隔离

  • 某账号对某模型触发 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-lite
  • google/gemini-3.1-flash-lite-preview
  • google/gemini-3.1-pro-preview

另外硬编码保留两个旧模型 id:deepseek/deepseek-v4-flashmimo/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.py

运行

uv sync
uv run freebuff2api

或:

python -m pip install -e .
python main.py

Docker 部署

# 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

GitHub Actions 自动构建

推送 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
  }'

Python (OpenAI Responses API)

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.completed
  • response.output_item.added / response.output_item.done
    • message 项:附带 response.content_part.added / response.output_text.delta / response.output_text.done / response.content_part.done
    • reasoning 项:附带 response.reasoning_text.delta / response.reasoning_text.done
    • function_call 项:附带 response.function_call_arguments.delta / response.function_call_arguments.done

工具过滤:由于上游只接受 type: "function",请求里携带的 type: "custom" 等非函数工具会在转发前自动剔除,避免上游返回 tools[N]: unknown variant custom 的 400 错误。

Python (Anthropic SDK)

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="")

感谢

FreeBuff

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages