youssefreda9 commited on
Commit
ebefc09
·
1 Parent(s): ec36a6d

Phase A: Upgrade L1 with TP/FP/FN/TN verdicts + word edits, add F1 scores to matrix

Browse files
tests/v2/benchmark_matrix.py CHANGED
@@ -62,6 +62,22 @@ def load_report(level: int) -> dict:
62
  return None
63
 
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
  def print_comparison_matrix():
66
  """Load all 3 reports and print side-by-side comparison."""
67
  l1 = load_report(1)
@@ -72,27 +88,29 @@ def print_comparison_matrix():
72
  print("BAYAN v2.0 — BENCHMARK COMPARISON MATRIX")
73
  print(f"{'='*70}")
74
 
75
- # Timestamps
76
  for level, report, name in [(1, l1, "L1-Raw"), (2, l2, "L2-Solo"), (3, l3, "L3-Pipeline")]:
77
  if report:
78
  ts = report.get("timestamp", "N/A")
79
- print(f" {name}: {ts}")
 
80
  else:
81
  print(f" {name}: NOT RUN")
82
 
83
  # ── Level 1 Summary ──
84
  if l1:
85
  print(f"\n{'─'*70}")
86
- print("Level 1: RAW MODEL OUTPUT (no filters)")
87
  print(f"{'─'*70}")
88
  a = l1.get("analysis", {})
89
  by_model = a.get("by_model", {})
 
 
90
  for model, data in by_model.items():
91
- changed = data.get("changed", 0)
92
- unchanged = data.get("unchanged", 0)
93
- total = changed + unchanged + data.get("errors", 0)
94
- rate = changed / total * 100 if total else 0
95
- print(f" {model:<12}: {changed}/{total} texts modified ({rate:.1f}%)")
96
 
97
  # ── Level 2 Summary ──
98
  if l2:
@@ -101,12 +119,13 @@ def print_comparison_matrix():
101
  print(f"{'─'*70}")
102
  a = l2.get("analysis", {})
103
  by_model = a.get("by_model", {})
104
- print(f" {'Model':<12} {'TP':>4} {'TN':>4} {'FP':>4} {'FN':>4} {'Pass%':>7}")
105
- print(f" {'-'*12} {'-'*4} {'-'*4} {'-'*4} {'-'*4} {'-'*7}")
106
  for model, data in by_model.items():
 
107
  pr = data.get("pass_rate", 0) * 100
108
- print(f" {model:<12} {data.get('TP',0):>4} {data.get('TN',0):>4} "
109
- f"{data.get('FP',0):>4} {data.get('FN',0):>4} {pr:>6.1f}%")
110
 
111
  # ── Level 3 Summary ──
112
  if l3:
@@ -117,32 +136,50 @@ def print_comparison_matrix():
117
  agg = a.get("aggregate", {})
118
  total = a.get("total", 0)
119
  pr = agg.get("pass_rate", 0) * 100
 
 
120
  print(f" Overall: {pr:.1f}% pass ({total} tests)")
121
- print(f" TP={agg.get('TP',0)} TN={agg.get('TN',0)} "
122
- f"FP={agg.get('FP',0)} FN={agg.get('FN',0)}")
123
 
124
- print(f"\n {'Dataset':<14} {'Pass%':>7} {'TP':>4} {'TN':>4} {'FP':>4} {'FN':>4}")
125
- print(f" {'-'*14} {'-'*7} {'-'*4} {'-'*4} {'-'*4} {'-'*4}")
126
  by_ds = a.get("by_dataset", {})
127
  for ds in sorted(by_ds.keys()):
128
  d = by_ds[ds]
129
  dp = d.get("pass_rate", 0) * 100
130
- print(f" {ds:<14} {dp:>6.1f}% {d.get('TP',0):>4} {d.get('TN',0):>4} "
131
- f"{d.get('FP',0):>4} {d.get('FN',0):>4}")
 
132
 
133
  # ── Cross-Level Comparison ──
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  if l2 and l3:
135
  print(f"\n{'─'*70}")
136
- print("CROSS-LEVEL COMPARISON: Solo vs Integrated")
137
  print(f"{'─'*70}")
138
 
139
  l2_ds = l2.get("analysis", {}).get("by_dataset", {})
140
  l3_ds = l3.get("analysis", {}).get("by_dataset", {})
141
-
142
  all_datasets = sorted(set(list(l2_ds.keys()) + list(l3_ds.keys())))
143
 
144
- print(f" {'Dataset':<14} {'L2-Spell':>10} {'L2-Gram':>10} {'L2-Punct':>10} {'L3-Pipeline':>12} {'Delta':>7}")
145
- print(f" {'-'*14} {'-'*10} {'-'*10} {'-'*10} {'-'*12} {'-'*7}")
146
 
147
  for ds in all_datasets:
148
  l2d = l2_ds.get(ds, {})
@@ -152,13 +189,9 @@ def print_comparison_matrix():
152
  l2_g = l2d.get("grammar", {}).get("pass_rate", 0) * 100
153
  l2_p = l2d.get("punctuation", {}).get("pass_rate", 0) * 100
154
  l3_p = l3d.get("pass_rate", 0) * 100
155
-
156
- # Best solo model vs pipeline
157
  best_solo = max(l2_s, l2_g, l2_p)
158
  delta = l3_p - best_solo
159
-
160
- delta_str = f"{delta:+.1f}%"
161
- print(f" {ds:<14} {l2_s:>9.1f}% {l2_g:>9.1f}% {l2_p:>9.1f}% {l3_p:>11.1f}% {delta_str:>7}")
162
 
163
  # Save comparison
164
  comparison = {
@@ -166,6 +199,7 @@ def print_comparison_matrix():
166
  "l1_timestamp": l1.get("timestamp") if l1 else None,
167
  "l2_timestamp": l2.get("timestamp") if l2 else None,
168
  "l3_timestamp": l3.get("timestamp") if l3 else None,
 
169
  "l2_summary": l2.get("analysis", {}).get("by_model") if l2 else None,
170
  "l3_summary": l3.get("analysis", {}).get("aggregate") if l3 else None,
171
  }
 
62
  return None
63
 
64
 
65
+ def _compute_f1(tp, fp, fn):
66
+ """Compute F1 score from TP/FP/FN."""
67
+ precision = tp / (tp + fp) if (tp + fp) > 0 else 0
68
+ recall = tp / (tp + fn) if (tp + fn) > 0 else 0
69
+ f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0
70
+ return round(f1, 4), round(precision, 4), round(recall, 4)
71
+
72
+
73
+ def _extract_model_metrics(report, model_key):
74
+ """Extract TP/TN/FP/FN for a model from a report."""
75
+ a = report.get("analysis", {})
76
+ by_model = a.get("by_model", {})
77
+ data = by_model.get(model_key, {})
78
+ return data.get("TP", 0), data.get("TN", 0), data.get("FP", 0), data.get("FN", 0)
79
+
80
+
81
  def print_comparison_matrix():
82
  """Load all 3 reports and print side-by-side comparison."""
83
  l1 = load_report(1)
 
88
  print("BAYAN v2.0 — BENCHMARK COMPARISON MATRIX")
89
  print(f"{'='*70}")
90
 
 
91
  for level, report, name in [(1, l1, "L1-Raw"), (2, l2, "L2-Solo"), (3, l3, "L3-Pipeline")]:
92
  if report:
93
  ts = report.get("timestamp", "N/A")
94
+ total = report.get("analysis", {}).get("total", "?")
95
+ print(f" {name}: {ts} ({total} tests)")
96
  else:
97
  print(f" {name}: NOT RUN")
98
 
99
  # ── Level 1 Summary ──
100
  if l1:
101
  print(f"\n{'─'*70}")
102
+ print("Level 1: RAW MODEL OUTPUT (through solo API endpoints)")
103
  print(f"{'─'*70}")
104
  a = l1.get("analysis", {})
105
  by_model = a.get("by_model", {})
106
+ print(f" {'Model':<12} {'TP':>4} {'TN':>4} {'FP':>4} {'FN':>4} {'Pass%':>7} {'ChgRate':>8} {'F1':>6}")
107
+ print(f" {'-'*12} {'-'*4} {'-'*4} {'-'*4} {'-'*4} {'-'*7} {'-'*8} {'-'*6}")
108
  for model, data in by_model.items():
109
+ tp, tn, fp, fn = data.get("TP",0), data.get("TN",0), data.get("FP",0), data.get("FN",0)
110
+ pr = data.get("pass_rate", 0) * 100
111
+ cr = data.get("change_rate", 0) * 100
112
+ f1, _, _ = _compute_f1(tp, fp, fn)
113
+ print(f" {model:<12} {tp:>4} {tn:>4} {fp:>4} {fn:>4} {pr:>6.1f}% {cr:>6.1f}% {f1:.3f}")
114
 
115
  # ── Level 2 Summary ──
116
  if l2:
 
119
  print(f"{'─'*70}")
120
  a = l2.get("analysis", {})
121
  by_model = a.get("by_model", {})
122
+ print(f" {'Model':<12} {'TP':>4} {'TN':>4} {'FP':>4} {'FN':>4} {'Pass%':>7} {'F1':>6}")
123
+ print(f" {'-'*12} {'-'*4} {'-'*4} {'-'*4} {'-'*4} {'-'*7} {'-'*6}")
124
  for model, data in by_model.items():
125
+ tp, tn, fp, fn = data.get("TP",0), data.get("TN",0), data.get("FP",0), data.get("FN",0)
126
  pr = data.get("pass_rate", 0) * 100
127
+ f1, _, _ = _compute_f1(tp, fp, fn)
128
+ print(f" {model:<12} {tp:>4} {tn:>4} {fp:>4} {fn:>4} {pr:>6.1f}% {f1:.3f}")
129
 
130
  # ── Level 3 Summary ──
131
  if l3:
 
136
  agg = a.get("aggregate", {})
137
  total = a.get("total", 0)
138
  pr = agg.get("pass_rate", 0) * 100
139
+ tp, tn, fp, fn = agg.get("TP",0), agg.get("TN",0), agg.get("FP",0), agg.get("FN",0)
140
+ f1, prec, rec = _compute_f1(tp, fp, fn)
141
  print(f" Overall: {pr:.1f}% pass ({total} tests)")
142
+ print(f" TP={tp} TN={tn} FP={fp} FN={fn}")
143
+ print(f" F1={f1:.3f} Precision={prec:.3f} Recall={rec:.3f}")
144
 
145
+ print(f"\n {'Dataset':<14} {'Pass%':>7} {'TP':>4} {'TN':>4} {'FP':>4} {'FN':>4} {'F1':>6}")
146
+ print(f" {'-'*14} {'-'*7} {'-'*4} {'-'*4} {'-'*4} {'-'*4} {'-'*6}")
147
  by_ds = a.get("by_dataset", {})
148
  for ds in sorted(by_ds.keys()):
149
  d = by_ds[ds]
150
  dp = d.get("pass_rate", 0) * 100
151
+ dtp, dtn, dfp, dfn = d.get("TP",0), d.get("TN",0), d.get("FP",0), d.get("FN",0)
152
+ df1, _, _ = _compute_f1(dtp, dfp, dfn)
153
+ print(f" {ds:<14} {dp:>6.1f}% {dtp:>4} {dtn:>4} {dfp:>4} {dfn:>4} {df1:.3f}")
154
 
155
  # ── Cross-Level Comparison ──
156
+ if l1 and l2:
157
+ print(f"\n{'─'*70}")
158
+ print("CROSS-LEVEL: L1 (Raw) vs L2 (Solo) — Filter Impact")
159
+ print(f"{'─'*70}")
160
+ print(f" {'Model':<12} {'L1-Pass%':>9} {'L2-Pass%':>9} {'Delta':>7} {'L1-F1':>7} {'L2-F1':>7}")
161
+ print(f" {'-'*12} {'-'*9} {'-'*9} {'-'*7} {'-'*7} {'-'*7}")
162
+ for model in ("spelling", "grammar", "punctuation"):
163
+ l1m = l1.get("analysis",{}).get("by_model",{}).get(model,{})
164
+ l2m = l2.get("analysis",{}).get("by_model",{}).get(model,{})
165
+ l1p = l1m.get("pass_rate",0) * 100
166
+ l2p = l2m.get("pass_rate",0) * 100
167
+ delta = l2p - l1p
168
+ l1f1, _, _ = _compute_f1(l1m.get("TP",0), l1m.get("FP",0), l1m.get("FN",0))
169
+ l2f1, _, _ = _compute_f1(l2m.get("TP",0), l2m.get("FP",0), l2m.get("FN",0))
170
+ print(f" {model:<12} {l1p:>8.1f}% {l2p:>8.1f}% {delta:>+6.1f}% {l1f1:>6.3f} {l2f1:>6.3f}")
171
+
172
  if l2 and l3:
173
  print(f"\n{'─'*70}")
174
+ print("CROSS-LEVEL: L2 (Solo) vs L3 (Pipeline) — Integration Impact")
175
  print(f"{'─'*70}")
176
 
177
  l2_ds = l2.get("analysis", {}).get("by_dataset", {})
178
  l3_ds = l3.get("analysis", {}).get("by_dataset", {})
 
179
  all_datasets = sorted(set(list(l2_ds.keys()) + list(l3_ds.keys())))
180
 
181
+ print(f" {'Dataset':<14} {'L2-Best':>8} {'L3-Pipe':>8} {'Delta':>7}")
182
+ print(f" {'-'*14} {'-'*8} {'-'*8} {'-'*7}")
183
 
184
  for ds in all_datasets:
185
  l2d = l2_ds.get(ds, {})
 
189
  l2_g = l2d.get("grammar", {}).get("pass_rate", 0) * 100
190
  l2_p = l2d.get("punctuation", {}).get("pass_rate", 0) * 100
191
  l3_p = l3d.get("pass_rate", 0) * 100
 
 
192
  best_solo = max(l2_s, l2_g, l2_p)
193
  delta = l3_p - best_solo
194
+ print(f" {ds:<14} {best_solo:>7.1f}% {l3_p:>7.1f}% {delta:>+6.1f}%")
 
 
195
 
196
  # Save comparison
197
  comparison = {
 
199
  "l1_timestamp": l1.get("timestamp") if l1 else None,
200
  "l2_timestamp": l2.get("timestamp") if l2 else None,
201
  "l3_timestamp": l3.get("timestamp") if l3 else None,
202
+ "l1_summary": l1.get("analysis", {}).get("by_model") if l1 else None,
203
  "l2_summary": l2.get("analysis", {}).get("by_model") if l2 else None,
204
  "l3_summary": l3.get("analysis", {}).get("aggregate") if l3 else None,
205
  }
tests/v2/reports/benchmark_matrix.json CHANGED
@@ -1,8 +1,8 @@
1
  {
2
- "timestamp": "2026-06-23T18:55:12Z",
3
- "l1_timestamp": null,
4
  "l2_timestamp": "2026-06-23T18:55:12Z",
5
- "l3_timestamp": "2026-06-23T18:54:52Z",
6
  "l2_summary": {
7
  "spelling": {
8
  "TP": 0,
@@ -27,10 +27,10 @@
27
  }
28
  },
29
  "l3_summary": {
30
- "TP": 22,
31
- "TN": 0,
32
- "FP": 58,
33
  "FN": 0,
34
- "pass_rate": 0.275
35
  }
36
  }
 
1
  {
2
+ "timestamp": "2026-06-23T19:01:36Z",
3
+ "l1_timestamp": "2026-06-23T19:01:36Z",
4
  "l2_timestamp": "2026-06-23T18:55:12Z",
5
+ "l3_timestamp": "2026-06-23T19:00:34Z",
6
  "l2_summary": {
7
  "spelling": {
8
  "TP": 0,
 
27
  }
28
  },
29
  "l3_summary": {
30
+ "TP": 49,
31
+ "TN": 17,
32
+ "FP": 14,
33
  "FN": 0,
34
+ "pass_rate": 0.825
35
  }
36
  }
