thearnabsarkar commited on
Commit
3307548
·
verified ·
1 Parent(s): 11f1f42

Upload app.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. app.py +151 -0
app.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ import gradio as gr
7
+
8
+ from json_semval.pipeline import run_validation
9
+
10
+
11
+ def load_fixtures() -> tuple[str, str]:
12
+ try:
13
+ schema = Path("tests/fixtures/sample_schema.json").read_text(encoding="utf-8")
14
+ bad = Path("tests/fixtures/sample_bad.json").read_text(encoding="utf-8")
15
+ return schema, bad
16
+ except Exception:
17
+ example_schema = '{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}'
18
+ example_json = '{"name":"Alice"}'
19
+ return example_schema, example_json
20
+
21
+
22
+ def infer(
23
+ schema_text: str, json_text: str, backend: str, apply_minimal: bool
24
+ ) -> tuple[str, str, str]:
25
+ try:
26
+ schema = json.loads(schema_text)
27
+ payload = json.loads(json_text)
28
+ except Exception as e:
29
+ return "[]", f"Invalid input JSON: {e}", json_text
30
+ report = run_validation(schema, payload, apply_fixes=apply_minimal, backend=backend)
31
+ rule_errors = json.dumps(report.get("rule_errors", []), indent=2)
32
+ ml_preds = json.dumps(report.get("ml_predictions", []), indent=2)
33
+ corrected = json.dumps(report.get("corrected_json", payload), indent=2)
34
+ return rule_errors, ml_preds, corrected
35
+
36
+
37
+ EXAMPLES = {
38
+ "Example 1: Fixtures (bad)": (
39
+ (
40
+ Path("tests/fixtures/sample_schema.json").read_text(encoding="utf-8")
41
+ if Path("tests/fixtures/sample_schema.json").exists()
42
+ 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"]}'
43
+ ),
44
+ (
45
+ Path("tests/fixtures/sample_bad.json").read_text(encoding="utf-8")
46
+ if Path("tests/fixtures/sample_bad.json").exists()
47
+ else '{"age":"twenty five","active":"yes","start_date":"15 Jan 2024","status":"pendng"}'
48
+ ),
49
+ ),
50
+ "Example 2: Minimal valid": (
51
+ '{"type":"object","properties":{"name":{"type":"string"}},"required":["name"]}',
52
+ '{"name":"Alice"}',
53
+ ),
54
+ "Example 3: Enum/date mix": (
55
+ '{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","rejected"]},"d":{"type":"string","format":"date"}},"required":["status","d"]}',
56
+ '{"status":"Pendng","d":"01/02/2024"}',
57
+ ),
58
+ }
59
+
60
+ with gr.Blocks(title="JSON Semantic Validator") as demo:
61
+ gr.Markdown(
62
+ """
63
+ # JSON Semantic Validator
64
+ Hybrid rules + tiny ML to validate and auto-fix JSON against a schema.
65
+ """
66
+ )
67
+
68
+ with gr.Row():
69
+ schema_in = gr.Code(label="JSON Schema", language="json")
70
+ json_in = gr.Code(label="JSON Payload", language="json")
71
+
72
+ with gr.Row():
73
+ backend = gr.Dropdown(
74
+ ["rules-only", "local", "onnx"], value="rules-only", label="Backend"
75
+ )
76
+ apply_minimal = gr.Checkbox(value=True, label="Apply minimal fixes")
77
+ example_dd = gr.Dropdown(list(EXAMPLES.keys()), label="Load Example")
78
+ load_btn = gr.Button("Load Fixtures")
79
+ run_btn = gr.Button("Run Validation")
80
+
81
+ with gr.Row():
82
+ rule_errors_out = gr.Code(label="Rule Errors", language="json")
83
+ ml_preds_out = gr.Code(label="ML Predictions", language="json")
84
+ corrected_out = gr.Code(label="Corrected JSON", language="json")
85
+ validity_md = gr.Markdown(visible=True)
86
+
87
+ def _backend_value(b: str) -> str:
88
+ if b == "rules-only":
89
+ return "local" # we will set apply_minimal False when executing
90
+ return b
91
+
92
+ def on_load() -> tuple[str, str]:
93
+ return load_fixtures()
94
+
95
+ def on_example_select(label: str) -> tuple[str, str]:
96
+ pair = EXAMPLES.get(label)
97
+ if not pair:
98
+ return "", ""
99
+ return pair
100
+
101
+ def on_run(
102
+ schema_text: str, json_text: str, b: str, do_fix: bool
103
+ ) -> tuple[str, str, str, str]:
104
+ # Compute rules-only and hybrid validity to show a delta badge
105
+ validity_text = ""
106
+ try:
107
+ schema = json.loads(schema_text)
108
+ payload = json.loads(json_text)
109
+ rules_only = run_validation(
110
+ schema, payload, apply_fixes=False, backend="rules-only"
111
+ )
112
+ hybrid = run_validation(
113
+ schema,
114
+ payload,
115
+ apply_fixes=(False if b == "rules-only" else do_fix),
116
+ backend=_backend_value(b),
117
+ )
118
+ r_ok = 1 if rules_only.get("valid") else 0
119
+ h_ok = 1 if hybrid.get("valid") else 0
120
+ delta = (h_ok - r_ok) * 100
121
+ validity_text = f"**Validity** — Rules-only: {r_ok*100}% · Hybrid: {h_ok*100}% · Δ: {delta:+d}%"
122
+ # Return details based on the hybrid run to keep UX consistent
123
+ rule_errors = json.dumps(hybrid.get("rule_errors", []), indent=2)
124
+ ml_preds = json.dumps(hybrid.get("ml_predictions", []), indent=2)
125
+ corrected = json.dumps(hybrid.get("corrected_json", payload), indent=2)
126
+ return rule_errors, ml_preds, corrected, validity_text
127
+ except Exception as e:
128
+ return "[]", f"Invalid input JSON: {e}", json_text, ""
129
+
130
+ load_btn.click(fn=on_load, outputs=[schema_in, json_in])
131
+ example_dd.change(
132
+ fn=on_example_select, inputs=[example_dd], outputs=[schema_in, json_in]
133
+ )
134
+ run_btn.click(
135
+ fn=on_run,
136
+ inputs=[schema_in, json_in, backend, apply_minimal],
137
+ outputs=[rule_errors_out, ml_preds_out, corrected_out, validity_md],
138
+ )
139
+
140
+ # Disable the "Apply minimal fixes" checkbox when backend == rules-only
141
+ def on_backend_change(b: str, current: bool):
142
+ if b == "rules-only":
143
+ return gr.update(value=False, interactive=False)
144
+ return gr.update(interactive=True)
145
+
146
+ backend.change(
147
+ fn=on_backend_change, inputs=[backend, apply_minimal], outputs=apply_minimal
148
+ )
149
+
150
+ if __name__ == "__main__":
151
+ demo.launch(share=True)