Initial SQL-only 1C adapter baseline
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import report_failed_turns
|
||||
import report_quality_metrics
|
||||
import report_repeated_failures
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_REPORT_DIR = ROOT / "reports" / "observability"
|
||||
DEFAULT_OUTPUT_DIR = DEFAULT_REPORT_DIR / "quality"
|
||||
|
||||
|
||||
def timestamp_slug(now: datetime | None = None) -> str:
|
||||
current = now or datetime.now(timezone.utc)
|
||||
return current.strftime("%Y%m%dT%H%M%SZ")
|
||||
|
||||
|
||||
def snapshot_paths(output_dir: Path, *, slug: str) -> dict[str, Path]:
|
||||
return {
|
||||
"quality": output_dir / f"weekly-quality-{slug}.json",
|
||||
"repeated_failures": output_dir / f"repeated-failures-{slug}.json",
|
||||
"failed_turns": output_dir / f"failed-turns-{slug}.json",
|
||||
"index": output_dir / "index.json",
|
||||
}
|
||||
|
||||
|
||||
def read_index(path: Path) -> dict[str, Any]:
|
||||
if not path.exists():
|
||||
return {"snapshots": []}
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
if not isinstance(data, dict):
|
||||
return {"snapshots": []}
|
||||
snapshots = data.get("snapshots")
|
||||
if not isinstance(snapshots, list):
|
||||
data["snapshots"] = []
|
||||
return data
|
||||
|
||||
|
||||
def write_index(path: Path, entry: dict[str, Any], *, keep: int = 50) -> dict[str, Any]:
|
||||
data = read_index(path)
|
||||
snapshots = [item for item in data.get("snapshots", []) if isinstance(item, dict)]
|
||||
snapshots.append(entry)
|
||||
snapshots.sort(key=lambda item: str(item.get("created_at") or ""), reverse=True)
|
||||
data["snapshots"] = snapshots[:keep]
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_text(json.dumps(data, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
|
||||
return data
|
||||
|
||||
|
||||
def build_snapshot(
|
||||
*,
|
||||
report_dir: Path,
|
||||
output_dir: Path,
|
||||
days: int,
|
||||
limit: int,
|
||||
min_repeat_count: int,
|
||||
failed_limit: int,
|
||||
) -> dict[str, Any]:
|
||||
records = report_quality_metrics.iter_records(report_dir)
|
||||
filtered = report_quality_metrics.filter_recent(records, days=days)
|
||||
quality_payload = report_quality_metrics.summarize(filtered, limit=limit)
|
||||
|
||||
turn_records = report_failed_turns.iter_turn_audit(report_dir)
|
||||
failed_payload = {"records": len(turn_records), "failed_turns": report_failed_turns.filter_failed(turn_records, limit=failed_limit)}
|
||||
repeated_payload = {
|
||||
"records": len(turn_records),
|
||||
"repeated_failures": report_repeated_failures.group_repeated_failures(turn_records, min_count=min_repeat_count, limit=limit),
|
||||
}
|
||||
|
||||
slug = timestamp_slug()
|
||||
paths = snapshot_paths(output_dir, slug=slug)
|
||||
report_quality_metrics.write_report(paths["quality"], quality_payload)
|
||||
report_repeated_failures.write_report(paths["repeated_failures"], repeated_payload)
|
||||
report_failed_turns.write_report(paths["failed_turns"], failed_payload)
|
||||
|
||||
created_at = datetime.now(timezone.utc).isoformat()
|
||||
entry = {
|
||||
"created_at": created_at,
|
||||
"slug": slug,
|
||||
"days": days,
|
||||
"limit": limit,
|
||||
"min_repeat_count": min_repeat_count,
|
||||
"failed_limit": failed_limit,
|
||||
"artifacts": {
|
||||
"quality": str(paths["quality"]),
|
||||
"repeated_failures": str(paths["repeated_failures"]),
|
||||
"failed_turns": str(paths["failed_turns"]),
|
||||
},
|
||||
"summary": {
|
||||
"records": quality_payload.get("records"),
|
||||
"top_failure_types": quality_payload.get("top_failure_types") or [],
|
||||
"repeated_failures": len(repeated_payload.get("repeated_failures") or []),
|
||||
"failed_turns": len(failed_payload.get("failed_turns") or []),
|
||||
},
|
||||
}
|
||||
write_index(paths["index"], entry)
|
||||
return {"snapshot": entry, "paths": {key: str(value) for key, value in paths.items()}}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description="Export dated observability quality snapshot bundle and update index.")
|
||||
parser.add_argument("--report-dir", type=Path, default=DEFAULT_REPORT_DIR)
|
||||
parser.add_argument("--output-dir", type=Path, default=DEFAULT_OUTPUT_DIR)
|
||||
parser.add_argument("--days", type=int, default=7)
|
||||
parser.add_argument("--limit", type=int, default=10)
|
||||
parser.add_argument("--min-repeat-count", type=int, default=2)
|
||||
parser.add_argument("--failed-limit", type=int, default=100)
|
||||
args = parser.parse_args()
|
||||
|
||||
payload = build_snapshot(
|
||||
report_dir=args.report_dir,
|
||||
output_dir=args.output_dir,
|
||||
days=args.days,
|
||||
limit=args.limit,
|
||||
min_repeat_count=args.min_repeat_count,
|
||||
failed_limit=args.failed_limit,
|
||||
)
|
||||
print(json.dumps(payload, ensure_ascii=False, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user