Skip to content

About

Kiro → Anthropic API 代理(Rust)。相比上游增加:首字超时透明重试、上游静默卡死自愈。多凭据池 / 故障转移 / Web 管理面板。

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

kiro-rs-plus

用 Rust 编写的 Anthropic Claude API 兼容代理,把 Anthropic 格式的请求转换成 Kiro API 请求。

在 hank9999/kiro.rs 的一个 fork 基础上二次开发,主要补充了首字超时重试——解决上游返回 HTTP 200 后静默卡死、现有重试机制完全失效的问题。

Claude Code / Cursor / Cline
        │  Anthropic 协议
        ▼  :8990
   kiro-rs-plus
        │
        ▼
   Kiro API (AWS CodeWhisperer)

本仓库改了什么

新增

功能 说明
首字超时重试 上游返回 200 后长时间不推送内容时,在流内部透明重试。客户端只看到稍慢的正常响应,不是错误
firstTokenTimeoutSecs 首个内容事件等待超时,默认 30 秒,0 关闭
firstTokenMaxRetries 首字超时后的最大重试次数,默认 2
Opus 5 / Sonnet 5 支持 上游 fork 的版本判断靠「包含 4-x」,claude-opus-5 一条都不匹配会被静默降级。改为按版本段精确匹配并补齐映射、模型列表与 thinking 门控
透传兜底 请求的版本比兜底目标新时(如未来的 Opus 4.9 / Opus 6),按 Kiro 命名合成 wire ID 直接交给上游裁决,而不是静默降级到旧模型。Kiro 上新版本通常不用改代码

工程改动

项 说明
包名 kiro-rs → kiro-rs-plus(二进制名仍为 kiro-rs,不破坏 Dockerfile 与 release 流程)
docker-compose.yml 默认改为从源码构建。上游预构建镜像不含本仓库改动,直拉会缺功能
可测性 新增内部 UpstreamReopener trait 作为注入接缝,使重试路径可被测试驱动
测试 新增 7 个测试,用 mock 上游驱动状态机

首字超时重试

问题

上游偶发返回 HTTP 200、建立 SSE 连接,然后一个字节都不推送——没有错误码,没有断连。

此时所有基于状态码的重试机制全部失效:401/403 换凭据、429/5xx 退避,都需要一个状态码才能触发。请求会一直挂着,客户端只能看到 ping 心跳,无法区分「模型在思考」和「上游已经死了」。

做法

重试在流内部完成,对客户端透明:

阶段 行为
请求发出后 HTTP 200 与响应头立即返回,客户端马上看到连接建立
等待首字期间 只发 ping 保活(25 秒间隔,与原行为一致),不发 message_start
超时触发 流内部重新发起上游请求并换掉数据源,此时尚未向客户端写出任何字节
收到首字 补发 message_start,计时器彻底失效
重试用尽 收尾为 stop_reason: error,仍是结构完整的 message

为什么必须延后 message_start——这是协议约束,不是设计偏好。一旦它发出,该 message 就已开启,重试会让客户端收到两个 message。所以「能否重试」的唯一判据是首个字节是否已写给客户端,延后发送正是为了保住这个窗口。

两个容易做错的地方

元数据事件不算首字。 上游首帧通常先来 initialResponseEvent / contextUsageEvent / meteringEvent,这些只是元数据,不代表模型开始生成。若以「收到任意事件」为信号,一个只发元数据然后卡死的上游会被漏过。本实现以 process_kiro_event 返回非空事件为判据(元数据事件返回空 Vec),无需手工枚举事件类型。

重试要重建流上下文。 Prompt cache usage 按凭据统计,而重试经由 call_api_stream → 凭据选择,可能落到另一条凭据上。沿用旧 StreamContext 会把上一条凭据的 cache 数据错记过来。

其他行为

  • 首字之前的读取失败也走重试——客户端尚未收到任何内容,重试严格优于报错。该路径与超时共享同一份重试配额
  • 收到首字后,后续 token 之间的正常停顿(模型思考、工具调用间隙)不受任何约束
  • 重试走 call_api_stream,因此天然经过凭据选择与 cooldown 降权,卡死的凭据更不容易被再次选中
  • 默认 30 秒而非更激进的值:thinking 模型首字前思考十几秒属正常,取值过小会误杀正常请求

