168 lines
5.7 KiB
Python
Executable File
168 lines
5.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import requests
|
|
|
|
|
|
BASE = "http://127.0.0.1:7868"
|
|
PROJECT = Path("/home/xia-lab/wutx/00test/bbav5")
|
|
PREFIX = "Ft.SPL"
|
|
|
|
|
|
def post(path: str, payload: dict) -> dict:
|
|
response = requests.post(f"{BASE}{path}", json=payload, timeout=45)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
|
|
def get(path: str) -> dict:
|
|
response = requests.get(f"{BASE}{path}", timeout=45)
|
|
response.raise_for_status()
|
|
return response.json()
|
|
|
|
|
|
def write(path: Path, text: str) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
|
|
|
|
def write_stage_manifest(final_dir: Path, stage: str, artifact_name: str) -> None:
|
|
artifact = final_dir / artifact_name
|
|
write(artifact, "ok\n")
|
|
manifest = {
|
|
"manifest_version": "1.0",
|
|
"stage": stage,
|
|
"status": "success",
|
|
"prefix": PREFIX,
|
|
"workspace_root": str(final_dir.parent.resolve()),
|
|
"artifacts": {
|
|
"primary": {
|
|
"path": f"final/{artifact.name}",
|
|
"full_path": str(artifact.resolve()),
|
|
"type": "file",
|
|
}
|
|
},
|
|
}
|
|
write(final_dir / f"{PREFIX}.{stage}.manifest.json", json.dumps(manifest, ensure_ascii=False, indent=2))
|
|
|
|
|
|
def wait_task(task_id: str, timeout_sec: int = 180) -> dict:
|
|
start = time.time()
|
|
while time.time() - start < timeout_sec:
|
|
task = get(f"/api/task/{task_id}")
|
|
if task.get("status") in {"success", "failed"}:
|
|
return task
|
|
time.sleep(1.5)
|
|
raise TimeoutError(task_id)
|
|
|
|
|
|
def prepare_job() -> tuple[str, Path, Path]:
|
|
job = post("/api/new_job", {})
|
|
job_id = job["job_id"]
|
|
input_dir = PROJECT / "input" / job_id
|
|
final_dir = PROJECT / "workspace" / job_id / "final"
|
|
|
|
for stage in ["rnaseq_qc", "rnaseq_align", "rnaseq_quant", "rnaseq_de"]:
|
|
write_stage_manifest(final_dir, stage, f"{stage}.done.txt")
|
|
|
|
write(final_dir / "treat_vs_control.DE_genes.txt", "GeneA\nGeneB\nGeneC\nGeneD\n")
|
|
write(final_dir / f"{PREFIX}.rnaseq_de.treat_vs_control.family_DE_genes.txt", "GeneA\nGeneC\n")
|
|
write(input_dir / "gene2go.txt", "\n".join([
|
|
"GeneA\tGO:0008150",
|
|
"GeneB\tGO:0008150",
|
|
"GeneC\tGO:0003674",
|
|
"GeneD\tGO:0003674",
|
|
"GeneE\tGO:0009987",
|
|
"",
|
|
]))
|
|
write(input_dir / "gene2ko.txt", "\n".join([
|
|
"GeneA\tK00001",
|
|
"GeneB\tK00001",
|
|
"GeneC\tK00002",
|
|
"GeneD\tK00002",
|
|
"GeneE\tK00003",
|
|
"",
|
|
]))
|
|
write(input_dir / "Ft.SPL.pep.fa", ">GeneA\nMKTLL\n>GeneB\nMSTAA\n")
|
|
write(input_dir / "mock_1.fastq", "@r1\nACGT\n+\nIIII\n")
|
|
write(input_dir / "mock_2.fastq", "@r2\nTGCA\n+\nIIII\n")
|
|
write(input_dir / "samples.yaml", "groups:\n control: control\ncompare:\n - [treat, control]\n")
|
|
return job_id, input_dir, final_dir
|
|
|
|
|
|
def assert_chat_intent(message: str, job_id: str, expected_stage: str) -> dict:
|
|
chat = post("/api/agent/chat", {"message": message, "job_id": job_id, "session_id": f"test_{int(time.time())}"})
|
|
intent = chat.get("intent") or {}
|
|
stages = intent.get("stages") or []
|
|
assert expected_stage in stages or intent.get("primary_stage") == expected_stage, (
|
|
f"{message} expected {expected_stage}, got intent={intent}"
|
|
)
|
|
return intent
|
|
|
|
|
|
def execute(stage: str, job_id: str, **params) -> dict:
|
|
payload = {
|
|
"stage": stage,
|
|
"job_id": job_id,
|
|
"prefix": PREFIX,
|
|
"cpu": 1,
|
|
"rnaseqForegroundMode": "auto",
|
|
"enrichBackgroundSource": "gene2go",
|
|
**params,
|
|
}
|
|
submitted = post("/api/execute", payload)
|
|
assert submitted.get("success"), submitted
|
|
if submitted.get("task_id"):
|
|
return wait_task(submitted["task_id"])
|
|
return submitted
|
|
|
|
|
|
def main() -> None:
|
|
job_id, _, final_dir = prepare_job()
|
|
|
|
go_intent = assert_chat_intent("帮我执行rnaseq GO富集分析", job_id, "rnaseq_go_enrich")
|
|
kegg_intent = assert_chat_intent("帮我执行rnaseq KEGG富集分析", job_id, "rnaseq_kegg_enrich")
|
|
annotation_intent = assert_chat_intent("帮我执行背景注释分析", job_id, "annotation")
|
|
|
|
plan = post("/api/agent/plan", {
|
|
"message": "帮我执行rnaseq GO富集分析",
|
|
"job_id": job_id,
|
|
"prefix": PREFIX,
|
|
"history": [],
|
|
"intent": go_intent,
|
|
})
|
|
plan_stages = [s.get("id", s) if isinstance(s, dict) else s for s in (plan.get("plan") or {}).get("stages", [])]
|
|
assert "rnaseq_go_enrich" in plan_stages, f"GO plan did not keep split stage: {plan}"
|
|
|
|
go_task = execute("rnaseq_go_enrich", job_id, enrichType="go")
|
|
assert go_task.get("status") == "success", go_task.get("message")
|
|
|
|
kegg_task = execute("rnaseq_kegg_enrich", job_id, enrichType="kegg", keggOrganism="ath")
|
|
assert kegg_task.get("status") == "success", kegg_task.get("message")
|
|
|
|
go_file = final_dir / f"{PREFIX}.rnaseq_enrich.treat_vs_control.GO_enrich.txt"
|
|
kegg_file = final_dir / f"{PREFIX}.rnaseq_enrich.treat_vs_control.KEGG_enrich.txt"
|
|
assert go_file.exists(), f"missing GO file: {go_file}"
|
|
assert kegg_file.exists(), f"missing KEGG file: {kegg_file}"
|
|
|
|
print(json.dumps({
|
|
"job_id": job_id,
|
|
"go_intent_stage": go_intent.get("primary_stage"),
|
|
"kegg_intent_stage": kegg_intent.get("primary_stage"),
|
|
"annotation_intent_stage": annotation_intent.get("primary_stage"),
|
|
"plan_stages": plan_stages,
|
|
"go_status": go_task.get("status"),
|
|
"kegg_status": kegg_task.get("status"),
|
|
"go_file": str(go_file),
|
|
"kegg_file": str(kegg_file),
|
|
}, ensure_ascii=False, indent=2))
|
|
print("PASS: RNA-seq GO/KEGG/annotation multi-agent flow")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|