sravaniamere commited on
Commit
eecac6e
Β·
1 Parent(s): 2194233

Fix grader: clamp rewards to strictly (0.01, 0.99)

Browse files
Files changed (2) hide show
  1. sql_env/grader.py +29 -20
  2. sql_env/models.py +1 -1
sql_env/grader.py CHANGED
@@ -32,30 +32,39 @@ def _sql_keywords_present(query: str) -> set:
32
  return found
33
 
34
 
 
 
 
 
 
35
  def grade(action: SQLAction, task: SQLTask) -> SQLReward:
36
  """
37
  5-level grader with partial progress signals.
38
-
39
- 1.0 β€” exact normalized match (perfect fix)
40
- 0.7 β€” same token set, minor structural/whitespace differences
41
- 0.4 β€” most SQL keywords correct AND high token overlap
42
- 0.3 β€” partial keyword and structure match
43
- 0.2 β€” basic SELECT/FROM structure present
44
- 0.0 β€” not recognizable SQL
 
45
  """
46
  agent = _normalize(action.corrected_query)
47
  correct = _normalize(task.canonical_answer)
48
 
49
  # ── Level 1: Exact match ─────────────────────────────────────────────────
50
  if agent == correct:
51
- return SQLReward(value=1.0, reason="Exact match β€” perfect correction.")
 
 
 
52
 
53
  # ── Level 2: Same token set (right words, minor ordering/alias diff) ─────
54
  agent_tokens = _tokenize(action.corrected_query)
55
  correct_tokens = _tokenize(task.canonical_answer)
56
  if agent_tokens == correct_tokens:
57
  return SQLReward(
58
- value=0.7,
59
  reason="All correct tokens present but structure differs slightly.",
60
  )
61
 
@@ -67,17 +76,17 @@ def grade(action: SQLAction, task: SQLTask) -> SQLReward:
67
 
68
  if kw_overlap >= 0.85 and token_overlap >= 0.75:
69
  return SQLReward(
70
- value=0.4,
71
  reason=(
72
  f"Most keywords correct "
73
  f"({kw_overlap:.0%} keyword match, {token_overlap:.0%} token match)."
74
  ),
75
  )
76
 
77
- # ── Level 3.5: Partial keyword and structure match ────────────────────────
78
  if kw_overlap >= 0.65 and token_overlap >= 0.50:
