@@ -82,6 +82,13 @@ class ImageCapabilityMixin(BaseModel):
8282 default = None ,
8383 contentMediaType = "image/*" ,
8484 )
85+
86+ class MultipleImageCapabilityMixin (BaseModel ):
87+ """Mixin for models that support image inputs."""
88+ images : Optional [List [File ]] = Field (
89+ description = "the images to use for the model" ,
90+ default = None ,
91+ )
8592
8693class ReasoningCapabilityMixin (BaseModel ):
8794 """Mixin for models that support reasoning."""
@@ -235,6 +242,13 @@ def render_message(msg: ContextMessage, allow_multipart: bool) -> str | List[dic
235242 parts .append ({"type" : "image_url" , "image_url" : {"url" : image_data_uri }})
236243 elif msg .image .uri :
237244 parts .append ({"type" : "image_url" , "image_url" : {"url" : msg .image .uri }})
245+ if msg .images :
246+ for image in msg .images :
247+ if image .path :
248+ image_data_uri = image_to_base64_data_uri (image .path )
249+ parts .append ({"type" : "image_url" , "image_url" : {"url" : image_data_uri }})
250+ elif image .uri :
251+ parts .append ({"type" : "image_url" , "image_url" : {"url" : image .uri }})
238252 if allow_multipart :
239253 return parts
240254 if len (parts ) == 1 and parts [0 ]["type" ] == "text" :
@@ -368,6 +382,13 @@ def build_tools(tools: Optional[List[Dict[str, Any]]]) -> Optional[List[Dict[str
368382 # Also ensure properties within parameters is not None
369383 elif func_def ["parameters" ].get ("properties" ) is None :
370384 func_def ["parameters" ]["properties" ] = {}
385+ else :
386+ # Remove properties with null values (OpenAI API doesn't accept them)
387+ properties = func_def ["parameters" ].get ("properties" , {})
388+ if properties :
389+ func_def ["parameters" ]["properties" ] = {
390+ k : v for k , v in properties .items () if v is not None
391+ }
371392
372393 # Wrap in OpenAI format
373394 result .append ({"type" : "function" , "function" : func_def })
0 commit comments