测试

用 mock 上游驱动状态机:stream::pending() 精确模拟「200 后永不推送」,并手写合法的 AWS Event Stream 帧(含 prelude / message CRC),走真实解码路径。

测试 验证
first_token_timeout_retries_and_recovers 静默 → 重试 → 拿到内容,message_start 只出现一次
metadata_only_does_not_count_as_first_token 只发元数据帧后卡死,仍触发重试
first_token_retries_exhausted_emits_error_stop 配额用尽后收尾为 error,且补发 message_start(避免无头 message)
no_retry_after_first_token_committed 收到首字后长时间停顿,重开次数为 0
read_error_before_first_token_retries 首字前断流同样重试
reopen_failure_terminates_stream 凭据用尽时立即收尾,不无限重试
timeout_zero_disables_retry timeout=0 完全关闭

已知未验证项

  • 重试时旧连接依赖 drop 释放。在「已发响应头、连接保持、不推数据」场景下,drop 是否立即关闭 TCP 取决于 reqwest 连接池行为,未经验证。最坏情况是每次重试泄漏一个挂死连接,上限为 firstTokenMaxRetries,不会累积
  • 该功能未对真实 Kiro 上游验证。测试覆盖的是状态机逻辑,而非真实网络下的有效性。日志出现 首个内容事件等待超时 的 warn 即为实际触发

已知遗留问题(继承自上游)

cargo test 有 4 个失败,全部位于 src/kiro/provider.rs 的 profileArn 注入测试。原因是实现与测试预期不一致:inject_profile_arn(src/kiro/endpoint/ide.rs)在凭据无 profileArn 时会移除请求体中该字段,而测试断言原样返回。属于行为变更后测试未同步,与本仓库改动无关。


继承的上游 fork 修复

以下来自上游 fork,非本仓库改动:

修复 说明
retry 不再撞同一个凭据 原版 affinity 短路会让失败凭据被反复选回,retry 链路加 exclude_ids 强制跳过
新凭据「雷暴防护」 新凭据 recent_usage=0 会被 LB 判为「最少使用」→ 1 秒内被打 47 次 429。改用现有凭据 recent_usage 中位数作 baseline
周期 balance 刷新 原版 cache 只是启动时快照,改为 10 分钟周期刷新 + 余额不足主动禁用
cache_tracker TTL 对齐上游 原版命中时刷新 expires_at,但 Anthropic 真实 TTL 从首次写入算
credentialRpm: 0 真禁用限流 原版 0 会落回默认 1-2 秒间隔 + 每日 500 上限,与字面意思相反
凭据级 IdP / 代理 / Portal 设置 Admin UI 支持逐条配置 Region、IdC 凭据、HTTP/SOCKS5 代理与 direct 直连
Overages 在线启停 SSE 实时开关,状态从 Web Portal 与 GetUserUsageAndLimits 同步并持久化
Overage-aware 余额 余额与自动禁用使用「基础额度 + 超额额度」,上游未返回 overageEnabled 时不误判
Thinking 兼容增强 opus-4-7/4-8-thinking 走 adaptive thinking;模型名不带 -thinking 时只要请求带 thinking 参数也启用

功能

  • Anthropic API 兼容 — 完整支持 Anthropic Claude API 格式
  • 流式响应 — SSE 流式输出
  • 首字超时重试 — 上游静默卡死自愈
  • 多凭据池 — 按优先级自动故障转移,支持 social / idc / external_idp / API Key
  • Token 自动刷新 — 含后台周期刷新,避免请求时才刷造成延迟
  • 凭据回写 — 多凭据格式下自动回写刷新后的 Token
  • 请求体压缩 — 规避上游约 5MiB 请求体上限
  • 凭据冷却 — 分类冷却与自动恢复
  • 凭据亲和 — 连续对话尽量固定同一凭据
  • Thinking 模式 — 支持 extended thinking,按模型版本自动选 adaptive / enabled
  • 透传兜底 — Kiro 上新模型版本时无需改代码,也不会静默降级
  • 工具调用 — 完整支持 function calling / tool use,含截断检测与软恢复
  • 图片处理 — token 计算与自动缩放
  • WebSearch — 内置转换逻辑
  • Admin 管理 — Web 面板与 API,含余额查询、Overages 启停、凭据级配置

