From b725a9c125dd1430a721198ec231cb97cc72240c Mon Sep 17 00:00:00 2001 From: sj Date: Mon, 10 Aug 2026 11:40:20 +0800 Subject: [PATCH] feat:sync-report-workflows-and-migration-runtime --- apps/cli/run_rnaseq_report.py | 66 ++++ apps/web/pipeline_backend.py | 66 +++- apps/web/vue3/index_ml.html | 144 +++++++- apps/web/vue3/main.py | 85 ++++- apps/web/vue3/static/i18n_en.js | 1 - docs/plantai_stage_revision_template.md | 2 +- gfa_core/gene_family_report_data.py | 11 +- gfa_core/mas/agents/diagnosis.py | 6 + gfa_core/mas/agents/intent_v2.py | 17 + gfa_core/mas/agents/planning_v2.py | 1 + gfa_core/mas/core/fast_intent.py | 17 + gfa_core/mas/stage_capabilities.py | 20 ++ gfa_core/mas/stage_catalog.py | 8 + gfa_core/mas/tools/stage_status.py | 1 + gfa_core/mas/tools/stage_tools.py | 13 + gfa_core/rnaseq_evidence.py | 313 ++++++++++++++++++ gfa_core/rnaseq_report.py | 19 +- .../stages/rnaseq_report_contract_v1.md | 45 +++ gfa_core/templates/gene_family_report_v1.md | 4 - manage_plantai.sh | 77 +++-- restart_with_prompts.sh | 79 ----- start-webui.sh | 22 -- start_celery.sh | 14 - start_celery_7880.sh | 93 ------ start_celery_9000.sh | 8 +- start_celery_port9000.sh | 73 ---- start_plantai_9000.sh | 21 +- 27 files changed, 882 insertions(+), 344 deletions(-) create mode 100755 apps/cli/run_rnaseq_report.py create mode 100644 gfa_core/rnaseq_evidence.py create mode 100644 gfa_core/schemas/stages/rnaseq_report_contract_v1.md delete mode 100755 restart_with_prompts.sh delete mode 100755 start-webui.sh delete mode 100755 start_celery.sh delete mode 100755 start_celery_7880.sh delete mode 100755 start_celery_port9000.sh diff --git a/apps/cli/run_rnaseq_report.py b/apps/cli/run_rnaseq_report.py new file mode 100755 index 000000000..3a3a9b48c --- /dev/null +++ b/apps/cli/run_rnaseq_report.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +"""Generate or open a formal RNA-seq evidence report.""" +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + + +PROJECT_ROOT = Path(__file__).absolute().parents[2] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from gfa_core.rnaseq_report import NoSuccessfulRNASeqEvidence, generate_rnaseq_report + + +def _csv(value: str) -> list[str] | None: + values = [item.strip() for item in str(value or "").split(",") if item.strip()] + return values or None + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Generate a deterministic formal RNA-seq result report from successful manifests." + ) + parser.add_argument("--workspace", required=True, help="RNA-seq job workspace containing final/") + parser.add_argument("--prefix", required=True, help="RNA-seq output prefix") + parser.add_argument("--job-id", default="") + parser.add_argument("--rnaseq-scope-id", default="") + parser.add_argument( + "--expected-stages", + default="", + help="Comma-separated stages selected for the current RNA-seq batch", + ) + parser.add_argument("--action", choices=("generate", "open"), default="generate") + parser.add_argument("--no-model", action="store_true", help="Disable optional LLM language enhancement") + parser.add_argument("--log-file", default="", help="Accepted for pipeline runner compatibility") + args = parser.parse_args() + + try: + result = generate_rnaseq_report( + Path(args.workspace), + args.prefix, + rnaseq_scope_id=args.rnaseq_scope_id, + expected_stages=_csv(args.expected_stages), + use_model=not args.no_model, + action=args.action, + job_id=args.job_id, + ) + except NoSuccessfulRNASeqEvidence as exc: + print(str(exc), file=sys.stderr) + return 2 + except Exception as exc: + print(f"RNA-seq 结果报告生成失败:{type(exc).__name__}: {exc}", file=sys.stderr) + return 1 + + reuse = "(复用最新报告)" if result.reused else "" + print( + f"RNA-seq 结果报告已就绪{reuse}: {result.report_path} " + f"[coverage={result.coverage_status}, stages={','.join(result.included_stages)}]" + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/apps/web/pipeline_backend.py b/apps/web/pipeline_backend.py index 105056efd..d783a6653 100644 --- a/apps/web/pipeline_backend.py +++ b/apps/web/pipeline_backend.py @@ -3355,6 +3355,7 @@ RNA_SEQ_STAGES = { "rnaseq_wgcna", "rnaseq_candidate_family", "rnaseq_family_score", + "rnaseq_report", } @@ -8383,7 +8384,32 @@ def run_stage( expected_stages = [str(value).strip() for value in expected_stages if str(value).strip()] report_action = str(kwargs.get("report_action") or kwargs.get("reportAction") or "generate").strip() if report_action == "generate" and not expected_stages: - return "❌ 基因家族结果报告生成需要非空 expected_stages", manifest + report_stage_order = ( + "identify", "phylogeny", "reference_classification", "motif", + "domain", "structure", "combo", "promoter", "tfbs", + "synteny", "karyotype", "interspecies_synteny", "pep", + ) + recovered_stages = set() + for candidate in final_dir.glob("*.manifest.json"): + try: + payload = json.loads(candidate.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + continue + candidate_stage = str(payload.get("stage") or "").strip() + candidate_prefix = str( + payload.get("prefix") + or (payload.get("parameters") or {}).get("prefix") + or "" + ).strip() + if ( + candidate_stage in report_stage_order + and str(payload.get("status") or "") == "success" + and candidate_prefix == prefix + ): + recovered_stages.add(candidate_stage) + expected_stages = [stage_id for stage_id in report_stage_order if stage_id in recovered_stages] + if report_action == "generate" and not expected_stages: + return "❌ 基因家族结果报告生成需要非空 expected_stages,且当前前缀没有可恢复的成功 manifest", manifest stage_statuses = kwargs.get("stage_statuses") or kwargs.get("stageStatuses") or {} if isinstance(stage_statuses, str): try: @@ -8392,6 +8418,8 @@ def run_stage( return "❌ 基因家族结果报告的 stage_statuses 不是合法 JSON", manifest if not isinstance(stage_statuses, dict): return "❌ 基因家族结果报告的 stage_statuses 必须是对象", manifest + if not stage_statuses: + stage_statuses = {stage_id: "success" for stage_id in expected_stages} report_use_model_raw = kwargs.get("report_use_model", kwargs.get("reportUseModel", True)) rnaseq_scope_id = str(kwargs.get("rnaseq_scope_id") or kwargs.get("rnaseqScopeId") or "").strip() cmd = [ @@ -11407,6 +11435,42 @@ def run_stage( return "✅ RNA-seq WGCNA completed.", wgcna_manifest return f"❌ RNA-seq WGCNA failed (exit code: {code}). Log: {log_file}", wgcna_manifest if wgcna_manifest.exists() else manifest + if stage == "rnaseq_report": + report_action = str(kwargs.get("rnaseq_report_action") or kwargs.get("report_action") or "generate").strip().lower() + if report_action not in {"generate", "open"}: + report_action = "generate" + expected_raw = kwargs.get("rnaseq_expected_stages") or kwargs.get("expected_stages") or "" + if isinstance(expected_raw, (list, tuple, set)): + expected_arg = ",".join(str(item).strip() for item in expected_raw if str(item).strip()) + else: + expected_arg = str(expected_raw or "").strip() + report_scope_id = str(kwargs.get("rnaseq_scope_id") or kwargs.get("rnaseqScopeId") or "").strip() + report_use_model = kwargs.get("rnaseq_report_use_model", kwargs.get("report_use_model", True)) + report_cmd = [ + sys.executable, + "apps/cli/run_rnaseq_report.py", + "--workspace", str(ws), + "--prefix", prefix, + "--job-id", job.job_id, + "--action", report_action, + "--log-file", str(log_file), + ] + if report_scope_id: + report_cmd.extend(["--rnaseq-scope-id", report_scope_id]) + if expected_arg: + report_cmd.extend(["--expected-stages", expected_arg]) + if not _truthy(report_use_model): + report_cmd.append("--no-model") + print(f"[CMD] {' '.join(report_cmd)}") + code = run_cmd_logged(report_cmd, log_file, cwd=Path(".")) + report_manifest = final_dir / f"{prefix}.rnaseq_report.manifest.json" + if code == 0 and report_manifest.exists(): + action_label = "打开" if report_action == "open" else "生成" + return f"✅ RNA-seq 结果报告已{action_label}。日志:{log_file}", report_manifest + if code == 2: + return "⚠️ 暂无可生成的 RNA-seq 结果:当前 prefix/scope 下没有成功阶段。", None + return f"❌ RNA-seq 结果报告生成失败(exit code: {code})。日志:{log_file}", report_manifest if report_manifest.exists() else None + if stage == "rnaseq_candidate_family": _SPECIES_RESOURCE_ALIASES = { "os": ["Os", "Oryza", "Oryza_sativa", "rice"], diff --git a/apps/web/vue3/index_ml.html b/apps/web/vue3/index_ml.html index 0f5967946..0eb05c901 100644 --- a/apps/web/vue3/index_ml.html +++ b/apps/web/vue3/index_ml.html @@ -1243,9 +1243,12 @@
-
-

CropAI

- Evidence-grounded multi-agent artificial intelligence for crop gene-family discovery and transcriptomic decision support +
+ OMaaX +
+

CropAI

+ Multi-Agent Platform for crop Bioinformatics Analysis +
@@ -1282,6 +1285,7 @@ + @@ -1297,6 +1301,7 @@ +