1
1
import {
2
+ ResponseTextConfig ,
2
3
type ParsedResponse ,
3
4
type Response ,
4
5
type ResponseCreateParamsBase ,
@@ -10,12 +11,40 @@ import { APIUserAbortError, OpenAIError } from '../../error';
10
11
import OpenAI from '../../index' ;
11
12
import { type BaseEvents , EventStream } from '../EventStream' ;
12
13
import { type ResponseFunctionCallArgumentsDeltaEvent , type ResponseTextDeltaEvent } from './EventTypes' ;
13
- import { maybeParseResponse } from '../ResponsesParser' ;
14
+ import { maybeParseResponse , ParseableToolsParams } from '../ResponsesParser' ;
15
+ import { Stream } from 'openai/streaming' ;
14
16
15
- export type ResponseStreamParams = Omit < ResponseCreateParamsBase , 'stream' > & {
17
+ export type ResponseStreamParams = ResponseCreateAndStreamParams | ResponseStreamByIdParams ;
18
+
19
+ export type ResponseCreateAndStreamParams = Omit < ResponseCreateParamsBase , 'stream' > & {
16
20
stream ?: true ;
17
21
} ;
18
22
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
+
19
48
type ResponseEvents = BaseEvents &
20
49
Omit <
21
50
{
@@ -52,7 +81,7 @@ export class ResponseStream<ParsedT = null>
52
81
) : ResponseStream < ParsedT > {
53
82
const runner = new ResponseStream < ParsedT > ( params as ResponseCreateParamsStreaming ) ;
54
83
runner . _run ( ( ) =>
55
- runner . _createResponse ( client , params , {
84
+ runner . _createOrRetrieveResponse ( client , params , {
56
85
...options ,
57
86
headers : { ...options ?. headers , 'X-Stainless-Helper-Method' : 'stream' } ,
58
87
} ) ,
@@ -65,11 +94,17 @@ export class ResponseStream<ParsedT = null>
65
94
this . #currentResponseSnapshot = undefined ;
66
95
}
67
96
68
- #addEvent( this : ResponseStream < ParsedT > , event : ResponseStreamEvent ) {
97
+ #addEvent( this : ResponseStream < ParsedT > , event : ResponseStreamEvent , starting_after : number | null ) {
69
98
if ( this . ended ) return ;
70
99
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
+
71
106
const response = this . #accumulateResponse( event ) ;
72
- this . _emit ( 'event' , event ) ;
107
+ maybeEmit ( 'event' , event ) ;
73
108
74
109
switch ( event . type ) {
75
110
case 'response.output_text.delta' : {
@@ -86,7 +121,7 @@ export class ResponseStream<ParsedT = null>
86
121
throw new OpenAIError ( `expected content to be 'output_text', got ${ content . type } ` ) ;
87
122
}
88
123
89
- this . _emit ( 'response.output_text.delta' , {
124
+ maybeEmit ( 'response.output_text.delta' , {
90
125
...event ,
91
126
snapshot : content . text ,
92
127
} ) ;
@@ -99,16 +134,15 @@ export class ResponseStream<ParsedT = null>
99
134
throw new OpenAIError ( `missing output at index ${ event . output_index } ` ) ;
100
135
}
101
136
if ( output . type === 'function_call' ) {
102
- this . _emit ( 'response.function_call_arguments.delta' , {
137
+ maybeEmit ( 'response.function_call_arguments.delta' , {
103
138
...event ,
104
139
snapshot : output . arguments ,
105
140
} ) ;
106
141
}
107
142
break ;
108
143
}
109
144
default :
110
- // @ts -ignore
111
- this . _emit ( event . type , event ) ;
145
+ maybeEmit ( event . type , event ) ;
112
146
break ;
113
147
}
114
148
}
@@ -128,9 +162,9 @@ export class ResponseStream<ParsedT = null>
128
162
return parsedResponse ;
129
163
}
130
164
131
- protected async _createResponse (
165
+ protected async _createOrRetrieveResponse (
132
166
client : OpenAI ,
133
- params : ResponseStreamingParams ,
167
+ params : ResponseStreamParams ,
134
168
options ?: Core . RequestOptions ,
135
169
) : Promise < ParsedResponse < ParsedT > > {
136
170
const signal = options ?. signal ;
@@ -140,13 +174,25 @@ export class ResponseStream<ParsedT = null>
140
174
}
141
175
this . #beginRequest( ) ;
142
176
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
+
147
193
this . _connected ( ) ;
148
194
for await ( const event of stream ) {
149
- this . #addEvent( event ) ;
195
+ this . #addEvent( event , starting_after ) ;
150
196
}
151
197
if ( stream . controller . signal ?. aborted ) {
152
198
throw new APIUserAbortError ( ) ;
0 commit comments