fix(minimax): cap tts timeout delays

This commit is contained in:
Peter Steinberger
2026-05-29 15:10:56 -04:00
parent 0167f0a6df
commit a39c2d784e
2 changed files with 52 additions and 2 deletions

View File

@@ -0,0 +1,48 @@
import { MAX_TIMER_TIMEOUT_MS } from "openclaw/plugin-sdk/number-runtime";
import { afterEach, describe, expect, it, vi } from "vitest";
const fetchWithSsrFGuardMock = vi.hoisted(() => vi.fn());
vi.mock("openclaw/plugin-sdk/ssrf-runtime", async (importOriginal) => {
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/ssrf-runtime")>();
return {
...actual,
fetchWithSsrFGuard: (...args: unknown[]) => fetchWithSsrFGuardMock(...args),
};
});
import { minimaxTTS } from "./tts.js";
describe("minimaxTTS", () => {
afterEach(() => {
fetchWithSsrFGuardMock.mockReset();
vi.restoreAllMocks();
});
it("caps oversized request timeout before arming abort timers", async () => {
const timeoutSpy = vi.spyOn(globalThis, "setTimeout");
fetchWithSsrFGuardMock.mockResolvedValue({
response: new Response(
JSON.stringify({ data: { audio: Buffer.from("audio").toString("hex") } }),
{ status: 200, headers: { "content-type": "application/json" } },
),
release: vi.fn(async () => undefined),
});
const audio = await minimaxTTS({
text: "hello",
apiKey: "sk-test",
baseUrl: "https://api.minimax.io",
model: "speech-2.8-hd",
voiceId: "English_expressive_narrator",
timeoutMs: Number.MAX_SAFE_INTEGER,
});
expect(audio.toString()).toBe("audio");
expect(timeoutSpy).toHaveBeenCalledWith(expect.any(Function), MAX_TIMER_TIMEOUT_MS);
expect(fetchWithSsrFGuardMock.mock.calls[0]?.[0]).toMatchObject({
timeoutMs: MAX_TIMER_TIMEOUT_MS,
});
expect(fetchWithSsrFGuardMock.mock.calls[0]?.[0]?.init?.signal).toBeInstanceOf(AbortSignal);
});
});

View File

@@ -1,3 +1,4 @@
import { clampTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
import { assertOkOrThrowProviderError } from "openclaw/plugin-sdk/provider-http";
import {
fetchWithSsrFGuard,
@@ -64,9 +65,10 @@ export async function minimaxTTS(params: {
sampleRate = 32000,
timeoutMs,
} = params;
const safeTimeoutMs = clampTimerTimeoutMs(timeoutMs) ?? 1;
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), timeoutMs);
const timeout = setTimeout(() => controller.abort(), safeTimeoutMs);
try {
const { response, release } = await fetchWithSsrFGuard({
@@ -93,7 +95,7 @@ export async function minimaxTTS(params: {
}),
signal: controller.signal,
},
timeoutMs,
timeoutMs: safeTimeoutMs,
policy: ssrfPolicyFromHttpBaseUrlAllowedHostname(baseUrl),
auditContext: "minimax.tts",
});