Skip to content
This repository was archived by the owner on Sep 4, 2026. It is now read-only.

Repository files navigation

autoapi

中文 | English

该项目已经停止维护 该项目提供的内容已经合并到我仓库的new-api二改项目 本项目不再维护

透明的 LLM API 故障转移代理喵~ 客户端把请求发给它,它按优先级链路转发给上游;上游返回错误、假成功(200 里塞 error)、空流、卡流时,它会静默换下一个候选(换 base_url / api_key / 真实模型名)重发,客户端完全感知不到发生过失败喵。

对客户端来说,它就是一个普通的 OpenAI 兼容端点:把 base_url 指向 autoapi,model 填一个虚拟模型名,剩下的事情不用管喵。

示例

autoapi 运行示例

它做了什么

  • 协议透传:客户端的请求路径、查询串、请求头、请求体全部原样转发给上游。只有三样东西会被替换 —— base_urlapi_key、请求体顶层的 model 字段。所以 OpenAI 的 /v1/chat/completions 和 Anthropic 的 /v1/messages 都能用,不需要做协议探测喵。
  • 静默转移:失败发生在「字节还没出门」的阶段,所以换候选对客户端完全不可见。流式请求会先探测这条流是不是真的健康,确认吐出足够内容之后才把连接交给客户端喵。
  • 假成功识别:不少中转站在额度不足或后端报错时照样回 200,把真正的错误塞进 body 的 error 字段,或者建了一条 SSE 流却一个字都不吐。这两种都会被判为失败并触发转移喵。

核心概念

虚拟模型 → 候选链

客户端只认虚拟模型这个名字(比如 auto-strong)。每个虚拟模型背后是一条按优先级排好的候选链,链上每个节点是一个真实上游:

虚拟模型 auto-strong
  1. 官方直连    https://api.openai.com      gpt-4o
  2. 中转A       https://relay-a.example.com  gpt-4o
  3. Claude兜底  https://api.anthropic.com    claude-sonnet-4-20250514

每条请求永远从链首开始,跳过正在冻结中的节点,往下找第一个可用的。链首就是你最想用的那个,只有它挂了才降级喵。

规则引擎

一次尝试失败之后,规则列表自上而下匹配,第一条命中的决定动作(和防火墙规则一个语义)。四种动作:

动作 含义
retry 在同一个候选上指数退避重试,次数用尽还失败才换下一个
next 立刻放弃这个候选,换下一个
freeze 全局冻结这个候选一段时间(所有虚拟模型都跳过它),然后换下一个
passthrough 不转移,把上游的响应原样回传给客户端

一条规则都没命中时,走最保守的默认动作 next(换下一个候选)喵。

全局冻结表

冻结是(base_url, api_key, model) 三元组算的,而且是全局的 —— 一个节点被冻上之后,所有引用它的虚拟模型都会跳过它。冻结到期自动失效;节点只要成功一次就立即解冻,因为成功证明它已经恢复了喵。

冻结时长的来源按优先级排:规则正则里捕获到的数字(上游明确说了「几分钟后恢复」时最精准)→ 上游的 Retry-After 响应头 → 规则里写死的 freeze_seconds 兜底值喵。

自动避险

有些节点会持续返回五花八门的错误,每一种都不足以让规则去冻结它,但它显然已经不能用了。自动避险兜住这种情况:一个节点连续失败达到 auto_hedge_threshold 次,就自动冻结 auto_hedge_minutes 分钟。「连续」的意思是中间只要成功过一次,之前攒的次数就清零重新数喵。

和规则的分工:规则看「这一次失败长什么样」来决定这条请求接下来怎么走;自动避险看「这个节点最近一直在失败」来决定接下来一段时间还要不要用它喵。

没有全局兜底链

这一点要明确说清楚:autoapi 没有全局兜底链。候选链是每个虚拟模型专属的,某个虚拟模型的候选链全挂了(或全在冻结中),代理就直接给客户端返回 502,并在错误体的 attempts 字段里列出每个候选各自的失败原因,不会去借用别的虚拟模型的节点喵。

