Spaces:
Sleeping
Sleeping
| """ | |
| Doubt Solver - Resolves student questions using Chain-of-Thought reasoning | |
| """ | |
| from typing import Tuple, Dict | |
| from config import LLM_PROVIDER | |
| from core.llm_engine import LLMEngine | |
| from core.prompt_builder import PromptBuilder | |
| from core.validator import InputValidator, ContentValidator | |
| from core.utils import log_event, truncate_text | |
| import re | |
| class DoubtSolver: | |
| """Solves student doubts using step-by-step reasoning.""" | |
| def __init__(self, llm_provider: str = LLM_PROVIDER): | |
| """ | |
| Initialize doubt solver. | |
| Args: | |
| llm_provider: LLM provider to use | |
| """ | |
| self.engine = LLMEngine(llm_provider) | |
| self.prompt_builder = PromptBuilder() | |
| self.validator = InputValidator() | |
| def solve( | |
| self, | |
| question: str, | |
| context: str = "", | |
| mode: str = "normal", | |
| use_cot: bool = True | |
| ) -> Tuple[bool, str]: | |
| """ | |
| Solve a student's doubt with reasoning. | |
| Args: | |
| question: Student's doubt/question | |
| context: Relevant material context | |
| mode: Response mode | |
| use_cot: Use Chain-of-Thought reasoning | |
| Returns: | |
| Tuple of (success, solution) | |
| """ | |
| # Validate input | |
| is_valid, msg = self.validator.validate_input(question) | |
| if not is_valid: | |
| log_event("VALIDATION_ERROR", f"DoubtSolver: {msg}") | |
| return False, msg | |
| # Build prompt | |
| try: | |
| prompt = self.prompt_builder.build_doubt_solver_prompt( | |
| question, | |
| context=truncate_text(context, 3000) if context else "", | |
| mode=mode | |
| ) | |
| if use_cot: | |
| # Add Chain-of-Thought emphasis | |
| prompt = f"""{prompt} | |
| IMPORTANT: Use Chain-of-Thought reasoning. | |
| 1. Break down the question into parts | |
| 2. Think through each part step by step | |
| 3. Show your reasoning clearly | |
| 4. Verify your answer | |
| 5. Provide the final answer | |
| """ | |
| log_event("PROMPT_BUILT", "Doubt solver prompt ready") | |
| except Exception as e: | |
| log_event("PROMPT_ERROR", f"Error building doubt solver: {str(e)}") | |
| return False, f"Error: {str(e)}" | |
| # Generate solution | |
| success, solution = self.engine.generate(prompt, max_tokens=2000) | |
| if not success: | |
| log_event("DOUBT_SOLVER_ERROR", solution) | |
| return False, solution | |
| # Quality check | |
| is_meaningful = ContentValidator.is_meaningful_response(solution, min_words=20) | |
| if not is_meaningful: | |
| log_event("QUALITY_CHECK_FAILED", "Solution too short") | |
| return False, "Response too short. Please try again." | |
| quality_score = ContentValidator.estimate_quality(solution) | |
| log_event("QUALITY_SCORE", f"Solution quality: {quality_score:.2f}") | |
| log_event("DOUBT_SOLVED", f"Solution provided") | |
| return True, solution | |
| def parse_solution(self, solution_text: str) -> Dict: | |
| """ | |
| Parse solution into structured components. | |
| Args: | |
| solution_text: Raw solution text | |
| Returns: | |
| Dictionary with thinking, answer, insights | |
| """ | |
| parsed = { | |
| "thinking": "", | |
| "answer": "", | |
| "insights": "", | |
| "raw_text": solution_text | |
| } | |
| # Extract THINKING section | |
| thinking_match = re.search( | |
| r'(?:THINKING|Step|Reasoning):\s*(.+?)(?=ANSWER|Final|$)', | |
| solution_text, | |
| re.IGNORECASE | re.DOTALL | |
| ) | |
| if thinking_match: | |
| parsed["thinking"] = thinking_match.group(1).strip() | |
| # Extract ANSWER section | |
| answer_match = re.search( | |
| r'(?:ANSWER|Final Answer):\s*(.+?)(?=INSIGHTS|Additional|$)', | |
| solution_text, | |
| re.IGNORECASE | re.DOTALL | |
| ) | |
| if answer_match: | |
| parsed["answer"] = answer_match.group(1).strip() | |
| else: | |
| # If no explicit answer, use last paragraph | |
| paragraphs = solution_text.split('\n\n') | |
| if paragraphs: | |
| parsed["answer"] = paragraphs[-1].strip() | |
| # Extract INSIGHTS section | |
| insights_match = re.search( | |
| r'(?:INSIGHTS|Additional|Tips):\s*(.+?)$', | |
| solution_text, | |
| re.IGNORECASE | re.DOTALL | |
| ) | |
| if insights_match: | |
| parsed["insights"] = insights_match.group(1).strip() | |
| return parsed | |
| def solve_with_context( | |
| self, | |
| question: str, | |
| context: str, | |
| mode: str = "normal" | |
| ) -> Tuple[bool, str]: | |
| """ | |
| Solve doubt with full context from notes. | |
| Args: | |
| question: Student's question | |
| context: Full context from notes | |
| mode: Response mode | |
| Returns: | |
| Tuple of (success, solution) | |
| """ | |
| return self.solve(question, context=context, mode=mode) | |
| def solve_step_by_step(self, question: str) -> Tuple[bool, str]: | |
| """ | |
| Solve doubt with emphasis on step-by-step reasoning. | |
| Args: | |
| question: Student's question | |
| Returns: | |
| Tuple of (success, solution) | |
| """ | |
| enhanced_prompt = f"""Solve this doubt step by step. | |
| Student's Question: {question} | |
| Your approach: | |
| 1. Clarify what's being asked | |
| 2. Identify key concepts | |
| 3. Work through it step by step | |
| 4. Check your reasoning | |
| 5. Provide clear final answer | |
| Use this format: | |
| STEP 1: [First step] | |
| STEP 2: [Second step] | |
| ... | |
| FINAL ANSWER: [Clear answer] | |
| Now solve:""" | |
| try: | |
| success, solution = self.engine.generate(enhanced_prompt, max_tokens=2000) | |
| return success, solution | |
| except Exception as e: | |
| return False, f"Error: {str(e)}" | |
| def compare_solutions( | |
| self, | |
| question: str, | |
| modes: list = None | |
| ) -> Tuple[bool, Dict]: | |
| """ | |
| Compare solutions in different modes. | |
| Args: | |
| question: Question to solve | |
| modes: List of modes to compare | |
| Returns: | |
| Tuple of (success, dict of solutions) | |
| """ | |
| if modes is None: | |
| modes = ["normal", "detailed", "teacher"] | |
| solutions = {} | |
| for mode in modes: | |
| success, solution = self.solve(question, mode=mode) | |
| solutions[mode] = solution if success else f"Error: {solution}" | |
| return True, solutions | |
| def is_question_valid(self, question: str) -> Tuple[bool, str]: | |
| """ | |
| Check if question is valid and answerable. | |
| Args: | |
| question: Question to validate | |
| Returns: | |
| Tuple of (is_valid, message) | |
| """ | |
| if len(question.strip()) < 5: | |
| return False, "Question too short" | |
| if len(question.strip().split()) < 3: | |
| return False, "Question not detailed enough" | |
| return True, "Valid question" | |