""" End-to-End Math Solver with Confidence-Triggered Hints You give it a math question. It: 1. Solves it step by step 2. Monitors confidence at each step 3. Automatically calls the HintTool when stuck 4. Returns the final answer """ import numpy as np from typing import Optional, Dict, Any, List from dataclasses import dataclass from hint_tool import HintTool @dataclass class SolverResult: """Result from solving a math problem.""" question: str final_answer: str steps: List[Dict[str, Any]] hints_used: List[Dict[str, Any]] confidence_trace: List[float] success: bool class MathSolverWithHints: """ A math solver that uses hints when confidence is low. """ def __init__( self, hint_tool: Optional[HintTool] = None, confidence_threshold: float = 0.5, max_steps: int = 8, ): self.hint_tool = hint_tool or HintTool() self.confidence_threshold = confidence_threshold self.max_steps = max_steps def solve(self, question: str, problem_id: Optional[str] = None) -> SolverResult: """Solve a math problem with automatic hint triggering.""" problem_id = problem_id or self._detect_problem_type(question) steps = [] hints_used = [] confidence_trace = [] current_answer = "" for step_num in range(1, self.max_steps + 1): step_result = self._reasoning_step( question=question, step_num=step_num, previous_steps=steps, hints_used=hints_used, ) confidence = step_result["confidence"] confidence_trace.append(confidence) step_data = { "step_num": step_num, "thought": step_result["thought"], "confidence": confidence, "action": step_result["action"], } # Check if hint is needed if confidence < self.confidence_threshold: hint = self.hint_tool.forward(problem_id, confidence) step_data["hint_triggered"] = True step_data["hint"] = hint hints_used.append({ "step": step_num, "confidence": confidence, "hint": hint, }) # Re-reason with hint step_result = self._reasoning_step( question=question, step_num=step_num, previous_steps=steps, hints_used=hints_used, current_hint=hint, ) step_data["thought_after_hint"] = step_result["thought"] step_data["confidence_after_hint"] = step_result["confidence"] else: step_data["hint_triggered"] = False steps.append(step_data) if step_result.get("is_final_answer", False): current_answer = step_result["answer"] break success = self._evaluate_answer(question, current_answer) return SolverResult( question=question, final_answer=current_answer, steps=steps, hints_used=hints_used, confidence_trace=confidence_trace, success=success, ) def _detect_problem_type(self, question: str) -> str: """Auto-detect problem category from question text.""" q = question.lower() if any(word in q for word in ["solve for", "equation", "x +", "2x", "3x"]): return "algebra_linear" elif any(word in q for word in ["circle", "radius", "diameter", "circumference", "area of circle"]): return "geometry_circle" elif any(word in q for word in ["derivative", "d/dx", "slope", "rate of change"]): return "calculus_derivative" elif any(word in q for word in ["probability", "bayes", "chance", "odds"]): return "probability_bayes" elif any(word in q for word in ["prime", "divisible", "factor", "gcd", "lcm"]): return "number_theory_prime" elif any(word in q for word in ["fraction", "numerator", "denominator"]): return "fractions_addition" return "algebra_linear" def _reasoning_step(self, question, step_num, previous_steps, hints_used, current_hint=None): """Perform one reasoning step (rule-based solver for demo).""" q = question.lower() if "solve for x" in q and "2x + 5 = 13" in q: return self._solve_linear_2x_plus_5(step_num, current_hint) elif "solve for x" in q and "3x - 7 = 14" in q: return self._solve_linear_3x_minus_7(step_num, current_hint) elif "area of circle" in q and "radius 5" in q: return self._solve_circle_area(step_num, current_hint) elif "derivative of x^2" in q: return self._solve_derivative_x2(step_num, current_hint) elif "prime" in q and "17" in q: return self._solve_prime_17(step_num, current_hint) else: return self._generic_step(step_num, current_hint) def _solve_linear_2x_plus_5(self, step_num, hint): if step_num == 1: conf = 0.8 if hint else 0.6 return {"thought": "Subtract 5 from both sides: 2x = 8", "confidence": conf, "action": "subtract 5", "is_final_answer": False} elif step_num == 2: conf = 0.9 if hint else 0.7 return {"thought": "Divide by 2: x = 4", "confidence": conf, "action": "divide by 2", "is_final_answer": True, "answer": "x = 4"} return {"thought": "Done", "confidence": 0.9, "action": "none", "is_final_answer": True, "answer": "x = 4"} def _solve_linear_3x_minus_7(self, step_num, hint): if step_num == 1: conf = 0.7 if hint else 0.4 return {"thought": "Add 7 to both sides: 3x = 21", "confidence": conf, "action": "add 7", "is_final_answer": False} elif step_num == 2: conf = 0.9 if hint else 0.6 return {"thought": "Divide by 3: x = 7", "confidence": conf, "action": "divide by 3", "is_final_answer": True, "answer": "x = 7"} return {"thought": "Done", "confidence": 0.9, "action": "none", "is_final_answer": True, "answer": "x = 7"} def _solve_circle_area(self, step_num, hint): if step_num == 1: conf = 0.6 if hint else 0.3 return {"thought": "Area = pi*r^2 = pi*25 = 25*pi ≈ 78.54", "confidence": conf, "action": "apply formula", "is_final_answer": True, "answer": "78.54 (or 25*pi)"} return {"thought": "Done", "confidence": 0.9, "action": "none", "is_final_answer": True, "answer": "78.54"} def _solve_derivative_x2(self, step_num, hint): if step_num == 1: conf = 0.85 if hint else 0.65 return {"thought": "Using power rule: d/dx(x^2) = 2x", "confidence": conf, "action": "power rule", "is_final_answer": True, "answer": "2x"} return {"thought": "Done", "confidence": 0.9, "action": "none", "is_final_answer": True, "answer": "2x"} def _solve_prime_17(self, step_num, hint): if step_num == 1: conf = 0.7 if hint else 0.45 return {"thought": "17 is only divisible by 1 and 17. No divisors between 2 and sqrt(17)≈4.1. So 17 is prime.", "confidence": conf, "action": "check divisibility", "is_final_answer": True, "answer": "Yes, 17 is prime"} return {"thought": "Done", "confidence": 0.9, "action": "none", "is_final_answer": True, "answer": "Yes"} def _generic_step(self, step_num, hint): conf = 0.7 if hint else 0.4 return {"thought": f"Step {step_num}: Analyzing...", "confidence": conf, "action": "analyze", "is_final_answer": step_num >= 3, "answer": "Unable to solve"} def _evaluate_answer(self, question, answer): if not answer: return False correct = { "2x + 5 = 13": "x = 4", "3x - 7 = 14": "x = 7", "radius 5": "78.54", "x^2": "2x", "17": "prime", } for key, val in correct.items(): if key in question.lower() and val in answer.lower(): return True return False def print_result(self, result): """Pretty print the solver result.""" print("=" * 60) print("MATH SOLVER RESULT") print("=" * 60) print(f"\nQuestion: {result.question}") print(f"Answer: {result.final_answer}") print(f"Success: {result.success}") print(f"\n--- Reasoning Steps ({len(result.steps)}) ---") for step in result.steps: print(f"\nStep {step['step_num']}:") print(f" Thought: {step['thought']}") print(f" Confidence: {step['confidence']:.3f}") if step.get("hint_triggered"): print(f" HINT USED: {step['hint'][:80]}...") if "thought_after_hint" in step: print(f" Revised thought: {step['thought_after_hint']}") if result.hints_used: print(f"\n--- Hints Used ({len(result.hints_used)}) ---") for h in result.hints_used: print(f" Step {h['step']}: {h['hint'][:60]}...") else: print("\n--- No hints needed ---") print(f"\nConfidence trace: {[f'{c:.2f}' for c in result.confidence_trace]}") print("=" * 60) def solve_math(question: str, problem_id: Optional[str] = None, **kwargs) -> SolverResult: """Quick function to solve a math problem with hints.""" solver = MathSolverWithHints(**kwargs) result = solver.solve(question, problem_id) solver.print_result(result) return result