refactor: enhance tool message handling and improve error logging

- Updated _send_tool_message to accept a title parameter for better message context.
- Modified various tool implementations to utilize the new title parameter for clearer messaging.
- Improved error logging across multiple tools to include exception details for better debugging.
This commit is contained in:
jxxghp
2025-10-31 09:16:53 +08:00
parent c6baf43986
commit 055117d83d
9 changed files with 115 additions and 50 deletions

View File

@@ -26,12 +26,13 @@ class MoviePilotTool(BaseTool):
async def _arun(self, **kwargs) -> str: async def _arun(self, **kwargs) -> str:
raise NotImplementedError raise NotImplementedError
def _send_tool_message(self, message: str, **kwargs): def _send_tool_message(self, message: str, title: str = None, **kwargs):
"""发送工具执行消息""" """发送工具执行消息"""
try: try:
self._message_helper.put( self._message_helper.put(
message=message, message=message,
role="system" role="system",
title=title or "工具执行"
) )
except Exception as e: except Exception as e:
logger.error(f"发送工具消息失败: {e}") logger.error(f"发送工具消息失败: {e}")

View File

@@ -20,12 +20,12 @@ class AddDownloadTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: torrent_title={torrent_title}, torrent_url={torrent_url}, downloader={downloader}, save_path={save_path}, labels={labels}") logger.info(f"执行工具: {self.name}, 参数: torrent_title={torrent_title}, torrent_url={torrent_url}, downloader={downloader}, save_path={save_path}, labels={labels}")
# 发送工具执行说明 # 发送工具执行说明
self._send_tool_message(f"正在添加下载任务: {torrent_title}", "info") self._send_tool_message(f"正在添加下载任务: {torrent_title}", title="添加下载")
try: try:
if not torrent_title or not torrent_url: if not torrent_title or not torrent_url:
error_message = "错误:必须提供种子标题和下载链接" error_message = "错误:必须提供种子标题和下载链接"
self._send_tool_message(error_message, "error") self._send_tool_message(error_message, title="下载失败")
return error_message return error_message
# 使用DownloadChain添加下载 # 使用DownloadChain添加下载
@@ -42,17 +42,22 @@ class AddDownloadTool(MoviePilotTool):
meta_info=meta_info meta_info=meta_info
) )
did = download_chain.download_single(context=context, downloader=downloader, did = download_chain.download_single(
save_path=save_path, label=labels) context=context,
downloader=downloader,
save_path=save_path,
label=labels
)
if did: if did:
success_message = f"成功添加下载任务:{torrent_title}" success_message = f"成功添加下载任务:{torrent_title}"
self._send_tool_message(success_message, "success") self._send_tool_message(success_message, title="下载成功")
return success_message return success_message
else: else:
error_message = "添加下载任务失败" error_message = "添加下载任务失败"
self._send_tool_message(error_message, "error") self._send_tool_message(error_message, title="下载失败")
return error_message return error_message
except Exception as e: except Exception as e:
error_message = f"添加下载任务时发生错误: {str(e)}" error_message = f"添加下载任务时发生错误: {str(e)}"
self._send_tool_message(error_message, "error") logger.error(f"添加下载任务失败: {e}", exc_info=True)
self._send_tool_message(error_message, title="下载失败")
return error_message return error_message

View File

