fix(openai): enforce final transcript bounds before callback delivery (#118928)

This commit is contained in:
Peter Steinberger
2026-08-03 13:56:21 -07:00
committed by GitHub
parent d29b9ad083
commit 665b47832a
2 changed files with 115 additions and 8 deletions
@@ -94,6 +94,113 @@ describe("OpenAI realtime transcription terminal history bounds", () => {
vi.unstubAllEnvs();
});
it.each([
{ label: "without item identity", itemId: undefined, committed: false },
{ label: "before item commit", itemId: "uncommitted-item", committed: false },
{ label: "after item commit", itemId: "committed-item", committed: true },
])("rejects an oversized final transcript $label", async ({ itemId, committed }) => {
const onError = vi.fn();
const onTranscript = vi.fn();
const session = buildOpenAIRealtimeTranscriptionProvider().createSession({
providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret
onError,
onTranscript,
});
const socket = await connectFakeSession(session);
if (committed) {
emitJson(socket, {
type: "input_audio_buffer.committed",
item_id: itemId,
previous_item_id: null,
});
}
const transcript = `${"🙂".repeat((256 * 1024) / 4)}x`;
emitJson(socket, {
type: "conversation.item.input_audio_transcription.completed",
...(itemId ? { item_id: itemId } : {}),
transcript,
});
emitJson(socket, {
type: "conversation.item.input_audio_transcription.completed",
...(itemId ? { item_id: itemId } : {}),
transcript: "late transcript",
});
expect(onError).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
message: "OpenAI realtime transcription exceeded the 256 KiB retained transcript limit",
}),
);
expect(onTranscript).not.toHaveBeenCalled();
expect(session.isConnected()).toBe(false);
expect(socket.closed).toBe(true);
session.close();
});
it("accepts an exact-limit UTF-8 final after releasing its own partial", async () => {
const onError = vi.fn();
const onPartial = vi.fn();
const onTranscript = vi.fn();
const session = buildOpenAIRealtimeTranscriptionProvider().createSession({
providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret
onError,
onPartial,
onTranscript,
});
const socket = await connectFakeSession(session);
const transcript = "🙂".repeat((256 * 1024) / 4);
emitJson(socket, {
type: "conversation.item.input_audio_transcription.delta",
item_id: "uncommitted-item",
delta: transcript,
});
emitJson(socket, {
type: "conversation.item.input_audio_transcription.completed",
item_id: "uncommitted-item",
transcript,
});
expect(onPartial).toHaveBeenCalledExactlyOnceWith(transcript);
expect(onTranscript).toHaveBeenCalledExactlyOnceWith(transcript);
expect(onError).not.toHaveBeenCalled();
expect(session.isConnected()).toBe(true);
session.close();
});
it("counts retained sibling text when admitting a final transcript", async () => {
const onError = vi.fn();
const onTranscript = vi.fn();
const session = buildOpenAIRealtimeTranscriptionProvider().createSession({
providerConfig: { apiKey: "sk-test" }, // pragma: allowlist secret
onError,
onTranscript,
});
const socket = await connectFakeSession(session);
const partial = "🙂".repeat((128 * 1024) / 4);
for (const itemId of ["retained-item", "completing-item"]) {
emitJson(socket, {
type: "conversation.item.input_audio_transcription.delta",
item_id: itemId,
delta: partial,
});
}
emitJson(socket, {
type: "conversation.item.input_audio_transcription.completed",
item_id: "completing-item",
transcript: `${partial}x`,
});
expect(onError).toHaveBeenCalledExactlyOnceWith(
expect.objectContaining({
message: "OpenAI realtime transcription exceeded the 256 KiB retained transcript limit",
}),
);
expect(onTranscript).not.toHaveBeenCalled();
expect(session.isConnected()).toBe(false);
session.close();
});
it("does not re-admit a terminal item after the settled frontier fills", async () => {
const onError = vi.fn();
const onPartial = vi.fn();
@@ -384,6 +384,14 @@ function createOpenAIRealtimeTranscriptionSession(
const partialBytes = pendingTranscripts.get(key)?.bytes ?? 0;
pendingTranscripts.delete(key);
retainedTranscriptBytes -= partialBytes;
const transcriptBytes = transcript ? Buffer.byteLength(transcript, "utf8") : 0;
if (
transcriptBytes >
OPENAI_REALTIME_TRANSCRIPTION_MAX_RETAINED_TRANSCRIPT_BYTES - retainedTranscriptBytes
) {
failTerminal(new Error(OPENAI_REALTIME_TRANSCRIPTION_TEXT_OVERFLOW_MESSAGE), transport);
return false;
}
if (!itemId || !committedItems.has(itemId)) {
if (itemId) {
if (!settleItem(itemId, transport)) {
@@ -397,14 +405,6 @@ function createOpenAIRealtimeTranscriptionSession(
}
return true;
}
const transcriptBytes = transcript ? Buffer.byteLength(transcript, "utf8") : 0;
if (
transcriptBytes >
OPENAI_REALTIME_TRANSCRIPTION_MAX_RETAINED_TRANSCRIPT_BYTES - retainedTranscriptBytes
) {
failTerminal(new Error(OPENAI_REALTIME_TRANSCRIPTION_TEXT_OVERFLOW_MESSAGE), transport);
return false;
}
completedTranscripts.set(itemId, transcript);
retainedTranscriptBytes += transcriptBytes;
return flushCompletedTranscripts(transport);