yashshinde0080 commited on
Commit
30e1fea
·
1 Parent(s): 1cdf020

10/4/2026

Browse files
app.py CHANGED
@@ -47,11 +47,11 @@ MAX_SESSIONS = 100
47
  # Global metrics tracking (shared)
48
  GLOBAL_METRICS = {
49
  "total_episodes": 0,
50
- "success_rate": 0.0,
51
  "total_successful": 0,
52
- "avg_easy_score": 0.0,
53
- "avg_medium_score": 0.0,
54
- "avg_hard_score": 0.0,
55
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
56
  }
57
 
 
47
  # Global metrics tracking (shared)
48
  GLOBAL_METRICS = {
49
  "total_episodes": 0,
50
+ "success_rate": 0.01,
51
  "total_successful": 0,
52
+ "avg_easy_score": 0.01,
53
+ "avg_medium_score": 0.01,
54
+ "avg_hard_score": 0.01,
55
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
56
  }
57
 
app_SuEnv.py CHANGED
@@ -36,11 +36,11 @@ active_sessions: Dict[str, SupportEnvironment] = {}
36
  episode_history: List[Dict[str, Any]] = []
37
  GLOBAL_METRICS = {
38
  "total_episodes": 0,
39
- "success_rate": 0.0,
40
  "total_successful": 0,
41
- "avg_easy_score": 0.0,
42
- "avg_medium_score": 0.0,
43
- "avg_hard_score": 0.0,
44
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
45
  }
46
 
 
36
  episode_history: List[Dict[str, Any]] = []
37
  GLOBAL_METRICS = {
38
  "total_episodes": 0,
39
+ "success_rate": 0.01,
40
  "total_successful": 0,
41
+ "avg_easy_score": 0.01,
42
+ "avg_medium_score": 0.01,
43
+ "avg_hard_score": 0.01,
44
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
45
  }
46
 
inference.py CHANGED
@@ -156,7 +156,7 @@ def main() -> None:
156
  rewards: List[float] = []
157
  steps_taken = 0
158
  success = False
159
- final_score = 0.0
160
 
161
  try:
162
  with env.sync() as conn:
@@ -212,7 +212,7 @@ def main() -> None:
212
  grade_resp = requests.post(grader_url, json={"session_id": session_id}, timeout=30)
213
  if grade_resp.status_code == 200:
214
  grade_data = grade_resp.json()
215
- final_score = float(grade_data.get('score', 0.0))
216
  success = bool(grade_data.get('passed', False))
217
  except Exception:
218
  pass
@@ -220,7 +220,7 @@ def main() -> None:
220
  log_end(success=success, steps=steps_taken, score=final_score, rewards=rewards)
221
 
222
  except Exception as e:
223
- log_end(success=False, steps=steps_taken, score=0.0, rewards=rewards)
224
 
225
  if __name__ == "__main__":
226
  main()
 
156
  rewards: List[float] = []
157
  steps_taken = 0
158
  success = False
159
+ final_score = 0.01
160
 
161
  try:
162
  with env.sync() as conn:
 
212
  grade_resp = requests.post(grader_url, json={"session_id": session_id}, timeout=30)
213
  if grade_resp.status_code == 200:
214
  grade_data = grade_resp.json()
215
+ final_score = float(grade_data.get('score', 0.01))
216
  success = bool(grade_data.get('passed', False))
217
  except Exception:
218
  pass
 
220
  log_end(success=success, steps=steps_taken, score=final_score, rewards=rewards)
221
 
222
  except Exception as e:
223
+ log_end(success=False, steps=steps_taken, score=0.01, rewards=rewards)
224
 
225
  if __name__ == "__main__":
226
  main()
models.py CHANGED
@@ -90,13 +90,13 @@ class SupportState(State):
90
 
91
  # Performance tracking
92
  classification_correct: bool = False
93
- response_quality_score: float = 0.0
94
  escalation_correct: bool = False
95
  resolved: bool = False
96
 
97
  # Cumulative metrics