@@ -17,21 +17,36 @@ class AddSubscribeTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}, tmdb_id={tmdb_id}") logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}, tmdb_id={tmdb_id}")
# 发送工具执行说明 # 发送工具执行说明
self._send_tool_message(f"正在添加订阅: {title} ({year}) - {media_type}", "info") self._send_tool_message(f"正在添加订阅: {title} ({year}) - {media_type}", title="添加订阅")
try: try:
subscribe_chain = SubscribeChain() subscribe_chain = SubscribeChain()
sid, message = subscribe_chain.add(mtype=MediaType(media_type), title=title, year=year, # 转换 tmdb_id 为整数
tmdbid=tmdb_id, season=season, username=self._user_id) tmdbid_int = None
if tmdb_id:
try:
tmdbid_int = int(tmdb_id)
except (ValueError, TypeError):
logger.warning(f"无效的 tmdb_id: {tmdb_id},将忽略")
sid, message = subscribe_chain.add(
mtype=MediaType(media_type),
title=title,
year=year,
tmdbid=tmdbid_int,
season=season,
username=self._user_id
)
if sid: if sid:
success_message = f"成功添加订阅:{title} ({year})" success_message = f"成功添加订阅:{title} ({year})"
self._send_tool_message(success_message, "success") self._send_tool_message(success_message, title="订阅成功")
return success_message return success_message
else: else:
error_message = f"添加订阅失败:{message}" error_message = f"添加订阅失败:{message}"
self._send_tool_message(error_message, "error") self._send_tool_message(error_message, title="订阅失败")
return error_message return error_message
except Exception as e: except Exception as e:
error_message = f"添加订阅时发生错误: {str(e)}" error_message = f"添加订阅时发生错误: {str(e)}"
self._send_tool_message(error_message, "error") logger.error(f"添加订阅失败: {e}", exc_info=True)
self._send_tool_message(error_message, title="订阅失败")
return error_message return error_message

View File

@@ -32,8 +32,9 @@ class GetRecommendationsTool(MoviePilotTool):
results = recommend_chain.bangumi_calendar(limit=limit) results = recommend_chain.bangumi_calendar(limit=limit)
if results: if results:
return json.dumps([r.dict() for r in results], ensure_ascii=False, indent=2) # 使用 to_dict() 方法
return json.dumps([r.to_dict() if hasattr(r, 'to_dict') else r.dict() if hasattr(r, 'dict') else r.model_dump() if hasattr(r, 'model_dump') else r for r in results], ensure_ascii=False, indent=2)
return "未找到推荐内容。" return "未找到推荐内容。"
except Exception as e: except Exception as e:
logger.error(f"获取推荐失败: {e}") logger.error(f"获取推荐失败: {e}", exc_info=True)
return f"获取推荐时发生错误: {str(e)}" return f"获取推荐时发生错误: {str(e)}"

View File

@@ -3,7 +3,7 @@
import json import json
from typing import Optional from typing import Optional
from app.db.download_oper import DownloadOper from app.chain.download import DownloadChain
from app.log import logger from app.log import logger
from app.agent.tools.base import MoviePilotTool from app.agent.tools.base import MoviePilotTool
@@ -16,8 +16,9 @@ class QueryDownloadsTool(MoviePilotTool):
status: Optional[str] = "all") -> str: status: Optional[str] = "all") -> str:
logger.info(f"执行工具: {self.name}, 参数: downloader={downloader}, status={status}") logger.info(f"执行工具: {self.name}, 参数: downloader={downloader}, status={status}")
try: try:
download_oper = DownloadOper() download_chain = DownloadChain()
downloads = download_oper.list() # 使用 DownloadChain.downloading 方法获取正在下载的任务
downloads = download_chain.downloading(name=downloader)
filtered_downloads = [] filtered_downloads = []
for dl in downloads: for dl in downloads:
if downloader and dl.downloader != downloader: if downloader and dl.downloader != downloader:
@@ -26,8 +27,8 @@ class QueryDownloadsTool(MoviePilotTool):
continue continue
filtered_downloads.append(dl) filtered_downloads.append(dl)
if filtered_downloads: if filtered_downloads:
return json.dumps([d.dict() for d in filtered_downloads], ensure_ascii=False, indent=2) return json.dumps([d.dict() if hasattr(d, 'dict') else d.model_dump() if hasattr(d, 'model_dump') else d for d in filtered_downloads], ensure_ascii=False, indent=2)
return "未找到相关下载任务。" return "未找到相关下载任务。"
except Exception as e: except Exception as e:
logger.error(f"查询下载失败: {e}") logger.error(f"查询下载失败: {e}", exc_info=True)
return f"查询下载时发生错误: {str(e)}" return f"查询下载时发生错误: {str(e)}"

View File

