decodingdatascience commited on
Commit
07a2209
·
verified ·
1 Parent(s): 6913e97

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +294 -0
app.py ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+
3
+ # ADD THIS IN APP.PY
4
+
5
+
6
+
7
+
8
+ import os
9
+ import gradio as gr
10
+ from openai import OpenAI
11
+
12
+ # -----------------------------
13
+ # Load OpenAI key from HF Secrets
14
+ # -----------------------------
15
+ # In Hugging Face Spaces, Secrets/Variables are available as environment variables.
16
+ # Add this in your Space Settings -> Variables and secrets -> Secrets
17
+ # Key name: OPENAI_API_KEY
18
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
19
+
20
+
21
+
22
+ client = OpenAI(api_key=OPENAI_API_KEY)
23
+
24
+ SYSTEM_PROMPT = """Create an intelligent Python chatbot capable of engaging in natural, helpful, and contextually appropriate conversations with human users.
25
+
26
+ Requirements:
27
+ - Maintain conversational context over multiple user turns.
28
+ - Respond helpfully and accurately to a wide range of user inputs.
29
+ - Reason about user intent before generating each response.
30
+ - Politely ask clarifying questions if a request is ambiguous or unclear.
31
+ - Avoid hallucination or speculation—respond only with information you can justify or infer from context.
32
+ - If unable to answer, politely acknowledge the limitation.
33
+
34
+ Process:
35
+ 1. On each user message, first analyze prior context (if any) and what the user is likely asking/intending.
36
+ 2. Think step-by-step (chain-of-thought) to determine the most relevant, helpful response. Always reason internally before presenting your answer.
37
+ 3. If more information is needed, ask targeted clarifying questions.
38
+ 4. Output your response, maintaining natural tone and conversational flow.
39
+ 5. Continue the conversation until the user indicates they are finished.
40
+
41
+ Output:
42
+ - Each response should be in plain English, no markdown or code blocks unless explicitly requested.
43
+ - Maintain a single-paragraph, natural-sounding chat response of 1–3 sentences (unless a longer reply is requested or required).
44
+
45
+ Example—Instructions:
46
+ - Reasoning: "Recognize the user asked for Python list examples and may want to know how lists work."
47
+ - Conclusion/Output: "Sure! In Python, a list is a collection of items in a particular order. For example: my_list = [1, 2, 3, 4]. Would you like to see how to add or remove items?"
48
+
49
+ (For more advanced technical requests, reasoning steps and explanations may be slightly longer, but always conclude with a concise, clear reply to the user.)
50
+
51
+ Edge Cases & Important Considerations:
52
+ - If the user refers to prior conversation context, recall and incorporate it.
53
+ - Be warm, engaging, and never condescending.
54
+ - If asked for code, provide only what is needed and explain concisely.
55
+
56
+ REMINDER: Your primary objective is to serve as a helpful Python chatbot, reasoning about context before each response, and outputting clear, appropriate conversational replies.
57
+ """
58
+
59
+ def init_messages():
60
+ return [
61
+ {
62
+ "role": "system",
63
+ "content": [{"type": "input_text", "text": SYSTEM_PROMPT}]
64
+ }
65
+ ]
66
+
67
+ def respond(user_text, chat_history, messages):
68
+ if messages is None:
69
+ messages = init_messages()
70
+
71
+ # add user turn
72
+ messages.append(
73
+ {
74
+ "role": "user",
75
+ "content": [{"type": "input_text", "text": user_text}]
76
+ }
77
+ )
78
+
79
+ # call API
80
+ response = client.responses.create(
81
+ model="gpt-5-chat-latest",
82
+ input=messages,
83
+ text={"format": {"type": "text"}},
84
+ reasoning={},
85
+ tools=[],
86
+ temperature=1,
87
+ max_output_tokens=2048,
88
+ top_p=1,
89
+ store=True
90
+ )
91
+
92
+ assistant_text = response.output_text
93
+
94
+ # add assistant turn
95
+ messages.append(
96
+ {
97
+ "role": "assistant",
98
+ "content": [{"type": "output_text", "text": assistant_text}]
99
+ }
100
+ )
101
+
102
+ # update UI chat history
103
+ chat_history = chat_history + [(user_text, assistant_text)]
104
+
105
+ return "", chat_history, messages
106
+
107
+
108
+ # -----------------------------
109
+ # Gradio UI (Upgraded)
110
+ # -----------------------------
111
+
112
+ FAQ_QUESTIONS = [
113
+ "What is the difference between a list, tuple, and set in Python?",
114
+ "How do I use dictionaries effectively in Python?",
115
+ "What are Python functions and how do *args and **kwargs work?",
116
+ "How does OOP work in Python (classes, objects, inheritance)?",
117
+ "How do I handle errors using try/except?",
118
+ "What are list comprehensions and when should I use them?",
119
+ "How do I read and write files in Python?"
120
+ ]
121
+
122
+ def set_question(q):
123
+ return q
124
+
125
+ def clear_all():
126
+ return [], init_messages(), ""
127
+
128
+ # Updated logo URL (RAW link)
129
+ LOGO_URL = "https://raw.githubusercontent.com/Decoding-Data-Science/nov25/main/logo_python.png"
130
+
131
+ css = """
132
+ /* Overall spacing + subtle polish */
133
+ #app_container {max-width: 1200px; margin: 0 auto;}
134
+
135
+ /* Header styling */
136
+ .header-wrap {
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 14px;
140
+ padding: 10px 6px 2px 6px;
141
+ }
142
+ .header-title {
143
+ font-size: 28px;
144
+ font-weight: 700;
145
+ line-height: 1.1;
146
+ }
147
+ .header-subtitle {
148
+ font-size: 12.5px;
149
+ opacity: 0.75;
150
+ margin-top: 2px;
151
+ }
152
+
153
+ /* FAQ card-like feel */
154
+ .faq-box {
155
+ border: 1px solid rgba(255,255,255,0.08);
156
+ border-radius: 12px;
157
+ padding: 14px;
158
+ }
159
+
160
+ /* Make buttons full width in FAQ column */
161
+ .faq-btn button {
162
+ width: 100%;
163
+ justify-content: flex-start;
164
+ }
165
+ """
166
+
167
+ with gr.Blocks(css=css, theme=gr.themes.Soft(), elem_id="app_container") as demo:
168
+ # Header Row
169
+ with gr.Row():
170
+ with gr.Column(scale=1, min_width=80):
171
+ logo = gr.Image(
172
+ value=LOGO_URL,
173
+ label=None,
174
+ show_label=False,
175
+ show_download_button=False,
176
+ show_share_button=False,
177
+ height=64,
178
+ width=64,
179
+ container=False
180
+ )
181
+ with gr.Column(scale=10):
182
+ gr.HTML(
183
+ """
184
+ <div class="header-wrap">
185
+ <div>
186
+ <div class="header-title">Python Tutor Bot</div>
187
+ <div class="header-subtitle">
188
+ Ask anything about Python — concepts, debugging, best practices, and examples.
189
+ </div>
190
+ </div>
191
+ </div>
192
+ """
193
+ )
194
+
195
+ gr.Markdown("---")
196
+
197
+ # State (unchanged logic)
198
+ state = gr.State(init_messages())
199
+
200
+ # Two-column layout
201
+ with gr.Row(equal_height=True):
202
+ # LEFT: FAQ + Quick Ask
203
+ with gr.Column(scale=4, min_width=320):
204
+ with gr.Group(elem_classes=["faq-box"]):
205
+ gr.Markdown("### FAQ — Most Asked Python Questions")
206
+
207
+ gr.Markdown(
208
+ "Click a question to auto-fill it, then press **Enter** or click **Send**."
209
+ )
210
+
211
+ # Buttons for FAQ
212
+ faq_buttons = []
213
+ for q in FAQ_QUESTIONS:
214
+ b = gr.Button(q, elem_classes=["faq-btn"])
215
+ faq_buttons.append(b)
216
+
217
+ gr.Markdown("### Quick prompt ideas")
218
+ quick = gr.Radio(
219
+ choices=[
220
+ "Explain with a simple example",
221
+ "Give me a beginner-friendly analogy",
222
+ "Show common mistakes to avoid",
223
+ "Provide a short quiz question",
224
+ "Compare two approaches briefly"
225
+ ],
226
+ label="Add a style preference (optional)",
227
+ value=None
228
+ )
229
+
230
+ # RIGHT: Chat area
231
+ with gr.Column(scale=8, min_width=520):
232
+ chatbot = gr.Chatbot(
233
+ height=520,
234
+ bubble_full_width=False,
235
+ label="Conversation"
236
+ )
237
+
238
+ with gr.Row():
239
+ msg = gr.Textbox(
240
+ placeholder="Type your Python question here…",
241
+ label=None,
242
+ scale=9
243
+ )
244
+ send = gr.Button("Send", variant="primary", scale=1)
245
+
246
+ with gr.Row():
247
+ clear = gr.Button("Clear Chat")
248
+ gr.Markdown(
249
+ "<span style='opacity:0.7;font-size:12px;'>Context is preserved across turns unless you clear.</span>"
250
+ )
251
+
252
+ # FAQ button -> fill textbox
253
+ for b, q in zip(faq_buttons, FAQ_QUESTIONS):
254
+ b.click(
255
+ fn=lambda q=q: set_question(q),
256
+ inputs=None,
257
+ outputs=msg
258
+ )
259
+
260
+ # Optional quick preference: append hint to textbox (UI-only)
261
+ def apply_quick_pref(pref, current_text):
262
+ if not pref:
263
+ return current_text
264
+ if current_text and current_text.strip():
265
+ return f"{current_text.strip()} ({pref})"
266
+ return pref
267
+
268
+ quick.change(
269
+ fn=apply_quick_pref,
270
+ inputs=[quick, msg],
271
+ outputs=msg
272
+ )
273
+
274
+ # Submit logic (unchanged)
275
+ msg.submit(
276
+ respond,
277
+ inputs=[msg, chatbot, state],
278
+ outputs=[msg, chatbot, state]
279
+ )
280
+
281
+ send.click(
282
+ respond,
283
+ inputs=[msg, chatbot, state],
284
+ outputs=[msg, chatbot, state]
285
+ )
286
+
287
+ # Clear
288
+ clear.click(
289
+ fn=clear_all,
290
+ inputs=None,
291
+ outputs=[chatbot, state, msg]
292
+ )
293
+
294
+ demo.launch(debug=False)