mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
fix(provider): retry google rest status failures
This commit is contained in:
@@ -106,6 +106,7 @@ describe("google video generation provider", () => {
|
||||
generateVideosMock.mockReset();
|
||||
getVideosOperationMock.mockReset();
|
||||
createGoogleGenAIMock.mockClear();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@@ -357,6 +358,75 @@ describe("google video generation provider", () => {
|
||||
expect(result.videos[0]?.buffer).toEqual(Buffer.from("rest-video"));
|
||||
});
|
||||
|
||||
it("retries transient Google REST poll failures with empty bodies", async () => {
|
||||
vi.useFakeTimers();
|
||||
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
|
||||
apiKey: "google-key",
|
||||
source: "env",
|
||||
mode: "api-key",
|
||||
});
|
||||
generateVideosMock.mockRejectedValue(Object.assign(new Error("sdk 404"), { status: 404 }));
|
||||
const fetchMock = vi
|
||||
.fn()
|
||||
.mockResolvedValueOnce(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
done: false,
|
||||
name: "operations/rest-123",
|
||||
}),
|
||||
),
|
||||
)
|
||||
.mockResolvedValueOnce(new Response("", { status: 503, statusText: "Service Unavailable" }))
|
||||
.mockResolvedValueOnce(
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
done: true,
|
||||
name: "operations/rest-123",
|
||||
response: {
|
||||
generateVideoResponse: {
|
||||
generatedSamples: [
|
||||
{
|
||||
video: {
|
||||
uri: "https://generativelanguage.googleapis.com/v1beta/files/rest-video:download?alt=media",
|
||||
mimeType: "video/mp4",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
)
|
||||
.mockResolvedValueOnce(
|
||||
new Response("rest-video", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
headers: { "content-type": "video/mp4" },
|
||||
}),
|
||||
);
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
|
||||
const provider = buildGoogleVideoGenerationProvider();
|
||||
const resultPromise = provider.generateVideo({
|
||||
provider: "google",
|
||||
model: "veo-3.1-fast-generate-preview",
|
||||
prompt: "A tiny robot watering a windowsill garden",
|
||||
cfg: {},
|
||||
durationSeconds: 3,
|
||||
});
|
||||
await vi.advanceTimersByTimeAsync(10_250);
|
||||
const result = await resultPromise;
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(4);
|
||||
expect(fetchInputUrl(fetchMock, 1)).toBe(
|
||||
"https://generativelanguage.googleapis.com/v1beta/operations/rest-123",
|
||||
);
|
||||
expect(fetchInputUrl(fetchMock, 2)).toBe(
|
||||
"https://generativelanguage.googleapis.com/v1beta/operations/rest-123",
|
||||
);
|
||||
expect(result.videos[0]?.buffer).toEqual(Buffer.from("rest-video"));
|
||||
});
|
||||
|
||||
it("does not fall back to REST when SDK video generation with reference inputs returns 404", async () => {
|
||||
vi.spyOn(providerAuthRuntime, "resolveApiKeyForProvider").mockResolvedValue({
|
||||
apiKey: "google-key",
|
||||
|
||||
@@ -300,6 +300,25 @@ async function requestGoogleVideoJson(params: {
|
||||
stage: "create" | "poll";
|
||||
body?: unknown;
|
||||
}): Promise<unknown> {
|
||||
function createHttpError(response: Response, detail: unknown): Error {
|
||||
const parts = [`HTTP ${response.status}`];
|
||||
const statusText = response.statusText.trim();
|
||||
if (statusText) {
|
||||
parts.push(statusText);
|
||||
}
|
||||
if (typeof detail === "string") {
|
||||
const trimmed = detail.trim();
|
||||
if (trimmed) {
|
||||
parts.push(trimmed);
|
||||
}
|
||||
} else if (detail && typeof detail === "object") {
|
||||
parts.push(JSON.stringify(detail));
|
||||
}
|
||||
const error = new Error(parts.join(": "));
|
||||
Object.assign(error, { status: response.status, statusCode: response.status });
|
||||
return error;
|
||||
}
|
||||
|
||||
return await executeProviderOperationWithRetry({
|
||||
provider: "google",
|
||||
stage: params.stage,
|
||||
@@ -328,12 +347,18 @@ async function requestGoogleVideoJson(params: {
|
||||
});
|
||||
try {
|
||||
const text = await response.text();
|
||||
const payload = text ? (JSON.parse(text) as unknown) : {};
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
typeof payload === "string" ? payload : JSON.stringify(payload ?? null),
|
||||
);
|
||||
let detail: unknown = text;
|
||||
if (text) {
|
||||
try {
|
||||
detail = JSON.parse(text) as unknown;
|
||||
} catch {
|
||||
detail = text;
|
||||
}
|
||||
}
|
||||
throw createHttpError(response, detail);
|
||||
}
|
||||
const payload = text ? (JSON.parse(text) as unknown) : {};
|
||||
return payload;
|
||||
} finally {
|
||||
await release();
|
||||
|
||||
Reference in New Issue
Block a user