| | import os |
| | import gradio as gr |
| | from openai import OpenAI |
| |
|
| | |
| | |
| | |
| | OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") |
| |
|
| | if not OPENAI_API_KEY: |
| | raise ValueError( |
| | "OPENAI_API_KEY is not set. " |
| | "Add it in your Hugging Face Space: Settings → Variables and secrets → Secrets." |
| | ) |
| |
|
| | client = OpenAI(api_key=OPENAI_API_KEY) |
| |
|
| | SYSTEM_PROMPT = """Create an intelligent Python chatbot capable of engaging in natural, helpful, and contextually appropriate conversations with human users. |
| | |
| | Requirements: |
| | - Maintain conversational context over multiple user turns. |
| | - Respond helpfully and accurately to a wide range of user inputs. |
| | - Reason about user intent before generating each response. |
| | - Politely ask clarifying questions if a request is ambiguous or unclear. |
| | - Avoid hallucination or speculation—respond only with information you can justify or infer from context. |
| | - If unable to answer, politely acknowledge the limitation. |
| | |
| | Process: |
| | 1. On each user message, first analyze prior context (if any) and what the user is likely asking/intending. |
| | 2. Think step-by-step (chain-of-thought) to determine the most relevant, helpful response. Always reason internally before presenting your answer. |
| | 3. If more information is needed, ask targeted clarifying questions. |
| | 4. Output your response, maintaining natural tone and conversational flow. |
| | 5. Continue the conversation until the user indicates they are finished. |
| | |
| | Output: |
| | - Each response should be in plain English, no markdown or code blocks unless explicitly requested. |
| | - Maintain a single-paragraph, natural-sounding chat response of 1–3 sentences (unless a longer reply is requested or required). |
| | |
| | Example—Instructions: |
| | - Reasoning: "Recognize the user asked for Python list examples and may want to know how lists work." |
| | - 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?" |
| | |
| | (For more advanced technical requests, reasoning steps and explanations may be slightly longer, but always conclude with a concise, clear reply to the user.) |
| | |
| | Edge Cases & Important Considerations: |
| | - If the user refers to prior conversation context, recall and incorporate it. |
| | - Be warm, engaging, and never condescending. |
| | - If asked for code, provide only what is needed and explain concisely. |
| | |
| | REMINDER: Your primary objective is to serve as a helpful Python chatbot, reasoning about context before each response, and outputting clear, appropriate conversational replies. |
| | """ |
| |
|
| | |
| | |
| | |
| | def init_messages(): |
| | return [ |
| | { |
| | "role": "system", |
| | "content": [{"type": "input_text", "text": SYSTEM_PROMPT}] |
| | } |
| | ] |
| |
|
| | |
| | |
| | |
| | |
| | def append_ui_history(chat_history, user_text, assistant_text): |
| | if chat_history is None: |
| | chat_history = [] |
| | chat_history = chat_history + [ |
| | {"role": "user", "content": user_text}, |
| | {"role": "assistant", "content": assistant_text}, |
| | ] |
| | return chat_history |
| |
|
| | def respond(user_text, chat_history, messages): |
| | if messages is None: |
| | messages = init_messages() |
| |
|
| | |
| | messages.append( |
| | { |
| | "role": "user", |
| | "content": [{"type": "input_text", "text": user_text}] |
| | } |
| | ) |
| |
|
| | |
| | response = client.responses.create( |
| | model="gpt-5-chat-latest", |
| | input=messages, |
| | text={"format": {"type": "text"}}, |
| | reasoning={}, |
| | tools=[], |
| | temperature=1, |
| | max_output_tokens=2048, |
| | top_p=1, |
| | store=True |
| | ) |
| |
|
| | assistant_text = response.output_text |
| |
|
| | |
| | messages.append( |
| | { |
| | "role": "assistant", |
| | "content": [{"type": "output_text", "text": assistant_text}] |
| | } |
| | ) |
| |
|
| | |
| | chat_history = append_ui_history(chat_history, user_text, assistant_text) |
| |
|
| | return "", chat_history, messages |
| |
|
| |
|
| | |
| | |
| | |
| | FAQ_QUESTIONS = [ |
| | "What is the difference between a list, tuple, and set in Python?", |
| | "How do I use dictionaries effectively in Python?", |
| | "What are Python functions and how do *args and **kwargs work?", |
| | "How does OOP work in Python (classes, objects, inheritance)?", |
| | "How do I handle errors using try/except?", |
| | "What are list comprehensions and when should I use them?", |
| | "How do I read and write files in Python?" |
| | ] |
| |
|
| | def set_question(q): |
| | return q |
| |
|
| | def clear_all(): |
| | return [], init_messages(), "" |
| |
|
| | LOGO_URL = "https://raw.githubusercontent.com/Decoding-Data-Science/nov25/main/logo_python.png" |
| |
|
| | css = """ |
| | #app_container {max-width: 1200px; margin: 0 auto;} |
| | |
| | .header-wrap { |
| | display: flex; |
| | align-items: center; |
| | gap: 14px; |
| | padding: 10px 6px 2px 6px; |
| | } |
| | .header-title { |
| | font-size: 28px; |
| | font-weight: 700; |
| | line-height: 1.1; |
| | } |
| | .header-subtitle { |
| | font-size: 12.5px; |
| | opacity: 0.75; |
| | margin-top: 2px; |
| | } |
| | |
| | .faq-box { |
| | border: 1px solid rgba(255,255,255,0.08); |
| | border-radius: 12px; |
| | padding: 14px; |
| | } |
| | |
| | .faq-btn button { |
| | width: 100%; |
| | justify-content: flex-start; |
| | } |
| | """ |
| |
|
| | with gr.Blocks(elem_id="app_container") as demo: |
| | |
| | with gr.Row(): |
| | with gr.Column(scale=1, min_width=80): |
| | gr.Image( |
| | value=LOGO_URL, |
| | label=None, |
| | show_label=False, |
| | height=64, |
| | width=64, |
| | container=False |
| | ) |
| | with gr.Column(scale=10): |
| | gr.HTML( |
| | """ |
| | <div class="header-wrap"> |
| | <div> |
| | <div class="header-title">Python Tutor Bot</div> |
| | <div class="header-subtitle"> |
| | Ask anything about Python — concepts, debugging, best practices, and examples. |
| | </div> |
| | </div> |
| | </div> |
| | """ |
| | ) |
| |
|
| | gr.Markdown("---") |
| |
|
| | |
| | state = gr.State(init_messages()) |
| |
|
| | |
| | with gr.Row(equal_height=True): |
| | |
| | with gr.Column(scale=4, min_width=320): |
| | with gr.Group(elem_classes=["faq-box"]): |
| | gr.Markdown("### FAQ — Most Asked Python Questions") |
| | gr.Markdown("Click a question to auto-fill it, then press **Enter** or click **Send**.") |
| |
|
| | faq_buttons = [] |
| | for q in FAQ_QUESTIONS: |
| | b = gr.Button(q, elem_classes=["faq-btn"]) |
| | faq_buttons.append(b) |
| |
|
| | gr.Markdown("### Quick prompt ideas") |
| | quick = gr.Radio( |
| | choices=[ |
| | "Explain with a simple example", |
| | "Give me a beginner-friendly analogy", |
| | "Show common mistakes to avoid", |
| | "Provide a short quiz question", |
| | "Compare two approaches briefly" |
| | ], |
| | label="Add a style preference (optional)", |
| | value=None |
| | ) |
| |
|
| | |
| | with gr.Column(scale=8, min_width=520): |
| | chatbot = gr.Chatbot( |
| | height=520, |
| | label="Conversation" |
| | |
| | |
| | ) |
| |
|
| | with gr.Row(): |
| | msg = gr.Textbox( |
| | placeholder="Type your Python question here…", |
| | label=None, |
| | scale=9 |
| | ) |
| | send = gr.Button("Send", variant="primary", scale=1) |
| |
|
| | with gr.Row(): |
| | clear = gr.Button("Clear Chat") |
| | gr.Markdown( |
| | "<span style='opacity:0.7;font-size:12px;'>Context is preserved across turns unless you clear.</span>" |
| | ) |
| |
|
| | |
| | for b, q in zip(faq_buttons, FAQ_QUESTIONS): |
| | b.click(fn=lambda q=q: set_question(q), inputs=None, outputs=msg) |
| |
|
| | |
| | def apply_quick_pref(pref, current_text): |
| | if not pref: |
| | return current_text |
| | if current_text and current_text.strip(): |
| | return f"{current_text.strip()} ({pref})" |
| | return pref |
| |
|
| | quick.change(fn=apply_quick_pref, inputs=[quick, msg], outputs=msg) |
| |
|
| | |
| | msg.submit(respond, inputs=[msg, chatbot, state], outputs=[msg, chatbot, state]) |
| | send.click(respond, inputs=[msg, chatbot, state], outputs=[msg, chatbot, state]) |
| |
|
| | |
| | clear.click(fn=clear_all, inputs=None, outputs=[chatbot, state, msg]) |
| |
|
| | demo.launch( |
| | debug=False, |
| | theme=gr.themes.Soft(), |
| | css=css |
| | ) |
| |
|