refactor: complete durable transfer execution settlement

This commit is contained in:
jxxghp
2026-08-27 20:27:42 +08:00
parent 8e7a553c1e
commit e82ce8447c
69 changed files with 11948 additions and 275 deletions
+12 -9
View File
@@ -39,10 +39,11 @@ task_types:
- "Use `query_transfer_history` with status='failed' to find the record with id={history_id} and understand the failure details such as source path, error message, and media info."
- "Analyze the error message to determine the best retry strategy."
- "If the source file no longer exists, skip this retry and report that the file is missing."
- "Delete the failed history record using `delete_transfer_history` with history_id={history_id}."
- "Re-identify the media using `recognize_media` with the source file path. For audio files, set media_type='music' and preserve artist/title/album context."
- "If recognition fails, try `search_media` with keywords from the filename. For music, distinguish recording, album, and browse-only artist results."
- "Re-transfer using `transfer_file` with the source path and exact identity fields. Reuse media_source + media_id for every media type, plus media_type + music_type for music."
- "Call `delete_transfer_history` with history_id={history_id}. Durable records are not deleted: the tool submits them to the persistent retry scheduler."
- "If `delete_transfer_history` reports that a durable retry was accepted or rejected, stop and report the exact scheduler result. Do not call `transfer_file`; do not delete the target, history, or retry evidence."
- "Only for a legacy history that was actually deleted, re-identify the media using `recognize_media` with the source file path. For audio files, set media_type='music' and preserve artist/title/album context."
- "If legacy recognition fails, try `search_media` with keywords from the filename. For music, distinguish recording, album, and browse-only artist results."
- "Re-transfer only the deleted legacy history using `transfer_file` with the source path and exact identity fields. Reuse media_source + media_id for every media type, plus media_type + music_type for music."
- "Report the final result."
batch_transfer_failed_retry:
header: "[System Task - Batch Transfer Failed Retry]"
@@ -56,7 +57,7 @@ task_types:
- "Use `query_transfer_history` with status='failed' to find all records with these IDs and understand the failure details."
- "Group records by exact media identity and source directory before retrying. Do not assume all selected files belong to one media."
- "If the error is about media recognition, identify each group once using `recognize_media` or `search_media`, then reuse that result inside the group. Album tracks should normally be retried from the shared album directory with the album identity."
- "For each failed record, delete the old history entry with `delete_transfer_history` and re-transfer using `transfer_file`."
- "For each failed record, call `delete_transfer_history`. Durable records are submitted to the persistent retry scheduler, and that result is final for this task: report its exact accepted or rejected state and do not call `transfer_file` or delete its evidence. Only an actually deleted legacy history may be re-transferred with `transfer_file`."
- "Report how many retries succeeded and how many still failed."
task_rules:
- "Within one verified group, do NOT call `recognize_media` or `search_media` repeatedly for each file. A music recording is one track; a music album is one multi-track directory; an artist is never a transfer target."
@@ -90,8 +91,9 @@ task_types:
- "If the current recognition is wrong or the record should be reorganized, determine the correct media identity first."
- "Prefer `recognize_media` with the source path. If recognition is not reliable, use `search_media` with keywords from filename, title, or year."
- "Only continue when you have high confidence in the target media."
- "Before re-organizing, delete the old transfer history record with `delete_transfer_history` so the system will not skip the source file."
- "Then use `transfer_file` to organize the source path directly."
- "Before re-organizing, call `delete_transfer_history`. Durable records are not deleted: the tool submits them to the persistent retry scheduler."
- "If `delete_transfer_history` reports that a durable retry was accepted or rejected, stop for that record and report the exact scheduler result. Do not call `transfer_file`; do not delete the target, history, or retry evidence. Only a legacy history deletion may be followed by `transfer_file`."
- "For a legacy history that was actually deleted, use `transfer_file` to organize the source path directly."
- "When calling `transfer_file`, reuse known context when appropriate: source storage, target path, target storage, transfer mode, season, media_source, media_id, media_type, and music_type. For an album, retry the album directory once with the album identity when the records share that directory."
- "If this record is already correct and no re-organize is needed, do not perform destructive actions; simply report that no change is necessary."
task_rules:
@@ -116,8 +118,9 @@ task_types:
- "For each group, decide whether the current recognition is trustworthy."
- "If multiple records clearly belong to the same movie, series, or music album, identify the media once with `recognize_media` or `search_media`, then reuse that result for the related records. A recording remains a single-track target, and an artist is browse-only."
- "If a source file no longer exists or cannot be safely processed, skip that record and note the reason."
- "Before re-organizing a record, delete the old transfer history record with `delete_transfer_history` so the system will not skip the source file."
- "Then use `transfer_file` to organize the source path directly."
- "Before re-organizing each record, call `delete_transfer_history`. Durable records are not deleted: the tool submits each task to the persistent retry scheduler."
- "For each durable result, report the exact accepted or rejected scheduler state and stop processing that record. Do not call `transfer_file`; do not delete its target, history, or retry evidence."
- "Only after a legacy history was actually deleted, use `transfer_file` to organize that source path directly."
- "When calling `transfer_file`, reuse known context when appropriate: source storage, target path, target storage, transfer mode, season, media_source, media_id, media_type, and music_type. Prefer one directory transfer for a verified complete album instead of treating each track as an unrelated media item."
- "If a record is already correct and no re-organize is needed, do not perform destructive actions; simply mark it as skipped."
- "Report only the aggregate outcome, including how many records succeeded, skipped, and failed."
@@ -7,8 +7,13 @@ from pydantic import BaseModel, Field
from app.agent.tools.base import MoviePilotTool
from app.agent.tools.tags import ToolTag
from app.chain.storage import StorageChain
from app.application.agentdata import get_agent_transfer_history_port
from app.application.chain.data import get_chain_transfer_execution_port
from app.application.transfer_execution import (
TransferExecutionCommand,
TransferRetryRequestResult,
)
from app.chain.storage import StorageChain
from app.runtime.log import logger
from app.schemas.workflow import FileItem
@@ -29,6 +34,22 @@ def _delete_history_destination_file(fileitem: FileItem) -> tuple[bool, bool]:
return True, bool(storage_chain.delete_media_file(fileitem))
def _request_transfer_retry(
*,
history_id: int,
task_id: str,
user_id: str,
) -> TransferRetryRequestResult:
"""在线程池中登记 durable 重试,避免 Agent 事件循环执行同步数据库 I/O。"""
return TransferExecutionCommand(
get_chain_transfer_execution_port()
).request_retry(
task_id=task_id,
reason=f"Agent 请求重试整理历史 #{history_id}",
requested_by=f"agent:{user_id or 'unknown'}",
)
class DeleteTransferHistoryTool(MoviePilotTool):
name: str = "delete_transfer_history"
tags: list[str] = [
@@ -37,9 +58,10 @@ class DeleteTransferHistoryTool(MoviePilotTool):
ToolTag.Admin,
]
description: str = (
"Delete a specific transfer history record by its ID. For non-successful-move records with an old "
"destination file, the tool removes that media-library file before deleting the history record. This is "
"useful before retrying or re-organizing because the system skips files that already have transfer history."
"Request a safe retry for durable transfer history, or delete a legacy transfer history record by its ID. "
"Durable records keep their files and history and are retried only by the persistent scheduler. For legacy "
"non-successful-move records, the tool removes the old destination before deleting the history. If a durable "
"retry is accepted or rejected, stop and report that result; do not call transfer_file for the same record."
)
args_schema: Type[BaseModel] = DeleteTransferHistoryInput
require_admin: bool = True
@@ -58,6 +80,22 @@ class DeleteTransferHistoryTool(MoviePilotTool):
if not history:
return f"错误:整理历史记录不存在,ID={history_id}"
task_id = getattr(history, "transfer_task_id", None)
if task_id:
retry = await self.run_blocking(
"db",
_request_transfer_retry,
history_id=history_id,
task_id=task_id,
user_id=self._user_id,
)
outcome = "已登记" if retry.accepted else "未登记"
return (
f"durable 整理任务{outcome}重试:ID={history_id}"
f"task_id={task_id}state={retry.state.value}{retry.message}"
"已保留目标文件、历史记录和失败计数;不要调用 transfer_file。"
)
title = history.title or "未知"
src = history.src or "未知"
status = "成功" if history.status else "失败"