shaw
d19265ddb2
fix(gateway): 拒绝含 OpenAI 网关 OAuth 的 composite 歧义路由,防止凭据误入 Anthropic 管线
...
composite 分组中 openai OAuth 账号与 deepseek API Key 账号同时显式映射同一
gpt 模型时,模型归属判为歧义、路由层不解析平台,请求落到默认 Anthropic
GatewayHandler;handler 再按模型名前缀猜回 openai 并选中 OpenAI OAuth 账号,
ChatGPT token 被发往 api.anthropic.com,上游 401 "Invalid bearer token"
经 failover 逐号打成临时不可调度,单请求即可扫空号池。
- 归属记录声明方是否含 OpenAI 网关族 OAuth / setup-token;当模型名兜底也
落在 OpenAI 网关族时判为配置错误,路由中间件与 WS 入口直接 400 拒绝并
提示配置显式路由。纯 API Key 歧义及 Anthropic/Antigravity/Gemini OAuth
歧义保持原行为。
- 模型归属改按配置计算(ListModelAvailabilityCandidates),不再随限流 /
临时不可调度在「唯一归属」与「歧义」之间翻转。
- Anthropic 请求构建前校验凭据:非 Anthropic 网关族的 OAuth 凭据不出站,
返回仅换号、不处罚账号的 failover;count_tokens 返回 404 不支持。
API Key 不受限(上游可能兼容多种协议格式)。
2026-10-15 14:57:03 +08:00
Wesley Liddick
53c21fed25
Merge pull request #8006 from feeeei/feat/gemini-explicit-cache
...
feat(gemini): 支持显式上下文缓存,并完善 Vertex 与第三方 API Key 账号
2026-10-13 14:51:11 +08:00
feeeei
43a3c197cb
fix(gemini): 显式缓存恢复流程区分账号已删除与暂不支持缓存,上游必已过期后作废意图
...
后台回滚在持有账号返回 nil 时把修改意图当作已回滚清除:账号临时关闭显式缓存能力后
意图丢失,重新开启也无法补偿。
- boundAccount 区分两种情形:账号已删除返回 nil,账号存在但不支持显式缓存返回
errGeminiCachedContentAccountUnsupported;BoundAccount 对外行为不变
- 账号暂不支持缓存时不改动上游、保留意图,租约过期后重试(期间同一缓存的修改返回 409);
账号已删除时没有凭据可回滚上游,软删本地记录
- 修改前到期时间加上单次最长有效期与 1 小时容忍都已过去时上游缓存必已自行过期:
账号不支持缓存或上游回滚持续失败的意图不再重试,直接作废本地记录
- 测试夹具 GetForOwner 过滤已软删记录
2026-10-13 00:54:27 +08:00
feeeei
8b74741efe
fix(groups): 广场 Vary 保留 Origin,分组译文语言代码统一大小写
...
- 模型广场以追加方式写入 Vary: Accept-Language,保留 CORS 中间件为具体来源
写入的 Vary: Origin
- 管理端写入分组译文时语言代码统一为 BCP 47 惯用大小写(en、zh-TW),
统一后重复的语言代码(如 en 与 EN)返回 400 INVALID_GROUP_I18N
- 用户侧按语言取译文时规范写法的键优先,仅大小写不同的多个键按字节序取最小者,
同一请求语言的结果固定
2026-10-12 18:09:03 +08:00
feeeei
26b31a83e4
fix(gemini): 显式缓存修改串行化、失败回滚,确定金额费用按 Key / 订阅预留
...
同一缓存的并发 PATCH 可能以与上游提交不同的顺序写回本地到期时间;
确定金额的存储费只预留用户余额,并发续期可越过 API Key 总额度与订阅额度;
上游已修改而本地写回失败时没有可恢复的状态。
- 新增迁移 245:缓存记录增加修改意图(mutation_token / mutation_lease_until)。
PATCH 在调用上游前登记意图并取得 2 分钟租约,条件为没有进行中的修改且到期时间
未变;上游修改、本地写回与计费均由持有者完成,并发的修改返回 409 ABORTED
- 上游结果未知、本地写回失败或进程退出时修改意图保留且不计费;租约过期后由下一次
修改或后台每分钟的恢复流程把上游有效期回滚到修改前的到期时间(已到期则删除上游
缓存与本地记录)。上游明确拒绝或请求未发出时直接清除意图
- 修改 / 删除上游缓存的单次调用超时改为 30 秒;请求发出前的失败包装为
ErrGeminiCachedContentRequestNotSent
- CheckChargeAffordable 与 ReserveInflightCharge 合并为 ReserveFixedCharge:在途预留
新增订阅、API Key 两个维度(Redis billing:inflight_scope:*),先登记本笔预留再读取
库中权威用量(Key quota_used 与限速窗口、订阅日 / 周 / 月用量)叠加同维度在途合计
校验;预留随计费任务交接,落库后释放
- GetRateLimitData 一并读取 quota_used
2026-10-12 17:14:56 +08:00
feeeei
daebb7a80c
fix(gemini): 模型列表与模型详情接口支持 Agent Platform (Vertex) 账号
...
Gemini 分组对外提供 AI Studio 兼容协议,分组内账号不一定是 AI Studio。
分组只剩 Vertex 服务账号时,GET /v1beta/models 与 /v1beta/models/{model}
选中服务账号后报 502 unsupported account type: service_account。
- SelectAccountForAIStudioEndpoints 改名 SelectAccountForModelsEndpoints,
Vertex 账号排在所有 AI Studio 兼容账号之后参与选号
- ForwardAIStudioGET 拆为 ForwardListModels / ForwardGetModel,按账号类型分流:
AI Studio 兼容账号原样转发;Vertex 服务账号读取 Google 发布方模型列表并
转换为 AI Studio 格式,模型详情在该模型所用区域的列表中查找;Vertex API Key
账号返回内置列表
- Vertex 发布方模型分页读取抽成 listVertexGeminiPublisherModels,与后台
同步上游模型共用
- 模型接口响应体上限改用 gateway.models_list_read_max_bytes,超出时报错
2026-10-11 22:03:36 +08:00
feeeei
05d7946af8
fix(gemini): 显式缓存错误透传收窄到上游网关账号,延长沿用创建时计费口径
...
- 绑定缓存的请求与缓存管理接口收到 429 / 5xx 时,只有上游网关账号的响应原样回写
(UpstreamFailoverError.BoundUpstreamPassthrough / GeminiCachedContentUpstreamError.UpstreamGateway),
官方账号仍走错误透传规则与通用映射,不向客户端暴露配额明细
- 记录新增 channel_usage(迁移 244)保存创建时的渠道计费口径,PATCH 延长按同一口径预检、报价与记账
- 创建 / 延长费用行的 request_id 改为固定前缀 gcache:{create|patch}:{id},不被客户端或网关请求 id 覆盖,
按缓存 ID 即可检索一个缓存的全部费用行
- 调度快照凭据投影补齐 base_url / auth_mode / 上游显式缓存声明,显式缓存候选过滤一次算全,
不再逐个选号后剔除
2026-10-11 01:37:04 +08:00
feeeei
3abd646582
fix(ops): Gemini 原生路由的网关本地错误按错误类型归类
...
Google 格式错误体没有 type 字段,ops 一律记为 api_error,客户端参数错误、
引用不存在的缓存等都被归为平台内部错误。
- googleError 写出时按状态码向 ops 登记错误类型:400/413 invalid_request_error、
401 authentication_error、403 permission_error、404 not_found_error,
429 与 5xx 不登记
- 计费、并发与风控拦截沿用 Claude / OpenAI 格式错误的类型与错误码,归类口径一致
- 存储价未配置、显式缓存未启用改用 googlePlatformError,仍按平台错误统计;
错误透传规则改写的上游错误不登记本地类型
- 上游返回的缓存不存在标记为请求级失败:按客户端请求错误分类,
上游原始错误保留为明细但不参与上游归因
2026-10-10 23:37:48 +08:00
feeeei
d5a802f84a
feat(gemini): 支持显式上下文缓存(AI Studio / Vertex / 自建网关上游)
...
新增 /v1beta/cachedContents 的创建、列表、查询、更新有效期与删除接口。
参与账号:官方地址的 AI Studio API Key 账号、Vertex service account 账号,
以及开启"上游支持显式缓存"的自定义地址 API Key 账号。
- 网关自发缓存 ID,绑定上游资源名、持有账号与创建它的 API Key
(迁移 243 gemini_cached_contents);list/get 只读本地记录
- 引用缓存的 generateContent / streamGenerateContent / countTokens
改写为上游资源名,只发往持有缓存的账号,不参与会话粘性、不换号;
拒绝重复键与 snake_case 写法;在持有账号上重试耗尽后,
上游 429 / 5xx 原样透传,401 / 403 映射为 502
- 计费:创建时一次性收取缓存 token 输入费与整个有效期的存储费;
延长按库中实际推进的时长收费,纯存储费不算一次请求;缩短与删除不退费
- 存储单价随计费模型取价,来自定价覆盖文件字段
cache_storage_cost_per_token_per_hour,未配置的模型拒绝创建;
存储费同时计入账号统计成本,渠道按 token 重算成本时叠加且不重复计入
- 扣费前校验:创建在上游建成后按确定金额校验余额 / 订阅额度 /
API Key 额度并登记不封顶的在途预留,不足时删除上游缓存、只收输入费;
延长在调用上游前按增量时长校验
- 有效期上限 gateway.gemini_cached_content_max_ttl_seconds,默认 86400,
有效期向上取整到秒;创建请求保留数字原文、兼容 snake_case 元数据字段
- 上游调用与落库同客户端连接脱钩;2xx 不可用响应不换号并删除已建缓存
- 缓存不存在:AI Studio 的 403、Vertex 生成请求的 400 与缓存资源接口的
404 统一识别;原生转发(含流式)不重试、不换号、不改账号状态,
向客户端返回标准 403,不暴露上游缓存资源 ID,上游原始错误登记到 ops;
PATCH 遇上游 404 移除本地记录并返回 403,DELETE 视为成功
- 自建网关上游:账号开关 explicit_cache_upstream,缓存接口发往账号自身
base_url;可选 explicit_cache_upstream_max_ttl_seconds 对齐上游 TTL 上限,
显式 TTL 超限时创建跳过该账号(全部超限返回 400),未显式时按上限创建,
PATCH 取本站与账号上限的较小值;绑定缓存的请求在网关账号上收到
429 / 5xx 时不重试、不限流该账号;前端创建 / 编辑账号弹窗新增开关与上限输入框
- 用量与 ops 端点统计:带缓存 ID 的路径归一化为 /v1beta/cachedContents,
缓存操作的上游端点记为 /v1beta/cachedContents
2026-10-10 23:37:47 +08:00
feeeei
9a51333101
feat(groups): 分组名称与描述支持多语言
...
- groups 新增 i18n JSONB 列(迁移 242):语言代码 -> {name, description},只存译文;
name/description 原列语义不变,仍用于唯一性校验、搜索与各类日志
- 用户侧接口由后端按请求语言输出:取 Accept-Language 中权重最高的一项,先按完整
语言代码、再按主语言匹配;该语言有译文的字段换成译文,否则保持原字段,且不再带出
译文表。覆盖密钥、可用分组、订阅(含进度与汇总)、兑换、使用记录、可用渠道、
模型广场、套餐列表
- 管理端接口一律返回原字段;分组管理接口额外带 i18n 供编辑
- 创建/编辑分组:名称、描述的标题行右侧新增语言下拉,同一个输入框在默认内容与各语言
版本之间切换;语种跟随前端 availableLocales
- 管理端写入:创建/更新接受 i18n(更新时缺省不修改,传入则整体替换),校验语言代码
形态与名称长度并丢弃空译文;简易模式同样放行;复制分组只带走描述译文
- 切换界面语言后整页刷新,让已加载的数据换成新语言;模型广场响应加
Vary: Accept-Language
- 网关路径,以及只带 group_name 字符串的统计、错误记录出口不做本地化
2026-10-09 11:39:18 +08:00
SavitarC
9264414612
fix(provider): 修复 Claude 计费与钱包冷却并隔离模型目录缓存
2026-10-09 10:11:06 +08:00
SavitarC and Claude Opus 5.5
c556aee8fd
refactor(platform): 多协议供应商的转发与探测判断改查 profile
...
平台清单之外,后端还有几处用"国产厂商"或"国产厂商 ∪ OpenCode"表示
多协议供应商,新登记的供应商会被漏掉(例如上游模型同步直接报不支持)。
- 适用于全部多协议供应商的判断改用 IsMultiProtocolAPIKey:上游模型同步与
模型目录基址、Anthropic count_tokens 本地估算、HTML 403 豁免与累计计数、Claude 型号
计费候选过滤;
- 新增 RoutesProtocolByInbound(profile 分流方式为按入站协议),替换原来
只针对国产厂商、实际描述"按入站协议分流"的判断:Responses 直转回退、
Responses 能力标记(含创建 / 更新后的异步探测调度)、上游计费探测的
adaptive 基址、Anthropic API Key 鉴权方式、OpenAI 输入 token 本地估算。
按模型分流的供应商(OpenCode)仍走 routesByModel 分支。
依赖各家额度 / 余额接口的逻辑(Coding Plan 窗口、余额不足冷却等)仍按平台
显式判断。现有平台上两组判断与改动前逐一相同(测试覆盖全部平台与非法值);
另测试临时登记的新供应商按分流方式分别获得国产厂商与 OpenCode 的行为。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-10-09 10:10:33 +08:00
SavitarC and Claude Opus 5.5
3b89cc08dd
refactor(platform): 后端平台列表改由平台清单派生
...
新增 domain/platforms.go 作为具体平台的单一数据来源:每个平台一条登记,
写明展示名、所走网关(anthropic / openai / gemini / antigravity)、是否国产
单一厂商、定价目录 provider 名,顺序即展示顺序。
以下列表与判断改为查平台清单:
- "全部具体平台":用户平台额度白名单、错误透传规则平台列表、组合路由目标
校验、上游计费探测资格、调度快照遍历、组合分组的定价 / 模型映射回退、
组合分组默认模型列表;
- "OpenAI 网关族":IsOpenAICompatible、NormalizeOpenAICompatiblePlatform、
Responses 兼容路由判断、count_tokens 分发、组合分组的 OpenAI 文本目标;
- 国产厂商判断、渠道定价同步的 provider 映射;
- 多协议 API Key 供应商相关的请求头覆盖资格、凭证测试、OpenAI 基址回退改用
IsMultiProtocolAPIKeyProvider;
- 分组平台与组合路由目标的请求校验改为自定义校验标签 group_platform /
concrete_platform,拒绝规则不变(大小写、首尾空格、别名一律拒绝)。
组合分组的逐平台回退是先命中者生效,CompositePrecedencePlatformIDs 保持
原有顺序 anthropic、gemini、openai,其余按登记顺序追加。依赖具体实现的
能力(OAuth、渠道监控、调度阈值、余额 / 用量窗口、Codex 模型目录等)仍在
各自代码中显式列出。
等价性测试把改动前的每个列表与 switch 原样保留为基准,逐一比较派生结果
(含顺序),谓词覆盖全部平台、composite 与非法值 × 全部账号类型。
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-10-09 10:10:33 +08:00
Wesley Liddick
8e2c4fad32
Merge pull request #7793 from wucm667/fix/issue-7784-composite-route-model-list
...
fix(models): list composite exact route aliases
2026-10-08 11:39:51 +08:00
Wesley Liddick
bc63bd2874
Merge pull request #7782 from lyy0709/codex/fix-remote-model-catalog
...
fix(models): enforce account mappings for remote Codex discovery
2026-10-08 11:33:27 +08:00
Wesley Liddick
3a7fd4ae78
Merge pull request #7837 from yJader/fix/antigravity-account-test-models
...
fix: use account mapping keys for Antigravity connectivity tests
2026-10-08 11:32:59 +08:00
Wesley Liddick
6db4171cfb
Merge pull request #7779 from SPHINX998/fix/ws-turn-group-pricing
...
fix(openai-ws): bill each later turn with the current group pricing
2026-10-08 11:25:10 +08:00
yJader
856d7ea905
fix: expose mapped antigravity test models
2026-10-03 20:56:49 +08:00
wucm667
5800abf1cb
fix(models): preserve composite catalog isolation after base update
2026-10-03 09:11:53 +08:00
Wesley Liddick
2529371bab
Merge pull request #7676 from william-drakemond/fix/email-code-token-atomic
...
fix(email): 验证码尝试次数原子化 + 重置密码 token 哈希存储且原子单次消费
2026-10-02 14:32:42 +08:00
Wesley Liddick
5637f469fa
Merge pull request #7802 from feeeei/feat/recharge-promotion-tiers
...
feat(payment): 充值优惠阶梯,支持赠金与折扣两种模式
2026-10-02 14:30:29 +08:00
shaw
9eb394e3e9
fix(typesafe): 堵住校验解析差异与审计盲区,收紧模型列表口径
...
- System One 校验拒绝重复键与大小写/Unicode 折叠变体键(请求顶层、
questions、问题对象):encoding/json 大小写不敏感且取最后一个重复键,
而原始 body 原样转发,此前 {"model":"x","MODEL":"jev-latest"} 可绕过
jev-latest 限制、模型白名单与流式限制。
- Prompt 审计与旧审核同时收集对象键名、问题 ID 与未知扩展字段(不含规范
字段名与已校验的 type),此前把内容放进键名即可完全绕过审计。
- 上游成功响应的 usage/model 宽松解码(浮点、数字字符串),避免上游已
计费而网关整体 502 不记账;解码失败记录 response_error ops 事件。
- 上游 413 与 400/422 同视为请求错误:透传状态码、不切换、不改账号状态。
- TypeSafe base_url 末尾的 /v1 自动剥离,避免拼出 /v1/v1/systemone;
/v1/systemone 改挂文本请求体上限。
- Composite 静态兜底模型列表、管理端候选列表恢复为不含 jev-latest;
Codex 清单永不列出 TypeSafe 模型;TypeSafe 分组候选默认 jev-latest。
- 创建账号从 Grok 切到 TypeSafe 时重置为白名单模式;错误透传平台列表补 typesafe。
2026-10-02 13:51:58 +08:00
shaw
2d8a386811
fix(typesafe): 补齐平台迁移、审计覆盖、端点隔离与错误策略
...
- 新增 241 迁移:user_platform_quotas / composite_model_routes 的 CHECK 约束
加入 typesafe。此前设置 typesafe 默认配额会让注册时的多行配额快照整体
违约(fail-open 后新用户所有平台配额丢失),单用户配额与 Composite 路由保存 500。
- Prompt 审计新增 typesafe_systemone 协议提取(state + 各问题 instructions/
criteria/选项标签,键排序保证哈希稳定);此前提取为空,阻断与异步审计均被放行。
旧内容审核同样覆盖问题文本,且不再丢弃含 <system-reminder> 的 System One 文本。
- TypeSafe 分组及路由到 TypeSafe 的 Composite 请求访问 Messages / count_tokens /
Chat Completions / Responses 时返回 404,避免以 x-api-key 打到错误上游路径并
污染账号状态;TypeSafe 账号 base_url 为空时不再回退 api.anthropic.com。
- SystemOne 补在途余额预留、利润控制准入终检、wrapReleaseOnDone,失败切换改用
共享 FailoverState(同账号重试 / 池模式 / 临时封禁)。
- 上游错误沿用共享账号错误策略:402/403、自定义错误码、临时不可调度规则生效,
记录 ops 上游错误;400/422 仍不切换且永不改变账号状态。
- 账号测试改走原生 System One 探测,可用模型只返回 jev-latest;
响应超限显式报错,响应 Content-Type 仅透传 JSON 类型。
2026-10-02 13:08:40 +08:00
aofee
7225538358
merge: sync Jev support with latest upstream main
2026-10-02 09:37:30 +08:00
feeeei
1aa34d478d
feat(payment): 充值优惠阶梯支持折扣模式,快捷金额角标改为促销价签
...
- 新增全局模式开关 RECHARGE_BONUS_MODE(bonus / discount),接入支付配置、后台设置接口与 checkout-info
- 折扣模式:到账不变,实付基数按百分比打折(按币种精度取整),手续费 / 每日限额 / 渠道选实例沿用折后基数;
百分比须 < 100,运行时 ≥ 100 按无优惠处理
- 充值页角标改为单行红色价签(赠金「+20%」/ 折扣「20% OFF」),第二行按模式显示到账或折后实付;
摘要卡折扣模式显示优惠行,渠道限额校验改用折后实付
- 后台「充值优惠阶梯」拆为独立卡片:赠金 / 折扣切换、紧凑档位表格、区间预览、活动文案
- 补前后端单测与 zh/en 文案
2026-10-02 00:25:41 +08:00
feeeei
1b1039f4fd
feat(payment): 充值赠送阶梯(按金额档位额外赠送到账额度)
...
- 后台支付设置新增「充值赠送阶梯」(满 X 送 Y%,RECHARGE_BONUS_TIERS)与 Markdown 活动文案
(RECHARGE_BONUS_NOTICE):阈值按用户输入的支付金额命中(取不超过金额的最大档),
赠送按到账基数(输入 × 充值倍率)计算;严格校验写入、宽松解析读取,订阅订单不参与
- 下单时按当时配置计算赠送并落库:payment_orders 新增 bonus_amount 列(迁移 241),
amount 仍为到账总额,到账/兑换码/退款逻辑不变;推广返利基数改为 amount - bonus_amount
- checkout-info 下发 recharge_bonus_tiers / recharge_bonus_notice;创建订单与订单查询响应带 bonus_amount
- 充值页:快捷金额按钮右上角「+N%」角标 + 第二行到账金额,金额卡顶部渲染活动文案,
订单摘要新增赠送额度行并在有赠送时始终显示到账余额;订单列表/详情/支付结果/成功面板显示含赠送
- 后台新增 RechargeBonusTierEditor(行内校验重复/非法阈值,自动排序并显示区间预览)
- 补后端单测、契约测试与前端 vitest,zh/en 文案
2026-10-02 00:25:41 +08:00
wucm667
1888de135d
fix(models): list composite exact route aliases
2026-10-01 22:54:05 +08:00
lyy0709
71e97d1353
fix(models): enforce account mappings for remote Codex discovery
2026-10-01 03:30:56 -07:00
SPHINX998 and Claude Opus 5.5
73381fc9c7
fix(openai-ws): bill each later turn with the current group pricing
...
The Responses WebSocket handler reads the API key auth snapshot once at
connect and reuses it for every turn: RecordUsage takes the group pricing
from that snapshot, and the profit gate reinstalled in BeforeTurn takes
its selling price from the connect-time ctxkey.Group. A group price
change therefore never reaches an open connection until the client
reconnects, while HTTP requests pick it up once the auth cache entry is
invalidated.
Each later turn now re-reads the key through APIKeyService.GetByKey (the
auth cache the middleware uses) in BeforeTurn. When the key ID, group ID,
group platform and subscription type are unchanged, the fresh group is
swapped into ctxkey.Group for the turn's profit gate and passed to that
turn's RecordUsage. A failed lookup or a key moved to another group keeps
the connection snapshot.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com >
2026-10-01 11:27:35 +08:00
Wesley Liddick
96f4c115c9
Merge pull request #7726 from william-drakemond/feat/claude-reset-redeem
...
feat(claude): 手动兑换 Claude 重置次数 / Manually redeem Claude limit resets
2026-09-30 14:33:23 +08:00
Wesley Liddick
327c322184
Merge pull request #7730 from lyy0709/codex/gpt61-sol-codex-plans
...
feat(openai): support GPT-6.1 Sol, Codex plans and Astra Ultrafast
2026-09-30 14:32:59 +08:00
Wesley Liddick
4f0c079e27
Merge pull request #7681 from william-drakemond/fix/balance-inflight-reservation
...
fix(billing): Redis 在途预留,防止余额模式并发透支
2026-09-30 14:31:04 +08:00
Wesley Liddick
a0f41f95a0
Merge pull request #7579 from feeeei/fix/claude-code-only-fallback-openai-compat
...
fix(gateway): Claude Code 限制分组的 OpenAI 兼容入口改走降级分组,不再直接 403
2026-09-30 10:47:08 +08:00
lyy0709
9688571a83
feat(models): support GPT-6.1 Sol
2026-09-29 11:49:26 -07:00
196b47757a
feat(claude): redeem native limit resets with server-selected grant
...
POST /admin/accounts/:id/claude/reset-credits/redeem (Idempotency-Key
required, same middleware chain as the Codex reset-quota route). The server
re-queries eligibility and claims only the upstream next_grant_id when it is
redeemable; grant/org IDs never reach the client. Claims are serialized by
account and organization Redis leases, and a durable per-organization fence
in the idempotency table blocks further redemption after an unconfirmed
outcome (settles after 24h). Ported from upstream PR #7591 .
Co-authored-by: korkin25 <eugeny.kuzakov@gmail.com >
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com >
2026-09-29 11:41:33 -04:00
Wesley Liddick
fe9dadfc7d
Merge pull request #7679 from PMExtra/feat/risk-control-allowlist
...
feat: 新增可信用户风控白名单,保留审计并免除本地自动处罚
2026-09-29 15:53:18 +08:00
Wesley Liddick
052e08e764
Merge pull request #7684 from william-drakemond/feat/claude-reset-credits-ui
...
feat(claude): 查询 Claude 重置次数(只读),UI 复用 Codex 次数按钮 / Read-only Claude reset credit count, reusing Codex count UI
2026-09-29 15:50:53 +08:00
Wesley Liddick
ccb027b515
Merge pull request #7378 from wucm667/fix/issue-7368-composite-ws-routing
...
fix(gateway): resolve composite routes for websocket aliases
2026-09-29 15:43:52 +08:00
korkin25
6a8f921650
feat(claude): expose sanitized native reset credit status
2026-09-28 18:04:13 -04:00
William Drake and Claude Opus 5.5
52c123e6f4
fix(billing): hold inflight reservation until billing lands; price estimate like billing; wire remaining endpoints
...
Address gaps found in adversarial verification of the in-flight balance
reservation:
- Early release: the reservation is now a ref-counted handle carried on the
request context. Billing tasks submitted via submit*UsageRecordTask take a
ref and return it only after RecordUsage has run; with reservation enabled
the balance cache is deducted synchronously inside the billing task, so the
reservation is released only after admission can see the deduction. Dropped
tasks return their ref immediately. The reservation is renewed every ttl/3
while the handler runs; after the handler returns it is no longer renewed,
so the post-handler hold is bounded by one TTL.
- Estimate: use the same billing model (channel mapping + BillingModelSource,
max over requested/mapped when ambiguous) and ModelPricingResolver (group
ModelPricing incl. wildcards, channel per_request/image/video/token
overrides), default/group/user rate + peak multiplier, plus image, video,
audio and search surcharge kinds. Unpriced requests fail open with a
throttled log; new billing.inflight_reservation.fail_closed_on_unpriced
(default false) rejects them instead.
- Wire images, embeddings, web/x search, alpha search, grok media generation,
grok realtime/voice, OpenAI-platform /v1/messages and ResponsesWebSocket
(session-level reservation with renewal).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com >
2026-09-28 16:14:43 -04:00
William Drake and Claude Opus 5.5
43f88c99aa
fix(billing): reserve in-flight balance to prevent concurrent overdraft
...
Balance-mode admission only checked cached balance > threshold, so N
concurrent expensive requests could all pass and overdraw the user.
Add a Redis in-flight reservation: at admission estimate a conservative
per-request cost (input estimate + output price x max_tokens, times rate
multiplier) and atomically require cached_balance - inflight >= estimate
(first in-flight request always admitted). Reservations are released via
defer on every path and expire by TTL. Fails open on estimation / Redis
errors. Configurable via billing.inflight_reservation (enabled by default).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com >
2026-09-28 14:20:49 -04:00
PMExtra
6a69dd051a
feat: add risk control user allowlist
2026-09-29 01:50:21 +08:00
William Drake and Claude Opus 5.5
b1ce3c6d59
fix(email): atomic verify-code attempts and hashed single-use reset tokens
...
- Verification code attempts (register + notify email) are now reserved via
an atomic Redis INCR (Lua) before comparing, so concurrent wrong guesses
cannot exceed the 5-attempt cap.
- Password reset tokens are stored as SHA-256 hashes and consumed with an
atomic Lua compare-and-delete, so a token can only be used once even under
concurrent requests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com >
2026-09-28 12:26:28 -04:00
wucm667
aa3a3ea787
feat(dashboard): toggle recent usage between tokens and spending
2026-09-28 14:48:42 +08:00
Wesley Liddick
b3c7038cf4
Merge pull request #7526 from HuntercodeT/fix/passthrough-model-discovery-merge
...
fix(gateway): let OpenAI passthrough accounts supplement model discovery instead of hiding mapped models
2026-09-28 10:49:01 +08:00
Wesley Liddick
79c18ec836
Merge pull request #7609 from feeeei/fix/client-disconnect-499
...
fix(gateway): 客户端断开统一标记 499,不再被记成 502/200 上游错误
2026-09-28 10:04:07 +08:00
feeeei
f4f8ff04db
fix(gateway): 客户端断开统一标记 499,不再被记成 502/200 上游错误
...
客户端在响应提交前断开时,只有排队/换号阶段和 OpenAI 的兜底写入会标 499。
Claude 非流式与 Gemini 的 Chat Completions / Messages / Responses 兼容入口
在等待上游时断开,由 GatewayHandler.ensureForwardErrorResponse 补写 502;
Gemini 原生入口什么都不写,落成 200 并被 ops 记成一条 provider 的
"Recovered upstream error";Antigravity 回写 502。这些行都按上游失败计入
SLA 与上游错误数。
- GatewayHandler.ensureForwardErrorResponse 补客户端断开检查,与 OpenAI 版一致
- GatewayHandler 的 Messages / ChatCompletions / Responses 与 Gemini 原生入口:
首次选号失败时先判断客户端断开;ChatCompletions / Responses 循环开头改用
failoverClientGone,不再直接 return 落成 200
- Gemini 原生入口:forward 失败时补 499 标记;并发槽错误改走
concurrencyErrorResponse 统一映射(断开 499、排队满/超时 429、其余 503)
- Antigravity 上游响应前断开回写 499;googleapi 与 Antigravity 的 Google 错误
格式把 499 映射为 CANCELLED
- OpenAI alpha search 非 failover 错误分支补断开检查
- Claude / Gemini / OpenAI 传输层错误:请求 context 已取消时不记 ops 上游事件
- ops:IgnoreContextCanceled 开启时跳过没有上游错误上下文的 499;带上游错误的
499(上游失败后客户端没等到换号结果就离开)仍按上游失败落库
计费不变:Gemini 流式中途断开的用量处理不在本次范围。新增 handler/service
单测覆盖各入口的断开场景与 ops 过滤规则。
2026-09-26 17:13:10 +08:00
Achord Chan
510b680fca
Merge upstream main and retain upstream scheduler test fix
2026-09-26 15:47:52 +08:00
feeeei
b31b435091
fix(gateway): Claude Code 限制分组的 OpenAI 兼容入口改走降级分组,不再直接 403
...
分组开启 claude_code_only 并配置降级分组时,/v1/messages 会在选号入口的
checkClaudeCodeRestriction 中把非 Claude Code 请求解析到降级分组;但
GatewayHandler.ChatCompletions / Responses 在 handler 层只要分组开了
claude_code_only 就直接返回 403,不看降级分组,请求到不了调度层:只会发
Chat Completions / Responses 的第三方客户端拿这类分组的 Key 一律 403。
- handler 层提前拒绝只在未配置降级分组时生效,拒绝的状态码与文案不变
- 配置了降级分组时请求继续,由 SelectAccountWithLoadAwareness 解析到降级分组
选号,再经已有的 Chat Completions / Responses ⇄ Anthropic Messages 转换转发;
service 层无改动
- 与 /v1/messages 的降级语义一致:选号与渠道限制预检按降级分组,计费与用量
记录仍按 API Key 所属分组
新增 handler 单测覆盖两个入口在有/无降级分组时的行为。
2026-09-24 16:20:28 +08:00
wucm667
13fcfc0f3b
fix(gateway): resolve composite routes for websocket aliases
2026-09-23 15:29:11 +08:00