2020 Commits
Author SHA1 Message Date
shaw 0acf00c4a1 Add admin compliance acknowledgement gate 2026-06-10 14:16:51 +08:00
Wesley Liddick c32e29bab0 Merge pull request #3187 from jianjianai/fix/gateway-debug-log-loop
优化调度日志循环开销 / Reduce debug logging overhead in scheduler hot path
2026-06-10 09:58:40 +08:00
Wesley Liddick f0748b98f6 Merge pull request #3173 from jianjianai/fix/idempotency-utf8-truncation
fix idempotency response utf8 truncation / 响应缓存按字节截断时可能切断 UTF-8 多字节字符的问题。
2026-06-10 09:58:09 +08:00
Wesley Liddick 5cb17fdc4f Merge pull request #3184 from touwaeriol/fix/bedrock-beta-and-error-passthrough
fix(gateway): prevent error passthrough double-write and Bedrock beta token leakage
2026-06-10 09:00:21 +08:00
shaw d662c97302 feat: claude-fable-5 2026-06-10 08:58:06 +08:00
jjaw 2c27548b82 优化调度日志循环开销 2026-06-10 01:03:01 +08:00
erio 448936d965 fix(ci): fix gofmt, errcheck, and test for supported context-management beta token 2026-06-10 00:46:19 +08:00
erio 12962bab24 refactor(bedrock): merge header filtering into ApplyBedrockCCCompat
Move anthropic-beta header filtering from separate FilterBedrockBetaHeader
into ApplyBedrockCCCompat, so one function handles all CC compat processing
(body cleanup + header filtering). Change signature from ctx to *gin.Context
to access request headers. Remove the redundant separate call in handler.
2026-06-10 00:18:09 +08:00
erio bf28a0098a fix(bedrock): filter unsupported top-level fields and fix beta token cleanup
- Remove `provider` and `metadata` fields from Bedrock request body.
  Bedrock returns 400 ValidationException for unknown top-level fields.
- When filtered beta tokens list is empty, delete any pre-existing
  `anthropic_beta` field in body to prevent client-injected tokens
  from leaking to Bedrock unfiltered.
- Add `context-management-2025-06-27` and `fine-grained-tool-streaming-2025-05-14`
  to bedrockSupportedBetaTokens whitelist per AWS documentation.
2026-06-10 00:16:30 +08:00
erio 20f3f2049b fix(gateway): complete MarkResponseCommitted coverage for all platforms
Add MarkResponseCommitted to 7 error-writing helper functions and
4 early-return branches in OpenAI handlers, plus 3 inline c.Data paths.

Helper functions (1 line each, covers 60+ call sites):
- writeGatewayCCError, writeResponsesError (Anthropic compat)
- writeClaudeError, writeGoogleError (Gemini + Antigravity)
- writeChatCompletionsError (Gemini Chat Completions compat)

OpenAI handleErrorResponse/handleCompatErrorResponse:
- passthrough rule and ShouldHandleErrorCode branches return before
  the existing MarkResponseCommitted, now each has its own Mark.

Inline c.Data: Gemini ForwardNative (2) + Antigravity ForwardGemini (1).
2026-06-10 00:16:08 +08:00
erio 6c88631690 fix(gateway): prevent double-write on error passthrough responses
Service layer writes a complete JSON error response then returns error.
Handler's ensureForwardErrorResponse couldn't distinguish this from
"no response written" and appended an SSE event, corrupting the body.

Use gin.Context flag: service marks MarkResponseCommitted(c) after
writing, ensureForwardErrorResponse checks IsResponseCommitted(c)
and skips. Zero function signature changes, zero error wrapping.
2026-06-10 00:15:51 +08:00
jjaw c10598dfe5 fix idempotency response utf8 truncation 2026-06-09 20:08:20 +08:00
DaydreamCodingandClaude Opus 4.8 329414ea4f feat(admin): /admin/users 新增按用户 API Key 所在分组过滤
- 支持专用/公开/订阅/已禁用四类分组:按 api_keys.group_id 精确匹配,
  排除软删除 key(EXISTS + DeletedAtIsNil);已禁用分组单独成区,
  覆盖 key 仍挂在禁用分组上的用户
- 后端:UserListFilters.APIKeyGroupID;handler 解析 api_key_group_id;
  repo HasAPIKeysWith 谓词;GetAllGroupsIncludingInactive 新接口
  (/admin/groups/all?include_inactive=true)
- 前端:下拉按类型分区单选;UsersView 独立 allGroupsForApiKeyFilter
  loader;分区标题用负数哨兵值修复 Vue :key 重复问题