tests/v2/reports/level1_raw_results.json ADDED
@@ -0,0 +1,544 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "timestamp": "2026-06-23T19:01:36Z",
3
+ "target": "https://bayan10-bayan-api.hf.space",
4
+ "analysis": {
5
+ "total": 30,
6
+ "by_model": {
7
+ "spelling": {
8
+ "changed": 11,
9
+ "unchanged": 19,
10
+ "errors": 0
11
+ },
12
+ "grammar": {
13
+ "changed": 5,
14
+ "unchanged": 25,
15
+ "errors": 0
16
+ },
17
+ "punctuation": {
18
+ "changed": 29,
19
+ "unchanged": 1,
20
+ "errors": 0
21
+ }
22
+ },
23
+ "by_dataset": {
24
+ "entities": {
25
+ "total": 30,
26
+ "spelling_changed": 11,
27
+ "grammar_changed": 5,
28
+ "punctuation_changed": 29
29
+ }
30
+ }
31
+ },
32
+ "results": [
33
+ {
34
+ "id": "E001",
35
+ "dataset": "entities",
36
+ "category": "person",
37
+ "input_text": "محمد صلاح لاعب كرة قدم مصري",
38
+ "expected": "محمد صلاح لاعب كرة قدم مصري",
39
+ "severity": "major",
40
+ "spelling_raw": "محمد صلاح لاعب كرة قدم مصري",
41
+ "spelling_ms": 3171,
42
+ "grammar_raw": "محمد صلاح لاعب كرة قدم مصري",
43
+ "grammar_ms": 4260,
44
+ "punctuation_raw": "محمد صلاح لاعب كرة قدم مصري.",
45
+ "punctuation_ms": 4161,
46
+ "spelling_changed": false,
47
+ "grammar_changed": false,
48
+ "punctuation_changed": true
49
+ },
50
+ {
51
+ "id": "E002",
52
+ "dataset": "entities",
53
+ "category": "person",
54
+ "input_text": "عبدالله يدرس في الجامعة",
55
+ "expected": "عبدالله يدرس في الجامعة",
56
+ "severity": "major",
57
+ "spelling_raw": "عبد الله يدرس في الجامعة",
58
+ "spelling_ms": 3213,
59
+ "grammar_raw": "عبدالله يدرس في الجامعة",
60
+ "grammar_ms": 3791,
61
+ "punctuation_raw": "عبدالله يدرس في الجامعة.",
62
+ "punctuation_ms": 3398,
63
+ "spelling_changed": true,
64
+ "grammar_changed": false,
65
+ "punctuation_changed": true
66
+ },
67
+ {
68
+ "id": "E003",
69
+ "dataset": "entities",
70
+ "category": "person",
71
+ "input_text": "عبد الرحمن أخي الأكبر",
72
+ "expected": "عبد الرحمن أخي الأكبر",
73
+ "severity": "major",
74
+ "spelling_raw": "عبد الرحمن أخي الأكبر",
75
+ "spelling_ms": 3931,
76
+ "grammar_raw": "عبد الرحمن أخي الأكبر",
77
+ "grammar_ms": 4482,
78
+ "punctuation_raw": "عبد الرحمن أخي الأكبر.",
79
+ "punctuation_ms": 3616,
80
+ "spelling_changed": false,
81
+ "grammar_changed": false,
82
+ "punctuation_changed": true
83
+ },
84
+ {
85
+ "id": "E004",
86
+ "dataset": "entities",
87
+ "category": "person",
88
+ "input_text": "أحمد محمود يعمل مهندساً",
89
+ "expected": "أحمد محمود يعمل مهندساً",
90
+ "severity": "major",
91
+ "spelling_raw": "أحمد محمود يعمل مهندسا",
92
+ "spelling_ms": 5051,
93
+ "grammar_raw": "أحمد محمود يعمل مهندسا",
94
+ "grammar_ms": 3249,
95
+ "punctuation_raw": "أحمد محمود يعمل مهندساً.",
96
+ "punctuation_ms": 3381,
97
+ "spelling_changed": false,
98
+ "grammar_changed": false,
99
+ "punctuation_changed": true
100
+ },
101
+ {
102
+ "id": "E005",
103
+ "dataset": "entities",
104
+ "category": "person",
105
+ "input_text": "الدكتور حسن علي أستاذ جامعي",
106
+ "expected": "الدكتور حسن علي أستاذ جامعي",
107
+ "severity": "major",
108
+ "spelling_raw": "الدكتور حسن على أستاذ جامعي",
109
+ "spelling_ms": 6359,
110
+ "grammar_raw": "الدكتور حسن علي أستاذ جامعي",
111
+ "grammar_ms": 3254,
112
+ "punctuation_raw": "الدكتور حسن علي أستاذ جامعي.",
113
+ "punctuation_ms": 4454,
114
+ "spelling_changed": true,
115
+ "grammar_changed": false,
116
+ "punctuation_changed": true
117
+ },
118
+ {
119
+ "id": "E006",
120
+ "dataset": "entities",
121
+ "category": "person",
122
+ "input_text": "السيدة فاطمة الزهراء معلمة",
123
+ "expected": "السيدة فاطمة الزهراء معلمة",
124
+ "severity": "major",
125
+ "spelling_raw": "السيدة فاطمة الزهراء معلمة",
126
+ "spelling_ms": 6272,
127
+ "grammar_raw": "السيدة فاطمة الزهراء معلمة",
128
+ "grammar_ms": 4581,
129
+ "punctuation_raw": "السيدة فاطمة الزهراء معلمة.",
130
+ "punctuation_ms": 4866,
131
+ "spelling_changed": false,
132
+ "grammar_changed": false,
133
+ "punctuation_changed": true
134
+ },
135
+ {
136
+ "id": "E007",
137
+ "dataset": "entities",
138
+ "category": "person",
139
+ "input_text": "الأستاذ عمر بن الخطاب عادل",
140
+ "expected": "الأستاذ عمر بن الخطاب عادل",
141
+ "severity": "major",
142
+ "spelling_raw": "الأستاذ عمر بن الخطاب عادل",
143
+ "spelling_ms": 2516,
144
+ "grammar_raw": "الأستاذ عمر بن الخطاب عادل",
145
+ "grammar_ms": 2612,
146
+ "punctuation_raw": "الأستاذ عمر بن الخطاب عادل.",
147
+ "punctuation_ms": 751,
148
+ "spelling_changed": false,
149
+ "grammar_changed": false,
150
+ "punctuation_changed": true
151
+ },
152
+ {
153
+ "id": "E008",
154
+ "dataset": "entities",
155
+ "category": "person",
156
+ "input_text": "خالد بن الوليد قائد عظيم",
157
+ "expected": "خالد بن الوليد قائد عظيم",
158
+ "severity": "major",
159
+ "spelling_raw": "خالد بن الوليد قائد عظيم",
160
+ "spelling_ms": 815,
161
+ "grammar_raw": "خالد بن الوليد قائد عظيم",
162
+ "grammar_ms": 858,
163
+ "punctuation_raw": "خالد بن الوليد قائد عظيم.",
164
+ "punctuation_ms": 665,
165
+ "spelling_changed": false,
166
+ "grammar_changed": false,
167
+ "punctuation_changed": true
168
+ },
169
+ {
170
+ "id": "E009",
171
+ "dataset": "entities",
172
+ "category": "person",
173
+ "input_text": "صلاح الدين الأيوبي حرر القدس",
174
+ "expected": "صلاح الدين الأيوبي حرر القدس",
175
+ "severity": "major",
176
+ "spelling_raw": "صلاح الدين الأيوبي حر القدس",
177
+ "spelling_ms": 743,
178
+ "grammar_raw": "صلاح الدين الأيوبي حرر القدس",
179
+ "grammar_ms": 852,
180
+ "punctuation_raw": "صلاح الدين الأيوبي حرر القدس.",
181
+ "punctuation_ms": 810,
182
+ "spelling_changed": true,
183
+ "grammar_changed": false,
184
+ "punctuation_changed": true
185
+ },
186
+ {
187
+ "id": "E010",
188
+ "dataset": "entities",
189
+ "category": "person",
190
+ "input_text": "ابن سينا عالم عربي مشهور",
191
+ "expected": "ابن سينا عالم عربي مشهور",
192
+ "severity": "major",
193
+ "spelling_raw": "ابن سينا عالم عربي مشهور",
194
+ "spelling_ms": 763,
195
+ "grammar_raw": "ابن سينا عالم عربي مشهور",
196
+ "grammar_ms": 887,
197
+ "punctuation_raw": "ابن سينا عالم عربي مشهور.",
198
+ "punctuation_ms": 644,
199
+ "spelling_changed": false,
200
+ "grammar_changed": false,
201
+ "punctuation_changed": true
202
+ },
203
+ {
204
+ "id": "E011",
205
+ "dataset": "entities",
206
+ "category": "place",
207
+ "input_text": "جامعة القاهرة من أعرق الجامعات",
208
+ "expected": "جامعة القاهرة من أعرق الجامعات",
209
+ "severity": "major",
210
+ "spelling_raw": "جامعة القاهرة من أعمق الجامعات",
211
+ "spelling_ms": 911,
212
+ "grammar_raw": "جامعة القاهرة من أعرق الجامعات",
213
+ "grammar_ms": 909,
214
+ "punctuation_raw": "جامعة القاهرة من أعرق الجامعات.",
215
+ "punctuation_ms": 869,
216
+ "spelling_changed": true,
217
+ "grammar_changed": false,
218
+ "punctuation_changed": true
219
+ },
220
+ {
221
+ "id": "E012",
222
+ "dataset": "entities",
223
+ "category": "place",
224
+ "input_text": "مدينة الرياض عاصمة المملكة",
225
+ "expected": "مدينة الرياض عاصمة المملكة",
226
+ "severity": "major",
227
+ "spelling_raw": "مدينة الرياض عاصمة المملكة",
228
+ "spelling_ms": 808,
229
+ "grammar_raw": "مدينة الرياض عاصمة المملكة",
230
+ "grammar_ms": 784,
231
+ "punctuation_raw": "مدينة الرياض عاصمة المملكة.",
232
+ "punctuation_ms": 767,
233
+ "spelling_changed": false,
234
+ "grammar_changed": false,
235
+ "punctuation_changed": true
236
+ },
237
+ {
238
+ "id": "E013",
239
+ "dataset": "entities",
240
+ "category": "place",
241
+ "input_text": "دبي مدينة عصرية وجميلة",
242
+ "expected": "دبي مدينة عصرية وجميلة",
243
+ "severity": "major",
244
+ "spelling_raw": "دبي مدينة عصرية وجميلة",
245
+ "spelling_ms": 958,
246
+ "grammar_raw": "دبي مدينة عصرية وجميلة",
247
+ "grammar_ms": 808,
248
+ "punctuation_raw": "دبي مدينة عصرية وجميلة.",
249
+ "punctuation_ms": 701,
250
+ "spelling_changed": false,
251
+ "grammar_changed": false,
252
+ "punctuation_changed": true
253
+ },
254
+ {
255
+ "id": "E014",
256
+ "dataset": "entities",
257
+ "category": "place",
258
+ "input_text": "القدس مدينة مقدسة عند المسلمين",
259
+ "expected": "القدس مدينة مقدسة عند المسلمين",
260
+ "severity": "major",
261
+ "spelling_raw": "القدس مدينة مقدسة عند المسلمين",
262
+ "spelling_ms": 786,
263
+ "grammar_raw": "القدس مدينة مقدسة عند المسلمين",
264
+ "grammar_ms": 840,
265
+ "punctuation_raw": "القدس مدينة مقدسة عند المسلمين.",
266
+ "punctuation_ms": 825,
267
+ "spelling_changed": false,
268
+ "grammar_changed": false,
269
+ "punctuation_changed": true
270
+ },
271
+ {
272
+ "id": "E015",
273
+ "dataset": "entities",
274
+ "category": "place",
275
+ "input_text": "مكة المكرمة أطهر البقاع",
276
+ "expected": "مكة المكرمة أطهر البقاع",
277
+ "severity": "major",
278
+ "spelling_raw": "مكة المكرمة أطهر البقاع",
279
+ "spelling_ms": 838,
280
+ "grammar_raw": "مكة المكرمة أطهر البقاع",
281
+ "grammar_ms": 797,
282
+ "punctuation_raw": "مكة المكرمة أطهر البقاع.",
283
+ "punctuation_ms": 778,
284
+ "spelling_changed": false,
285
+ "grammar_changed": false,
286
+ "punctuation_changed": true
287
+ },
288
+ {
289
+ "id": "E016",
290
+ "dataset": "entities",
291
+ "category": "place",
292
+ "input_text": "المدينة المنورة طابة الطيبة",
293
+ "expected": "المدينة المنورة طابة الطيبة",
294
+ "severity": "major",
295
+ "spelling_raw": "المدينة المنورة الطيبة",
296
+ "spelling_ms": 689,
297
+ "grammar_raw": "المدينة المنورة طابة طيبة",
298
+ "grammar_ms": 810,
299
+ "punctuation_raw": "المدينة المنورة طابة الطيبة.",
300
+ "punctuation_ms": 826,
301
+ "spelling_changed": true,
302
+ "grammar_changed": true,
303
+ "punctuation_changed": true
304
+ },
305
+ {
306
+ "id": "E017",
307
+ "dataset": "entities",
308
+ "category": "place",
309
+ "input_text": "بغداد عاصمة العراق",
310
+ "expected": "بغداد عاصمة العراق",
311
+ "severity": "major",
312
+ "spelling_raw": "بغداد عاصمة العراق",
313
+ "spelling_ms": 715,
314
+ "grammar_raw": "بغداد عاصمة العراق",
315
+ "grammar_ms": 651,
316
+ "punctuation_raw": "بغداد عاصمة العراق.",
317
+ "punctuation_ms": 696,
318
+ "spelling_changed": false,
319
+ "grammar_changed": false,
320
+ "punctuation_changed": true
321
+ },
322
+ {
323
+ "id": "E018",
324
+ "dataset": "entities",
325
+ "category": "place",
326
+ "input_text": "دمشق أقدم عاصمة في التاريخ",
327
+ "expected": "دمشق أقدم عاصمة في التاريخ",
328
+ "severity": "major",
329
+ "spelling_raw": "دمشق أقدم عاصمة في التاريخ",
330
+ "spelling_ms": 783,
331
+ "grammar_raw": "دمشق أقدم عاصمة في التاريخ",
332
+ "grammar_ms": 796,
333
+ "punctuation_raw": "دمشق أقدم عاصمة في التاريخ.",
334
+ "punctuation_ms": 669,
335
+ "spelling_changed": false,
336
+ "grammar_changed": false,
337
+ "punctuation_changed": true
338
+ },
339
+ {
340
+ "id": "E019",
341
+ "dataset": "entities",
342
+ "category": "company",
343
+ "input_text": "شركة OpenAI تطور الذكاء الاصطناعي",
344
+ "expected": "شركة OpenAI تطور الذكاء الاصطناعي",
345
+ "severity": "major",
346
+ "spelling_raw": "شركة OpenAI تطور الذكاء الاصطناعي",
347
+ "spelling_ms": 1480,
348
+ "grammar_raw": "شركة OpenAI تطور الذكاء الاصطناعي",
349
+ "grammar_ms": 860,
350
+ "punctuation_raw": "شركة OpenAI تطور الذكاء الاصطناعي",
351
+ "punctuation_ms": 798,
352
+ "spelling_changed": false,
353
+ "grammar_changed": false,
354
+ "punctuation_changed": false
355
+ },
356
+ {
357
+ "id": "E020",
358
+ "dataset": "entities",
359
+ "category": "company",
360
+ "input_text": "شركة Google عملاق التقنية",
361
+ "expected": "شركة Google عملاق التقنية",
362
+ "severity": "major",
363
+ "spelling_raw": "شركة جوجل جوجل عملاق التقنية",
364
+ "spelling_ms": 994,
365
+ "grammar_raw": "شركة Google عملاق التقنية",
366
+ "grammar_ms": 751,
367
+ "punctuation_raw": "شركة Google عملاق التقنية.",
368
+ "punctuation_ms": 623,
369
+ "spelling_changed": true,
370
+ "grammar_changed": false,
371
+ "punctuation_changed": true
372
+ },
373
+ {
374
+ "id": "E021",
375
+ "dataset": "entities",
376
+ "category": "company",
377
+ "input_text": "شركة Microsoft تنتج البرمجيات",
378
+ "expected": "شركة Microsoft تنتج البرمجيات",
379
+ "severity": "major",
380
+ "spelling_raw": "شركة مايكروسوفت تنتج البرمجيات",
381
+ "spelling_ms": 1041,
382
+ "grammar_raw": "شركة Microsoft تنتج البرمجيات",
383
+ "grammar_ms": 700,
384
+ "punctuation_raw": "شركة Microsoft تنتج البرمجيات.",
385
+ "punctuation_ms": 812,
386
+ "spelling_changed": true,
387
+ "grammar_changed": false,
388
+ "punctuation_changed": true
389
+ },
390
+ {
391
+ "id": "E022",
392
+ "dataset": "entities",
393
+ "category": "company",
394
+ "input_text": "منصة GitHub للمطورين",
395
+ "expected": "منصة GitHub للمطورين",
396
+ "severity": "major",
397
+ "spelling_raw": "منصة GitHub المطورين",
398
+ "spelling_ms": 1202,
399
+ "grammar_raw": "منصة GitHuب للمطورين",
400
+ "grammar_ms": 938,
401
+ "punctuation_raw": "منصة GitHub للمطورين.",
402
+ "punctuation_ms": 863,
403
+ "spelling_changed": true,
404
+ "grammar_changed": true,
405
+ "punctuation_changed": true
406
+ },
407
+ {
408
+ "id": "E023",
409
+ "dataset": "entities",
410
+ "category": "company",
411
+ "input_text": "شركة Tesla للسيارات الكهربائية",
412
+ "expected": "شركة Tesla للسيارات الكهربائية",
413
+ "severity": "major",
414
+ "spelling_raw": "شركة تي اس لا للسيارات الكهربائية",
415
+ "spelling_ms": 960,
416
+ "grammar_raw": "شركة Teسla للسيارات الكهربائية",
417
+ "grammar_ms": 849,
418
+ "punctuation_raw": "شركة Tesla للسيارات الكهربائية.",
419
+ "punctuation_ms": 791,
420
+ "spelling_changed": true,
421
+ "grammar_changed": true,
422
+ "punctuation_changed": true
423
+ },
424
+ {
425
+ "id": "E024",
426
+ "dataset": "entities",
427
+ "category": "tech",
428
+ "input_text": "أستخدم Python في البرمجة",
429
+ "expected": "أستخدم Python في البرمجة",
430
+ "severity": "major",
431
+ "spelling_raw": "أستخدم Python في البرمجة",
432
+ "spelling_ms": 1042,
433
+ "grammar_raw": "أستخدمت Python في البرمجة",
434
+ "grammar_ms": 794,
435
+ "punctuation_raw": "أستخدم Python في البرمجة؟",
436
+ "punctuation_ms": 723,
437
+ "spelling_changed": false,
438
+ "grammar_changed": true,
439
+ "punctuation_changed": true
440
+ },
441
+ {
442
+ "id": "E025",
443
+ "dataset": "entities",
444
+ "category": "tech",
445
+ "input_text": "إطار TensorFlow مفيد للتعلم",
446
+ "expected": "إطار TensorFlow مفيد للتعلم",
447
+ "severity": "major",
448
+ "spelling_raw": "إطار TensorFlow مفيد للتعلم",
449
+ "spelling_ms": 1027,
450
+ "grammar_raw": "إطار TensorFlow مفيد للتعلم",
451
+ "grammar_ms": 989,
452
+ "punctuation_raw": "إطار TensorFlow، مفيد: للتعلم",
453
+ "punctuation_ms": 992,
454
+ "spelling_changed": false,
455
+ "grammar_changed": false,
456
+ "punctuation_changed": true
457
+ },
458
+ {
459
+ "id": "E026",
460
+ "dataset": "entities",
461
+ "category": "tech",
462
+ "input_text": "مكتبة PyTorch للتعلم العميق",
463
+ "expected": "مكتبة PyTorch للتعلم العميق",
464
+ "severity": "major",
465
+ "spelling_raw": "مكتبة PyTorch للتعلم العميق",
466
+ "spelling_ms": 1002,
467
+ "grammar_raw": "مكتبة PyTorch للتعلم العميق",
468
+ "grammar_ms": 891,
469
+ "punctuation_raw": "مكتبة PyTorch للتعلم العميق.",
470
+ "punctuation_ms": 1038,
471
+ "spelling_changed": false,
472
+ "grammar_changed": false,
473
+ "punctuation_changed": true
474
+ },
475
+ {
476
+ "id": "E027",
477
+ "dataset": "entities",
478
+ "category": "tech",
479
+ "input_text": "منصة Node.js للخوادم",
480
+ "expected": "منصة Node.js للخوادم",
481
+ "severity": "major",
482
+ "spelling_raw": "منصة Node. js للخوادم",
483
+ "spelling_ms": 1441,
484
+ "grammar_raw": "منصة Node ، jة للخوادم",
485
+ "grammar_ms": 1141,
486
+ "punctuation_raw": "منصة Node. js للخوادم:",
487
+ "punctuation_ms": 857,
488
+ "spelling_changed": true,
489
+ "grammar_changed": true,
490
+ "punctuation_changed": true
491
+ },
492
+ {
493
+ "id": "E028",
494
+ "dataset": "entities",
495
+ "category": "tech",
496
+ "input_text": "لغة JavaScript للويب",
497
+ "expected": "لغة JavaScript للويب",
498
+ "severity": "major",
499
+ "spelling_raw": "لغة JavaScript الويب",
500
+ "spelling_ms": 1346,
501
+ "grammar_raw": "لغة JavaScript للويب",
502
+ "grammar_ms": 813,
503
+ "punctuation_raw": "لغة JavaScript: للويب",
504
+ "punctuation_ms": 951,
505
+ "spelling_changed": true,
506
+ "grammar_changed": false,
507
+ "punctuation_changed": true
508
+ },
509
+ {
510
+ "id": "E029",
511
+ "dataset": "entities",
512
+ "category": "tech",
513
+ "input_text": "قاعدة بيانات MongoDB جيدة",
514
+ "expected": "قاعدة بيانات MongoDB جيدة",
515
+ "severity": "major",
516
+ "spelling_raw": "قاعدة بيانات MongoDB جيدة",
517
+ "spelling_ms": 969,
518
+ "grammar_raw": "قاعدة بيانات MongoDB جيدة",
519
+ "grammar_ms": 903,
520
+ "punctuation_raw": "قاعدة بيانات MongoDB. جيدة",
521
+ "punctuation_ms": 862,
522
+ "spelling_changed": false,
523
+ "grammar_changed": false,
524
+ "punctuation_changed": true
525
+ },
526
+ {
527
+ "id": "E030",
528
+ "dataset": "entities",
529
+ "category": "tech",
530
+ "input_text": "خدمة Docker للحاويات",
531
+ "expected": "خدمة Docker للحاويات",
532
+ "severity": "major",
533
+ "spelling_raw": "خدمة Docker للحاويات",
534
+ "spelling_ms": 1411,
535
+ "grammar_raw": "خدمة Docker للحاويات",
536
+ "grammar_ms": 946,
537
+ "punctuation_raw": "خدمة Docker للحاويات.",
538
+ "punctuation_ms": 806,
539
+ "spelling_changed": false,
540
+ "grammar_changed": false,
541
+ "punctuation_changed": true
542
+ }
543
+ ]
544
+ }
tests/v2/reports/level3_integrated_results.json CHANGED
@@ -1,23 +1,23 @@
1
  {
2
- "timestamp": "2026-06-23T18:54:52Z",
3
  "target": "https://bayan10-bayan-api.hf.space",
4
  "analysis": {
5
  "total": 80,
6
  "aggregate": {
7
- "TP": 22,
8
- "TN": 0,
9
- "FP": 58,
10
  "FN": 0,
11
- "pass_rate": 0.275
12
  },
13
  "by_dataset": {
14
  "spelling": {
15
- "TP": 22,
16
- "TN": 0,
17
- "FP": 58,
18
  "FN": 0,
19
  "total": 80,
20
- "pass_rate": 0.275
21
  }
22
  }
23
  },
@@ -31,12 +31,12 @@
31
  "severity": "major",
32
  "pipeline_corrected": "أنا طالب في الجامعة.",
33
  "pipeline_suggestions": 2,
34
- "pipeline_ms": 8033,
35
- "spelling_ms": 3645,
36
- "grammar_ms": 670,
37
- "punctuation_ms": 2748,
38
- "verdict": "FP",
39
- "detail": "Wrong correction"
40
  },
