Sync upstream main through 9c97e78a - #11
Conversation
…ntly retry after an upstream stream reset (QuantumNous#6249) * fix(relay): set Request.GetBody so the HTTP/2 transport can transparently retry after an upstream stream reset The outbound request body is a type-erased io.Reader over BodyStorage, so net/http cannot derive Request.GetBody (it only does so for *bytes.Reader, *bytes.Buffer and *strings.Reader). With GetBody nil, the HTTP/2 transport cannot transparently retry a request once the body has been written and the upstream resets the stream with a retryable error (REFUSED_STREAM, or a connection-level GOAWAY); the relay request then fails with: http2: Transport: cannot retry err [...] after Request.Body was written; define Request.GetBody to avoid this error This affects every relay path that goes through DoApiRequest (chat, claude, gemini, responses, embedding, image, rerank). BodyStorage (memory and disk) already implements io.Seeker, so replay support only needed wiring: - NewOutboundJSONBody additionally returns a getBody that rewinds the storage and hands out a fresh non-closing reader. The transport only calls GetBody after the previous attempt's body has been abandoned, so the rewind cannot race an in-flight read. - RelayInfo carries it in the new UpstreamRequestGetBody field, set alongside UpstreamRequestBodySize by the handlers that build storage-backed bodies. - applyUpstreamGetBody (symmetric with applyUpstreamContentLength) wires it into DoApiRequest/DoFormRequest/DoTaskApiRequest, only when req.GetBody is still nil. Also remove the hand-rolled GetBody override in DoTaskApiRequest: it returned the same already-consumed reader, so any transport-level replay would have silently sent an empty body, and it clobbered the correct snapshot-based GetBody that net/http derives from the *bytes.Reader bodies the task adaptors pass in. For non-replayable bodies GetBody now stays nil, so a retry fails loudly instead of corrupting the request. Covered by unit tests plus an end-to-end raw-frame HTTP/2 test that resets the first stream with REFUSED_STREAM after the body is written and asserts the transport transparently retries with the complete body. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(relay): hand out independent readers from GetBody (address review) Per the http.Request.GetBody contract ("returns a new copy of Body"), each call must yield a reader with its own cursor. The previous implementation rewound and reused the shared BodyStorage, so two consecutive GetBody readers would interfere with each other, and a replay could disturb the primary body's offset under extreme transport timing (e.g. attempt N's body write not yet fully abandoned when the transport builds attempt N+1). Instead of snapshotting the payload (an extra copy), add BodyStorage.NewReader, which returns an independent zero-copy reader: - memory mode: a fresh bytes.Reader over the same immutable backing array; - disk mode: a separate file descriptor over the cache file, so the transport closing a replayed body only closes that descriptor. NewOutboundJSONBody's getBody now simply hands out storage.NewReader, and once the handler releases the storage, GetBody fails with ErrStorageClosed instead of replaying stale data. Tests: interleaved reads across two replay readers and the primary body each observe exactly their own byte stream, for both the memory and the disk-backed storage; the existing GetBody and HTTP/2 retry suites still pass (h2 e2e tests flake-free with -count=20). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(relay): bind replayable metadata on pass-through requests * fix(relay): reset upstream body metadata between channels * test(relay): cover replay across retries and channel attempts * fix(relay): stop following upstream redirects --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(channels): refine fetched model categorization * fix: channel category * fix: hy3 category
…token and aff transfer routes
* fix: 修复兑换码额度精度损失(QuantumNous#6680) * fix(redemption): guard update data integrity
…ntumNous#6561) * feat(billing): highlight matched conditional multipliers in usage logs * fix(billing): make request rule tracing stable and type-safe
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1f838fd113
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| --data-binary "@$request_json" \ | ||
| "$request_url" | ||
| )" | ||
| if [[ "$request_status" != "200" ]]; then |
There was a problem hiding this comment.
Accept the successful create-release status
When the release does not yet exist, this step uses POST /releases, whose successful creation response is 201 Created, but the shared status check accepts only 200. The release is therefore created while the job reports failure, so gitcode-release-assets is skipped and every new release requires a second workflow run. Accept the documented success status for each request method, or accept the full successful 2xx range.
Useful? React with 👍 / 👎.
| --repo "$GITHUB_REPOSITORY" \ | ||
| --json body,isPrerelease | ||
| )" | ||
| release_body="$(jq -r '.body // ""' <<< "$release_json" | sed "s/'/’/g")" |
There was a problem hiding this comment.
Preserve apostrophes in synchronized release notes
Every manually synchronized release now replaces ASCII apostrophes with typographic ones before constructing the JSON request. That escaping was needed by the previous reusable workflow's single-quoted shell input, but jq --arg and --data-binary safely handle apostrophes; retaining the substitution silently corrupts prose and copyable commands or code snippets in GitCode release notes.
Useful? React with 👍 / 👎.
Important
📝 变更描述 / Description
Merge
QuantumNous/new-apiupstream/mainthrough9c97e78ainto the fork without conflicts. The merge preserves the fork's existing commits and deployment behavior, including the mergedllmapi.withcortex.airouting, authentication, and OAuth work.The upstream sync and its verification were AI-assisted. The configured git identity, Shuojie Wang (
latioswang@gmail.com), is not a recurring upstream core developer.🚀 变更类型 / Type of change
🔗 关联任务 / Related Issue
✅ 提交前检查项 / Checklist
Bug fix,我已提交或关联对应 Issue,且不会将设计取舍、预期不一致或理解偏差直接归类为 bug。📸 运行证明 / Proof of Work
go test ./...passed.cd web && bun run typecheckpassed.cd web && bun run buildpassed.git diff --check origin/main..HEADpassed.relaykit/was not changed by this merge.cd web && bun testsuite still reproduces the fork's existing Bun/node:testbaseline failures. The newly upstream-added redemption drawer test also enters a repeating React maximum-update-depth loop, so that run was terminated after the behavior was confirmed. These failures do not affect the passing typecheck, production build, or backend suite above.