j-js commited on
Commit
abb868e
·
verified ·
1 Parent(s): adf41c9

Update conversation_logic.py

Browse files
Files changed (1) hide show
  1. conversation_logic.py +146 -18
conversation_logic.py CHANGED
@@ -69,7 +69,6 @@ MISMATCH_TERMS = {
69
  "number_theory": ["circle", "triangle", "median salary"],
70
  }
71
 
72
-
73
  def _normalize_classified_topic(topic: Optional[str], category: Optional[str], question_text: str) -> str:
74
  t = (topic or "").strip().lower()
75
  q = (question_text or "").lower()
@@ -80,7 +79,7 @@ def _normalize_classified_topic(topic: Optional[str], category: Optional[str], q
80
 
81
  if "%" in q or "percent" in q:
82
  return "percent"
83
- if "ratio" in q or ":" in q:
84
  return "ratio"
85
  if "probability" in q or "chosen at random" in q:
86
  return "probability"
@@ -115,10 +114,6 @@ def _teaching_lines(chunks: List[RetrievedChunk]) -> List[str]:
115
 
116
 
117
  def _safe_steps(steps: List[str]) -> List[str]:
118
- """
119
- Defensive cleanup in case any solver step accidentally contains
120
- explicit final-answer phrasing.
121
- """
122
  cleaned: List[str] = []
123
  banned_patterns = [
124
  r"\bthe answer is\b",
@@ -174,12 +169,12 @@ def _compose_reply(
174
  return steps[0]
175
 
176
  if normalize_category(category) == "Verbal":
177
- return "I can help analyse the wording or logic, but I do not have a full verbal solver yet."
178
 
179
  if normalize_category(category) == "DataInsight":
180
- return "I can help reason through the data, but I cannot confidently solve this from the current parse alone yet."
181
 
182
- return "I can help with this, but I cannot confidently solve it from the current parse alone yet."
183
 
184
 
185
  def _normalize_text(text: str) -> str:
@@ -215,7 +210,7 @@ def _infer_structure_terms(question_text: str, topic: Optional[str], question_ty
215
  terms.extend(["multiply", "undo operations"])
216
  if "%" in q or "percent" in q:
217
  terms.extend(["percent", "percentage"])
218
- if "ratio" in q:
219
  terms.extend(["ratio", "proportion"])
220
  if "mean" in q or "average" in q:
221
  terms.extend(["mean", "average"])
@@ -404,6 +399,117 @@ def _build_retrieval_query(
404
  return " ".join(parts).strip()
405
 
406
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  class ConversationEngine:
408
  def __init__(
409
  self,
@@ -445,8 +551,6 @@ class ConversationEngine:
445
  resolved_intent = intent or detect_intent(user_text, help_mode)
446
  resolved_help_mode = help_mode or intent_to_help_mode(resolved_intent)
447
 
448
- # Critical change:
449
- # never reveal answers from this layer, regardless of transparency/help mode
450
  result = SolverResult(
451
  domain="general",
452
  solved=False,
@@ -530,7 +634,12 @@ class ConversationEngine:
530
  if selected_chunks and resolved_help_mode != "answer":
531
  reply = f"{reply}\n\nRelevant study notes:\n" + "\n".join(_teaching_lines(selected_chunks))
532
 
533
- if not result.solved and self.generator is not None:
 
 
 
 
 
534
  try:
535
  generated = self.generator.generate(
536
  user_text=user_text or solver_input,
@@ -540,16 +649,35 @@ class ConversationEngine:
540
  retrieval_context=selected_chunks,
541
  chat_history=chat_history or [],
542
  )
 
543
  if generated and generated.strip():
544
- reply = generated.strip()
545
- result.used_generator = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  except Exception:
547
- pass
 
 
 
 
548
 
549
  reply = format_reply(reply, tone, verbosity, transparency, resolved_help_mode)
550
 
551
- # Critical change:
552
- # never expose final answer fields to Unity/client
553
  result.answer_letter = None
554
  result.answer_value = None
555
  result.internal_answer = None
 
69
  "number_theory": ["circle", "triangle", "median salary"],
70
  }
71
 
 
72
  def _normalize_classified_topic(topic: Optional[str], category: Optional[str], question_text: str) -> str:
73
  t = (topic or "").strip().lower()
74
  q = (question_text or "").lower()
 
79
 
80
  if "%" in q or "percent" in q:
81
  return "percent"
82
+ if "ratio" in q or re.search(r"\b\d+\s*:\s*\d+\b", q):
83
  return "ratio"
84
  if "probability" in q or "chosen at random" in q:
85
  return "probability"
 
114
 
115
 
116
  def _safe_steps(steps: List[str]) -> List[str]:
 
 
 
 
117
  cleaned: List[str] = []
118
  banned_patterns = [
119
  r"\bthe answer is\b",
 
169
  return steps[0]
170
 
171
  if normalize_category(category) == "Verbal":
172
+ return "I can help analyse the wording or logic, but I need the full question text to guide you properly."
173
 
174
  if normalize_category(category) == "DataInsight":
175
+ return "I can help reason through the data, but I need the full question or chart details to guide you properly."
176
 
177
+ return "I can help with this, but I need the full question text to guide you properly."
178
 
179
 
180
  def _normalize_text(text: str) -> str:
 
210
  terms.extend(["multiply", "undo operations"])
211
  if "%" in q or "percent" in q:
212
  terms.extend(["percent", "percentage"])
213
+ if "ratio" in q or re.search(r"\b\d+\s*:\s*\d+\b", q):
214
  terms.extend(["ratio", "proportion"])
215
  if "mean" in q or "average" in q:
216
  terms.extend(["mean", "average"])
 
399
  return " ".join(parts).strip()
400
 
401
 
402
+ def _fallback_more_info_reply(
403
+ category: Optional[str],
404
+ topic: Optional[str],
405
+ intent: str,
406
+ ) -> str:
407
+ normalized_category = normalize_category(category)
408
+
409
+ if normalized_category == "Quantitative" or topic in {
410
+ "algebra", "percent", "ratio", "probability", "number_theory", "geometry", "statistics", "quant"
411
+ }:
412
+ if intent in {"walkthrough", "step_by_step", "method", "explain", "hint", "instruction"}:
413
+ return (
414
+ "I need the full question wording to guide this properly step by step. "
415
+ "Please paste the complete problem, and include the answer choices if there are any."
416
+ )
417
+ return (
418
+ "I need the full question wording to help properly. "
419
+ "Please paste the complete problem, and include the answer choices if there are any."
420
+ )
421
+
422
+ if normalized_category == "DataInsight":
423
+ return (
424
+ "I need the full chart, table, or question wording to help properly. "
425
+ "Please send the complete prompt and any answer choices."
426
+ )
427
+
428
+ if normalized_category == "Verbal":
429
+ return (
430
+ "I need the full passage, sentence, or question wording to help properly. "
431
+ "Please paste the complete text and any answer choices."
432
+ )
433
+
434
+ return (
435
+ "I need a bit more information to help properly. "
436
+ "Please send the full question or exact wording."
437
+ )
438
+
439
+
440
+ def _is_bad_generated_reply(text: str, user_text: str = "") -> bool:
441
+ t = (text or "").strip()
442
+ tl = t.lower()
443
+ ul = (user_text or "").strip().lower()
444
+
445
+ if not t:
446
+ return True
447
+
448
+ if len(t) < 12:
449
+ return True
450
+
451
+ bad_exact = {
452
+ "0",
453
+ "formula",
454
+ "formula formula",
455
+ "the answer",
456
+ "answer only",
457
+ "unknown",
458
+ "none",
459
+ "n/a",
460
+ }
461
+ if tl in bad_exact:
462
+ return True
463
+
464
+ bad_substrings = [
465
+ "if the problem is not fully solvable",
466
+ "if the problem is not fully solvable from the parse",
467
+ "give the test a chance to solve it",
468
+ "use the formula formula",
469
+ "cannot parse alone yet",
470
+ "i cannot parse",
471
+ "current parse alone",
472
+ "from the parse alone",
473
+ ]
474
+ if any(b in tl for b in bad_substrings):
475
+ return True
476
+
477
+ banned_answer_patterns = [
478
+ r"\bthe answer is\b",
479
+ r"\banswer:\b",
480
+ r"\bx\s*=",
481
+ r"\by\s*=",
482
+ r"\btherefore\b",
483
+ r"\bthat gives\b",
484
+ r"\bresult is\b",
485
+ ]
486
+ if any(re.search(p, tl) for p in banned_answer_patterns):
487
+ return True
488
+
489
+ words = re.findall(r"\b\w+\b", tl)
490
+ if len(words) >= 4:
491
+ unique_ratio = len(set(words)) / max(1, len(words))
492
+ if unique_ratio < 0.45:
493
+ return True
494
+
495
+ user_keywords = _extract_keywords(ul)
496
+ gen_keywords = _extract_keywords(tl)
497
+ if user_keywords and gen_keywords:
498
+ overlap = user_keywords.intersection(gen_keywords)
499
+ if len(overlap) == 0 and len(t) < 180:
500
+ return True
501
+
502
+ nonsense_patterns = [
503
+ r"\bformula\s+formula\b",
504
+ r"\btest\s+a\s+chance\s+to\s+solve\b",
505
+ r"^[\W_]*\d+[\W_]*$",
506
+ ]
507
+ if any(re.search(p, tl) for p in nonsense_patterns):
508
+ return True
509
+
510
+ return False
511
+
512
+
513
  class ConversationEngine:
514
  def __init__(
515
  self,
 
551
  resolved_intent = intent or detect_intent(user_text, help_mode)
552
  resolved_help_mode = help_mode or intent_to_help_mode(resolved_intent)
553
 
 
 
554
  result = SolverResult(
555
  domain="general",
556
  solved=False,
 
634
  if selected_chunks and resolved_help_mode != "answer":
635
  reply = f"{reply}\n\nRelevant study notes:\n" + "\n".join(_teaching_lines(selected_chunks))
636
 
637
+ should_try_generator = (
638
+ self.generator is not None
639
+ and not result.solved
640
+ )
641
+
642
+ if should_try_generator:
643
  try:
644
  generated = self.generator.generate(
645
  user_text=user_text or solver_input,
 
649
  retrieval_context=selected_chunks,
650
  chat_history=chat_history or [],
651
  )
652
+
653
  if generated and generated.strip():
654
+ candidate = generated.strip()
655
+
656
+ if not _is_bad_generated_reply(candidate, user_text or solver_input):
657
+ reply = candidate
658
+ result.used_generator = True
659
+ else:
660
+ reply = _fallback_more_info_reply(
661
+ category=inferred_category,
662
+ topic=result.topic,
663
+ intent=resolved_intent,
664
+ )
665
+ else:
666
+ reply = _fallback_more_info_reply(
667
+ category=inferred_category,
668
+ topic=result.topic,
669
+ intent=resolved_intent,
670
+ )
671
+
672
  except Exception:
673
+ reply = _fallback_more_info_reply(
674
+ category=inferred_category,
675
+ topic=result.topic,
676
+ intent=resolved_intent,
677
+ )
678
 
679
  reply = format_reply(reply, tone, verbosity, transparency, resolved_help_mode)
680
 
 
 
681
  result.answer_letter = None
682
  result.answer_value = None
683
  result.internal_answer = None