fix(transfer): expire stale jobs and deduplicate diagnostics

This commit is contained in:
jxxghp
2026-08-07 12:44:04 +08:00
parent 63e492be7c
commit 759b9e47eb
13 changed files with 713 additions and 64 deletions
+4 -1
View File
@@ -1,6 +1,6 @@
---
name: feedback-issue
version: 7
version: 8
description: >-
Use this skill ONLY when the user EXPLICITLY requests filing an
upstream issue for MoviePilot core, frontend, or an installed plugin,
@@ -92,6 +92,9 @@ Log relevance rules:
then applies a recent time window, removes Agent/tool dispatch noise,
and keeps only timestamped log blocks whose first line contains a
normalized keyword.
- Consecutive log records with the same template are compacted to the
first record, a repetition count, and the last record. Verify the
retained boundary records before treating the excerpt as evidence.
- If no specific keyword survives normalization, the script records the
doctor report and log-selection metadata but does not include recent
log lines. This avoids attaching unrelated noise.
@@ -33,6 +33,10 @@ _LOG_TIMESTAMP_FORMAT = "%Y-%m-%d %H:%M:%S"
_LOG_MODULE_RE = re.compile(
r"^【[^】]+】\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2},\d+\s+-\s+([^\s][^\-]*?)\s+-\s+"
)
_LOG_DYNAMIC_VALUE_RE = re.compile(
r"(?<![A-Za-z])(?:[0-9a-f]{8,}|\d+(?:\.\d+)?)(?![A-Za-z])",
re.IGNORECASE,
)
_META_NOISE_MODULES = frozenset({
"collect_feedback_diagnostics.py",
@@ -238,6 +242,43 @@ def is_meta_noise(line: str) -> bool:
return match.group(1).strip() in _META_NOISE_MODULES
def _repetition_fingerprint(line: str) -> str:
"""生成用于识别连续重复日志模板的指纹。"""
if parse_line_timestamp(line) is None:
return line
normalized = _LOG_TIMESTAMP_RE.sub("<time>", line)
normalized = _LOG_DYNAMIC_VALUE_RE.sub("<value>", normalized)
return re.sub(r"\s+", " ", normalized).strip().lower()
def _compact_repeated_lines(lines: list[str]) -> list[str]:
"""压缩连续重复日志模板,同时保留首条、末条和重复次数。"""
compacted: list[str] = []
group: list[str] = []
fingerprint: Optional[str] = None
def flush_group() -> None:
if len(group) < 4:
compacted.extend(group)
return
compacted.append(group[0])
compacted.append(
f"... 同类日志连续重复 {len(group)} 次,已省略 {len(group) - 2} 行 ..."
)
compacted.append(group[-1])
for line in lines:
current_fingerprint = _repetition_fingerprint(line)
if group and current_fingerprint != fingerprint:
flush_group()
group = []
group.append(line)
fingerprint = current_fingerprint
if group:
flush_group()
return compacted
def filter_lines(
text: str,
keywords: list[str],
@@ -285,7 +326,8 @@ def filter_lines(
elif keep_block:
matched.append(line)
if matched:
return matched[-max_lines:], sorted(matched_keywords)
compacted = _compact_repeated_lines(matched)
return compacted[-max_lines:], sorted(matched_keywords)
return [], []
@@ -371,6 +371,14 @@ def build_prefill_url(
return f"{issue_new_url(repo)}?{encoded}"
def _safe_count(value: Any) -> int:
"""把不可信的诊断计数字段转换为非负整数。"""
try:
return max(int(value or 0), 0)
except (TypeError, ValueError):
return 0
def format_doctor_summary(doctor: Optional[dict[str, Any]]) -> str:
"""把 doctor JSON 报告压缩成适合 Issue 和预览展示的摘要。"""
if not isinstance(doctor, dict):
@@ -390,28 +398,75 @@ def format_doctor_summary(doctor: Optional[dict[str, Any]]) -> str:
runtime = environment.get("runtime")
if runtime:
lines.append(f"运行环境:{runtime}")
findings = report.get("findings") or []
summary = report.get("summary") or {}
if isinstance(summary, dict):
advisory_count = summary.get("advisory")
if advisory_count is None and isinstance(findings, list):
advisory_count = sum(
1
for item in findings
if isinstance(item, dict)
and item.get("affects_report_status") is False
and not item.get("fixed")
)
lines.append(
"汇总:"
f"total={summary.get('total', 0)} "
f"error={summary.get('error', 0)} "
f"warn={summary.get('warn', 0)} "
f"advisory={advisory_count or 0} "
f"fixed={summary.get('fixed', 0)}"
)
findings = report.get("findings") or []
if isinstance(findings, list):
important = [
item for item in findings
if isinstance(item, dict) and item.get("severity") in {"error", "warn"}
][:8]
grouped: dict[tuple[str, str, str, bool], dict[str, Any]] = {}
for item in findings:
if not isinstance(item, dict) or item.get("severity") not in {"error", "warn"}:
continue
title = str(item.get("title") or item.get("id") or "未知诊断项")
recommendation = str(item.get("recommendation") or "").strip()
advisory = item.get("affects_report_status") is False
key = (str(item.get("severity")), title, recommendation, advisory)
group = grouped.setdefault(
key,
{
"count": 0,
"matches": 0,
"unique_matches": 0,
"sources": [],
},
)
group["count"] += 1
context = item.get("context") or {}
if not isinstance(context, dict):
continue
group["matches"] += _safe_count(context.get("matches"))
group["unique_matches"] += _safe_count(
context.get("unique_matches") or context.get("matches") or 0
)
source_files = context.get("log_files") or [context.get("log_file")]
for source_file in source_files:
if not source_file:
continue
source_name = Path(str(source_file)).name
if source_name not in group["sources"]:
group["sources"].append(source_name)
important = list(grouped.items())[:8]
if important:
lines.append("关键发现:")
for item in important:
title = str(item.get("title") or item.get("id") or "未知诊断项")
recommendation = str(item.get("recommendation") or "").strip()
line = f"- [{item.get('severity')}] {title}"
for (severity, title, recommendation, advisory), group in important:
marker = f"{severity}/advisory" if advisory else severity
line = f"- [{marker}] {title}"
if group["count"] > 1:
line = f"{line}(合并 {group['count']} 项)"
if group["sources"]:
line = f"{line};来源:{', '.join(group['sources'])}"
if group["matches"]:
line = f"{line};命中:{group['matches']}"
if group["unique_matches"] < group["matches"]:
line = f"{line},去重后 {group['unique_matches']}"
if recommendation:
line = f"{line};建议:{recommendation}"
lines.append(line)