From 1b9276e0b7ccd2dbe4841ae13999b57e4b05e756 Mon Sep 17 00:00:00 2001 From: Md Arshad Ahmad <70535034+arshad831@users.noreply.github.com> Date: Sat, 9 May 2026 23:40:33 +0400 Subject: [PATCH] Add env template and ignore local secret file --- .env.example | 1 + .gitignore | 2 + README.md | 69 ++++++++++++++++++++++++++- app.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++ prompts.py | 25 ++++++++++ requirements.txt | 3 ++ 6 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 app.py create mode 100644 prompts.py create mode 100644 requirements.txt diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3cac181 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +OPENAI_API_KEY=your_openai_api_key_here diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d50a09f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +__pycache__/ diff --git a/README.md b/README.md index 6b6cd15..e095511 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,67 @@ -# pythontutorbot -pythontutorbot +# Advanced Python Tutor Bot + +Advanced Python Tutor Bot is a small educational AI app for beginner Python learners. + +It helps with: +- Explaining Python concepts +- Debugging Python code +- Running interactive quizzes +- Improving code quality +- Beginner-friendly data structures and algorithms (DSA) guidance + +## Tech Stack +- Python +- Gradio +- OpenAI Python SDK +- python-dotenv + +## Features +- Professional Gradio layout with theme and clear mode descriptions +- Chat interface with persistent in-session chat history +- Four tutor modes: + - Explain Concept + - Debug Code + - Quiz Me + - Improve Code +- Reusable prompt templates +- Input validation and API key error handling + +## Project Files +- `app.py` → Gradio chat UI and OpenAI integration +- `prompts.py` → reusable prompt templates and mode descriptions +- `requirements.txt` → dependencies +- `.env` → your local API key file (you create this) + +## Setup +1. Create and activate a virtual environment: + +```bash +python -m venv .venv +source .venv/bin/activate +``` + +2. Install dependencies: + +```bash +pip install -r requirements.txt +``` + +3. Create a `.env` file in the project folder and add your API key: + +```env +OPENAI_API_KEY=your_api_key_here +``` + +4. Run the app: + +```bash +python app.py +``` + +5. Open the local Gradio URL shown in your terminal. + +## Usage +1. Choose a tutor mode from the dropdown. +2. Chat naturally in the message box. +3. Continue the conversation to use chat history context. +4. Use **Clear Chat** to start a new session. diff --git a/app.py b/app.py new file mode 100644 index 0000000..9240600 --- /dev/null +++ b/app.py @@ -0,0 +1,119 @@ +import os +from pathlib import Path +from typing import Any + +import gradio as gr +from dotenv import load_dotenv +from openai import OpenAI + +from prompts import MODE_DESCRIPTIONS, PROMPT_TEMPLATES + +MODEL_NAME = "gpt-4.1-mini" + +# Load environment variables from a local .env file if it exists. +load_dotenv(dotenv_path=Path(__file__).parent / ".env") + +SYSTEM_PROMPT = ( + "You are Advanced Python Tutor Bot, an expert and patient teacher for beginners. " + "Help with Python fundamentals, debugging, quizzes, and code quality. " + "When relevant, include beginner-friendly data structures and algorithms guidance " + "(lists, dicts, sets, stacks, queues, recursion, sorting, searching, Big-O at a simple level)." +) + + +def _history_to_messages(history: list[Any]) -> list[dict[str, str]]: + """Convert Gradio chat history into OpenAI message objects. + + Supports both older history formats (list of [user, assistant]) and + newer message formats (list of dicts). + """ + messages: list[dict[str, str]] = [] + + for item in history: + if isinstance(item, dict): + role = item.get("role") + content = item.get("content", "") + if role in {"user", "assistant"} and content: + messages.append({"role": role, "content": str(content)}) + continue + + if isinstance(item, (list, tuple)) and len(item) == 2: + user_text, assistant_text = item + if user_text: + messages.append({"role": "user", "content": str(user_text)}) + if assistant_text: + messages.append({"role": "assistant", "content": str(assistant_text)}) + + return messages + + +def build_messages(mode: str, user_input: str, history: list[Any]) -> list[dict[str, str]]: + """Build OpenAI messages using mode template + ongoing chat history.""" + messages = [{"role": "system", "content": SYSTEM_PROMPT}] + messages.extend(_history_to_messages(history)) + + mode_instruction = PROMPT_TEMPLATES[mode].format(user_input=user_input) + messages.append({"role": "user", "content": mode_instruction}) + return messages + + +def get_tutor_response(user_input: str, history: list[Any], mode: str) -> str: + """Generate a response from OpenAI for the selected tutoring mode.""" + if not user_input or not user_input.strip(): + return "Please enter some text or code so I can help you." + + api_key = os.getenv("OPENAI_API_KEY") + if not api_key: + return ( + "Missing OPENAI_API_KEY. Add it to a `.env` file in this folder like: " + "OPENAI_API_KEY=your_key_here" + ) + + try: + client = OpenAI(api_key=api_key) + messages = build_messages(mode, user_input.strip(), history or []) + response = client.chat.completions.create( + model=MODEL_NAME, + messages=messages, + temperature=0.4, + ) + return response.choices[0].message.content or "I could not generate a response. Please try again." + except Exception as error: + return f"Something went wrong while contacting the AI service: {error}" + + +def build_app() -> gr.Blocks: + theme = gr.themes.Soft(primary_hue="blue", secondary_hue="slate", neutral_hue="slate") + + with gr.Blocks(title="Advanced Python Tutor Bot", theme=theme) as app: + gr.Markdown( + """ + # 🧠 Advanced Python Tutor Bot + A professional learning assistant for beginners: explanations, debugging, quizzes, and code improvements. + """ + ) + + with gr.Row(): + with gr.Column(scale=2): + mode = gr.Dropdown( + choices=["Explain Concept", "Debug Code", "Quiz Me", "Improve Code"], + value="Explain Concept", + label="Tutor Mode", + info="Pick how you want the tutor to help in this chat.", + ) + with gr.Column(scale=3): + mode_note = gr.Markdown(MODE_DESCRIPTIONS["Explain Concept"]) + + mode.change(lambda m: MODE_DESCRIPTIONS[m], inputs=mode, outputs=mode_note) + + gr.ChatInterface( + fn=get_tutor_response, + additional_inputs=[mode], + ) + + + return app + + +if __name__ == "__main__": + build_app().launch() diff --git a/prompts.py b/prompts.py new file mode 100644 index 0000000..7dee3e8 --- /dev/null +++ b/prompts.py @@ -0,0 +1,25 @@ +PROMPT_TEMPLATES = { + "Explain Concept": ( + "Explain this Python concept for a beginner using simple words, a short example, " + "and a quick summary at the end:\n\n{user_input}" + ), + "Debug Code": ( + "Help debug this Python code. Identify likely errors, explain why they happen, and " + "show a corrected version. End with a short prevention checklist:\n\n{user_input}" + ), + "Quiz Me": ( + "Run a short interactive Python quiz for a beginner based on this topic. " + "Ask one question at a time, wait for the learner's answer, then give feedback and continue:\n\n{user_input}" + ), + "Improve Code": ( + "Improve this Python code for readability, style, and beginner-friendly best practices. " + "Explain each improvement clearly and show before/after snippets when possible:\n\n{user_input}" + ), +} + +MODE_DESCRIPTIONS = { + "Explain Concept": "**Explain Concept**: Learn a topic with plain-English explanations and examples.", + "Debug Code": "**Debug Code**: Find errors, understand causes, and get corrected code.", + "Quiz Me": "**Quiz Me**: Practice with a guided one-question-at-a-time mini quiz.", + "Improve Code": "**Improve Code**: Refactor code for readability, structure, and best practices.", +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..ec3de13 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +gradio +openai +python-dotenv