j-js commited on
Commit
626316f
·
verified ·
1 Parent(s): 1c8257c

Update conversation_logic.py

Browse files
Files changed (1) hide show
  1. conversation_logic.py +104 -57
conversation_logic.py CHANGED
@@ -38,24 +38,12 @@ STRUCTURE_KEYWORDS = {
38
  "equation", "solve", "isolate", "variable", "linear", "expression",
39
  "unknown", "algebra", "substitute", "rearrange"
40
  ],
41
- "percent": [
42
- "percent", "%", "percentage", "increase", "decrease", "of"
43
- ],
44
- "ratio": [
45
- "ratio", "proportion", "proportional", "part", "share"
46
- ],
47
- "statistics": [
48
- "mean", "median", "mode", "range", "average", "standard deviation"
49
- ],
50
- "probability": [
51
- "probability", "chance", "likely", "odds", "event"
52
- ],
53
- "geometry": [
54
- "triangle", "circle", "angle", "area", "perimeter", "radius", "diameter"
55
- ],
56
- "number_properties": [
57
- "integer", "odd", "even", "prime", "divisible", "factor", "multiple"
58
- ],
59
  }
60
 
61
  INTENT_KEYWORDS = {
@@ -74,24 +62,12 @@ MISMATCH_TERMS = {
74
  "absolute value", "modulus", "square root", "quadratic", "inequality",
75
  "roots", "parabola", "simultaneous equations"
76
  ],
77
- "percent": [
78
- "triangle", "circle", "prime", "absolute value"
79
- ],
80
- "ratio": [
81
- "absolute value", "quadratic", "circle"
82
- ],
83
- "statistics": [
84
- "absolute value", "prime", "triangle"
85
- ],
86
- "probability": [
87
- "absolute value", "circle area", "quadratic"
88
- ],
89
- "geometry": [
90
- "absolute value", "prime", "median salary"
91
- ],
92
- "number_properties": [
93
- "circle", "triangle", "absolute value"
94
- ],
95
  }
96
 
97
 
