message_transformer.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import logging
  2. from mimetypes import guess_extension
  3. from typing import Optional
  4. from core.file import File, FileTransferMethod, FileType
  5. from core.tools.entities.tool_entities import ToolInvokeMessage
  6. from core.tools.tool_file_manager import ToolFileManager
  7. logger = logging.getLogger(__name__)
  8. class ToolFileMessageTransformer:
  9. @classmethod
  10. def transform_tool_invoke_messages(
  11. cls, messages: list[ToolInvokeMessage], user_id: str, tenant_id: str, conversation_id: str | None
  12. ) -> list[ToolInvokeMessage]:
  13. """
  14. Transform tool message and handle file download
  15. """
  16. result = []
  17. for message in messages:
  18. if message.type in {ToolInvokeMessage.MessageType.TEXT, ToolInvokeMessage.MessageType.LINK}:
  19. result.append(message)
  20. elif message.type == ToolInvokeMessage.MessageType.IMAGE and isinstance(message.message, str):
  21. # try to download image
  22. try:
  23. file = ToolFileManager.create_file_by_url(
  24. user_id=user_id, tenant_id=tenant_id, conversation_id=conversation_id, file_url=message.message
  25. )
  26. url = f'/files/tools/{file.id}{guess_extension(file.mimetype) or ".png"}'
  27. result.append(
  28. ToolInvokeMessage(
  29. type=ToolInvokeMessage.MessageType.IMAGE_LINK,
  30. message=url,
  31. save_as=message.save_as,
  32. meta=message.meta.copy() if message.meta is not None else {},
  33. )
  34. )
  35. except Exception as e:
  36. logger.exception(e)
  37. result.append(
  38. ToolInvokeMessage(
  39. type=ToolInvokeMessage.MessageType.TEXT,
  40. message=f"Failed to download image: {message.message}, please try to download it manually.",
  41. meta=message.meta.copy() if message.meta is not None else {},
  42. save_as=message.save_as,
  43. )
  44. )
  45. elif message.type == ToolInvokeMessage.MessageType.BLOB:
  46. # get mime type and save blob to storage
  47. assert message.meta is not None
  48. mimetype = message.meta.get("mime_type", "octet/stream")
  49. # if message is str, encode it to bytes
  50. if isinstance(message.message, str):
  51. message.message = message.message.encode("utf-8")
  52. # FIXME: should do a type check here.
  53. assert isinstance(message.message, bytes)
  54. file = ToolFileManager.create_file_by_raw(
  55. user_id=user_id,
  56. tenant_id=tenant_id,
  57. conversation_id=conversation_id,
  58. file_binary=message.message,
  59. mimetype=mimetype,
  60. )
  61. url = cls.get_tool_file_url(tool_file_id=file.id, extension=guess_extension(file.mimetype))
  62. # check if file is image
  63. if "image" in mimetype:
  64. result.append(
  65. ToolInvokeMessage(
  66. type=ToolInvokeMessage.MessageType.IMAGE_LINK,
  67. message=url,
  68. save_as=message.save_as,
  69. meta=message.meta.copy() if message.meta is not None else {},
  70. )
  71. )
  72. else:
  73. result.append(
  74. ToolInvokeMessage(
  75. type=ToolInvokeMessage.MessageType.LINK,
  76. message=url,
  77. save_as=message.save_as,
  78. meta=message.meta.copy() if message.meta is not None else {},
  79. )
  80. )
  81. elif message.type == ToolInvokeMessage.MessageType.FILE:
  82. assert message.meta is not None
  83. file = message.meta.get("file")
  84. if isinstance(file, File):
  85. if file.transfer_method == FileTransferMethod.TOOL_FILE:
  86. assert file.related_id is not None
  87. url = cls.get_tool_file_url(tool_file_id=file.related_id, extension=file.extension)
  88. if file.type == FileType.IMAGE:
  89. result.append(
  90. ToolInvokeMessage(
  91. type=ToolInvokeMessage.MessageType.IMAGE_LINK,
  92. message=url,
  93. save_as=message.save_as,
  94. meta=message.meta.copy() if message.meta is not None else {},
  95. )
  96. )
  97. else:
  98. result.append(
  99. ToolInvokeMessage(
  100. type=ToolInvokeMessage.MessageType.LINK,
  101. message=url,
  102. save_as=message.save_as,
  103. meta=message.meta.copy() if message.meta is not None else {},
  104. )
  105. )
  106. else:
  107. result.append(message)
  108. else:
  109. result.append(message)
  110. return result
  111. @classmethod
  112. def get_tool_file_url(cls, tool_file_id: str, extension: Optional[str]) -> str:
  113. return f'/files/tools/{tool_file_id}{extension or ".bin"}'