这是刻意的设计:虚拟模型之间的成本、能力、数据流向都可能完全不同,偷偷把请求转到另一个虚拟模型的节点上,比直接失败更糟糕喵。

安装与启动

WIndows请使用Powershell启动!! 需要 Python 3.10 及以上(代码里用了 X | None 这种类型写法)喵。

# 装依赖喵
pip install -r requirements.txt

# 从模板复制出自己的配置喵
cp config.example config.yaml

# 编辑 config.yaml,把里面的 api_key 换成自己的真 key 喵
# 然后启动喵
python main.py

启动之后把客户端的 base_url 指向 http://127.0.0.1:8787model 填配置里的虚拟模型名就行喵。

Windows EXE 发行包

GitHub Actions 会在 master 推送、手动触发和版本 tag(v*)时自动构建 Windows EXE 喵~ 普通构建的 ZIP 可以从 Actions 的 artifact 下载,版本 tag 构建还会自动附加到 GitHub Release 喵~

发行包只包含 autoapi.exe 和公开的 config.example 模板,不会内置 config.yaml 或任何真实 API key 喵~ 下载并解压后,在 EXE 同目录用 PowerShell 创建自己的配置文件喵:

Copy-Item config.example config.yaml

编辑 config.yaml 填入自己的真实 key 后启动代理喵~ 推荐后台运行时关闭交互式 REPL:

.\\autoapi.exe --no-repl -c config.yaml

也可以通过 -c / --config 指向其他位置的配置文件喵~ 配置文件必须由主人自行保管,绝对不要上传到 GitHub 或放进发行包喵~

命令行参数

参数 说明
-c <路径> / --config <路径> 指定配置文件,默认 config.yaml。相对路径会先在当前工作目录找、再在 main.py 所在目录找,所以从任何目录启动都能找到项目里那份配置喵
--no-repl 不启动交互式命令行,适合用 nohup 之类的方式后台运行喵

HTTP 接口

路径 说明
GET /healthz 健康检查,返回 JSON 字段 statusvirtual_modelsfrozen_candidatestotal_requeststotal_exhausted 喵~
GET /v1/models 按 OpenAI 格式列出所有虚拟模型,很多 GUI 客户端启动时会拉这个来填下拉框喵
其余任意路径 通配透传,原样转发给上游喵

配置说明

完整的带注释模板见 config.example,下面按段落讲各字段的含义喵。

server

字段 默认值 说明
host 127.0.0.1 监听地址。强烈建议保持回环地址,理由见下面的安全提醒喵
port 8787 监听端口。这一项改了要重启代理才生效,因为 socket 在启动时就绑好了喵

三个超时的分工

这三项管的是三件完全不同的事,搞清楚了就不会配错喵:

字段 默认值 衡量的是 说明
stall_timeout 60 上游还活着吗 允许上游连续静默多少秒。静默 = 一个字节都没发过来。只要收到任何字节就归零重新数 —— 所以上游发心跳、吐思维链期间都不会触发它。触发了说明连接真的挂死了,判为 stalled_stream
stream_timeout 300 等太久了吗 流式请求的总预算,从发出请求算到「确认这条流健康、可以放行」为止,中途不归零。连接一直健康但正文迟迟不来时靠它兜住,判为 timeout
nonstream_timeout 600 同上 非流式请求的总预算,从发出请求算到整个响应体读完为止,判为 timeout
connect_timeout 15 连得上吗 建立连接的握手超时。连不上要快速失败好换下一个候选,判为 network

两个要点:

  • stream_timeout 只管「放行之前」那一段。 一旦确认流健康、字节开始流向客户端,代理就不再计时了 —— 模型愿意写多久就写多久,绝不会中途掐断一个正在正常输出的回答喵。
  • nonstream_timeout 天生比 stream_timeout 大。 流式是「一点点吐」,几秒内就能判断健不健康,剩下的交给客户端;非流式是「上游把整篇憋完再一次性返回」,在它返回之前什么都看不到,只能一直等喵。

