129 lines
4.9 KiB
Python
129 lines
4.9 KiB
Python
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / "scripts"))
|
|
|
|
from route_1c_question import route_question # noqa: E402
|
|
|
|
|
|
DEFAULT_INDEX = ROOT / "reports" / "1c-sql" / "upo" / "unified-object-route-index.json"
|
|
|
|
|
|
CASES = [
|
|
{
|
|
"id": "docs_only_form_open",
|
|
"question": "Как работает событие ПриОткрытии формы?",
|
|
"expected_route": "docs_rag",
|
|
"expected_fact_paths": [],
|
|
},
|
|
{
|
|
"id": "current_fact_extension_attribute",
|
|
"question": "Есть ли реквизит ДатаСоздания у документа ПриходнаяНакладная?",
|
|
"expected_route": "current_config_fact",
|
|
"expected_fact_paths": ["Документ.ПриходнаяНакладная.ДатаСоздания"],
|
|
"expected_exists": {"Документ.ПриходнаяНакладная.ДатаСоздания": True},
|
|
},
|
|
{
|
|
"id": "rag_example_requires_current_fact_check",
|
|
"question": "В примере RAG есть реквизит Артикул у справочника Номенклатура. Напиши код для текущей базы.",
|
|
"expected_route": "mixed_docs_and_current_config",
|
|
"expected_fact_paths": ["Справочник.Номенклатура.Артикул"],
|
|
"expected_risk": "example_is_not_current_fact",
|
|
},
|
|
]
|
|
|
|
|
|
def fact_exists_by_path(route: dict) -> dict[str, bool | None]:
|
|
result = {}
|
|
for row in route.get("fact_checks") or []:
|
|
path = row.get("path")
|
|
if not path:
|
|
continue
|
|
if row.get("status") != "checked":
|
|
result[path] = None
|
|
else:
|
|
result[path] = bool((row.get("result") or {}).get("exists"))
|
|
return result
|
|
|
|
|
|
def run_case(case: dict, *, index: Path, view: str) -> dict:
|
|
route = route_question(case["question"], index_path=index, view=view)
|
|
failures = []
|
|
decision = route.get("decision") or {}
|
|
if decision.get("route") != case.get("expected_route"):
|
|
failures.append({"code": "route_mismatch", "expected": case.get("expected_route"), "actual": decision.get("route")})
|
|
|
|
actual_paths = route.get("fact_paths") or []
|
|
expected_paths = case.get("expected_fact_paths") or []
|
|
if actual_paths != expected_paths:
|
|
failures.append({"code": "fact_paths_mismatch", "expected": expected_paths, "actual": actual_paths})
|
|
|
|
expected_risk = case.get("expected_risk")
|
|
if expected_risk:
|
|
risks = {row.get("code") for row in route.get("source_risks") or []}
|
|
if expected_risk not in risks:
|
|
failures.append({"code": "risk_missing", "expected": expected_risk, "actual": sorted(risks)})
|
|
|
|
exists = fact_exists_by_path(route)
|
|
for path, expected in (case.get("expected_exists") or {}).items():
|
|
if exists.get(path) is not expected:
|
|
failures.append({"code": "fact_exists_mismatch", "path": path, "expected": expected, "actual": exists.get(path)})
|
|
|
|
return {
|
|
"id": case["id"],
|
|
"status": "passed" if not failures else "failed",
|
|
"question": case["question"],
|
|
"failures": failures,
|
|
"route": {
|
|
"decision": route.get("decision"),
|
|
"source_risks": route.get("source_risks"),
|
|
"fact_paths": route.get("fact_paths"),
|
|
"fact_exists": exists,
|
|
},
|
|
}
|
|
|
|
|
|
def run_check(index: Path, *, view: str) -> dict:
|
|
if not index.exists():
|
|
return {
|
|
"schema": "onec_question_router_check.v1",
|
|
"status": "failed",
|
|
"error": f"route index not found: {index}",
|
|
"cases": [],
|
|
}
|
|
results = [run_case(case, index=index, view=view) for case in CASES]
|
|
return {
|
|
"schema": "onec_question_router_check.v1",
|
|
"status": "ok" if all(row["status"] == "passed" for row in results) else "failed",
|
|
"index": str(index),
|
|
"view": view,
|
|
"case_count": len(results),
|
|
"cases": results,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description="Check 1C question router behavior.")
|
|
parser.add_argument("--index", type=Path, default=DEFAULT_INDEX)
|
|
parser.add_argument("--view", choices=["effective", "base"], default="effective")
|
|
parser.add_argument("--output", type=Path)
|
|
parser.add_argument("--print", action="store_true")
|
|
args = parser.parse_args()
|
|
|
|
report = run_check(args.index, view=args.view)
|
|
if args.output:
|
|
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
args.output.write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
|
if args.print or not args.output:
|
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
return 0 if report["status"] == "ok" else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|