不支持 OpenAI 格式(/v1/chat/completions)。仅 Anthropic 协议,Claude Code / Cline 可直接用;需要 OpenAI 接口的客户端(如 Codex)用不了。


快速开始

Docker(推荐)

git clone https://github.com/zenitlab/kiro-rs-plus.git
cd kiro-rs-plus

mkdir -p config
# 准备 config/config.json 与 config/credentials.json(见下方配置)

docker compose up -d
docker compose logs -f

容器内 host 必须是 0.0.0.0,配 127.0.0.1 只绑容器内环回,端口映射不通。

从源码编译

需要 Rust 1.93+(edition 2024)与 Node.js + pnpm。

# 先构建前端,否则 rust-embed 会因缺少 admin-ui/dist 而编译失败
cd admin-ui && pnpm install --frozen-lockfile && pnpm build && cd ..

cargo build --release
./target/release/kiro-rs -c config/config.json --credentials config/credentials.json

最小配置

config/config.json:

{
  "host": "0.0.0.0",
  "port": 8990,
  "apiKey": "sk-your-own-key",
  "region": "us-east-1"
}

config/credentials.json:

{
  "refreshToken": "你的 refreshToken",
  "authMethod": "social"
}

客户端接入

export ANTHROPIC_BASE_URL=http://127.0.0.1:8990
export ANTHROPIC_AUTH_TOKEN=sk-your-own-key

URL 填到根即可,Claude Code 会自己拼 /v1/messages。


配置

config.json

字段 类型 默认值 描述
host string 127.0.0.1 服务监听地址(容器内用 0.0.0.0)
port number 8080 服务监听端口
apiKey string - 客户端认证用的 API Key(必配)
region string us-east-1 AWS 区域
authRegion string - Token 刷新用,未配置回退到 region
apiRegion string - API 请求用,未配置回退到 region
firstTokenTimeoutSecs number 30 首字等待超时(秒),0 关闭
firstTokenMaxRetries number 2 首字超时最大重试次数
adminApiKey string - 配置后启用 Admin API 与 Web 面板
credentialRpm number - 单凭据目标 RPM,0 或未配置用内置策略
tlsBackend string rustls rustls 或 native-tls
proxyUrl string - HTTP/SOCKS5 代理地址
proxyUsername / proxyPassword string - 代理认证
machineId string - 自定义机器码(64 位十六进制),不填自动生成
kiroVersion string 0.10.0 Kiro 版本号
systemVersion string 随机 系统版本标识
nodeVersion string 22.21.1 Node.js 版本标识
countTokensApiUrl string - 外部 count_tokens API 地址
countTokensApiKey string - 外部 count_tokens API 密钥
countTokensAuthType string x-api-key x-api-key 或 bearer
promptCacheTtlSeconds number 300 本地 Prompt Cache TTL(秒)
promptCacheAccountingEnabled boolean true 关闭后不再输出或扣减 cache token

credentials.json

字段 类型 描述
refreshToken string OAuth 刷新令牌
authMethod string social / idc / external_idp / api_key
kiroApiKey string Kiro API Key(ksk_ 开头),设置后直接作为 Bearer Token,无需刷新
accessToken string 可选,可自动刷新
profileArn string 可选,登录时返回
expiresAt string Token 过期时间(RFC3339)
clientId / clientSecret string IdC 认证必填
priority number 数字越小越优先,默认 0
region / authRegion / apiRegion string 凭据级 Region
machineId string 凭据级机器码,不填自动派生
email string 可选,从 API 获取
proxyUrl string 凭据级代理,direct 表示显式直连
proxyUsername / proxyPassword string 凭据级代理认证
disabled boolean 是否禁用该凭据
overageEnabled boolean 是否开启 Overages,自动同步
overageCap number Overages 超额上限

IdC / Builder-ID / IAM 属于同一种登录方式,统一用 authMethod: "idc"(builder-id / iam 仍可识别,按 idc 处理)。

多凭据