超时和卡流默认都会原地重发 1 次config.examplestatus: timeoutstatus: stalled_stream 那两条规则),重发还不行才降级到下一个候选喵。

按节点单独配超时

上面三项超时都可以在单个候选节点上覆盖,只写想改的那几项,没写的继续跟随全局值:

- name: 慢速推理模型
  base_url: https://relay-c.example.com
  api_key: sk-xxx
  model: o3-deep-research
  stall_timeout: 240        # 这个模型可能安静想 4 分钟
  stream_timeout: 900       # 流式总预算放到 15 分钟
  nonstream_timeout: 1800   # 非流式给到 30 分钟

为什么需要这个:同一条链上常常混着快慢差很多的模型 —— 链首是会先想很久的推理模型、兜底是秒回的小模型。给它们配同一套超时,要么把推理模型冤枉成卡流,要么让小模型挂死太久才降级喵。

在 REPL 里也能改,填 default 就恢复跟随全局值。配了专属超时的节点会在 vm 命令的输出里标出来:

cand set auto-strong 1 stream_timeout 900
cand set auto-strong 1 stream_timeout default

已退役的配置项

老字段 现在改用
request_timeout 拆成了 stream_timeoutnonstream_timeout
first_content_timeout stall_timeoutstream_timeout 接手

first_content_timeout 是从「开始读流」那一刻起算的,只要迟迟等不到正文就判失败 —— 哪怕连接一直活着、上游正在发心跳或吐思维链。推理模型先想一两分钟再吐第一个字是很正常的事,于是就会出现「首字还没出就被自动重试」。现在改成了「静默才算卡」,正在干活的上游不会再被冤枉喵。

配置里如果还留着这两个老字段,代理照旧能正常启动,但会在启动时和每次热重载时各打一条警告,提醒它们已经不生效了喵。

字段 默认值 说明
min_content_chars 10 放行给客户端之前需要先累积够多少个内容字符。不设成 1 是因为「先吐一两个字符然后卡死」的上游会骗过检查 —— 字节一出门就再也换不了候选了。整条流在凑够之前就正常结束(短回答),或者模型只调工具不吐文字,都会照常放行,不会让客户端干等喵
auto_hedge_threshold 5 一个节点连续失败多少次就自动避险。设成 0 表示关闭自动避险喵
auto_hedge_minutes 10 自动避险触发后冻结多少分钟喵
ignored_error_endpoints 默认忽略 POST /v1/messages/count_tokens 按 HTTP method + path 精确匹配的接口列表,仅支持代理路由承接的 POSTPUTPATCHGETDELETE,path 不可包含 ?#。命中后候选内部规则仍执行,但不累计自动避险、不进入目标模式、不输出候选 warning;候选全部失败时只记一条 info,客户端仍收到 502。未配置时使用默认条目;显式写 [] 可关闭默认忽略;填写任意非空列表时会整体替换默认条目,如需保留默认接口请一并写入列表喵
metrics_window_minutes 30 平均耗时统计窗口,单位:分钟。RPM/TPM/平均缓存命中率固定统计最近 60 秒,可用 set metrics_window_minutes <分钟> 修改喵
reload_poll_interval 2.0 配置热重载的轮询间隔,单位:秒。设成 0 表示关闭自动重载喵

virtual_models

key 是客户端请求体里要填的 model 名字,value 是按优先级排好的候选链(列表顺序就是优先级,第 1 位最优先)喵。

virtual_models:
  auto-strong:
    - name: 官方直连                     # 可读名字,日志和 REPL 里显示用,可选喵
      base_url: https://api.openai.com   # 上游根地址,客户端的路径会原样拼在后面喵
      api_key: sk-REPLACE-ME-1           # 该上游的真实 api key 喵
      model: gpt-4o                      # 替换进请求体顶层 model 字段的真实模型名喵
      auth_style: bearer                 # 鉴权头风格,可选,默认 bearer 喵

