File size: 3,085 Bytes
c793f45 | 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 | """Command-line interface."""
from __future__ import annotations
import json
import typer
from rich.console import Console
from veil_pgd.config import get_settings
app = typer.Typer(
add_completion=False,
help="veil-pgd: image-side protection for vision-language training data",
)
console = Console()
@app.command()
def protect(
image: str = typer.Argument(..., help="Path to the input image"),
truth: str = typer.Option(..., "--truth", "-t", help="True label of the image"),
skip_blackbox: bool = typer.Option(False, help="Skip Tier B (no API calls)"),
):
"""Find and render the best protective overlay for IMAGE."""
from veil_pgd.pipeline import protect_image
res = protect_image(image, truth, skip_blackbox=skip_blackbox)
console.print_json(json.dumps(res.manifest))
@app.command()
def evaluate(
manifest: str = typer.Argument(..., help="CSV of image_path,truth per line"),
out: str = typer.Option("artifacts/eval", help="Output dir"),
):
"""Run the eval harness over a labeled image set (single stealth level)."""
from veil_pgd.eval.harness import run_eval
from veil_pgd.eval.report import to_markdown
report = run_eval(manifest, out)
console.print(to_markdown(report))
@app.command()
def sweep(
manifest: str = typer.Argument(..., help="CSV of image_path,truth per line"),
levels: str = typer.Option("strict,medium,loose", help="Comma-separated stealth levels"),
out: str = typer.Option("artifacts/eval", help="Output dir"),
skip_blackbox: bool = typer.Option(False, help="Skip Tier B (surrogate-only, no API calls)"),
):
"""Stealth sweep: map attack strength vs human-visibility across levels."""
from veil_pgd.eval.harness import run_sweep
report = run_sweep(
manifest, [x.strip() for x in levels.split(",")], out,
skip_blackbox=skip_blackbox,
)
console.print("[bold]Stealth frontier[/bold] (level: mean_dist | success@0.5 | stealth_pass)")
for row in report["frontier"]:
console.print(
f" {row['level']:>7}: {row['mean_distance']:.3f} | "
f"{row['success@0.5']:.1%} | {row['stealth_pass_rate']:.1%}"
)
@app.command()
def doctor():
"""Check config + connectivity to klaus-3 services and OpenRouter."""
import httpx
s = get_settings()
console.print(f"[bold]OpenRouter key set:[/bold] {bool(s.openrouter_api_key)}")
console.print(f"[bold]Targets:[/bold] {', '.join(s.blackbox_target_models)}")
for name, url in [
("qwen", s.klaus3_qwen_base_url),
("gemma4b", s.klaus3_gemma4b_base_url),
("vision-svc", s.klaus3_vision_service_url),
]:
try:
r = httpx.get(url.rstrip("/") + "/models", timeout=5.0)
ok = r.status_code < 500
except Exception as e: # noqa: BLE001
ok = False
console.print(f" {name}: [red]unreachable[/red] ({e})")
continue
console.print(f" {name}: {'[green]ok[/green]' if ok else '[yellow]?[/yellow]'} ({url})")
if __name__ == "__main__":
app()
|