diff --git a/src/gateway/server-methods/chat.ts b/src/gateway/server-methods/chat.ts index 353e7d74771..b8e99447929 100644 --- a/src/gateway/server-methods/chat.ts +++ b/src/gateway/server-methods/chat.ts @@ -700,6 +700,17 @@ function sanitizeChatHistoryContentBlock( entry.bytes = bytes; changed = true; } + if (type === "audio" && entry.source && typeof entry.source === "object") { + const source = { ...(entry.source as Record) }; + if (source.type === "base64" && typeof source.data === "string") { + const bytes = Buffer.byteLength(source.data, "utf8"); + delete source.data; + source.omitted = true; + source.bytes = bytes; + entry.source = source; + changed = true; + } + } return { block: changed ? entry : block, changed }; } diff --git a/src/gateway/server-methods/server-methods.test.ts b/src/gateway/server-methods/server-methods.test.ts index fc3a6c8eda8..46bd9aad86b 100644 --- a/src/gateway/server-methods/server-methods.test.ts +++ b/src/gateway/server-methods/server-methods.test.ts @@ -263,6 +263,46 @@ describe("injectTimestamp", () => { }); describe("sanitizeChatHistoryMessages", () => { + it("redacts base64 audio content blocks from chat history", () => { + const data = Buffer.from("voice-bytes").toString("base64"); + const result = sanitizeChatHistoryMessages([ + { + role: "assistant", + content: [ + { type: "text", text: "Audio reply" }, + { + type: "audio", + source: { + type: "base64", + media_type: "audio/mp3", + data, + }, + }, + ], + timestamp: 1, + }, + ]); + + expect(result).toEqual([ + { + role: "assistant", + content: [ + { type: "text", text: "Audio reply" }, + { + type: "audio", + source: { + type: "base64", + media_type: "audio/mp3", + omitted: true, + bytes: Buffer.byteLength(data, "utf8"), + }, + }, + ], + timestamp: 1, + }, + ]); + }); + it("drops commentary-only assistant entries when phase exists only in textSignature", () => { const result = sanitizeChatHistoryMessages([ {