11import base64
22import io
3+ import mimetypes
34import os
5+ import re
46from typing import Any , Dict , List , Union
57from urllib .parse import urlparse
6- import re
7- import mimetypes
88
99import pydantic
1010import requests
1919
2020class Image (pydantic .BaseModel ):
2121 url : str
22-
22+
2323 model_config = {
24- ' frozen' : True ,
25- ' str_strip_whitespace' : True ,
26- ' validate_assignment' : True ,
27- ' extra' : ' forbid' ,
24+ " frozen" : True ,
25+ " str_strip_whitespace" : True ,
26+ " validate_assignment" : True ,
27+ " extra" : " forbid" ,
2828 }
29-
29+
3030 @pydantic .model_validator (mode = "before" )
3131 @classmethod
3232 def validate_input (cls , values ):
@@ -52,7 +52,7 @@ def from_file(cls, file_path: str):
5252 return cls (url = encode_image (file_path ))
5353
5454 @classmethod
55- def from_PIL (cls , pil_image ):
55+ def from_PIL (cls , pil_image ): # noqa: N802
5656 return cls (url = encode_image (pil_image ))
5757
5858 @pydantic .model_serializer ()
@@ -66,9 +66,10 @@ def __repr__(self):
6666 if "base64" in self .url :
6767 len_base64 = len (self .url .split ("base64," )[1 ])
6868 image_type = self .url .split (";" )[0 ].split ("/" )[- 1 ]
69- return f"Image(url=data:image/{ image_type } ;base64,<IMAGE_BASE_64_ENCODED({ str ( len_base64 ) } )>)"
69+ return f"Image(url=data:image/{ image_type } ;base64,<IMAGE_BASE_64_ENCODED({ len_base64 !s } )>)"
7070 return f"Image(url='{ self .url } ')"
7171
72+
7273def is_url (string : str ) -> bool :
7374 """Check if a string is a valid URL."""
7475 try :
@@ -162,7 +163,7 @@ def _encode_image_from_url(image_url: str) -> str:
162163 return f"data:{ mime_type } ;base64,{ encoded_data } "
163164
164165
165- def _encode_pil_image (image : ' PILImage' ) -> str :
166+ def _encode_pil_image (image : " PILImage" ) -> str :
166167 """Encode a PIL Image object to a base64 data URI."""
167168 buffered = io .BytesIO ()
168169 file_format = image .format or "PNG"
@@ -197,6 +198,7 @@ def is_image(obj) -> bool:
197198 return True
198199 return False
199200
201+
200202def try_expand_image_tags (messages : List [Dict [str , Any ]]) -> List [Dict [str , Any ]]:
201203 """Try to expand image tags in the messages."""
202204 for message in messages :
@@ -205,43 +207,44 @@ def try_expand_image_tags(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]
205207 message ["content" ] = expand_image_tags (message ["content" ])
206208 return messages
207209
210+
208211def expand_image_tags (text : str ) -> Union [str , List [Dict [str , Any ]]]:
209- """Expand image tags in the text. If there are any image tags,
212+ """Expand image tags in the text. If there are any image tags,
210213 turn it from a content string into a content list of texts and image urls.
211-
214+
212215 Args:
213216 text: The text content that may contain image tags
214-
217+
215218 Returns:
216219 Either the original string if no image tags, or a list of content dicts
217220 with text and image_url entries
218221 """
219222 image_tag_regex = r'"?<DSPY_IMAGE_START>(.*?)<DSPY_IMAGE_END>"?'
220-
223+
221224 # If no image tags, return original text
222225 if not re .search (image_tag_regex , text ):
223226 return text
224-
227+
225228 final_list = []
226229 remaining_text = text
227-
230+
228231 while remaining_text :
229232 match = re .search (image_tag_regex , remaining_text )
230233 if not match :
231234 if remaining_text .strip ():
232235 final_list .append ({"type" : "text" , "text" : remaining_text .strip ()})
233236 break
234-
237+
235238 # Get text before the image tag
236- prefix = remaining_text [:match .start ()].strip ()
239+ prefix = remaining_text [: match .start ()].strip ()
237240 if prefix :
238241 final_list .append ({"type" : "text" , "text" : prefix })
239-
242+
240243 # Add the image
241244 image_url = match .group (1 )
242245 final_list .append ({"type" : "image_url" , "image_url" : {"url" : image_url }})
243-
246+
244247 # Update remaining text
245- remaining_text = remaining_text [match .end ():].strip ()
246-
248+ remaining_text = remaining_text [match .end () :].strip ()
249+
247250 return final_list
0 commit comments