@@ -255,8 +231,7 @@ def _score_chunk(
255
  elif topic.lower() in text:
256
  score += 2.0
257
 
258
- structure_terms = _infer_structure_terms(question_text, topic)
259
- for term in structure_terms:
260
  if term.lower() in text:
261
  score += 1.5
262
 
@@ -268,8 +243,7 @@ def _score_chunk(
268
  overlap = sum(1 for kw in q_keywords if kw in text)
269
  score += min(overlap * 0.4, 3.0)
270
 
271
- mismatch_terms = _infer_mismatch_terms(topic, question_text)
272
- for bad in mismatch_terms:
273
  if bad.lower() in text:
274
  score -= 2.5
275
 
@@ -325,6 +299,78 @@ def _build_retrieval_query(
325
  return " ".join(parts).strip()
326
 
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  class ConversationEngine:
329
  def __init__(
330
  self,
@@ -374,7 +420,9 @@ class ConversationEngine:
374
  selected_chunks: List[RetrievedChunk] = []
375
 
376
  if is_quant_question(solver_input):
377
- result = solve_quant(solver_input)
 
 
378
  result.help_mode = resolved_help_mode
379
 
380
  reply = _compose_quant_reply(
@@ -391,8 +439,10 @@ class ConversationEngine:
391
  )
392
 
393
  if allow_retrieval and retrieval_context:
 
 
394
  filtered = _filter_retrieved_chunks(
395
- chunks=retrieval_context,
396
  intent=resolved_intent,
397
  topic=result.topic,
398
  question_text=solver_input,
@@ -403,14 +453,13 @@ class ConversationEngine:
403
  result.teaching_chunks = filtered
404
 
405
  elif allow_retrieval and self.retriever is not None:
406
- query = _build_retrieval_query(
407
  raw_user_text=user_text,
408
  question_text=solver_input,
409
  intent=resolved_intent,
410
  topic=result.topic,
411
  solved=bool(result.solved),
412
- )
413
- retrieved = self.retriever.search(query, top_k=6)
414
  filtered = _filter_retrieved_chunks(
415
  chunks=retrieved,
416
  intent=resolved_intent,
@@ -426,18 +475,16 @@ class ConversationEngine:
426
  reply = f"{reply}\n\nRelevant study notes:\n" + "\n".join(_teaching_lines(selected_chunks))
427
 
428
  if not result.solved and self.generator is not None:
429
- try:
430
- generated = self.generator.generate(
431
- user_text=user_text or solver_input,
432
- intent=resolved_intent,
433
- topic=result.topic,
434
- chat_history=chat_history or [],
435
- )
436
- if generated and generated.strip():
437
- reply = generated.strip()
438
- result.used_generator = True
439
- except Exception:
440
- pass
441
 
442
  reply = format_reply(
443
  text=reply,
 
38
  "equation", "solve", "isolate", "variable", "linear", "expression",
39
  "unknown", "algebra", "substitute", "rearrange"
40
  ],
41
+ "percent": ["percent", "%", "percentage", "increase", "decrease", "of"],
42
+ "ratio": ["ratio", "proportion", "proportional", "part", "share"],
43
+ "statistics": ["mean", "median", "mode", "range", "average", "standard deviation"],
44
+ "probability": ["probability", "chance", "likely", "odds", "event"],
45
+ "geometry": ["triangle", "circle", "angle", "area", "perimeter", "radius", "diameter"],
46
+ "number_properties": ["integer", "odd", "even", "prime", "divisible", "factor", "multiple"],
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
 
49
  INTENT_KEYWORDS = {
 
62
  "absolute value", "modulus", "square root", "quadratic", "inequality",
63
  "roots", "parabola", "simultaneous equations"
64
  ],
65
+ "percent": ["triangle", "circle", "prime", "absolute value"],
66
+ "ratio": ["absolute value", "quadratic", "circle"],
67
+ "statistics": ["absolute value", "prime", "triangle"],
68
+ "probability": ["absolute value", "circle area", "quadratic"],
69
+ "geometry": ["absolute value", "prime", "median salary"],
70
+ "number_properties": ["circle", "triangle", "absolute value"],
 
 
 
 
 
 
 
 
 
 
 
 
71
  }
72
 
73
 
 
231
  elif topic.lower() in text:
232
  score += 2.0
233
 
234
+ for term in _infer_structure_terms(question_text, topic):
 
235
  if term.lower() in text:
236
  score += 1.5
237
 
 
243
  overlap = sum(1 for kw in q_keywords if kw in text)
244
  score += min(overlap * 0.4, 3.0)
245
 
246
+ for bad in _infer_mismatch_terms(topic, question_text):
 
247
  if bad.lower() in text:
248
  score -= 2.5
249
 
 
299
  return " ".join(parts).strip()
300
 
301
 
302
+ def _to_chunk(item: Any) -> Optional[RetrievedChunk]:
303
+ if isinstance(item, RetrievedChunk):
304
+ return item
305
+
306
+ if isinstance(item, dict):
307
+ return RetrievedChunk(
308
+ text=str(item.get("text", "")).strip(),
309
+ topic=str(item.get("topic", "general")).strip() or "general",
310
+ source=str(item.get("source", "local")).strip() or "local",
311
+ score=float(item.get("score", 0.0) or 0.0),
312
+ )
313
+
314
+ if isinstance(item, str):
315
+ txt = item.strip()
316
+ if txt:
317
+ return RetrievedChunk(text=txt)
318
+
319
+ return None
320
+
321
+
322
+ def _retrieve_chunks(retriever: Any, query: str) -> List[RetrievedChunk]:
323
+ raw = None
324
+
325
+ try:
326
+ raw = retriever.search(query, top_k=6)
327
+ except TypeError:
328
+ try:
329
+ raw = retriever.search(query)
330
+ except Exception:
331
+ raw = None
332
+ except Exception:
333
+ raw = None
334
+
335
+ if raw is None:
336
+ for method_name in ["retrieve", "run", "query"]:
337
+ method = getattr(retriever, method_name, None)
338
+ if callable(method):
339
+ try:
340
+ raw = method(query)
341
+ break
342
+ except Exception:
343
+ raw = None
344
+
345
+ if raw is None:
346
+ return []
347
+
348
+ if not isinstance(raw, list):
349
+ raw = [raw]
350
+
351
+ out: List[RetrievedChunk] = []
352
+ for item in raw:
353
+ chunk = _to_chunk(item)
354
+ if chunk and chunk.text:
355
+ out.append(chunk)
356
+ return out
357
+
358
+
359
+ def _generate_fallback(generator: Any, user_text: str, intent: str, topic: Optional[str], chat_history: List[Dict[str, Any]]) -> Optional[str]:
360
+ for call in [
361
+ lambda: generator.generate(user_text=user_text, intent=intent, topic=topic, chat_history=chat_history),
362
+ lambda: generator.generate(user_text, intent=intent, topic=topic, chat_history=chat_history),
363
+ lambda: generator.generate(user_text),
364
+ ]:
365
+ try:
366
+ result = call()
367
+ if isinstance(result, str) and result.strip():
368
+ return result.strip()
369
+ except Exception:
370
+ continue
371
+ return None
372
+
373
+
374
  class ConversationEngine:
375
  def __init__(
376
  self,
 
420
  selected_chunks: List[RetrievedChunk] = []
421
 
422
  if is_quant_question(solver_input):
423
+ solved_result = solve_quant(solver_input)
424
+ if solved_result is not None:
425
+ result = solved_result
426
  result.help_mode = resolved_help_mode
427
 
428
  reply = _compose_quant_reply(
 
439
  )
440
 
441
  if allow_retrieval and retrieval_context:
442
+ safe_chunks = [_to_chunk(c) for c in retrieval_context]
443
+ safe_chunks = [c for c in safe_chunks if c and c.text]
444
  filtered = _filter_retrieved_chunks(
445
+ chunks=safe_chunks,
446
  intent=resolved_intent,
447
  topic=result.topic,
448
  question_text=solver_input,
 
453
  result.teaching_chunks = filtered
454
 
455
  elif allow_retrieval and self.retriever is not None:
456
+ retrieved = _retrieve_chunks(self.retriever, _build_retrieval_query(
457
  raw_user_text=user_text,
458
  question_text=solver_input,
459
  intent=resolved_intent,
460
  topic=result.topic,
461
  solved=bool(result.solved),
462
+ ))
 
463
  filtered = _filter_retrieved_chunks(
464
  chunks=retrieved,
465
  intent=resolved_intent,
 
475
  reply = f"{reply}\n\nRelevant study notes:\n" + "\n".join(_teaching_lines(selected_chunks))
476
 
477
  if not result.solved and self.generator is not None:
478
+ generated = _generate_fallback(
479
+ generator=self.generator,
480
+ user_text=user_text or solver_input,
481
+ intent=resolved_intent,
482
+ topic=result.topic,
483
+ chat_history=chat_history or [],
484
+ )
485
+ if generated:
486
+ reply = generated
487
+ result.used_generator = True
 
 
488
 
489
  reply = format_reply(
490
  text=reply,