Download app.py from praedico/LeGuard: direct link, hf CLI and curl.
- Browser
- Download file 24.4 kB
-
https://huggingface.co/spaces/praedico/LeGuard/resolve/main/app.py
- Command line
-
hf download hf://spaces/praedico/LeGuard/app.py
-
curl -L -o app.py https://huggingface.co/spaces/praedico/LeGuard/resolve/main/app.py
24.4 kB
| import json | |
| import os | |
| import shutil | |
| import subprocess | |
| import uuid | |
| from pathlib import Path | |
| import gradio as gr | |
| from huggingface_hub import snapshot_download | |
| LEGUARD_BIN = os.environ.get("LEGUARD_BIN", "/usr/local/bin/leguard") | |
| WORKDIR = Path("/tmp/leguard-space") | |
| DATASETS_DIR = WORKDIR / "datasets" | |
| RUNS_DIR = WORKDIR / "runs" | |
| CHECK_ORDER = [ | |
| "structure", | |
| "schema", | |
| "consistency", | |
| "episodes", | |
| "temporal", | |
| "numerical", | |
| "video", | |
| "annotation", | |
| "training", | |
| "portability", | |
| ] | |
| BANNER_URL = "https://raw.githubusercontent.com/0xPraedico/LeGuard/main/assets/leguard-banner.png" | |
| REPO_URL = "https://github.com/0xPraedico/LeGuard" | |
| ISSUES_URL = f"{REPO_URL}/issues" | |
| BOOTSTRAP_JS = """ | |
| () => { | |
| const root = document.documentElement; | |
| const applyTheme = (mode) => { | |
| const normalized = String(mode || "light").toLowerCase(); | |
| const finalMode = normalized === "light" ? "light" : "dark"; | |
| root.setAttribute("data-leguard-theme", finalMode); | |
| }; | |
| window.__leguardApplyTheme = applyTheme; | |
| applyTheme("light"); | |
| const localizeFooter = () => { | |
| const localizedBuiltWith = [67, 114, 233, 233, 32, 97, 118, 101, 99] | |
| .map((cp) => String.fromCodePoint(cp)) | |
| .join(""); | |
| const localizedSettings = [80, 97, 114, 97, 109, 232, 116, 114, 101, 115] | |
| .map((cp) => String.fromCodePoint(cp)) | |
| .join(""); | |
| const localizedError = [69, 114, 114, 101, 117, 114] | |
| .map((cp) => String.fromCodePoint(cp)) | |
| .join(""); | |
| const localizedWarning = [65, 118, 101, 114, 116, 105, 115, 115, 101, 109, 101, 110, 116] | |
| .map((cp) => String.fromCodePoint(cp)) | |
| .join(""); | |
| const replacer = { | |
| [localizedBuiltWith]: "Built with", | |
| [localizedSettings]: "Settings", | |
| [localizedError]: "Error", | |
| [localizedWarning]: "Warning", | |
| }; | |
| const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); | |
| let current = walker.nextNode(); | |
| while (current) { | |
| if (current.nodeValue) { | |
| Object.entries(replacer).forEach(([from, to]) => { | |
| if (current.nodeValue.includes(from)) { | |
| current.nodeValue = current.nodeValue.replace(from, to); | |
| } | |
| }); | |
| } | |
| current = walker.nextNode(); | |
| } | |
| }; | |
| localizeFooter(); | |
| const observer = new MutationObserver(() => localizeFooter()); | |
| observer.observe(document.body, { childList: true, subtree: true }); | |
| } | |
| """ | |
| THEME_CHANGE_JS = """ | |
| (mode) => { | |
| if (window.__leguardApplyTheme) { | |
| window.__leguardApplyTheme(mode); | |
| } | |
| return []; | |
| } | |
| """ | |
| CHECK_LABELS = { | |
| "structure": "Structure", | |
| "schema": "Schema", | |
| "consistency": "Consistency", | |
| "episodes": "Episodes", | |
| "temporal": "Temporal", | |
| "numerical": "Numerical", | |
| "video": "Video", | |
| "annotation": "Annotation", | |
| "training": "Training Readiness", | |
| "portability": "Portability", | |
| } | |
| CSS = """ | |
| :root, | |
| html[data-leguard-theme="light"] { | |
| --lg-page-bg: #f3f6fb; | |
| --lg-panel-bg: #ffffff; | |
| --lg-surface-bg: #ffffff; | |
| --lg-border: #d8e1ef; | |
| --lg-text: #111827; | |
| --lg-muted: #5b6473; | |
| --lg-accent: #4f46e5; | |
| --lg-label-color: #5b6473; | |
| --lg-info-color: #4f46e5; | |
| --lg-accent-soft: #e0e7ff; | |
| --lg-input-bg: #ffffff; | |
| --lg-input-border: #c7d2e5; | |
| --lg-input-text: #111827; | |
| } | |
| html[data-leguard-theme="dark"] { | |
| --lg-page-bg: #060c1b; | |
| --lg-panel-bg: #0a1226; | |
| --lg-surface-bg: #0d1a34; | |
| --lg-border: #19345c; | |
| --lg-text: #e7eefc; | |
| --lg-muted: #93a9c9; | |
| --lg-accent: #22d3ee; | |
| --lg-label-color: #5b6473; | |
| --lg-info-color: #4f46e5; | |
| --lg-accent-soft: #14324a; | |
| --lg-input-bg: #09162d; | |
| --lg-input-border: #1b3d68; | |
| --lg-input-text: #dbeafe; | |
| } | |
| .gradio-container { | |
| background: var(--lg-page-bg) !important; | |
| color: var(--lg-text) !important; | |
| } | |
| .gradio-container, | |
| .gradio-container * { | |
| border-color: var(--lg-border); | |
| } | |
| .gradio-container input[type="text"], | |
| .gradio-container input[type="number"], | |
| .gradio-container input[type="search"], | |
| .gradio-container textarea, | |
| .gradio-container .wrap.svelte-1ykxmf5, | |
| .gradio-container .wrap.svelte-1rjryqp { | |
| background: var(--lg-input-bg) !important; | |
| color: var(--lg-input-text) !important; | |
| border-color: var(--lg-input-border) !important; | |
| } | |
| .gradio-container .tabs, | |
| .gradio-container .gradio-accordion, | |
| .gradio-container [role="tablist"], | |
| .gradio-container .form, | |
| .gradio-container .panel { | |
| background: var(--lg-panel-bg) !important; | |
| border-color: var(--lg-border) !important; | |
| } | |
| .gradio-container [role="tab"] { | |
| color: var(--lg-muted) !important; | |
| } | |
| .gradio-container [role="tab"][aria-selected="true"] { | |
| color: var(--lg-text) !important; | |
| border-bottom-color: var(--lg-accent) !important; | |
| } | |
| .gradio-container button { | |
| border-radius: 10px !important; | |
| } | |
| .gradio-container label, | |
| .gradio-container legend, | |
| .gradio-container .gr-form label, | |
| .gradio-container .gr-block label, | |
| .gradio-container .caption, | |
| .gradio-container [data-testid="block-label"], | |
| .gradio-container .svelte-1ipelgc, | |
| .gradio-container [data-testid="block-label"] *, | |
| .gradio-container .svelte-1ipelgc * { | |
| color: var(--lg-label-color) !important; | |
| opacity: 1 !important; | |
| } | |
| .gradio-container .prose, | |
| .gradio-container .prose *, | |
| .gradio-container .markdown, | |
| .gradio-container .markdown * { | |
| color: var(--lg-text) !important; | |
| opacity: 1 !important; | |
| } | |
| .gradio-container .prose p, | |
| .gradio-container .prose li, | |
| .gradio-container .prose ul, | |
| .gradio-container .prose ol, | |
| .gradio-container .prose span { | |
| color: var(--lg-text) !important; | |
| } | |
| .gradio-container .prose h1, | |
| .gradio-container .prose h2, | |
| .gradio-container .prose h3, | |
| .gradio-container .prose strong { | |
| color: var(--lg-text) !important; | |
| } | |
| .gradio-container .prose a, | |
| .gradio-container .markdown a { | |
| color: var(--lg-accent) !important; | |
| } | |
| .gradio-container [data-testid="block-info"], | |
| .gradio-container .block-info, | |
| .gradio-container .gradio-info, | |
| .gradio-container [data-testid="block-info"] *, | |
| .gradio-container .block-info *, | |
| .gradio-container .gradio-info * { | |
| color: var(--lg-info-color) !important; | |
| opacity: 1 !important; | |
| } | |
| .field-help { | |
| margin-top: 4px; | |
| font-size: 0.78rem; | |
| line-height: 1.25; | |
| color: var(--lg-info-color) !important; | |
| opacity: 1 !important; | |
| } | |
| .app-container { | |
| max-width: 1080px; | |
| margin: 0 auto; | |
| } | |
| .hero-card { | |
| border: 1px solid var(--lg-border); | |
| border-radius: 14px; | |
| padding: 14px 16px 10px 16px; | |
| background: var(--lg-panel-bg); | |
| margin-bottom: 14px; | |
| } | |
| .hero-card img { | |
| width: 100%; | |
| border-radius: 10px; | |
| } | |
| .hero-card h1 { | |
| margin: 10px 0 4px 0; | |
| font-size: 1.35rem; | |
| } | |
| .hero-card p { | |
| margin: 0; | |
| color: var(--lg-muted); | |
| } | |
| .hero-links { | |
| margin-top: 10px; | |
| } | |
| .hero-links a { | |
| color: var(--lg-accent); | |
| text-decoration: none; | |
| font-weight: 600; | |
| } | |
| .hero-links a:hover { | |
| text-decoration: underline; | |
| } | |
| .run-btn button { | |
| min-height: 48px !important; | |
| font-size: 1rem !important; | |
| font-weight: 700 !important; | |
| background: linear-gradient(90deg, #4f46e5, #4338ca) !important; | |
| color: #ffffff !important; | |
| border: none !important; | |
| } | |
| html[data-leguard-theme="dark"] .run-btn button { | |
| background: linear-gradient(90deg, #0ea5e9, #06b6d4) !important; | |
| } | |
| .refresh-btn button { | |
| min-height: 40px !important; | |
| font-size: 0.88rem !important; | |
| font-weight: 600 !important; | |
| background: var(--lg-surface-bg) !important; | |
| color: var(--lg-text) !important; | |
| border: 1px solid var(--lg-border) !important; | |
| } | |
| .status-card { | |
| border-radius: 10px; | |
| padding: 12px 14px; | |
| margin-bottom: 12px; | |
| font-weight: 600; | |
| } | |
| .gradio-container .status-card, | |
| .gradio-container .status-card * { | |
| color: inherit !important; | |
| opacity: 1 !important; | |
| } | |
| .status-neutral { | |
| background: var(--lg-surface-bg); | |
| color: var(--lg-muted); | |
| border: 1px solid var(--lg-border); | |
| } | |
| .status-pass { | |
| background: #ecfdf3 !important; | |
| color: #166534 !important; | |
| border: 1px solid #bbf7d0 !important; | |
| } | |
| .status-warn { | |
| background: #fff7ed !important; | |
| color: #9a3412 !important; | |
| border: 1px solid #fed7aa !important; | |
| } | |
| .status-fail { | |
| background: #fef2f2 !important; | |
| color: #991b1b !important; | |
| border: 1px solid #fecaca !important; | |
| } | |
| html[data-leguard-theme="dark"] .status-pass { | |
| background: #052e1f !important; | |
| color: #86efac !important; | |
| border: 1px solid #166534 !important; | |
| } | |
| html[data-leguard-theme="dark"] .status-warn { | |
| background: #3b2206 !important; | |
| color: #fdba74 !important; | |
| border: 1px solid #9a5800 !important; | |
| } | |
| html[data-leguard-theme="dark"] .status-fail { | |
| background: #3a0b10 !important; | |
| color: #fca5a5 !important; | |
| border: 1px solid #b91c1c !important; | |
| } | |
| .metrics-grid { | |
| display: grid; | |
| grid-template-columns: repeat(3, minmax(120px, 1fr)); | |
| gap: 10px; | |
| margin-bottom: 14px; | |
| } | |
| .metric { | |
| border: 1px solid var(--lg-border); | |
| border-radius: 10px; | |
| padding: 10px 12px; | |
| background: var(--lg-surface-bg); | |
| } | |
| .metric-label { | |
| font-size: 0.74rem; | |
| color: var(--lg-muted); | |
| text-transform: uppercase; | |
| letter-spacing: 0.04em; | |
| } | |
| .metric-value { | |
| margin-top: 4px; | |
| font-size: 1.1rem; | |
| font-weight: 700; | |
| color: var(--lg-text) !important; | |
| } | |
| .section-note { | |
| color: var(--lg-muted); | |
| font-size: 0.92rem; | |
| } | |
| .issues-card { | |
| border: 1px solid var(--lg-border); | |
| border-radius: 10px; | |
| background: var(--lg-surface-bg); | |
| padding: 12px; | |
| margin-top: 4px; | |
| color: var(--lg-muted); | |
| font-size: 0.92rem; | |
| } | |
| .issues-card a { | |
| color: var(--lg-accent); | |
| text-decoration: none; | |
| font-weight: 600; | |
| } | |
| .issues-card a:hover { | |
| text-decoration: underline; | |
| } | |
| """ | |
| for directory in (DATASETS_DIR, RUNS_DIR): | |
| directory.mkdir(parents=True, exist_ok=True) | |
| def run_command(command): | |
| completed = subprocess.run(command, capture_output=True, text=True) | |
| logs = [f"$ {' '.join(command)}"] | |
| if completed.stdout.strip(): | |
| logs.append(completed.stdout.strip()) | |
| if completed.stderr.strip(): | |
| logs.append(completed.stderr.strip()) | |
| return completed.returncode, "\n\n".join(logs) | |
| def resolve_dataset_path(dataset_repo_id): | |
| dataset_repo_id = dataset_repo_id.strip() | |
| if not dataset_repo_id: | |
| raise ValueError("Enter a Hugging Face dataset id, for example: praedico/SO101_pillbox_vita") | |
| target_dir = DATASETS_DIR / dataset_repo_id.replace("/", "__") | |
| if target_dir.exists(): | |
| shutil.rmtree(target_dir) | |
| token = os.environ.get("HF_TOKEN") | |
| snapshot_download( | |
| repo_id=dataset_repo_id, | |
| repo_type="dataset", | |
| local_dir=str(target_dir), | |
| token=token, | |
| ) | |
| return target_dir | |
| def summarize_report(report, check_exit): | |
| counts = report["issue_counts"] | |
| errors = counts["errors"] | |
| warnings = counts["warnings"] | |
| if errors > 0 or check_exit != 0: | |
| status = "FAIL" | |
| style = "status-fail" | |
| status_text = "Blocking issues detected. Review and fix before using this dataset in CI/training." | |
| elif warnings > 0: | |
| status = "WARN" | |
| style = "status-warn" | |
| status_text = "Warnings detected. Dataset is likely usable, but review issues before large runs." | |
| else: | |
| status = "PASS" | |
| style = "status-pass" | |
| status_text = "No blocking issues detected." | |
| return ( | |
| f"<div class='status-card {style}'>[{status}] {status_text}</div>", | |
| style, | |
| ) | |
| def build_metrics_html(report): | |
| dataset = report["dataset"]["name"] | |
| counts = report["issue_counts"] | |
| return ( | |
| "<div class='metrics-grid'>" | |
| f"<div class='metric'><div class='metric-label'>Dataset</div><div class='metric-value'>{dataset}</div></div>" | |
| f"<div class='metric'><div class='metric-label'>Total issues</div><div class='metric-value'>{counts['total']}</div></div>" | |
| f"<div class='metric'><div class='metric-label'>Errors</div><div class='metric-value'>{counts['errors']}</div></div>" | |
| f"<div class='metric'><div class='metric-label'>Warnings</div><div class='metric-value'>{counts['warnings']}</div></div>" | |
| f"<div class='metric'><div class='metric-label'>Infos</div><div class='metric-value'>{counts['infos']}</div></div>" | |
| "</div>" | |
| ) | |
| def empty_status_html(): | |
| return "<div class='status-card status-neutral'>No diagnostics run yet.</div>" | |
| def empty_details_markdown(): | |
| return "Run diagnostics to display issues and recommendations." | |
| def reset_diagnostics_state(): | |
| return ( | |
| empty_status_html(), | |
| "", | |
| empty_details_markdown(), | |
| "", | |
| None, | |
| None, | |
| None, | |
| ) | |
| def build_issue_markdown(report): | |
| grouped = {name: [] for name in CHECK_ORDER} | |
| grouped["other"] = [] | |
| for issue in report.get("issues", []): | |
| check_id = issue.get("check_id", "unknown") | |
| prefix = check_id.split(".", 1)[0] | |
| if prefix in grouped: | |
| grouped[prefix].append(issue) | |
| else: | |
| grouped["other"].append(issue) | |
| sections = [] | |
| for check_name in CHECK_ORDER + ["other"]: | |
| issues = grouped.get(check_name) or [] | |
| if not issues: | |
| continue | |
| title = CHECK_LABELS.get(check_name, "Other") | |
| sections.append(f"### {title}") | |
| for issue in issues[:12]: | |
| severity = str(issue.get("severity", "info")).upper() | |
| message = issue.get("message", "No message") | |
| suggestion = issue.get("suggestion") | |
| line = f"- **[{severity}]** {message}" | |
| if suggestion: | |
| line += f" \n Suggestion: {suggestion}" | |
| sections.append(line) | |
| if len(issues) > 12: | |
| sections.append(f"- ... {len(issues) - 12} additional issue(s)") | |
| sections.append("") | |
| if not sections: | |
| return "No issues found." | |
| return "\n".join(sections).strip() | |
| def build_runtime_config(selected_checks, max_episodes): | |
| if not selected_checks: | |
| raise ValueError("Select at least one check to run.") | |
| selected = set(selected_checks) | |
| lines = ["checks:"] | |
| for check_name in CHECK_ORDER: | |
| enabled = "true" if check_name in selected else "false" | |
| lines.append(f" {check_name}: {enabled}") | |
| if max_episodes is not None: | |
| parsed_max_episodes = int(max_episodes) | |
| if parsed_max_episodes < 0: | |
| raise ValueError("Max episodes must be 0 or a positive integer.") | |
| if parsed_max_episodes > 0: | |
| lines.append(f"max_episodes: {parsed_max_episodes}") | |
| return "\n".join(lines) + "\n" | |
| def selected_checks_from_flags(*flags): | |
| return [name for name, enabled in zip(CHECK_ORDER, flags) if enabled] | |
| def set_check_flags(mode): | |
| if mode == "all": | |
| return (True,) * len(CHECK_ORDER) | |
| if mode == "recommended": | |
| recommended = { | |
| "structure", | |
| "schema", | |
| "consistency", | |
| "episodes", | |
| "temporal", | |
| "numerical", | |
| "training", | |
| "portability", | |
| } | |
| return tuple(check in recommended for check in CHECK_ORDER) | |
| return (False,) * len(CHECK_ORDER) | |
| def run_leguard( | |
| dataset_repo_id, | |
| fail_on, | |
| max_episodes, | |
| check_structure, | |
| check_schema, | |
| check_consistency, | |
| check_episodes, | |
| check_temporal, | |
| check_numerical, | |
| check_video, | |
| check_annotation, | |
| check_training, | |
| check_portability, | |
| ): | |
| selected_checks = selected_checks_from_flags( | |
| check_structure, | |
| check_schema, | |
| check_consistency, | |
| check_episodes, | |
| check_temporal, | |
| check_numerical, | |
| check_video, | |
| check_annotation, | |
| check_training, | |
| check_portability, | |
| ) | |
| try: | |
| dataset_path = resolve_dataset_path(dataset_repo_id) | |
| runtime_config = build_runtime_config(selected_checks, max_episodes) | |
| except Exception as exc: | |
| return ( | |
| "<div class='status-card status-fail'>[FAIL] Unable to start diagnostics.</div>", | |
| "", | |
| f"### Error\n- {exc}", | |
| str(exc), | |
| None, | |
| None, | |
| None, | |
| ) | |
| run_id = uuid.uuid4().hex[:8] | |
| run_dir = RUNS_DIR / run_id | |
| run_dir.mkdir(parents=True, exist_ok=True) | |
| runtime_config_path = run_dir / "space-config.yml" | |
| runtime_config_path.write_text(runtime_config, encoding="utf-8") | |
| json_report = run_dir / "report.json" | |
| html_report = run_dir / "report.html" | |
| check_exit, check_logs = run_command( | |
| [ | |
| LEGUARD_BIN, | |
| "check", | |
| str(dataset_path), | |
| "--fail-on", | |
| fail_on, | |
| "--config", | |
| str(runtime_config_path), | |
| ] | |
| ) | |
| json_exit, json_logs = run_command( | |
| [ | |
| LEGUARD_BIN, | |
| "report", | |
| str(dataset_path), | |
| "--format", | |
| "json", | |
| "--out", | |
| str(json_report), | |
| "--config", | |
| str(runtime_config_path), | |
| ] | |
| ) | |
| html_exit, html_logs = run_command( | |
| [ | |
| LEGUARD_BIN, | |
| "report", | |
| str(dataset_path), | |
| "--format", | |
| "html", | |
| "--out", | |
| str(html_report), | |
| "--config", | |
| str(runtime_config_path), | |
| ] | |
| ) | |
| all_logs = "\n\n---\n\n".join([check_logs, json_logs, html_logs]) | |
| if json_exit != 0 or html_exit != 0 or not json_report.exists(): | |
| status_html = ( | |
| "<div class='status-card status-fail'>[FAIL] Report generation failed. " | |
| "Check command logs for details.</div>" | |
| ) | |
| details_md = ( | |
| "### Run failed\n" | |
| f"- Dataset path: `{dataset_path}`\n" | |
| "- `leguard report` did not produce the expected JSON/HTML outputs." | |
| ) | |
| return status_html, "", details_md, all_logs, None, None, None | |
| report = json.loads(json_report.read_text(encoding="utf-8")) | |
| status_html, _ = summarize_report(report, check_exit) | |
| metrics_html = build_metrics_html(report) | |
| issues_markdown = build_issue_markdown(report) | |
| return ( | |
| status_html, | |
| metrics_html, | |
| issues_markdown, | |
| all_logs, | |
| report, | |
| str(json_report), | |
| str(html_report), | |
| ) | |
| with gr.Blocks( | |
| title="LeGuard Space", | |
| css=CSS, | |
| theme=gr.themes.Soft(), | |
| js=BOOTSTRAP_JS, | |
| ) as demo: | |
| with gr.Column(elem_classes=["app-container"]): | |
| gr.HTML( | |
| f""" | |
| <div class="hero-card"> | |
| <img src="{BANNER_URL}" alt="LeGuard banner" /> | |
| <h1>LeGuard Space</h1> | |
| <p>Run LeGuard diagnostics on Hugging Face LeRobot datasets and export actionable reports.</p> | |
| <div class="hero-links"><a href="{REPO_URL}" target="_blank" rel="noopener noreferrer">GitHub</a></div> | |
| </div> | |
| """ | |
| ) | |
| with gr.Row(): | |
| dataset_repo_id = gr.Textbox( | |
| label="Dataset (Hugging Face repo id)", | |
| placeholder="praedico/SO101_pillbox_vita", | |
| scale=4, | |
| ) | |
| with gr.Column(scale=1): | |
| max_episodes = gr.Number( | |
| label="Max episodes (optional)", | |
| value=0, | |
| precision=0, | |
| minimum=0, | |
| ) | |
| gr.HTML("<div class='field-help'>Set 0 to validate all episodes.</div>") | |
| theme_mode = gr.Radio( | |
| [("Light", "light"), ("Dark", "dark")], | |
| value="light", | |
| label="Theme mode", | |
| scale=1, | |
| ) | |
| with gr.Accordion("Advanced: checks & policy", open=False): | |
| gr.Markdown("**Checks to run**") | |
| with gr.Row(): | |
| select_all_btn = gr.Button("Select all", variant="secondary", size="sm") | |
| select_recommended_btn = gr.Button("Recommended", variant="secondary", size="sm") | |
| clear_btn = gr.Button("Clear all", variant="secondary", size="sm") | |
| with gr.Row(): | |
| check_structure = gr.Checkbox(label="Structure", value=True) | |
| check_schema = gr.Checkbox(label="Schema", value=True) | |
| check_consistency = gr.Checkbox(label="Consistency", value=True) | |
| check_episodes = gr.Checkbox(label="Episodes", value=True) | |
| check_temporal = gr.Checkbox(label="Temporal", value=True) | |
| with gr.Row(): | |
| check_numerical = gr.Checkbox(label="Numerical", value=True) | |
| check_video = gr.Checkbox(label="Video", value=True) | |
| check_annotation = gr.Checkbox(label="Annotation", value=True) | |
| check_training = gr.Checkbox(label="Training readiness", value=True) | |
| check_portability = gr.Checkbox(label="Portability", value=True) | |
| fail_on = gr.Radio( | |
| ["error", "warning"], | |
| value="error", | |
| label="Fail policy", | |
| ) | |
| check_outputs = [ | |
| check_structure, | |
| check_schema, | |
| check_consistency, | |
| check_episodes, | |
| check_temporal, | |
| check_numerical, | |
| check_video, | |
| check_annotation, | |
| check_training, | |
| check_portability, | |
| ] | |
| select_all_btn.click(fn=lambda: set_check_flags("all"), outputs=check_outputs) | |
| select_recommended_btn.click( | |
| fn=lambda: set_check_flags("recommended"), | |
| outputs=check_outputs, | |
| ) | |
| clear_btn.click(fn=lambda: set_check_flags("none"), outputs=check_outputs) | |
| with gr.Row(): | |
| run_button = gr.Button( | |
| "Run LeGuard diagnostics", | |
| variant="primary", | |
| scale=6, | |
| elem_classes=["run-btn"], | |
| ) | |
| refresh_button = gr.Button( | |
| "Refresh", | |
| variant="secondary", | |
| scale=1, | |
| min_width=120, | |
| elem_classes=["refresh-btn"], | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=4): | |
| with gr.Tabs(): | |
| with gr.TabItem("Report"): | |
| status_output = gr.HTML() | |
| metrics_output = gr.HTML() | |
| details_output = gr.Markdown() | |
| with gr.TabItem("JSON"): | |
| json_view = gr.JSON(label="Validation report (JSON)") | |
| with gr.Column(scale=2, min_width=260): | |
| gr.HTML( | |
| f""" | |
| <div class="issues-card"> | |
| Issues or recommendations? Open an issue on | |
| <a href="{ISSUES_URL}" target="_blank" rel="noopener noreferrer">GitHub</a>. | |
| </div> | |
| """ | |
| ) | |
| with gr.Accordion("Command logs", open=False): | |
| logs_output = gr.Textbox(lines=16, show_copy_button=True) | |
| with gr.Row(): | |
| json_file_output = gr.File(label="Download JSON report") | |
| html_file_output = gr.File(label="Download HTML report") | |
| run_inputs = [ | |
| dataset_repo_id, | |
| fail_on, | |
| max_episodes, | |
| check_structure, | |
| check_schema, | |
| check_consistency, | |
| check_episodes, | |
| check_temporal, | |
| check_numerical, | |
| check_video, | |
| check_annotation, | |
| check_training, | |
| check_portability, | |
| ] | |
| run_outputs = [ | |
| status_output, | |
| metrics_output, | |
| details_output, | |
| logs_output, | |
| json_view, | |
| json_file_output, | |
| html_file_output, | |
| ] | |
| run_button.click( | |
| fn=run_leguard, | |
| inputs=run_inputs, | |
| outputs=run_outputs, | |
| ) | |
| refresh_button.click( | |
| fn=reset_diagnostics_state, | |
| inputs=[], | |
| outputs=run_outputs, | |
| ) | |
| theme_mode.change( | |
| fn=lambda _mode: None, | |
| inputs=[theme_mode], | |
| outputs=[], | |
| js=THEME_CHANGE_JS, | |
| ) | |
| if __name__ == "__main__": | |
| launch_kwargs = {"show_api": False} | |
| if os.environ.get("SPACE_ID"): | |
| # Hugging Face Spaces provides the runtime ingress. | |
| demo.queue().launch(**launch_kwargs) | |
| else: | |
| demo.queue().launch( | |
| server_name="0.0.0.0", | |
| server_port=int(os.environ.get("PORT", "7860")), | |
| **launch_kwargs, | |
| ) | |