@@ -76,7 +76,52 @@ def test_tool_call_output_item_mixed_list() -> None:
7676 assert  items [2 ]["type" ] ==  "input_file"  and  items [2 ]["file_data" ] ==  "ZmlsZS1kYXRh" 
7777
7878
79+ def  test_tool_call_output_item_image_forwards_file_id_and_detail () ->  None :
80+  """Ensure image outputs forward provided file_id and detail fields.""" 
81+  call  =  _make_tool_call ()
82+  out  =  ToolOutputImage (file_id = "file_123" , detail = "high" )
83+  payload  =  ItemHelpers .tool_call_output_item (call , out )
84+ 
85+  assert  payload ["type" ] ==  "function_call_output" 
86+  assert  payload ["call_id" ] ==  call .call_id 
87+  item  =  payload ["output" ][0 ]
88+  assert  isinstance (item , dict )
89+  assert  item ["type" ] ==  "input_image" 
90+  assert  item ["file_id" ] ==  "file_123" 
91+  assert  item ["detail" ] ==  "high" 
92+ 
93+ 
94+ def  test_tool_call_output_item_file_forwards_file_id_and_filename () ->  None :
95+  """Ensure file outputs forward provided file_id and filename fields.""" 
96+  call  =  _make_tool_call ()
97+  out  =  ToolOutputFileContent (file_id = "file_456" , filename = "report.pdf" )
98+  payload  =  ItemHelpers .tool_call_output_item (call , out )
99+ 
100+  assert  payload ["type" ] ==  "function_call_output" 
101+  assert  payload ["call_id" ] ==  call .call_id 
102+  item  =  payload ["output" ][0 ]
103+  assert  isinstance (item , dict )
104+  assert  item ["type" ] ==  "input_file" 
105+  assert  item ["file_id" ] ==  "file_456" 
106+  assert  item ["filename" ] ==  "report.pdf" 
107+ 
108+ 
109+ def  test_tool_call_output_item_file_forwards_file_url () ->  None :
110+  """Ensure file outputs forward provided file_url when present.""" 
111+  call  =  _make_tool_call ()
112+  out  =  ToolOutputFileContent (file_url = "https://example.com/report.pdf" )
113+  payload  =  ItemHelpers .tool_call_output_item (call , out )
114+ 
115+  assert  payload ["type" ] ==  "function_call_output" 
116+  assert  payload ["call_id" ] ==  call .call_id 
117+  item  =  payload ["output" ][0 ]
118+  assert  isinstance (item , dict )
119+  assert  item ["type" ] ==  "input_file" 
120+  assert  item ["file_url" ] ==  "https://example.com/report.pdf" 
121+ 
122+ 
79123def  test_tool_call_output_item_text_dict_variant () ->  None :
124+  """Dict with type='text' and text field should be treated as structured output.""" 
80125 call  =  _make_tool_call ()
81126 # Dict variant using the pydantic model schema (type="text"). 
82127 out  =  {"type" : "text" , "text" : "hey" }
@@ -91,45 +136,237 @@ def test_tool_call_output_item_text_dict_variant() -> None:
91136 assert  item ["text" ] ==  "hey" 
92137
93138
94- def  test_tool_call_output_item_image_forwards_file_id_and_detail () ->  None :
95-  """Ensure  image outputs forward provided file_id and detail fields .""" 
139+ def  test_tool_call_output_item_image_dict_variant () ->  None :
140+  """Dict with type=' image' and image_url field should be treated as structured output .""" 
96141 call  =  _make_tool_call ()
97-  out  =  ToolOutputImage ( file_id = "file_123" ,  detail = "high" ) 
142+  out  =  { "type" :  "image" ,  "image_url" :  "http://example.com/img.png" ,  " detail" :  "auto" } 
98143 payload  =  ItemHelpers .tool_call_output_item (call , out )
99144
100145 assert  payload ["type" ] ==  "function_call_output" 
101146 assert  payload ["call_id" ] ==  call .call_id 
147+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
148+  item  =  payload ["output" ][0 ]
149+  assert  isinstance (item , dict )
150+  assert  item ["type" ] ==  "input_image" 
151+  assert  item ["image_url" ] ==  "http://example.com/img.png" 
152+  assert  item ["detail" ] ==  "auto" 
153+ 
154+ 
155+ def  test_tool_call_output_item_image_dict_variant_with_file_id () ->  None :
156+  """Dict with type='image' and image_url field should be treated as structured output.""" 
157+  call  =  _make_tool_call ()
158+  out  =  {"type" : "image" , "file_id" : "file_123" }
159+  payload  =  ItemHelpers .tool_call_output_item (call , out )
160+ 
161+  assert  payload ["type" ] ==  "function_call_output" 
162+  assert  payload ["call_id" ] ==  call .call_id 
163+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
102164 item  =  payload ["output" ][0 ]
103165 assert  isinstance (item , dict )
104166 assert  item ["type" ] ==  "input_image" 
105167 assert  item ["file_id" ] ==  "file_123" 
106-  assert  item ["detail" ] ==  "high" 
107168
108169
109- def  test_tool_call_output_item_file_forwards_file_id_and_filename () ->  None :
110-  """Ensure  file outputs forward provided file_id and filename fields .""" 
170+ def  test_tool_call_output_item_file_dict_variant_with_file_data () ->  None :
171+  """Dict with type=' file' and file_data field should be treated as structured output .""" 
111172 call  =  _make_tool_call ()
112-  out  =  ToolOutputFileContent ( file_id = "file_456" ,  filename = " report.pdf") 
173+  out  =  { "type" :  "file" ,  "file_data" :  "foobar" ,  " filename" :  " report.pdf"} 
113174 payload  =  ItemHelpers .tool_call_output_item (call , out )
114175
115176 assert  payload ["type" ] ==  "function_call_output" 
116177 assert  payload ["call_id" ] ==  call .call_id 
178+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
117179 item  =  payload ["output" ][0 ]
118180 assert  isinstance (item , dict )
119181 assert  item ["type" ] ==  "input_file" 
120-  assert  item ["file_id " ] ==  "file_456 " 
182+  assert  item ["file_data " ] ==  "foobar " 
121183 assert  item ["filename" ] ==  "report.pdf" 
122184
123185
124- def  test_tool_call_output_item_file_forwards_file_url () ->  None :
125-  """Ensure  file outputs forward provided file_url when present .""" 
186+ def  test_tool_call_output_item_file_dict_variant_with_file_url () ->  None :
187+  """Dict with type=' file' and file_url field should be treated as structured output .""" 
126188 call  =  _make_tool_call ()
127-  out  =  ToolOutputFileContent ( file_url = " https://example.com/report.pdf") 
189+  out  =  { "type" :  "file" ,  " file_url" :  " https://example.com/report.pdf",  "filename" :  "report.pdf" } 
128190 payload  =  ItemHelpers .tool_call_output_item (call , out )
129191
130192 assert  payload ["type" ] ==  "function_call_output" 
131193 assert  payload ["call_id" ] ==  call .call_id 
194+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
132195 item  =  payload ["output" ][0 ]
133196 assert  isinstance (item , dict )
134197 assert  item ["type" ] ==  "input_file" 
135198 assert  item ["file_url" ] ==  "https://example.com/report.pdf" 
199+  assert  item ["filename" ] ==  "report.pdf" 
200+ 
201+ 
202+ def  test_tool_call_output_item_file_dict_variant_with_file_id () ->  None :
203+  """Dict with type='file' and file_id field should be treated as structured output.""" 
204+  call  =  _make_tool_call ()
205+  out  =  {"type" : "file" , "file_id" : "file_123" , "filename" : "report.pdf" }
206+  payload  =  ItemHelpers .tool_call_output_item (call , out )
207+ 
208+  assert  payload ["type" ] ==  "function_call_output" 
209+  assert  payload ["call_id" ] ==  call .call_id 
210+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
211+  item  =  payload ["output" ][0 ]
212+  assert  isinstance (item , dict )
213+  assert  item ["type" ] ==  "input_file" 
214+  assert  item ["file_id" ] ==  "file_123" 
215+  assert  item ["filename" ] ==  "report.pdf" 
216+ 
217+ 
218+ def  test_tool_call_output_item_image_with_extra_fields () ->  None :
219+  """Dict with type='image', image_url, and extra fields should still be converted.""" 
220+  call  =  _make_tool_call ()
221+  out  =  {"type" : "image" , "image_url" : "http://example.com/img.png" , "foobar" : 213 }
222+  payload  =  ItemHelpers .tool_call_output_item (call , out )
223+ 
224+  assert  payload ["type" ] ==  "function_call_output" 
225+  assert  payload ["call_id" ] ==  call .call_id 
226+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  1 
227+  item  =  payload ["output" ][0 ]
228+  assert  isinstance (item , dict )
229+  assert  item ["type" ] ==  "input_image" 
230+  assert  item ["image_url" ] ==  "http://example.com/img.png" 
231+  # Extra field should be ignored by Pydantic 
232+  assert  "foobar"  not  in item 
233+ 
234+ 
235+ def  test_tool_call_output_item_mixed_list_with_valid_dicts () ->  None :
236+  """List with valid dict variants (with type field) should be converted.""" 
237+  call  =  _make_tool_call ()
238+  out  =  [
239+  {"type" : "text" , "text" : "hello" },
240+  {"type" : "image" , "image_url" : "http://example.com/img.png" },
241+  {"type" : "file" , "file_id" : "file_123" },
242+  ]
243+  payload  =  ItemHelpers .tool_call_output_item (call , out )
244+ 
245+  assert  payload ["type" ] ==  "function_call_output" 
246+  assert  payload ["call_id" ] ==  call .call_id 
247+  assert  isinstance (payload ["output" ], list ) and  len (payload ["output" ]) ==  3 
248+ 
249+  assert  payload ["output" ][0 ]["type" ] ==  "input_text" 
250+  assert  payload ["output" ][0 ]["text" ] ==  "hello" 
251+  assert  payload ["output" ][1 ]["type" ] ==  "input_image" 
252+  assert  payload ["output" ][1 ]["image_url" ] ==  "http://example.com/img.png" 
253+  assert  payload ["output" ][2 ]["type" ] ==  "input_file" 
254+  assert  payload ["output" ][2 ]["file_id" ] ==  "file_123" 
255+ 
256+ 
257+ def  test_tool_call_output_item_text_type_only_not_converted () ->  None :
258+  """Dict with only type='text' should NOT be treated as structured output.""" 
259+  call  =  _make_tool_call ()
260+  out  =  {"type" : "text" }
261+  payload  =  ItemHelpers .tool_call_output_item (call , out )
262+ 
263+  assert  payload ["type" ] ==  "function_call_output" 
264+  assert  payload ["call_id" ] ==  call .call_id 
265+  # Should be converted to string since it doesn't have required fields 
266+  assert  isinstance (payload ["output" ], str )
267+  assert  payload ["output" ] ==  "{'type': 'text'}" 
268+ 
269+ 
270+ def  test_tool_call_output_item_image_type_only_not_converted () ->  None :
271+  """Dict with only type='image' should NOT be treated as structured output.""" 
272+  call  =  _make_tool_call ()
273+  out  =  {"type" : "image" }
274+  payload  =  ItemHelpers .tool_call_output_item (call , out )
275+ 
276+  assert  payload ["type" ] ==  "function_call_output" 
277+  assert  payload ["call_id" ] ==  call .call_id 
278+  # Should be converted to string since it doesn't have required fields 
279+  assert  isinstance (payload ["output" ], str )
280+  assert  payload ["output" ] ==  "{'type': 'image'}" 
281+ 
282+ 
283+ def  test_tool_call_output_item_file_type_only_not_converted () ->  None :
284+  """Dict with only type='file' should NOT be treated as structured output.""" 
285+  call  =  _make_tool_call ()
286+  out  =  {"type" : "file" }
287+  payload  =  ItemHelpers .tool_call_output_item (call , out )
288+ 
289+  assert  payload ["type" ] ==  "function_call_output" 
290+  assert  payload ["call_id" ] ==  call .call_id 
291+  assert  isinstance (payload ["output" ], str )
292+  assert  payload ["output" ] ==  "{'type': 'file'}" 
293+ 
294+ 
295+ def  test_tool_call_output_item_empty_dict_not_converted () ->  None :
296+  """Empty dict should NOT be treated as structured output.""" 
297+  call  =  _make_tool_call ()
298+  out : dict [str , str ] =  {}
299+  payload  =  ItemHelpers .tool_call_output_item (call , out )
300+ 
301+  assert  payload ["type" ] ==  "function_call_output" 
302+  assert  payload ["call_id" ] ==  call .call_id 
303+  assert  isinstance (payload ["output" ], str )
304+  assert  payload ["output" ] ==  "{}" 
305+ 
306+ 
307+ def  test_tool_call_output_item_dict_without_type_not_converted () ->  None :
308+  """Dict without 'type' field should NOT be treated as structured output.""" 
309+  call  =  _make_tool_call ()
310+  out  =  {"msg" : "1234" }
311+  payload  =  ItemHelpers .tool_call_output_item (call , out )
312+ 
313+  assert  payload ["type" ] ==  "function_call_output" 
314+  assert  payload ["call_id" ] ==  call .call_id 
315+  # Should be converted to string since it lacks 'type' field 
316+  assert  isinstance (payload ["output" ], str )
317+  assert  payload ["output" ] ==  "{'msg': '1234'}" 
318+ 
319+ 
320+ def  test_tool_call_output_item_image_dict_variant_with_location_not_converted () ->  None :
321+  """Dict with type='image' and location field should NOT be treated as structured output.""" 
322+  call  =  _make_tool_call ()
323+  out  =  {"type" : "image" , "location" : "/path/to/img.png" }
324+  payload  =  ItemHelpers .tool_call_output_item (call , out )
325+ 
326+  assert  payload ["type" ] ==  "function_call_output" 
327+  assert  payload ["call_id" ] ==  call .call_id 
328+  # Should be converted to string since it lacks required fields (image_url or file_id) 
329+  assert  isinstance (payload ["output" ], str )
330+  assert  payload ["output" ] ==  "{'type': 'image', 'location': '/path/to/img.png'}" 
331+ 
332+ 
333+ def  test_tool_call_output_item_file_dict_variant_with_path_not_converted () ->  None :
334+  """Dict with type='file' and path field should NOT be treated as structured output.""" 
335+  call  =  _make_tool_call ()
336+  out  =  {"type" : "file" , "path" : "/path/to/file.txt" }
337+  payload  =  ItemHelpers .tool_call_output_item (call , out )
338+ 
339+  assert  payload ["type" ] ==  "function_call_output" 
340+  assert  payload ["call_id" ] ==  call .call_id 
341+  # Should be converted to string since it lacks required fields (file_data, file_url, or file_id) 
342+  assert  isinstance (payload ["output" ], str )
343+  assert  payload ["output" ] ==  "{'type': 'file', 'path': '/path/to/file.txt'}" 
344+ 
345+ 
346+ def  test_tool_call_output_item_list_without_type_not_converted () ->  None :
347+  """List with dicts lacking 'type' field should NOT be treated as structured output.""" 
348+  call  =  _make_tool_call ()
349+  out  =  [{"msg" : "foobar" }]
350+  payload  =  ItemHelpers .tool_call_output_item (call , out )
351+ 
352+  assert  payload ["type" ] ==  "function_call_output" 
353+  assert  payload ["call_id" ] ==  call .call_id 
354+  # Should be converted to string since list items lack 'type' field 
355+  assert  isinstance (payload ["output" ], str )
356+  assert  payload ["output" ] ==  "[{'msg': 'foobar'}]" 
357+ 
358+ 
359+ def  test_tool_call_output_item_mixed_list_partial_invalid_not_converted () ->  None :
360+  """List with mix of valid and invalid dicts should NOT be treated as structured output.""" 
361+  call  =  _make_tool_call ()
362+  out  =  [
363+  {"type" : "text" , "text" : "hello" }, # Valid 
364+  {"msg" : "foobar" }, # Invalid 
365+  ]
366+  payload  =  ItemHelpers .tool_call_output_item (call , out )
367+ 
368+  assert  payload ["type" ] ==  "function_call_output" 
369+  assert  payload ["call_id" ] ==  call .call_id 
370+  # All-or-nothing: if any item is invalid, convert entire list to string 
371+  assert  isinstance (payload ["output" ], str )
372+  assert  payload ["output" ] ==  "[{'type': 'text', 'text': 'hello'}, {'msg': 'foobar'}]" 
0 commit comments