message_transformer.py 5.6 KB

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