Commit Graph

2732 Commits

Author SHA1 Message Date
erio
29d58f2414 feat(channel): 模型映射 + 分组搜索 + 卡片折叠 + 冲突校验
- 渠道模型映射:新增 model_mapping JSONB 字段,在账号映射之前执行
- 分组选择:添加搜索过滤 + 平台图标
- 定价卡片:支持折叠/展开,已有数据默认折叠
- 模型冲突校验:前后端均禁止同一渠道内重复模型
- 迁移 083: channels 表添加 model_mapping 列
2026-04-04 11:06:36 +08:00
erio
dca0054e93 feat(channel): 模型标签输入 + $/MTok 价格单位 + 左开右闭区间 + i18n
- 模型输入改为标签列表(输入回车添加,支持粘贴批量导入)
- 价格显示单位改为 $/MTok(每百万 token),提交时自动转换
- Token 模式增加图片输出价格字段(适配 Gemini 图片模型按 token 计费)
- 区间边界改为左开右闭 (min, max],右边界包含
- 默认价格作为未命中区间时的回退价格
- 添加完整中英文 i18n 翻译
2026-04-04 11:01:22 +08:00
erio
983fe58959 fix: CI lint/test fixes — gofmt, errcheck, handler test args 2026-04-04 11:01:22 +08:00
erio
91c9b8d062 feat(channel): 渠道管理系统 — 多模式定价 + 统一计费解析
Cherry-picked from release/custom-0.1.106: a9117600
2026-04-04 11:00:55 +08:00
Wesley Liddick
b384570de3 Merge pull request #1439 from touwaeriol/feat/redeem-negative-value
feat(redeem): support negative values for refund/deduction
2026-04-03 22:22:54 +08:00
Wesley Liddick
0507852a34 Merge pull request #1437 from DaydreamCoding/fix/openai-oauth-improvements
feat(openai): OpenAI OAuth 账号管理增强:订阅状态、隐私设置、Token 刷新修复
2026-04-03 09:22:10 +08:00
Wesley Liddick
7b6ff135fb Merge pull request #1424 from DaydreamCoding/fix/antigravity-batch-privacy
fix(antigravity): 修复批量刷新令牌不设置隐私模式的问题
2026-04-03 09:21:39 +08:00
erio
bf24de88ed style: fix gofmt struct tag alignment 2026-04-03 02:00:21 +08:00
erio
ff6d4ab39a chore: add lodash/lodash-es audit exception for GHSA-r5fr-rjxr-66jc 2026-04-03 01:53:17 +08:00
erio
66fde7a2e6 feat(redeem): support negative values for refund/deduction
Allow redeem codes with negative values to enable refund scenarios:
- Balance: negative value deducts balance (clamped to 0, never negative)
- Concurrency: negative value reduces concurrency (clamped to 0)
- Subscription: negative validity_days reduces remaining days; if
  remaining days <= 0, the subscription is canceled (set to expired)

All deductions generate standard redeem code records for audit trail.
2026-04-03 01:50:26 +08:00
QTom
e8efaa4cd9 style: gofmt struct field alignment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 20:50:38 +08:00
QTom
00947d6492 feat(openai): 显示订阅到期时间
从 /backend-api/accounts/check 的 entitlement.expires_at 提取订阅
到期日期,每次 token 刷新时更新并存入 credentials,前端账号列表
的订阅类型和隐私下方以灰色小字显示(仅非 Free 账号)。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 20:44:28 +08:00
QTom
cf70fb1b4e fix(openai): Mobile RT 账号隐私设置失败
1. CreateAccount 补齐 OpenAI OAuth 隐私入口(与 BatchCreate 对齐)
2. disableOpenAITraining 请求头修正:覆盖 ImpersonateChrome() 的
   浏览器导航默认头(accept: text/html, sec-fetch-mode: navigate),
   改为 API 请求语义(Accept: application/json, sec-fetch-mode: cors),
   避免 Cloudflare 将 PATCH API 请求误判为异常导航流量而拦截

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 20:44:22 +08:00
QTom
ef1a992cf0 fix(openai): refresh token when expires_at missing and account is rate-limited
Prevents token silent expiry during 7-day rate limit periods.

Made-with: Cursor
2026-04-02 20:44:12 +08:00
QTom
1f6a73f0db fix(openai): treat 401 {"detail":"Unauthorized"} as permanent auth failure
- ratelimit_service: detect non-standard OpenAI 401 format and permanently disable account
- account_test_service: mark account error on 401 during connection test

Made-with: Cursor
2026-04-02 20:44:05 +08:00
QTom
f2e596f6ec fix(oauth): 每次刷新都通过 backend-api 获取最新 plan_type
账号订阅类型可能每月变化,id_token 中的 plan_type 是签发时的快照,
不一定反映当前状态。移除 plan_type == "" 前置条件,确保每次刷新都
调用 ChatGPT backend-api 获取实时订阅类型并覆盖旧值。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 20:43:56 +08:00
Yanzhe Lee
77ba9e728d Merge branch 'Wei-Shaw:main' into fix/openai-gateway-content-session-hash-fallback 2026-04-02 01:56:18 +08:00
YanzheL
cf9efefd96 fix(lint): satisfy errcheck for strings.Builder.WriteString calls 2026-04-02 01:03:22 +08:00
YanzheL
4fb1603001 test(gateway): add tests for content-based session hash fallback
- 20 unit tests for deriveOpenAIContentSessionSeed covering:
  - Empty/nil inputs, model-only, stable across turns
  - Different model/system/first-user produce different seeds
  - Tools, functions, developer role, structured content
  - Responses API: input string, input array, instructions, input_text typed items
  - JSON canonicalization (whitespace/key-order insensitive)
  - Prefix presence, empty tools ignored, messages preferred over input
- 3 integration tests for GenerateSessionHash content fallback:
  - Content fallback produces stable hash
  - Explicit signals override content fallback
  - Empty body still returns empty hash
2026-04-02 00:11:17 +08:00
YanzheL
c5aac1251d fix(gateway): add content-based session hash fallback for non-Codex clients
When no explicit session signals (session_id, conversation_id, prompt_cache_key)
are provided, derive a stable session seed from the request body content
(model + tools + system prompt + first user message) to enable sticky routing
and prompt caching for non-Codex clients using the Chat Completions API.

This mirrors the content-based fallback already present in GatewayService.
GenerateSessionHash, adapted for the OpenAI gateway's request formats (both
Chat Completions messages and Responses API input).

JSON fragments are canonicalized via normalizeCompatSeedJSON to ensure
semantically identical requests produce the same seed regardless of
whitespace or key ordering.

Closes #1421
2026-04-02 00:11:06 +08:00
QTom
b155bc564b fix(antigravity): 修复批量刷新令牌不设置隐私模式的问题
- refreshSingleAccount ProjectIDMissing 提前返回前补上 EnsureAntigravityPrivacy 调用
- EnsureAntigravityPrivacy 跳过条件从"有任何值"改为"仅 privacy_set 成功时跳过",
  privacy_set_failed 允许重试,对齐 OpenAI shouldSkipOpenAIPrivacyEnsure 的行为
- 后台 TokenRefreshService.ensureAntigravityPrivacy 同步修改
- ExchangeCode/ValidateRefreshToken 获得令牌后立即调用 setAntigravityPrivacy,
  不依赖后续账号创建流程

Made-with: Cursor
2026-04-01 12:24:52 +08:00
Wesley Liddick
055c48ab33 Merge pull request #1262 from InCerryGit/main
fix(openai): preserve bare gpt-5.3-codex-spark across forwarding paths
2026-04-01 08:31:12 +08:00
Wesley Liddick
6663e1eda6 Merge pull request #1420 from YanzheL/fix/1202-gemini-customtools-404
Fix Gemini CLI 404s for gemini-3.1-pro-preview-customtools
2026-04-01 08:30:40 +08:00
YanzheL
649afef512 fix(handler): fallback known gemini models on v1beta 404 2026-04-01 02:20:13 +08:00
YanzheL
4514f3fc11 fix(gemini): resolve customtools alias in mapping lookup 2026-04-01 02:19:42 +08:00
YanzheL
095bef9554 fix(gemini): add customtools fallback metadata 2026-04-01 02:19:10 +08:00
YanzheL
f00351c106 fix(openai): sanitize empty base64 input images 2026-04-01 00:46:38 +08:00
YanzheL
936fce68d0 fix(apicompat): skip empty base64 image URLs 2026-04-01 00:46:16 +08:00
YanzheL
d978ac97f1 test(antigravity): cover mixed web search transforms 2026-04-01 00:46:14 +08:00
YanzheL
dd5978f222 fix(gemini): normalize ai studio google search tools 2026-04-01 00:45:56 +08:00
YanzheL
0ebe0ce585 fix(gemini): preserve google search in Claude compat tools 2026-04-01 00:33:39 +08:00
YanzheL
c8cfad7c00 fix(antigravity): preserve google search with function tools 2026-04-01 00:33:16 +08:00
Wesley Liddick
83a16dec19 Merge pull request #1407 from DaydreamCoding/feat/cache-driven-rpm-buffer
feat(gateway): Cache-Driven RPM Buffer
2026-03-31 14:01:23 +08:00
Wesley Liddick
820c531814 Merge pull request #1406 from DaydreamCoding/feat/group-account-filter
feat(group-filter): 分组账号过滤控制 — require_oauth_only + require_privacy_set
2026-03-31 14:01:05 +08:00
Wesley Liddick
1727b8df3b Merge pull request #1404 from DaydreamCoding/feat/antigravity-privacy-on-refresh-fail
feat(antigravity): 令牌刷新失败及创建账号时也设置隐私
2026-03-31 14:00:53 +08:00
shaw
a025a15f5d feat: add refresh button to admin and user dashboard pages 2026-03-31 13:53:49 +08:00
QTom
72e5876c64 feat(gateway): Cache-Driven RPM Buffer
- 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:24:22 +08:00
QTom
aeed2eb9ad feat(group-filter): 分组账号过滤控制 — require_oauth_only + require_privacy_set
为 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: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:04:55 +08:00
QTom
46bc5ca73b feat(antigravity): 令牌刷新失败及创建账号时也设置隐私
- token_refresh: 不可重试错误和重试耗尽两条路径添加 ensureAntigravityPrivacy
- admin_service: CreateAccount 为 Antigravity OAuth 账号异步设置隐私

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:42:23 +08:00
InCerry
0b3feb9d4c fix(openai): resolve Anthropic compat mapping from normalized model
Anthropic compat requests normalize reasoning suffixes before forwarding, but the account mapping step was still using the raw request model. Resolve billing and upstream models from the normalized compat model so explicit account mappings win over fallback defaults.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-03-31 10:33:28 +08:00
InCerry
ca8692c747 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	backend/internal/service/openai_gateway_messages.go
2026-03-31 09:38:40 +08:00
weak-fox
a61d58716f fix(admin): exclude rate-limited accounts from active filter 2026-03-31 00:00:46 +08:00
qingyuzhang
6b646b6127 fix(openai): fail over passthrough 429 and 529 2026-03-30 22:29:26 +08:00
shaw
318aa5e0d3 feat: add cache hit rate line to token usage trend chart
Add a purple dashed line showing cache hit rate percentage
(cache_read / (cache_read + cache_creation)) on a secondary
right Y-axis (0-100%). Applies to both user and admin dashboards.
2026-03-30 21:43:07 +08:00
haruka
49e99e9d51 fix: resolve errcheck lint for sync.Map type assertion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:44:15 +08:00
shaw
1dfd974432 chore: update readme 2026-03-30 16:28:31 +08:00
shaw
cc396f59cf chore: update readme 2026-03-30 16:24:29 +08:00
haruka
ad2cd97618 fix: resolve refresh token race condition causing false invalid_grant errors
When multiple goroutines/workers concurrently refresh the same OAuth token,
the first succeeds but invalidates the old refresh_token (rotation). Subsequent
attempts using the stale token get invalid_grant, which was incorrectly treated
as non-retryable, permanently marking the account as ERROR.

Three complementary fixes:
1. Race-aware recovery: after invalid_grant, re-read DB to check if another
   worker already refreshed (refresh_token changed) — return success instead
   of error
2. In-process mutex (sync.Map of per-account locks): prevents concurrent
   refreshes within the same process, complementing the Redis distributed lock
3. Increase default lock TTL from 30s to 60s to reduce TTL-expiry races

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 16:23:38 +08:00
github-actions[bot]
aa8b9cc508 chore: sync VERSION to 0.1.106 [skip ci] 2026-03-30 08:13:49 +00:00
Wesley Liddick
6a2cf09ee0 Merge pull request #1349 from touwaeriol/feat/antigravity-internal500-penalty
feat(antigravity): progressive penalty for consecutive INTERNAL 500 errors
2026-03-30 15:54:04 +08:00