feat: initial HSAP platform

Huaxu Sentinel Active Safety Platform with embedded algorithm code,
Docker Compose setup, and vendored dataset scaffolds for clone-and-run.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-25 16:59:59 +08:00
commit 7c43b44c57
1619 changed files with 373355 additions and 0 deletions

View File

View File

@@ -0,0 +1,30 @@
"""ingest_flow感知 returned 批次 → 提交 build 审核。"""
from __future__ import annotations
from typing import Any
from as_platform.agents.tools import invoke_tool, submit_build_for_batch
from as_platform.agents.trace import start_trace, trace_span
def run_ingest_flow(*, task: str = "dam", submitted_by: str = "agent") -> dict[str, Any]:
trace_id = start_trace("ingest_flow", task=task)
with trace_span("list_pending"):
report = invoke_tool("list_pending_batches")
submitted = []
for batch in report.get("batches", []):
if batch.get("task") != task:
continue
if batch.get("stage") != "returned":
continue
with trace_span("submit_build", batch=batch.get("batch")):
apr = submit_build_for_batch(
task=task,
batch=batch["batch"],
pack=batch.get("pack") or "dms_v2",
submitted_by=submitted_by,
)
submitted.append(apr)
return {"trace_id": trace_id, "submitted": submitted, "count": len(submitted)}

View File

@@ -0,0 +1,20 @@
"""labeling_flow列出 raw_pool / out_for_labeling 批次。"""
from __future__ import annotations
from typing import Any
from as_platform.agents.tools import invoke_tool
from as_platform.agents.trace import start_trace, trace_span
def run_labeling_flow(*, task: str | None = None) -> dict[str, Any]:
trace_id = start_trace("labeling_flow", task=task)
with trace_span("list_pending"):
report = invoke_tool("list_pending_batches")
batches = [
b for b in report.get("batches", [])
if b.get("stage") in ("raw_pool", "out_for_labeling", "returned")
and (task is None or b.get("task") == task)
]
return {"trace_id": trace_id, "batches": batches, "count": len(batches)}

View File

@@ -0,0 +1,18 @@
"""train_promote_flow提交 train 审核platform 轨)。"""
from __future__ import annotations
from typing import Any
from as_platform.agents.tools import get_model_versions, invoke_tool, submit_train_job
from as_platform.agents.trace import start_trace, trace_span
def run_train_promote_flow(*, task: str = "dam", submitted_by: str = "agent") -> dict[str, Any]:
trace_id = start_trace("train_promote_flow", task=task)
with trace_span("get_versions"):
versions = get_model_versions(task)
with trace_span("submit_train"):
apr = submit_train_job("dms", task, track="platform", submitted_by=submitted_by)
return {"trace_id": trace_id, "versions_before": versions, "approval": apr}

View File

@@ -0,0 +1,86 @@
"""LangChain 风格 Tool 注册(纯 Python + 可选 langchain"""
from __future__ import annotations
from typing import Any, Callable
from as_platform.audit.queue import submit_approval
from as_platform.data.core import get_catalog, get_pending_report, load_wf
from as_platform.jobs.queue import get_job, list_jobs
import yaml
from pathlib import Path
WORKSPACE = Path(__file__).resolve().parents[2]
def list_pending_batches() -> dict[str, Any]:
return get_pending_report()
def get_dataset_catalog() -> dict[str, Any]:
return get_catalog()
def submit_build_for_batch(task: str, batch: str, pack: str = "dms_v2", submitted_by: str | None = None) -> dict:
return submit_approval(
"build_dms",
{"task": task, "pack": pack, "batch": batch},
submitted_by=submitted_by,
note=f"agent build {batch}",
)
def submit_train_job(project: str, task: str, track: str = "platform", submitted_by: str | None = None) -> dict:
action = "train_dms" if project == "dms" else "train_lane"
params: dict[str, Any] = {"track": track}
if project == "dms":
params["task"] = task
return submit_approval(action, params, submitted_by=submitted_by, note=f"agent train {project}/{task}")
def get_job_status(job_id: str) -> dict[str, Any] | None:
return get_job(job_id)
def get_model_versions(task: str) -> dict[str, Any]:
root = WORKSPACE / "datasets/dms/manifests/train_versions.yaml"
if not root.is_file():
return {}
data = yaml.safe_load(root.read_text(encoding="utf-8"))
return data.get(task, {})
TOOL_REGISTRY: dict[str, Callable[..., Any]] = {
"list_pending_batches": list_pending_batches,
"get_dataset_catalog": get_dataset_catalog,
"submit_build_for_batch": submit_build_for_batch,
"submit_train_job": submit_train_job,
"get_job_status": get_job_status,
"get_model_versions": get_model_versions,
}
def invoke_tool(name: str, **kwargs: Any) -> Any:
fn = TOOL_REGISTRY.get(name)
if not fn:
raise ValueError(f"未知 tool: {name}")
return fn(**kwargs)
def as_langchain_tools() -> list[Any]:
try:
from langchain_core.tools import tool
except ImportError:
return []
@tool
def t_list_pending_batches() -> dict:
"""列出待处理批次与送标状态。"""
return list_pending_batches()
@tool
def t_get_dataset_catalog() -> dict:
"""获取 DMS/Lane 数据目录统计。"""
return get_dataset_catalog()
return [t_list_pending_batches, t_get_dataset_catalog]

View File

@@ -0,0 +1,62 @@
"""LangSmith 式 tracemanifests/trace_log.jsonl"""
from __future__ import annotations
import json
import uuid
from contextlib import contextmanager
from datetime import datetime, timezone
from typing import Any, Iterator
from as_platform.config import TRACE_LOG, MANIFESTS
_current_trace: str | None = None
def _now() -> str:
return datetime.now(timezone.utc).isoformat()
def start_trace(name: str, **meta: Any) -> str:
global _current_trace
trace_id = f"trace-{uuid.uuid4().hex[:12]}"
_current_trace = trace_id
_append({"type": "trace_start", "trace_id": trace_id, "name": name, "ts": _now(), **meta})
return trace_id
def _append(entry: dict[str, Any]) -> None:
MANIFESTS.mkdir(parents=True, exist_ok=True)
if not TRACE_LOG.is_file():
TRACE_LOG.write_text("", encoding="utf-8")
with TRACE_LOG.open("a", encoding="utf-8") as f:
f.write(json.dumps(entry, ensure_ascii=False) + "\n")
@contextmanager
def trace_span(span_type: str, **fields: Any) -> Iterator[None]:
span_id = f"span-{uuid.uuid4().hex[:8]}"
_append({"type": span_type, "span_id": span_id, "trace_id": _current_trace, "ts": _now(), **fields})
try:
yield
finally:
_append({"type": f"{span_type}_end", "span_id": span_id, "trace_id": _current_trace, "ts": _now()})
def get_trace(trace_id: str) -> list[dict[str, Any]]:
if not TRACE_LOG.is_file():
return []
return [json.loads(l) for l in TRACE_LOG.read_text().strip().splitlines() if l.strip() and trace_id in l]
def list_traces(limit: int = 50) -> list[str]:
if not TRACE_LOG.is_file():
return []
ids = []
for line in TRACE_LOG.read_text().strip().splitlines():
try:
o = json.loads(line)
if o.get("type") == "trace_start" and o.get("trace_id"):
ids.append(o["trace_id"])
except json.JSONDecodeError:
pass
return list(reversed(ids[-limit:]))