decodingdatascience commited on
Commit
0119c00
·
verified ·
1 Parent(s): dcec93d

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +119 -0
  2. prompts.py +25 -0
  3. requirements.txt +2 -0
app.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Any
4
+
5
+ import gradio as gr
6
+ from dotenv import load_dotenv
7
+ from openai import OpenAI
8
+
9
+ from prompts import MODE_DESCRIPTIONS, PROMPT_TEMPLATES
10
+
11
+ MODEL_NAME = "gpt-4.1-mini"
12
+
13
+ # Load environment variables from a local .env file if it exists.
14
+ load_dotenv(dotenv_path=Path(__file__).parent / ".env")
15
+
16
+ SYSTEM_PROMPT = (
17
+ "You are Advanced Python Tutor Bot, an expert and patient teacher for beginners. "
18
+ "Help with Python fundamentals, debugging, quizzes, and code quality. "
19
+ "When relevant, include beginner-friendly data structures and algorithms guidance "
20
+ "(lists, dicts, sets, stacks, queues, recursion, sorting, searching, Big-O at a simple level)."
21
+ )
22
+
23
+
24
+ def _history_to_messages(history: list[Any]) -> list[dict[str, str]]:
25
+ """Convert Gradio chat history into OpenAI message objects.
26
+
27
+ Supports both older history formats (list of [user, assistant]) and
28
+ newer message formats (list of dicts).
29
+ """
30
+ messages: list[dict[str, str]] = []
31
+
32
+ for item in history:
33
+ if isinstance(item, dict):
34
+ role = item.get("role")
35
+ content = item.get("content", "")
36
+ if role in {"user", "assistant"} and content:
37
+ messages.append({"role": role, "content": str(content)})
38
+ continue
39
+
40
+ if isinstance(item, (list, tuple)) and len(item) == 2:
41
+ user_text, assistant_text = item
42
+ if user_text:
43
+ messages.append({"role": "user", "content": str(user_text)})
44
+ if assistant_text:
45
+ messages.append({"role": "assistant", "content": str(assistant_text)})
46
+
47
+ return messages
48
+
49
+
50
+ def build_messages(mode: str, user_input: str, history: list[Any]) -> list[dict[str, str]]:
51
+ """Build OpenAI messages using mode template + ongoing chat history."""
52
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
53
+ messages.extend(_history_to_messages(history))
54
+
55
+ mode_instruction = PROMPT_TEMPLATES[mode].format(user_input=user_input)
56
+ messages.append({"role": "user", "content": mode_instruction})
57
+ return messages
58
+
59
+
60
+ def get_tutor_response(user_input: str, history: list[Any], mode: str) -> str:
61
+ """Generate a response from OpenAI for the selected tutoring mode."""
62
+ if not user_input or not user_input.strip():
63
+ return "Please enter some text or code so I can help you."
64
+
65
+ api_key = os.getenv("OPENAI_API_KEY")
66
+ if not api_key:
67
+ return (
68
+ "Missing OPENAI_API_KEY. Add it to a `.env` file in this folder like: "
69
+ "OPENAI_API_KEY=your_key_here"
70
+ )
71
+
72
+ try:
73
+ client = OpenAI(api_key=api_key)
74
+ messages = build_messages(mode, user_input.strip(), history or [])
75
+ response = client.chat.completions.create(
76
+ model=MODEL_NAME,
77
+ messages=messages,
78
+ temperature=0.4,
79
+ )
80
+ return response.choices[0].message.content or "I could not generate a response. Please try again."
81
+ except Exception as error:
82
+ return f"Something went wrong while contacting the AI service: {error}"
83
+
84
+
85
+ def build_app() -> gr.Blocks:
86
+ theme = gr.themes.Soft(primary_hue="blue", secondary_hue="slate", neutral_hue="slate")
87
+
88
+ with gr.Blocks(title="Advanced Python Tutor Bot", theme=theme) as app:
89
+ gr.Markdown(
90
+ """
91
+ # 🧠 Advanced Python Tutor Bot
92
+ A professional learning assistant for beginners: explanations, debugging, quizzes, and code improvements.
93
+ """
94
+ )
95
+
96
+ with gr.Row():
97
+ with gr.Column(scale=2):
98
+ mode = gr.Dropdown(
99
+ choices=["Explain Concept", "Debug Code", "Quiz Me", "Improve Code"],
100
+ value="Explain Concept",
101
+ label="Tutor Mode",
102
+ info="Pick how you want the tutor to help in this chat.",
103
+ )
104
+ with gr.Column(scale=3):
105
+ mode_note = gr.Markdown(MODE_DESCRIPTIONS["Explain Concept"])
106
+
107
+ mode.change(lambda m: MODE_DESCRIPTIONS[m], inputs=mode, outputs=mode_note)
108
+
109
+ gr.ChatInterface(
110
+ fn=get_tutor_response,
111
+ additional_inputs=[mode],
112
+ )
113
+
114
+
115
+ return app
116
+
117
+
118
+ if __name__ == "__main__":
119
+ build_app().launch()
prompts.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ PROMPT_TEMPLATES = {
2
+ "Explain Concept": (
3
+ "Explain this Python concept for a beginner using simple words, a short example, "
4
+ "and a quick summary at the end:\n\n{user_input}"
5
+ ),
6
+ "Debug Code": (
7
+ "Help debug this Python code. Identify likely errors, explain why they happen, and "
8
+ "show a corrected version. End with a short prevention checklist:\n\n{user_input}"
9
+ ),
10
+ "Quiz Me": (
11
+ "Run a short interactive Python quiz for a beginner based on this topic. "
12
+ "Ask one question at a time, wait for the learner's answer, then give feedback and continue:\n\n{user_input}"
13
+ ),
14
+ "Improve Code": (
15
+ "Improve this Python code for readability, style, and beginner-friendly best practices. "
16
+ "Explain each improvement clearly and show before/after snippets when possible:\n\n{user_input}"
17
+ ),
18
+ }
19
+
20
+ MODE_DESCRIPTIONS = {
21
+ "Explain Concept": "**Explain Concept**: Learn a topic with plain-English explanations and examples.",
22
+ "Debug Code": "**Debug Code**: Find errors, understand causes, and get corrected code.",
23
+ "Quiz Me": "**Quiz Me**: Practice with a guided one-question-at-a-time mini quiz.",
24
+ "Improve Code": "**Improve Code**: Refactor code for readability, structure, and best practices.",
25
+ }
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ gradio
2
+ openai