候选字段说明:

  • 必填:base_urlapi_keymodel
  • 可选:name(不写会自动生成 虚拟模型名#序号)、auth_style
  • auth_style 只能是 bearerAuthorization: Bearer xxx,OpenAI 系和绝大多数中转站)或 x-api-key(Anthropic 系,会自动补 anthropic-version 头)喵

rules

规则是一个列表,顺序即优先级。每条规则由 match(什么情况)和 action(怎么办)组成喵。

match 里可以写两个条件,都写时必须同时满足:

  • status:单个整数 429、整数列表 [500, 502],也可以混合别名 [429, "network"]
  • body_regex:对响应体做正则搜索,忽略大小写。上游一律回 200 或 429、真正的原因只写在 body 里时靠它区分喵

除了真实 HTTP 状态码,status 还支持四个特殊别名:

别名 含义
network 连不上上游 / 握手超时 / 读取时断连
bad_stream 200 但已确定这条流是坏的(明确收到 error 事件,或流结束了却一个字都没有;非流式的「200 里塞 error」也归这里)
stalled_stream 上游静默太久,一个字节都不发了 —— 连接像是挂死了(卡流)
timeout 连接一直活着、上游也一直在发东西,但超过总预算还没拿到足够内容(太慢了)

这几个的区别值得记一下,因为它们对应的处置策略不一样喵:

  • bad_stream已经确定坏了。重发同一个上游大概率还是坏的,换候选更划算。
  • stalled_stream连接挂死了。不代表这个上游整体不行,很可能只是这一次调度倒霉,原地重发一次经常就正常了。
  • timeout连接健康但太慢。也适合重发一次,但因为已经等了很久(默认流式 5 分钟、非流式 10 分钟),再等一整轮的代价很大,所以只重发 1 次就换候选喵。

按动作可用的额外字段:

字段 适用动作 说明
max_attempts retry 同一候选上总共尝试几次(含首次)。至少为 1 喵
backoff_base retry 退避基数,第 n 次重试前等 backoff_base ^ n 秒,单次等待上限 30 秒。至少为 1.0 喵
freeze_from_group freeze body_regex 的第几个捕获组取冻结时长数字喵
freeze_unit freeze 捕获到的数字的单位,seconds / minutes / hours,默认 minutes
freeze_seconds freeze 没能抽取到数字时的兜底冻结秒数,默认 300 喵

例子:上游说「额度用尽,6 分钟后恢复」,就照它说的分钟数精确冻结喵。

rules:
  - match:
      status: 429
      body_regex: 'refreshes?\s+in\s+(\d+)\s+minutes?'
    action: freeze
    freeze_from_group: 1        # 用第 1 个捕获组的数字当时长喵
    freeze_unit: minutes        # 单位是分钟喵
    freeze_seconds: 300         # 万一没抽到数字,退回冻 300 秒喵

一条既没写 status 也没写 body_regex 的规则会匹配一切,属于危险配置,加载阶段会直接被拒喵。

日志与动态负载

成功日志会按请求类型附带性能信息喵:

INFO autoapi.proxy [d3c518] 成功 主力(...)(第 1 次尝试)喵 非流 返回请求耗时=842ms
INFO autoapi.proxy [d3c518] 成功 主力(...)(第 1 次尝试)喵 流 首字=410ms 请求首字=760ms
INFO autoapi.server 流式请求结束 虚拟模型=auto-strong 返回请求耗时=12840ms 正常完成=True usage_tokens=1536 喵
  • 非流式请求的返回请求耗时:从服务端收到客户端请求,到完整上游响应成功取得并准备返回的耗时喵~
  • 流式请求的首字:从本次向上游节点发请求,到节点首次返回任意非空字节的耗时喵~
  • 流式请求的请求首字:从服务端收到客户端请求,到代理确认流健康并开始向客户端转发的耗时喵~
  • 流式请求的返回请求耗时:从服务端收到客户端请求,到上游流自然结束的完整耗时;放行后不会再受 stream_timeout 截断喵~

