mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 08:43:59 +00:00
fix(agents): MiniMax VLM can consume oversized success responses (#100694)
* Cap MiniMax VLM success response reads * fix: share bounded MiniMax response reader * test: return fresh MiniMax response bodies * test: allow stream prefetch at MiniMax bound --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -243,6 +243,45 @@ describe("minimaxUnderstandImage apiKey normalization", () => {
|
||||
expect(error.message.length).toBeLessThan(520);
|
||||
expect(canceled).toBe(true);
|
||||
});
|
||||
|
||||
it("bounds large successful response bodies before parsing JSON", async () => {
|
||||
let canceled = false;
|
||||
let pullCount = 0;
|
||||
const body = new ReadableStream<Uint8Array>({
|
||||
pull(controller) {
|
||||
pullCount += 1;
|
||||
controller.enqueue(new Uint8Array(1024 * 1024));
|
||||
},
|
||||
cancel() {
|
||||
canceled = true;
|
||||
},
|
||||
});
|
||||
const fetchSpy = vi.fn(async () => {
|
||||
return new Response(body, {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json", "Trace-Id": "trace-success" },
|
||||
});
|
||||
});
|
||||
global.fetch = withFetchPreconnect(fetchSpy);
|
||||
|
||||
const error = await minimaxUnderstandImage({
|
||||
apiKey: "minimax-test-key",
|
||||
prompt: "hi",
|
||||
imageDataUrl: "data:image/png;base64,AAAA",
|
||||
apiHost: "https://api.minimax.io",
|
||||
}).catch((caught: unknown) => caught);
|
||||
|
||||
if (!(error instanceof Error)) {
|
||||
throw new Error("expected MiniMax VLM request to reject oversized successful JSON");
|
||||
}
|
||||
expect(error.message).toBe(
|
||||
"MiniMax VLM response [Trace-Id=trace-success]: JSON response exceeds 16777216 bytes",
|
||||
);
|
||||
// WHATWG streams may pre-pull one chunk beyond the bytes consumed by the reader.
|
||||
expect(pullCount).toBeGreaterThanOrEqual(17);
|
||||
expect(pullCount).toBeLessThanOrEqual(18);
|
||||
expect(canceled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMinimaxVlmModel", () => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ensureGlobalUndiciEnvProxyDispatcher } from "../infra/net/undici-global
|
||||
import { resolvePositiveTimerTimeoutMs } from "../shared/number-coercion.js";
|
||||
import { isRecord } from "../utils.js";
|
||||
import { normalizeSecretInput } from "../utils/normalize-secret-input.js";
|
||||
import { readProviderJsonResponse } from "./provider-http-errors.js";
|
||||
|
||||
type MinimaxBaseResp = {
|
||||
status_code?: number;
|
||||
@@ -142,7 +143,10 @@ export async function minimaxUnderstandImage(params: {
|
||||
);
|
||||
}
|
||||
|
||||
const json = (await res.json().catch(() => null)) as unknown;
|
||||
const responseLabel = traceId
|
||||
? `MiniMax VLM response [Trace-Id=${traceId}]`
|
||||
: "MiniMax VLM response";
|
||||
const json = await readProviderJsonResponse<unknown>(res, responseLabel);
|
||||
if (!isRecord(json)) {
|
||||
const trace = traceId ? ` Trace-Id: ${traceId}` : "";
|
||||
throw new Error(`MiniMax VLM response was not JSON.${trace}`);
|
||||
|
||||
@@ -447,32 +447,24 @@ function registerImageToolEnvReset(priorFetch: typeof global.fetch, keys: string
|
||||
}
|
||||
|
||||
function stubMinimaxOkFetch() {
|
||||
const fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: new Headers(),
|
||||
json: async () => ({
|
||||
const fetch = vi.fn().mockImplementation(async () =>
|
||||
Response.json({
|
||||
content: "ok",
|
||||
base_resp: { status_code: 0, status_msg: "" },
|
||||
}),
|
||||
});
|
||||
);
|
||||
global.fetch = withFetchPreconnect(fetch);
|
||||
vi.stubEnv("MINIMAX_API_KEY", "minimax-test");
|
||||
return fetch;
|
||||
}
|
||||
|
||||
function stubMinimaxFetch(baseResp: { status_code: number; status_msg: string }, content = "ok") {
|
||||
const fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: new Headers(),
|
||||
json: async () => ({
|
||||
const fetch = vi.fn().mockImplementation(async () =>
|
||||
Response.json({
|
||||
content,
|
||||
base_resp: baseResp,
|
||||
}),
|
||||
});
|
||||
);
|
||||
global.fetch = withFetchPreconnect(fetch);
|
||||
return fetch;
|
||||
}
|
||||
|
||||
@@ -150,17 +150,12 @@ describe("describeImageWithModel", () => {
|
||||
vi.stubEnv("OPENCLAW_BUNDLED_PLUGINS_DIR", path.join(process.cwd(), "extensions"));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
vi.clearAllMocks();
|
||||
fetchMock.mockResolvedValue({
|
||||
ok: true,
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: { get: vi.fn(() => null) },
|
||||
json: vi.fn(async () => ({
|
||||
fetchMock.mockImplementation(async () =>
|
||||
Response.json({
|
||||
base_resp: { status_code: 0 },
|
||||
content: "portal ok",
|
||||
})),
|
||||
text: vi.fn(async () => ""),
|
||||
});
|
||||
}),
|
||||
);
|
||||
discoverModelsMock.mockReturnValue({
|
||||
find: vi.fn(() => ({
|
||||
provider: "minimax-portal",
|
||||
|
||||
Reference in New Issue
Block a user