Jev maintenance tools: shared client, spec coverage audit, voice check, nightly triage
ci / test (3.13) (push) Successful in 4m32s
ci / test (3.12) (push) Successful in 4m36s

This commit is contained in:
kemal committed 2026-10-01 13:21:36 +01:00
1 parent 0ff888e41b
commit 3ed0503bdf
9 files changed
+722 -48

No files matched your search

+6 -48
View File
@@ -17,24 +17,18 @@ Exit code 1 when any claim is contradicted or has no source, so the script can g
from __future__ import annotations
import argparse
import hashlib
import json
import os
import re
import sys
import time
import urllib.error
import urllib.request
from dataclasses import dataclass, field
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "jev"))
from client import MODEL, api_key, system_one
ROOT = Path(__file__).resolve().parents[2]
API_URL = "https://api.typesafe.ai/v1/systemone"
MODEL = "jev-latest"
AUTO_ACCEPT = 0.8 # cookbook default; evaluate on our own claims before trusting it
TOP_K = 3
DEFAULT_SOURCES = ["docs/validation", "docs/known_unknowns.md", "SIMURGARRAY_FUNCTIONAL_SPEC.md"]
CACHE = ROOT / ".cache" / "claims"
RELATION = {
"type": "choice",
@@ -255,33 +249,6 @@ def build_request(claim: Claim, cands: list[Passage]) -> dict[str, object]:
return {"model": MODEL, "state": state, "questions": questions}
def call_api(payload: dict[str, object], key: str, retries: int = 5) -> dict[str, object]:
body = json.dumps(payload).encode()
digest = hashlib.sha256(body).hexdigest()
cached = CACHE / f"{digest}.json"
if cached.exists():
return json.loads(cached.read_text()) # type: ignore[no-any-return]
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
"User-Agent": "simurgarray-claims/1",
}
req = urllib.request.Request(API_URL, data=body, method="POST", headers=headers)
for attempt in range(retries):
try:
with urllib.request.urlopen(req, timeout=60) as r:
res = json.loads(r.read())
CACHE.mkdir(parents=True, exist_ok=True)
cached.write_text(json.dumps(res))
return res # type: ignore[no-any-return]
except urllib.error.HTTPError as e:
if e.code in (429, 529) and attempt < retries - 1:
time.sleep(2**attempt)
continue
raise RuntimeError(f"TypeSafe API error {e.code}: {e.read()[:200]!r}") from e
raise RuntimeError("TypeSafe API: retries exhausted")
def verdict(answers: dict[str, dict[str, object]], threshold: float = AUTO_ACCEPT) -> tuple[str, str | None, float]:
"""Combine per-section answers: any confident 'supports' wins; else a confident 'contradicts'; else a confident
'says_nothing' from every section; anything else goes to a person."""
@@ -306,15 +273,6 @@ def verdict(answers: dict[str, dict[str, object]], threshold: float = AUTO_ACCEP
# ------------------------------------------------------------------ report
def _key_from_file(path: Path) -> str:
"""``TYPESAFE_API_KEY=...`` from a private local file (never from the repository)."""
if path.exists():
for ln in path.read_text().splitlines():
if ln.startswith("TYPESAFE_API_KEY="):
return ln.split("=", 1)[1].strip()
return ""
def main(argv: list[str] | None = None) -> int:
ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
ap.add_argument("files", nargs="+", type=Path, help="texts to check (markdown or plain text)")
@@ -323,7 +281,7 @@ def main(argv: list[str] | None = None) -> int:
ap.add_argument("--threshold", type=float, default=AUTO_ACCEPT)
ap.add_argument("--dry-run", action="store_true", help="extract claims and evidence, no API calls")
a = ap.parse_args(argv)
key = os.environ.get("TYPESAFE_API_KEY", "") or _key_from_file(Path.home() / ".config" / "typesafe" / "credentials")
key = api_key()
if not a.dry_run and not key:
print("TYPESAFE_API_KEY is not set (use --dry-run to check extraction without the API).", file=sys.stderr)
return 2
@@ -338,8 +296,8 @@ def main(argv: list[str] | None = None) -> int:
if a.dry_run:
rows.append(("planned", c, cands[0], 0.0, f"{len(cands)} candidate(s)"))
continue
res = call_api(build_request(c, cands), key)
answers = res["answers"] # type: ignore[index]
req = build_request(c, cands)
answers = system_one(req["state"], req["questions"], key) # type: ignore[arg-type]
v, qid, conf = verdict(answers, a.threshold) # type: ignore[arg-type]
src = cands[int(qid[1:])] if qid else cands[0]
probs = ", ".join(f"{q}: {answers[q]['choice']} {answers[q]['confidence']:.2f}" for q in answers) # type: ignore[index]