41
  {
42
  "id": "S002",
@@ -47,12 +47,12 @@
47
  "severity": "major",
48
  "pipeline_corrected": "إذا جاء الربيع تزهر الأشجار.",
49
  "pipeline_suggestions": 2,
50
- "pipeline_ms": 5444,
51
- "spelling_ms": 3501,
52
- "grammar_ms": 586,
53
- "punctuation_ms": 522,
54
- "verdict": "FP",
55
- "detail": "Wrong correction"
56
  },
57
  {
58
  "id": "S003",
@@ -63,10 +63,10 @@
63
  "severity": "major",
64
  "pipeline_corrected": "ايضا هذا الأمر مهم جداً.",
65
  "pipeline_suggestions": 1,
66
- "pipeline_ms": 5593,
67
- "spelling_ms": 3361,
68
- "grammar_ms": 654,
69
- "punctuation_ms": 664,
70
  "verdict": "FP",
71
  "detail": "Wrong correction"
72
  },
@@ -79,12 +79,12 @@
79
  "severity": "major",
80
  "pipeline_corrected": "لأن الأمر يتعلق بالمستقبل.",
81
  "pipeline_suggestions": 2,
82
- "pipeline_ms": 5150,
83
- "spelling_ms": 3364,
84
- "grammar_ms": 494,
85
- "punctuation_ms": 569,
86
- "verdict": "FP",
87
- "detail": "Wrong correction"
88
  },
89
  {
90
  "id": "S005",
@@ -95,12 +95,12 @@
95
  "severity": "major",
96
  "pipeline_corrected": "أين ذهبت أمس؟",
97
  "pipeline_suggestions": 2,
98
- "pipeline_ms": 3457,
99
- "spelling_ms": 1889,
100
- "grammar_ms": 436,
101
- "punctuation_ms": 338,
102
- "verdict": "FP",
103
- "detail": "Wrong correction"
104
  },
105
  {
106
  "id": "S006",
@@ -111,10 +111,10 @@
111
  "severity": "major",
112
  "pipeline_corrected": "اول مرة أزور هذا المكان.",
113
  "pipeline_suggestions": 1,
114
- "pipeline_ms": 5318,
115
- "spelling_ms": 3370,
116
- "grammar_ms": 655,
117
- "punctuation_ms": 558,
118
  "verdict": "FP",
119
  "detail": "Wrong correction"
120
  },
@@ -127,12 +127,12 @@
127
  "severity": "major",
128
  "pipeline_corrected": "هذا أو ذاك لا فرق.",
129
  "pipeline_suggestions": 2,
130
- "pipeline_ms": 6027,
131
- "spelling_ms": 4153,
132
- "grammar_ms": 604,
133
- "punctuation_ms": 701,
134
- "verdict": "FP",
135
- "detail": "Wrong correction"
136
  },
137
  {
138
  "id": "S008",
@@ -143,12 +143,12 @@
143
  "severity": "major",
144
  "pipeline_corrected": "أكبر مدينة في العالم؟",
145
  "pipeline_suggestions": 2,
146
- "pipeline_ms": 8374,
147
- "spelling_ms": 4040,
148
- "grammar_ms": 610,
149
- "punctuation_ms": 2543,
150
- "verdict": "FP",
151
- "detail": "Wrong correction"
152
  },
153
  {
154
  "id": "S009",
@@ -159,12 +159,12 @@
159
  "severity": "major",
160
  "pipeline_corrected": "أصغر طالب في الصف؟",
161
  "pipeline_suggestions": 2,
162
- "pipeline_ms": 4214,
163
- "spelling_ms": 2207,
164
- "grammar_ms": 595,
165
- "punctuation_ms": 513,
166
- "verdict": "FP",
167
- "detail": "Wrong correction"
168
  },
169
  {
170
  "id": "S010",
@@ -175,12 +175,12 @@
175
  "severity": "major",
176
  "pipeline_corrected": "أبناء الوطن يعملون بجد.",
177
  "pipeline_suggestions": 2,
178
- "pipeline_ms": 4294,
179
- "spelling_ms": 2184,
180
- "grammar_ms": 699,
181
- "punctuation_ms": 620,
182
- "verdict": "FP",
183
- "detail": "Wrong correction"
184
  },
185
  {
186
  "id": "S011",
@@ -191,12 +191,12 @@
191
  "severity": "major",
192
  "pipeline_corrected": "أطفال المدرسة يلعبون.",
193
  "pipeline_suggestions": 2,
194
- "pipeline_ms": 4345,
195
- "spelling_ms": 2268,
196
- "grammar_ms": 542,
197
- "punctuation_ms": 567,
198
- "verdict": "FP",
199
- "detail": "Wrong correction"
200
  },
201
  {
202
  "id": "S012",
@@ -207,12 +207,12 @@
207
  "severity": "major",
208
  "pipeline_corrected": "أخيراً وصلنا إلى الهدف.",
209
  "pipeline_suggestions": 2,
210
- "pipeline_ms": 6255,
211
- "spelling_ms": 4085,
212
- "grammar_ms": 596,
213
- "punctuation_ms": 729,
214
- "verdict": "FP",
215
- "detail": "Wrong correction"
216
  },
217
  {
218
  "id": "S013",
@@ -223,12 +223,12 @@
223
  "severity": "major",
224
  "pipeline_corrected": "وقف أمام المدرسة.",
225
  "pipeline_suggestions": 2,
226
- "pipeline_ms": 2917,
227
- "spelling_ms": 1380,
228
- "grammar_ms": 441,
229
- "punctuation_ms": 481,
230
- "verdict": "FP",
231
- "detail": "Wrong correction"
232
  },
233
  {
234
  "id": "S014",
@@ -239,12 +239,12 @@
239
  "severity": "major",
240
  "pipeline_corrected": "أنت طالب مجتهد.",
241
  "pipeline_suggestions": 2,
242
- "pipeline_ms": 3440,
243
- "spelling_ms": 1755,
244
- "grammar_ms": 535,
245
- "punctuation_ms": 337,
246
- "verdict": "FP",
247
- "detail": "Wrong correction"
248
  },
249
  {
250
  "id": "S015",
@@ -255,10 +255,10 @@
255
  "severity": "major",
256
  "pipeline_corrected": "اننا نحب الوطن.",
257
  "pipeline_suggestions": 1,
258
- "pipeline_ms": 4129,
259
- "spelling_ms": 2441,
260
- "grammar_ms": 440,
261
- "punctuation_ms": 463,
262
  "verdict": "FP",
263
  "detail": "Wrong correction"
264
  },
@@ -271,12 +271,12 @@
271
  "severity": "major",
272
  "pipeline_corrected": "إن شاء الله سنفوز.",
273
  "pipeline_suggestions": 2,
274
- "pipeline_ms": 5342,
275
- "spelling_ms": 3569,
276
- "grammar_ms": 636,
277
- "punctuation_ms": 533,
278
- "verdict": "FP",
279
- "detail": "Wrong correction"
280
  },
281
  {
282
  "id": "S017",
@@ -287,12 +287,12 @@
287
  "severity": "none",
288
  "pipeline_corrected": "اقترب الموعد النهائي.",
289
  "pipeline_suggestions": 1,
290
- "pipeline_ms": 4093,
291
- "spelling_ms": 1957,
292
- "grammar_ms": 754,
293
- "punctuation_ms": 584,
294
- "verdict": "FP",
295
- "detail": "Modified correct text"
296
  },
297
  {
298
  "id": "S018",
@@ -303,12 +303,12 @@
303
  "severity": "major",
304
  "pipeline_corrected": "أرسل الرسالة فوراً.",
305
  "pipeline_suggestions": 2,
306
- "pipeline_ms": 4064,
307
- "spelling_ms": 2103,
308
- "grammar_ms": 651,
309
- "punctuation_ms": 518,
310
- "verdict": "FP",
311
- "detail": "Wrong correction"
312
  },
313
  {
314
  "id": "S019",
@@ -319,12 +319,12 @@
319
  "severity": "major",
320
  "pipeline_corrected": "آخر الأخبار في المساء.",
321
  "pipeline_suggestions": 2,
322
- "pipeline_ms": 4463,
323
- "spelling_ms": 2236,
324
- "grammar_ms": 549,
325
- "punctuation_ms": 721,
326
- "verdict": "FP",
327
- "detail": "Wrong correction"
328
  },
329
  {
330
  "id": "S020",
@@ -335,12 +335,12 @@
335
  "severity": "major",
336
  "pipeline_corrected": "أسرع في المشي قليلاً!",
337
  "pipeline_suggestions": 2,
338
- "pipeline_ms": 6099,
339
- "spelling_ms": 3801,
340
- "grammar_ms": 705,
341
- "punctuation_ms": 588,
342
- "verdict": "FP",
343
- "detail": "Wrong correction"
344
  },
345
  {
346
  "id": "S021",
@@ -351,12 +351,12 @@
351
  "severity": "major",
352
  "pipeline_corrected": "أجمل مكان في العالم؟",
353
  "pipeline_suggestions": 2,
354
- "pipeline_ms": 4010,
355
- "spelling_ms": 2218,
356
- "grammar_ms": 540,
357
- "punctuation_ms": 408,
358
- "verdict": "FP",
359
- "detail": "Wrong correction"
360
  },
361
  {
362
  "id": "S022",
@@ -367,10 +367,10 @@
367
  "severity": "major",
368
  "pipeline_corrected": "اعلن الرئيس عن القرار.",
369
  "pipeline_suggestions": 1,
370
- "pipeline_ms": 4267,
371
- "spelling_ms": 2265,
372
- "grammar_ms": 497,
373
- "punctuation_ms": 598,
374
  "verdict": "FP",
375
  "detail": "Wrong correction"
376
  },
