feat: add async resume jobs and doc navigation

This commit is contained in:
root
2026-04-14 15:55:02 +08:00
parent 6705613aa4
commit b8727f1885
26 changed files with 2791 additions and 665 deletions
@@ -51,6 +51,10 @@ SUMMARY_STAGE = "generate_summaries"
FILTER_STAGE = "apply_filters"
DELIVERY_STAGE = "build_delivery_payload"
REPORT_STAGE = "write_run_report"
SUMMARY_BATCH_ARTIFACT = "summary_batch"
CANDIDATE_BATCH_ARTIFACT = "candidate_batch"
SUMMARY_BATCH_FILENAME = "summary-batch.json"
CANDIDATE_BATCH_FILENAME = "candidate-batch.json"
def _save_json(path: Path, payload: dict[str, Any] | list[Any]) -> None:
@@ -137,6 +141,77 @@ def _build_item_context(*, index: int, item: Any, resolved_output_dir: Path, deb
}
def _summary_batch_output(run_dir: Path) -> Path:
return run_dir / "summary" / SUMMARY_BATCH_FILENAME
def _candidate_batch_output(run_dir: Path) -> Path:
return run_dir / "candidates" / CANDIDATE_BATCH_FILENAME
def _build_summary_batch_payload(*, run_id: str, item_contexts: list[dict[str, Any]]) -> dict[str, Any]:
items: list[dict[str, Any]] = []
for item_context in item_contexts:
summary_payload = item_context.get("summary_payload")
if summary_payload is None:
continue
item = item_context["item"]
items.append(
{
"item_key": item_context["item_key"],
"item_id": item.item_id,
"summary": summary_payload,
}
)
return {
"run_id": run_id,
"summary_count": len(items),
"items": items,
}
def _build_candidate_batch_payload(*, run_id: str, item_contexts: list[dict[str, Any]]) -> dict[str, Any]:
items: list[dict[str, Any]] = []
for item_context in item_contexts:
candidate = item_context.get("candidate")
if candidate is None:
continue
item = item_context["item"]
items.append(
{
"item_key": item_context["item_key"],
"item_id": item.item_id,
"candidate_id": candidate.candidate_id,
"candidate": candidate.model_dump(mode="json"),
}
)
return {
"run_id": run_id,
"candidate_count": len(items),
"items": items,
}
def _persist_summary_batch_artifact(*, run_store: RunStore, run_dir: Path, item_contexts: list[dict[str, Any]]) -> Path:
output_path = _summary_batch_output(run_dir)
_save_json(
output_path,
_build_summary_batch_payload(run_id=run_store.state.run_id, item_contexts=item_contexts),
)
run_store.register_artifact(name=SUMMARY_BATCH_ARTIFACT, path=output_path, kind="json", stage=SUMMARY_STAGE)
return output_path
def _persist_candidate_batch_artifact(*, run_store: RunStore, run_dir: Path, item_contexts: list[dict[str, Any]]) -> Path:
output_path = _candidate_batch_output(run_dir)
_save_json(
output_path,
_build_candidate_batch_payload(run_id=run_store.state.run_id, item_contexts=item_contexts),
)
run_store.register_artifact(name=CANDIDATE_BATCH_ARTIFACT, path=output_path, kind="json", stage=FILTER_STAGE)
return output_path
def _build_run_report(
*,
resolved_run_id: str,
@@ -418,6 +493,11 @@ def run_freshrss_pipeline(
},
)
summary_batch_output = _persist_summary_batch_artifact(
run_store=run_store,
run_dir=resolved_output_dir,
item_contexts=item_contexts,
)
if debug_artifacts and (resolved_output_dir / "summary").exists():
run_store.register_artifact(name="summary_dir", path=resolved_output_dir / "summary", kind="directory", stage=SUMMARY_STAGE)
run_store.finish_stage(
@@ -427,6 +507,7 @@ def run_freshrss_pipeline(
"completed_items": summary_success_count + summary_failed_count,
"success_count": summary_success_count,
"failed_count": summary_failed_count,
"summary_batch_output": str(summary_batch_output),
},
)
@@ -510,6 +591,11 @@ def run_freshrss_pipeline(
},
)
candidate_batch_output = _persist_candidate_batch_artifact(
run_store=run_store,
run_dir=resolved_output_dir,
item_contexts=item_contexts,
)
if debug_artifacts and (resolved_output_dir / "candidates").exists():
run_store.register_artifact(name="candidate_dir", path=resolved_output_dir / "candidates", kind="directory", stage=FILTER_STAGE)
run_store.finish_stage(
@@ -521,6 +607,7 @@ def run_freshrss_pipeline(
"keep_count": keep_count,
"review_count": review_count,
"drop_count": drop_count,
"candidate_batch_output": str(candidate_batch_output),
},
)