@@ -13,10 +13,13 @@ use codex_core::WireApi;
1313use  codex_core:: spawn:: CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR ; 
1414use  codex_otel:: otel_event_manager:: OtelEventManager ; 
1515use  codex_protocol:: ConversationId ; 
16+ use  codex_protocol:: models:: FunctionCallOutputContentItem ; 
17+ use  codex_protocol:: models:: FunctionCallOutputPayload ; 
1618use  codex_protocol:: models:: ReasoningItemContent ; 
1719use  core_test_support:: load_default_config_for_test; 
1820use  futures:: StreamExt ; 
1921use  serde_json:: Value ; 
22+ use  serde_json:: json; 
2023use  tempfile:: TempDir ; 
2124use  wiremock:: Mock ; 
2225use  wiremock:: MockServer ; 
@@ -254,6 +257,66 @@ async fn attaches_reasoning_to_function_call_anchor() {
254257    assert_eq ! ( tool_calls[ 0 ] [ "type" ] ,  Value :: String ( "function" . into( ) ) ) ; 
255258} 
256259
260+ #[ tokio:: test( flavor = "multi_thread" ,  worker_threads = 2 ) ]  
261+ async  fn  function_call_output_uses_content_items_for_images ( )  { 
262+     if  network_disabled ( )  { 
263+         println ! ( 
264+             "Skipping test because it cannot execute when network is disabled in a Codex sandbox." 
265+         ) ; 
266+         return ; 
267+     } 
268+ 
269+     let  content_items = vec ! [ 
270+         FunctionCallOutputContentItem :: InputText  { 
271+             text:  "See image" . to_string( ) , 
272+         } , 
273+         FunctionCallOutputContentItem :: InputImage  { 
274+             image_url:  "data:image/png;base64,abc" . to_string( ) , 
275+         } , 
276+     ] ; 
277+     let  serialized_items = serde_json:: to_string ( & content_items) . expect ( "serialize content items" ) ; 
278+     let  payload = FunctionCallOutputPayload  { 
279+         content :  serialized_items, 
280+         content_items :  Some ( content_items) , 
281+         success :  Some ( true ) , 
282+     } ; 
283+ 
284+     let  body = run_request ( vec ! [ 
285+         user_message( "u1" ) , 
286+         function_call( ) , 
287+         ResponseItem :: FunctionCallOutput  { 
288+             call_id:  "c1" . to_string( ) , 
289+             output:  payload, 
290+         } , 
291+     ] ) 
292+     . await ; 
293+ 
294+     let  messages = messages_from ( & body) ; 
295+     let  tool = messages
296+         . iter ( ) 
297+         . find ( |msg| msg[ "role" ]  == "tool" ) 
298+         . expect ( "tool message present" ) ; 
299+     let  content = tool[ "content" ] 
300+         . as_array ( ) 
301+         . expect ( "tool content serialized as array" ) ; 
302+ 
303+     assert_eq ! ( content. len( ) ,  2 ) ; 
304+     assert_eq ! ( 
305+         content[ 0 ] , 
306+         json!( { 
307+             "type" :  "input_text" , 
308+             "text" :  "See image" , 
309+         } ) 
310+     ) ; 
311+     assert_eq ! ( 
312+         content[ 1 ] , 
313+         json!( { 
314+             "type" :  "input_image" , 
315+             "image_url" :  "data:image/png;base64,abc" , 
316+         } ) 
317+     ) ; 
318+ } 
319+ 
257320#[ tokio:: test( flavor = "multi_thread" ,  worker_threads = 2 ) ]  
258321async  fn  attaches_reasoning_to_local_shell_call ( )  { 
259322    if  network_disabled ( )  { 
0 commit comments