Add isolated burst and pollution impact evidence, parallel work-order workflows, persisted notifications and graph updates. Checkpoint current code. Known follow-ups remain for large pollution result previews and graceful geometry failure.
35 lines
2.0 KiB
Python
35 lines
2.0 KiB
Python
"""Temporary deterministic candidate selection behind the source-tracing contract.
|
|
|
|
Replace this selection with source inversion when the algorithm is available.
|
|
Only project-owned nodes supplied from the investigation are eligible. No
|
|
observation fit, confidence or contamination extent is inferred here.
|
|
"""
|
|
from psycopg import AsyncConnection
|
|
|
|
from app.infra.db.postgresql.read_queries import read_rows
|
|
from app.mcp.analysis_models import ContaminantSourceTraceInput
|
|
|
|
|
|
async def trace_contaminant_source(connection: AsyncConnection, args: ContaminantSourceTraceInput) -> dict:
|
|
rows = await read_rows(connection, """
|
|
SELECT id, node_type::text AS kind FROM network.nodes
|
|
WHERE id = ANY(%s) ORDER BY id
|
|
""", (args.candidate_node_ids,))
|
|
# Sort in Python as well so selection does not depend on DB collation.
|
|
nodes = sorted({str(row["id"]) for row in rows})
|
|
selected = nodes[0] if nodes else None
|
|
return {
|
|
"located_node": selected,
|
|
"top_candidates": [{"node_id": selected}] if selected else [],
|
|
"exit_condition": "candidate_selected" if selected else "no_candidate",
|
|
"method": "deterministic_candidate_selection",
|
|
"algorithm_status": "provisional",
|
|
"selection_basis": "lexicographically_first_existing_candidate_node",
|
|
"validated_node_ids": nodes,
|
|
"missing_node_ids": sorted(set(args.candidate_node_ids) - set(nodes)),
|
|
"notice": ("已返回待现场核验的候选节点;先展示节点、保存阶段结论并提出现场核查/采样处置工单,"
|
|
"工单审核派发与污染传播模拟、节点隔离及影响分析并行,结果补入同一调查。"
|
|
"正式污染反演算法尚未接入,本次选择不代表污染源确诊或污染传播范围。" if selected else
|
|
"提交的候选节点均不在当前项目模型中;核对事件位置及资产映射后重新提交,继续不依赖源节点的调查。"),
|
|
}
|