@@ -26,8 +26,8 @@ class QueryMediaLibraryTool(MoviePilotTool):
continue continue
filtered_medias.append(media) filtered_medias.append(media)
if filtered_medias: if filtered_medias:
return json.dumps([m.dict() for m in filtered_medias], ensure_ascii=False, indent=2) return json.dumps([m.to_dict() if hasattr(m, 'to_dict') else m.dict() if hasattr(m, 'dict') else m.model_dump() if hasattr(m, 'model_dump') else m for m in filtered_medias], ensure_ascii=False, indent=2)
return "媒体库中未找到相关媒体。" return "媒体库中未找到相关媒体。"
except Exception as e: except Exception as e:
logger.error(f"查询媒体库失败: {e}") logger.error(f"查询媒体库失败: {e}", exc_info=True)
return f"查询媒体库时发生错误: {str(e)}" return f"查询媒体库时发生错误: {str(e)}"

View File

@@ -20,14 +20,14 @@ class QuerySubscribesTool(MoviePilotTool):
subscribes = subscribe_oper.list() subscribes = subscribe_oper.list()
filtered_subscribes = [] filtered_subscribes = []
for sub in subscribes: for sub in subscribes:
if status != "all" and sub.status != status: if status != "all" and sub.state != status:
continue continue
if media_type != "all" and sub.type != media_type: if media_type != "all" and sub.type != media_type:
continue continue
filtered_subscribes.append(sub) filtered_subscribes.append(sub)
if filtered_subscribes: if filtered_subscribes:
return json.dumps([s.dict() for s in filtered_subscribes], ensure_ascii=False, indent=2) return json.dumps([s.to_dict() for s in filtered_subscribes], ensure_ascii=False, indent=2)
return "未找到相关订阅。" return "未找到相关订阅。"
except Exception as e: except Exception as e:
logger.error(f"查询订阅失败: {e}") logger.error(f"查询订阅失败: {e}", exc_info=True)
return f"查询订阅时发生错误: {str(e)}" return f"查询订阅时发生错误: {str(e)}"

View File

@@ -4,7 +4,9 @@ import json
from typing import Optional from typing import Optional
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.core.metainfo import MetaInfo
from app.log import logger from app.log import logger
from app.schemas.types import MediaType
from app.agent.tools.base import MoviePilotTool from app.agent.tools.base import MoviePilotTool
@@ -17,26 +19,58 @@ class SearchMediaTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}") logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}")
# 发送工具执行说明 # 发送工具执行说明
self._send_tool_message(f"正在搜索媒体资源: {title}" + (f" ({year})" if year else ""), "info") self._send_tool_message(f"正在搜索媒体资源: {title}" + (f" ({year})" if year else ""), title="搜索中")
try: try:
media_chain = MediaChain() media_chain = MediaChain()
results = media_chain.search_media(title=title, year=year, mtype=media_type, season=season) # 构建搜索标题
search_title = title
if year:
search_title = f"{title} {year}"
if media_type:
search_title = f"{search_title} {media_type}"
if season:
search_title = f"{search_title} S{season:02d}"
# 使用 MediaChain.search 方法
meta, results = media_chain.search(title=search_title)
# 过滤结果
if results: if results:
result_message = f"找到 {len(results)} 个相关媒体资源" filtered_results = []
self._send_tool_message(result_message, "success") for result in results:
if year and result.year != year:
continue
if media_type:
try:
if result.type != MediaType(media_type):
continue
except:
pass
if season and result.season != season:
continue
filtered_results.append(result)
# 发送详细结果 if filtered_results:
for i, result in enumerate(results[:5]): # 只显示前5个结果 result_message = f"找到 {len(filtered_results)} 个相关媒体资源"
media_info = f"{i+1}. {result.title} ({result.year}) - {result.type}" self._send_tool_message(result_message, title="搜索成功")
self._send_tool_message(media_info, "info")
# 发送详细结果
return json.dumps([r.dict() for r in results], ensure_ascii=False, indent=2) for i, result in enumerate(filtered_results[:5]): # 只显示前5个结果
media_info = f"{i+1}. {result.title} ({result.year}) - {result.type.value if result.type else '未知'}"
self._send_tool_message(media_info, title="搜索结果")
return json.dumps([r.to_dict() for r in filtered_results], ensure_ascii=False, indent=2)
else:
error_message = f"未找到符合条件的媒体资源: {title}"
self._send_tool_message(error_message, title="搜索完成")
return error_message
else: else:
error_message = f"未找到相关媒体资源: {title}" error_message = f"未找到相关媒体资源: {title}"
self._send_tool_message(error_message, "warning") self._send_tool_message(error_message, title="搜索完成")
return error_message return error_message
except Exception as e: except Exception as e:
error_message = f"搜索媒体失败: {str(e)}" error_message = f"搜索媒体失败: {str(e)}"
self._send_tool_message(error_message, "error") logger.error(f"搜索媒体失败: {e}", exc_info=True)
self._send_tool_message(error_message, title="搜索失败")
return error_message return error_message