- 测试:repo 集成 5/5(含软删除排除、多 key 去重、叠加 status 过滤)、
  handler 单测 5/5、前端 vitest 6/6

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 22:54:01 +08:00
Jiahao Luo d251487da8 fix(openai): propagate prompt cache key for chat completions 2026-06-08 11:13:18 +08:00
Wesley Liddick 8c782bcc81 Merge pull request #3083 from xzz0081/fix/non-streaming-content-type
fix: force Content-Type to application/json on non-streaming responses
2026-06-08 09:20:17 +08:00
Wesley Liddick 870cefddf5 Merge pull request #3097 from NeckBozia/fix/5h-resets-at-stale
fix(usage): sync 5h ResetsAt to SessionWindowEnd and zero expired window
2026-06-08 09:08:23 +08:00
Wesley Liddick a2bf6ad77a Merge pull request #3107 from DaydreamCoding/feat/proxy-failover-resilience
feat: 代理生命周期与故障健壮性增强
2026-06-08 08:59:15 +08:00
Wesley Liddick d7eef1e942 Merge pull request #3104 from CoolCoolTomato/fix/api-key-exclusive-group-auth
fix(bug): Fix the bug related to unauthorized use of groups
2026-06-08 08:58:44 +08:00
Wesley Liddick 7b394ed157 Merge pull request #3094 from okbexx/fix-openai-sticky-group-validation
fix: ignore stale OpenAI sticky sessions outside request group
2026-06-08 08:49:09 +08:00
DaydreamCoding af19d44327 feat(proxies): 代理有效期与失败回退
- schema/迁移: 代理有效期、提醒天数、失败回退配置 + 账号 fallback 来源字段
- service/repo/DTO/handler: CRUD 透传新字段 + 校验
- fallback 目标解析纯函数(链式解析 + 环检测 + 兜底)
- SweepExpiredProxies 到期改投账号 + outbox 失效
- ProxyExpiryService 后台到期扫描任务 + wire 注册
- 账号侧手动回切原代理 + fallback 来源徽章/按钮
- 前端: 创建/编辑表单、列表到期徽章、类型/API/i18n
- ops 告警: proxy_expired_count / proxy_expiring_soon_count 指标
- 导入导出携带有效期/回退字段(备用按 name 映射)
- 补全测试 stub + 集成测试 + review 问题修复
2026-06-08 00:01:30 +08:00
DaydreamCodingandClaude Opus 4.8 f20e6bf769 feat(ops): 新增 account_temp_unscheduled_count 告警指标
临时摘除(temp-unschedulable)的账号被 account_error_count 指标显式排除
(acc.HasError && TempUnschedulableUntil == nil),且 SetTempUnschedulable 不
改账号 Status,导致代理/凭据故障触发的自动摘除无法被现有告警覆盖。

新增 account_temp_unscheduled_count 指标,统计当前处于临时不可调度窗口
(TempUnschedulableUntil 未过期) 的账号数,打通对自动摘除的定向告警:
- evaluator computeRuleMetric 新增分支 + handler 允许列表;
- 前端联合类型、告警规则下拉项与 en/zh 文案同步。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:31:45 +08:00
DaydreamCodingandClaude Opus 4.8 217f85999c fix(openai): /responses 传输层错误转 failover + 持久故障临时摘除账号
代理/网络等传输层失败(Do/DoWithTLS 返回 error、无 HTTP 状态码,例如 SOCKS5
代理凭据过期返回 "username/password authentication failed")此前直接写
502 "Upstream request failed" 并返回普通 error:既不 failover 到健康账号,
也不摘除故障账号,导致同一坏账号被反复调度、大量用户持续收到 502。

- 新增 classifyOpenAITransportError:typed-error 优先
  (ECONNREFUSED/EHOSTUNREACH/ENETUNREACH、*net.DNSError.IsNotFound)
  + 字符串兜底(SOCKS5 认证失败无 typed 形式)区分持久 vs 瞬时。
- 新增 handleOpenAIUpstreamTransportError:记录 ops 错误;对持久故障调用
  SetTempUnschedulable(10min,DB) + 内存 BlockAccountScheduling 摘除账号并打
  稳定 WARN 事件;统一返回 *UpstreamFailoverError 让 handler 切换到健康账号;
  context.Canceled(客户端断开)不 failover、不摘除。