98
- total_reward: float = 0.0
99
- customer_sentiment: float = 0.0
100
 
101
 
102
  class PublicSupportState(State):
@@ -111,10 +111,10 @@ class PublicSupportState(State):
111
 
112
  # Performance tracking
113
  classification_correct: bool = False
114
- response_quality_score: float = 0.0
115
  escalation_correct: bool = False
116
  resolved: bool = False
117
 
118
  # Cumulative metrics
119
- total_reward: float = 0.0
120
- customer_sentiment: float = 0.0
 
90
 
91
  # Performance tracking
92
  classification_correct: bool = False
93
+ response_quality_score: float = 0.01
94
  escalation_correct: bool = False
95
  resolved: bool = False
96
 
97
  # Cumulative metrics
98
+ total_reward: float = 0.01
99
+ customer_sentiment: float = 0.01
100
 
101
 
102
  class PublicSupportState(State):
 
111
 
112
  # Performance tracking
113
  classification_correct: bool = False
114
+ response_quality_score: float = 0.01
115
  escalation_correct: bool = False
116
  resolved: bool = False
117
 
118
  # Cumulative metrics
119
+ total_reward: float = 0.01
120
+ customer_sentiment: float = 0.01
server/app.py CHANGED
@@ -56,11 +56,11 @@ SESSION_TTL_SECONDS = 3600
56
  # Global metrics tracking
57
  METRICS = {
58
  "total_episodes": 0,
59
- "success_rate": 0.0,
60
  "total_successful": 0,
61
- "avg_easy_score": 0.0,
62
- "avg_medium_score": 0.0,
63
- "avg_hard_score": 0.0,
64
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
65
  }
66
 
 
56
  # Global metrics tracking
57
  METRICS = {
58
  "total_episodes": 0,
59
+ "success_rate": 0.01,
60
  "total_successful": 0,
61
+ "avg_easy_score": 0.01,
62
+ "avg_medium_score": 0.01,
63
+ "avg_hard_score": 0.01,
64
  "scores_by_difficulty": {"easy": [], "medium": [], "hard": []}
65
  }
66
 
server/environment.py CHANGED
@@ -107,10 +107,10 @@ class SupportEnvironment(Environment):
107
  task_difficulty=difficulty,
108
  max_steps=max_steps,
109
  classification_correct=False,
110
- response_quality_score=0.0,
111
  escalation_correct=False,
112
  resolved=False,
113
- total_reward=0.0
114
  )
115
 
116
  # Reset episode flags
 
107
  task_difficulty=difficulty,
108
  max_steps=max_steps,
109
  classification_correct=False,
110
+ response_quality_score=0.01,
111
  escalation_correct=False,
112
  resolved=False,
113
+ total_reward=0.01
114
  )
115
 
116
  # Reset episode flags
server/graders.py CHANGED
@@ -116,8 +116,9 @@ class SupportGrader:
116
  total_score += ordering_penalty
117
 
118
  # Ensure score is strictly within (0, 1) range per validation rules
119
- # Clipping to [0.0001, 0.9999] ensures we never return exactly 0.0 or 1.0
120
- total_score = max(0.0001, min(0.9999, total_score))
 
121
 
122
  # Generate feedback
123
  feedback = self._generate_feedback(breakdown, task_difficulty)
@@ -126,8 +127,8 @@ class SupportGrader:
126
  passed = total_score >= 0.6
127
 
128
  return GradeResult(
129
- score=round(total_score, 4),
130
- breakdown={k: round(max(0.0001, min(0.9999, v)), 4) for k, v in breakdown.items()},
131
  feedback=feedback,
132
  passed=passed
133
  )
@@ -146,7 +147,7 @@ class SupportGrader:
146
  if idx_resolve_or_escalate != -1:
147
  if idx_classify == -1 or idx_resolve_or_escalate < idx_classify:
148
  return -0.25 # Penalty
149
- return 0.0
150
 
151
  def _grade_classification(
152
  self,
@@ -159,14 +160,14 @@ class SupportGrader:
159
  ]
