Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_MANIFEST = ROOT / "reports" / "llm-artifact-manifest.json"
|
||||
DEFAULT_OUTPUT = ROOT / "reports" / "llm-artifact-check.json"
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
data = json.loads(path.read_text(encoding="utf-8-sig"))
|
||||
if not isinstance(data, dict):
|
||||
raise ValueError(f"{path} must contain a JSON object")
|
||||
return data
|
||||
|
||||
|
||||
def iter_files(path: Path) -> list[Path]:
|
||||
if not path.exists():
|
||||
return []
|
||||
return sorted(item for item in path.rglob("*") if item.is_file())
|
||||
|
||||
|
||||
def resolve_record_path(record_path: str, *, source_root: Path, target_root: Path | None) -> Path:
|
||||
path = Path(record_path)
|
||||
if target_root is None:
|
||||
return path
|
||||
try:
|
||||
relative = path.relative_to(source_root)
|
||||
return target_root / relative
|
||||
except ValueError:
|
||||
return target_root / path.name
|
||||
|
||||
|
||||
def check_artifact(record: dict[str, Any], *, source_root: Path, target_root: Path | None, strict_counts: bool) -> list[dict[str, Any]]:
|
||||
findings: list[dict[str, Any]] = []
|
||||
path = resolve_record_path(str(record.get("path") or ""), source_root=source_root, target_root=target_root)
|
||||
expected_exists = bool(record.get("exists"))
|
||||
if expected_exists and not path.exists():
|
||||
return [{"severity": "error", "code": "artifact_missing", "artifact": record.get("name"), "path": str(path)}]
|
||||
if not path.exists():
|
||||
return []
|
||||
if not path.is_dir():
|
||||
findings.append({"severity": "error", "code": "artifact_not_directory", "artifact": record.get("name"), "path": str(path)})
|
||||
return findings
|
||||
|
||||
files = iter_files(path)
|
||||
size = sum(item.stat().st_size for item in files)
|
||||
expected_count = int(record.get("file_count") or 0)
|
||||
expected_size = int(record.get("total_size_bytes") or 0)
|
||||
if strict_counts and len(files) != expected_count:
|
||||
findings.append(
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "file_count_mismatch",
|
||||
"artifact": record.get("name"),
|
||||
"path": str(path),
|
||||
"expected": expected_count,
|
||||
"actual": len(files),
|
||||
}
|
||||
)
|
||||
elif len(files) < expected_count:
|
||||
findings.append(
|
||||
{
|
||||
"severity": "warning",
|
||||
"code": "file_count_decreased",
|
||||
"artifact": record.get("name"),
|
||||
"path": str(path),
|
||||
"expected_at_least": expected_count,
|
||||
"actual": len(files),
|
||||
}
|
||||
)
|
||||
if strict_counts and size != expected_size:
|
||||
findings.append(
|
||||
{
|
||||
"severity": "error",
|
||||
"code": "total_size_mismatch",
|
||||
"artifact": record.get("name"),
|
||||
"path": str(path),
|
||||
"expected": expected_size,
|
||||
"actual": size,
|
||||
}
|
||||
)
|
||||
elif size < expected_size:
|
||||
findings.append(
|
||||
{
|
||||
"severity": "warning",
|
||||
"code": "total_size_decreased",
|
||||
"artifact": record.get("name"),
|
||||
"path": str(path),
|
||||
"expected_at_least": expected_size,
|
||||
"actual": size,
|
||||
}
|
||||
)
|
||||
return findings
|
||||
|
||||
|
||||
def check_manifest(manifest: dict[str, Any], *, target_root: Path | None, strict_counts: bool) -> dict[str, Any]:
|
||||
source_root = Path(str(manifest.get("workspace_root") or ROOT))
|
||||
findings: list[dict[str, Any]] = []
|
||||
for record in manifest.get("artifacts") or []:
|
||||
findings.extend(check_artifact(record, source_root=source_root, target_root=target_root, strict_counts=strict_counts))
|
||||
|
||||
errors = [item for item in findings if item.get("severity") == "error"]
|
||||
warnings = [item for item in findings if item.get("severity") == "warning"]
|
||||
return {
|
||||
"schema": "llm_artifact_manifest_check.v1",
|
||||
"manifest_schema": manifest.get("schema"),
|
||||
"manifest_created_at": manifest.get("created_at"),
|
||||
"target_root": str(target_root) if target_root else None,
|
||||
"strict_counts": strict_counts,
|
||||
"passed": not errors,
|
||||
"counts": {
|
||||
"artifacts": len(manifest.get("artifacts") or []),
|
||||
"errors": len(errors),
|
||||
"warnings": len(warnings),
|
||||
"findings": len(findings),
|
||||
},
|
||||
"findings": findings,
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Check that local LLM/RAG artifacts from a manifest are present after transfer or before Docker launch.")
|
||||
parser.add_argument("--manifest", type=Path, default=DEFAULT_MANIFEST)
|
||||
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT)
|
||||
parser.add_argument("--target-root", type=Path, help="New workspace root after transfer. If omitted, paths are checked as recorded.")
|
||||
parser.add_argument("--strict-counts", action="store_true", help="Fail when file counts or total sizes differ exactly.")
|
||||
args = parser.parse_args()
|
||||
|
||||
result = check_manifest(load_json(args.manifest), target_root=args.target_root, strict_counts=args.strict_counts)
|
||||
args.output.parent.mkdir(parents=True, exist_ok=True)
|
||||
args.output.write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
print(json.dumps({"output": str(args.output), "passed": result["passed"], "counts": result["counts"]}, ensure_ascii=False))
|
||||
return 0 if result["passed"] else 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user