# Copyright (c) Meta Platforms, Inc. and affiliates.# All rights reserved.## This source code is licensed under the terms described in the LICENSE file in# the root directory of this source tree.importbase64importloggingfromioimportBytesIOfromPILimportImagefrompydanticimportBaseModel,field_serializer,field_validatorlogger:logging.Logger=logging.getLogger(__name__)
[docs]classAttachment(BaseModel):base64_data:bytesmime:strname:str|None=Nonedef__init__(self,**kwargs)->None:# Validate mimeif("mime"notinkwargsorkwargs["mime"]isNoneor(isinstance(kwargs["mime"],str)andkwargs["mime"]=="")):raiseValueError("mime cannot be null or empty")# Validate base64_dataif("base64_data"notinkwargsorkwargs["base64_data"]isNoneor(isinstance(kwargs["base64_data"],bytes)andlen(kwargs["base64_data"])==0)):raiseValueError("base64_data cannot be null or empty")super().__init__(**kwargs)def__hash__(self)->int:""" Generate a hash for the Attachment object based on its content. This allows Attachment objects to be used in sets and as dictionary keys. """returnhash((self.base64_data,self.mime,self.name))
[docs]@field_validator("base64_data",mode="before")@classmethoddefencode_base64_data(cls,value):""" Convert base64_data string to bytes for proper handling. Only accepts string input - raises ValueError for other types. """ifisinstance(value,str):returnvalue.encode("utf-8")elifisinstance(value,bytes):returnvalueelse:raiseValueError(f"base64_data must be a string or bytes, got {type(value).__name__}")
[docs]@field_serializer("base64_data")defserialize_base64_data(self,value:bytes)->str:""" Serialize base64_data bytes to string for JSON serialization. This ensures json.dumps() works properly on Attachment objects. """returnvalue.decode("utf-8")
[docs]defattachments_to_pil(attachments:list[Attachment])->list[Image.Image]:images=[]forattachmentinattachments:base64_image=attachment.base64_data.decode("utf-8")image_bytes=base64.b64decode(base64_image)# Create a BytesIO object from the bytesimage_buffer=BytesIO(image_bytes)# Open the image using PILimages.append(Image.open(image_buffer))returnimages