@@ -383,12 +383,12 @@
383
  "severity": "major",
384
  "pipeline_corrected": "أكد الوزير على الخطة.",
385
  "pipeline_suggestions": 2,
386
- "pipeline_ms": 4566,
387
- "spelling_ms": 2472,
388
- "grammar_ms": 554,
389
- "punctuation_ms": 519,
390
- "verdict": "FP",
391
- "detail": "Wrong correction"
392
  },
393
  {
394
  "id": "S024",
@@ -399,12 +399,12 @@
399
  "severity": "major",
400
  "pipeline_corrected": "أشار التقرير إلى ذلك.",
401
  "pipeline_suggestions": 2,
402
- "pipeline_ms": 4563,
403
- "spelling_ms": 2521,
404
- "grammar_ms": 590,
405
- "punctuation_ms": 568,
406
- "verdict": "FP",
407
- "detail": "Wrong correction"
408
  },
409
  {
410
  "id": "S025",
@@ -415,12 +415,12 @@
415
  "severity": "major",
416
  "pipeline_corrected": "أعتقد انه سيحضر غداً.",
417
  "pipeline_suggestions": 2,
418
- "pipeline_ms": 4144,
419
- "spelling_ms": 2119,
420
- "grammar_ms": 652,
421
- "punctuation_ms": 578,
422
- "verdict": "FP",
423
- "detail": "Wrong correction"
424
  },
425
  {
426
  "id": "S026",
@@ -431,12 +431,12 @@
431
  "severity": "major",
432
  "pipeline_corrected": "والأسعار مرتفعة جداً.",
433
  "pipeline_suggestions": 2,
434
- "pipeline_ms": 4880,
435
- "spelling_ms": 2705,
436
- "grammar_ms": 593,
437
- "punctuation_ms": 585,
438
- "verdict": "FP",
439
- "detail": "Wrong correction"
440
  },
441
  {
442
  "id": "S027",
@@ -447,12 +447,12 @@
447
  "severity": "major",
448
  "pipeline_corrected": "بالإضافة إلى ذلك.",
449
  "pipeline_suggestions": 2,
450
- "pipeline_ms": 4101,
451
- "spelling_ms": 2414,
452
- "grammar_ms": 438,
453
- "punctuation_ms": 442,
454
- "verdict": "FP",
455
- "detail": "Wrong correction"
456
  },
457
  {
458
  "id": "S028",
@@ -463,10 +463,10 @@
463
  "severity": "major",
464
  "pipeline_corrected": "فالانسان يحتاج للعلم.",
465
  "pipeline_suggestions": 1,
466
- "pipeline_ms": 5749,
467
- "spelling_ms": 3787,
468
- "grammar_ms": 484,
469
- "punctuation_ms": 692,
470
  "verdict": "FP",
471
  "detail": "Wrong correction"
472
  },
@@ -479,10 +479,10 @@
479
  "severity": "major",
480
  "pipeline_corrected": "كلا الأطفال في اللعب.",
481
  "pipeline_suggestions": 2,
482
- "pipeline_ms": 6739,
483
- "spelling_ms": 4640,
484
- "grammar_ms": 701,
485
- "punctuation_ms": 540,
486
  "verdict": "FP",
487
  "detail": "Wrong correction"
488
  },
@@ -495,12 +495,12 @@
495
  "severity": "major",
496
  "pipeline_corrected": "للأسف لم ينجح.",
497
  "pipeline_suggestions": 2,
498
- "pipeline_ms": 4198,
499
- "spelling_ms": 2413,
500
  "grammar_ms": 437,
501
- "punctuation_ms": 476,
502
- "verdict": "FP",
503
- "detail": "Wrong correction"
504
  },
505
  {
506
  "id": "S031",
@@ -511,12 +511,12 @@
511
  "severity": "major",
512
  "pipeline_corrected": "المدرسة كبيرة وجميلة.",
513
  "pipeline_suggestions": 3,
514
- "pipeline_ms": 3680,
515
- "spelling_ms": 1927,
516
- "grammar_ms": 494,
517
- "punctuation_ms": 654,
518
  "verdict": "TP",
519
- "detail": "Partial improvement"
520
  },
521
  {
522
  "id": "S032",
@@ -527,12 +527,12 @@
527
  "severity": "major",
528
  "pipeline_corrected": "الجامعة في القاهرة.",
529
  "pipeline_suggestions": 2,
530
- "pipeline_ms": 6067,
531
- "spelling_ms": 4080,
532
- "grammar_ms": 537,
533
- "punctuation_ms": 527,
534
  "verdict": "TP",
535
- "detail": "Partial improvement"
536
  },
537
  {
538
  "id": "S033",
@@ -543,12 +543,12 @@
543
  "severity": "major",
544
  "pipeline_corrected": "السيارة سريعة جداً.",
545
  "pipeline_suggestions": 3,
546
- "pipeline_ms": 4645,
547
- "spelling_ms": 2783,
548
- "grammar_ms": 595,
549
- "punctuation_ms": 423,
550
  "verdict": "TP",
551
- "detail": "Partial improvement"
552
  },
553
  {
554
  "id": "S034",
@@ -559,12 +559,12 @@
559
  "severity": "major",
560
  "pipeline_corrected": "الشجرة طويلة وجميلة.",
561
  "pipeline_suggestions": 3,
562
- "pipeline_ms": 3937,
563
- "spelling_ms": 2031,
564
- "grammar_ms": 496,
565
  "punctuation_ms": 519,
566
  "verdict": "TP",
567
- "detail": "Partial improvement"
568
  },
569
  {
570
  "id": "S035",
@@ -575,12 +575,12 @@
575
  "severity": "major",
576
  "pipeline_corrected": "الحياة صعبة في المدينة.",
577
  "pipeline_suggestions": 3,
578
- "pipeline_ms": 4159,
579
- "spelling_ms": 2357,
580
- "grammar_ms": 535,
581
- "punctuation_ms": 577,
582
  "verdict": "TP",
583
- "detail": "Partial improvement"
584
  },
585
  {
586
  "id": "S036",
@@ -591,12 +591,12 @@
591
  "severity": "major",
592
  "pipeline_corrected": "المكتبة قريبة من البيت.",
593
  "pipeline_suggestions": 3,
594
- "pipeline_ms": 5008,
595
- "spelling_ms": 3269,
596
- "grammar_ms": 597,
597
- "punctuation_ms": 503,
598
  "verdict": "TP",
599
- "detail": "Partial improvement"
600
  },
601
  {
602
  "id": "S037",
@@ -607,12 +607,12 @@
607
  "severity": "major",
608
  "pipeline_corrected": "الغرفة نظيفة ومرتبة.",
609
  "pipeline_suggestions": 3,
610
- "pipeline_ms": 2678,
611
- "spelling_ms": 811,
612
- "grammar_ms": 653,
613
- "punctuation_ms": 459,
614
  "verdict": "TP",
615
- "detail": "Partial improvement"
616
  },
617
  {
618
  "id": "S038",
@@ -623,12 +623,12 @@
623
  "severity": "major",
624
  "pipeline_corrected": "القصة مثيرة للاهتمام!",
625
  "pipeline_suggestions": 3,
626
- "pipeline_ms": 2345,
627
- "spelling_ms": 526,
628
- "grammar_ms": 536,
629
- "punctuation_ms": 599,
630
  "verdict": "TP",
631
- "detail": "Partial improvement"
632
  },
633
  {
634
  "id": "S039",
@@ -639,12 +639,12 @@
639
  "severity": "major",
640
  "pipeline_corrected": "الرحلة طويلة ومتعبة.",
641
  "pipeline_suggestions": 3,
642
- "pipeline_ms": 2842,
643
- "spelling_ms": 993,
644
- "grammar_ms": 615,
645
- "punctuation_ms": 478,
646
  "verdict": "TP",
647
- "detail": "Partial improvement"
648
  },
649
  {
650
  "id": "S040",
@@ -655,12 +655,12 @@
655
  "severity": "major",
656
  "pipeline_corrected": "الوظيفة صعبة لكنها مفيدة.",
657
  "pipeline_suggestions": 3,
658
- "pipeline_ms": 2828,
659
- "spelling_ms": 824,
660
- "grammar_ms": 496,
661
- "punctuation_ms": 759,
662
  "verdict": "TP",
663
- "detail": "Partial improvement"
664
  },
665
  {
666
  "id": "S041",
@@ -671,10 +671,10 @@
671
  "severity": "major",
672
  "pipeline_corrected": "بالمدرسة الكبيرة",
673
  "pipeline_suggestions": 2,
674
- "pipeline_ms": 2387,
675
- "spelling_ms": 782,
676
- "grammar_ms": 348,
677
- "punctuation_ms": 477,
678
  "verdict": "TP",
679
  "detail": "Exact match"
680
  },
@@ -687,10 +687,10 @@
687
  "severity": "major",
688
  "pipeline_corrected": "والجامعة المصرية",
689
  "pipeline_suggestions": 2,
690
- "pipeline_ms": 2143,
691
- "spelling_ms": 481,
692
- "grammar_ms": 441,
693
- "punctuation_ms": 415,
694
  "verdict": "TP",
695
  "detail": "Exact match"
696
  },
@@ -703,10 +703,10 @@
703
  "severity": "major",
704
  "pipeline_corrected": "السيارة الجديدة",
705
  "pipeline_suggestions": 2,
706
- "pipeline_ms": 2365,
707
- "spelling_ms": 764,
708
- "grammar_ms": 336,
709
- "punctuation_ms": 402,
710
  "verdict": "TP",
711
  "detail": "Partial improvement"
712
  },
@@ -719,10 +719,10 @@
719
  "severity": "major",
720
  "pipeline_corrected": "كالمدرسه قديمة",
721
  "pipeline_suggestions": 1,
722
- "pipeline_ms": 2443,
723
- "spelling_ms": 706,
724
- "grammar_ms": 434,
725
- "punctuation_ms": 407,
726
  "verdict": "FP",
727
  "detail": "Wrong correction"
728
  },
@@ -735,10 +735,10 @@
735
  "severity": "major",
736
  "pipeline_corrected": "للمدرسة الابتدائية",
737
  "pipeline_suggestions": 2,
738
- "pipeline_ms": 2449,
739
- "spelling_ms": 770,
740
- "grammar_ms": 332,
741
- "punctuation_ms": 661,
742
  "verdict": "TP",
743
  "detail": "Exact match"
744
  },
@@ -751,12 +751,12 @@
751
  "severity": "major",
752
  "pipeline_corrected": "ذهبت إلى المكتبة؟",
753
  "pipeline_suggestions": 2,
754
- "pipeline_ms": 2163,
755
- "spelling_ms": 492,
756
- "grammar_ms": 435,
757
- "punctuation_ms": 434,
758
  "verdict": "TP",
759
- "detail": "Partial improvement"
760
  },
761
  {
762
  "id": "S047",
@@ -767,10 +767,10 @@
767
  "severity": "major",
768
  "pipeline_corrected": "المستشفى الكبير",
769
  "pipeline_suggestions": 1,
770
- "pipeline_ms": 1927,
771
- "spelling_ms": 514,
772
- "grammar_ms": 336,
773
- "punctuation_ms": 436,
774
  "verdict": "TP",
775
  "detail": "Exact match"
776
  },
@@ -783,10 +783,10 @@
783
  "severity": "major",
784
  "pipeline_corrected": "هدى الطالبة ممتازة",
785
  "pipeline_suggestions": 2,
786
- "pipeline_ms": 2224,
787
- "spelling_ms": 443,
788
- "grammar_ms": 545,
789
- "punctuation_ms": 436,
790
  "verdict": "FP",
791
  "detail": "Wrong correction"
792
  },
@@ -799,12 +799,12 @@
799
  "severity": "major",
800
  "pipeline_corrected": "مصطفى طالب مجتهد.",
801
  "pipeline_suggestions": 2,
802
- "pipeline_ms": 2224,
803
- "spelling_ms": 589,
804
- "grammar_ms": 534,
805
- "punctuation_ms": 320,
806
- "verdict": "FP",
807
- "detail": "Wrong correction"
808
  },
809
  {
810
  "id": "S050",
@@ -815,12 +815,12 @@
815
  "severity": "major",
816
  "pipeline_corrected": "موسى نبي عظيم.",
817
  "pipeline_suggestions": 2,
818
- "pipeline_ms": 1946,
819
- "spelling_ms": 521,
820
- "grammar_ms": 438,
821
- "punctuation_ms": 318,
822
- "verdict": "FP",
823
- "detail": "Wrong correction"
824
  },
825
  {
826
  "id": "S051",
@@ -831,12 +831,12 @@
831
  "severity": "none",
832
  "pipeline_corrected": "علي يدرس في الكلية.",
833
  "pipeline_suggestions": 1,
834
- "pipeline_ms": 2092,
835
- "spelling_ms": 516,
836
- "grammar_ms": 540,
837
- "punctuation_ms": 455,
838
- "verdict": "FP",
839
- "detail": "Modified correct text"
840
  },
841
  {
842
  "id": "S052",
@@ -847,12 +847,12 @@
847
  "severity": "major",
848
  "pipeline_corrected": "ذهب إلى السوق.",
849
  "pipeline_suggestions": 2,
850
- "pipeline_ms": 2033,
851
- "spelling_ms": 604,
852
- "grammar_ms": 408,
853
- "punctuation_ms": 393,
854
  "verdict": "TP",
855
- "detail": "Partial improvement"
856
  },
857
  {
858
  "id": "S053",
@@ -863,12 +863,12 @@
863
  "severity": "major",
864
  "pipeline_corrected": "بنى المبنى الجديد.",
865
  "pipeline_suggestions": 2,
866
- "pipeline_ms": 2459,
867
- "spelling_ms": 635,
868
- "grammar_ms": 436,
869
- "punctuation_ms": 513,
870
- "verdict": "FP",
871
- "detail": "Wrong correction"
872
  },
873
  {
874
  "id": "S054",
@@ -879,10 +879,10 @@
879
  "severity": "major",
880
  "pipeline_corrected": "ذهبت في البيت",
881
  "pipeline_suggestions": 1,
882
- "pipeline_ms": 2495,
883
- "spelling_ms": 836,
884
- "grammar_ms": 424,
885
- "punctuation_ms": 490,
886
  "verdict": "TP",
887
  "detail": "Exact match"
888
  },
@@ -895,10 +895,10 @@
895
  "severity": "major",
896
  "pipeline_corrected": "خرج منالمدرسة.",
897
  "pipeline_suggestions": 1,
898
- "pipeline_ms": 2287,
899
- "spelling_ms": 611,
900
- "grammar_ms": 395,
901
- "punctuation_ms": 552,
902
  "verdict": "FP",
903
  "detail": "Wrong correction"
904
  },
@@ -911,10 +911,10 @@
911
  "severity": "major",
912
  "pipeline_corrected": "بقي عندالباب.",
913
  "pipeline_suggestions": 1,
914
- "pipeline_ms": 7894,
915
- "spelling_ms": 682,
916
- "grammar_ms": 468,
917
- "punctuation_ms": 408,
918
  "verdict": "FP",
919
  "detail": "Wrong correction"
920
  },
@@ -927,10 +927,10 @@
927
  "severity": "major",
928
  "pipeline_corrected": "جلس عندالنافذة.",
929
  "pipeline_suggestions": 1,
930
- "pipeline_ms": 2314,
931
- "spelling_ms": 540,
932
- "grammar_ms": 488,
933
- "punctuation_ms": 435,
934
  "verdict": "FP",
935
  "detail": "Wrong correction"
936
  },
@@ -943,10 +943,10 @@
943
  "severity": "major",
944
  "pipeline_corrected": "رجع الىالبيت.",
945
  "pipeline_suggestions": 1,
946
- "pipeline_ms": 2280,
947
- "spelling_ms": 659,
948
- "grammar_ms": 437,
949
- "punctuation_ms": 380,
950
  "verdict": "FP",
951
  "detail": "Wrong correction"
952
  },
@@ -959,12 +959,12 @@
959
  "severity": "major",
960
  "pipeline_corrected": "خرج من الباب الخلفي.",
961
  "pipeline_suggestions": 2,
962
- "pipeline_ms": 5938,
963
- "spelling_ms": 512,
964
- "grammar_ms": 487,
965
- "punctuation_ms": 554,
966
- "verdict": "FP",
967
- "detail": "Wrong correction"
968
  },
969
  {
970
  "id": "S060",
@@ -975,10 +975,10 @@
975
  "severity": "major",
976
  "pipeline_corrected": "نظر الىالسماء.",
977
  "pipeline_suggestions": 1,
978
- "pipeline_ms": 2092,
979
- "spelling_ms": 653,
980
- "grammar_ms": 484,
981
- "punctuation_ms": 327,
982
  "verdict": "FP",
983
  "detail": "Wrong correction"
984
  },
