2626 DocumentUrl ,
2727 FinishReason ,
2828 ImageUrl ,
29- MagicBinaryContent ,
30- MagicDocumentUrl ,
3129 ModelMessage ,
3230 ModelRequest ,
3331 ModelResponse ,
@@ -725,12 +723,7 @@ async def _map_user_prompt_items(items: Sequence[object]) -> list[ChatCompletion
725723 async def _map_single_item (item : object ) -> list [ChatCompletionContentPartParam ]:
726724 if isinstance (item , str ):
727725 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
734727 handled = OpenAIChatModel ._handle_image_url (item )
735728 if handled is not None :
736729 return handled
@@ -748,55 +741,6 @@ async def _map_single_item(item: object) -> list[ChatCompletionContentPartParam]
748741 # Fallback: unknown type — return empty parts to avoid type-checker Never error
749742 return []
750743
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-
800744 @staticmethod
801745 def _handle_image_url (item : object ) -> list [ChatCompletionContentPartParam ] | None :
802746 if not isinstance (item , ImageUrl ):
@@ -808,6 +752,27 @@ def _handle_image_url(item: object) -> list[ChatCompletionContentPartParam] | No
808752 async def _handle_binary_content (item : object ) -> list [ChatCompletionContentPartParam ] | None :
809753 if not isinstance (item , BinaryContent ):
810754 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' )]
811776 base64_encoded = base64 .b64encode (item .data ).decode ('utf-8' )
812777 if item .is_image :
813778 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
843808 async def _handle_document_url (item : object ) -> list [ChatCompletionContentPartParam ] | None :
844809 if not isinstance (item , DocumentUrl ):
845810 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' )]
846816 downloaded_item = await download_item (item , data_format = 'base64_uri' , type_format = 'extension' )
847817 return [
848818 File (
0 commit comments