- /responses 主路径、passthrough、chat-completions 回退三处统一接入共享 helper。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 22:31:45 +08:00
Wesley Liddick d01e4fd84d Merge pull request #3075 from bwliangc/feat/openai-cross-group-prev-response
fix(openai): 切组后剥离失配的 previous_response_id,修复跨组会话鉴权失败
2026-06-07 22:21:24 +08:00
CoolCoolTomato 1a86c6ce12 fix: enforce exclusive group access for api keys 2026-06-07 20:46:03 +08:00
bwlcandClaude Opus 4.8 9a0e439803 fix(openai): 跨组会话失配保护移到生效的 WSv2 路径并补测
87dd5f5d 把 previous_response_id 剥离保护加在了 HTTP Responses 路径,但该路径
在 previousResponseID != "" 时已无条件返回 400(0fcddce6 引入),剥离块恒不可达,
RemovePreviousResponseIDFromBody 也只被这段死分支调用、无覆盖。

- 删除 HTTP Responses 路径的死代码剥离块,留注释指明保护应在 WSv2 路径。
- 在 ResponsesWebSocket 首包(wsFirstMessage)处补回等价保护:previous_response_id
  未命中当前分组粘连账号(StickyPreviousHit=false)时剥离,改用首包 input 重建;
  带 function_call_output 的工具续链保持原样。这是 previous_response_id 真正生效、
  会触发跨组会话链鉴权失败的路径。
- 为 RemovePreviousResponseIDFromBody 增加单元测试(现已有真实 caller)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:30:56 +08:00
NeckBozia 16bc876935 fix(usage): sync 5h ResetsAt to SessionWindowEnd and zero expired window
active poll 拿到新 5h ResetsAt 时只回写了 Extra.session_window_utilization,
没回写 SessionWindowEnd column;estimateSetupTokenUsage 读这个 column 作为 5h
窗口结束时间,导致被动采样模式下 dashboard 显示 utilization > 0 但 reset
时间渲染为「现在」。

- syncActiveToPassive 增加 UpdateSessionWindowEnd 回写
- estimateSetupTokenUsage 抄齐 Codex 分支的过期归零 guard,避免 active
  poll 没回写时 UI 渲染矛盾
- UsageProgressBar 区分「待刷新」/「现在」语义;i18n key 中英补齐
2026-06-07 15:23:45 +08:00
okbexx a436296357 fix: validate OpenAI sticky session groups 2026-06-07 14:15:27 +08:00
xzz0081 154e0ed6c5 fix: force Content-Type to application/json on non-streaming responses
When the gateway forces streaming to the upstream, the upstream returns
Content-Type: text/event-stream. In the non-streaming branches, the body
is correctly aggregated into JSON, but WriteFilteredHeaders has already
passed the upstream's text/event-stream header through. Since Gin's
c.JSON/c.Data only set Content-Type via writeContentType when it is not
already present, the SSE header is never overwritten -- so the body is JSON
while the header says SSE.

Downstream middlewares that decide "is this streaming?" by Content-Type
(e.g. new-api) then mis-handle the response as SSE, breaking non-streaming
JSON clients.

Fix: explicitly Set Content-Type to application/json right after
WriteFilteredHeaders in the three non-streaming JSON write paths.
2026-06-07 02:56:26 +08:00
feitianbubu 029b6d61a2 feat(usage): 聚合统计拆分缓存创建与命中 token 2026-06-06 22:32:37 +08:00
bwlcandClaude Opus 4.8 87dd5f5d72 fix(openai): 切组后剥离失配的 previous_response_id,修复跨组会话鉴权失败
用户从公开组切回订阅组后,客户端沿用旧 Responses 会话 ID,订阅组调度到
不拥有该会话链的账号,上游因会话链鉴权不匹配返回“鉴权失败,请检查 API Key”。

两处修复:
- openai_ws_state_store: 本地热缓存 responseToAccount 改为按 {groupID}:{responseID}
  命名空间,与 Redis 层 sticky_session:{groupID}:... 一致,避免单实例下跨组命中
  其他分组遗留的本地绑定,确保 StickyPreviousHit 信号可信。
- openai_gateway_handler: 转发前若 previous_response_id 未在当前分组命中粘连账号
  (StickyPreviousHit=false),主动剥离并改用完整 input 重建上下文;带
  function_call_output 的工具续链保持原样(与 WS 重连恢复逻辑一致)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 16:54:56 +08:00