@@ -991,12 +991,12 @@
991
  "severity": "none",
992
  "pipeline_corrected": "أنا ذهبت إلى الجامعة.",
993
  "pipeline_suggestions": 1,
994
- "pipeline_ms": 2067,
995
- "spelling_ms": 563,
996
- "grammar_ms": 487,
997
- "punctuation_ms": 449,
998
- "verdict": "FP",
999
- "detail": "Modified correct text"
1000
  },
1001
  {
1002
  "id": "S062",
@@ -1007,12 +1007,12 @@
1007
  "severity": "none",
1008
  "pipeline_corrected": "هذه المدرسة جميلة جداً.",
1009
  "pipeline_suggestions": 1,
1010
- "pipeline_ms": 4721,
1011
- "spelling_ms": 500,
1012
- "grammar_ms": 694,
1013
- "punctuation_ms": 532,
1014
- "verdict": "FP",
1015
- "detail": "Modified correct text"
1016
  },
1017
  {
1018
  "id": "S063",
@@ -1023,12 +1023,12 @@
1023
  "severity": "none",
1024
  "pipeline_corrected": "كان الجو ممطراً اليوم؟",
1025
  "pipeline_suggestions": 1,
1026
- "pipeline_ms": 2771,
1027
- "spelling_ms": 823,
1028
- "grammar_ms": 738,
1029
- "punctuation_ms": 585,
1030
- "verdict": "FP",
1031
- "detail": "Modified correct text"
1032
  },
1033
  {
1034
  "id": "S064",
@@ -1039,12 +1039,12 @@
1039
  "severity": "none",
1040
  "pipeline_corrected": "وكان أحمد في المنزل.",
1041
  "pipeline_suggestions": 1,
1042
- "pipeline_ms": 2497,
1043
- "spelling_ms": 614,
1044
- "grammar_ms": 499,
1045
- "punctuation_ms": 659,
1046
- "verdict": "FP",
1047
- "detail": "Modified correct text"
1048
  },
1049
  {
1050
  "id": "S065",
@@ -1055,12 +1055,12 @@
1055
  "severity": "none",
1056
  "pipeline_corrected": "إلى اللقاء يا صديقي.",
1057
  "pipeline_suggestions": 1,
1058
- "pipeline_ms": 6038,
1059
- "spelling_ms": 665,
1060
- "grammar_ms": 484,
1061
- "punctuation_ms": 440,
1062
- "verdict": "FP",
1063
- "detail": "Modified correct text"
1064
  },
1065
  {
1066
  "id": "S066",
@@ -1071,12 +1071,12 @@
1071
  "severity": "none",
1072
  "pipeline_corrected": "ذلك الكتاب مفيد جداً.",
1073
  "pipeline_suggestions": 1,
1074
- "pipeline_ms": 2372,
1075
- "spelling_ms": 502,
1076
- "grammar_ms": 589,
1077
- "punctuation_ms": 411,
1078
- "verdict": "FP",
1079
- "detail": "Modified correct text"
1080
  },
1081
  {
1082
  "id": "S067",
@@ -1087,12 +1087,12 @@
1087
  "severity": "none",
1088
  "pipeline_corrected": "لكن الأمر صعب علينا.",
1089
  "pipeline_suggestions": 1,
1090
- "pipeline_ms": 2401,
1091
- "spelling_ms": 617,
1092
- "grammar_ms": 485,
1093
- "punctuation_ms": 424,
1094
- "verdict": "FP",
1095
- "detail": "Modified correct text"
1096
  },
1097
  {
1098
  "id": "S068",
@@ -1103,12 +1103,12 @@
1103
  "severity": "none",
1104
  "pipeline_corrected": "هذا أو ذاك سواء عندي.",
1105
  "pipeline_suggestions": 1,
1106
- "pipeline_ms": 6476,
1107
- "spelling_ms": 636,
1108
- "grammar_ms": 683,
1109
- "punctuation_ms": 435,
1110
- "verdict": "FP",
1111
- "detail": "Modified correct text"
1112
  },
1113
  {
1114
  "id": "S069",
@@ -1119,12 +1119,12 @@
1119
  "severity": "none",
1120
  "pipeline_corrected": "الطالب المجتهد ينجح دائماً.",
1121
  "pipeline_suggestions": 1,
1122
- "pipeline_ms": 2658,
1123
- "spelling_ms": 548,
1124
- "grammar_ms": 645,
1125
- "punctuation_ms": 579,
1126
- "verdict": "FP",
1127
- "detail": "Modified correct text"
1128
  },
1129
  {
1130
  "id": "S070",
@@ -1135,12 +1135,12 @@
1135
  "severity": "none",
1136
  "pipeline_corrected": "العلم نور والجهل ظلام.",
1137
  "pipeline_suggestions": 1,
1138
- "pipeline_ms": 2694,
1139
- "spelling_ms": 608,
1140
- "grammar_ms": 726,
1141
- "punctuation_ms": 582,
1142
- "verdict": "FP",
1143
- "detail": "Modified correct text"
1144
  },
1145
  {
1146
  "id": "S071",
@@ -1151,12 +1151,12 @@
1151
  "severity": "none",
1152
  "pipeline_corrected": "أحب القراءة والكتابة.",
1153
  "pipeline_suggestions": 1,
1154
- "pipeline_ms": 5967,
1155
- "spelling_ms": 627,
1156
- "grammar_ms": 437,
1157
- "punctuation_ms": 485,
1158
- "verdict": "FP",
1159
- "detail": "Modified correct text"
1160
  },
1161
  {
1162
  "id": "S072",
@@ -1167,12 +1167,12 @@
1167
  "severity": "none",
1168
  "pipeline_corrected": "المعلم يشرح الدرس.",
1169
  "pipeline_suggestions": 1,
1170
- "pipeline_ms": 2178,
1171
- "spelling_ms": 549,
1172
- "grammar_ms": 431,
1173
- "punctuation_ms": 466,
1174
- "verdict": "FP",
1175
- "detail": "Modified correct text"
1176
  },
1177
  {
1178
  "id": "S073",
@@ -1183,12 +1183,12 @@
1183
  "severity": "none",
1184
  "pipeline_corrected": "ذهبت إلى السوق واشتريت خبزاً.",
1185
  "pipeline_suggestions": 1,
1186
- "pipeline_ms": 3214,
1187
- "spelling_ms": 720,
1188
- "grammar_ms": 837,
1189
- "punctuation_ms": 828,
1190
- "verdict": "FP",
1191
- "detail": "Modified correct text"
1192
  },
1193
  {
1194
  "id": "S074",
@@ -1199,12 +1199,12 @@
1199
  "severity": "none",
1200
  "pipeline_corrected": "الطقس جميل في الربيع.",
1201
  "pipeline_suggestions": 1,
1202
- "pipeline_ms": 5596,
1203
- "spelling_ms": 497,
1204
- "grammar_ms": 637,
1205
- "punctuation_ms": 424,
1206
- "verdict": "FP",
1207
- "detail": "Modified correct text"
1208
  },
1209
  {
1210
  "id": "S075",
@@ -1215,12 +1215,12 @@
1215
  "severity": "none",
1216
  "pipeline_corrected": "نحن نعمل بجد كل يوم.",
1217
  "pipeline_suggestions": 1,
1218
- "pipeline_ms": 2688,
1219
- "spelling_ms": 619,
1220
- "grammar_ms": 785,
1221
- "punctuation_ms": 360,
1222
- "verdict": "FP",
1223
- "detail": "Modified correct text"
1224
  },
1225
  {
1226
  "id": "S076",
@@ -1231,12 +1231,12 @@
1231
  "severity": "critical",
1232
  "pipeline_corrected": "أنا ذهبت إلى المدرسة.",
1233
  "pipeline_suggestions": 3,
1234
- "pipeline_ms": 2497,
1235
- "spelling_ms": 630,
1236
- "grammar_ms": 485,
1237
- "punctuation_ms": 442,
1238
  "verdict": "TP",
1239
- "detail": "Partial improvement"
1240
  },
1241
  {
1242
  "id": "S077",
@@ -1247,12 +1247,12 @@
1247
  "severity": "critical",
1248
  "pipeline_corrected": "أيضا الجامعة كبيرة.",
1249
  "pipeline_suggestions": 2,
1250
- "pipeline_ms": 5834,
1251
- "spelling_ms": 557,
1252
- "grammar_ms": 582,
1253
- "punctuation_ms": 406,
1254
  "verdict": "TP",
1255
- "detail": "Partial improvement"
1256
  },
1257
  {
1258
  "id": "S078",
@@ -1263,12 +1263,12 @@
1263
  "severity": "critical",
1264
  "pipeline_corrected": "لأن المدرسة بعيدة جداً.",
1265
  "pipeline_suggestions": 4,
1266
- "pipeline_ms": 2671,
1267
- "spelling_ms": 669,
1268
- "grammar_ms": 648,
1269
- "punctuation_ms": 531,
1270
  "verdict": "TP",
1271
- "detail": "Partial improvement"
1272
  },
1273
  {
1274
  "id": "S079",
@@ -1279,10 +1279,10 @@
1279
  "severity": "critical",
1280
  "pipeline_corrected": "اين أن الجامعة الكبيرة",
1281
  "pipeline_suggestions": 2,
1282
- "pipeline_ms": 2619,
1283
- "spelling_ms": 710,
1284
- "grammar_ms": 493,
1285
- "punctuation_ms": 456,
1286
  "verdict": "FP",
1287
  "detail": "Wrong correction"
1288
  },
@@ -1295,12 +1295,12 @@
1295
  "severity": "critical",
1296
  "pipeline_corrected": "أول مرة أزور المكتبة.",
1297
  "pipeline_suggestions": 4,
1298
- "pipeline_ms": 5759,
1299
- "spelling_ms": 799,
1300
- "grammar_ms": 513,
1301
- "punctuation_ms": 483,
1302
  "verdict": "TP",
1303
- "detail": "Partial improvement"
1304
  }
1305
  ]
1306
  }
 
1
  {
2
+ "timestamp": "2026-06-23T19:00:34Z",
3
  "target": "https://bayan10-bayan-api.hf.space",
4
  "analysis": {
5
  "total": 80,
6
  "aggregate": {
7
+ "TP": 49,
8
+ "TN": 17,
9
+ "FP": 14,
10
  "FN": 0,
11
+ "pass_rate": 0.825
12
  },
13
  "by_dataset": {
14
  "spelling": {
15
+ "TP": 49,
16
+ "TN": 17,
17
+ "FP": 14,
18
  "FN": 0,
19
  "total": 80,
20
+ "pass_rate": 0.825
21
  }
22
  }
23
  },
 
31
  "severity": "major",
32
  "pipeline_corrected": "أنا طالب في الجامعة.",
33
  "pipeline_suggestions": 2,
34
+ "pipeline_ms": 2089,
35
+ "spelling_ms": 497,
36
+ "grammar_ms": 593,
37
+ "punctuation_ms": 412,
38
+ "verdict": "TP",
39
+ "detail": "Exact match"
40
  },
41
  {
42
  "id": "S002",
 
47
  "severity": "major",
48
  "pipeline_corrected": "إذا جاء الربيع تزهر الأشجار.",
49
  "pipeline_suggestions": 2,
50
+ "pipeline_ms": 3020,
51
+ "spelling_ms": 786,
52
+ "grammar_ms": 594,
53
+ "punctuation_ms": 1421,
54
+ "verdict": "TP",
55
+ "detail": "Exact match"
56
  },
57
  {
58
  "id": "S003",
 
63
  "severity": "major",
64
  "pipeline_corrected": "ايضا هذا الأمر مهم جداً.",
65
  "pipeline_suggestions": 1,
66
+ "pipeline_ms": 2143,
67
+ "spelling_ms": 677,
68
+ "grammar_ms": 646,
69
+ "punctuation_ms": 605,
70
  "verdict": "FP",
71
  "detail": "Wrong correction"
72
  },
 
79
  "severity": "major",
80
  "pipeline_corrected": "لأن الأمر يتعلق بالمستقبل.",
81
  "pipeline_suggestions": 2,
82
+ "pipeline_ms": 2039,
83
+ "spelling_ms": 661,
84
+ "grammar_ms": 497,
85
+ "punctuation_ms": 663,
86
+ "verdict": "TP",
87
+ "detail": "Exact match"
88
  },
89
  {
90
  "id": "S005",
 
95
  "severity": "major",
96
  "pipeline_corrected": "أين ذهبت أمس؟",
97
  "pipeline_suggestions": 2,
98
+ "pipeline_ms": 1601,
99
+ "spelling_ms": 604,
100
+ "grammar_ms": 386,
101
+ "punctuation_ms": 394,
102
+ "verdict": "TP",
103
+ "detail": "Exact match"
104
  },
105
  {
106
  "id": "S006",
 
111
  "severity": "major",
112
  "pipeline_corrected": "اول مرة أزور هذا المكان.",
113
  "pipeline_suggestions": 1,
114
+ "pipeline_ms": 2175,
115
+ "spelling_ms": 796,
116
+ "grammar_ms": 547,
117
+ "punctuation_ms": 613,
118
  "verdict": "FP",
119
  "detail": "Wrong correction"
120
  },
 
127
  "severity": "major",
128
  "pipeline_corrected": "هذا أو ذاك لا فرق.",
129
  "pipeline_suggestions": 2,
130
+ "pipeline_ms": 1792,
131
+ "spelling_ms": 637,
132
+ "grammar_ms": 536,
133
+ "punctuation_ms": 400,
134
+ "verdict": "TP",
135
+ "detail": "Exact match"
136
  },
137
  {
138
  "id": "S008",
 
143
  "severity": "major",
144
  "pipeline_corrected": "أكبر مدينة في العالم؟",
145
  "pipeline_suggestions": 2,
146
+ "pipeline_ms": 1641,
147
+ "spelling_ms": 464,
148
+ "grammar_ms": 493,
149
+ "punctuation_ms": 465,
150
+ "verdict": "TP",
151
+ "detail": "Exact match"
152
  },
153
  {
154
  "id": "S009",
 
159
  "severity": "major",
160
  "pipeline_corrected": "أصغر طالب في الصف؟",
161
  "pipeline_suggestions": 2,
162
+ "pipeline_ms": 1794,
163
+ "spelling_ms": 573,
164
+ "grammar_ms": 638,
165
+ "punctuation_ms": 364,
166
+ "verdict": "TP",
167
+ "detail": "Exact match"
168
  },
169
  {
170
  "id": "S010",
 
175
  "severity": "major",
176
  "pipeline_corrected": "أبناء الوطن يعملون بجد.",
177
  "pipeline_suggestions": 2,
178
+ "pipeline_ms": 1696,
179
+ "spelling_ms": 536,
180
+ "grammar_ms": 531,
181
+ "punctuation_ms": 405,
182
+ "verdict": "TP",
183
+ "detail": "Exact match"
184
  },
185
  {
186
  "id": "S011",
 
191
  "severity": "major",
192
  "pipeline_corrected": "أطفال المدرسة يلعبون.",
193
  "pipeline_suggestions": 2,
194
+ "pipeline_ms": 1582,
195
+ "spelling_ms": 481,
196
+ "grammar_ms": 477,
197
+ "punctuation_ms": 408,
198
+ "verdict": "TP",
199
+ "detail": "Exact match"
200
  },
201
  {
202
  "id": "S012",
 
207
  "severity": "major",
208
  "pipeline_corrected": "أخيراً وصلنا إلى الهدف.",
209
  "pipeline_suggestions": 2,
210
+ "pipeline_ms": 1954,
211
+ "spelling_ms": 660,
212
+ "grammar_ms": 535,
213
+ "punctuation_ms": 543,
214
+ "verdict": "TP",
215
+ "detail": "Exact match"
216
  },
217
  {
218
  "id": "S013",
 
223
  "severity": "major",
224
  "pipeline_corrected": "وقف أمام المدرسة.",
225
  "pipeline_suggestions": 2,
226
+ "pipeline_ms": 1380,
227
+ "spelling_ms": 393,
228
+ "grammar_ms": 386,
229
+ "punctuation_ms": 386,
230
+ "verdict": "TP",
231
+ "detail": "Exact match"
232
  },
233
  {
234
  "id": "S014",
 
239
  "severity": "major",
240
  "pipeline_corrected": "أنت طالب مجتهد.",
241
  "pipeline_suggestions": 2,
242
+ "pipeline_ms": 1359,
243
+ "spelling_ms": 399,
244
+ "grammar_ms": 479,
245
+ "punctuation_ms": 260,
246
+ "verdict": "TP",
247
+ "detail": "Exact match"
248
  },
249
  {
250
  "id": "S015",
 
255
  "severity": "major",
256
  "pipeline_corrected": "اننا نحب الوطن.",
257
  "pipeline_suggestions": 1,
258
+ "pipeline_ms": 1566,
259
+ "spelling_ms": 540,
260
+ "grammar_ms": 433,
261
+ "punctuation_ms": 377,
262
  "verdict": "FP",
263
  "detail": "Wrong correction"
264
  },
 