160
 
161
  if not classifications:
162
- return 0.0 # No classification attempted
163
 
164
  # Check first classification (most important)
165
  first_class = classifications[0].get("content", "").lower().strip()
166
  target_clean = target.lower().strip()
167
 
168
  if first_class == target_clean:
169
- return 1.0
170
 
171
  # Partial credit for related categories
172
  category_relations = {
@@ -186,7 +187,7 @@ class SupportGrader:
186
  if cls.get("content", "").lower().strip() == target_clean:
187
  return 0.5 # Corrected but not first try
188
 
189
- return 0.2 # Wrong classification
190
 
191
  def _grade_responses(
192
  self,
@@ -200,7 +201,7 @@ class SupportGrader:
200
  ]
201
 
202
  if not responses:
203
- return 0.0
204
 
205
  total_score = 0.0
206
 
@@ -227,7 +228,7 @@ class SupportGrader:
227
 
228
  # Response length check
229
  word_count = len(content.split())
230
- length_score = min(1.0, word_count / 20) # At least 20 words ideal
231
 
232
  # ANTI-GAMING: Detect keyword stuffing
233
  # Stuffing = many keywords in short text (high density), not just many keywords
@@ -264,7 +265,7 @@ class SupportGrader:
264
  # If confidence is < 0.5, it's a weak response
265
  resp_score *= (0.5 + 0.5 * confidence)
266
 
267
- total_score += max(0.0, min(1.0, resp_score))
268
 
269
  # Average across responses
270
  avg_score = total_score / len(responses)
@@ -283,7 +284,7 @@ class SupportGrader:
283
  # easy → no adjustment
284
  # medium → scores below 0.6 are compressed (×0.85 multiplier)
285
  # hard → scores below 0.7 are compressed (×0.75 multiplier)
286
- # A perfect raw score still maps to 1.0 at every difficulty.
287
  if difficulty == "hard":
288
  if avg_score < 0.7:
289
  avg_score *= 0.75 # weak response on a hard task
@@ -292,7 +293,7 @@ class SupportGrader:
292
  if avg_score < 0.6:
293
  avg_score *= 0.85 # weak response on a medium task
294
 
295
- return max(0.0, min(1.0, avg_score))
296
 
297
  def _grade_escalation(
298
  self,
@@ -333,18 +334,18 @@ class SupportGrader:
333
  # Check reason quality
334
  reason = escalations[0].get("content", "")
335
  if len(reason.split()) >= 10 and any(kw in reason.lower() for kw in ["immediate", "severity", "sensitivity", "human"]):
336
- return 1.0
337
  elif len(reason.split()) >= 5:
338
  return 0.9
339
  return 0.7 # Right decision but poor explanation
340
 
341
  elif not should_escalate and not escalated:
342
  # Correct: did not escalate when not needed
343
- return 1.0
344
 
345
  elif should_escalate and not escalated:
346
  # Wrong: should have escalated but didn't
347
- return 0.0 # Harsher penalty for missing required escalation
348
 
349
  else:
350
  # Wrong: escalated when not needed (unnecessary escalation)
@@ -408,12 +409,12 @@ class SupportGrader:
408
  base_score = overlap_ratio * 0.7
409
  action_bonus = 0.3 if has_action_word else 0.0
410
 
411
- return min(1.0, base_score + action_bonus)
412
 
413
  def _grade_efficiency(self, steps: int, max_steps: int, difficulty: str = "easy") -> float:
414
  """Grade step efficiency. Harder tasks require more deliberation."""
415
  if steps <= 1:
416
- return 1.0 if difficulty == "easy" else 0.5 # Discourage one-step solutions for complex tasks
417
 
418
  # Stricter for hard: optimal path is usually 4-6 steps
419
  if difficulty == "hard":
@@ -421,12 +422,12 @@ class SupportGrader:
421
  if steps < 5:
422
  return 0.4 # Superficial handling
423
  if steps <= 9:
424
- return 1.0 # High quality deliberation
425
  # Penalize the tail end for inefficiency
426
  return round(max(0.3, 1.0 - 0.8 * ((steps - 9) / (max_steps - 9))), 2)
427
 
428
  # Standard linear for easy/medium
429
- return round(1.0 - 0.8 * ((steps - 1) / (max_steps - 1)), 2)
430
 
431
  def _get_weights(self, difficulty: str) -> Dict[str, float]:
432
  """Get grading weights based on difficulty."""
 
116
  total_score += ordering_penalty
117
 
118
  # Ensure score is strictly within (0, 1) range per validation rules
119
+ # We round and then clamp to [0.01, 0.99] to guarantee it never hits 0.0 or 1.0
120
+ total_score = round(total_score, 4)
121
+ total_score = max(0.01, min(0.99, total_score))
122
 
123
  # Generate feedback
124
  feedback = self._generate_feedback(breakdown, task_difficulty)
 
127
  passed = total_score >= 0.6
128
 
129
  return GradeResult(
130
+ score=total_score,
131
+ breakdown={k: max(0.01, min(0.99, round(v, 4))) for k, v in breakdown.items()},
132
  feedback=feedback,
133
  passed=passed
134
  )
 
147
  if idx_resolve_or_escalate != -1:
148
  if idx_classify == -1 or idx_resolve_or_escalate < idx_classify:
149
  return -0.25 # Penalty
150
+ return 0.01
151
 
152
  def _grade_classification(
153
  self,
 
160
  ]
161
 
162
  if not classifications:
163
+ return 0.01 # No classification attempted
164
 
165
  # Check first classification (most important)
166
  first_class = classifications[0].get("content", "").lower().strip()
167
  target_clean = target.lower().strip()
168
 
169
  if first_class == target_clean:
170
+ return 0.99
171
 
172
  # Partial credit for related categories
173
  category_relations = {
 
187
  if cls.get("content", "").lower().strip() == target_clean:
188
  return 0.5 # Corrected but not first try
189
 
190
+ return 0.21 # Wrong classification
191
 
192
  def _grade_responses(
193
  self,
 
201
  ]
202
 
203
  if not responses:
204
+ return 0.01
205
 
206
  total_score = 0.0
207
 
 
228
 
229
  # Response length check
230
  word_count = len(content.split())
231
+ length_score = min(0.99, word_count / 20) # At least 20 words ideal
232
 
233
  # ANTI-GAMING: Detect keyword stuffing
234
  # Stuffing = many keywords in short text (high density), not just many keywords
 
265
  # If confidence is < 0.5, it's a weak response
266
  resp_score *= (0.5 + 0.5 * confidence)
267
 
268
+ total_score += max(0.01, min(0.99, resp_score))
269
 
270
  # Average across responses
271
  avg_score = total_score / len(responses)
 
284
  # easy → no adjustment
285
  # medium → scores below 0.6 are compressed (×0.85 multiplier)
286
  # hard → scores below 0.7 are compressed (×0.75 multiplier)
287
+ # A perfect raw score still maps to 0.99 at every difficulty.
288
  if difficulty == "hard":
289
  if avg_score < 0.7:
290
  avg_score *= 0.75 # weak response on a hard task
 
293
  if avg_score < 0.6:
294
  avg_score *= 0.85 # weak response on a medium task
295
 
296
+ return max(0.01, min(0.99, avg_score))
297
 
298
  def _grade_escalation(
299
  self,
 
334
  # Check reason quality
335
  reason = escalations[0].get("content", "")
336
  if len(reason.split()) >= 10 and any(kw in reason.lower() for kw in ["immediate", "severity", "sensitivity", "human"]):
337
+ return 0.99
338
  elif len(reason.split()) >= 5:
339
  return 0.9
340
  return 0.7 # Right decision but poor explanation
341
 
342
  elif not should_escalate and not escalated:
343
  # Correct: did not escalate when not needed
344
+ return 0.99
345
 
346
  elif should_escalate and not escalated:
347
  # Wrong: should have escalated but didn't
348
+ return 0.01 # Harsher penalty for missing required escalation
349
 
350
  else:
351
  # Wrong: escalated when not needed (unnecessary escalation)
 
409
  base_score = overlap_ratio * 0.7
410
  action_bonus = 0.3 if has_action_word else 0.0
411
 
412
+ return min(0.99, base_score + action_bonus)
413
 
414
  def _grade_efficiency(self, steps: int, max_steps: int, difficulty: str = "easy") -> float:
415
  """Grade step efficiency. Harder tasks require more deliberation."""
416
  if steps <= 1:
417
+ return 0.99 if difficulty == "easy" else 0.5 # Discourage one-step solutions for complex tasks
418
 
419
  # Stricter for hard: optimal path is usually 4-6 steps
420
  if difficulty == "hard":
 
422
  if steps < 5:
423
  return 0.4 # Superficial handling
424
  if steps <= 9:
425
+ return 0.99 # High quality deliberation
426
  # Penalize the tail end for inefficiency
427
  return round(max(0.3, 1.0 - 0.8 * ((steps - 9) / (max_steps - 9))), 2)
428
 
429
  # Standard linear for easy/medium
430
+ return round(0.99 - 0.8 * ((steps - 1) / (max_steps - 1)), 2)
431
 
432
  def _get_weights(self, difficulty: str) -> Dict[str, float]:
433
  """Get grading weights based on difficulty."""
server/semantic_scorer.py CHANGED
@@ -45,14 +45,14 @@ class SemanticScorer:
45
  Returns a dict with empathy, solution, and resolution alignment scores.
46
  """
47
  if not responses or not expected_resolution:
48
- return {"empathy": 0.0, "solution": 0.0, "resolution": 0.0, "overall": 0.0}
49
 
50
  if self.model is None:
51
  return None
52
 
53
  combined_response = " ".join([r for r in responses if len(r) > 10])
54
  if not combined_response:
55
- return {"empathy": 0.0, "solution": 0.0, "resolution": 0.0, "overall": 0.0}
56
 
57
  try:
58
  from sklearn.metrics.pairwise import cosine_similarity
@@ -73,11 +73,11 @@ class SemanticScorer:
73
 
74
  # Non-linear scaling: 0.2 similarity is baseline, 0.9 similarity is perfect.
75
  def scale(sim):
76
- return min(0.9999, max(0.0001, (sim - 0.1) / 0.7))
77
 
78
  empathy_score = scale(sim_empathy)
79
  solution_score = scale(sim_solution)
80
- resolution_score = max(0.0001, min(0.9999, (sim_resolution - 0.2) / 0.7))
81
 
82
  overall = (empathy_score * 0.2) + (solution_score * 0.2) + (resolution_score * 0.6)
83
 
 
45
  Returns a dict with empathy, solution, and resolution alignment scores.
46
  """
47
  if not responses or not expected_resolution:
48
+ return {"empathy": 0.01, "solution": 0.01, "resolution": 0.01, "overall": 0.01}
49
 
50
  if self.model is None:
51
  return None
52
 
53
  combined_response = " ".join([r for r in responses if len(r) > 10])
54
  if not combined_response:
55
+ return {"empathy": 0.01, "solution": 0.01, "resolution": 0.01, "overall": 0.01}
56
 
57
  try:
58
  from sklearn.metrics.pairwise import cosine_similarity
 
73
 
74
  # Non-linear scaling: 0.2 similarity is baseline, 0.9 similarity is perfect.
75
  def scale(sim):
76
+ return min(0.99, max(0.01, (sim - 0.1) / 0.7))
77
 
78
  empathy_score = scale(sim_empathy)
79
  solution_score = scale(sim_solution)
80
+ resolution_score = max(0.01, min(0.99, (sim_resolution - 0.2) / 0.7))
81
 
82
  overall = (empathy_score * 0.2) + (solution_score * 0.2) + (resolution_score * 0.6)
83