From 9bad1c574ccc422e3e2223f59ebd4ccf5bc61512 Mon Sep 17 00:00:00 2001 From: Kazuhiro Sera Date: Wed, 1 Oct 2025 10:35:11 -0700 Subject: [PATCH] fix: #552 WebSocket Realtime Agent: invalid_request_error with decimal audio_end_ms data --- .changeset/bumpy-cats-run.md | 5 +++++ .../src/openaiRealtimeWebsocket.ts | 3 ++- .../test/openaiRealtimeWebsocket.test.ts | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/bumpy-cats-run.md diff --git a/.changeset/bumpy-cats-run.md b/.changeset/bumpy-cats-run.md new file mode 100644 index 00000000..47f2894c --- /dev/null +++ b/.changeset/bumpy-cats-run.md @@ -0,0 +1,5 @@ +--- +'@openai/agents-realtime': patch +--- + +fix: #552 WebSocket Realtime Agent: invalid_request_error with decimal audio_end_ms data diff --git a/packages/agents-realtime/src/openaiRealtimeWebsocket.ts b/packages/agents-realtime/src/openaiRealtimeWebsocket.ts index de1e9dcc..3c4e7935 100644 --- a/packages/agents-realtime/src/openaiRealtimeWebsocket.ts +++ b/packages/agents-realtime/src/openaiRealtimeWebsocket.ts @@ -426,7 +426,8 @@ export class OpenAIRealtimeWebSocket } const length = this._audioLengthMs ?? Number.POSITIVE_INFINITY; - const audio_end_ms = Math.max(0, Math.min(Math.floor(elapsedTime), length)); + // audio_end_ms must be an integer + const audio_end_ms = Math.max(0, Math.floor(Math.min(elapsedTime, length))); this.emit('audio_interrupted'); this.sendEvent({ diff --git a/packages/agents-realtime/test/openaiRealtimeWebsocket.test.ts b/packages/agents-realtime/test/openaiRealtimeWebsocket.test.ts index 75b2b8bf..db135648 100644 --- a/packages/agents-realtime/test/openaiRealtimeWebsocket.test.ts +++ b/packages/agents-realtime/test/openaiRealtimeWebsocket.test.ts @@ -205,6 +205,22 @@ describe('OpenAIRealtimeWebSocket', () => { expect(baseSpy).toHaveBeenCalled(); }); + it('_interrupt floors fractional audio length when clamping', () => { + const ws = new OpenAIRealtimeWebSocket(); + const sendSpy = vi + .spyOn(OpenAIRealtimeWebSocket.prototype as any, 'sendEvent') + .mockImplementation(() => {}); + // @ts-expect-error - testing protected field. + ws._audioLengthMs = 42.8; + ws._interrupt(100, false); + const call = sendSpy.mock.calls.find( + (c: unknown[]) => (c[0] as any).type === 'conversation.item.truncate', + ); + expect((call?.[0] as any).audio_end_ms).toBe(42); + expect(Number.isInteger((call?.[0] as any).audio_end_ms)).toBe(true); + sendSpy.mockRestore(); + }); + it('_interrupt quantizes and clamps elapsedTime', () => { const ws = new OpenAIRealtimeWebSocket(); const sendSpy = vi