1+ import type { CompletionUsage } from "openai/resources/index.js" ;
12import {
23 ChatCompletion ,
4+ ChatCompletionAssistantMessageParam ,
35 ChatCompletionChunk ,
6+ ChatCompletionContentPart ,
7+ ChatCompletionContentPartImage ,
8+ ChatCompletionContentPartInputAudio ,
9+ ChatCompletionContentPartRefusal ,
10+ ChatCompletionContentPartText ,
411 ChatCompletionCreateParams ,
5- ChatCompletionCreateParamsNonStreaming ,
612 ChatCompletionCreateParamsStreaming ,
713 ChatCompletionMessageParam ,
14+ ChatCompletionMessageToolCall ,
815 ChatCompletionTool ,
916} from "openai/resources/index.js" ;
10- import type { CompletionUsage } from "openai/resources/index.js" ;
1117import {
1218 Response ,
1319 ResponseCreateParams ,
1420 ResponseFunctionCallArgumentsDoneEvent ,
15- ResponseFunctionCallArgumentsDeltaEvent ,
1621 ResponseIncompleteEvent ,
1722 ResponseInput ,
1823 ResponseInputAudio ,
@@ -25,9 +30,7 @@ import {
2530 ResponseOutputRefusal ,
2631 ResponseOutputText ,
2732 ResponseReasoningSummaryTextDeltaEvent ,
28- ResponseReasoningTextDeltaEvent ,
2933 ResponseStreamEvent ,
30- ResponseTextDeltaEvent ,
3134 ResponseUsage ,
3235} from "openai/resources/responses/responses.js" ;
3336
@@ -37,97 +40,55 @@ export function isResponsesModel(model: string): boolean {
3740 return ! ! model && RESPONSES_MODEL_REGEX . test ( model ) ;
3841}
3942
40- function ensureText ( value : string | null | undefined ) : string {
41- if ( value === undefined || value === null ) {
42- return " " ;
43- }
44- return value === "" ? " " : value ;
45- }
46-
4743function convertTextPart ( text : string ) : ResponseInputText {
4844 return {
45+ text,
4946 type : "input_text" ,
50- text : ensureText ( text ) ,
5147 } ;
5248}
5349
5450function convertImagePart (
55- image : ChatCompletionMessageParam [ "content" ] ,
56- ) : ResponseInputImage | undefined {
57- if (
58- Array . isArray ( image ) ||
59- ! image ||
60- typeof image !== "object" ||
61- ( image as any ) . type !== "image_url"
62- ) {
63- return undefined ;
64- }
65- const imageUrlPart = image as any ;
51+ image : ChatCompletionContentPartImage ,
52+ ) : ResponseInputImage {
6653 const converted : ResponseInputImage = {
6754 type : "input_image" ,
68- image_url : imageUrlPart . image_url ? .url ,
69- detail : imageUrlPart . image_url ? .detail ?? "auto" ,
55+ image_url : image . image_url . url ,
56+ detail : image . image_url . detail ?? "auto" ,
7057 } ;
71- if ( imageUrlPart . image_url ? .file_id ) {
72- ( converted as any ) . file_id = imageUrlPart . image_url . file_id ;
58+ if ( ( image . image_url as any ) . file_id ) {
59+ ( converted as any ) . file_id = ( image . image_url as any ) . file_id ;
7360 }
7461 return converted ;
7562}
7663
7764function convertAudioPart (
78- part : ChatCompletionMessageParam [ "content" ] ,
79- ) : ResponseInputAudio | undefined {
80- if (
81- Array . isArray ( part ) ||
82- ! part ||
83- typeof part !== "object" ||
84- ( part as any ) . type !== "input_audio"
85- ) {
86- return undefined ;
87- }
88- const audio = part as any ;
89- if ( ! audio . input_audio ) {
90- return undefined ;
91- }
65+ part : ChatCompletionContentPartInputAudio ,
66+ ) : ResponseInputAudio {
9267 return {
9368 type : "input_audio" ,
9469 input_audio : {
95- data : audio . input_audio . data ,
96- format : audio . input_audio . format ,
70+ data : part . input_audio . data ,
71+ format : part . input_audio . format ,
9772 } ,
9873 } ;
9974}
10075
10176function convertFilePart (
102- part : ChatCompletionMessageParam [ "content" ] ,
103- ) : ResponseInputFile | undefined {
104- if (
105- Array . isArray ( part ) ||
106- ! part ||
107- typeof part !== "object" ||
108- ( part as any ) . type !== "file"
109- ) {
110- return undefined ;
111- }
112-
113- const filePart = part as any ;
114- const file = filePart . file ;
115- if ( ! file ) {
116- return undefined ;
117- }
77+ part : ChatCompletionContentPart . File ,
78+ ) : ResponseInputFile {
11879 return {
11980 type : "input_file" ,
120- file_id : file . file_id ?? undefined ,
121- file_data : file . file_data ?? undefined ,
122- filename : file . filename ?? undefined ,
123- file_url : file . file_url ?? undefined ,
81+ file_id : part . file . file_id ?? undefined ,
82+ file_data : part . file . file_data ?? undefined ,
83+ filename : part . file . filename ?? undefined ,
84+ file_url : ( part . file as any ) . file_url ?? undefined ,
12485 } ;
12586}
12687
12788function convertMessageContentPart (
128- part : any ,
89+ part : ChatCompletionContentPart | ChatCompletionContentPartRefusal ,
12990) : ResponseInputContent | undefined {
130- switch ( part ? .type ) {
91+ switch ( part . type ) {
13192 case "text" :
13293 return convertTextPart ( part . text ) ;
13394 case "image_url" :
@@ -136,6 +97,9 @@ function convertMessageContentPart(
13697 return convertAudioPart ( part ) ;
13798 case "file" :
13899 return convertFilePart ( part ) ;
100+ case "refusal" :
101+ // Skip refusal parts - they're not input content
102+ return undefined ;
139103 default :
140104 return undefined ;
141105 }
@@ -168,14 +132,13 @@ function createOutputTextPart(
168132 text : string ,
169133 source ?: Partial < ResponseOutputText > ,
170134) : AssistantContentPart {
171- const normalizedText = ensureText ( text ) ;
172135 const annotations =
173136 Array . isArray ( source ?. annotations ) && source . annotations . length > 0
174137 ? source . annotations
175138 : [ ] ;
176139 const part : ResponseOutputText = {
140+ text,
177141 type : "output_text" ,
178- text : normalizedText ,
179142 annotations,
180143 } ;
181144 if ( Array . isArray ( source ?. logprobs ) && source . logprobs . length > 0 ) {
@@ -186,8 +149,8 @@ function createOutputTextPart(
186149
187150function createRefusalPart ( refusal : string ) : AssistantContentPart {
188151 return {
152+ refusal,
189153 type : "refusal" ,
190- refusal : ensureText ( refusal ) ,
191154 } ;
192155}
193156
@@ -203,29 +166,33 @@ function collectAssistantContentParts(
203166 }
204167 } else if ( Array . isArray ( content ) ) {
205168 for ( const rawPart of content ) {
206- const part : any = rawPart as any ;
169+ // Content array should be ChatCompletionContentPartText | ChatCompletionContentPartRefusal
170+ // but we handle "output_text" type which may come from Response API conversions
171+ const part = rawPart as
172+ | ChatCompletionContentPartText
173+ | ChatCompletionContentPartRefusal
174+ | { type : "output_text" ; text : string } ;
207175 if ( ! part ) {
208176 continue ;
209177 }
210- if ( typeof part === "string" ) {
211- if ( part . trim ( ) . length > 0 ) {
212- parts . push ( createOutputTextPart ( part ) ) ;
213- }
214- continue ;
215- }
216- const partType : string | undefined = part . type ;
178+
179+ const partType = part . type ;
217180 if ( partType === "text" ) {
218- const textValue = part . text ;
219- if ( typeof textValue === "string" && textValue . trim ( ) . length > 0 ) {
220- parts . push ( createOutputTextPart ( textValue , part ) ) ;
181+ const textPart = part as ChatCompletionContentPartText ;
182+ if (
183+ typeof textPart . text === "string" &&
184+ textPart . text . trim ( ) . length > 0
185+ ) {
186+ parts . push ( createOutputTextPart ( textPart . text ) ) ;
221187 }
222188 } else if ( partType === "output_text" ) {
223- const textValue = part . text ;
189+ const textValue = ( part as { type : "output_text" ; text : string } ) . text ;
224190 if ( typeof textValue === "string" && textValue . trim ( ) . length > 0 ) {
225- parts . push ( createOutputTextPart ( textValue , part ) ) ;
191+ parts . push ( createOutputTextPart ( textValue ) ) ;
226192 }
227193 } else if ( partType === "refusal" ) {
228- const refusalText = part . refusal ?? part . text ;
194+ const refusalPart = part as ChatCompletionContentPartRefusal ;
195+ const refusalText = refusalPart . refusal ;
229196 if ( typeof refusalText === "string" && refusalText . trim ( ) . length > 0 ) {
230197 parts . push ( createRefusalPart ( refusalText ) ) ;
231198 }
@@ -364,9 +331,10 @@ export function toResponsesInput(
364331 }
365332
366333 if ( message . role === "assistant" ) {
334+ const assistantMessage = message as ChatCompletionAssistantMessageParam ;
367335 const assistantContentParts = collectAssistantContentParts (
368- message . content ,
369- ( message as any ) . refusal ?? ( message as any ) . declination ?? null ,
336+ assistantMessage . content ,
337+ assistantMessage . refusal ?? null ,
370338 ) ;
371339 if ( assistantContentParts . length > 0 ) {
372340 const providedId = ( message as any ) . id ;
@@ -382,16 +350,15 @@ export function toResponsesInput(
382350 status : "completed" ,
383351 } as ResponseOutputMessage as any ) ;
384352 }
385- if ( message . tool_calls ?. length ) {
386- message . tool_calls . forEach ( ( toolCall , index ) => {
387- if ( toolCall . type === "function" && ( toolCall as any ) . function ) {
388- const func = ( toolCall as any ) . function ;
353+ if ( assistantMessage . tool_calls ?. length ) {
354+ assistantMessage . tool_calls . forEach ( ( toolCall , index ) => {
355+ if ( toolCall . type === "function" ) {
389356 const callId = toolCall . id ?? `tool_call_${ index } ` ;
390357 const functionCall : any = {
391358 type : "function_call" ,
392359 call_id : callId ,
393- name : func . name ?? "" ,
394- arguments : func . arguments ?? "{}" ,
360+ name : toolCall . function . name ?? "" ,
361+ arguments : toolCall . function . arguments ?? "{}" ,
395362 } ;
396363 if (
397364 typeof toolCall . id === "string" &&
0 commit comments