View File

@@ -19,36 +19,44 @@ class SearchTorrentsTool(MoviePilotTool):
logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}, sites={sites}") logger.info(f"执行工具: {self.name}, 参数: title={title}, year={year}, media_type={media_type}, season={season}, sites={sites}")
# 发送工具执行说明 # 发送工具执行说明
self._send_tool_message(f"正在搜索种子资源: {title}" + (f" ({year})" if year else ""), "info") self._send_tool_message(f"正在搜索种子资源: {title}" + (f" ({year})" if year else ""), title="搜索种子")
try: try:
search_chain = SearchChain() search_chain = SearchChain()
torrents = search_chain.search_by_title(title=title, sites=sites) torrents = search_chain.search_by_title(title=title, sites=sites)
filtered_torrents = [] filtered_torrents = []
for torrent in torrents: for torrent in torrents:
if year and torrent.meta_info.year != year: # torrent 是 Context 对象,需要通过 meta_info 和 media_info 访问属性
if year and torrent.meta_info and torrent.meta_info.year != year:
continue continue
if media_type and torrent.media_info and torrent.media_info.type != MediaType(media_type): if media_type and torrent.media_info:
continue try:
if season and torrent.meta_info.begin_season != season: if torrent.media_info.type != MediaType(media_type):
continue
except:
pass
if season and torrent.meta_info and torrent.meta_info.begin_season != season:
continue continue
filtered_torrents.append(torrent) filtered_torrents.append(torrent)
if filtered_torrents: if filtered_torrents:
result_message = f"找到 {len(filtered_torrents)} 个相关种子资源" result_message = f"找到 {len(filtered_torrents)} 个相关种子资源"
self._send_tool_message(result_message, "success") self._send_tool_message(result_message, title="搜索成功")
# 发送详细结果 # 发送详细结果
for i, torrent in enumerate(filtered_torrents[:5]): # 只显示前5个结果 for i, torrent in enumerate(filtered_torrents[:5]): # 只显示前5个结果
torrent_info = f"{i+1}. {torrent.title} - {torrent.site_name}" torrent_title = torrent.torrent_info.title if torrent.torrent_info else torrent.meta_info.title if torrent.meta_info else "未知"
self._send_tool_message(torrent_info, "info") site_name = torrent.torrent_info.site_name if torrent.torrent_info else "未知站点"
torrent_info = f"{i+1}. {torrent_title} - {site_name}"
self._send_tool_message(torrent_info, title="搜索结果")
return json.dumps([t.dict() for t in filtered_torrents], ensure_ascii=False, indent=2) return json.dumps([t.to_dict() for t in filtered_torrents], ensure_ascii=False, indent=2)
else: else:
error_message = f"未找到相关种子资源: {title}" error_message = f"未找到相关种子资源: {title}"
self._send_tool_message(error_message, "warning") self._send_tool_message(error_message, title="搜索完成")
return error_message return error_message
except Exception as e: except Exception as e:
error_message = f"搜索种子时发生错误: {str(e)}" error_message = f"搜索种子时发生错误: {str(e)}"
self._send_tool_message(error_message, "error") logger.error(f"搜索种子失败: {e}", exc_info=True)
self._send_tool_message(error_message, title="搜索失败")
return error_message return error_message