nishu08 commited on
Commit
7aae828
·
verified ·
1 Parent(s): 8a3099e

Deploy CodeBERT inference Space

Browse files
README.md CHANGED
@@ -25,7 +25,7 @@ Model: [nishu08/sql-codebert-classifier](https://huggingface.co/nishu08/sql-code
25
  ## Labels
26
 
27
  `JOIN_ERROR`, `AGGREGATION_ERROR`, `FILTER_ERROR`, `WINDOW_FUNCTION_ERROR`,
28
- `SUBQUERY_ERROR`, `NULL_HANDLING_ERROR`, `PERFORMANCE_ERROR`, `LOGICAL_ERROR`, `SYNTAX_ERROR`
29
 
30
  ## Secrets (optional)
31
 
 
25
  ## Labels
26
 
27
  `JOIN_ERROR`, `AGGREGATION_ERROR`, `FILTER_ERROR`, `WINDOW_FUNCTION_ERROR`,
28
+ `SUBQUERY_ERROR`, `NULL_HANDLING_ERROR`, `PERFORMANCE_ERROR`, `LOGICAL_ERROR`, `SYNTAX_ERROR`, `NO_ERROR`
29
 
30
  ## Secrets (optional)
31
 
README_INFERENCE_SPACE.md CHANGED
@@ -25,7 +25,7 @@ Model: [nishu08/sql-codebert-classifier](https://huggingface.co/nishu08/sql-code
25
  ## Labels
26
 
27
  `JOIN_ERROR`, `AGGREGATION_ERROR`, `FILTER_ERROR`, `WINDOW_FUNCTION_ERROR`,
28
- `SUBQUERY_ERROR`, `NULL_HANDLING_ERROR`, `PERFORMANCE_ERROR`, `LOGICAL_ERROR`, `SYNTAX_ERROR`
29
 
30
  ## Secrets (optional)
31
 
 
25
  ## Labels
26
 
27
  `JOIN_ERROR`, `AGGREGATION_ERROR`, `FILTER_ERROR`, `WINDOW_FUNCTION_ERROR`,
28
+ `SUBQUERY_ERROR`, `NULL_HANDLING_ERROR`, `PERFORMANCE_ERROR`, `LOGICAL_ERROR`, `SYNTAX_ERROR`, `NO_ERROR`
29
 
30
  ## Secrets (optional)
31
 
app.py CHANGED
@@ -42,6 +42,13 @@ EXAMPLES = [
42
  "SELECT students.name, departments.name FROM students INNER JOIN departments ON students.department_id = departments.id",
43
  0.5,
44
  ],
 
 
 
 
 
 
 
45
  ]
46
 
47
 
@@ -55,14 +62,27 @@ def classify(question, schema, student_sql, correct_sql, threshold):
55
  correct_sql=correct_sql.strip(),
56
  threshold=threshold,
57
  )
58
- summary = (
59
- f"### {result['primary_label']}\n"
60
- f"Confidence: **{result['primary_confidence']:.1%}**\n\n"
61
- f"**Active labels:** {', '.join(result['error_labels']) or 'none'}"
62
- )
63
- probs = "\n".join(
64
- f"- **{k}**: {v:.1%}" for k, v in result["probabilities"].items()
65
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  return summary, probs
67
 
68
 
 
42
  "SELECT students.name, departments.name FROM students INNER JOIN departments ON students.department_id = departments.id",
43
  0.5,
44
  ],
45
+ [
46
+ "What is the average score of students in each department?",
47
+ "students(id, name, score, department_id) | departments(id, name)",
48
+ "SELECT department_id, AVG(score) FROM students GROUP BY department_id",
49
+ "SELECT department_id, AVG(score) FROM students GROUP BY department_id",
50
+ 0.5,
51
+ ],
52
  ]
53
 
54
 
 
62
  correct_sql=correct_sql.strip(),
63
  threshold=threshold,
64
  )
65
+ if result["primary_label"] == "NO_ERROR":
66
+ if result.get("match_detected"):
67
+ summary = (
68
+ "### No error\n"
69
+ "Student SQL matches the correct answer — no mistake to classify."
70
+ )
71
+ else:
72
+ summary = (
73
+ "### No error\n"
74
+ f"No label exceeded the {threshold:.0%} threshold."
75
+ )
76
+ probs = "_All probabilities below threshold._"
77
+ else:
78
+ summary = (
79
+ f"### {result['primary_label']}\n"
80
+ f"Confidence: **{result['primary_confidence']:.1%}**\n\n"
81
+ f"**Active labels:** {', '.join(result['error_labels']) or 'none'}"
82
+ )
83
+ probs = "\n".join(
84
+ f"- **{k}**: {v:.1%}" for k, v in result["probabilities"].items()
85
+ )
86
  return summary, probs
