@@ -6,9 +6,12 @@ import { InternalEventEmitter } from '../../core/EventEmitter';
66import { sleep } from '../../internal/utils/sleep' ;
77import {
88 SendQueue ,
9+ flattenRawData ,
910 isRecoverableClose ,
11+ type RawWebSocketData ,
1012 type ReconnectingEvent ,
1113 type ReconnectingOverrides ,
14+ type UnsentMessage ,
1215} from '../../internal/ws' ;
1316import * as ResponsesAPI from './responses' ;
1417import { OpenAI } from '../../client' ;
@@ -77,7 +80,7 @@ export class ResponsesWS extends ResponsesEmitter {
7780 socketSwap : ( oldSocket : WS . WebSocket , newSocket : WS . WebSocket ) => void ;
7881 reconnecting : ( event : ReconnectingEvent < Record < string , unknown > > ) => void ;
7982 reconnected : ( ) => void ;
80- close : ( code : number , reason : string , unsent : ResponsesAPI . ResponsesClientEvent [ ] ) => void ;
83+ close : ( code : number , reason : string , unsent : UnsentMessage < ResponsesAPI . ResponsesClientEvent > [ ] ) => void ;
8184 } > ( ) ;
8285
8386 constructor ( client : OpenAI , options ?: ResponsesWSClientOptions | null | undefined ) {
@@ -110,6 +113,24 @@ export class ResponsesWS extends ResponsesEmitter {
110113 }
111114 }
112115
116+ sendRaw ( data : RawWebSocketData ) {
117+ if ( this . _isReconnecting || this . socket . readyState === WS . WebSocket . CONNECTING ) {
118+ if ( ! this . _sendQueue . enqueueRaw ( data ) ) {
119+ this . _onError ( null , 'send queue is full, message discarded' , undefined ) ;
120+ }
121+ return ;
122+ }
123+ if ( this . socket . readyState !== WS . WebSocket . OPEN ) {
124+ this . _onError ( null , 'cannot send on a closed WebSocket' , undefined ) ;
125+ return ;
126+ }
127+ try {
128+ this . socket . send ( flattenRawData ( data ) ) ;
129+ } catch ( err ) {
130+ this . _onError ( null , 'could not send data' , err ) ;
131+ }
132+ }
133+
113134 close ( props ?: { code : number ; reason : string } ) {
114135 this . _intentionallyClosed = true ;
115136 this . _closeCode = props ?. code ?? 1000 ;
@@ -167,6 +188,10 @@ export class ResponsesWS extends ResponsesEmitter {
167188 push ( { type : 'message' , message : event } ) ;
168189 } ;
169190
191+ const onRaw = ( data : RawWebSocketData ) => {
192+ push ( { type : 'raw' , data } ) ;
193+ } ;
194+
170195 // All errors (API + socket) funnel through _onError → 'error' event
171196 const onEmitterError = ( err : WebSocketError ) => {
172197 push ( { type : 'error' , error : err } ) ;
@@ -190,7 +215,11 @@ export class ResponsesWS extends ResponsesEmitter {
190215 }
191216 } ;
192217
193- const onClose = ( code : number , reason : string , unsent : ResponsesAPI . ResponsesClientEvent [ ] ) => {
218+ const onClose = (
219+ code : number ,
220+ reason : string ,
221+ unsent : UnsentMessage < ResponsesAPI . ResponsesClientEvent > [ ] ,
222+ ) => {
194223 push ( { type : 'close' , code, reason, unsent } ) ;
195224 done = true ;
196225 flushResolvers ( ) ;
@@ -205,6 +234,7 @@ export class ResponsesWS extends ResponsesEmitter {
205234
206235 const cleanup = ( ) => {
207236 this . off ( 'event' , onEvent ) ;
237+ this . off ( 'raw' , onRaw ) ;
208238 this . off ( 'error' , onEmitterError ) ;
209239 currentSocket . off ( 'open' , onOpen ) ;
210240 this . _internalEvents . off ( 'close' , onClose ) ;
@@ -214,6 +244,7 @@ export class ResponsesWS extends ResponsesEmitter {
214244 } ;
215245
216246 this . on ( 'event' , onEvent ) ;
247+ this . on ( 'raw' , onRaw ) ;
217248 this . on ( 'error' , onEmitterError ) ;
218249 this . socket . on ( 'open' , onOpen ) ;
219250 this . _internalEvents . on ( 'close' , onClose ) ;
@@ -297,25 +328,27 @@ export class ResponsesWS extends ResponsesEmitter {
297328 } ,
298329 } ) ;
299330
300- socket . on ( 'message' , ( wsEvent ) => {
301- const event = ( ( ) => {
302- try {
303- return JSON . parse ( wsEvent . toString ( ) ) as ResponsesAPI . ResponsesServerEvent ;
304- } catch ( err ) {
305- this . _onError ( null , 'could not parse websocket event' , err ) ;
306- return null ;
307- }
308- } ) ( ) ;
331+ socket . on ( 'message' , ( wsEvent , isBinary ) => {
332+ if ( isBinary ) {
333+ this . _emit ( 'raw' , wsEvent as RawWebSocketData ) ;
334+ return ;
335+ }
309336
310- if ( event ) {
311- this . _emit ( 'event' , event ) ;
337+ let event : ResponsesAPI . ResponsesServerEvent ;
338+ try {
339+ event = JSON . parse ( wsEvent . toString ( ) ) as ResponsesAPI . ResponsesServerEvent ;
340+ } catch {
341+ this . _emit ( 'raw' , wsEvent as RawWebSocketData ) ;
342+ return ;
343+ }
312344
313- if ( event . type === 'error' ) {
314- this . _onError ( event ) ;
315- } else {
316- // @ts -ignore TS isn't smart enough to get the relationship right here
317- this . _emit ( event . type , event ) ;
318- }
345+ this . _emit ( 'event' , event ) ;
346+
347+ if ( event . type === 'error' ) {
348+ this . _onError ( event ) ;
349+ } else {
350+ // @ts -ignore TS isn't smart enough to get the relationship right here
351+ this . _emit ( event . type , event ) ;
319352 }
320353 } ) ;
321354
@@ -523,7 +556,9 @@ export class ResponsesWS extends ResponsesEmitter {
523556
524557 private _flushSendQueue ( ) : void {
525558 try {
526- this . _sendQueue . flush ( ( data ) => this . socket . send ( data ) ) ;
559+ this . _sendQueue . flush ( ( data ) =>
560+ this . socket . send ( typeof data === 'string' ? data : flattenRawData ( data ) ) ,
561+ ) ;
527562 } catch ( err ) {
528563 this . _onError ( null , 'could not send queued data' , err ) ;
529564 }
0 commit comments