26
26
DocumentUrl ,
27
27
FinishReason ,
28
28
ImageUrl ,
29
- MagicBinaryContent ,
30
- MagicDocumentUrl ,
31
29
ModelMessage ,
32
30
ModelRequest ,
33
31
ModelResponse ,
@@ -725,12 +723,7 @@ async def _map_user_prompt_items(items: Sequence[object]) -> list[ChatCompletion
725
723
async def _map_single_item (item : object ) -> list [ChatCompletionContentPartParam ]:
726
724
if isinstance (item , str ):
727
725
return [ChatCompletionContentPartTextParam (text = item , type = 'text' )]
728
- handled = await OpenAIChatModel ._handle_magic_document (item )
729
- if handled is not None :
730
- return handled
731
- handled = await OpenAIChatModel ._handle_magic_binary (item )
732
- if handled is not None :
733
- return handled
726
+ # Magic* no longer used; logic ported to base handlers
734
727
handled = OpenAIChatModel ._handle_image_url (item )
735
728
if handled is not None :
736
729
return handled
@@ -748,55 +741,6 @@ async def _map_single_item(item: object) -> list[ChatCompletionContentPartParam]
748
741
# Fallback: unknown type — return empty parts to avoid type-checker Never error
749
742
return []
750
743
751
- @staticmethod
752
- async def _handle_magic_document (item : object ) -> list [ChatCompletionContentPartParam ] | None :
753
- if not isinstance (item , MagicDocumentUrl ):
754
- return None
755
- if OpenAIChatModel ._is_text_like_media_type (item .media_type ):
756
- downloaded = await download_item (item , data_format = 'text' , type_format = 'extension' )
757
- filename = item .filename or f'file.{ downloaded ["data_type" ] or "txt" } '
758
- inline = OpenAIChatModel ._inline_file_block (filename , item .media_type , downloaded ['data' ])
759
- return [ChatCompletionContentPartTextParam (text = inline , type = 'text' )]
760
- downloaded_item = await download_item (item , data_format = 'base64_uri' , type_format = 'extension' )
761
- return [
762
- File (
763
- file = FileFile (
764
- file_data = downloaded_item ['data' ],
765
- filename = f'filename.{ downloaded_item ["data_type" ]} ' ,
766
- ),
767
- type = 'file' ,
768
- )
769
- ]
770
-
771
- @staticmethod
772
- async def _handle_magic_binary (item : object ) -> list [ChatCompletionContentPartParam ] | None :
773
- if not isinstance (item , MagicBinaryContent ):
774
- return None
775
- if OpenAIChatModel ._is_text_like_media_type (item .media_type ):
776
- text = item .data .decode ('utf-8' )
777
- filename = item .filename or 'file.txt'
778
- inline = OpenAIChatModel ._inline_file_block (filename , item .media_type , text )
779
- return [ChatCompletionContentPartTextParam (text = inline , type = 'text' )]
780
- base64_encoded = base64 .b64encode (item .data ).decode ('utf-8' )
781
- if item .is_image :
782
- image_url = ImageURL (url = f'data:{ item .media_type } ;base64,{ base64_encoded } ' )
783
- return [ChatCompletionContentPartImageParam (image_url = image_url , type = 'image_url' )]
784
- if item .is_audio :
785
- assert item .format in ('wav' , 'mp3' )
786
- audio = InputAudio (data = base64_encoded , format = item .format )
787
- return [ChatCompletionContentPartInputAudioParam (input_audio = audio , type = 'input_audio' )]
788
- if item .is_document :
789
- return [
790
- File (
791
- file = FileFile (
792
- file_data = f'data:{ item .media_type } ;base64,{ base64_encoded } ' ,
793
- filename = f'filename.{ item .format } ' ,
794
- ),
795
- type = 'file' ,
796
- )
797
- ]
798
- raise RuntimeError (f'Unsupported binary content type: { item .media_type } ' ) # pragma: no cover
799
-
800
744
@staticmethod
801
745
def _handle_image_url (item : object ) -> list [ChatCompletionContentPartParam ] | None :
802
746
if not isinstance (item , ImageUrl ):
@@ -808,6 +752,27 @@ def _handle_image_url(item: object) -> list[ChatCompletionContentPartParam] | No
808
752
async def _handle_binary_content (item : object ) -> list [ChatCompletionContentPartParam ] | None :
809
753
if not isinstance (item , BinaryContent ):
810
754
return None
755
+ if OpenAIChatModel ._is_text_like_media_type (item .media_type ):
756
+ # Inline text-like binary content as a text block
757
+ text = item .data .decode ('utf-8' )
758
+ # Derive a sensible default filename from media type
759
+ media_type = item .media_type
760
+ if media_type == 'text/plain' :
761
+ filename = 'file.txt'
762
+ elif media_type == 'text/csv' :
763
+ filename = 'file.csv'
764
+ elif media_type == 'text/markdown' :
765
+ filename = 'file.md'
766
+ elif media_type == 'application/json' or media_type .endswith ('+json' ):
767
+ filename = 'file.json'
768
+ elif media_type == 'application/xml' or media_type .endswith ('+xml' ):
769
+ filename = 'file.xml'
770
+ elif media_type in ('application/x-yaml' , 'application/yaml' , 'text/yaml' ):
771
+ filename = 'file.yaml'
772
+ else :
773
+ filename = 'file.txt'
774
+ inline = OpenAIChatModel ._inline_file_block (filename , media_type , text )
775
+ return [ChatCompletionContentPartTextParam (text = inline , type = 'text' )]
811
776
base64_encoded = base64 .b64encode (item .data ).decode ('utf-8' )
812
777
if item .is_image :
813
778
image_url = ImageURL (url = f'data:{ item .media_type } ;base64,{ base64_encoded } ' )
@@ -843,6 +808,11 @@ async def _handle_audio_url(item: object) -> list[ChatCompletionContentPartParam
843
808
async def _handle_document_url (item : object ) -> list [ChatCompletionContentPartParam ] | None :
844
809
if not isinstance (item , DocumentUrl ):
845
810
return None
811
+ if OpenAIChatModel ._is_text_like_media_type (item .media_type ):
812
+ downloaded_text = await download_item (item , data_format = 'text' , type_format = 'extension' )
813
+ filename = f'file.{ downloaded_text ["data_type" ] or "txt" } '
814
+ inline = OpenAIChatModel ._inline_file_block (filename , item .media_type , downloaded_text ['data' ])
815
+ return [ChatCompletionContentPartTextParam (text = inline , type = 'text' )]
846
816
downloaded_item = await download_item (item , data_format = 'base64_uri' , type_format = 'extension' )
847
817
return [
848
818
File (
0 commit comments