87
 
88
 
config/codebert_labels.yaml CHANGED
@@ -9,6 +9,7 @@ labels:
9
  - PERFORMANCE_ERROR
10
  - LOGICAL_ERROR
11
  - SYNTAX_ERROR
 
12
 
13
  # Map dataset label_name values → one or more CodeBERT labels (multi-label)
14
  alias_map:
@@ -27,3 +28,4 @@ alias_map:
27
  TABLE_REFERENCE_ERROR: [SYNTAX_ERROR]
28
  DATA_TYPE_ERROR: [SYNTAX_ERROR]
29
  DUPLICATE_RECORD_ERROR: [FILTER_ERROR]
 
 
9
  - PERFORMANCE_ERROR
10
  - LOGICAL_ERROR
11
  - SYNTAX_ERROR
12
+ - NO_ERROR
13
 
14
  # Map dataset label_name values → one or more CodeBERT labels (multi-label)
15
  alias_map:
 
28
  TABLE_REFERENCE_ERROR: [SYNTAX_ERROR]
29
  DATA_TYPE_ERROR: [SYNTAX_ERROR]
30
  DUPLICATE_RECORD_ERROR: [FILTER_ERROR]
31
+ NO_ERROR: [NO_ERROR]
config/error_categories.yaml CHANGED
@@ -44,3 +44,6 @@ categories:
44
  - id: 14
45
  name: FILTERING_ERROR
46
  description: Incorrect WHERE clause
 
 
 
 
44
  - id: 14
45
  name: FILTERING_ERROR
46
  description: Incorrect WHERE clause
47
+ - id: 15
48
+ name: NO_ERROR
49
+ description: Student SQL matches the correct answer
src/codebert_formatting.py CHANGED
@@ -2,12 +2,24 @@
2
 
3
  from __future__ import annotations
4
 
 
 
5
  QUESTION_TAG = "QUESTION:"
6
  SCHEMA_TAG = "SCHEMA:"
7
  STUDENT_TAG = "STUDENT_SQL:"
8
  CORRECT_TAG = "CORRECT_SQL:"
9
 
10
 
 
 
 
 
 
 
 
 
 
 
