| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 | from collections.abc import Mapping, Sequencefrom typing import Any, Optionalfrom pydantic import BaseModel, Field, model_validatorfrom core.model_runtime.entities.message_entities import ImagePromptMessageContentfrom . import helpersfrom .constants import FILE_MODEL_IDENTITYfrom .enums import FileTransferMethod, FileTypefrom .tool_file_parser import ToolFileParserclass ImageConfig(BaseModel):    """    NOTE: This part of validation is deprecated, but still used in app features "Image Upload".    """    number_limits: int = 0    transfer_methods: Sequence[FileTransferMethod] = Field(default_factory=list)    detail: ImagePromptMessageContent.DETAIL | None = Noneclass FileUploadConfig(BaseModel):    """    File Upload Entity.    """    image_config: Optional[ImageConfig] = None    allowed_file_types: Sequence[FileType] = Field(default_factory=list)    allowed_file_extensions: Sequence[str] = Field(default_factory=list)    allowed_file_upload_methods: Sequence[FileTransferMethod] = Field(default_factory=list)    number_limits: int = 0class File(BaseModel):    dify_model_identity: str = FILE_MODEL_IDENTITY    id: Optional[str] = None  # message file id    tenant_id: str    type: FileType    transfer_method: FileTransferMethod    remote_url: Optional[str] = None  # remote url    related_id: Optional[str] = None    filename: Optional[str] = None    extension: Optional[str] = Field(default=None, description="File extension, should contains dot")    mime_type: Optional[str] = None    size: int = -1    # Those properties are private, should not be exposed to the outside.    _storage_key: str    def __init__(        self,        *,        id: Optional[str] = None,        tenant_id: str,        type: FileType,        transfer_method: FileTransferMethod,        remote_url: Optional[str] = None,        related_id: Optional[str] = None,        filename: Optional[str] = None,        extension: Optional[str] = None,        mime_type: Optional[str] = None,        size: int = -1,        storage_key: Optional[str] = None,        dify_model_identity: Optional[str] = FILE_MODEL_IDENTITY,        url: Optional[str] = None,    ):        super().__init__(            id=id,            tenant_id=tenant_id,            type=type,            transfer_method=transfer_method,            remote_url=remote_url,            related_id=related_id,            filename=filename,            extension=extension,            mime_type=mime_type,            size=size,            dify_model_identity=dify_model_identity,            url=url,        )        self._storage_key = str(storage_key)    def to_dict(self) -> Mapping[str, str | int | None]:        data = self.model_dump(mode="json")        return {            **data,            "url": self.generate_url(),        }    @property    def markdown(self) -> str:        url = self.generate_url()        if self.type == FileType.IMAGE:            text = f""        else:            text = f"[{self.filename or url}]({url})"        return text    def generate_url(self) -> Optional[str]:        if self.transfer_method == FileTransferMethod.REMOTE_URL:            return self.remote_url        elif self.transfer_method == FileTransferMethod.LOCAL_FILE:            if self.related_id is None:                raise ValueError("Missing file related_id")            return helpers.get_signed_file_url(upload_file_id=self.related_id)        elif self.transfer_method == FileTransferMethod.TOOL_FILE:            assert self.related_id is not None            assert self.extension is not None            return ToolFileParser.get_tool_file_manager().sign_file(                tool_file_id=self.related_id, extension=self.extension            )    def to_plugin_parameter(self) -> dict[str, Any]:        return {            "dify_model_identity": FILE_MODEL_IDENTITY,            "mime_type": self.mime_type,            "filename": self.filename,            "extension": self.extension,            "size": self.size,            "type": self.type,            "url": self.generate_url(),        }    @model_validator(mode="after")    def validate_after(self):        match self.transfer_method:            case FileTransferMethod.REMOTE_URL:                if not self.remote_url:                    raise ValueError("Missing file url")                if not isinstance(self.remote_url, str) or not self.remote_url.startswith("http"):                    raise ValueError("Invalid file url")            case FileTransferMethod.LOCAL_FILE:                if not self.related_id:                    raise ValueError("Missing file related_id")            case FileTransferMethod.TOOL_FILE:                if not self.related_id:                    raise ValueError("Missing file related_id")        return self
 |