271
  "severity": "major",
272
  "pipeline_corrected": "إن شاء الله سنفوز.",
273
  "pipeline_suggestions": 2,
274
+ "pipeline_ms": 1891,
275
+ "spelling_ms": 670,
276
+ "grammar_ms": 529,
277
+ "punctuation_ms": 474,
278
+ "verdict": "TP",
279
+ "detail": "Exact match"
280
  },
281
  {
282
  "id": "S017",
 
287
  "severity": "none",
288
  "pipeline_corrected": "اقترب الموعد النهائي.",
289
  "pipeline_suggestions": 1,
290
+ "pipeline_ms": 1622,
291
+ "spelling_ms": 483,
292
+ "grammar_ms": 478,
293
+ "punctuation_ms": 444,
294
+ "verdict": "TN",
295
+ "detail": "Only punctuation added"
296
  },
297
  {
298
  "id": "S018",
 
303
  "severity": "major",
304
  "pipeline_corrected": "أرسل الرسالة فوراً.",
305
  "pipeline_suggestions": 2,
306
+ "pipeline_ms": 1576,
307
+ "spelling_ms": 428,
308
+ "grammar_ms": 531,
309
+ "punctuation_ms": 395,
310
+ "verdict": "TP",
311
+ "detail": "Exact match"
312
  },
313
  {
314
  "id": "S019",
 
319
  "severity": "major",
320
  "pipeline_corrected": "آخر الأخبار في المساء.",
321
  "pipeline_suggestions": 2,
322
+ "pipeline_ms": 1721,
323
+ "spelling_ms": 493,
324
+ "grammar_ms": 486,
325
+ "punctuation_ms": 526,
326
+ "verdict": "TP",
327
+ "detail": "Exact match"
328
  },
329
  {
330
  "id": "S020",
 
335
  "severity": "major",
336
  "pipeline_corrected": "أسرع في المشي قليلاً!",
337
  "pipeline_suggestions": 2,
338
+ "pipeline_ms": 2011,
339
+ "spelling_ms": 554,
340
+ "grammar_ms": 634,
341
+ "punctuation_ms": 608,
342
+ "verdict": "TP",
343
+ "detail": "Exact match"
344
  },
345
  {
346
  "id": "S021",
 
351
  "severity": "major",
352
  "pipeline_corrected": "أجمل مكان في العالم؟",
353
  "pipeline_suggestions": 2,
354
+ "pipeline_ms": 1706,
355
+ "spelling_ms": 623,
356
+ "grammar_ms": 485,
357
+ "punctuation_ms": 378,
358
+ "verdict": "TP",
359
+ "detail": "Exact match"
360
  },
361
  {
362
  "id": "S022",
 
367
  "severity": "major",
368
  "pipeline_corrected": "اعلن الرئيس عن القرار.",
369
  "pipeline_suggestions": 1,
370
+ "pipeline_ms": 1783,
371
+ "spelling_ms": 648,
372
+ "grammar_ms": 478,
373
+ "punctuation_ms": 440,
374
  "verdict": "FP",
375
  "detail": "Wrong correction"
376
  },
 
383
  "severity": "major",
384
  "pipeline_corrected": "أكد الوزير على الخطة.",
385
  "pipeline_suggestions": 2,
386
+ "pipeline_ms": 1680,
387
+ "spelling_ms": 586,
388
+ "grammar_ms": 496,
389
+ "punctuation_ms": 382,
390
+ "verdict": "TP",
391
+ "detail": "Exact match"
392
  },
393
  {
394
  "id": "S024",
 
399
  "severity": "major",
400
  "pipeline_corrected": "أشار التقرير إلى ذلك.",
401
  "pipeline_suggestions": 2,
402
+ "pipeline_ms": 1766,
403
+ "spelling_ms": 689,
404
+ "grammar_ms": 487,
405
+ "punctuation_ms": 370,
406
+ "verdict": "TP",
407
+ "detail": "Exact match"
408
  },
409
  {
410
  "id": "S025",
 
415
  "severity": "major",
416
  "pipeline_corrected": "أعتقد انه سيحضر غداً.",
417
  "pipeline_suggestions": 2,
418
+ "pipeline_ms": 1719,
419
+ "spelling_ms": 561,
420
+ "grammar_ms": 530,
421
+ "punctuation_ms": 413,
422
+ "verdict": "TP",
423
+ "detail": "Partial improvement"
424
  },
425
  {
426
  "id": "S026",
 
431
  "severity": "major",
432
  "pipeline_corrected": "والأسعار مرتفعة جداً.",
433
  "pipeline_suggestions": 2,
434
+ "pipeline_ms": 1905,
435
+ "spelling_ms": 589,
436
+ "grammar_ms": 592,
437
+ "punctuation_ms": 506,
438
+ "verdict": "TP",
439
+ "detail": "Exact match"
440
  },
441
  {
442
  "id": "S027",
 
447
  "severity": "major",
448
  "pipeline_corrected": "بالإضافة إلى ذلك.",
449
  "pipeline_suggestions": 2,
450
+ "pipeline_ms": 1555,
451
+ "spelling_ms": 538,
452
+ "grammar_ms": 429,
453
+ "punctuation_ms": 371,
454
+ "verdict": "TP",
455
+ "detail": "Exact match"
456
  },
457
  {
458
  "id": "S028",
 
463
  "severity": "major",
464
  "pipeline_corrected": "فالانسان يحتاج للعلم.",
465
  "pipeline_suggestions": 1,
466
+ "pipeline_ms": 1849,
467
+ "spelling_ms": 576,
468
+ "grammar_ms": 483,
469
+ "punctuation_ms": 575,
470
  "verdict": "FP",
471
  "detail": "Wrong correction"
472
  },
 
479
  "severity": "major",
480
  "pipeline_corrected": "كلا الأطفال في اللعب.",
481
  "pipeline_suggestions": 2,
482
+ "pipeline_ms": 2995,
483
+ "spelling_ms": 1635,
484
+ "grammar_ms": 636,
485
+ "punctuation_ms": 413,
486
  "verdict": "FP",
487
  "detail": "Wrong correction"
488
  },
 
495
  "severity": "major",
496
  "pipeline_corrected": "للأسف لم ينجح.",
497
  "pipeline_suggestions": 2,
498
+ "pipeline_ms": 1590,
499
+ "spelling_ms": 498,
500
  "grammar_ms": 437,
501
+ "punctuation_ms": 437,
502
+ "verdict": "TP",
503
+ "detail": "Exact match"
504
  },
505
  {
506
  "id": "S031",
 
511
  "severity": "major",
512
  "pipeline_corrected": "المدرسة كبيرة وجميلة.",
513
  "pipeline_suggestions": 3,
514
+ "pipeline_ms": 2278,
515
+ "spelling_ms": 832,
516
+ "grammar_ms": 498,
517
+ "punctuation_ms": 730,
518
  "verdict": "TP",
519
+ "detail": "Exact match"
520
  },
521
  {
522
  "id": "S032",
 
527
  "severity": "major",
528
  "pipeline_corrected": "الجامعة في القاهرة.",
529
  "pipeline_suggestions": 2,
530
+ "pipeline_ms": 1974,
531
+ "spelling_ms": 741,
532
+ "grammar_ms": 492,
533
+ "punctuation_ms": 520,
534
  "verdict": "TP",
535
+ "detail": "Exact match"
536
  },
537
  {
538
  "id": "S033",
 
543
  "severity": "major",
544
  "pipeline_corrected": "السيارة سريعة جداً.",
545
  "pipeline_suggestions": 3,
546
+ "pipeline_ms": 1858,
547
+ "spelling_ms": 688,
548
+ "grammar_ms": 535,
549
+ "punctuation_ms": 418,
550
  "verdict": "TP",
551
+ "detail": "Exact match"
552
  },
553
  {
554
  "id": "S034",
 
559
  "severity": "major",
560
  "pipeline_corrected": "الشجرة طويلة وجميلة.",
561
  "pipeline_suggestions": 3,
562
+ "pipeline_ms": 2099,
563
+ "spelling_ms": 865,
564
+ "grammar_ms": 495,
565
  "punctuation_ms": 519,
566
  "verdict": "TP",
567
+ "detail": "Exact match"
568
  },
569
  {
570
  "id": "S035",
 
575
  "severity": "major",
576
  "pipeline_corrected": "الحياة صعبة في المدينة.",
577
  "pipeline_suggestions": 3,
578
+ "pipeline_ms": 2175,
579
+ "spelling_ms": 742,
580
+ "grammar_ms": 594,
581
+ "punctuation_ms": 622,
582
  "verdict": "TP",
583
+ "detail": "Exact match"
584
  },
585
  {
586
  "id": "S036",
 
591
  "severity": "major",
592
  "pipeline_corrected": "المكتبة قريبة من البيت.",
593
  "pipeline_suggestions": 3,
594
+ "pipeline_ms": 2074,
595
+ "spelling_ms": 701,
596
+ "grammar_ms": 536,
597
+ "punctuation_ms": 573,
598
  "verdict": "TP",
599
+ "detail": "Exact match"
600
  },
601
  {
602
  "id": "S037",
 
607
  "severity": "major",
608
  "pipeline_corrected": "الغرفة نظيفة ومرتبة.",
609
  "pipeline_suggestions": 3,
610
+ "pipeline_ms": 4644,
611
+ "spelling_ms": 3274,
612
+ "grammar_ms": 610,
613
+ "punctuation_ms": 540,
614
  "verdict": "TP",
615
+ "detail": "Exact match"
616
  },
617
  {
618
  "id": "S038",
 
623
  "severity": "major",
624
  "pipeline_corrected": "القصة مثيرة للاهتمام!",
625
  "pipeline_suggestions": 3,
626
+ "pipeline_ms": 3675,
627
+ "spelling_ms": 2465,
628
+ "grammar_ms": 483,
629
+ "punctuation_ms": 511,
630
  "verdict": "TP",
631
+ "detail": "Exact match"
632
  },
633
  {
634
  "id": "S039",
 
639
  "severity": "major",
640
  "pipeline_corrected": "الرحلة طويلة ومتعبة.",
641
  "pipeline_suggestions": 3,
642
+ "pipeline_ms": 3737,
643
+ "spelling_ms": 2430,
644
+ "grammar_ms": 599,
645
+ "punctuation_ms": 488,
646
  "verdict": "TP",
647
+ "detail": "Exact match"
648
  },
649
  {
650
  "id": "S040",
 
655
  "severity": "major",
656
  "pipeline_corrected": "الوظيفة صعبة لكنها مفيدة.",
657
  "pipeline_suggestions": 3,
658
+ "pipeline_ms": 4159,
659
+ "spelling_ms": 2751,
660
+ "grammar_ms": 491,
661
+ "punctuation_ms": 695,
662
  "verdict": "TP",
663
+ "detail": "Exact match"
664
  },
665
  {
666
  "id": "S041",
 
671
  "severity": "major",
672
  "pipeline_corrected": "بالمدرسة الكبيرة",
673
  "pipeline_suggestions": 2,
674
+ "pipeline_ms": 4988,
675
+ "spelling_ms": 4001,
676
+ "grammar_ms": 327,
677
+ "punctuation_ms": 439,
678
  "verdict": "TP",
679
  "detail": "Exact match"
680
  },
 
687
  "severity": "major",
688
  "pipeline_corrected": "والجامعة المصرية",
689
  "pipeline_suggestions": 2,
690
+ "pipeline_ms": 3656,
691
+ "spelling_ms": 2532,
692
+ "grammar_ms": 392,
693
+ "punctuation_ms": 500,
694
  "verdict": "TP",
695
  "detail": "Exact match"
696
  },
 
703
  "severity": "major",
704
  "pipeline_corrected": "السيارة الجديدة",
705
  "pipeline_suggestions": 2,
706
+ "pipeline_ms": 4550,
707
+ "spelling_ms": 3621,
708
+ "grammar_ms": 333,
709
+ "punctuation_ms": 375,
710
  "verdict": "TP",
711
  "detail": "Partial improvement"
712
  },
 
719
  "severity": "major",
720
  "pipeline_corrected": "كالمدرسه قديمة",
721
  "pipeline_suggestions": 1,
722
+ "pipeline_ms": 4082,
723
+ "spelling_ms": 3050,
724
+ "grammar_ms": 432,
725
+ "punctuation_ms": 381,
726
  "verdict": "FP",
727
  "detail": "Wrong correction"
728
  },
 
735
  "severity": "major",
736
  "pipeline_corrected": "للمدرسة الابتدائية",
737
  "pipeline_suggestions": 2,
738
+ "pipeline_ms": 4153,
739
+ "spelling_ms": 2978,
740
+ "grammar_ms": 327,
741
+ "punctuation_ms": 594,
742
  "verdict": "TP",
743
  "detail": "Exact match"
744
  },
 
751
  "severity": "major",
752
  "pipeline_corrected": "ذهبت إلى المكتبة؟",
753
  "pipeline_suggestions": 2,
754
+ "pipeline_ms": 2945,
755
+ "spelling_ms": 1796,
756
+ "grammar_ms": 443,
757
+ "punctuation_ms": 487,
758
  "verdict": "TP",
759
+ "detail": "Exact match"
760
  },
761
  {
762
  "id": "S047",
 
767
  "severity": "major",
768
  "pipeline_corrected": "المستشفى الكبير",
769
  "pipeline_suggestions": 1,
770
+ "pipeline_ms": 2470,
771
+ "spelling_ms": 1529,
772
+ "grammar_ms": 326,
773
+ "punctuation_ms": 401,
774
  "verdict": "TP",
775
  "detail": "Exact match"
776
  },
 
783
  "severity": "major",
784
  "pipeline_corrected": "هدى الطالبة ممتازة",
785
  "pipeline_suggestions": 2,
786
+ "pipeline_ms": 2781,
787
+ "spelling_ms": 1610,
788
+ "grammar_ms": 519,
789
+ "punctuation_ms": 431,
790
  "verdict": "FP",
791
  "detail": "Wrong correction"
792
  },
 
799
  "severity": "major",
800
  "pipeline_corrected": "مصطفى طالب مجتهد.",
801
  "pipeline_suggestions": 2,
802
+ "pipeline_ms": 2432,
803
+ "spelling_ms": 1465,
804
+ "grammar_ms": 482,
805
+ "punctuation_ms": 267,
806
+ "verdict": "TP",
807
+ "detail": "Exact match"
808
  },
809
  {
810
  "id": "S050",
 
815
  "severity": "major",
816
  "pipeline_corrected": "موسى نبي عظيم.",
817
  "pipeline_suggestions": 2,
818
+ "pipeline_ms": 2483,
819
+ "spelling_ms": 1626,
820
+ "grammar_ms": 376,
821
+ "punctuation_ms": 267,
822
+ "verdict": "TP",
823
+ "detail": "Exact match"
824
  },
825
  {
826
  "id": "S051",
 
831
  "severity": "none",
832
  "pipeline_corrected": "علي يدرس في الكلية.",
833
  "pipeline_suggestions": 1,
834
+ "pipeline_ms": 2644,
835
+ "spelling_ms": 1528,
836
+ "grammar_ms": 490,
837
+ "punctuation_ms": 409,
838
+ "verdict": "TN",
839
+ "detail": "Only punctuation added"
840
  },
841
  {
842
  "id": "S052",
 
847
  "severity": "major",
848
  "pipeline_corrected": "ذهب إلى السوق.",
849
  "pipeline_suggestions": 2,
850
+ "pipeline_ms": 2478,
851
+ "spelling_ms": 1516,
852
+ "grammar_ms": 437,
853
+ "punctuation_ms": 306,
854
  "verdict": "TP",
855
+ "detail": "Exact match"
856
  },
857
  {
858
  "id": "S053",
 
863
  "severity": "major",
864
  "pipeline_corrected": "بنى المبنى الجديد.",
865
  "pipeline_suggestions": 2,
866
+ "pipeline_ms": 2441,
867
+ "spelling_ms": 1448,
868
+ "grammar_ms": 381,
869
+ "punctuation_ms": 395,
870
+ "verdict": "TP",
871
+ "detail": "Exact match"
872
  },
873
  {
874
  "id": "S054",
 
879
  "severity": "major",
880
  "pipeline_corrected": "ذهبت في البيت",
881
  "pipeline_suggestions": 1,
882
+ "pipeline_ms": 2773,
883
+ "spelling_ms": 1751,
884
+ "grammar_ms": 391,
885
+ "punctuation_ms": 415,
886
  "verdict": "TP",
887
  "detail": "Exact match"
888
  },
 
895
  "severity": "major",
896
  "pipeline_corrected": "خرج منالمدرسة.",