11
  def format_cross_encoder_input(
12
  question: str,
13
  schema: str,
 
2
 
3
  from __future__ import annotations
4
 
5
+ import re
6
+
7
  QUESTION_TAG = "QUESTION:"
8
  SCHEMA_TAG = "SCHEMA:"
9
  STUDENT_TAG = "STUDENT_SQL:"
10
  CORRECT_TAG = "CORRECT_SQL:"
11
 
12
 
13
+ def normalize_sql(sql: str) -> str:
14
+ """Normalize SQL for equality checks (whitespace, case, trailing semicolon)."""
15
+ text = sql.strip().rstrip(";")
16
+ return re.sub(r"\s+", " ", text).lower()
17
+
18
+
19
+ def sql_queries_equivalent(student_sql: str, correct_sql: str) -> bool:
20
+ return normalize_sql(student_sql) == normalize_sql(correct_sql)
21
+
22
+
23
  def format_cross_encoder_input(
24
  question: str,
25
  schema: str,
src/hf_metrics.py CHANGED
@@ -12,7 +12,7 @@ from sklearn.metrics import (
12
  precision_score,
13
  recall_score,
14
  )
15
-
16
 
17
  def sigmoid(x: np.ndarray) -> np.ndarray:
18
  return 1.0 / (1.0 + np.exp(-x))
 
12
  precision_score,
13
  recall_score,
14
  )
15
+
16
 
17
  def sigmoid(x: np.ndarray) -> np.ndarray:
18
  return 1.0 / (1.0 + np.exp(-x))
src/hf_predict_codebert.py CHANGED
@@ -11,7 +11,7 @@ import numpy as np
11
  import torch
12
  from transformers import AutoModelForSequenceClassification, AutoTokenizer
13
 
14
- from src.codebert_formatting import format_cross_encoder_input
15
  from src.device_utils import get_device
16
  from src.codebert_labels import load_codebert_labels, multihot_to_label_names
17
  from src.hf_metrics import sigmoid
@@ -74,6 +74,21 @@ class CodeBERTSQLErrorClassifier:
74
  threshold: Optional[float] = None,
75
  top_k: int = 5,
76
  ) -> dict:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  text = format_cross_encoder_input(
78
  question=question,
79
  schema=schema,
@@ -92,7 +107,6 @@ class CodeBERTSQLErrorClassifier:
92
  logits = self.model(**encoded).logits.cpu().numpy()[0]
93
 
94
  probs = sigmoid(logits)
95
- thr = threshold if threshold is not None else self.threshold
96
  predicted = multihot_to_label_names(probs, self.label_list, threshold=thr)
97
 
98
  ranked = sorted(
@@ -101,14 +115,23 @@ class CodeBERTSQLErrorClassifier:
101
  reverse=True,
102
  )[:top_k]
103
 
 
 
 
 
 
 
 
 
104
  return {
105
  "error_labels": predicted,
106
  "probabilities": {name: float(p) for name, p in ranked},
107
  "top_k": [
108
  {"label": name, "probability": float(p)} for name, p in ranked
109
  ],
110
- "primary_label": ranked[0][0],
111
- "primary_confidence": float(ranked[0][1]),
 
112
  }
113
 
114
  def predict_batch(
 
11
  import torch
12
  from transformers import AutoModelForSequenceClassification, AutoTokenizer
13
 
14
+ from src.codebert_formatting import format_cross_encoder_input, sql_queries_equivalent
15
  from src.device_utils import get_device
16
  from src.codebert_labels import load_codebert_labels, multihot_to_label_names
17
  from src.hf_metrics import sigmoid
 
74
  threshold: Optional[float] = None,
75
  top_k: int = 5,
76
  ) -> dict:
77
+ thr = threshold if threshold is not None else self.threshold
78
+
79
+ if sql_queries_equivalent(student_sql, correct_sql):
80
+ return {
81
+ "error_labels": [],
82
+ "probabilities": {name: 0.0 for name in self.label_list},
83
+ "top_k": [
84
+ {"label": name, "probability": 0.0}
85
+ for name in self.label_list[:5]
86
+ ],
87
+ "primary_label": "NO_ERROR",
88
+ "primary_confidence": 1.0,
89
+ "match_detected": True,
90
+ }
91
+
92
  text = format_cross_encoder_input(
93
  question=question,
94
  schema=schema,
 
107
  logits = self.model(**encoded).logits.cpu().numpy()[0]
108
 
109
  probs = sigmoid(logits)
 
110
  predicted = multihot_to_label_names(probs, self.label_list, threshold=thr)
111
 
112
  ranked = sorted(
 
115
  reverse=True,
116
  )[:top_k]
117
 
118
+ top_label, top_prob = ranked[0]
119
+ if top_prob >= thr:
120
+ primary_label = top_label
121
+ primary_confidence = float(top_prob)
122
+ else:
123
+ primary_label = "NO_ERROR"
124
+ primary_confidence = 1.0 - float(top_prob)
125
+
126
  return {
127
  "error_labels": predicted,
128
  "probabilities": {name: float(p) for name, p in ranked},
129
  "top_k": [
130
  {"label": name, "probability": float(p)} for name, p in ranked
131
  ],
132
+ "primary_label": primary_label,
133
+ "primary_confidence": primary_confidence,
134
+ "match_detected": False,
135
  }
136
 
137
  def predict_batch(
src/sql_templates.py CHANGED
@@ -223,6 +223,12 @@ def inject_performance_error(rng: random.Random, exercise: Exercise) -> Tuple[st
223
  return rng.choice(variants), "inefficient query: SELECT * or cartesian join detected"
224
 
225
 
 
 
 
 
 
 
226
  def inject_filtering_error(rng: random.Random, exercise: Exercise) -> Tuple[str, str]:
227
  sql = exercise.correct_query
228
  col = _pick(rng, list(exercise.columns))
@@ -255,4 +261,5 @@ ERROR_INJECTORS: Dict[int, Callable[[random.Random, Exercise], Tuple[str, str]]]
255
  12: inject_logical_error,
256
  13: inject_performance_error,
257
  14: inject_filtering_error,
 
258
  }
 
223
  return rng.choice(variants), "inefficient query: SELECT * or cartesian join detected"
224
 
225
 
226
+ def inject_no_error(rng: random.Random, exercise: Exercise) -> Tuple[str, str]:
227
+ """Correct submission: student SQL equals the reference answer."""
228
+ sql = exercise.correct_query.strip()
229
+ return sql, ""
230
+
231
+
232
  def inject_filtering_error(rng: random.Random, exercise: Exercise) -> Tuple[str, str]:
233
  sql = exercise.correct_query
234
  col = _pick(rng, list(exercise.columns))
 
261
  12: inject_logical_error,
262
  13: inject_performance_error,
263
  14: inject_filtering_error,
264
+ 15: inject_no_error,
265
  }