[
  {
    "refreshToken": "第一个凭据",
    "authMethod": "social",
    "priority": 0
  },
  {
    "refreshToken": "第二个凭据",
    "authMethod": "idc",
    "clientId": "xxx",
    "clientSecret": "xxx",
    "region": "us-east-2",
    "priority": 1,
    "proxyUrl": "socks5://proxy.example.com:1080"
  },
  {
    "kiroApiKey": "ksk_xxxxxxxx",
    "authMethod": "api_key",
    "priority": 2
  }
]
  • 按 priority 排序,数字越小越优先
  • 自动故障转移到下一个可用凭据
  • Token 刷新后自动回写源文件
  • Admin UI 的修改也会回写

同一个 refreshToken 不能同时用在两处。 Kiro 每次刷新都会作废旧 token,谁先刷谁把对方打死,另一边会 401。kiroApiKey 没这个问题,因为它不刷新。

环境变量

变量 说明
KIRO_API_KEY 设置后自动追加一条最高优先级的 API Key 凭据
CONFIG_PATH 配置文件路径

API 端点

端点 方法 描述
/v1/models GET 可用模型列表
/v1/messages POST 创建消息
/v1/messages/count_tokens POST 估算 Token 数量

模型映射

GET /v1/models 返回固定的能力列表,不按单个凭据区分。

请求的模型名先剥离 -thinking / -agentic 后缀,再按子串匹配(map_model,src/anthropic/converter.rs):

请求模型名包含 映射到 Kiro 模型
opus + 版本段 5 claude-opus-5
opus + 4-5/4.5 claude-opus-4.5
opus + 4-7/4.7 claude-opus-4.7
opus + 4-8/4.8 claude-opus-4.8
opus + 版本 > 4.6 合成后透传(见下)
opus(其他) claude-opus-4.6
sonnet + 版本段 5 claude-sonnet-5
sonnet + 4-6/4.6 claude-sonnet-4.6
sonnet + 版本 > 4.5 合成后透传
sonnet(其他) claude-sonnet-4.5
haiku + 版本 > 4.5 合成后透传
haiku(其他) claude-haiku-4.5

模型名不含 sonnet / opus / haiku 时返回 UnsupportedModel 错误,不做兜底。

「版本段 5」不是 contains("5")。 5 代上游 ID 没有小版本号(claude-opus-5),而 4.5 代写作 claude-opus-4-5——两者都含字符 5。所以判据是取系列名之后的第一段版本号,要求它恰好等于 5,这样 claude-opus-4-5-20251101 与 claude-3-5-sonnet 都不会被误判成 5 代。这条边界有专门的回归测试 test_map_model_version_5_does_not_swallow_4_5。

透传兜底

参照 kiro-gateway 的设计原则——网关不做守门员,Kiro API 才是最终裁判。显式分支没命中时不直接降级,先判断版本:

请求 结果 理由
claude-opus-6 透传 claude-opus-6 比兜底目标 4.6 新
claude-opus-4-9 透传 claude-opus-4.9 同上,合成时按 Kiro 习惯用 . 分隔
claude-haiku-5 透传 claude-haiku-5 比兜底目标 4.5 新
claude-opus-4-1 降级 claude-opus-4.6 比兜底目标旧,Kiro 无此型号
claude-opus-4-20250514 降级 claude-opus-4.6 8 位数字是日期戳不是版本号,解析为 (4, 无)
claude-3-5-sonnet-20241022 降级 claude-sonnet-4.5 系列名之前的数字不算版本号

判据是「比兜底目标新」而非「比已知最新版本新」,因为要回答的问题是「降级会不会把用户请求的模型变旧」。若拿 5 当门槛,claude-opus-4-9 按 (4,9) < (5,0) 会被判为无需透传,然后落进 else 静默降级成 4.6——正是要避免的情况。

透传合成的是 Kiro 格式的 ID,不是原样转发客户端传来的名字:客户端可能用 Anthropic 的日期戳命名(claude-opus-6-20270101),上游不认这种写法。

上游若不认合成出来的 ID,表现为请求报错,而不是悄悄换成旧模型——这正是设计意图,错得明显比错得安静好。

对齐 Kiro 在售模型

Kiro 官方当前提供的 Claude 模型(kiro.dev/docs/models,倍率以 Auto = 1.0x 为基准):

