File size: 4,246 Bytes
281c084
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
Command-line interface for frontier-check.

Usage:
    frontier-check "claim text"
    frontier-check -                      # read claim from stdin
    echo "claim" | frontier-check -
    frontier-check --json "claim"
    frontier-check --strict "claim"
    frontier-check --selftest
"""

from __future__ import annotations

import argparse
import json
import sys
from dataclasses import asdict

from .model import Claim, frontier_check, self_test


def _result_to_json(result) -> dict:
    return {
        "claim": result.claim.text,
        "status": result.assessment.status,
        "reason": result.assessment.reason,
        "theorem_used": result.assessment.theorem_used or None,
        "papers": [
            {"title": p.title, "year": p.year, "on_target": p.on_target}
            for p in result.papers
        ],
        "sub_claims": [
            {"kind": s.kind, "text": s.text,
             "verdict": s.verdict or None,
             "evidence": s.evidence or None}
            for s in result.sub_claims
        ],
        "verdict": result.verdict,
        "action": result.action,
        "residual": result.residual,
    }


def _render(result) -> str:
    lines = []
    lines.append("=" * 72)
    lines.append(f"CLAIM: {result.claim.text}")
    lines.append("=" * 72)

    a = result.assessment
    lines.append(f"  [1] Status : {a.status}")
    lines.append(f"      {a.reason}")
    if a.theorem_used:
        lines.append(f"      forbidden by: {a.theorem_used}")

    if result.papers:
        lines.append(f"  [2] Retrieval: {len(result.papers)} paper(s)")
        for p in result.papers:
            flag = "target" if p.on_target else "background"
            lines.append(f"      - [{flag}] {p.title} ({p.year})")
    else:
        lines.append("  [2] Retrieval: none")

    if result.sub_claims:
        lines.append("  [3] Sub-claims:")
        for s in result.sub_claims:
            lines.append(f"      [{s.kind:12s}] {s.text}")
            if s.verdict:
                lines.append(f"                    -> {s.verdict}: "
                             f"{s.evidence}")

    label = "*** FRONTIER ***" if result.verdict == "FRONTIER" else result.verdict
    lines.append(f"  [4] Verdict: {label}")
    if result.action:
        lines.append(f"      action   : {result.action}")
    if result.residual:
        lines.append(f"      residual : {result.residual}")
    return "\n".join(lines)


def main(argv=None) -> int:
    p = argparse.ArgumentParser(
        prog="frontier-check",
        description=(
            "Route LLM claims by their training-data status. "
            "FRONTIER is first-class."
        ),
    )
    p.add_argument(
        "claim",
        nargs="?",
        default=None,
        help="claim text, or '-' to read from stdin",
    )
    p.add_argument(
        "--json",
        action="store_true",
        help="emit JSON instead of a rendered report",
    )
    p.add_argument(
        "--strict",
        action="store_true",
        help="abort with exit code 1 if the self-test fails",
    )
    p.add_argument(
        "--selftest",
        action="store_true",
        help="run the self-test and exit",
    )
    p.add_argument(
        "--quiet",
        action="store_true",
        help="suppress the self-test banner",
    )
    args = p.parse_args(argv)

    # Self-test runs on every invocation.
    if not args.quiet:
        passed, total = self_test(verbose=True)
    else:
        passed, total = self_test(verbose=False)

    if args.selftest:
        return 0 if passed == total else 1

    if args.strict and passed < total:
        print("\n--strict: self-test failed; aborting.", file=sys.stderr)
        return 1

    if args.claim is None:
        p.print_help()
        return 0

    if args.claim == "-":
        claim_text = sys.stdin.read().strip()
    else:
        claim_text = args.claim

    if not claim_text:
        print("empty claim", file=sys.stderr)
        return 1

    result = frontier_check(Claim(claim_text))

    if args.json:
        print(json.dumps(_result_to_json(result), indent=2, ensure_ascii=False))
    else:
        print()
        print(_render(result))

    return 0


if __name__ == "__main__":
    sys.exit(main())