@@ -126,47 +126,30 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
126126 }
127127
128128 /**
129- * Makes a request to the OpenAI Responses API endpoint
129+ * Makes a request to the OpenAI Responses API endpoint using the OpenAI SDK
130130 * Used by codex-mini-latest model which requires the v1/responses endpoint
131131 */
132- private async makeResponsesApiRequest (
132+ private async createResponsesApiRequest (
133133 modelId : string ,
134134 instructions : string ,
135135 input : string ,
136136 stream : boolean = true ,
137- ) : Promise < Response > {
138- // Note: Using fetch() instead of OpenAI client because the OpenAI SDK v5.0.0
139- // does not support the v1/responses endpoint used by codex-mini-latest model.
140- // This is a special endpoint that requires a different request/response format.
141- const apiKey = this . options . openAiNativeApiKey ?? "not-provided"
142- const baseURL = this . options . openAiNativeBaseUrl ?? "https://api.openai.com/v1"
143-
137+ ) {
144138 try {
145- const response = await fetch ( `${ baseURL } /responses` , {
146- method : "POST" ,
147- headers : {
148- "Content-Type" : "application/json" ,
149- Authorization : `Bearer ${ apiKey } ` ,
150- } ,
151- body : JSON . stringify ( {
139+ if ( stream ) {
140+ return await this . client . responses . stream ( {
152141 model : modelId ,
153142 instructions : instructions ,
154143 input : input ,
155- stream : stream ,
156- } ) ,
157- } )
158-
159- if ( ! response . ok ) {
160- const errorText = await response . text ( )
161- throw new Error ( `OpenAI Responses API error: ${ response . status } ${ response . statusText } - ${ errorText } ` )
144+ } )
145+ } else {
146+ return await this . client . responses . create ( {
147+ model : modelId ,
148+ instructions : instructions ,
149+ input : input ,
150+ } )
162151 }
163-
164- return response
165152 } catch ( error ) {
166- // Handle network failures and other errors
167- if ( error instanceof TypeError && error . message . includes ( "fetch" ) ) {
168- throw new Error ( `Network error while calling OpenAI Responses API: ${ error . message } ` )
169- }
170153 if ( error instanceof Error ) {
171154 throw new Error ( `OpenAI Responses API error: ${ error . message } ` )
172155 }
@@ -182,9 +165,9 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
182165 // Convert messages to a single input string
183166 const input = this . convertMessagesToInput ( messages )
184167
185- // Make API call using shared helper
186- const response = await this . makeResponsesApiRequest ( model . id , systemPrompt , input , true )
187- yield * this . handleResponsesStreamResponse ( response . body , model , systemPrompt , input )
168+ // Make API call using OpenAI SDK
169+ const stream = await this . createResponsesApiRequest ( model . id , systemPrompt , input , true )
170+ yield * this . handleResponsesSDKStreamResponse ( stream , model , systemPrompt , input )
188171 }
189172
190173 private convertMessagesToInput ( messages : Anthropic . Messages . MessageParam [ ] ) : string {
@@ -206,81 +189,46 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
206189 . join ( "\n\n" )
207190 }
208191
209- private async * handleResponsesStreamResponse (
210- stream : ReadableStream < Uint8Array > | null ,
192+ private async * handleResponsesSDKStreamResponse (
193+ stream : any , // OpenAI SDK stream type
211194 model : OpenAiNativeModel ,
212195 systemPrompt : string ,
213196 userInput : string ,
214197 ) : ApiStream {
215- if ( ! stream ) {
216- throw new Error ( "No response stream available" )
217- }
218-
219198 let totalText = ""
220- const reader = stream . getReader ( )
221- const decoder = new TextDecoder ( )
222- let buffer = ""
223199
224200 try {
225- while ( true ) {
226- const { done, value } = await reader . read ( )
227- if ( done ) break
228-
229- buffer += decoder . decode ( value , { stream : true } )
230- const lines = buffer . split ( "\n" )
231- buffer = lines . pop ( ) || ""
232-
233- for ( const line of lines ) {
234- if ( line . trim ( ) === "" ) continue
235- if ( line . startsWith ( "data: " ) ) {
236- const data = line . slice ( 6 )
237- if ( data === "[DONE]" ) continue
238-
239- try {
240- const event = JSON . parse ( data )
241- // Handle different event types from responses API
242- if ( event . type === "response.output_text.delta" ) {
243- yield {
244- type : "text" ,
245- text : event . delta ,
246- }
247- totalText += event . delta
248- } else if ( event . type === "response.completed" ) {
249- // Calculate usage based on text length (approximate)
250- // Estimate tokens: ~1 token per 4 characters
251- const promptTokens = Math . ceil ( ( systemPrompt . length + userInput . length ) / 4 )
252- const completionTokens = Math . ceil ( totalText . length / 4 )
253- yield * this . yieldUsage ( model . info , {
254- prompt_tokens : promptTokens ,
255- completion_tokens : completionTokens ,
256- total_tokens : promptTokens + completionTokens ,
257- } )
258- } else if ( event . type === "response.error" ) {
259- // Handle error events from the API
260- throw new Error (
261- `OpenAI Responses API stream error: ${ event . error ?. message || "Unknown error" } ` ,
262- )
263- } else {
264- // Log unknown event types for debugging and future compatibility
265- console . debug (
266- `OpenAI Responses API: Unknown event type '${ event . type } ' received` ,
267- event ,
268- )
269- }
270- } catch ( e ) {
271- // Only skip if it's a JSON parsing error
272- if ( e instanceof SyntaxError ) {
273- console . debug ( "OpenAI Responses API: Failed to parse SSE data" , data )
274- } else {
275- // Re-throw other errors (like API errors)
276- throw e
277- }
278- }
201+ for await ( const chunk of stream ) {
202+ // Handle different event types from responses API
203+ if ( chunk . type === "response.output_text.delta" ) {
204+ yield {
205+ type : "text" ,
206+ text : chunk . delta ,
279207 }
208+ totalText += chunk . delta
209+ } else if ( chunk . type === "response.completed" ) {
210+ // Calculate usage based on text length (approximate)
211+ // Estimate tokens: ~1 token per 4 characters
212+ const promptTokens = Math . ceil ( ( systemPrompt . length + userInput . length ) / 4 )
213+ const completionTokens = Math . ceil ( totalText . length / 4 )
214+ yield * this . yieldUsage ( model . info , {
215+ prompt_tokens : promptTokens ,
216+ completion_tokens : completionTokens ,
217+ total_tokens : promptTokens + completionTokens ,
218+ } )
219+ } else if ( chunk . type === "response.error" ) {
220+ // Handle error events from the API
221+ throw new Error ( `OpenAI Responses API stream error: ${ chunk . error ?. message || "Unknown error" } ` )
222+ } else {
223+ // Log unknown event types for debugging and future compatibility
224+ console . debug ( `OpenAI Responses API: Unknown event type '${ chunk . type } ' received` , chunk )
280225 }
281226 }
282- } finally {
283- reader . releaseLock ( )
227+ } catch ( error ) {
228+ if ( error instanceof Error ) {
229+ throw new Error ( `OpenAI Responses API stream error: ${ error . message } ` )
230+ }
231+ throw error
284232 }
285233 }
286234
@@ -348,10 +296,15 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
348296 const { id, temperature, reasoning } = this . getModel ( )
349297
350298 if ( id === "codex-mini-latest" ) {
351- // Make API call using shared helper
352- const response = await this . makeResponsesApiRequest ( id , "Complete the following prompt:" , prompt , false )
353- const data = await response . json ( )
354- return data . output_text || ""
299+ // Make API call using OpenAI SDK
300+ const response = await this . createResponsesApiRequest (
301+ id ,
302+ "Complete the following prompt:" ,
303+ prompt ,
304+ false ,
305+ )
306+ // The SDK response structure may differ from the raw API response
307+ return ( response as any ) . output_text || ""
355308 }
356309
357310 const params : OpenAI . Chat . Completions . ChatCompletionCreateParamsNonStreaming = {
0 commit comments