897
  "pipeline_suggestions": 1,
898
+ "pipeline_ms": 3061,
899
+ "spelling_ms": 2075,
900
+ "grammar_ms": 386,
901
+ "punctuation_ms": 384,
902
  "verdict": "FP",
903
  "detail": "Wrong correction"
904
  },
 
911
  "severity": "major",
912
  "pipeline_corrected": "بقي عندالباب.",
913
  "pipeline_suggestions": 1,
914
+ "pipeline_ms": 2616,
915
+ "spelling_ms": 1640,
916
+ "grammar_ms": 490,
917
+ "punctuation_ms": 269,
918
  "verdict": "FP",
919
  "detail": "Wrong correction"
920
  },
 
927
  "severity": "major",
928
  "pipeline_corrected": "جلس عندالنافذة.",
929
  "pipeline_suggestions": 1,
930
+ "pipeline_ms": 2763,
931
+ "spelling_ms": 1517,
932
+ "grammar_ms": 585,
933
+ "punctuation_ms": 445,
934
  "verdict": "FP",
935
  "detail": "Wrong correction"
936
  },
 
943
  "severity": "major",
944
  "pipeline_corrected": "رجع الىالبيت.",
945
  "pipeline_suggestions": 1,
946
+ "pipeline_ms": 3082,
947
+ "spelling_ms": 2096,
948
+ "grammar_ms": 431,
949
+ "punctuation_ms": 338,
950
  "verdict": "FP",
951
  "detail": "Wrong correction"
952
  },
 
959
  "severity": "major",
960
  "pipeline_corrected": "خرج من الباب الخلفي.",
961
  "pipeline_suggestions": 2,
962
+ "pipeline_ms": 2927,
963
+ "spelling_ms": 1719,
964
+ "grammar_ms": 497,
965
+ "punctuation_ms": 494,
966
+ "verdict": "TP",
967
+ "detail": "Exact match"
968
  },
969
  {
970
  "id": "S060",
 
975
  "severity": "major",
976
  "pipeline_corrected": "نظر الىالسماء.",
977
  "pipeline_suggestions": 1,
978
+ "pipeline_ms": 2998,
979
+ "spelling_ms": 1978,
980
+ "grammar_ms": 479,
981
+ "punctuation_ms": 323,
982
  "verdict": "FP",
983
  "detail": "Wrong correction"
984
  },
 
991
  "severity": "none",
992
  "pipeline_corrected": "أنا ذهبت إلى الجامعة.",
993
  "pipeline_suggestions": 1,
994
+ "pipeline_ms": 3099,
995
+ "spelling_ms": 1856,
996
+ "grammar_ms": 489,
997
+ "punctuation_ms": 536,
998
+ "verdict": "TN",
999
+ "detail": "Only punctuation added"
1000
  },
1001
  {
1002
  "id": "S062",
 
1007
  "severity": "none",
1008
  "pipeline_corrected": "هذه المدرسة جميلة جداً.",
1009
  "pipeline_suggestions": 1,
1010
+ "pipeline_ms": 4323,
1011
+ "spelling_ms": 2252,
1012
+ "grammar_ms": 642,
1013
+ "punctuation_ms": 464,
1014
+ "verdict": "TN",
1015
+ "detail": "Only punctuation added"
1016
  },
1017
  {
1018
  "id": "S063",
 
1023
  "severity": "none",
1024
  "pipeline_corrected": "كان الجو ممطراً اليوم؟",
1025
  "pipeline_suggestions": 1,
1026
+ "pipeline_ms": 4450,
1027
+ "spelling_ms": 2278,
1028
+ "grammar_ms": 729,
1029
+ "punctuation_ms": 497,
1030
+ "verdict": "TN",
1031
+ "detail": "Only punctuation added"
1032
  },
1033
  {
1034
  "id": "S064",
 
1039
  "severity": "none",
1040
  "pipeline_corrected": "وكان أحمد في المنزل.",
1041
  "pipeline_suggestions": 1,
1042
+ "pipeline_ms": 3157,
1043
+ "spelling_ms": 1560,
1044
+ "grammar_ms": 485,
1045
+ "punctuation_ms": 454,
1046
+ "verdict": "TN",
1047
+ "detail": "Only punctuation added"
1048
  },
1049
  {
1050
  "id": "S065",
 
1055
  "severity": "none",
1056
  "pipeline_corrected": "إلى اللقاء يا صديقي.",
1057
  "pipeline_suggestions": 1,
1058
+ "pipeline_ms": 3780,
1059
+ "spelling_ms": 2220,
1060
+ "grammar_ms": 480,
1061
+ "punctuation_ms": 367,
1062
+ "verdict": "TN",
1063
+ "detail": "Only punctuation added"
1064
  },
1065
  {
1066
  "id": "S066",
 
1071
  "severity": "none",
1072
  "pipeline_corrected": "ذلك الكتاب مفيد جداً.",
1073
  "pipeline_suggestions": 1,
1074
+ "pipeline_ms": 3597,
1075
+ "spelling_ms": 1896,
1076
+ "grammar_ms": 588,
1077
+ "punctuation_ms": 374,
1078
+ "verdict": "TN",
1079
+ "detail": "Only punctuation added"
1080
  },
1081
  {
1082
  "id": "S067",
 
1087
  "severity": "none",
1088
  "pipeline_corrected": "لكن الأمر صعب علينا.",
1089
  "pipeline_suggestions": 1,
1090
+ "pipeline_ms": 3659,
1091
+ "spelling_ms": 2195,
1092
+ "grammar_ms": 492,
1093
+ "punctuation_ms": 447,
1094
+ "verdict": "TN",
1095
+ "detail": "Only punctuation added"
1096
  },
1097
  {
1098
  "id": "S068",
 
1103
  "severity": "none",
1104
  "pipeline_corrected": "هذا أو ذاك سواء عندي.",
1105
  "pipeline_suggestions": 1,
1106
+ "pipeline_ms": 4575,
1107
+ "spelling_ms": 2778,
1108
+ "grammar_ms": 578,
1109
+ "punctuation_ms": 426,
1110
+ "verdict": "TN",
1111
+ "detail": "Only punctuation added"
1112
  },
1113
  {
1114
  "id": "S069",
 
1119
  "severity": "none",
1120
  "pipeline_corrected": "الطالب المجتهد ينجح دائماً.",
1121
  "pipeline_suggestions": 1,
1122
+ "pipeline_ms": 3740,
1123
+ "spelling_ms": 1907,
1124
+ "grammar_ms": 586,
1125
+ "punctuation_ms": 544,
1126
+ "verdict": "TN",
1127
+ "detail": "Only punctuation added"
1128
  },
1129
  {
1130
  "id": "S070",
 
1135
  "severity": "none",
1136
  "pipeline_corrected": "العلم نور والجهل ظلام.",
1137
  "pipeline_suggestions": 1,
1138
+ "pipeline_ms": 4941,
1139
+ "spelling_ms": 3158,
1140
+ "grammar_ms": 694,
1141
+ "punctuation_ms": 511,
1142
+ "verdict": "TN",
1143
+ "detail": "Only punctuation added"
1144
  },
1145
  {
1146
  "id": "S071",
 
1151
  "severity": "none",
1152
  "pipeline_corrected": "أحب القراءة والكتابة.",
1153
  "pipeline_suggestions": 1,
1154
+ "pipeline_ms": 3160,
1155
+ "spelling_ms": 1507,
1156
+ "grammar_ms": 443,
1157
+ "punctuation_ms": 523,
1158
+ "verdict": "TN",
1159
+ "detail": "Only punctuation added"
1160
  },
1161
  {
1162
  "id": "S072",
 
1167
  "severity": "none",
1168
  "pipeline_corrected": "المعلم يشرح الدرس.",
1169
  "pipeline_suggestions": 1,
1170
+ "pipeline_ms": 3649,
1171
+ "spelling_ms": 1720,
1172
+ "grammar_ms": 430,
1173
+ "punctuation_ms": 492,
1174
+ "verdict": "TN",
1175
+ "detail": "Only punctuation added"
1176
  },
1177
  {
1178
  "id": "S073",
 
1183
  "severity": "none",
1184
  "pipeline_corrected": "ذهبت إلى السوق واشتريت خبزاً.",
1185
  "pipeline_suggestions": 1,
1186
+ "pipeline_ms": 6100,
1187
+ "spelling_ms": 3889,
1188
+ "grammar_ms": 810,
1189
+ "punctuation_ms": 758,
1190
+ "verdict": "TN",
1191
+ "detail": "Only punctuation added"
1192
  },
1193
  {
1194
  "id": "S074",
 
1199
  "severity": "none",
1200
  "pipeline_corrected": "الطقس جميل في الربيع.",
1201
  "pipeline_suggestions": 1,
1202
+ "pipeline_ms": 3330,
1203
+ "spelling_ms": 1617,
1204
+ "grammar_ms": 532,
1205
+ "punctuation_ms": 417,
1206
+ "verdict": "TN",
1207
+ "detail": "Only punctuation added"
1208
  },
1209
  {
1210
  "id": "S075",
 
1215
  "severity": "none",
1216
  "pipeline_corrected": "نحن نعمل بجد كل يوم.",
1217
  "pipeline_suggestions": 1,
1218
+ "pipeline_ms": 4609,
1219
+ "spelling_ms": 2915,
1220
+ "grammar_ms": 591,
1221
+ "punctuation_ms": 406,
1222
+ "verdict": "TN",
1223
+ "detail": "Only punctuation added"
1224
  },
1225
  {
1226
  "id": "S076",
 
1231
  "severity": "critical",
1232
  "pipeline_corrected": "أنا ذهبت إلى المدرسة.",
1233
  "pipeline_suggestions": 3,
1234
+ "pipeline_ms": 5931,
1235
+ "spelling_ms": 4275,
1236
+ "grammar_ms": 536,
1237
+ "punctuation_ms": 580,
1238
  "verdict": "TP",
1239
+ "detail": "Exact match"
1240
  },
1241
  {
1242
  "id": "S077",
 
1247
  "severity": "critical",
1248
  "pipeline_corrected": "أيضا الجامعة كبيرة.",
1249
  "pipeline_suggestions": 2,
1250
+ "pipeline_ms": 4776,
1251
+ "spelling_ms": 2819,
1252
+ "grammar_ms": 595,
1253
+ "punctuation_ms": 482,
1254
  "verdict": "TP",
1255
+ "detail": "Exact match"
1256
  },
1257
  {
1258
  "id": "S078",
 
1263
  "severity": "critical",
1264
  "pipeline_corrected": "لأن المدرسة بعيدة جداً.",
1265
  "pipeline_suggestions": 4,
1266
+ "pipeline_ms": 4641,
1267
+ "spelling_ms": 2773,
1268
+ "grammar_ms": 642,
1269
+ "punctuation_ms": 540,
1270
  "verdict": "TP",
1271
+ "detail": "Exact match"
1272
  },
1273
  {
1274
  "id": "S079",
 
1279
  "severity": "critical",
1280
  "pipeline_corrected": "اين أن الجامعة الكبيرة",
1281
  "pipeline_suggestions": 2,
1282
+ "pipeline_ms": 2614,
1283
+ "spelling_ms": 696,
1284
+ "grammar_ms": 496,
1285
+ "punctuation_ms": 510,
1286
  "verdict": "FP",
1287
  "detail": "Wrong correction"
1288
  },
 
1295
  "severity": "critical",
1296
  "pipeline_corrected": "أول مرة أزور المكتبة.",
1297
  "pipeline_suggestions": 4,
1298
+ "pipeline_ms": 2637,
1299
+ "spelling_ms": 808,
1300
+ "grammar_ms": 496,
1301
+ "punctuation_ms": 522,
1302
  "verdict": "TP",
1303
+ "detail": "Exact match"
1304
  }
1305
  ]
1306
  }
tests/v2/test_level1_raw.py CHANGED
@@ -1,8 +1,18 @@
1
  """
2
  BAYAN v2.0 — Level 1: Raw Model Tests
3
  ======================================
4
- Tests each ML model DIRECTLY no filters, no pipeline, no StageLocker.
5
- Measures the raw model ceiling: what's the best each model can do?
 
 
 
 
 
 
 
 
 
 
6
 
7
  Usage:
8
  python tests/v2/test_level1_raw.py --url URL [--dataset DATASET]
@@ -19,9 +29,11 @@ import requests
19
 
20
  # Datasets
21
  DATASETS_DIR = Path(__file__).parent.parent / "phase10" / "gold_datasets"
22
-
23
  REPORT_DIR = Path(__file__).parent / "reports"
24
 
 
 
 
25
 
26
  @dataclass
27
  class RawModelResult:
@@ -38,10 +50,18 @@ class RawModelResult:
38
  grammar_ms: int = 0
39
  punctuation_raw: str = ""
40
  punctuation_ms: int = 0
41
- # Analysis
42
  spelling_changed: bool = False
43
  grammar_changed: bool = False
44
  punctuation_changed: bool = False
 
 
 
 
 
 
 
 
45
 
46
 
47
  class APIClient:
@@ -67,49 +87,114 @@ class APIClient:
67
  return {"error": str(e)}, ms
68
 
69
  def spelling_raw(self, text):
70
- """Call /api/spelling — raw spelling model through API."""
71
  data, ms = self._post("/api/spelling", text)
72
  corrected = data.get("corrected_text", data.get("corrected", text))
73
  return corrected, ms
74
 
75
  def grammar_raw(self, text):
76
- """Call /api/grammar — raw grammar model through API."""
77
  data, ms = self._post("/api/grammar", text)
78
  corrected = data.get("corrected_text", data.get("corrected", text))
79
  return corrected, ms
80
 
81
  def punctuation_raw(self, text):
82
- """Call /api/punctuation — raw punctuation model through API."""
83
  data, ms = self._post("/api/punctuation", text)
84
  corrected = data.get("corrected_text", data.get("corrected", text))
85
  return corrected, ms
86
 
87
 
88
  def load_datasets(dataset_filter=None):
89
- """Load all gold datasets."""
90
  datasets = {}
91
  for f in sorted(DATASETS_DIR.glob("*.json")):
92
  name = f.stem
93
  if dataset_filter and name != dataset_filter:
94
  continue
95
  with open(f, 'r', encoding='utf-8') as fh:
96
- data = json.load(fh)
97
- datasets[name] = data
98
  return datasets
99
 
100
 
101
- def strip_diacritics(text):
102
- return re.sub(r'[\u064B-\u065F\u0670]', '', text)
103
-
104
-
105
  def normalize(text):
106
- t = strip_diacritics(text)
107
  t = re.sub(r'\s+', ' ', t).strip()
108
  return t
109
 
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  def run_level1(api: APIClient, datasets: dict) -> List[RawModelResult]:
112
- """Run each test case through all 3 raw models."""
113
  results = []
114
  total = sum(len(v) for v in datasets.values())
115
  idx = 0
@@ -138,30 +223,37 @@ def run_level1(api: APIClient, datasets: dict) -> List[RawModelResult]:
138
  try:
139
  r.spelling_raw, r.spelling_ms = api.spelling_raw(inp)
140
  r.spelling_changed = (normalize(r.spelling_raw) != normalize(inp))
 
 
 
141
  except Exception as e:
142
  r.spelling_raw = f"ERROR: {e}"
 
143
 
144
  # ── Grammar ──
145
  try:
146
  r.grammar_raw, r.grammar_ms = api.grammar_raw(inp)
147
  r.grammar_changed = (normalize(r.grammar_raw) != normalize(inp))
 
 
 
148
  except Exception as e:
149
  r.grammar_raw = f"ERROR: {e}"
 
150
 
151
  # ── Punctuation ──
152
  try:
153
  r.punctuation_raw, r.punctuation_ms = api.punctuation_raw(inp)
154
  r.punctuation_changed = (normalize(r.punctuation_raw) != normalize(inp))
 
 
 
155
  except Exception as e:
156
  r.punctuation_raw = f"ERROR: {e}"
 
157
 
158
  total_ms = r.spelling_ms + r.grammar_ms + r.punctuation_ms
159
- changes = []
160
- if r.spelling_changed: changes.append("S")
161
- if r.grammar_changed: changes.append("G")
162
- if r.punctuation_changed: changes.append("P")
163
- change_str = "+".join(changes) if changes else "none"
164
- print(f"[{change_str}] ({total_ms}ms)")
165
 
166
  results.append(r)
167
 
@@ -169,74 +261,100 @@ def run_level1(api: APIClient, datasets: dict) -> List[RawModelResult]:
169
 
170
 
171
  def analyze_results(results: List[RawModelResult]) -> dict:
172
- """Produce per-model analysis of raw outputs."""
173
  analysis = {
174
  "total": len(results),
175
- "by_model": {
176
- "spelling": {"changed": 0, "unchanged": 0, "errors": 0},
177
- "grammar": {"changed": 0, "unchanged": 0, "errors": 0},
178
- "punctuation": {"changed": 0, "unchanged": 0, "errors": 0},
179
- },
180
  "by_dataset": {},
181
  }
182
 
183
- for r in results:
184
- ds = r.dataset
185
- if ds not in analysis["by_dataset"]:
186
- analysis["by_dataset"][ds] = {
187
- "total": 0,
188
- "spelling_changed": 0,
189
- "grammar_changed": 0,
190
- "punctuation_changed": 0,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
  }
192
- analysis["by_dataset"][ds]["total"] += 1
193
-
194
- # Spelling
195
- if "ERROR" in r.spelling_raw:
196
- analysis["by_model"]["spelling"]["errors"] += 1
197
- elif r.spelling_changed:
198
- analysis["by_model"]["spelling"]["changed"] += 1
199
- analysis["by_dataset"][ds]["spelling_changed"] += 1
200
- else:
201
- analysis["by_model"]["spelling"]["unchanged"] += 1
202
-
203
- # Grammar
204
- if "ERROR" in r.grammar_raw:
205
- analysis["by_model"]["grammar"]["errors"] += 1
206
- elif r.grammar_changed:
207
- analysis["by_model"]["grammar"]["changed"] += 1
208
- analysis["by_dataset"][ds]["grammar_changed"] += 1
209
- else:
210
- analysis["by_model"]["grammar"]["unchanged"] += 1
211
-
212
- # Punctuation
213
- if "ERROR" in r.punctuation_raw:
214
- analysis["by_model"]["punctuation"]["errors"] += 1
215
- elif r.punctuation_changed:
216
- analysis["by_model"]["punctuation"]["changed"] += 1
217
- analysis["by_dataset"][ds]["punctuation_changed"] += 1
218
- else:
219
- analysis["by_model"]["punctuation"]["unchanged"] += 1
220
 
221
  return analysis
222
 
223
 
224
- def print_analysis(analysis: dict):
225
  print(f"\n{'='*60}")
226
  print("LEVEL 1: RAW MODEL ANALYSIS")
227
  print(f"{'='*60}")
228
 
 
229
  print(f"\n## Per-Model Summary ({analysis['total']} tests)")
230
- print(f"| Model | Changed | Unchanged | Errors |")
231
- print(f"|-------------|---------|-----------|--------|")
232
  for model, data in analysis["by_model"].items():
233
- print(f"| {model:<11} | {data['changed']:>7} | {data['unchanged']:>9} | {data['errors']:>6} |")
234
-
235
- print(f"\n## Change Rate by Dataset")
236
- print(f"| Dataset | Total | S-Changed | G-Changed | P-Changed |")
237
- print(f"|--------------|-------|-----------|-----------|-----------|")
238
- for ds, data in sorted(analysis["by_dataset"].items()):
239
- print(f"| {ds:<12} | {data['total']:>5} | {data['spelling_changed']:>9} | {data['grammar_changed']:>9} | {data['punctuation_changed']:>9} |")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
 
242
  def main():
@@ -258,7 +376,7 @@ def main():
258
 
259
  results = run_level1(api, datasets)
260
  analysis = analyze_results(results)
261
- print_analysis(analysis)
262
 
263
  # Save results
264
  REPORT_DIR.mkdir(parents=True, exist_ok=True)
 
1
  """
