FactEval / facteval /cli.py
Sahil al farib
Fix gradio 5 compatibility
80ec89b
"""
CLI – Command-line interface for FactEval.
Usage:
facteval check input.json
facteval check input.json --output output.json
facteval check --answer "..." --context "ctx1" --context "ctx2"
"""
import argparse
import json
import sys
import logging
def main():
"""Entry point for the facteval CLI."""
parser = argparse.ArgumentParser(
prog="facteval",
description="FactEval – Claim-level factuality evaluation with calibrated confidence.",
)
subparsers = parser.add_subparsers(dest="command", help="Available commands")
# ── facteval check ───────────────────────────────────────────────────────
check_parser = subparsers.add_parser(
"check", help="Check an answer for factual accuracy against provided contexts."
)
check_parser.add_argument(
"input_file", nargs="?", default=None,
help='JSON file with "answer" and "contexts" keys.',
)
check_parser.add_argument(
"--answer", "-a", type=str, default=None,
help="The answer text to check (alternative to input file).",
)
check_parser.add_argument(
"--context", "-c", action="append", default=None,
help="Context passage (can be repeated). Alternative to input file.",
)
check_parser.add_argument(
"--output", "-o", type=str, default=None,
help="Output file path. If not provided, prints to stdout.",
)
check_parser.add_argument(
"--calibrator", type=str, default=None,
help="Path to a pre-fitted calibrator pickle file.",
)
check_parser.add_argument(
"--top-k", type=int, default=3,
help="Number of evidence sentences to retrieve per claim (default: 3).",
)
check_parser.add_argument(
"--max-claims", type=int, default=10,
help="Maximum number of claims to extract (default: 10).",
)
check_parser.add_argument(
"--verbose", "-v", action="store_true",
help="Enable verbose logging.",
)
args = parser.parse_args()
if args.command is None:
parser.print_help()
sys.exit(0)
if args.command == "check":
_run_check(args)
def _run_check(args):
"""Execute the check command."""
# Configure logging
level = logging.INFO if args.verbose else logging.WARNING
logging.basicConfig(level=level, format="%(name)s | %(message)s")
# Parse input
answer, contexts = _parse_input(args)
if answer is None:
print("Error: Provide either an input JSON file or --answer + --context flags.", file=sys.stderr)
sys.exit(1)
# Import here to avoid slow import on --help
from facteval.core import check
# Run pipeline
result = check(
answer=answer,
contexts=contexts,
top_k=args.top_k,
max_claims=args.max_claims,
calibrator_path=args.calibrator,
)
# Output
output_json = json.dumps(result, indent=2, ensure_ascii=False)
if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(output_json)
print(f"Results saved to {args.output}", file=sys.stderr)
else:
print(output_json)
def _parse_input(args) -> tuple[str | None, list[str]]:
"""Parse answer and contexts from file or CLI flags."""
# Option 1: JSON file
if args.input_file:
with open(args.input_file, "r", encoding="utf-8-sig") as f:
data = json.load(f)
return data.get("answer"), data.get("contexts", [])
# Option 2: CLI flags
if args.answer:
return args.answer, args.context or []
# Option 3: stdin
if not sys.stdin.isatty():
data = json.load(sys.stdin)
return data.get("answer"), data.get("contexts", [])
return None, []
if __name__ == "__main__":
main()