Files changed (1) hide show
  1. app.py +282 -135
app.py CHANGED
@@ -1,173 +1,320 @@
1
  import gradio as gr
2
  import os
3
  import requests
4
- import time
5
  from typing import List, Tuple
 
6
 
7
- # --- GROQ API Setup ---
8
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
9
  GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
10
 
 
11
  MODELS = {
12
- "Llama 3.2 (3B) - Fast & Free": "llama-3.2-3b-preview",
13
- "Mixtral (8x7B) - Balanced": "mixtral-8x7b-32768"
 
 
 
 
14
  }
15
 
16
- SYSTEM_PROMPT = """You are CodeMentor, a helpful programming tutor.
17
- Keep responses concise (3-5 sentences max) with clear code examples.
18
- Format code in markdown code blocks."""
19
 
20
- # --- GROQ API Class ---
21
- class GroqAPI:
22
- def __init__(self):
23
- self.api_key = GROQ_API_KEY
24
- self.timeout = 15
25
- self.max_retries = 2
 
 
26
 
27
- def query(self, message: str, chat_history: List[Tuple[str, str]], model: str, temperature: float, max_tokens: int) -> str:
28
- if not self.api_key:
29
- return "❌ **API Key Missing**: Please set GROQ_API_KEY in Hugging Face Secrets"
30
-
31
- headers = {
32
- "Authorization": f"Bearer {self.api_key}",
33
- "Content-Type": "application/json"
34
- }
35
-
36
- messages = [{"role": "system", "content": SYSTEM_PROMPT}]
37
- for user_msg, bot_msg in chat_history[-3:]:
38
- messages.append({"role": "user", "content": user_msg})
39
- messages.append({"role": "assistant", "content": bot_msg})
40
- messages.append({"role": "user", "content": message})
41
-
42
- actual_model = MODELS.get(model, "llama-3.2-3b-preview")
43
- payload = {
44
- "model": actual_model,
45
- "messages": messages,
46
- "temperature": min(temperature, 0.7),
47
- "max_tokens": min(max_tokens, 300),
48
- "top_p": 0.9,
49
- "stream": False
50
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
- for attempt in range(self.max_retries):
53
- try:
54
- response = requests.post(GROQ_API_URL, headers=headers, json=payload, timeout=self.timeout)
55
- if response.status_code == 200:
56
- data = response.json()
57
- return data["choices"][0]["message"]["content"]
58
- elif response.status_code == 429:
59
- wait_time = (attempt + 1) * 5
60
- if attempt < self.max_retries - 1:
61
- time.sleep(wait_time)
62
- continue
63
- return "⏰ **Rate Limited**: Free tier limits requests. Try again in 1 minute."
64
- elif response.status_code == 404:
65
- return f"❌ **Model Not Found**: '{actual_model}' not available."
66
- else:
67
- return f"❌ **Error {response.status_code}**: {response.text[:100]}"
68
- except requests.exceptions.Timeout:
69
- return "⏱️ **Timeout**: Request took too long."
70
- except requests.exceptions.RequestException as e:
71
- return f"🌐 **Connection Error**: {str(e)}"
72
- except Exception as e:
73
- return f"⚠️ **Unexpected Error**: {str(e)}"
74
- return "❌ **Failed after retries**."
75
 
76
- # --- Initialize API ---
77
- groq_api = GroqAPI()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
 
79
- # --- Functions ---
80
  def clear_chat():
 
81
  return [], []
82
 
83
- def create_example_buttons():
84
- return [
85
- ("📝 List Comprehensions", "Explain Python list comprehensions with 2 examples"),
86
- ("🐛 Debug Help", "How to fix 'IndexError: list index out of range'?"),
87
- ("🔤 String Format", "Show 3 ways to format strings in Python"),
88
- ("📊 Data Types", "What are Python's main data types?")
89
- ]
90
-
91
- def process_message(message, history, model, temp, tokens):
92
- if not message:
93
- return "", history
94
- history.append((message, " Thinking..."))
95
- response = groq_api.query(message, history[:-1], model, temp, tokens)
96
- history[-1] = (message, response)
97
- return "", history
98
-
99
- def test_connection():
100
- test_msg = "Say 'Hello' if you're working."
101
- test_response = groq_api.query(test_msg, [], "Llama 3.2 (3B) - Fast & Free", 0.7, 100)
102
- if "Hello" in test_response or "hello" in test_response:
103
- return " Connection successful! API is working."
104
- else:
105
- return f"⚠️ Test response: {test_response[:100]}..."
 
 
 
 
 
 
 
 
106
 
107
- # --- Gradio Interface ---
108
- with gr.Blocks(theme=gr.themes.Soft(), title="CodeMentor - Fast Programming Tutor") as demo:
 
 
109
  chat_state = gr.State([])
110
-
111
  gr.Markdown("""
112
- # 👨‍💻 CodeMentor - Fast Programming Help
113
- ⚡ Optimized for GROQ Free Tier - Faster responses, timeouts handled
114
- ⚠️ Free API has rate limits (1-2 requests/minute). If stuck, wait 60 seconds.
 
 
 
 
 
 
 
 
115
  """)
116
-
117
  with gr.Row():
118
  with gr.Column(scale=1):
119
- gr.Markdown("### ⚙️ Quick Settings")
 
 
 
120
  model_dropdown = gr.Dropdown(
121
  choices=list(MODELS.keys()),
122
- value="Llama 3.2 (3B) - Fast & Free",
123
- label="Model"
 
124
  )
125
-
126
- gr.Markdown("### 🎯 Quick Questions")
127
 
128
- # Example buttons
129
- examples = create_example_buttons()
130
- example_buttons = []
131
- for btn_text, question in examples:
132
- btn = gr.Button(btn_text, size="sm", variant="secondary")
133
- btn.click(fn=lambda q=question: q, inputs=[], outputs=None) # we'll connect later
134
- example_buttons.append(btn)
135
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  with gr.Column(scale=2):
137
- chatbot = gr.Chatbot(value=[], label="Chat", height=450)
138
- msg = gr.Textbox(placeholder="Ask about programming...", label="Your Question", lines=2)
139
-
140
- # Sliders
141
- temp_slider = gr.Slider(minimum=0.0, maximum=1.0, value=0.5, label="temp", visible=False)
142
- tokens_slider = gr.Slider(minimum=1, maximum=300, value=300, step=1, label="tokens", visible=False)
143
-
144
- # Send / Clear / Test buttons
145
- send_btn = gr.Button("🚀 Send", variant="primary")
146
- clear_btn = gr.Button("🗑️ Clear", size="sm")
147
- test_btn = gr.Button("🔍 Test Connection", size="sm", variant="primary")
148
- test_output = gr.Textbox(label="Connection Test", visible=False)
149
-
150
- # Connect example buttons to textbox
151
- for btn, (_, question) in zip(example_buttons, examples):
152
- btn.click(fn=lambda q=question: q, inputs=[], outputs=msg)
153
-
154
- # Connect send button and Enter key
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  msg.submit(
156
- fn=process_message,
157
- inputs=[msg, chat_state, model_dropdown, temp_slider, tokens_slider],
158
  outputs=[msg, chatbot]
159
  )
 
160
  send_btn.click(
161
- fn=process_message,
162
- inputs=[msg, chat_state, model_dropdown, temp_slider, tokens_slider],
163
  outputs=[msg, chatbot]
164
  )
165
-
166
- # Clear chat
167
- clear_btn.click(fn=clear_chat, inputs=[], outputs=[chatbot, chat_state])
168
-
169
- # Test connection
170
- test_btn.click(fn=test_connection, inputs=[], outputs=test_output)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  if __name__ == "__main__":
173
- demo.launch(debug=False, server_name="0.0.0.0", server_port=7860, share=True)
 
1
  import gradio as gr
2
  import os
3
  import requests
4
+ import json
5
  from typing import List, Tuple
6
+ import time
7
 
8
+ # Load GROQ API key from environment (set it in Hugging Face secrets)
9
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
10
  GROQ_API_URL = "https://api.groq.com/openai/v1/chat/completions"
11
 
12
+ # ✅ UPDATED: Correct GROQ model names (as of Dec 2024)
13
  MODELS = {
14
+ "Llama 3.2 (3B) - Fast": "llama-3.2-3b-preview",
15
+ "Llama 3.2 (1B) - Light": "llama-3.2-1b-preview",
16
+ "Llama 3.2 (90B Text) - Powerful": "llama-3.2-90b-text-preview",
17
+ "Llama 3.2 (11B Text) - Balanced": "llama-3.2-11b-text-preview",
18
+ "Mixtral (8x7B)": "mixtral-8x7b-32768",
19
+ "Gemma 2 (9B)": "gemma2-9b-it"
20
  }
21
 
22
+ # 🎯 Customize this system prompt based on your bot's role
23
+ SYSTEM_PROMPT = """You are CodeMentor, a friendly and knowledgeable programming tutor.
24
+ Your role is to help users learn programming concepts, debug code, and understand different programming languages.
25
 
26
+ Key personality traits:
27
+ 1. Patient and encouraging - never make users feel bad for not knowing something
28
+ 2. Explain concepts clearly with simple analogies first
29
+ 3. Provide practical code examples
30
+ 4. When debugging, guide users to discover the solution rather than just giving the answer
31
+ 5. Adapt explanations to the user's skill level
32
+ 6. Include best practices and common pitfalls
33
+ 7. Be enthusiastic about programming!
34
 
35
+ Always format code examples with proper syntax highlighting using markdown code blocks.
36
+ If a user asks about something non-programming related, gently steer the conversation back to programming topics."""
37
+
38
+ def query_groq_api(message: str, chat_history: List[Tuple[str, str]], model: str, temperature: float, max_tokens: int) -> str:
39
+ """Send request to GROQ API and get response"""
40
+
41
+ if not GROQ_API_KEY:
42
+ return "⚠️ API Key not configured. Please set GROQ_API_KEY in environment variables."
43
+
44
+ headers = {
45
+ "Authorization": f"Bearer {GROQ_API_KEY}",
46
+ "Content-Type": "application/json"
47
+ }
48
+
49
+ # Build messages list
50
+ messages = [{"role": "system", "content": SYSTEM_PROMPT}]
51
+
52
+ # Add chat history
53
+ for user_msg, bot_msg in chat_history:
54
+ messages.append({"role": "user", "content": user_msg})
55
+ messages.append({"role": "assistant", "content": bot_msg})
56
+
57
+ # Add current message
58
+ messages.append({"role": "user", "content": message})
59
+
60
+ # ✅ Get the actual model name from the dictionary
61
+ actual_model = MODELS.get(model, "llama-3.2-3b-preview")
62
+
63
+ # Prepare payload
64
+ payload = {
65
+ "model": actual_model,
66
+ "messages": messages,
67
+ "temperature": temperature,
68
+ "max_tokens": max_tokens,
69
+ "top_p": 0.9,
70
+ "stream": False
71
+ }
72
+
73
+ try:
74
+ response = requests.post(GROQ_API_URL, headers=headers, json=payload, timeout=30)
75
 
76
+ if response.status_code == 200:
77
+ data = response.json()
78
+ return data["choices"][0]["message"]["content"]
79
+ elif response.status_code == 404:
80
+ # Specific error for model not found
81
+ return f"❌ Error: Model '{actual_model}' not found. Available models are: {', '.join(MODELS.values())}"
82
+ else:
83
+ return f"❌ Error {response.status_code}: {response.text}"
84
+
85
+ except requests.exceptions.Timeout:
86
+ return "⏰ Request timeout. Please try again."
87
+ except requests.exceptions.RequestException as e:
88
+ return f"🚫 Connection error: {str(e)}"
89
+ except Exception as e:
90
+ return f"⚠️ Unexpected error: {str(e)}"
 
 
 
 
 
 
 
 
91
 
92
+ def respond(message: str, chat_history: List[Tuple[str, str]], model: str, temperature: float, max_tokens: int):
93
+ """Process user message and return bot response"""
94
+
95
+ if not message.strip():
96
+ return "", chat_history
97
+
98
+ # Show typing indicator
99
+ chat_history.append((message, "🤔 Thinking..."))
100
+ yield "", chat_history
101
+
102
+ # Get bot response
103
+ bot_reply = query_groq_api(message, chat_history[:-1], model, temperature, max_tokens)
104
+
105
+ # Replace typing indicator with actual response
106
+ chat_history[-1] = (message, bot_reply)
107
+
108
+ return "", chat_history
109
 
 
110
  def clear_chat():
111
+ """Clear chat history"""
112
  return [], []
113
 
114
+ def update_example_questions(programming_language: str):
115
+ """Update example questions based on selected programming language"""
116
+
117
+ examples = {
118
+ "Python": [
119
+ "Explain list comprehensions with examples",
120
+ "How do decorators work in Python?",
121
+ "What's the difference between 'is' and '=='?",
122
+ "Show me how to handle exceptions properly"
123
+ ],
124
+ "JavaScript": [
125
+ "Explain promises and async/await",
126
+ "What is the event loop?",
127
+ "How does 'this' keyword work?",
128
+ "Explain closure with an example"
129
+ ],
130
+ "Java": [
131
+ "Explain polymorphism with examples",
132
+ "Difference between abstract class and interface",
133
+ "How does garbage collection work?",
134
+ "What are Java Streams?"
135
+ ],
136
+ "General": [
137
+ "What's the difference between SQL and NoSQL?",
138
+ "Explain REST API principles",
139
+ "What are design patterns?",
140
+ "How does Git branching work?"
141
+ ]
142
+ }
143
+
144
+ return gr.update(choices=examples.get(programming_language, examples["General"]))
145
 
146
+ # Create Gradio interface
147
+ with gr.Blocks(theme=gr.themes.Soft(), title="CodeMentor - Programming Tutor") as demo:
148
+
149
+ # Store chat history in state
150
  chat_state = gr.State([])
151
+
152
  gr.Markdown("""
153
+ # 👨‍💻 CodeMentor - Your Personal Programming Tutor
154
+
155
+ Hi! I'm CodeMentor, your friendly AI programming assistant. I can help you with:
156
+ - Learning programming concepts
157
+ - Debugging code
158
+ - Understanding different languages
159
+ - Best practices and design patterns
160
+
161
+ Select your preferences below and start asking questions!
162
+
163
+ ⚠️ **Note**: Using GROQ API with free tier (limited requests per minute)
164
  """)
165
+
166
  with gr.Row():
167
  with gr.Column(scale=1):
168
+ # UI Improvements
169
+ gr.Markdown("### ⚙️ Settings")
170
+
171
+ # Model selection dropdown
172
  model_dropdown = gr.Dropdown(
173
  choices=list(MODELS.keys()),
174
+ value="Llama 3.2 (3B) - Fast",
175
+ label="Select AI Model",
176
+ info="✅ Updated with correct GROQ model names"
177
  )
 
 
178
 
179
+ # Programming language selection
180
+ language_dropdown = gr.Dropdown(
181
+ choices=["Python", "JavaScript", "Java", "C++", "General"],
182
+ value="Python",
183
+ label="Programming Language Focus",
184
+ info="Get language-specific examples"
185
+ )
186
+
187
+ # Temperature slider
188
+ temperature_slider = gr.Slider(
189
+ minimum=0.1,
190
+ maximum=1.0,
191
+ value=0.7,
192
+ step=0.1,
193
+ label="Creativity (Temperature)",
194
+ info="Lower = more focused, Higher = more creative"
195
+ )
196
+
197
+ # Response length slider
198
+ max_tokens_slider = gr.Slider(
199
+ minimum=100,
200
+ maximum=2000,
201
+ value=500,
202
+ step=100,
203
+ label="Response Length (Tokens)",
204
+ info="Maximum length of responses"
205
+ )
206
+
207
+ # Example questions dropdown
208
+ gr.Markdown("### 💡 Example Questions")
209
+ example_dropdown = gr.Dropdown(
210
+ choices=[
211
+ "Explain list comprehensions with examples",
212
+ "How do decorators work in Python?",
213
+ "What's the difference between 'is' and '=='?",
214
+ "Show me how to handle exceptions properly"
215
+ ],
216
+ label="Quick Questions",
217
+ info="Select a question to ask",
218
+ allow_custom_value=True
219
+ )
220
+
221
+ # Quick action buttons
222
+ gr.Markdown("### ⚡ Quick Actions")
223
+ with gr.Row():
224
+ clear_btn = gr.Button("🧹 Clear Chat", variant="secondary", size="sm")
225
+ reset_btn = gr.Button("🔄 Reset Settings", variant="secondary", size="sm")
226
+
227
  with gr.Column(scale=2):
228
+ # Chat interface
229
+ chatbot = gr.Chatbot(
230
+ value=[],
231
+ label="CodeMentor",
232
+ height=500,
233
+ bubble_full_width=False
234
+ )
235
+
236
+ # Message input
237
+ msg = gr.Textbox(
238
+ placeholder="Type your programming question here... (Press Enter to send)",
239
+ label="Your Question",
240
+ lines=2
241
+ )
242
+
243
+ with gr.Row():
244
+ send_btn = gr.Button("🚀 Send", variant="primary")
245
+ stop_btn = gr.Button("⏹️ Stop", variant="stop")
246
+
247
+ # Update example questions when language changes
248
+ language_dropdown.change(
249
+ fn=update_example_questions,
250
+ inputs=language_dropdown,
251
+ outputs=example_dropdown
252
+ )
253
+
254
+ # Handle example question selection
255
+ example_dropdown.change(
256
+ fn=lambda x: x,
257
+ inputs=[example_dropdown],
258
+ outputs=msg
259
+ )
260
+
261
+ # Handle message submission
262
  msg.submit(
263
+ fn=respond,
264
+ inputs=[msg, chat_state, model_dropdown, temperature_slider, max_tokens_slider],
265
  outputs=[msg, chatbot]
266
  )
267
+
268
  send_btn.click(
269
+ fn=respond,
270
+ inputs=[msg, chat_state, model_dropdown, temperature_slider, max_tokens_slider],
271
  outputs=[msg, chatbot]
272
  )
273
+
274
+ # Handle clear button
275
+ clear_btn.click(
276
+ fn=clear_chat,
277
+ inputs=None,
278
+ outputs=[chatbot, chat_state]
279
+ )
280
+
281
+ # Handle reset button
282
+ def reset_settings():
283
+ return [
284
+ "Llama 3.2 (3B) - Fast", # model_dropdown
285
+ "Python", # language_dropdown
286
+ 0.7, # temperature_slider
287
+ 500, # max_tokens_slider
288
+ "Explain list comprehensions with examples" # example_dropdown
289
+ ]
290
+
291
+ reset_btn.click(
292
+ fn=reset_settings,
293
+ inputs=None,
294
+ outputs=[model_dropdown, language_dropdown, temperature_slider, max_tokens_slider, example_dropdown]
295
+ )
296
+
297
+ # Footer with troubleshooting info
298
+ gr.Markdown("""
299
+ ---
300
+ ### ℹ️ About & Troubleshooting
301
+
302
+ **Powered by**: GROQ API
303
+ **Current Models Available**:
304
+ - `llama-3.2-3b-preview` (Fast, 3B parameters)
305
+ - `llama-3.2-1b-preview` (Lightweight, 1B)
306
+ - `llama-3.2-90b-text-preview` (Most powerful, 90B)
307
+ - `llama-3.2-11b-text-preview` (Balanced, 11B)
308
+ - `mixtral-8x7b-32768` (Mixture of experts)
309
+ - `gemma2-9b-it` (Google's model)
310
+
311
+ **If you see "model not found" error**:
312
+ 1. Check GROQ Console for available models
313
+ 2. Ensure your API key has access to the selected model
314
+ 3. Try a different model from the dropdown
315
+
316
+ **Note**: Free tier has rate limits. If requests fail, wait 1 minute and try again.
317
+ """)
318
 
319
  if __name__ == "__main__":
320
+ demo.launch(debug=False, server_name="0.0.0.0", server_port=7860)