2
  BAYAN v2.0 — Level 1: Raw Model Tests
3
  ======================================
4
+ Tests each ML model through its individual API endpoint measuring the RAW
5
+ model output with no pipeline integration (no StageLocker, no OffsetMapper,
6
+ no cross-stage interaction).
7
+
8
+ NOTE: On HF Spaces deployment, the solo endpoints (/api/spelling, /api/grammar,
9
+ /api/punctuation) call the models directly with minimal preprocessing. This is
10
+ as close to "raw model" as we can get without local model access.
11
+
12
+ Produces:
13
+ - TP/FP/FN/TN verdicts per model per test case
14
+ - Per-word edit analysis (what words each model changed)
15
+ - Change rate and accuracy per model per dataset
16
 
17
  Usage:
18
  python tests/v2/test_level1_raw.py --url URL [--dataset DATASET]
 
29
 
30
  # Datasets
31
  DATASETS_DIR = Path(__file__).parent.parent / "phase10" / "gold_datasets"
 
32
  REPORT_DIR = Path(__file__).parent / "reports"
33
 
34
+ # Terminal punctuation to strip before comparison
35
+ _TERMINAL_PUNCT = '.،؛؟!?!'
36
+
37
 
38
  @dataclass
39
  class RawModelResult:
 
50
  grammar_ms: int = 0
51
  punctuation_raw: str = ""
52
  punctuation_ms: int = 0
53
+ # Change detection
54
  spelling_changed: bool = False
55
  grammar_changed: bool = False
56
  punctuation_changed: bool = False
57
+ # Verdicts (TP/TN/FP/FN)
58
+ spelling_verdict: str = ""
59
+ grammar_verdict: str = ""
60
+ punctuation_verdict: str = ""
61
+ # Word-level edits
62
+ spelling_edits: str = "" # "word1→word2, word3→word4"
63
+ grammar_edits: str = ""
64
+ punctuation_edits: str = ""
65
 
66
 
67
  class APIClient:
 
87
  return {"error": str(e)}, ms
88
 
89
  def spelling_raw(self, text):
 
90
  data, ms = self._post("/api/spelling", text)
91
  corrected = data.get("corrected_text", data.get("corrected", text))
92
  return corrected, ms
93
 
94
  def grammar_raw(self, text):
 
95
  data, ms = self._post("/api/grammar", text)
96
  corrected = data.get("corrected_text", data.get("corrected", text))
97
  return corrected, ms
98
 
99
  def punctuation_raw(self, text):
 
100
  data, ms = self._post("/api/punctuation", text)
101
  corrected = data.get("corrected_text", data.get("corrected", text))
102
  return corrected, ms
103
 
104
 
105
  def load_datasets(dataset_filter=None):
 
106
  datasets = {}
107
  for f in sorted(DATASETS_DIR.glob("*.json")):
108
  name = f.stem
109
  if dataset_filter and name != dataset_filter:
110
  continue
111
  with open(f, 'r', encoding='utf-8') as fh:
112
+ datasets[name] = json.load(fh)
 
113
  return datasets
114
 
115
 
 
 
 
 
116
  def normalize(text):
117
+ t = re.sub(r'[\u064B-\u065F\u0670]', '', text)
118
  t = re.sub(r'\s+', ' ', t).strip()
119
  return t
120
 
121
 
122
+ def _edit_distance(a, b):
123
+ if len(a) < len(b):
124
+ return _edit_distance(b, a)
125
+ if len(b) == 0:
126
+ return len(a)
127
+ prev = list(range(len(b) + 1))
128
+ for i, ca in enumerate(a):
129
+ curr = [i + 1]
130
+ for j, cb in enumerate(b):
131
+ cost = 0 if ca == cb else 1
132
+ curr.append(min(curr[j] + 1, prev[j + 1] + 1, prev[j] + cost))
133
+ prev = curr
134
+ return prev[len(b)]
135
+
136
+
137
+ def get_word_edits(input_text, output_text):
138
+ """Get word-level edits between input and output."""
139
+ inp_words = normalize(input_text).split()
140
+ out_words = normalize(output_text).split()
141
+ edits = []
142
+
143
+ # Simple alignment: match by position
144
+ max_len = max(len(inp_words), len(out_words))
145
+ for i in range(max_len):
146
+ iw = inp_words[i] if i < len(inp_words) else "∅"
147
+ ow = out_words[i] if i < len(out_words) else "∅"
148
+ if iw != ow:
149
+ edits.append(f"{iw}→{ow}")
150
+
151
+ return ", ".join(edits[:5]) # Cap at 5 edits for readability
152
+
153
+
154
+ def classify_raw(input_text, output_text, expected_text, dataset):
155
+ """Classify raw model output as TP/TN/FP/FN.
156
+
157
+ Same logic as L2/L3 but applied to raw model output.
158
+ """
159
+ inp_n = normalize(input_text)
160
+ out_n = normalize(output_text)
161
+ exp_n = normalize(expected_text)
162
+
163
+ out_stripped = out_n.rstrip(_TERMINAL_PUNCT).rstrip()
164
+ text_changed = (out_n != inp_n)
165
+
166
+ is_preservation = dataset in ('entities', 'religious', 'structured', 'hallucination')
167
+
168
+ if is_preservation:
169
+ if not text_changed:
170
+ return "TN"
171
+ elif out_stripped == inp_n:
172
+ return "TN" # Only punctuation added — not entity corruption
173
+ else:
174
+ return "FP"
175
+ else:
176
+ needs_correction = (inp_n != exp_n)
177
+ if needs_correction:
178
+ if out_n == exp_n or out_stripped == exp_n:
179
+ return "TP"
180
+ elif text_changed and _edit_distance(out_stripped, exp_n) < _edit_distance(inp_n, exp_n):
181
+ return "TP"
182
+ elif text_changed and _edit_distance(out_n, exp_n) < _edit_distance(inp_n, exp_n):
183
+ return "TP"
184
+ elif not text_changed:
185
+ return "FN"
186
+ else:
187
+ return "FP"
188
+ else:
189
+ if not text_changed:
190
+ return "TN"
191
+ elif out_stripped == inp_n:
192
+ return "TN" # Only punctuation added
193
+ else:
194
+ return "FP"
195
+
196
+
197
  def run_level1(api: APIClient, datasets: dict) -> List[RawModelResult]:
 
198
  results = []
199
  total = sum(len(v) for v in datasets.values())
200
  idx = 0
 
223
  try:
224
  r.spelling_raw, r.spelling_ms = api.spelling_raw(inp)
225
  r.spelling_changed = (normalize(r.spelling_raw) != normalize(inp))
226
+ r.spelling_verdict = classify_raw(inp, r.spelling_raw, expected, ds_name)
227
+ if r.spelling_changed:
228
+ r.spelling_edits = get_word_edits(inp, r.spelling_raw)
229
  except Exception as e:
230
  r.spelling_raw = f"ERROR: {e}"
231
+ r.spelling_verdict = "ERR"
232
 
233
  # ── Grammar ──
234
  try:
235
  r.grammar_raw, r.grammar_ms = api.grammar_raw(inp)
236
  r.grammar_changed = (normalize(r.grammar_raw) != normalize(inp))
237
+ r.grammar_verdict = classify_raw(inp, r.grammar_raw, expected, ds_name)
238
+ if r.grammar_changed:
239
+ r.grammar_edits = get_word_edits(inp, r.grammar_raw)
240
  except Exception as e:
241
  r.grammar_raw = f"ERROR: {e}"
242
+ r.grammar_verdict = "ERR"
243
 
244
  # ── Punctuation ──
245
  try:
246
  r.punctuation_raw, r.punctuation_ms = api.punctuation_raw(inp)
247
  r.punctuation_changed = (normalize(r.punctuation_raw) != normalize(inp))
248
+ r.punctuation_verdict = classify_raw(inp, r.punctuation_raw, expected, ds_name)
249
+ if r.punctuation_changed:
250
+ r.punctuation_edits = get_word_edits(inp, r.punctuation_raw)
251
  except Exception as e:
252
  r.punctuation_raw = f"ERROR: {e}"
253
+ r.punctuation_verdict = "ERR"
254
 
255
  total_ms = r.spelling_ms + r.grammar_ms + r.punctuation_ms
256
+ print(f"S={r.spelling_verdict} G={r.grammar_verdict} P={r.punctuation_verdict} ({total_ms}ms)")
 
 
 
 
 
257
 
258
  results.append(r)
259
 
 
261
 
262
 
263
  def analyze_results(results: List[RawModelResult]) -> dict:
 
264
  analysis = {
265
  "total": len(results),
266
+ "by_model": {},
 
 
 
 
267
  "by_dataset": {},
268
  }
269
 
270
+ for model in ("spelling", "grammar", "punctuation"):
271
+ verdicts = {"TP": 0, "TN": 0, "FP": 0, "FN": 0, "ERR": 0}
272
+ changed = 0
273
+ for r in results:
274
+ v = getattr(r, f"{model}_verdict", "")
275
+ if v in verdicts:
276
+ verdicts[v] += 1
277
+ if getattr(r, f"{model}_changed", False):
278
+ changed += 1
279
+ total = sum(v for k, v in verdicts.items() if k != "ERR")
280
+ pass_count = verdicts["TP"] + verdicts["TN"]
281
+ analysis["by_model"][model] = {
282
+ **verdicts,
283
+ "changed": changed,
284
+ "total": len(results),
285
+ "change_rate": round(changed / len(results), 4) if results else 0,
286
+ "pass_rate": round(pass_count / total, 4) if total else 0,
287
+ }
288
+
289
+ # Per-dataset breakdown
290
+ for ds in sorted(set(r.dataset for r in results)):
291
+ ds_results = [r for r in results if r.dataset == ds]
292
+ ds_analysis = {"total": len(ds_results)}
293
+ for model in ("spelling", "grammar", "punctuation"):
294
+ verdicts = {"TP": 0, "TN": 0, "FP": 0, "FN": 0}
295
+ changed = 0
296
+ for r in ds_results:
297
+ v = getattr(r, f"{model}_verdict", "")
298
+ if v in verdicts:
299
+ verdicts[v] += 1
300
+ if getattr(r, f"{model}_changed", False):
301
+ changed += 1
302
+ total = sum(verdicts.values())
303
+ pass_count = verdicts["TP"] + verdicts["TN"]
304
+ ds_analysis[model] = {
305
+ **verdicts,
306
+ "changed": changed,
307
+ "change_rate": round(changed / len(ds_results), 4) if ds_results else 0,
308
+ "pass_rate": round(pass_count / total, 4) if total else 0,
309
  }
310
+ analysis["by_dataset"][ds] = ds_analysis
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
  return analysis
313
 
314
 
315
+ def print_analysis(analysis: dict, results: List[RawModelResult]):
316
  print(f"\n{'='*60}")
317
  print("LEVEL 1: RAW MODEL ANALYSIS")
318
  print(f"{'='*60}")
319
 
320
+ # Per-model summary
321
  print(f"\n## Per-Model Summary ({analysis['total']} tests)")
322
+ print(f"| Model | Changed | TP | TN | FP | FN | ChgRate | Pass% |")
323
+ print(f"|-------------|---------|-----|-----|-----|-----|---------|--------|")
324
  for model, data in analysis["by_model"].items():
325
+ cr = data['change_rate'] * 100
326
+ pr = data['pass_rate'] * 100
327
+ print(f"| {model:<11} | {data['changed']:>7} | {data['TP']:>3} | {data['TN']:>3} "
328
+ f"| {data['FP']:>3} | {data['FN']:>3} | {cr:5.1f}% | {pr:5.1f}% |")
329
+
330
+ # Per-dataset breakdown
331
+ print(f"\n## Per-Dataset × Model Pass Rate")
332
+ print(f"| Dataset | Total | S-Pass% | G-Pass% | P-Pass% | S-Chg% | G-Chg% | P-Chg% |")
333
+ print(f"|--------------|-------|---------|---------|---------|--------|--------|--------|")
334
+ for ds in sorted(analysis["by_dataset"].keys()):
335
+ d = analysis["by_dataset"][ds]
336
+ sp = d["spelling"]["pass_rate"] * 100
337
+ gp = d["grammar"]["pass_rate"] * 100
338
+ pp = d["punctuation"]["pass_rate"] * 100
339
+ sc = d["spelling"]["change_rate"] * 100
340
+ gc = d["grammar"]["change_rate"] * 100
341
+ pc = d["punctuation"]["change_rate"] * 100
342
+ print(f"| {ds:<12} | {d['total']:>5} | {sp:5.1f}% | {gp:5.1f}% | {pp:5.1f}% "
343
+ f"| {sc:4.1f}% | {gc:4.1f}% | {pc:4.1f}% |")
344
+
345
+ # Word-level edit summary (top FP edits)
346
+ print(f"\n## Top FP Word Edits (model changed text incorrectly)")
347
+ for model in ("spelling", "grammar", "punctuation"):
348
+ fp_edits = []
349
+ for r in results:
350
+ if getattr(r, f"{model}_verdict") == "FP":
351
+ edits = getattr(r, f"{model}_edits", "")
352
+ if edits:
353
+ fp_edits.append(f" {r.id}: {edits}")
354
+ if fp_edits:
355
+ print(f"\n [{model.upper()}] {len(fp_edits)} FP cases:")
356
+ for e in fp_edits[:10]: # Show first 10
357
+ print(e)
358
 
359
 
360
  def main():
 
376
 
377
  results = run_level1(api, datasets)
378
  analysis = analyze_results(results)
379
+ print_analysis(analysis, results)
380
 
381
  # Save results
382
  REPORT_DIR.mkdir(parents=True, exist_ok=True)