message_transformer.py 5.7 KB

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