@@ -480,6 +480,16 @@ pub(crate) fn decode_openai_content(
480480 source : decode_file_source ( block) ,
481481 } ) ;
482482 }
483+ Some ( "audio" ) | Some ( "audio_url" ) | Some ( "input_audio" ) => {
484+ content. push ( ContentBlock :: Audio {
485+ source : decode_media_source ( block, "audio" ) ,
486+ } ) ;
487+ }
488+ Some ( "video" ) | Some ( "video_url" ) | Some ( "input_video" ) => {
489+ content. push ( ContentBlock :: Video {
490+ source : decode_media_source ( block, "video" ) ,
491+ } ) ;
492+ }
483493 _ => content. push ( ContentBlock :: Unknown {
484494 provider : provider. into ( ) ,
485495 raw : Value :: Object ( block. clone ( ) ) ,
@@ -561,6 +571,76 @@ pub(crate) fn decode_file_source(block: &Map<String, Value>) -> FileSource {
561571 FileSource :: Raw ( Value :: Object ( block. clone ( ) ) )
562572}
563573
574+ /// Decodes common OpenAI audio and video block shapes into normalized media sources.
575+ pub ( crate ) fn decode_media_source ( block : & Map < String , Value > , kind : & str ) -> MediaSource {
576+ let url_key = format ! ( "{kind}_url" ) ;
577+ if let Some ( value) = block. get ( & url_key) {
578+ if let Some ( url) = value. as_str ( ) {
579+ return MediaSource :: Url {
580+ url : url. to_string ( ) ,
581+ media_type : media_type ( block, kind) ,
582+ } ;
583+ }
584+ if let Some ( payload) = value. as_object ( )
585+ && let Some ( url) = payload. get ( "url" ) . and_then ( Value :: as_str)
586+ {
587+ return MediaSource :: Url {
588+ url : url. to_string ( ) ,
589+ media_type : media_type ( payload, kind) . or_else ( || media_type ( block, kind) ) ,
590+ } ;
591+ }
592+ }
593+
594+ let input_key = format ! ( "input_{kind}" ) ;
595+ for key in [ input_key. as_str ( ) , kind] {
596+ let Some ( value) = block. get ( key) else {
597+ continue ;
598+ } ;
599+ if let Some ( data) = value. as_str ( ) {
600+ return MediaSource :: Base64 {
601+ media_type : media_type ( block, kind) ,
602+ data : data. to_string ( ) ,
603+ } ;
604+ }
605+ if let Some ( payload) = value. as_object ( ) {
606+ if let Some ( url) = payload. get ( "url" ) . and_then ( Value :: as_str) {
607+ return MediaSource :: Url {
608+ url : url. to_string ( ) ,
609+ media_type : media_type ( payload, kind) . or_else ( || media_type ( block, kind) ) ,
610+ } ;
611+ }
612+ if let Some ( data) = payload. get ( "data" ) . and_then ( Value :: as_str) {
613+ return MediaSource :: Base64 {
614+ media_type : media_type ( payload, kind) . or_else ( || media_type ( block, kind) ) ,
615+ data : data. to_string ( ) ,
616+ } ;
617+ }
618+ }
619+ }
620+
621+ if let Some ( data) = block. get ( "data" ) . and_then ( Value :: as_str) {
622+ return MediaSource :: Base64 {
623+ media_type : media_type ( block, kind) ,
624+ data : data. to_string ( ) ,
625+ } ;
626+ }
627+ MediaSource :: Raw ( Value :: Object ( block. clone ( ) ) )
628+ }
629+
630+ fn media_type ( object : & Map < String , Value > , kind : & str ) -> Option < String > {
631+ object
632+ . get ( "media_type" )
633+ . or_else ( || object. get ( "format" ) )
634+ . and_then ( Value :: as_str)
635+ . map ( |value| {
636+ if value. contains ( '/' ) {
637+ value. to_string ( )
638+ } else {
639+ format ! ( "{kind}/{value}" )
640+ }
641+ } )
642+ }
643+
564644/// Decodes one OpenAI tool call into a normalized tool call.
565645pub ( crate ) fn decode_openai_tool_call (
566646 tool_call : & Value ,
0 commit comments