mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-18 16:41:45 -06:00
fix(vydra): bound control response reads
Reviewed and accepted after live preflight: mergeable clean, checks passing, no unresolved review threads.
(cherry picked from commit 74a9beb83f)
This commit is contained in:
@@ -17,6 +17,13 @@ function fetchCall(fetchMock: ReturnType<typeof vi.fn>, index = 0): [string, Req
|
||||
return call as [string, RequestInit];
|
||||
}
|
||||
|
||||
function oversizedJsonResponse(): Response {
|
||||
return new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("vydra image-generation provider", () => {
|
||||
installPinnedHostnameTestHooks();
|
||||
|
||||
@@ -94,6 +101,21 @@ describe("vydra image-generation provider", () => {
|
||||
).rejects.toThrow("Vydra image download exceeds 1 bytes");
|
||||
});
|
||||
|
||||
it("rejects image creation JSON responses that exceed the provider cap", async () => {
|
||||
stubVydraApiKey();
|
||||
stubFetch(oversizedJsonResponse());
|
||||
|
||||
const provider = buildVydraImageGenerationProvider();
|
||||
await expect(
|
||||
provider.generateImage({
|
||||
provider: "vydra",
|
||||
model: "grok-imagine",
|
||||
prompt: "draw a cat",
|
||||
cfg: {},
|
||||
}),
|
||||
).rejects.toThrow("vydra.image-generation: JSON response exceeds 16777216 bytes");
|
||||
});
|
||||
|
||||
it("passes request SSRF policy to the image creation request", async () => {
|
||||
stubVydraApiKey();
|
||||
const fetchMock = stubFetch(
|
||||
@@ -151,4 +173,19 @@ describe("vydra image-generation provider", () => {
|
||||
expect(pollCall[0]).toBe("https://www.vydra.ai/api/v1/jobs/job-456");
|
||||
expect(pollCall[1].method).toBe("GET");
|
||||
});
|
||||
|
||||
it("rejects job poll JSON responses that exceed the provider cap", async () => {
|
||||
stubVydraApiKey();
|
||||
stubFetch(jsonResponse({ jobId: "job-456", status: "queued" }), oversizedJsonResponse());
|
||||
|
||||
const provider = buildVydraImageGenerationProvider();
|
||||
await expect(
|
||||
provider.generateImage({
|
||||
provider: "vydra",
|
||||
model: "grok-imagine",
|
||||
prompt: "draw a cat",
|
||||
cfg: {},
|
||||
}),
|
||||
).rejects.toThrow("Vydra job status: JSON response exceeds 16777216 bytes");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
assertOkOrThrowHttpError,
|
||||
createProviderOperationDeadline,
|
||||
fetchWithTimeout,
|
||||
readProviderJsonResponse,
|
||||
resolveProviderOperationTimeoutMs,
|
||||
resolveProviderHttpRequestConfig,
|
||||
waitProviderOperationPollInterval,
|
||||
@@ -287,7 +288,7 @@ async function waitForVydraJob(params: {
|
||||
params.fetchFn,
|
||||
);
|
||||
await assertOkOrThrowHttpError(response, "Vydra job status request failed");
|
||||
const payload = await response.json();
|
||||
const payload = await readProviderJsonResponse<unknown>(response, "Vydra job status");
|
||||
const status = resolveVydraResponseStatus(payload);
|
||||
if (status === "completed" || extractVydraResultUrls(payload, params.kind).length > 0) {
|
||||
return payload;
|
||||
|
||||
@@ -8,6 +8,12 @@ describe("vydra speech provider", () => {
|
||||
|
||||
const provider = buildVydraSpeechProvider();
|
||||
|
||||
const oversizedJsonResponse = () =>
|
||||
new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
vi.restoreAllMocks();
|
||||
@@ -103,4 +109,18 @@ describe("vydra speech provider", () => {
|
||||
}),
|
||||
).rejects.toThrow("Vydra audio download exceeds 1 bytes");
|
||||
});
|
||||
|
||||
it("rejects speech synthesis JSON responses that exceed the provider cap", async () => {
|
||||
vi.stubGlobal("fetch", vi.fn().mockResolvedValueOnce(oversizedJsonResponse()));
|
||||
|
||||
await expect(
|
||||
provider.synthesize({
|
||||
text: "OpenClaw test",
|
||||
cfg: {} as never,
|
||||
providerConfig: { apiKey: "vydra-test-key" },
|
||||
target: "audio-file",
|
||||
timeoutMs: 30_000,
|
||||
}),
|
||||
).rejects.toThrow("Vydra speech synthesis: JSON response exceeds 16777216 bytes");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import {
|
||||
assertOkOrThrowHttpError,
|
||||
postJsonRequest,
|
||||
readProviderJsonResponse,
|
||||
resolveProviderHttpRequestConfig,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import { normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
|
||||
@@ -129,7 +130,7 @@ export function buildVydraSpeechProvider(): SpeechProviderPlugin {
|
||||
|
||||
try {
|
||||
await assertOkOrThrowHttpError(response, "Vydra speech synthesis failed");
|
||||
const payload = await response.json();
|
||||
const payload = await readProviderJsonResponse<unknown>(response, "Vydra speech synthesis");
|
||||
const audioUrl = extractVydraResultUrls(payload, "audio")[0];
|
||||
if (!audioUrl) {
|
||||
throw new Error("Vydra speech synthesis response missing audio URL");
|
||||
|
||||
@@ -18,6 +18,13 @@ function fetchCall(fetchMock: ReturnType<typeof vi.fn>, index: number) {
|
||||
return call;
|
||||
}
|
||||
|
||||
function oversizedJsonResponse(): Response {
|
||||
return new Response(Buffer.alloc(16 * 1024 * 1024 + 1, 0x20), {
|
||||
status: 200,
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("vydra video-generation provider", () => {
|
||||
installPinnedHostnameTestHooks();
|
||||
|
||||
@@ -96,6 +103,21 @@ describe("vydra video-generation provider", () => {
|
||||
).rejects.toThrow("Vydra video download exceeds 1 bytes");
|
||||
});
|
||||
|
||||
it("rejects video creation JSON responses that exceed the provider cap", async () => {
|
||||
stubVydraApiKey();
|
||||
stubFetch(oversizedJsonResponse());
|
||||
|
||||
const provider = buildVydraVideoGenerationProvider();
|
||||
await expect(
|
||||
provider.generateVideo({
|
||||
provider: "vydra",
|
||||
model: "veo3",
|
||||
prompt: "tiny city at sunrise",
|
||||
cfg: {},
|
||||
}),
|
||||
).rejects.toThrow("Vydra video generation: JSON response exceeds 16777216 bytes");
|
||||
});
|
||||
|
||||
it("requires a remote image url for kling", async () => {
|
||||
stubVydraApiKey();
|
||||
vi.stubGlobal("fetch", vi.fn());
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
createProviderOperationDeadline,
|
||||
createProviderOperationTimeoutResolver,
|
||||
postJsonRequest,
|
||||
readProviderJsonResponse,
|
||||
resolveProviderOperationTimeoutMs,
|
||||
} from "openclaw/plugin-sdk/provider-http";
|
||||
import type { VideoGenerationProvider } from "openclaw/plugin-sdk/video-generation";
|
||||
@@ -111,7 +112,10 @@ export function buildVydraVideoGenerationProvider(): VideoGenerationProvider {
|
||||
|
||||
try {
|
||||
await assertOkOrThrowHttpError(response, "Vydra video generation failed");
|
||||
const submitted = await response.json();
|
||||
const submitted = await readProviderJsonResponse<unknown>(
|
||||
response,
|
||||
"Vydra video generation",
|
||||
);
|
||||
const completedPayload = await resolveCompletedVydraPayload({
|
||||
submitted,
|
||||
baseUrl,
|
||||
|
||||
Reference in New Issue
Block a user