Kiro 模型 上下文 倍率 本代理
Claude Opus 5 1M 2.2x 支持
Claude Opus 4.8 1M 2.2x 支持
Claude Opus 4.7 1M 2.2x 支持
Claude Opus 4.6 1M 2.2x 支持
Claude Opus 4.5 200K 2.2x 支持
Claude Sonnet 5 1M 1.3x 支持
Claude Sonnet 4.6 1M 1.3x 支持
Claude Sonnet 4.5 200K 1.3x 支持
Claude Sonnet 4.0 200K 1.3x 归并到 claude-sonnet-4.5
Claude Haiku 4.5 200K 0.4x 支持

Opus 4.7 及更新版本(4.8 / 5,以及未来透传出来的型号)只接受 adaptive thinking,不能用 enabled + budget_tokens 手动预算,映射后自动切到 adaptive + high effort。这条门控按映射结果的版本号判断,所以透传出来的 Opus 6 会自动落到正确路径,不用再改一次代码。Sonnet 全系保留 enabled + budget 兼容路径。

Kiro 后续新增版本时:日期戳变体(claude-opus-5-20270101)本来就归一化,新小版本与新大版本走透传兜底,两种情况都不用改代码。只有想让某个新版本走专门的处理(比如新的 thinking 形态、不同的上下文上限)才需要加显式分支。

Opus 5 / Sonnet 5 的上游 modelId 字面量未在真实账号上验证过。 claude-opus-5 / claude-sonnet-5 取自 Kiro 官方 changelog 与第三方 Kiro 客户端实测的 wire ID,但本仓库的 20 个测试凭据均指向同一个被封的 AWS 账号,无法发真请求确认。若上游拒绝该 ID,表现为请求返回错误而非降级。

Kiro 同时提供 GPT-5.6 Sol / Terra / Luna、GLM-5、DeepSeek 等非 Claude 模型,本代理不路由。Claude 全系 US / EU 双区可用,实际区域跟随凭据的 profile region。


Admin(可选)

配置 adminApiKey 后启用。Web 面板在 /admin(不是根路径)。

主要能力:凭据列表与详情、逐条编辑 Region / IdC / 代理、余额查询、Overages SSE 实时启停、失败计数重置。

主要接口:

端点 方法 描述
/api/admin/credentials GET / POST 获取全部凭据 / 添加凭据
/api/admin/credentials/:id DELETE 删除凭据
/api/admin/credentials/:id/disabled POST 启用 / 禁用
/api/admin/credentials/:id/priority POST 设置优先级
/api/admin/credentials/:id/reset POST 重置失败计数
/api/admin/credentials/:id/refresh POST 强制刷新 Token
/api/admin/credentials/:id/balance GET 实时余额并更新缓存
/api/admin/credentials/balances/cached GET 全部缓存余额
/api/admin/credentials/:id/overage/enable POST 开启 Overages(SSE)
/api/admin/credentials/:id/portal-settings POST 更新凭据级配置

认证同 API:x-api-key 或 Authorization: Bearer。

Admin 权限可启停凭据、改优先级、查余额。若绑定 0.0.0.0 且映射端口,同局域网设备均可访问——不放心就把端口映射改成 127.0.0.1:8990:8990。


注意事项

  1. 凭证安全 — credentials.json 已在 .gitignore 中,不要提交
  2. TLS 后端 — 遇到无法刷新 token 或直接返回 error request,尝试把 tlsBackend 切成 native-tls
  3. Write Failed / 会话卡死 — 通常与输出过长被截断有关,可调低输出 token 上限,参考上游 #22 与 #49

免责声明

本项目仅供研究使用,Use at your own risk,使用本项目所导致的任何后果由使用人承担,与本项目无关。

本项目与 AWS / Kiro / Anthropic / Claude 等官方无关,不代表官方立场。


技术栈

Rust + Tokio + Axum + reqwest + rustls,前端 React + Vite + Tailwind,通过 rust-embed 打进单个二进制。


License

MIT,见 LICENSE。原始版权归 hank9999 and contributors。

致谢

地基由 hank9999/kiro.rs 原作者打就,向原作者致敬。上游 fork 贡献了负载均衡与缓存修复。

上游实现参考过以下项目,一并致谢:

About

Kiro → Anthropic API 代理(Rust)。相比上游增加:首字超时透明重试、上游静默卡死自愈。多凭据池 / 故障转移 / Web 管理面板。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages