Wesley Liddick and GitHub
bfabfe60c8
Merge pull request #4593 from StarryKira/fix/image-storage-env-unreachable
...
fix: 异步生图开关配了却不生效(环境变量被静默丢弃 + 迁移到后台开关)
2026-07-20 09:20:15 +08:00
Jlypx and Sisyphus
dd0cbe91c9
fix: 避免复制客户端 IP 原子状态
...
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent )
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai >
2026-07-20 00:22:45 +08:00
Jlypx and Sisyphus
041db5d824
feat: 支持自定义客户端 IP 请求头配置
...
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent )
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai >
2026-07-20 00:08:26 +08:00
Jlypx and Sisyphus
8a147fcc51
fix: 解析显式可信代理配置
...
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent )
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai >
2026-07-19 21:39:24 +08:00
Jlypx
732aeef880
fix: 兼容反代和 Docker 客户端 IP 解析
2026-07-19 19:41:01 +08:00
haruka and Claude Opus 4.8
37db8d031b
fix(config): 让环境变量能真正配置 image_storage 等凭证
...
viper.Unmarshal 只解码 AllKeys() 返回的键,而 AllKeys() 只汇总 SetDefault、
配置文件和显式 BindEnv 三个来源。AutomaticEnv 仅能覆盖已在其中的键,无法引入
新键;能兜底的 viper_bind_struct 又被 build tag 排除(我们只用 -tags embed)。
因此任何「没有注册默认值、且不在 config.yaml 里」的配置项,其环境变量会被静默
丢弃。image_storage 的 endpoint/bucket/access_key_id/secret_access_key/
public_base_url 正属此列,于是纯环境变量部署落到最坏组合:IMAGE_STORAGE_ENABLED
生效使 Enabled=true,四个凭证却为空 → Active()=false → 异步生图接口整体 404,
运维看到的却是"凭证不完整"。deploy/docker-compose.yml 默认就是纯环境变量驱动,
且自动生成的 config.yaml 从不写 image_storage 段,必然踩中(见 #4458、#4542)。
同类缺口不止于此:github_oauth、google_oauth、dingtalk_connect 三组第三方登录
配置(含 client_secret)同样完全无法用环境变量设置。
- 为这些键注册零值默认,使其进入 AllKeys() 而可被环境变量覆盖。零值与"键缺失"
时的解码结果一致,故行为不变。
- sticky_escape_enabled 例外:它的实际默认是 true(靠 IsSet 守卫在解码后补上),
注册 false 会让 IsSet 恒真而永久关闭该特性,故直接注册 true。
- 启动告警补上 missing_keys 字段,指明到底哪个凭证为空。
- 新增反射守卫测试:Config 结构体上每个可由环境变量表达的字段都必须已注册默认值。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Claude-Session: https://claude.ai/code/session_01VHreE5pzCkSYz7J45fmd2Y
2026-07-18 05:24:32 -07:00
benjamin
b92bbf0299
fix: 过滤入口拒绝日志并强化鉴权边界
2026-07-18 00:11:18 +08:00
haruka and Claude Opus 4.8
0eb6e21aaa
feat: 异步图片任务结果落对象存储
...
为异步生图任务增加 S3 兼容对象存储支持,任务结果不再把大图内联存进 Redis:
- 新增可插拔接口 service.ImageStorage(Save -> url),适配别的厂商只需实现它
- S3 实现 S3ImageStorage(AWS S3 / R2 / 阿里云 OSS / MinIO),与备份共用 S3 客户端构造
- 新增 image_storage 配置(config.yaml + IMAGE_STORAGE_* 环境变量),默认关闭
- enabled 同时作为总开关:关闭或未配置对象存储时,异步生图接口返回 404 且不写
Redis,从根上避免几 MB 的 b64_json 结果撑爆 Redis
- 完成时把图片上传对象存储并把结果改写为短链接(公开直链或 presigned),
上传失败则任务标记为失败
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
Claude-Session: https://claude.ai/code/session_01SM1tf3CFVRzC7guuhBXvMd
2026-07-15 19:57:37 -07:00
Tian Lee
90ee85f3ef
feat: 按上游计费倍率调度 OpenAI 账号
2026-07-16 00:40:46 +08:00
Wesley Liddick and GitHub
0de768e8be
Merge pull request #4221 from heathermhuang/codex/fix-grok-oauth-pool-health
...
fix(grok): refresh OAuth pools proactively
2026-07-15 16:07:09 +08:00
Wesley Liddick and GitHub
bac925624f
Merge pull request #4289 from Tiantianr/fix/openai-ws-first-message-timeout
...
fix(openai-ws): make first-message timeout configurable
2026-07-15 15:45:24 +08:00
王鹏
fc4089f292
fix(openai): bound native responses first output wait
...
Add an opt-in first semantic output budget for native HTTP Responses, including response-header wait. Keep preamble and keepalive bytes non-semantic so a stalled account can fail over once without replaying its response IDs. Defaults remain disabled.
Related to #4201 , #4185 , and #4248 . Complements the HTTP/2 dead-connection fix in #4207 .
2026-07-15 13:01:16 +08:00
Heatherm Huang
6b25900403
fix(grok): refresh OAuth pools proactively
2026-07-15 09:40:08 +08:00
Tiantianr
74e296703a
fix(openai-ws): make first-message timeout configurable
...
Add a dedicated client first-message timeout while preserving the legacy 30-second default.
Use the resolved value for both the WebSocket read deadline and structured timeout logs, and document tuning for large requests or slow links.
Add configuration, validation, handler, and resolver regression coverage.
Refs #4158
2026-07-14 22:40:05 +08:00
Wesley Liddick and GitHub
c361b0606d
Merge pull request #4219 from zh239ns/codex/fix-openai-images-nonstream-keepalive
...
fix(images): add opt-in non-stream JSON keepalive
2026-07-14 11:30:28 +08:00
zh239ns
002c0b9fda
fix(images): keep non-stream requests alive
2026-07-14 07:39:21 +08:00
bestony and multica-agent
54d228dda5
feat(admin): add opt-in server timing metrics
...
Co-authored-by: multica-agent <github@multica.ai >
2026-07-14 01:29:30 +08:00
Bestony@Homelab
c8cfc93632
fix(openai-ws): bound ingress session lifecycle
2026-07-13 15:32:42 +08:00
Turtle_Li
3c43fdec11
docs: add batch image PR readiness notes
2026-07-07 03:31:39 +08:00
Turtle_Li
9703ca9d33
merge: sync batch image foundation with upstream main
2026-07-06 13:40:09 +08:00
Turtle_Li
8fab636998
feat: complete batch image workflow
2026-07-06 12:22:04 +08:00
linshuboy
f26ca5661e
feat: add OpenAI advanced scheduler controls
...
Related: #1089 , #408 , #123
2026-07-05 17:24:38 +08:00
Turtle_Li
a994fbd77a
feat: add batch image MVP
2026-07-04 05:30:50 +08:00
shaw
a1b2b32e08
fix: prevent silent usage_logs drops under queue overflow ( #3656 )
...
高并发下扣费成功但 usage_logs 被双层异步队列静默丢弃(单用户实测丢失 49%),
导致消费排行与对账缺口。根因是 6a685727 为避免溢出时单行 INSERT 踩踏数据库,
把三处"队列满"改成了立即终态丢弃。
改为有界阻塞背压,兼顾数据不丢与批量写入模式不变:
- CreateBestEffort/createBatched 入队移除 default 立即丢弃分支,
队列满时阻塞等待(受 detached 15s ctx 期限约束)
- writeUsageLogBestEffort 收到 dropped 不再直接放弃,统一走
repo.Create 同步兜底(仍经批处理器;ctx 耗尽时换新 detached 窗口),
重复写入由 ON CONFLICT DO NOTHING 幂等防护
- worker 池默认溢出策略 sample→sync(viper 默认 + 池常量),
溢出时提交方内联执行(提交点在响应写出后,不阻塞客户端),
显式配置 sample/drop 的部署不受影响
- 附带修复 ensure*Batcher 的存量数据竞争:channel nil 检查移入 sync.Once
正常负载零行为变化;突发时短暂等待替代永久丢失;数据库只见批量 INSERT。
2026-07-03 21:02:32 +08:00
zy6p
901958ba1b
feat(openai-ws): add http_bridge ingress mode and account ws selector
...
(cherry picked from commit 58647ff63d7ff994c7c14c84c4913a8d3ed6be05)
(cherry picked from commit 9f18fb7c24e187fb20e90d1d9e89fcec91c56937)
2026-06-30 10:40:05 +08:00
Wesley Liddick and GitHub
7c857bd080
Merge pull request #3441 from deqiying/feature/openai-quota-headroom-scheduler
...
新增 OpenAI 剩余额度调度权重
2026-06-29 09:23:32 +08:00
deqiying
a2cf297d90
feat: 新增 OpenAI quota headroom 调度权重
2026-06-24 00:13:22 +08:00
wucm667
9f5b57fc96
fix(billing): 防止余额计费持续透支
2026-06-22 10:30:27 +08:00
kangjwme
510adf703c
feat(scheduling): add opt-in "prefer soonest reset" account selection
...
Adds a use-it-or-lose-it scheduling strategy: prefer accounts whose
session window resets soonest, so near-reset accounts get drained first
instead of accounts whose reset is still far away.
Both schedulers, opt-in, default behavior unchanged:
- Anthropic (gateway_service.go): new GatewaySchedulingConfig
.PreferSoonestReset flag. When on, the layered load-aware selection
inserts a filterBySoonestReset stage (priority -> soonest-reset ->
load -> LRU). Accounts with no active SessionWindowEnd are treated as
lowest priority; ties fall through to LRU.
- OpenAI/Codex (openai_account_scheduler.go): new "reset" score weight
in GatewayOpenAIWSSchedulerScoreWeights. Soonest-reset accounts score
higher; weight defaults to 0 (no effect).
SessionWindowEnd (upstream 5h/quota ResetsAt) is already carried in the
scheduler snapshot, so no snapshot changes are needed.
Documented in deploy/config.example.yaml. Adds unit tests for the
Anthropic filter and the OpenAI reset factor.
2026-06-18 22:50:46 +08:00
Wesley Liddick and GitHub
f332e0a83c
Merge pull request #2872 from wucm667/fix/scheduler-sticky-health-escape
...
fix(scheduler): session_hash sticky 引入健康度逃逸,慢账号不再独占用户会话
2026-06-05 13:54:37 +08:00
xlx0852
08e19bb15c
fix(openai): bridge oversized websocket requests
2026-06-01 10:42:55 +08:00
DaydreamCoding and Claude Opus 4.8
f7f5e33830
feat(quota): user×platform 配额 DB 写聚合 flusher
...
Redis 同步权威 + DB 镜像,不在进程内维护 delta:
- 写入点 HasUserPlatformQuotaLimit 守卫:无 limit 跳过 Redis 写与持久化
- 累加 usage 的 Lua 在 flusher_enabled 时 SADD 脏集 billing:upq:dirty
- UserPlatformQuotaUsageFlusher 定时 SPOP 脏集 → 批量 HGETALL 读当前窗口 usage 快照
→ BatchSnapshotUsage 绝对值 UPSERT 覆盖 DB(去 SELECT FOR UPDATE 行锁)
→ 失败 SADD 回 / FK(23503)整批丢弃
- flusher 单批 clamp 到 ≤6000,保证一次 flush 只生成一条 UPSERT(单事务原子)
- flusher_enabled 默认 false(降级=旧异步直写 DB)
效果:DB 写连接从 O(QPS) 收敛到 O(副本)。
循环依赖:service 层独立 Snapshot/FK 类型,repository adapter 转换 + %w 映射 FK error。
admin reset/upsert 后失效 cache(脏残留被 flusher 当 MISS 跳过)。
健壮性与可观测性:
- flusher_enabled=false 时 Start 不注册定时器;flush_interval_ms 非法回退 2s
- Readd 回填失败单独计 dirty_lost(不再误记 dirty_readd)并 ALERT;脏集 Readd 补兜底 TTL
- 单 tick 达 max batches 上限仍有积压时记 log
- admin 失效 cache 失败升级为 ALERT(提示 enforcement 可能延迟至 sentinel TTL)
- BatchGet 单条命令失败 / usage 字段损坏均记 log,避免静默以 0 覆写 DB
三态 go vet + 单测/集成测全绿。
已知取舍(默认 flusher_enabled=false 不触发):
- FK 整批丢弃牵连同批正常 key(活跃 key 靠下次 SADD+绝对值快照自愈;Redis 仍权威)
- admin reset/upsert 直写 DB 与 flusher 异步刷存在覆盖竞态:flusher 持旧快照在途时可能覆盖
admin 刚写值(limit 列不受影响;usage 有 preflight windowExpired 兜底;低频)。彻底消除需 version OCC。
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-05-29 17:19:15 +08:00
DaydreamCoding and Claude Opus 4.8
06fca66273
feat(quota): sentinel 回填消除无配额行用户 preflight 每请求回源 DB
...
无 user×platform 配额行的用户,preflight 每次 cache MISS 后回源 DB 查得"无行"
却不缓存该结论,导致每请求一次 DB 往返。本 PR 回填 sentinel 占位 entry,使后续
请求命中 Redis 后稳定判"无 limit",TTL 内不再查 DB。
- config: 加 UserPlatformQuotaSentinelTTLSeconds(默认 3600s,短于普通 quota
cache 的 86400s 以控 Redis 内存)
- metrics: 加 userPlatformQuotaSentinelSetCacheErrorTotal,并入
GatewayUserPlatformQuotaIncrStats 暴露
- billing_cache: checkUserPlatformQuotaEligibility 在 cache MISS + DB 无行且
cacheErr==nil 时回填 sentinel(三 limit nil、三 window_start non-nil、SchemaV1);
TTL<=0 fallback 1h 防 EXPIRE 立即删 key 击穿;SET 失败 fail-open + 计 metric
- billing_cache: HIT 路径对 sentinel(三 limit nil)跳过 windowExpired refresh,
避免短 sentinel TTL 被误升级为 86400s
有配额 limit 的用户 enforcement 行为不变(rec!=nil 不回填、isSentinel=false 不跳过 refresh)。
测试:扩展 fakeFullCache 夹具(setCalls/lastSetTTL/getErr/setErr);新增回填正确性 /
Redis-GET-故障不回填 / SET-失败 fail-open / sentinel 跨窗口不 refresh 四个单测。
go build、quota+billing unit、三态 go vet 全绿。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-05-29 17:19:15 +08:00
wucm667
415d08f255
fix(scheduler): add sticky health escape
2026-05-29 10:25:26 +08:00
Wesley Liddick and GitHub
bebc082306
Merge pull request #2766 from DaydreamCoding/feat/user-platform-quota
...
feat(quota): 用户 × 平台 USD 配额
2026-05-26 14:13:18 +08:00
mt21625457
33ac8eb27d
fix openai http2 response header timeout
2026-05-26 13:57:59 +08:00
6b39b344d8
feat(quota): 用户 × 平台 USD 配额
...
为用户在 anthropic/openai/gemini/antigravity 四个平台上提供日/周/月
三个窗口的 USD 配额管控。配额语义:未设置=不限制,0=禁用,>0=美元上限。
两层模型:
- 配置层:系统默认配额,以及 email/linuxdo/oidc/wechat/github/google/
dingtalk 七个鉴权来源的默认配额,存于 settings,以嵌套 JSON 整体读写
(系统 1 个 key + 每个来源 1 个 key),整体替换语义。
- 运行时层:user_platform_quota 表按用户记录实际配额,与配置层解耦。
后端:新增 ent schema 与 140_user_platform_quotas.sql 迁移、repository
与 service 端口、计费链路集成、管理端与用户端读写接口。
前端:管理端设置页配额编辑、用户配额管理 Modal、用户 Dashboard 展示、
中英文案。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com >
2026-05-26 10:49:20 +08:00
shaw
1e406fed52
fix: optimize OpenAI account cooldown scheduling
2026-05-23 10:18:43 +08:00
lyen1688
1d2445ff52
修复 API Key ACL 开关的 CI 校验
2026-05-20 23:51:39 +08:00
lyen1688
08c8c67df7
为 API Key ACL 增加反代真实 IP 开关
2026-05-20 22:51:46 +08:00
b19da9c7fe
feat(dingtalk): 钉钉 OAuth 登录接入与 internal_only 用户属性同步
...
⚠️ 应用类型约束:当前实现仅支持「钉钉登录-企业内部应用」(DingTalk 开放平台
internal_app 类型)。第三方个人应用、第三方企业应用类型暂不支持——OAuth 流程
相同但 corp 校验、跨企业行为不同。backend 通过 DingTalkAppKind 校验对非
internal_app 类型 fail-closed(硬约束)。
钉钉 OAuth 登录主链
- 4 步 OAuth 链:ExchangeCodeForUserToken / GetUnionIdByUserToken /
GetUserIdByUnionId / GetStaffInfoByUserId;app token 缓存
- pending session 机制持久化 OAuth 中间态;cookie-only token 持久化
- 三种分流:bind_login_required / email_completion / choose_account_action
- corp_restriction_policy 支持 none + internal_only;stale "whitelist" 在
加载层与写入层均静默 coerce 为 none + slog.Warn
- bypass_registration 开关:企业内部模式豁免全局 REGISTRATION_DISABLED
- isReservedEmail / signup_source / canUnbindProvider / OAuth pending flow
等横切点支持 dingtalk provider
- migration 136:4 表 CHECK 约束加入 'dingtalk' provider 值
internal_only 模式同步企业邮箱/姓名/部门到用户属性
- SyncCorpEmail / SyncDisplayName / SyncDept 三个独立开关 + 对应
SyncXxxAttrKey 目标属性 key(默认 dingtalk_email / dingtalk_name /
dingtalk_department);非 internal_only policy 在写入层与加载层均
coerce 为 false,admin handler 与 setting_service 双层兜底
- 同步语义:首次注册写 users.username(昵称优先 → 企业姓名 fallback),
之后每次登录刷新 3 个属性;空值也写入以覆盖旧值
- 邮箱三级 fallback:org_email > email > extension["企业邮箱"]
(钉钉自定义字段 JSON)
- 部门路径递归向上拼接,跳过 dept_id=1 选首个真实子部门,剥离根组织名
- GetUnionIdByUserToken 同时返回 OIDC /contact/users/me 的 nick 字段;
新增 GetDeptInfo 调用 OAPI /topapi/v2/department/get
- AuthHandler 注入 UserAttributeService;OAuth pending flow 在
createPendingOAuthAccount / bindPendingOAuthLogin 分别派发到
AfterRegistration(syncUsername=true)/ AfterLogin
- migration 137 seed dingtalk_email/name/department 三个用户属性定义
附带修复(同集成路径暴露的两个 OAuth 注册回归)
- LoginOrRegisterOAuthWithTokenPair 新建用户分支用 inferLegacySignupSource
覆写 caller 显式传入的 signupSource,导致 dingtalk/linuxdo/oidc/wechat
渠道授权按 email 渠道读取;改为只在 caller 未显式传入时回退邮箱推断
- mergeProviderDefaultGrantSettings 把 parse fallback 默认值
(Concurrency=5 / Balance=0) 当作"未配置"哨兵,admin 显式设 5 时被误判
退回全局默认(复现:全局默认 1 + 渠道默认并发 5 + grant_on_signup → 新
用户实际 concurrency=1);去掉哨兵,admin 任何 >=0 值都覆盖 globalDefaults
前端
- DingTalk Login / Callback / EmailCompletion / ChoiceAccount / Error
视图;router + auth API client
- admin SettingsView:corp policy radio(none / internal_only)+ bypass
注册开关 + i18n;internal_only 下展示三同步开关 + 目标 attr key 下拉
(拉取 user attribute definitions),展示 fieldEmail /
qyapi_get_department_list 钉钉权限申请提示
- Profile:S1 主动绑定 / S5 解绑钉钉按钮 + 合成邮箱防自锁
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-19 15:27:47 +08:00
shaw
b23055af5b
feat: add Airwallex payments and multi-currency support
2026-05-11 11:17:26 +08:00
Wesley Liddick and GitHub
45b1e6ae41
Merge pull request #2233 from Arron196/fix/codex-image-generation-bridge-switch
...
fix(openai): 增加 Codex 图片生成桥接显式开关
2026-05-07 10:30:26 +08:00
Jlypx and Sisyphus
26043a8f29
fix(openai): gate Codex image bridge injection
...
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent )
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai >
2026-05-07 00:10:20 +08:00
lyen1688
af550fa64e
feat: 增加 GitHub 和 Google 邮箱快捷登录
2026-05-06 16:06:11 +08:00
shaw
11ae6f2105
fix(rate-limit): remove 429 cooldown config option
2026-05-05 20:11:12 +08:00
Wesley Liddick and GitHub
37f7c7128c
Merge pull request #2120 from gaoren002/fix/rate-limit-429-cooldown-config
...
fix(rate-limit): make 429 fallback cooldown configurable
2026-05-05 19:46:11 +08:00
2ue
6faa344916
feat: add OpenAI image generation controls
2026-05-05 03:26:54 +08:00
gaoren002
4b904c887c
fix(rate-limit): make 429 fallback cooldown configurable
2026-04-30 03:01:39 +00:00
IanShaw027
36aed35957
fix(auth): harden oauth identity upgrade paths
2026-04-22 14:56:56 +08:00