fix(openai): honor OpenCode Go usage reset durations - #5658
Open
william-drakemond wants to merge 1 commit into
Open
fix(openai): honor OpenCode Go usage reset durations#5658william-drakemond wants to merge 1 commit into
william-drakemond wants to merge 1 commit into
Conversation
Contributor
|
All contributors have signed the CLA. ✅ |
Recognize GoUsageLimitError responses and parse both single and composite reset durations from their human-readable messages. Keep API-key accounts rate-limited until the parsed reset instead of falling back to the short default cooldown.
william-drakemond
force-pushed
the
fix/opencode-go-usage-limit-reset
branch
from
August 15, 2026 00:01
d87d4c8 to
5ea482f
Compare
Author
|
I have read the CLA Document and I hereby sign the CLA |
ziyue67
added a commit
to ziyue67/sub2api
that referenced
this pull request
Aug 15, 2026
…i-Shaw#5487 (#29) * ci: skip golangci-lint config verify to avoid schema fetch flakes The action runs `golangci-lint config verify` before linting, which downloads the JSON schema from golangci-lint.run at runtime. That fetch timed out on main (run 31835621724, 21s failure) even though the code and config were fine. Set verify: false so lint results no longer depend on that external request. * feat(opencode): 支持 OpenCode Go 官方用量窗口查询与自动刷新 OpenCode Go 上游(https://opencode.ai/zen/go/v1)官方提供用量端点 GET /zen/go/v1/usage(Bearer 复用消费 key),返回 rolling(5h)/weekly/monthly 三窗口的 percent 与 resetsAt。本提交为 openai apikey 账号(base_url 为 opencode.ai/zen/go/v1)接入该用量窗口: - 后端:OpenCodeGoUsageService——手动刷新(每账号 10s 节流)、 周期自动刷新(全局开关 + 账号开关双开启,interval 5-1440 分钟, leader lock + 每轮 20 账号 + 并发 4)、失败指数退避(上限 24h)、 Retry-After 尊重;401→unauthorized、403→failed;snapshot 持久化于 accounts.extra(单账号写入,无分组 CAS、无会话加密) - admin API:settings GET/PUT、/:id/opencode-go-usage GET、 auto-refresh PUT、refresh POST(与 Ollama Cloud usage 同构) - 前端:账号列表行内三窗口进度条(复用 UsageProgressBar)、 编辑弹窗内配置面板(详情+手动刷新+auto-refresh)、全局设置卡片、 en/zh i18n 验证: - 上游端点已用生产真实 Go key 实测 200,响应 {"usage":{"rolling":{"status":"ok","percent":6,...},...}}(2026-08-13) - go build ./... OK;service/handler/repo/dto/cmd 全量回归 OK - 新增单测 20 例(解析 401/403/畸形 JSON、退避、节流等)全绿 - frontend vitest 新增 7 例 + Ollama 回归 4 例全绿;vue-tsc 零错误 * feat(opencode): 用量窗口支持同 Key 组共享、活动防抖与超窗强刷 与 Ollama Cloud usage 对齐:OpenCode Go 作为同 Key 聚合订阅, 多个 openai/apikey/base_url=opencode.ai/zen/go/v1 账号共享同一份 用量状态与刷新节奏: - 组共享:组指纹 sha256("opencode.ai\0"+api_key);auto_refresh 开关 与 snapshot 按组写(事务 + FOR NO KEY UPDATE + 锚点 CAS + 纯合并, 写 snapshot 不会抹掉开关);列表/详情经 ResolveAccounts 组内共享; 账号换 Key → IdentityChanged → 组级关闭 auto_refresh 并清快照 - 活动防抖 + 超窗强刷:settings 新增 debounce_minutes(默认 1, 1-60,必须小于 interval_minutes);模型请求活动经网关 5 处调用点 驱动刷新,due = min(lastUsed+debounce, fetchedAt+maxWait), 成功路径有 5 分钟最小抓取间隔,失败路径退避优先 - 手动刷新限频 10s→30s 且按组;singleflight/RunDue 按组去重; ListDue 在 SQL 内按组算 due(CTE 分组取组内 MAX(last_used_at)) 验证:go build + 五包回归 OK;新增组共享/三态 due/组单飞/ IdentityChanged/debounce 校验单测全绿;vitest 11 例全绿; vue-tsc 零错误。 * fix(openai): honor OpenCode Go usage reset durations Recognize GoUsageLimitError responses and parse both single and composite reset durations from their human-readable messages. Keep API-key accounts rate-limited until the parsed reset instead of falling back to the short default cooldown. * fix(apicompat): Anthropic→Chat 桥回传工具调用的 thinking,修复 DeepSeek 多轮 400 /v1/messages 打到只支持 Chat Completions 的 OpenAI 兼容上游时, anthropicAssistantToChatMessages 把历史 assistant 消息里的 thinking 块整块丢弃。 DeepSeek 的 thinking mode 要求产生工具调用的 reasoning_content 随该 assistant 消息回传,缺失即 400 —— 表现为「单轮正常,一进多轮工具对话必现失败」。 这些 thinking 块本来就是同一个文件的出站方向 (chatMessageToAnthropicBlocks) 用上游 reasoning_content 生成的,客户端只是 原样回传:桥自己造出来的东西,回来时被自己丢掉。 兄弟路径 Responses→Chat (buildChatMessagesFromItems 的 pendingReasoning) 早已 处理同一约束。本次把同一策略补到漏掉的这条桥上,作用域严格对齐:reasoning 只挂 到带 tool_calls 的 assistant 消息,纯文本轮次维持现状。 Fixes Wei-Shaw#5528 * fix(apicompat): chat/completions file part 不再被静默丢弃,转换为 Responses input_file /v1/chat/completions → Responses 转换层此前只处理 text 和 image_url 两种 content part,type:"file"(PDF 附件)被静默丢弃:请求返回 200、模型正常 回答,但 prompt 里没有文件(prompt_tokens 只剩纯文字)。 现将 file part 映射为 Responses API 的 input_file(filename/file_data/ file_id 透传),与同网关 /v1/responses + input_file 实测可用的格式一致。 无 file_data 且无 file_id 的空 file part 跳过,与空 image URL 行为一致。 参考上游 PR Wei-Shaw#2497(因混入无关改动未合并)。 * fix: preserve fork dependency wiring when porting upstream PRs --------- Co-authored-by: ziyue67 <ziyue67@users.noreply.github.com> Co-authored-by: alfadb <alfadb@163.com> Co-authored-by: William Drake <william.drakemond@gmail.com> Co-authored-by: li <fengshao1227@gmail.com> Co-authored-by: Pengap <penganpingprivte@gmail.com>
ziyue67
added a commit
to ziyue67/sub2api
that referenced
this pull request
Aug 15, 2026
…#5616 Wei-Shaw#5487 (#29)" This reverts commit 3bbdadf.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
GoUsageLimitErrorresponses as account usage limits.Resets in 2 daysand composite values such asResets in 4hr 59min.Problem
OpenCode Go usage-limit responses do not provide the reset through the OpenAI fields already handled by sub2api, such as
resets_atorresets_in_seconds. The parser therefore returned no reset time. API-key accounts then fell through to the configured 429 cooldown, which defaults to 5 seconds, became schedulable again, and repeatedly hit the same exhausted account before its quota actually reset.OpenCode rolling-window messages can contain more than one duration component. Parsing only the first component would turn
4hr 59mininto4hand release the account up to 59 minutes too early.Fix
parseOpenAIRateLimitResetTimenow recognizesGoUsageLimitErrorand, only for that error type, parses the reset duration from its human-readable message.The duration parser:
Resets inmarker.4hr 59min.The computed timestamp flows through the existing 429 path. That path persists the rate limit with
SetRateLimitedand notifies the runtime blocker, so API-key accounts stay out of scheduling until the parsed reset time. Unknown error types continue to return no parsed reset.Tests
Added coverage for:
Validated with:
go test -tags=unit ./internal/service -count=1go test -tags=integration ./...golangci-lint v2.9.0 run ./internal/service --timeout=10m(0 issues)