""" =============================================== AnveshAI Edge v2 - HuggingFace Spaces (Gradio) =============================================== Offline-first AI tutor for JEE Advanced. Correctness-first pipeline: deterministic engines, then LLM explanation. """ import os import sys from typing import Optional sys.path.insert(0, os.path.dirname(__file__)) import gradio as gr 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 from main import compose_response initialize_db() knowledge_engine = KnowledgeEngine() conversation_engine = ConversationEngine() inference_engine = InferenceEngine() physics_engine_obj = PhysicsEngine() chemistry_engine_obj = ChemistryEngine() llm_engine = LLMEngine() reasoning_eng = ReasoningEngine() _last_question: list[str] = [""] WELCOME = ( "Hi, I am **AnveshAI Edge v2**.\n\n" "What are we solving today?" ) STARTER_HISTORY = [{"role": "assistant", "content": WELCOME}] APP_CSS = """ :root { --edge-bg: #f7f5ef; --edge-panel: #fffdfa; --edge-ink: #1f2328; --edge-muted: #6b7280; --edge-line: #e5e0d6; --edge-sidebar: #111318; --edge-sidebar-soft: #1c2028; --edge-sidebar-text: #f8f5ec; --edge-teal: #0f766e; --edge-amber: #b45309; --edge-indigo: #4f46e5; } .gradio-container { background: var(--edge-bg) !important; color: var(--edge-ink) !important; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif !important; } #anvesh-app { max-width: 1380px; margin: 0 auto; } .app-shell { min-height: calc(100vh - 32px); gap: 0 !important; overflow: hidden; border: 1px solid var(--edge-line); border-radius: 18px; background: var(--edge-panel); box-shadow: 0 20px 60px rgba(17, 19, 24, 0.08); } .sidebar { min-width: 260px !important; max-width: 300px !important; background: var(--edge-sidebar); color: var(--edge-sidebar-text); padding: 18px !important; } .sidebar .prose, .sidebar .prose *, .sidebar label, .sidebar span { color: var(--edge-sidebar-text) !important; } .brand-lockup { padding: 2px 2px 16px; border-bottom: 1px solid rgba(255, 255, 255, 0.12); } .brand-lockup h1 { margin: 0; font-size: 1.1rem; line-height: 1.2; letter-spacing: 0; } .brand-lockup p { margin: 8px 0 0; color: #c9c2b8 !important; font-size: 0.88rem; } .rail-card { margin-top: 16px; padding: 14px; border: 1px solid rgba(255, 255, 255, 0.11); border-radius: 8px; background: var(--edge-sidebar-soft); } .rail-card h2, .rail-card h3, .rail-card p { margin-top: 0; } .rail-card code { color: #f7d08a !important; background: rgba(255, 255, 255, 0.08) !important; border-radius: 6px; } .sidebar button { border-radius: 8px !important; } .quick-grid { gap: 8px !important; } .main-panel { min-height: calc(100vh - 32px); background: var(--edge-panel); padding: 0 !important; } .topbar { align-items: center; gap: 16px !important; padding: 18px 24px !important; border-bottom: 1px solid var(--edge-line); background: rgba(255, 253, 250, 0.92); } .topbar .prose { margin: 0 !important; } .topbar h2 { margin: 0; font-size: 1.15rem; line-height: 1.25; letter-spacing: 0; } .topbar p { margin: 4px 0 0; color: var(--edge-muted); font-size: 0.9rem; } .status-pill { justify-content: flex-end; } .status-pill .prose { display: flex; justify-content: flex-end; } .status-pill p { width: fit-content; margin: 0; padding: 8px 10px; border: 1px solid #d7ecdf; border-radius: 999px; color: #14532d; background: #edf8f1; font-size: 0.82rem; font-weight: 650; } .chat-region { padding: 20px 24px 0 !important; } #chatbot { min-height: 56vh; border: 0 !important; background: transparent !important; } #chatbot .message, #chatbot .bubble-wrap, #chatbot .message-row { font-size: 0.98rem; line-height: 1.6; } #chatbot .message.user, #chatbot .user .message, #chatbot .user-message { background: #ebe7ff !important; color: #211f35 !important; border: 1px solid #d9d2ff !important; border-radius: 16px !important; } #chatbot .message.bot, #chatbot .bot .message, #chatbot .assistant-message { background: #ffffff !important; color: var(--edge-ink) !important; border: 1px solid var(--edge-line) !important; border-radius: 16px !important; box-shadow: 0 8px 24px rgba(17, 19, 24, 0.05); } #chatbot code { white-space: pre-wrap; } .composer { margin: 12px 24px 18px !important; padding: 12px !important; border: 1px solid var(--edge-line) !important; border-radius: 14px !important; background: #ffffff !important; box-shadow: 0 12px 32px rgba(17, 19, 24, 0.08); } .composer textarea { min-height: 58px !important; border: 0 !important; box-shadow: none !important; font-size: 1rem !important; } .composer .form { border: 0 !important; } .send-row { align-items: center; border-top: 1px solid #f0ece3; padding-top: 10px !important; } .send-row .prose p { margin: 0; color: var(--edge-muted); font-size: 0.84rem; } .examples-wrap { padding: 0 24px 24px !important; } .examples-wrap .examples { border: 1px solid var(--edge-line) !important; border-radius: 8px !important; background: #fbfaf7 !important; } footer { display: none !important; } @media (max-width: 900px) { .app-shell { border-radius: 0; min-height: 100vh; } .sidebar { max-width: none !important; min-width: 100% !important; } .main-panel { min-height: auto; } .topbar, .chat-region, .examples-wrap { padding-left: 14px !important; padding-right: 14px !important; } .composer { margin-left: 14px !important; margin-right: 14px !important; } } """ def _initial_history() -> list[dict]: return [message.copy() for message in STARTER_HISTORY] def new_chat() -> tuple[list[dict], list[dict], str]: _last_question[0] = "" history = _initial_history() return history, history, "" def chat(user_input: str, history: Optional[list[dict]]) -> tuple[list[dict], list[dict], str]: history = list(history or _initial_history()) user_input = (user_input or "").strip() if not user_input: return history, history, "" intent = classify_intent(user_input) if intent == "system": parts = user_input.lower().split() cmd = parts[0] if cmd == "/help": reply = ( "**Available commands**\n\n" "| Command | Description |\n" "|---|---|\n" "| `/help` | Show this message |\n" "| `/history` | Last 10 interactions |\n" "| `/clear` | Clear conversation history |\n" "| `/test` | JEE mock test (20 q, +4/-1) |\n" "| `/formulas` | Full formula sheet |\n" "| `/formulas p` | Physics formulas |\n" "| `/formulas c` | Chemistry formulas |\n" "| `/formulas m` | Math formulas |\n" "| `/hint` | Conceptual hint for last question |\n" "| `/hint2` | Hint + relevant formula |\n" "| `/hint3` | Hint + formula + first step |\n" "| `/review` | Spaced repetition review (SM-2) |\n" "| `/progress` | Topic-level accuracy summary |\n" "| `/benchmark` | Full JEE readiness report |\n" ) elif cmd == "/history": reply = f"**Conversation History**\n\n```\n{format_history()}\n```" elif cmd == "/clear": clear_history() _last_question[0] = "" reply = "Conversation history cleared." elif cmd == "/test": reply = f"```\n{run_mock_test()}\n```" 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 reply = f"```\n{get_formula_sheet(subject)}\n```" elif cmd in ("/hint", "/hint1"): if _last_question[0]: reply = get_hints(_last_question[0], level=1) else: reply = "Ask a question first, then use /hint." elif cmd == "/hint2": if _last_question[0]: reply = get_hints(_last_question[0], level=2) else: reply = "Ask a question first, then use /hint2." elif cmd == "/hint3": if _last_question[0]: reply = get_hints(_last_question[0], level=3) else: reply = "Ask a question first, then use /hint3." elif cmd == "/review": reply = f"```\n{run_review_session()}\n```" elif cmd == "/progress": summary = get_progress_summary() if not summary: reply = "No progress data yet. Use `/test` or ask JEE questions." else: lines = ["**Progress Summary**\n"] for subj, data in sorted(summary.items()): a = data['attempted'] c = data['correct'] acc = data['accuracy'] lines.append(f"**{subj}** - {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 = "OK" if tacc >= 60 else "Needs work" lines.append(f" {marker} {topic}: {td['correct']}/{td['attempted']} ({tacc:.0f}%)") reply = "\n".join(lines) elif cmd == "/benchmark": summary = get_progress_summary() due_topics = get_due_topics() weak = get_weak_topics() reply = f"```\n{generate_report(summary, due_topics, weak)}\n```" else: reply = f"Unknown command `{user_input}`. Type `/help` for available commands." else: _last_question[0] = user_input label, response = compose_response( user_input, intent, knowledge_engine, conversation_engine, llm_engine, reasoning_eng, inference_engine, physics_engine_obj, chemistry_engine_obj, ) reply = f"**[{label}]**\n\n{response}" save_interaction(user_input, response) 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) history.append({"role": "user", "content": user_input}) history.append({"role": "assistant", "content": reply}) return history, history, "" QUICK_COMMANDS = [ ("Help", "/help"), ("Formula sheet", "/formulas"), ("Physics formulas", "/formulas p"), ("Hint", "/hint"), ("Review", "/review"), ("Progress", "/progress"), ("Benchmark", "/benchmark"), ] EXAMPLES = [ ["integrate x^2 sin(x)"], ["solve x^2 - 5x + 6 = 0"], ["A car accelerates at 3 m/s^2 for 5 s from rest. Find final velocity."], ["pH of 0.01 M HCl"], ["Molar mass of Ca(OH)2"], ["If it rains then the ground is wet. It is raining. Therefore?"], ["/formulas p"], ["/test"], ["/help"], ] with gr.Blocks( title="AnveshAI Edge v2", elem_id="anvesh-app", ) as demo: with gr.Row(elem_classes=["app-shell"]): with gr.Column(scale=1, elem_classes=["sidebar"]): gr.Markdown( """

AnveshAI Edge v2

JEE Advanced tutor for physics, chemistry, and mathematics.

""" ) new_chat_btn = gr.Button("New chat", variant="secondary") with gr.Group(elem_classes=["rail-card"]): gr.Markdown( "### Study tools\n" "`/help` `/test` `/formulas` `/hint` `/review` `/progress` `/benchmark`" ) command_buttons = [] with gr.Group(elem_classes=["rail-card"]): gr.Markdown("### Quick actions") with gr.Column(elem_classes=["quick-grid"]): for label, command in QUICK_COMMANDS: command_buttons.append((gr.Button(label, variant="secondary"), command)) with gr.Group(elem_classes=["rail-card"]): gr.Markdown( "### Engine\n" "Deterministic solvers first. LLM explanation second." ) with gr.Column(scale=4, elem_classes=["main-panel"]): with gr.Row(elem_classes=["topbar"]): with gr.Column(scale=4): gr.Markdown( "## Ask AnveshAI\n" "Fast, offline-first help for JEE reasoning and revision." ) with gr.Column(scale=1, elem_classes=["status-pill"]): gr.Markdown("Ready") with gr.Column(elem_classes=["chat-region"]): chatbot = gr.Chatbot( value=_initial_history(), height=560, show_label=False, elem_id="chatbot", ) with gr.Group(elem_classes=["composer"]): txt = gr.Textbox( placeholder="Message AnveshAI Edge...", show_label=False, lines=2, max_lines=6, container=False, ) with gr.Row(elem_classes=["send-row"]): gr.Markdown("Physics | Chemistry | Mathematics") send_btn = gr.Button("Send", variant="primary", scale=1, min_width=112) with gr.Column(elem_classes=["examples-wrap"]): gr.Examples( examples=EXAMPLES, inputs=txt, label="Starter prompts", ) state = gr.State(_initial_history()) send_btn.click(chat, [txt, state], [chatbot, state, txt]) txt.submit(chat, [txt, state], [chatbot, state, txt]) new_chat_btn.click(new_chat, None, [chatbot, state, txt]) for button, command in command_buttons: button.click(lambda history, c=command: chat(c, history), [state], [chatbot, state, txt]) if __name__ == "__main__": demo.launch(theme=gr.themes.Soft(), css=APP_CSS)