Wesley Liddick 96b4bf624c Merge pull request #2997 from bobobo2026/codex/openai-responses-sticky-account
Bind OpenAI HTTP response IDs to selected accounts
2026-06-06 14:52:55 +08:00
Wesley Liddick c65f9f19d2 Merge pull request #2955 from paraliine/main
Align OpenAI Codex usage stats with reset windows
2026-06-06 14:16:40 +08:00
Wesley Liddick 5824359e97 Merge pull request #3062 from visa2/fix/multi-instance-bg-job-leader-lock
fix(service): gate periodic background jobs with a leader lock for multi-instance
2026-06-06 13:59:42 +08:00
Wesley Liddick 84cedd3d71 Merge pull request #2960 from CheriWen/fix/cwe-79-stored-xss-key-name
[Security] fix: sanitize API key name to prevent stored XSS
2026-06-06 11:09:08 +08:00
Wesley Liddick ff3c976dbd Merge pull request #3009 from liulinhuai/fix/images-upstream-error-passthrough
fix(images): surface real upstream error instead of generic 502
2026-06-06 11:07:53 +08:00
Wesley Liddick 1dda8626d5 Merge pull request #3018 from Zbl1007/openmeta/2989-0
Fix fixed weekly quota usage after switching from rolling reset
2026-06-06 10:59:18 +08:00
Wesley Liddick 1a0eecb6ef Merge pull request #3047 from fchange/codex/delete-user-api-keys
fix(admin): delete API keys when deleting a user
2026-06-06 10:14:28 +08:00
Wesley Liddick 1512ba747e Merge pull request #2995 from feitianbubu/opt-proxy-quality-pass
fix: treat allowed proxy quality statuses as pass not warn
2026-06-06 09:58:16 +08:00
Wesley Liddick 75af992819 Merge pull request #3026 from Zbl1007/openmeta/2956-deepseek
Surface DeepSeek reasoning-only responses
2026-06-06 09:35:29 +08:00
Wesley Liddick 427d591212 Merge pull request #2930 from touwaeriol/feat/image-token-billing
fix(billing): channel pricing override for image generation + display image_output_tokens
2026-06-06 09:28:46 +08:00
shaw 650981f2e4 fix(lint): 修复 golangci-lint errcheck 与 gofmt 报错
- gateway_service.go buildStableSessionSeed: strings.Builder.WriteString
  返回值用 _, _ = 显式忽略(errcheck)
- openai_codex_transform_additions_test.go: client_metadata 类型断言改为
  双值形式 cm3, _ := ...(errcheck check-type-assertions)
- openai_gateway_service.go: codexCLIUserAgent 常量单独成组,收敛 = 前多余
  空格对齐(gofmt)
2026-06-06 09:21:32 +08:00
eyre 480f0cba22 merge: 合并 Codex 与 Claude Code 双重模拟改造为统一分支
- feat/closer-to-codex:Codex 指纹/prompt/SSE/include/client_metadata 改造
- feat/closer-to-claude-code:Claude Code 指纹对齐 CLI 2.1.161
两组改动改动文件互不重叠,合并无冲突。
2026-06-06 01:06:20 +08:00
eyre 219da4b9e5 feat(claude-mimicry): align Claude Code fingerprint with CLI 2.1.161
- bump impersonated CLI version 2.1.92 -> 2.1.161; derive User-Agent from
  CLICurrentVersion so the two hardcoded copies can no longer drift apart
- fix x-stainless headers to real 2.1.161 values: package-version
  0.70.0 -> 0.94.0, runtime-version v24.13.0 -> v24.3.0 (verified against
  the installed Bun-compiled binary)
- expand the disguise-path system prompt from a 2-block identity skeleton
  to a 3-block layout (billing + identity + tool-agnostic prose), matching
  real CC's multi-block shape; cache breakpoint moved to the last static
  block. Deliberately excludes # Doing tasks / # Using your tools /
  # Executing actions to avoid polluting proxied-client behavior
- stabilize the synthesized metadata.user_id session_id across conversation
  turns: derive it from (account + client discriminator + first user
  message) instead of a per-turn content/body hash. Sticky-routing
  GenerateSessionHash is intentionally left untouched; remove now-dead
  hashBodyForSessionSeed

Tests: update the 3-block system assertions in gateway_prompt_test and
gateway_anthropic_apikey_passthrough_test; add a session_id cross-turn
stability test in gateway_oauth_metadata_test.
2026-06-06 01:01:58 +08:00
eyre 5e6effd79c feat: 模型感知 Codex prompt / client_metadata / anthropic SSE 补全
- 模型感知 instructions:刷新 instructions.txt 至最新 GPT-5-Codex prompt,新增
  GPT-5.1 / GPT-5.2 真实 Codex 编码 agent prompt;新增 openai.CodexBaseInstructionsForModel
  按模型选用(codex 系→GPT-5-Codex,gpt-5.2→GPT-5.2,gpt-5.1/gpt-5→GPT-5.1),
  defaultCodexSynthInstructions 改为按 model 选择
