dify 支持图片http请求,方便随时追加API

This commit is contained in:
liuwei
2025-04-07 15:53:19 +08:00
parent 0474548eaa
commit 3b34be2e7a
2 changed files with 167 additions and 4 deletions

View File

@@ -11,6 +11,7 @@ from plugin_common.message_plugin_interface import MessagePluginInterface
from plugin_common.plugin_interface import PluginStatus
from plugins.stats_collector.decorators import plugin_stats_decorator
from robot_cmd.robot_command import Feature, PermissionStatus, GroupBotManager
from utils.media_downloader import MediaDownloader
class DifyPlugin(MessagePluginInterface):
@@ -152,7 +153,13 @@ class DifyPlugin(MessagePluginInterface):
# 发送回复
if response:
wcf.send_text(response, (roomid if roomid else sender), sender if roomid else "")
# 判断是否为本地文件路径
if os.path.isfile(response):
# 如果是文件路径,使用发送文件方法
wcf.send_file(response, (roomid if roomid else sender))
else:
# 如果是普通文本,使用发送文本方法
wcf.send_text(response, (roomid if roomid else sender), sender if roomid else "")
return True, "发送成功"
else:
wcf.send_text("❌未能获取到回复,请稍后再试", (roomid if roomid else sender), sender if roomid else "")
@@ -256,10 +263,17 @@ class DifyPlugin(MessagePluginInterface):
# 获取输出内容
outputs = response_data.get("data", {}).get("outputs", {})
if outputs:
# 尝试从text字段获取回答
if "text" in outputs and isinstance(outputs["text"], str):
# 处理媒体类型返回
if "result" in outputs and "type" in outputs:
if outputs["type"] == "image":
downloader = MediaDownloader()
image_url = outputs["result"]
image_path = downloader.download_media(image_url)
answer = image_path
# 处理文本类型返回
elif "text" in outputs and isinstance(outputs["text"], str):
answer = outputs["text"]
# 如果没有text字段尝试从其他字段获取
# 兼容旧版处理逻辑
else:
for key, value in outputs.items():
if isinstance(value, str) and value.strip():

149
utils/media_downloader.py Normal file
View File

@@ -0,0 +1,149 @@
import os
import requests
import uuid
from typing import Optional
from urllib.parse import urlparse
import logging
import time
class MediaDownloader:
"""媒体下载工具类,用于下载图片等媒体文件"""
def __init__(self, download_dir: str = None):
"""
初始化下载器
Args:
download_dir: 下载目录,默认为项目下的 media_downloads 目录
"""
self.logger = logging.getLogger("MediaDownloader")
self.download_dir = download_dir or os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"media_downloads"
)
os.makedirs(self.download_dir, exist_ok=True)
self.logger.info(f"媒体下载目录: {self.download_dir}")
def download_media(self, url: str, file_type: str = None) -> Optional[str]:
"""
下载媒体文件
Args:
url: 媒体文件URL
file_type: 文件类型(如'jpg','png'等)如果不指定则从URL中推断
Returns:
下载文件的本地绝对路径如果下载失败则返回None
"""
try:
# 从URL获取文件名和扩展名
parsed_url = urlparse(url)
filename = os.path.basename(parsed_url.path)
# 如果没有文件名或扩展名,则生成一个随机文件名
if not filename or '.' not in filename:
ext = file_type if file_type else self._guess_file_type(url)
filename = f"{uuid.uuid4().hex}.{ext}" if ext else f"{uuid.uuid4().hex}"
local_path = os.path.join(self.download_dir, filename)
self.logger.info(f"开始下载媒体文件: {url} -> {local_path}")
# 下载文件
response = requests.get(url, stream=True, timeout=30)
response.raise_for_status()
with open(local_path, 'wb') as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
self.logger.info(f"媒体文件下载成功: {local_path}")
# 下载成功后清理旧文件
self.clear_downloads()
return os.path.abspath(local_path)
except Exception as e:
self.logger.error(f"下载媒体文件失败: {url}, 错误: {str(e)}")
return None
def _guess_file_type(self, url: str) -> Optional[str]:
"""
从URL推断文件类型
Args:
url: 媒体文件URL
Returns:
文件扩展名(不含点)如果无法推断则返回None
"""
try:
parsed_url = urlparse(url)
path = parsed_url.path.lower()
if path.endswith('.jpg') or path.endswith('.jpeg'):
return 'jpg'
elif path.endswith('.png'):
return 'png'
elif path.endswith('.gif'):
return 'gif'
elif path.endswith('.mp4'):
return 'mp4'
elif path.endswith('.mp3'):
return 'mp3'
elif path.endswith('.pdf'):
return 'pdf'
else:
# 检查Content-Type
response = requests.head(url, timeout=5)
content_type = response.headers.get('Content-Type', '')
if 'image/jpeg' in content_type:
return 'jpg'
elif 'image/png' in content_type:
return 'png'
elif 'image/gif' in content_type:
return 'gif'
return None
except:
return None
def clear_downloads(self, max_age_days: int = 3) -> None:
"""
清理超过指定天数的下载文件
Args:
max_age_days: 最大保留天数默认为3天
"""
try:
current_time = time.time()
max_age_seconds = max_age_days * 24 * 60 * 60
cleared_count = 0
# 遍历下载目录中的所有文件
for filename in os.listdir(self.download_dir):
file_path = os.path.join(self.download_dir, filename)
# 检查是否为文件
if os.path.isfile(file_path):
# 获取文件最后修改时间
file_mtime = os.path.getmtime(file_path)
file_age = current_time - file_mtime
# 如果文件超过最大保留时间,则删除
if file_age > max_age_seconds:
try:
os.remove(file_path)
cleared_count += 1
self.logger.debug(f"已删除过期文件: {file_path}")
except Exception as e:
self.logger.error(f"删除文件失败 {file_path}: {str(e)}")
if cleared_count > 0:
self.logger.info(f"清理完成,共删除 {cleared_count} 个过期文件")
except Exception as e:
self.logger.error(f"清理下载文件时出错: {str(e)}")