79
  return SQLReward(
80
- value=0.3,
81
  reason=(
82
  f"Partial keyword and structure match "
83
  f"({kw_overlap:.0%} keyword match, {token_overlap:.0%} token match)."
@@ -87,29 +96,29 @@ def grade(action: SQLAction, task: SQLTask) -> SQLReward:
87
  # ── Level 4: Basic structure present ─────────────────────────────────────
88
  if 'SELECT' in agent and 'FROM' in agent:
89
  return SQLReward(
90
- value=0.2,
91
  reason="Basic SELECT/FROM structure present but significant errors remain.",
92
  )
93
 
94
  # ── Level 0: No recognizable SQL ─────────────────────────────────────────
95
- return SQLReward(value=0.0, reason="Response is not valid SQL.")
96
 
97
 
98
  def generate_feedback(action: SQLAction, task: SQLTask, reward: SQLReward) -> str:
99
  """Human-readable feedback shown in the next observation."""
100
- if reward.value >= 1.0:
101
  return "Correct! Query matches perfectly."
102
- if reward.value >= 0.7:
103
  return "Very close β€” check spacing or minor clause differences."
104
- if reward.value >= 0.4:
105
  return (
106
  "Good progress β€” most keywords are right, "
107
  "but check for typos in keywords or column names."
108
  )
109
- if reward.value >= 0.3:
110
  return "Partial match β€” right direction but several keywords or columns are off."
111
- if reward.value >= 0.2:
112
  return (
113
  "Basic structure is there β€” look carefully at every SQL keyword for typos."
114
  )
115
- return "The response doesn't look like valid SQL. Start with SELECT ... FROM ..."
 
32
  return found
33
 
34
 
35
+ def _clamp(value: float) -> float:
36
+ """Ensure reward is strictly within (0, 1) as required by the OpenEnv spec."""
37
+ return max(0.01, min(0.99, value))
38
+
39
+
40
  def grade(action: SQLAction, task: SQLTask) -> SQLReward:
41
  """
42
  5-level grader with partial progress signals.
43
+ All scores are clamped to [0.01, 0.99] β€” strictly between 0 and 1.
44
+
45
+ 0.99 β€” exact normalized match (perfect fix)
46
+ 0.70 β€” same token set, minor structural/whitespace differences
47
+ 0.40 β€” most SQL keywords correct AND high token overlap
48
+ 0.30 β€” partial keyword and structure match
49
+ 0.20 β€” basic SELECT/FROM structure present
50
+ 0.01 β€” not recognizable SQL
51
  """
52
  agent = _normalize(action.corrected_query)
53
  correct = _normalize(task.canonical_answer)
54
 
55
  # ── Level 1: Exact match ─────────────────────────────────────────────────
56
  if agent == correct:
57
+ return SQLReward(
58
+ value=_clamp(0.99),
59
+ reason="Exact match β€” perfect correction.",
60
+ )
61
 
62
  # ── Level 2: Same token set (right words, minor ordering/alias diff) ─────
63
  agent_tokens = _tokenize(action.corrected_query)
64
  correct_tokens = _tokenize(task.canonical_answer)
65
  if agent_tokens == correct_tokens:
66
  return SQLReward(
67
+ value=_clamp(0.70),
68
  reason="All correct tokens present but structure differs slightly.",
69
  )
70
 
 
76
 
77
  if kw_overlap >= 0.85 and token_overlap >= 0.75:
78
  return SQLReward(
79
+ value=_clamp(0.40),
80
  reason=(
81
  f"Most keywords correct "
82
  f"({kw_overlap:.0%} keyword match, {token_overlap:.0%} token match)."
83
  ),
84
  )
85
 
86
+ # ── Level 3.5: Partial keyword and structure match ───────────────────────
87
  if kw_overlap >= 0.65 and token_overlap >= 0.50:
88
  return SQLReward(
89
+ value=_clamp(0.30),
90
  reason=(
91
  f"Partial keyword and structure match "
92
  f"({kw_overlap:.0%} keyword match, {token_overlap:.0%} token match)."
 
96
  # ── Level 4: Basic structure present ─────────────────────────────────────
97
  if 'SELECT' in agent and 'FROM' in agent:
98
  return SQLReward(
99
+ value=_clamp(0.20),
100
  reason="Basic SELECT/FROM structure present but significant errors remain.",
101
  )
102
 
103
  # ── Level 0: No recognizable SQL ─────────────────────────────────────────
104
+ return SQLReward(value=_clamp(0.01), reason="Response is not valid SQL.")
105
 
106
 
107
  def generate_feedback(action: SQLAction, task: SQLTask, reward: SQLReward) -> str:
108
  """Human-readable feedback shown in the next observation."""
109
+ if reward.value >= 0.99:
110
  return "Correct! Query matches perfectly."
111
+ if reward.value >= 0.70:
112
  return "Very close β€” check spacing or minor clause differences."
113
+ if reward.value >= 0.40:
114
  return (
115
  "Good progress β€” most keywords are right, "
116
  "but check for typos in keywords or column names."
117
  )
118
+ if reward.value >= 0.30:
119
  return "Partial match β€” right direction but several keywords or columns are off."
120
+ if reward.value >= 0.20:
121
  return (
122
  "Basic structure is there β€” look carefully at every SQL keyword for typos."
123
  )
124
+ return "The response doesn't look like valid SQL. Start with SELECT ... FROM ..."
sql_env/models.py CHANGED
@@ -43,7 +43,7 @@ class SQLState(State):
43
 
44
  class SQLReward(BaseModel):
45
  # Allow full [0.0, 1.0] range so perfect matches can return exactly 1.0
46
- value: float = Field(ge=0.0, le=1.0)
47
  reason: str
48
 
49
 
 
43
 
44
  class SQLReward(BaseModel):
45
  # Allow full [0.0, 1.0] range so perfect matches can return exactly 1.0
46
+ value: float = Field(gt=0.0, lt=1.0)
47
  reason: str
48
 
49