- client_metadata:OAuth /responses 请求用账号真实 openai_device_id 注入
  client_metadata["x-codex-installation-id"];加法式、幂等、不覆盖既有项,
  无 device_id(非 OAuth 账号)则不写入、不伪造
- anthropic 转换补全 SSE 事件 reasoning_text.delta 与 custom_tool_call_input.delta
  (含 custom_tool_call item 注册为 tool_use),与 chat completions 路径对齐
- 新增单元测试覆盖以上行为(apicompat / openai / service)

承接上一提交,仍为加法式改进,不改动既有可工作的请求头。
2026-06-06 00:59:27 +08:00
eyre baf078fdb7 feat: 提升 Codex 行为模拟保真度(仅加法式改进)
- ForceCodexCLI 兜底 User-Agent 补全为完整结构 {originator}/{ver} ({OS} {ver}; {arch}) {terminal},
  与真实 codex_cli_rs UA 对齐,避免被上游指纹识别为非官方客户端
- 合成路径默认 instructions 改用内嵌的真实 Codex base prompt(openai.DefaultInstructions,
  "You are Codex, based on GPT-5..."),替换通用占位符;transform 与 hotpath 两处统一走
  defaultCodexSynthInstructions()
- /responses→chat 转换补全 SSE 事件 reasoning_text.delta 与 custom_tool_call_input.delta
  (流式 ResponsesEventToChatChunks 与缓冲 BufferedResponseAccumulator 两条路径),
  并将 custom_tool_call item 一并注册以正确映射工具索引
- OAuth /responses 请求带 reasoning 时补齐 include:["reasoning.encrypted_content"],
  幂等且加法式,不改动已有 include

保持旧稳定版兼容:不改动现有可工作的请求头(session_id/conversation_id/originator/
OpenAI-Beta 维持原样)。client_metadata(installation-id) 属最新版特征,会与旧稳定版
头指纹混搭冲突,本次未加。
2026-06-06 00:44:49 +08:00
visa2andClaude Opus 4.8 362f9e77bf fix(service): gate periodic background jobs with a leader lock for multi-instance
Three periodic background jobs ran on every instance with no cross-instance
coordination, multiplying their cost (and side effects) by the replica count:

- DashboardAggregationService.runScheduledAggregation: N× heavy GROUP BY
  aggregation queries every minute plus watermark write races.
- PaymentOrderExpiryService.runOnce: N× upstream payment-provider reconcile/
  expiry API calls per pending order.
- SubscriptionExpiryService.sendExpiryReminders: N× full active-subscription
  scans every minute and potential duplicate reminder emails.

Add a LeaderLockCache abstraction so only one instance runs each job per cycle:

- The interface lives in the service layer; the Redis-backed implementation
  (SetNX + compare-and-delete release) lives in the repository layer, so the
  service package keeps its depguard "must not import redis" boundary intact.
- tryAcquireSingletonLeaderLock prefers the cache and falls back to a Postgres
  advisory lock when Redis errors, mirroring the Ops background services. When
  neither backend is configured the job runs ungated, preserving single-instance
  and test behavior (no self-lockout: the lock is released every cycle).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 22:47:24 +08:00
Wesley Liddick 1cecd2716c Merge pull request #2972 from northya/fix/chat-completions-response-failed
fix: 修复 Chat Completions 兼容层误将 response.failed 返回为成功响应
2026-06-05 21:51:23 +08:00
Wesley Liddick 8775047f84 Merge pull request #3051 from wucm667/fix/openai-messages-missing-terminal-event-failover
fix(openai): /v1/messages 流式缺终止事件时纳入 failover 与 ops 错误归因
2026-06-05 21:34:14 +08:00
Wesley Liddick 872e56aed7 Merge pull request #3052 from wucm667/feat/openai-image-ratelimit-cooldown-failover
feat(openai): gpt-image 图片限流按能力维度冷却并 failover,不再误伤文字请求
2026-06-05 21:25:40 +08:00
Wesley Liddick b5b68f86bd Merge pull request #3037 from feitianbubu/pr/fix-auto-mode-invalid-by-security-monitor
fix: cc开启auto mode后无法通过cc客户端验证的问题
2026-06-05 20:43:37 +08:00