-
-
Notifications
You must be signed in to change notification settings - Fork 391
fix(proxy): prevent large session requests from cascading into OOM #1457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,10 @@ import { | |
| writeLiveRoutingTrace, | ||
| } from "@/lib/redis/live-chain-store"; | ||
| import type { SessionBindingSnapshot } from "@/lib/redis/session-binding"; | ||
| import { | ||
| getSessionRequestArtifactByteSize, | ||
| getSessionRequestArtifactMaxBytes, | ||
| } from "@/lib/session-request-artifact-limit"; | ||
| import { clientRequestsContext1m as clientRequestsContext1mHelper } from "@/lib/special-attributes"; | ||
| import { ERROR_CODES, getErrorMessageServer } from "@/lib/utils/error-messages"; | ||
| import { | ||
|
|
@@ -581,6 +585,22 @@ export class ProxySession { | |
| return !this.highConcurrencyModeEnabled; | ||
| } | ||
|
|
||
| shouldPersistSessionRequestArtifacts(): boolean { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] [TEST-MISSING-CRITICAL] No unit test covers the artifact-size gate's measurement logic Why this is a problem: This method contains the boundary logic of the OOM fix itself - buffer-hint precedence (avoiding re-serialization of the body), the multipart exception (measuring the small logical body instead of the raw multipart buffer, which would falsely reject image requests), and the Suggested fix (add to describe("ProxySession request artifact gate", () => {
it("buffer 提示超过上限时跳过 request artifacts", () => {
const session = createSession({ redirectedModel: null, requestMessage: { messages: [] } });
session.request.buffer = new Uint8Array(6 * 1024 * 1024);
expect(session.shouldPersistSessionRequestArtifacts()).toBe(false);
});
it("multipart 请求按逻辑 body 而非原始 buffer 判定大小", () => {
const session = createSession({
redirectedModel: null,
requestMessage: { model: "gpt-image-1", prompt: "cat" },
});
session.request.buffer = new Uint8Array(6 * 1024 * 1024);
vi.spyOn(session, "isOpenAIImageMultipartRequest").mockReturnValue(true);
expect(session.shouldPersistSessionRequestArtifacts()).toBe(true);
});
it("恰好等于上限时仍保留 artifacts", () => {
const session = createSession({ redirectedModel: null, requestMessage: { messages: [] } });
session.request.buffer = new Uint8Array(5 * 1024 * 1024);
expect(session.shouldPersistSessionRequestArtifacts()).toBe(true);
});
}); |
||
| const byteSize = getSessionRequestArtifactByteSize( | ||
| this.request.message, | ||
| this.isOpenAIImageMultipartRequest() ? undefined : this.request.buffer?.byteLength | ||
| ); | ||
| const maxBytes = getSessionRequestArtifactMaxBytes(); | ||
| if (byteSize <= maxBytes) return true; | ||
|
|
||
| logger.warn("[ProxySession] Skipped oversized session request artifacts", { | ||
| byteSize, | ||
| maxBytes, | ||
| endpoint: this.getEndpoint(), | ||
| }); | ||
| return false; | ||
| } | ||
|
|
||
| shouldTrackSessionObservability(): boolean { | ||
| return !this.highConcurrencyModeEnabled; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[MEDIUM] [LOGIC-BUG] Legacy
requestBodyfield now prefers the upstream-forwarded body over the client bodyWhy this is a problem: The legacy key this field mirrors (
storeSessionRequestBody/getSessionRequestBody) is documented as "客户端原始请求体" - the client's request body captured at guard time, before any format conversion or request filtering. This derivation preferssnapshots.request.after.body, which issession.forwardedRequestBody: the post-conversion body actually sent to the upstream provider. For cross-format routing (e.g. an OpenAI client served by a Claude provider) or model-redirected requests,data.requestBodyreturned byGET /api/v1/sessions/{id}now contains a completely different payload than it did before this PR, while both snapshots still exist (the normal case for completed requests). The adjacentsnapshotMessagesderivation two lines below prefersbeforefirst, correctly preserving the client-side semantics of its legacy counterpart - both legacy fields were captured at the same guard-time point, sorequestBodyshould match that ordering.Suggested fix:
The forwarded body remains available to consumers via
data.snapshots.request.after, which is what the dashboard UI reads (session-messages-client.tsxonly consumessnapshots).