77from collections .abc import Mapping
88from functools import lru_cache , cache
99from pathlib import Path
10- from typing import Any , Callable , Literal , Optional , TypeVar , TypedDict , ClassVar
10+ from typing import Any , Callable , Literal , Optional , TypeVar , TypedDict , ClassVar , Union
1111from urllib .parse import urlparse
1212
1313import requests
@@ -47,14 +47,16 @@ class Image(BaseModel):
4747 "image/gif" ,
4848 "image/webp" ,
4949 ]
50- source : str | Path = Field (
50+ source : Union [ str , Path ] = Field (
5151 description = "URL, file path, or base64 data of the image"
5252 )
5353 media_type : str = Field (description = "MIME type of the image" )
54- data : str | None = Field (None , description = "Base64 encoded image data" , repr = False )
54+ data : Union [str , None ] = Field (
55+ None , description = "Base64 encoded image data" , repr = False
56+ )
5557
5658 @classmethod
57- def autodetect (cls , source : str | Path ) -> Image | None :
59+ def autodetect (cls , source : Union [ str , Path ] ) -> Union [ Image , None ] :
5860 """Attempt to autodetect an image from a source string or Path.
5961
6062 Args:
@@ -83,7 +85,7 @@ def autodetect(cls, source: str | Path) -> Image | None:
8385 return None
8486
8587 @classmethod
86- def autodetect_safely (cls , source : str | Path ) -> Image | str :
88+ def autodetect_safely (cls , source : Union [ str , Path ] ) -> Union [ Image , str ] :
8789 """Safely attempt to autodetect an image from a source string or path.
8890
8991 Args:
@@ -126,7 +128,7 @@ def from_base64(cls, data: str) -> Image:
126128 return cls (source = data , media_type = media_type , data = encoded )
127129
128130 @classmethod # Caching likely unnecessary
129- def from_raw_base64 (cls , data : str ) -> Image | None :
131+ def from_raw_base64 (cls , data : str ) -> Union [ Image , None ] :
130132 """Create an Image from raw base64 data.
131133
132134 Args:
@@ -137,7 +139,7 @@ def from_raw_base64(cls, data: str) -> Image | None:
137139 """
138140 try :
139141 decoded : bytes = base64 .b64decode (data )
140- img_type : str | None = imghdr .what (None , decoded )
142+ img_type : Union [ str , None ] = imghdr .what (None , decoded )
141143 if img_type :
142144 media_type = mimetypes .guess_type (data )[0 ]
143145 if media_type in cls .VALID_MIME_TYPES :
@@ -152,7 +154,7 @@ def from_url(cls, url: str) -> Image:
152154 if cls .is_base64 (url ):
153155 return cls .from_base64 (url )
154156 parsed_url = urlparse (url )
155- media_type : str | None = mimetypes .guess_type (parsed_url .path )[0 ]
157+ media_type : Union [ str , None ] = mimetypes .guess_type (parsed_url .path )[0 ]
156158
157159 if not media_type :
158160 try :
@@ -167,7 +169,7 @@ def from_url(cls, url: str) -> Image:
167169
168170 @classmethod
169171 @lru_cache
170- def from_path (cls , path : str | Path ) -> Image :
172+ def from_path (cls , path : Union [ str , Path ] ) -> Image :
171173 path = Path (path )
172174 if not path .is_file ():
173175 raise FileNotFoundError (f"Image file not found: { path } " )
@@ -180,7 +182,7 @@ def from_path(cls, path: str | Path) -> Image:
180182 f"Image file size ({ path .stat ().st_size / 1024 / 1024 :.1f} MB) "
181183 f"exceeds Mistral's limit of { MAX_MISTRAL_IMAGE_SIZE / 1024 / 1024 :.1f} MB"
182184 )
183- media_type : str | None = mimetypes .guess_type (str (path ))[0 ]
185+ media_type : Union [ str , None ] = mimetypes .guess_type (str (path ))[0 ]
184186 if media_type not in VALID_MISTRAL_MIME_TYPES :
185187 raise ValueError (
186188 f"Unsupported image format: { media_type } . "
@@ -277,8 +279,10 @@ def to_mistral(self) -> dict[str, Any]:
277279class Audio (BaseModel ):
278280 """Represents an audio that can be loaded from a URL or file path."""
279281
280- source : str | Path = Field (description = "URL or file path of the audio" )
281- data : str | None = Field (None , description = "Base64 encoded audio data" , repr = False )
282+ source : Union [str , Path ] = Field (description = "URL or file path of the audio" )
283+ data : Union [str , None ] = Field (
284+ None , description = "Base64 encoded audio data" , repr = False
285+ )
282286
283287
284288class ImageWithCacheControl (Image ):
@@ -290,8 +294,8 @@ class ImageWithCacheControl(Image):
290294
291295 @classmethod
292296 def from_image_params (
293- cls , source : str | Path , image_params : dict [str , Any ]
294- ) -> ImageWithCacheControl | None :
297+ cls , source : Union [ str , Path ] , image_params : dict [str , Any ]
298+ ) -> Union [ ImageWithCacheControl , None ] :
295299 """Create an ImageWithCacheControl from image parameters.
296300
297301 Args:
@@ -322,11 +326,13 @@ def to_anthropic(self) -> dict[str, Any]:
322326
323327
324328def convert_contents (
325- contents : str | Image | dict [str , Any ] | list [str | Image | dict [str , Any ]],
329+ contents : Union [
330+ str , Image , dict [str , Any ], list [Union [str , Image , dict [str , Any ]]]
331+ ],
326332 mode : Mode ,
327333 * , # Make autodetect_images keyword-only since it's unused
328334 _autodetect_images : bool = True , # Prefix with _ to indicate intentionally unused
329- ) -> str | list [dict [str , Any ]]:
335+ ) -> Union [ str , list [dict [str , Any ] ]]:
330336 """Convert contents to the appropriate format for the given mode."""
331337 # Handle single string case
332338 if isinstance (contents , str ):
@@ -403,7 +409,9 @@ def convert_messages(
403409
404410 # Handle list content
405411 if isinstance (content , list ):
406- converted_message ["content" ] = convert_contents (content , mode )
412+ # Explicitly type the content as Union[str, Image, dict[str, Any]]
413+ typed_content : list [Union [str , Image , dict [str , Any ]]] = content
414+ converted_message ["content" ] = convert_contents (typed_content , mode )
407415 converted_messages .append (converted_message )
408416 continue
409417
0 commit comments