REPL 底部状态栏会按虚拟模型显示最近 60 秒 的动态 RPM/TPM,以及配置窗口内的平均耗时和平均缓存命中率喵~ RPM/TPM/平均缓存命中率固定统计最近 60 秒,只有平均耗时使用 metrics_window_minutes 配置窗口,默认是 30 分钟,可以用 set metrics_window_minutes <分钟> 修改喵~ RPM 是 60 秒内成功请求数,TPM 只使用上游响应中的 usage token,不会按字符数或本地 tokenizer 估算喵~ 缓存命中率按上游明确上报的缓存读取 Token / 输入 Token 加权计算;OpenAI 使用 usage.prompt_tokens_details.cached_tokens,Anthropic 使用 usage.cache_read_input_tokens,缺少任一必要字段时显示「未完整上报」而不猜测数值喵~ 没有成功请求的虚拟模型(RPM=0)不会显示,节约状态栏空间喵~ 平均耗时只统计配置窗口内的正常完成请求:非流式响应必须完整成功,流式上游必须自然结束;客户端断开、上游读取异常和其他异常结束的流不会进入平均耗时喵~

常见 usage 字段包括 OpenAI 的 usage.total_tokens,或 prompt_tokens + completion_tokens,以及 Anthropic 的 input_tokens + output_tokens 喵~ 上游不返回这些字段时,代理不会擅自修改请求或猜测 token 数喵。

目标模式

目标模式是一个仅当前进程有效的临时保活开关,不会写入 config.yaml 喵~

target status
target on
target off

开启后,某个虚拟模型的候选链整轮失败时,代理不会立即把 502 返回给客户端,而是按 server.target_mode_round_interval_seconds 从链首重新尝试,最多持续 server.target_mode_max_wait_seconds 喵~ 超时后由 server.target_mode_timeout_action 决定结果:return_504(默认)返回 504 和错误类型 target_mode_gateway_timeoutreturn_429 返回 429target_mode_all_unavailablereturn_502 返回 502target_mode_all_unavailabledrop_connection 直接断开连接喵~

  • target on/off/status 只改变当前进程内存状态,重启后自动关闭;target_mode_max_wait_secondstarget_mode_round_interval_secondstarget_mode_timeout_action 是 YAML 配置,需手动编辑后热重载、执行 reload 或重启才会更新喵~
  • 已冻结的节点仍然会跳过,不会为了目标模式反复撞额度限制或绕过自动避险喵~
  • passthrough 仍然立即回传,不会重试客户端自己的明确错误喵~
  • 目标模式会让单个客户端请求最长占用约 target_mode_max_wait_seconds,请只在确实希望尽量不断线时开启喵~

代理跑起来之后,同一个终端里就是一个 REPL(提示符 autoapi> ),可以随时看状态、改配置、清冻结,改完立即生效,不用重启喵。它跑在独立线程里,敲命令不会影响正在转发的请求喵。

所有改配置的命令都走同一套流程:从磁盘重读原始 YAML → 改动 → 完整校验 → 通过才替换内存配置并写回磁盘。校验不通过就打印错误、什么都不改,磁盘上的文件也完全没被动过喵。

查看类

命令 说明 例子
vm 列出所有虚拟模型及其候选链,标注每个节点是可用还是冻结中 vm
rule ls 列出所有规则和它们的序号 rule ls
freeze ls 列出当前所有冻结中的候选、剩余时间和冻结原因 freeze ls
stats 打印代理累计计数,以及候选和虚拟模型的健康、资源与缓存统计,详见下文 stats

stats 会显示:

  • 代理累计请求数 total_requests 和累计用尽整条候选链数 total_exhausted
  • 每个候选的成功/失败/被冻结次数、自动避险次数 hedged_times、当前连续失败数和最近错误
  • 候选与虚拟模型在所有时间、近 6 小时、近 1 小时、近 30 分钟、近 10 分钟窗口内的成功率、请求数、Token 数、平均耗时和平均缓存命中率

