""" AnveshAI Edge — v2 ================== Terminal-based offline-first AI tutor for JEE Advanced. Correctness-First pipeline: deterministic engines → LLM explanation. Routing: /commands → instant handler Arithmetic → math_engine (AST) Advanced math → SymPy + LLM Physics / Chemistry → deterministic solver + LLM Logic → inference engine + LLM Knowledge → BM25 KB + LLM Conversation → pattern rules + LLM Commands: /help → list commands /history → last 10 interactions /clear → clear conversation history /test → JEE mock test (20 questions, scored) /formulas → full formula sheet /formulas p → physics formulas only /formulas c → chemistry formulas only /formulas m → math formulas only /hint → level-1 hint for last question /hint2 → level-2 hint (+ formula) /hint3 → level-3 hint (+ first step) /review → spaced repetition review session /progress → topic-level progress summary /benchmark → full benchmark report /exit → quit """ import sys try: from colorama import init as colorama_init, Fore, Style colorama_init(autoreset=True) except ImportError: class _NoColor: def __getattr__(self, _): return "" Fore = Style = _NoColor() from router import classify_intent from math_engine import evaluate as math_evaluate from advanced_math_engine import solve as advanced_math_solve from knowledge_engine import KnowledgeEngine from conversation_engine import ConversationEngine from llm_engine import LLMEngine, MATH_SYSTEM_PROMPT, MATH_TEMPERATURE, CHAT_SYSTEM_PROMPT from reasoning_engine import ReasoningEngine from inference_engine import InferenceEngine from physics_engine import PhysicsEngine from chemistry_engine import ChemistryEngine from memory import ( initialize_db, save_interaction, format_history, clear_history, save_progress, get_progress_summary, get_weak_topics, get_due_topics, ) from mock_test import run_mock_test from formula_sheet import get_formula_sheet from hint_engine import get_hints from spaced_repetition import run_review_session from benchmark_report import generate_report BANNER = r""" ╔══════════════════════════════════════════════════════╗ ║ ___ __ ___ ____ ║ ║ / _ | ___ _ _____ ___ / / / _ | / _/ ║ ║ / __ |/ _ \ |/ / -_|_- None: print(f"{color}{text}{Style.RESET_ALL}" if color else text) def _prompt() -> str: try: return input(f"\n{Fore.CYAN}You{Style.RESET_ALL} › ").strip() except (EOFError, KeyboardInterrupt): return "/exit" def _respond(label: str, text: str) -> None: print( f"\n{Fore.GREEN}AnveshAI{Style.RESET_ALL} " f"[{Fore.YELLOW}{label}{Style.RESET_ALL}] › {text}" ) def _system(text: str) -> None: print(f"{Fore.MAGENTA} {text}{Style.RESET_ALL}") def compose_response( user_input: str, intent: str, knowledge_engine: KnowledgeEngine, conversation_engine: ConversationEngine, llm_engine: LLMEngine, reasoning_engine: ReasoningEngine, inference_engine: InferenceEngine, physics_engine: PhysicsEngine, chemistry_engine: ChemistryEngine, ) -> tuple[str, str]: """ Route input through the full hierarchy. Returns (label, response_text). """ # ── Simple arithmetic ───────────────────────────────────────────────────── if intent == "math": return "Math", math_evaluate(user_input) # ── Advanced math ───────────────────────────────────────────────────────── if intent == "advanced_math": success, result_str, _latex = advanced_math_solve(user_input) if success: _system(f"SymPy → {result_str}") _system("Reasoning engine v2: decomposing problem…") plan = reasoning_engine.analyze(user_input, intent, has_symbolic_result=True) _system(plan.summary()) if plan.warnings: for w in plan.warnings: _system(f" ⚠ {w}") _system(f"Mode: {plan.reasoning_mode} — building prompt → LLM…") prompt = reasoning_engine.build_math_prompt(user_input, result_str, plan) explanation = llm_engine.generate( prompt, system_prompt=MATH_SYSTEM_PROMPT, temperature=MATH_TEMPERATURE, ) full_response = ( f"{result_str}\n\n" f"[{plan.reasoning_mode} | {plan.problem_type} | " f"confidence: {plan.confidence}]\n\n" f"{explanation}" ) return "AdvMath+CoT+LLM", full_response else: _system(f"SymPy error: {result_str}") _system("Reasoning engine v2: building fallback chain-of-thought…") plan = reasoning_engine.analyze(user_input, intent) _system(plan.summary()) prompt = reasoning_engine.build_math_fallback_prompt( user_input, plan, error_context=result_str ) llm_response = llm_engine.generate(prompt) return "AdvMath+CoT", llm_response # ── Physics ─────────────────────────────────────────────────────────────── if intent == "physics": success, result_str, formula_type = physics_engine.solve(user_input) if success: _system(f"Physics engine → {result_str}") _system("Reasoning engine: building physics explanation prompt…") plan = reasoning_engine.analyze(user_input, intent, has_symbolic_result=True) _system(plan.summary()) prompt = reasoning_engine.build_physics_prompt(user_input, result_str, formula_type, plan) explanation = llm_engine.generate( prompt, system_prompt=MATH_SYSTEM_PROMPT, temperature=MATH_TEMPERATURE, ) full_response = ( f"{result_str}\n\n" f"[{formula_type} | confidence: {plan.confidence}]\n\n" f"{explanation}" ) return "Physics+CoT+LLM", full_response else: _system(f"Physics engine: {result_str}") _system("Reasoning engine: building fallback physics prompt…") plan = reasoning_engine.analyze(user_input, intent) _system(plan.summary()) prompt = reasoning_engine.build_physics_fallback_prompt( user_input, plan, error_context=result_str ) return "Physics+CoT", llm_engine.generate(prompt) # ── Chemistry ───────────────────────────────────────────────────────────── if intent == "chemistry": success, result_str, chem_type = chemistry_engine.solve(user_input) if success: _system(f"Chemistry engine → {result_str}") _system("Reasoning engine: building chemistry explanation prompt…") plan = reasoning_engine.analyze(user_input, intent, has_symbolic_result=True) _system(plan.summary()) prompt = reasoning_engine.build_chemistry_prompt(user_input, result_str, chem_type, plan) explanation = llm_engine.generate( prompt, system_prompt=MATH_SYSTEM_PROMPT, temperature=MATH_TEMPERATURE, ) full_response = ( f"{result_str}\n\n" f"[{chem_type} | confidence: {plan.confidence}]\n\n" f"{explanation}" ) return "Chemistry+CoT+LLM", full_response else: _system(f"Chemistry engine: {result_str}") _system("Reasoning engine: building fallback chemistry prompt…") plan = reasoning_engine.analyze(user_input, intent) _system(plan.summary()) prompt = reasoning_engine.build_chemistry_fallback_prompt( user_input, plan, error_context=result_str ) return "Chemistry+CoT", llm_engine.generate(prompt) # ── Logic / Inference ───────────────────────────────────────────────────── if intent == "logic": _system("Inference engine: parsing logical structure…") inf_result = inference_engine.infer(user_input) if inf_result.valid: _system(f" ✔ Rule: {inf_result.rule_applied}") _system(f" → Conclusion: {inf_result.conclusion}") return "Logic+Inference", inf_result.to_response() _system(" Inference engine: no rule matched — reasoning-guided LLM…") plan = reasoning_engine.analyze(user_input, intent) _system(plan.summary()) kb_context = knowledge_engine.get_context(user_input) prompt = reasoning_engine.build_general_prompt( user_input, intent, kb_context, plan ) return "Logic+CoT+LLM", llm_engine.generate(prompt) # ── Knowledge ───────────────────────────────────────────────────────────── if intent == "knowledge": _system("Knowledge engine v2: BM25 retrieval…") kb_response, kb_found = knowledge_engine.query(user_input) if kb_found: _system(" ✔ KB match found (BM25)") return "Knowledge", kb_response _system(" KB: no confident match — reasoning engine + LLM (with KB context)…") plan = reasoning_engine.analyze(user_input, intent) _system(plan.summary()) kb_context = knowledge_engine.get_context(user_input, top_k=2) prompt = reasoning_engine.build_general_prompt( user_input, intent, kb_context, plan ) return "LLM+CoT-KB", llm_engine.generate(prompt) # ── Conversation ────────────────────────────────────────────────────────── chat_response, pattern_matched = conversation_engine.respond(user_input) if pattern_matched: return "Chat", chat_response # Guard: very short or clearly nonsensical input — answer without LLM stripped = user_input.strip() words = [w for w in stripped.split() if w.isalpha()] if len(stripped) < 4 or (len(words) == 0 and len(stripped) < 20): return "Chat", "I'm not sure what you mean — could you rephrase or ask me a JEE question?" # Fallback: use a lightweight conversational prompt (no CoT / reasoning plan) _system("No pattern match — LLM chat fallback…") return "Chat", llm_engine.generate( user_input, system_prompt=CHAT_SYSTEM_PROMPT, temperature=0.85, ) def main() -> None: _print(BANNER, Fore.CYAN) _system("Initialising modules…") initialize_db() _system("✔ Memory (SQLite) ready") knowledge_engine = KnowledgeEngine() _system( "✔ Knowledge base loaded (BM25, multi-passage synthesis)" if knowledge_engine.is_loaded() else "⚠ knowledge.txt not found" ) conversation_engine = ConversationEngine() inference_engine = InferenceEngine() physics_engine_obj = PhysicsEngine() chemistry_engine_obj = ChemistryEngine() _system("✔ Conversation engine ready") _system("✔ Math engine ready (AST safe-eval)") _system("✔ Advanced math engine ready (SymPy — 31+ operations)") _system("✔ Physics engine ready (deterministic formula solver — 20 domains)") _system("✔ Chemistry engine ready (deterministic solver — moles, pH, gas laws, …)") _system("✔ Reasoning engine v2 ready (CoT + Tree-of-Thought + self-consistency)") _system("✔ Inference engine ready (modus ponens/tollens, syllogisms, propositional logic)") _system("✔ Intent router v2 ready (8-way classification)") llm_engine = LLMEngine() reasoning_eng = ReasoningEngine() _system("✔ LLM engine ready (Qwen2.5-1.5B loads on first use)") _print(f"\n{Fore.WHITE}Type /help for commands or just start chatting!{Style.RESET_ALL}") last_question: str = "" # track last question for /hint while True: user_input = _prompt() if not user_input: continue intent = classify_intent(user_input) # ── System commands ─────────────────────────────────────────────────── if intent == "system": parts = user_input.lower().split() cmd = parts[0] if cmd == "/exit": _print(f"\n{Fore.CYAN}Goodbye! Session closed.{Style.RESET_ALL}") sys.exit(0) elif cmd == "/history": _print(f"\n{Fore.YELLOW}── Conversation History ─────────────────────{Style.RESET_ALL}") _print(format_history()) _print(f"{Fore.YELLOW}─────────────────────────────────────────────{Style.RESET_ALL}") elif cmd == "/clear": clear_history() _system("✔ Conversation history cleared.") elif cmd == "/help": _print(f"\n{Fore.YELLOW}── Help ──────────────────────────────────────{Style.RESET_ALL}") _print(HELP_TEXT) _print(f"{Fore.YELLOW}─────────────────────────────────────────────{Style.RESET_ALL}") # ── Mock test ───────────────────────────────────────────────── elif cmd == "/test": results = run_mock_test() _print(results, Fore.WHITE) # ── Formula sheet ───────────────────────────────────────────── elif cmd == "/formulas": subj_arg = parts[1] if len(parts) > 1 else None subj_map = { "p": "physics", "ph": "physics", "physics": "physics", "c": "chemistry", "ch": "chemistry", "chem": "chemistry", "chemistry": "chemistry", "m": "math", "ma": "math", "math": "math", "maths": "math", } subject = subj_map.get(subj_arg, None) if subj_arg else None _print(get_formula_sheet(subject), Fore.WHITE) # ── Hint system ─────────────────────────────────────────────── elif cmd in ("/hint", "/hint1"): if last_question: _print(get_hints(last_question, level=1), Fore.YELLOW) else: _respond("Hint", "Ask a question first, then use /hint for a hint.") elif cmd == "/hint2": if last_question: _print(get_hints(last_question, level=2), Fore.YELLOW) else: _respond("Hint", "Ask a question first, then use /hint2.") elif cmd == "/hint3": if last_question: _print(get_hints(last_question, level=3), Fore.YELLOW) else: _respond("Hint", "Ask a question first, then use /hint3.") # ── Spaced repetition review ────────────────────────────────── elif cmd == "/review": result_msg = run_review_session() _print(result_msg, Fore.WHITE) # ── Progress dashboard ──────────────────────────────────────── elif cmd == "/progress": summary = get_progress_summary() if not summary: _respond("Progress", "No progress data yet. Use /test or ask JEE questions.") else: lines = [f"\n{Fore.YELLOW}── Progress Summary ───────────────────────{Style.RESET_ALL}"] for subj, data in sorted(summary.items()): a = data['attempted'] c = data['correct'] acc = data['accuracy'] lines.append(f" {subj:<14} {c}/{a} correct ({acc:.1f}%)") for topic, td in sorted(data['topics'].items()): tacc = td['correct']/td['attempted']*100 if td['attempted'] else 0 marker = "✔" if tacc >= 60 else "⚠" lines.append(f" {marker} {topic:<25} {td['correct']}/{td['attempted']} ({tacc:.0f}%)") _print("\n".join(lines)) # ── Benchmark report ────────────────────────────────────────── elif cmd == "/benchmark": summary = get_progress_summary() due_topics = get_due_topics() weak = get_weak_topics() _print(generate_report(summary, due_topics, weak), Fore.WHITE) else: _respond("System", f"Unknown command '{user_input}'. Type /help.") continue # ── Compose response ────────────────────────────────────────────────── last_question = user_input # track for /hint label, response = compose_response( user_input, intent, knowledge_engine, conversation_engine, llm_engine, reasoning_eng, inference_engine, physics_engine_obj, chemistry_engine_obj, ) _respond(label, response) save_interaction(user_input, response) # ── Auto-track progress for physics/chemistry/math ──────────────── if intent in ("physics", "chemistry", "advanced_math"): subj_map = {"physics": "Physics", "chemistry": "Chemistry", "advanced_math": "Mathematics"} subj = subj_map[intent] topic = label.split("+")[0] if "+" in label else label is_correct = ( "Could not" not in response and "Provide" not in response[:80] and response.strip() != "" ) save_progress(subj, topic, is_correct, question=user_input) if __name__ == "__main__": main()