11import {
2+ ResponseTextConfig ,
23 type ParsedResponse ,
34 type Response ,
45 type ResponseCreateParamsBase ,
@@ -10,12 +11,40 @@ import { APIUserAbortError, OpenAIError } from '../../error';
1011import OpenAI from '../../index' ;
1112import { type BaseEvents , EventStream } from '../EventStream' ;
1213import { type ResponseFunctionCallArgumentsDeltaEvent , type ResponseTextDeltaEvent } from './EventTypes' ;
13- import { maybeParseResponse } from '../ResponsesParser' ;
14+ import { maybeParseResponse , ParseableToolsParams } from '../ResponsesParser' ;
15+ import { Stream } from 'openai/streaming' ;
1416
15- export type ResponseStreamParams = Omit < ResponseCreateParamsBase , 'stream' > & {
17+ export type ResponseStreamParams = ResponseCreateAndStreamParams | ResponseStreamByIdParams ;
18+
19+ export type ResponseCreateAndStreamParams = Omit < ResponseCreateParamsBase , 'stream' > & {
1620 stream ?: true ;
1721} ;
1822
23+ export type ResponseStreamByIdParams = {
24+ /**
25+ * The ID of the response to stream.
26+ */
27+ response_id : string ;
28+ /**
29+ * If provided, the stream will start after the event with the given sequence number.
30+ */
31+ starting_after ?: number ;
32+ /**
33+ * Configuration options for a text response from the model. Can be plain text or
34+ * structured JSON data. Learn more:
35+ *
36+ * - [Text inputs and outputs](https://platform.openai.com/docs/guides/text)
37+ * - [Structured Outputs](https://platform.openai.com/docs/guides/structured-outputs)
38+ */
39+ text ?: ResponseTextConfig ;
40+
41+ /**
42+ * An array of tools the model may call while generating a response. When continuing a stream, provide
43+ * the same tools as the original request.
44+ */
45+ tools ?: ParseableToolsParams ;
46+ } ;
47+
1948type ResponseEvents = BaseEvents &
2049 Omit <
2150 {
@@ -52,7 +81,7 @@ export class ResponseStream<ParsedT = null>
5281 ) : ResponseStream < ParsedT > {
5382 const runner = new ResponseStream < ParsedT > ( params as ResponseCreateParamsStreaming ) ;
5483 runner . _run ( ( ) =>
55- runner . _createResponse ( client , params , {
84+ runner . _createOrRetrieveResponse ( client , params , {
5685 ...options ,
5786 headers : { ...options ?. headers , 'X-Stainless-Helper-Method' : 'stream' } ,
5887 } ) ,
@@ -65,11 +94,17 @@ export class ResponseStream<ParsedT = null>
6594 this . #currentResponseSnapshot = undefined ;
6695 }
6796
68- #addEvent( this : ResponseStream < ParsedT > , event : ResponseStreamEvent ) {
97+ #addEvent( this : ResponseStream < ParsedT > , event : ResponseStreamEvent , starting_after : number | null ) {
6998 if ( this . ended ) return ;
7099
100+ const maybeEmit = ( name : string , event : ResponseStreamEvent & { snapshot ?: string } ) => {
101+ if ( starting_after == null || event . sequence_number > starting_after ) {
102+ this . _emit ( name as any , event ) ;
103+ }
104+ } ;
105+
71106 const response = this . #accumulateResponse( event ) ;
72- this . _emit ( 'event' , event ) ;
107+ maybeEmit ( 'event' , event ) ;
73108
74109 switch ( event . type ) {
75110 case 'response.output_text.delta' : {
@@ -86,7 +121,7 @@ export class ResponseStream<ParsedT = null>
86121 throw new OpenAIError ( `expected content to be 'output_text', got ${ content . type } ` ) ;
87122 }
88123
89- this . _emit ( 'response.output_text.delta' , {
124+ maybeEmit ( 'response.output_text.delta' , {
90125 ...event ,
91126 snapshot : content . text ,
92127 } ) ;
@@ -99,16 +134,15 @@ export class ResponseStream<ParsedT = null>
99134 throw new OpenAIError ( `missing output at index ${ event . output_index } ` ) ;
100135 }
101136 if ( output . type === 'function_call' ) {
102- this . _emit ( 'response.function_call_arguments.delta' , {
137+ maybeEmit ( 'response.function_call_arguments.delta' , {
103138 ...event ,
104139 snapshot : output . arguments ,
105140 } ) ;
106141 }
107142 break ;
108143 }
109144 default :
110- // @ts -ignore
111- this . _emit ( event . type , event ) ;
145+ maybeEmit ( event . type , event ) ;
112146 break ;
113147 }
114148 }
@@ -128,9 +162,9 @@ export class ResponseStream<ParsedT = null>
128162 return parsedResponse ;
129163 }
130164
131- protected async _createResponse (
165+ protected async _createOrRetrieveResponse (
132166 client : OpenAI ,
133- params : ResponseStreamingParams ,
167+ params : ResponseStreamParams ,
134168 options ?: Core . RequestOptions ,
135169 ) : Promise < ParsedResponse < ParsedT > > {
136170 const signal = options ?. signal ;
@@ -140,13 +174,25 @@ export class ResponseStream<ParsedT = null>
140174 }
141175 this . #beginRequest( ) ;
142176
143- const stream = await client . responses . create (
144- { ...params , stream : true } ,
145- { ...options , signal : this . controller . signal } ,
146- ) ;
177+ let stream : Stream < ResponseStreamEvent > | undefined ;
178+ let starting_after : number | null = null ;
179+ if ( 'response_id' in params ) {
180+ stream = await client . responses . retrieve (
181+ params . response_id ,
182+ { stream : true } ,
183+ { ...options , signal : this . controller . signal , stream : true } ,
184+ ) ;
185+ starting_after = params . starting_after ?? null ;
186+ } else {
187+ stream = await client . responses . create (
188+ { ...params , stream : true } ,
189+ { ...options , signal : this . controller . signal } ,
190+ ) ;
191+ }
192+
147193 this . _connected ( ) ;
148194 for await ( const event of stream ) {
149- this . #addEvent( event ) ;
195+ this . #addEvent( event , starting_after ) ;
150196 }
151197 if ( stream . controller . signal ?. aborted ) {
152198 throw new APIUserAbortError ( ) ;
0 commit comments