stats 的请求数、Token 数和平均耗时按对应统计窗口展示;缺少 usage 或缓存字段时不会猜测,相关指标显示「未完整上报」喵~

虚拟模型

命令 说明 例子
vm add <名字> <候选JSON> 新建虚拟模型,同时配上第一个候选(空链是非法配置,所以必须一起给) vm add my-model {"base_url": "https://y.com", "api_key": "sk-yyy", "model": "gpt-4o-mini"}
vm rm <名字> 删掉一整个虚拟模型(连带它的所有候选)。不能删掉最后一个 vm rm auto-cheap

候选链

命令 说明 例子
cand add <虚拟模型> <候选JSON> 给候选链末尾追加一个候选(也就是优先级最低) cand add auto-strong {"name": "新中转", "base_url": "https://x.com", "api_key": "sk-xxx", "model": "gpt-4o"}
cand rm <虚拟模型> <序号> 删掉指定序号的候选。链里只剩一个时不许删,要整个不要就用 vm rm cand rm auto-strong 2
cand mv <虚拟模型> <原序号> <新序号> 挪动候选位置来调整优先级,第 1 位最优先 cand mv auto-strong 3 1
cand set <虚拟模型> <序号> <字段> <值> 改某个候选的单个字段,位置(优先级)不变。字段可选 base_urlapi_keymodelnameauth_stylestall_timeoutstream_timeoutnonstream_timeout cand set auto-strong 2 api_key sk-new-key-here

cand set 比「删了重加」好用得多:不用把整个候选重新敲一遍,节点在链里的位置也不会变。可修改 base_urlapi_keymodelnameauth_style,以及候选级 stall_timeoutstream_timeoutnonstream_timeout 覆盖。候选级超时填 defaultnone- 可删除覆盖,恢复跟随 server 全局值;改 api_key 时终端不会回显完整的新旧 key,只显示脱敏结果喵~

规则

命令 说明 例子
rule add <JSON> 追加一条规则到列表末尾(优先级最低) rule add {"match": {"status": 429}, "action": "retry", "max_attempts": 3}
rule rm <序号> 删掉指定序号的规则,会把删掉的内容打出来确认 rule rm 3
rule mv <原序号> <新序号> 挪动规则位置来调整匹配优先级 rule mv 5 1

冻结

命令 说明 例子
freeze add <虚拟模型> <模型名或序号> <秒数> 主动冻结某个节点,到期自动恢复。上游要维护、或想临时把流量挪走时用 freeze add auto-strong gpt-4o 600
freeze rm <虚拟模型> <模型名或序号> 解冻指定的那一个节点。节点被自动避险冻上、但你确认它其实已经好了时用 freeze rm auto-strong 2
freeze clear 一把清空所有冻结记录,所有候选立刻可用 freeze clear

节点可以用真实模型名指定(从日志或横幅里直接抄最方便),也可以用 vm 命令里看到的序号(同名节点多时更准)。按模型名匹配到多个时会全部冻上,并额外提示一句喵。

服务器配置

set <字段> <值>server 段的单个数值字段,能改的有 stall_timeoutstream_timeoutnonstream_timeoutconnect_timeoutmin_content_charsauto_hedge_thresholdauto_hedge_minutesreload_poll_intervalport。字段名打错时会把当前允许的完整列表打出来,敲 help 也能看到喵。

除了 port 要重启才生效(socket 启动时就绑好了),其余都立即生效喵。

set auto_hedge_threshold 3
set min_content_chars 20
set stream_timeout 600

敲已经退役的字段名(request_timeoutfirst_content_timeout)会告诉你现在该改哪一项,不会只回一句「不认识」喵。

其他

