from __future__ import annotations import json from pathlib import Path import gradio as gr from json_semval.pipeline import run_validation def load_fixtures() -> tuple[str, str]: try: schema = Path("tests/fixtures/sample_schema.json").read_text(encoding="utf-8") bad = Path("tests/fixtures/sample_bad.json").read_text(encoding="utf-8") return schema, bad except Exception: example_schema = '{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}' example_json = '{"name":"Alice"}' return example_schema, example_json def infer( schema_text: str, json_text: str, backend: str, apply_minimal: bool ) -> tuple[str, str, str]: try: schema = json.loads(schema_text) payload = json.loads(json_text) except Exception as e: return "[]", f"Invalid input JSON: {e}", json_text report = run_validation(schema, payload, apply_fixes=apply_minimal, backend=backend) rule_errors = json.dumps(report.get("rule_errors", []), indent=2) ml_preds = json.dumps(report.get("ml_predictions", []), indent=2) corrected = json.dumps(report.get("corrected_json", payload), indent=2) return rule_errors, ml_preds, corrected EXAMPLES = { "Example 1: Fixtures (bad)": ( ( Path("tests/fixtures/sample_schema.json").read_text(encoding="utf-8") if Path("tests/fixtures/sample_schema.json").exists() else '{"type":"object","properties":{"age":{"type":"integer"},"start_date":{"type":"string","format":"date"},"active":{"type":"boolean"},"status":{"type":"string","enum":["pending","approved","rejected"]}},"required":["age","start_date","active","status"]}' ), ( Path("tests/fixtures/sample_bad.json").read_text(encoding="utf-8") if Path("tests/fixtures/sample_bad.json").exists() else '{"age":"twenty five","active":"yes","start_date":"15 Jan 2024","status":"pendng"}' ), ), "Example 2: Minimal valid": ( '{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}', '{"name":"Alice"}', ), "Example 3: Enum/date mix": ( '{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","rejected"]},"d":{"type":"string","format":"date"}},"required":["status","d"]}', '{"status":"Pendng","d":"01/02/2024"}', ), } with gr.Blocks(title="JSON Semantic Validator") as demo: gr.Markdown( """ # JSON Semantic Validator Hybrid rules + tiny ML to validate and auto-fix JSON against a schema. """ ) with gr.Row(): schema_in = gr.Code(label="JSON Schema", language="json") json_in = gr.Code(label="JSON Payload", language="json") with gr.Row(): backend = gr.Dropdown( ["rules-only", "local", "onnx"], value="rules-only", label="Backend" ) apply_minimal = gr.Checkbox(value=True, label="Apply minimal fixes") example_dd = gr.Dropdown(list(EXAMPLES.keys()), label="Load Example") load_btn = gr.Button("Load Fixtures") run_btn = gr.Button("Run Validation") with gr.Row(): rule_errors_out = gr.Code(label="Rule Errors", language="json") ml_preds_out = gr.Code(label="ML Predictions", language="json") corrected_out = gr.Code(label="Corrected JSON", language="json") validity_md = gr.Markdown(visible=True) def _backend_value(b: str) -> str: if b == "rules-only": return "local" # we will set apply_minimal False when executing return b def on_load() -> tuple[str, str]: return load_fixtures() def on_example_select(label: str) -> tuple[str, str]: pair = EXAMPLES.get(label) if not pair: return "", "" return pair def on_run( schema_text: str, json_text: str, b: str, do_fix: bool ) -> tuple[str, str, str, str]: # Compute rules-only and hybrid validity to show a delta badge validity_text = "" try: schema = json.loads(schema_text) payload = json.loads(json_text) rules_only = run_validation( schema, payload, apply_fixes=False, backend="rules-only" ) hybrid = run_validation( schema, payload, apply_fixes=(False if b == "rules-only" else do_fix), backend=_backend_value(b), ) r_ok = 1 if rules_only.get("valid") else 0 h_ok = 1 if hybrid.get("valid") else 0 delta = (h_ok - r_ok) * 100 validity_text = f"**Validity** — Rules-only: {r_ok*100}% · Hybrid: {h_ok*100}% · Δ: {delta:+d}%" # Return details based on the hybrid run to keep UX consistent rule_errors = json.dumps(hybrid.get("rule_errors", []), indent=2) ml_preds = json.dumps(hybrid.get("ml_predictions", []), indent=2) corrected = json.dumps(hybrid.get("corrected_json", payload), indent=2) return rule_errors, ml_preds, corrected, validity_text except Exception as e: return "[]", f"Invalid input JSON: {e}", json_text, "" load_btn.click(fn=on_load, outputs=[schema_in, json_in]) example_dd.change( fn=on_example_select, inputs=[example_dd], outputs=[schema_in, json_in] ) run_btn.click( fn=on_run, inputs=[schema_in, json_in, backend, apply_minimal], outputs=[rule_errors_out, ml_preds_out, corrected_out, validity_md], ) # Disable the "Apply minimal fixes" checkbox when backend == rules-only def on_backend_change(b: str, current: bool): if b == "rules-only": return gr.update(value=False, interactive=False) return gr.update(interactive=True) backend.change( fn=on_backend_change, inputs=[backend, apply_minimal], outputs=apply_minimal ) if __name__ == "__main__": demo.launch(share=True)