1. 08 Apr, 2026 5 commits
    • shaw's avatar
      feat: Beta策略支持按模型区分处理(模型白名单) · f1f9640c
      shaw authored and 陈曦's avatar 陈曦 committed
      f1f9640c
    • shaw's avatar
      fix: 非Claude Code客户端system prompt迁移至messages以绕过第三方应用检测 · b717956c
      shaw authored and 陈曦's avatar 陈曦 committed
      Anthropic近期引入基于system参数内容的第三方应用检测机制,原有的前置追加
      Claude Code提示词策略无法通过检测(后续内容仍为非Claude Code格式触发429)。
      
      新策略:对非Claude Code客户端的OAuth/SetupToken账号请求,将system字段
      完整替换为Claude Code标识提示词,原始system内容作为user/assistant消息对
      注入messages开头,模型仍接收完整指令。
      
      仅影响/v1/messages路径,chat_completions和responses路径保持原有逻辑不变。
      真正的Claude Code客户端请求完全不受影响(原样透传)。
      b717956c
    • shaw's avatar
      fix: 修复非CC客户端OAuth伪装被Anthropic检测为第三方应用的问题 · 49c19a55
      shaw authored and 陈曦's avatar 陈曦 committed
      commit f3aa54b7 的 rewriteSystemForNonClaudeCode 未能通过 Anthropic 第三方检测,
      根因是两个关键信号与真实 Claude Code 不一致:
      
      1. anthropic-beta 头缺少 claude-code-20250219:伪装路径主动将该 beta
         加入 drop set 并移除,但 Anthropic 依赖此 beta 识别 Claude Code 请求。
         修复:非 haiku 模型的伪装请求强制包含 claude-code beta。
      
      2. system 字段使用 string 格式而非 array+cache_control:真实 Claude Code
         始终以 [{type,text,cache_control:{type:"ephemeral"}}] 发送 system,
         string 格式成为第三方检测信号。
         修复:rewriteSystemForNonClaudeCode 改为注入 array 格式。
      
      附带调整:stripSystemCacheControl 按 system 是否被重写动态决定,
      重写时保留 CC prompt 的 cache_control,未重写时(haiku/已含CC前缀)
      保持原有剥离行为。
      49c19a55
    • shaw's avatar
      feat: Beta策略支持按模型区分处理(模型白名单) · 248fe092
      shaw authored and 陈曦's avatar 陈曦 committed
      248fe092
    • shaw's avatar
      fix: 非Claude Code客户端system prompt迁移至messages以绕过第三方应用检测 · f568ec76
      shaw authored and 陈曦's avatar 陈曦 committed
      Anthropic近期引入基于system参数内容的第三方应用检测机制,原有的前置追加
      Claude Code提示词策略无法通过检测(后续内容仍为非Claude Code格式触发429)。
      
      新策略:对非Claude Code客户端的OAuth/SetupToken账号请求,将system字段
      完整替换为Claude Code标识提示词,原始system内容作为user/assistant消息对
      注入messages开头,模型仍接收完整指令。
      
      仅影响/v1/messages路径,chat_completions和responses路径保持原有逻辑不变。
      真正的Claude Code客户端请求完全不受影响(原样透传)。
      f568ec76
  2. 05 Apr, 2026 3 commits
  3. 04 Apr, 2026 21 commits
    • erio's avatar
      refactor: unify interval filtering and eliminate redundant Resolve calls · e88b2890
      erio authored
      - applyRequestTierOverrides now uses filterValidIntervals consistently
        with applyTokenOverrides (per_request/image modes were not filtering)
      - CostInput accepts optional pre-resolved pricing via Resolved field,
        eliminating duplicate Resolver.Resolve() calls in gateway billing paths
      e88b2890
    • erio's avatar
      fix: resolve golangci-lint issues — remove unused constants and functions, fix gofmt · 1b5ae71d
      erio authored
      - Remove unused claudeMax*Tokens constants (Claude Max feature not included)
      - Remove unused UsageMapHook type, SetUsageMapHook method, and usageToMap function
      - Fix gofmt formatting in channel_service.go, openai_model_mapping_test.go,
        chatcompletions_to_responses.go
      1b5ae71d
    • erio's avatar
      fix: resolve cherry-pick compilation and test issues · e59fa863
      erio authored
      - Add int64(0) param to SelectAccountWithLoadAwareness callers (signature change from channel scheduling refactor)
      - Add UsageMapHook type and struct field to StreamingProcessor
      - Revert Claude Max cache billing code to upstream/main (not part of channel feature)
      - Revert credits overages logic to upstream/main (non-channel change)
      - Remove Instructions field reference (non-channel OpenAI feature)
      - Restore sora_client_handler_test.go from upstream + add channel service nil params
      e59fa863
    • erio's avatar
      feat(channel): improve cache strategy and add restriction logging · 58f758c8
      erio authored
      - Change channel cache TTL from 60s to 10min (reduce unnecessary DB queries)
      - Actively rebuild cache after CRUD instead of lazy invalidation
      - Add slog.Warn logging for channel pricing restriction blocks (4 places)
      58f758c8
    • erio's avatar
      fix: resolve 5 audit findings in channel/credits/scheduling · 71f61bbc
      erio authored
      P0-1: Credits degraded response retry + fail-open
      - Add isAntigravityDegradedResponse() to detect transient API failures
      - Retry up to 3 times with exponential backoff (500ms/1s/2s)
      - Invalidate singleflight cache between retries
      - Fail-open after exhausting retries instead of 5h circuit break
      
      P1-1: Fix channel restriction pre-check timing conflict
      - Swap checkClaudeCodeRestriction before checkChannelPricingRestriction
      - Ensures channel restriction is checked against final fallback groupID
      
      P1-2: Add interval pricing validation (frontend + backend)
      - Backend: ValidateIntervals() with boundary, price, overlap checks
      - Frontend: validateIntervals() with Chinese error messages
      - Rules: MinTokens>=0, MaxTokens>MinTokens, prices>=0, no overlap
      
      P2: Fix cross-platform same-model pricing/mapping override
      - Store cache keys using original platform instead of group platform
      - Lookup across matching platforms (antigravity→anthropic→gemini)
      - Prevents anthropic/gemini same-name models from overwriting each other
      71f61bbc
    • erio's avatar
      fix: address review findings for channel restriction refactoring · 1fca2bfa
      erio authored
      - Fix 7 stale comments still mentioning "限制检查" in handlers/services
      - Make billingModelForRestriction explicitly list channel_mapped case
      - Add slog.Warn for error swallowing in ResolveChannelMapping and
        needsUpstreamChannelRestrictionCheck
      - Document sticky session upstream check exemption
      1fca2bfa
    • erio's avatar
      refactor: move channel model restriction from handler to scheduling phase · ce41afb7
      erio authored
      Move the model pricing restriction check from 8 handler entry points
      to the account scheduling phase (SelectAccountForModelWithExclusions /
      SelectAccountWithLoadAwareness), aligning restriction with billing:
      
      - requested: check original request model against pricing list
      - channel_mapped: check channel-mapped model against pricing list
      - upstream: per-account check using account-mapped model
      
      Handler layer now only resolves channel mapping (no restriction).
      Scheduling layer performs pre-check for requested/channel_mapped,
      and per-account filtering for upstream billing source.
      ce41afb7
    • erio's avatar
      refactor: extract helpers to reduce duplication and function length in gateway billing · b4a42a64
      erio authored
      - Extract resolveChannelPricing to DRY the resolver pattern shared by calculateImageCost/calculateTokenCost
      - Remove unnecessary IIFE wrapper and pass accountRateMultiplier as parameter
      - Extract resolveBillingMode, resolveMediaType, optionalSubscriptionID to simplify buildRecordUsageLog (104→65 lines)
      - Extract shouldDeductAPIKeyQuota/shouldUpdateRateLimits/shouldUpdateAccountQuota methods on postUsageBillingParams to unify duplicated billing conditions
      b4a42a64
    • erio's avatar
      refactor: merge RecordUsage and RecordUsageWithLongContext into shared core · 58b26cb4
      erio authored
      - Extract recordUsageCore with recordUsageOpts for parameterized differences
      - RecordUsage (276 lines) → thin wrapper (~40 lines)
      - RecordUsageWithLongContext (251 lines) → thin wrapper (~20 lines)
      - Split billing logic into calculateSoraMediaCost, calculateImageCost,
        calculateTokenCost sub-functions
      - Extract buildRecordUsageLog for usage log construction
      - Net reduction: -79 lines, eliminated ~170 lines of duplication
      58b26cb4
    • erio's avatar
      refactor: replace magic strings with named constants · 0d241d52
      erio authored
      - PricingSourceChannel/LiteLLM/Fallback for resolver source
      - MediaTypeImage/Video/Prompt for result.MediaType
      - Reuse BillingModeToken/BillingModeImage for billing mode
      - Reuse BillingModelSourceChannelMapped/PlatformAnthropic in handler
      0d241d52
    • erio's avatar
      fix: billing mode display follows cost calculation result · f3ab3fe5
      erio authored
      Instead of hardcoding BillingMode="image" when ImageCount>0,
      let cost.BillingMode (set by CalculateCostUnified/CalculateImageCost)
      take priority. This ensures channel token pricing shows "token" mode.
      f3ab3fe5
    • erio's avatar
      feat: channel token pricing takes priority over per-image billing · 38da737e
      erio authored
      When ImageCount > 0, check if channel has token pricing configured:
      - YES (source=channel, mode=token) → use token billing with image_output_tokens
      - NO → fall back to CalculateImageCost (original per-image billing)
      
      This allows channels to configure $/MTok pricing for image generation
      models while maintaining backward compatibility for setups without
      channel pricing.
      38da737e
    • erio's avatar
      fix: add cost nil guard to Anthropic/Antigravity RecordUsage paths · 35a92905
      erio authored
      - Apply same nil-pointer protection as OpenAI path
      - Remove unused accessToken/proxyURL params from checkAccountCredits
      35a92905
    • erio's avatar
      feat: image output token billing, channel-mapped billing source, credits balance precheck · d72ac926
      erio authored
      - Parse candidatesTokensDetails from Gemini API to separate image/text output tokens
      - Add image_output_tokens and image_output_cost to usage_log (migration 089)
      - Support per-image-token pricing via output_cost_per_image_token from model pricing data
      - Channel pricing ImageOutputPrice override works in token billing mode
      - Auto-fill image_output_price in channel pricing form from model defaults
      - Add "channel_mapped" billing model source as new default (migration 088)
      - Bills by model name after channel mapping, before account mapping
      - Fix channel cache error TTL sign error (115s → 5s)
      - Fix Update channel only invalidating new groups, not removed groups
      - Fix frontend model_mapping clearing sending undefined instead of {}
      - Credits balance precheck via shared AccountUsageService cache before injection
      - Skip credits injection for accounts with insufficient balance
      - Don't mark credits exhausted for "exhausted your capacity on this model" 429s
      d72ac926
    • erio's avatar
      feat(channel): 渠道管理全链路集成 — 模型映射、定价、限制、用量统计 · 2555951b
      erio authored
      - 渠道模型映射:支持精确匹配和通配符映射,按平台隔离
      - 渠道模型定价:支持 token/按次/图片三种计费模式,区间分层定价
      - 模型限制:渠道可限制仅允许定价列表中的模型
      - 计费模型来源:支持 requested/upstream 两种计费模型选择
      - 用量统计:usage_logs 新增 channel_id/model_mapping_chain/billing_tier/billing_mode 字段
      - Dashboard 支持 model_source 维度(requested/upstream/mapping)查看模型统计
      - 全部 gateway handler 统一接入 ResolveChannelMappingAndRestrict
      - 修复测试:同步 SoraGenerationRepository 接口、SQL INSERT 参数、scan 字段
      2555951b
    • erio's avatar
      fix(channel): 全平台渠道映射覆盖 + 公共函数抽取 + 死代码清理 · eb385457
      erio authored
      - 4个缺失handler入口添加渠道映射+限制检查(ChatCompletions/Responses/Gemini)
      - 模型限制错误信息优化,区分"模型不可用"和"无账号"
      - OpenAI RecordUsage RequestedModel 改用 OriginalModel
      - ResolveChannelMappingAndRestrict/ReplaceModelInBody 抽取到 ChannelService 消除跨service重复
      - validateNoDuplicateModels 按 platform:model 去重
      - 删除 Channel.ResolveMappedModel 死代码和 CalculateCostWithChannel Deprecated方法
      - 移除冗余nil检查,抽取 validatePricingBillingMode 公共校验
      eb385457
    • erio's avatar
      refactor(channel): 抽取渠道映射公共函数 + OpenAI映射到body + 空响应修复 + 清理日志 · 4ea8b4cb
      erio authored
      - 抽取 ResolveChannelMappingAndRestrict 统一入口(5处→1个方法)
      - 抽取 BuildModelMappingChain 到 ChannelMappingResult 方法(5处→1行调用)
      - OpenAI 三入口 Forward 前应用渠道映射到请求体
      - OpenAI Responses/Messages 限制检查添加错误响应
      - 清理前端 3 处 console.log 调试日志
      4ea8b4cb
    • erio's avatar
      feat(billing): 网关计费迁移到 CalculateCostUnified + 模型限制错误统一 · 632035aa
      erio authored
      - GatewayService/OpenAIGatewayService 注入 ModelPricingResolver
      - RecordUsage 从旧路径迁移到 CalculateCostUnified(支持 per_request/image 模式)
      - 无渠道时自动回退旧路径,保持原有行为
      - 长上下文双倍计费仅在无渠道定价时生效
      - CostBreakdown 新增 BillingMode 字段,使用日志记录实际计费模式
      - 模型限制错误改为与"无可用账号"相同的 503 响应
      632035aa
    • erio's avatar
      feat(usage): 使用记录增加计费模式字段 — 记录/展示/筛选 token/按次/图片 · a51e0047
      erio authored
      - DB: usage_logs 表新增 billing_mode VARCHAR(20) 列
      - 后端: RecordUsage 写入时根据 image_count 判定计费模式
      - 前端: 使用记录表格新增计费模式 badge 列 + 筛选下拉
      a51e0047
    • erio's avatar
      feat(channel): 缓存扁平化 + 网关映射集成 + 计费模式统一 + 模型限制 · ebac0dc6
      erio authored
      - 缓存重构为 O(1) 哈希结构 (pricingByGroupModel, mappingByGroupModel)
      - 渠道模型映射接入网关流程 (Forward 前应用, a→b→c 映射链)
      - 新增 billing_model_source 配置 (请求模型/最终模型计费)
      - usage_logs 新增 channel_id, model_mapping_chain, billing_tier 字段
      - 每种计费模式统一支持默认价格 + 区间定价
      - 渠道模型限制开关 (restrict_models)
      - 分组按平台分类展示 + 彩色图标
      - 必填字段红色星号 + 模型映射 UI
      - 去除模型通配符支持
      ebac0dc6
    • erio's avatar
      feat(channel): 渠道管理系统 — 多模式定价 + 统一计费解析 · 91c9b8d0
      erio authored
      Cherry-picked from release/custom-0.1.106: a9117600
      91c9b8d0
  4. 03 Apr, 2026 2 commits
    • QTom's avatar
      feat(gateway): Cache-Driven RPM Buffer · 044f1be9
      QTom authored and 陈曦's avatar 陈曦 committed
      
      
      - buffer 公式从 baseRPM/5 改为 concurrency + maxSessions
        保留 baseRPM/5 作为 floor 向后兼容
      - 粘性路径 fallback 新增 [StickyCacheMiss] 结构化日志
        reason: rpm_red / gate_check / session_limit / wait_queue_full / account_cleared
      - session_limit 路径跳过 wait queue 重试(RegisterSession 拒绝无副作用)
      - 典型配置 buffer 从 3 提升至 13,大幅减少高峰期 Prompt Cache Miss
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      044f1be9
    • QTom's avatar
      feat(group-filter): 分组账号过滤控制 — require_oauth_only + require_privacy_set · e2ec1d30
      QTom authored and 陈曦's avatar 陈曦 committed
      
      
      为 OpenAI/Antigravity/Anthropic/Gemini 分组新增两个布尔控制字段:
      - require_oauth_only: 创建/更新账号绑定分组时拒绝 apikey 类型加入
      - require_privacy_set: 调度选号时跳过 privacy 未成功设置的账号并标记 error
      
      后端:Ent schema 新增字段 + 迁移、Group CRUD 全链路透传、
            gateway_service 与 openai_account_scheduler 两套调度路径过滤
      前端:创建/编辑表单 toggle 开关(OpenAI/Antigravity/Anthropic/Gemini 平台可见)
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      e2ec1d30
  5. 31 Mar, 2026 2 commits
    • QTom's avatar
      feat(gateway): Cache-Driven RPM Buffer · 72e5876c
      QTom authored
      
      
      - buffer 公式从 baseRPM/5 改为 concurrency + maxSessions
        保留 baseRPM/5 作为 floor 向后兼容
      - 粘性路径 fallback 新增 [StickyCacheMiss] 结构化日志
        reason: rpm_red / gate_check / session_limit / wait_queue_full / account_cleared
      - session_limit 路径跳过 wait queue 重试(RegisterSession 拒绝无副作用)
      - 典型配置 buffer 从 3 提升至 13,大幅减少高峰期 Prompt Cache Miss
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      72e5876c
    • QTom's avatar
      feat(group-filter): 分组账号过滤控制 — require_oauth_only + require_privacy_set · aeed2eb9
      QTom authored
      
      
      为 OpenAI/Antigravity/Anthropic/Gemini 分组新增两个布尔控制字段:
      - require_oauth_only: 创建/更新账号绑定分组时拒绝 apikey 类型加入
      - require_privacy_set: 调度选号时跳过 privacy 未成功设置的账号并标记 error
      
      后端:Ent schema 新增字段 + 迁移、Group CRUD 全链路透传、
            gateway_service 与 openai_account_scheduler 两套调度路径过滤
      前端:创建/编辑表单 toggle 开关(OpenAI/Antigravity/Anthropic/Gemini 平台可见)
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      aeed2eb9
  6. 30 Mar, 2026 3 commits
  7. 29 Mar, 2026 2 commits
    • QTom's avatar
      fix(gateway): 修复 OpenAI→Anthropic 转换路径 system prompt 被静默丢弃的 bug · 7754a2f5
      QTom authored and 陈曦's avatar 陈曦 committed
      
      
      injectClaudeCodePrompt 和 systemIncludesClaudeCodePrompt 的 type switch
      无法匹配 json.RawMessage 类型(Go typed nil 陷阱),导致 ForwardAsResponses
      和 ForwardAsChatCompletions 路径中用户 system prompt 被替换为仅 Claude Code
      banner。新增 normalizeSystemParam 将 json.RawMessage 转为标准 Go 类型。
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      7754a2f5
    • shaw's avatar
      feat(tls-fingerprint): 新增 TLS 指纹 Profile 数据库管理及代码质量优化 · b22ab6ad
      shaw authored and 陈曦's avatar 陈曦 committed
      新增功能:
      - 新增 TLS 指纹 Profile CRUD 管理(Ent schema + 迁移 + Admin API + 前端管理界面)
      - 支持账号绑定数据库中的自定义 TLS Profile,或随机选择(profile_id=-1)
      - HTTPUpstream.DoWithTLS 接口从 bool 改为 *tlsfingerprint.Profile,支持按账号指定 Profile
      - AccountUsageService 注入 TLSFingerprintProfileService,统一 usage 场景与网关的 Profile 解析逻辑
      
      代码优化:
      - 删除已被 TLSFingerprintProfileService 完全取代的 registry.go 死代码(418 行)
      - 提取 3 个 dialer 的重复 TLS 握手逻辑为 performTLSHandshake() 共用函数
      - 修复 GetTLSFingerprintProfileID 缺少 json.Number 处理的 bug
      - gateway_service.Forward 中 ResolveTLSProfile 从重试循环内重复调用改为预解析局部变量
      - 删除冗余的 buildClientHelloSpec() 单行 wrapper 和 int64(e.ID) 无效转换
      - tls_fingerprint_profile_cache.go 日志从 log.Printf 改为 slog 结构化日志
      - dialer_capture_test.go 添加 //go:build integration 标签,防止 CI 失败
      - 去重 TestProfileExpectation 类型至共享 test_types_test.go
      - 修复 9 个测试文件缺少 tlsfingerprint import 的编译错误
      - 修复 error_policy_integration_test.go 中 handleError 回调签名被错误替换的问题
      b22ab6ad
  8. 27 Mar, 2026 2 commits
    • QTom's avatar
      fix(gateway): 修复 OpenAI→Anthropic 转换路径 system prompt 被静默丢弃的 bug · c729ee42
      QTom authored
      
      
      injectClaudeCodePrompt 和 systemIncludesClaudeCodePrompt 的 type switch
      无法匹配 json.RawMessage 类型(Go typed nil 陷阱),导致 ForwardAsResponses
      和 ForwardAsChatCompletions 路径中用户 system prompt 被替换为仅 Claude Code
      banner。新增 normalizeSystemParam 将 json.RawMessage 转为标准 Go 类型。
      Co-Authored-By: default avatarClaude Opus 4.6 (1M context) <noreply@anthropic.com>
      c729ee42
    • shaw's avatar
      feat(tls-fingerprint): 新增 TLS 指纹 Profile 数据库管理及代码质量优化 · 1854050d
      shaw authored
      新增功能:
      - 新增 TLS 指纹 Profile CRUD 管理(Ent schema + 迁移 + Admin API + 前端管理界面)
      - 支持账号绑定数据库中的自定义 TLS Profile,或随机选择(profile_id=-1)
      - HTTPUpstream.DoWithTLS 接口从 bool 改为 *tlsfingerprint.Profile,支持按账号指定 Profile
      - AccountUsageService 注入 TLSFingerprintProfileService,统一 usage 场景与网关的 Profile 解析逻辑
      
      代码优化:
      - 删除已被 TLSFingerprintProfileService 完全取代的 registry.go 死代码(418 行)
      - 提取 3 个 dialer 的重复 TLS 握手逻辑为 performTLSHandshake() 共用函数
      - 修复 GetTLSFingerprintProfileID 缺少 json.Number 处理的 bug
      - gateway_service.Forward 中 ResolveTLSProfile 从重试循环内重复调用改为预解析局部变量
      - 删除冗余的 buildClientHelloSpec() 单行 wrapper 和 int64(e.ID) 无效转换
      - tls_fingerprint_profile_cache.go 日志从 log.Printf 改为 slog 结构化日志
      - dialer_capture_test.go 添加 //go:build integration 标签,防止 CI 失败
      - 去重 TestProfileExpectation 类型至共享 test_types_test.go
      - 修复 9 个测试文件缺少 tlsfingerprint import 的编译错误
      - 修复 error_policy_integration_test.go 中 handleError 回调签名被错误替换的问题
      1854050d