命令 说明 例子
reload 从磁盘重新加载配置并校验,手改了文件想立刻应用时用 reload
save 校验当前磁盘配置并重新格式化写回,用于确认配置能被正确序列化 save
help 显示完整帮助(也可以敲 ?h help
quit 优雅停机,退出整个代理进程(也可以敲 exitq quit

底部常驻冻结表

提示符下方常驻一条横幅,每秒自动刷新,按 虚拟模型/节点model 的格式列出当前所有被冻结的节点和倒计时:

⚠ 下列模型达到了配额限制或异常自动避险!(2 个)
  auto-strong/gpt-4o             将在 05 分 42 秒 后再次可用
  auto-cheap/gpt-4o-mini         将在 09 分 13 秒 后再次可用

一个节点都没被冻结时显示 ✓ 所有节点可用,所以横幅位置固定、内容不会忽然出现或消失导致上下跳动喵。

横幅由 prompt_toolkit 渲染,刷新时完全不影响正在敲的命令,顺带还有命令历史(上下键)和 Tab 补全。stdin 不是终端时(管道、nohup)会自动退回朴素的 input() 模式,功能照常,只是没有常驻横幅喵。

配置热重载

代理会每隔 reload_poll_interval 秒看一眼 config.yaml 的修改时间,变了就自动重新加载并整体替换内存里的配置 —— 用编辑器改完保存,几秒后就生效,不用重启喵。

几个细节:

  • 新配置有语法错或校验不通过时,保留旧配置继续服务,只打一条错误日志。不会因为编辑器保存了一个写坏的配置就让整个代理停摆喵。
  • 轮询间隔是每一轮现读的,所以改了 reload_poll_interval 当场生效。设成 0 关掉之后热重载任务并不退出,而是转成每 5 秒瞄一眼这个开关,所以在 REPL 里敲 set reload_poll_interval 2 还能重新打开,不需要重启喵。
  • hostport 是例外,它们在启动时就绑好了 socket,改了要重启才生效喵。

已知取舍:REPL 写回会丢掉 YAML 注释

REPL 里那些改配置的命令(rule addcand setsetsave 等)写回文件时用的是 PyYAML,而 PyYAML 不保留注释 —— 所以第一次用这类命令之后,config.yaml 里原本那些中文注释就没了喵。

想保住注释的话,改配置时手改文件、然后靠热重载或 reload 命令生效,不要用 REPL 的写回类命令。config.example 是独立的模板文件,不会被 REPL 碰到,注释一直在喵。

安全提醒

这个代理不校验客户端身份。 任何能访问到这个端口的人,都能免费用掉你所有上游的额度,而且能通过它把请求发到任意上游喵。

所以默认只绑 127.0.0.1host 改成 0.0.0.0 或任何非回环地址,等于把所有上游额度和 key 的使用权免费送人,除非外层已经有防火墙或反向代理鉴权兜着。代理启动时如果发现绑的不是回环地址,会在终端打一条醒目的警告喵。

另外两点:

  • config.yaml 里存着所有上游的真 api key,它已经在 .gitignore了。别把它从忽略列表里拿出来,也别把真 key 提交上去喵。
  • 要分享配置的话改 config.example,那份里面的 key 全是 sk-REPLACE-ME-x 这样的占位符喵。日志和 REPL 里的 key 一律脱敏成 sk-abc***1234 的形式,可以放心贴出来喵。

测试

# 单元测试喵
pytest

# 端到端冒烟测试喵
python smoke_test.py

pytest 跑的是 tests/ 下的单元测试(配置解析、规则匹配、冻结逻辑、流探测这些)喵。

smoke_test.py 不用任何 mock:它起四个真的 HTTP 服务(一个一律返回带恢复时间的 429 的坏上游、一个正常上游、一个吐几个字就挂着不动的卡流上游,以及 autoapi 本体),然后用真的 httpx 客户端打过去,用真 socket 验证故障转移确实发生了、流确实能收到内容喵。

许可证

Apache License 2.0 喵~

Star History

Star History Chart

About

为低价不稳定LLM中转站提供请求级自动重试、熔断与冻结,CLI实时监控,客户端运行时的模型切换等

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages