StanDataCamp commited on
Commit
f2e9604
Β·
1 Parent(s): 361389f

Refactor app.py for improved readability and organization; streamline comments and enhance Gradio interface styling

Browse files
Files changed (1) hide show
  1. app.py +34 -64
app.py CHANGED
@@ -12,25 +12,24 @@ import gradio as gr
12
  from openai import OpenAI
13
  from dotenv import load_dotenv
14
 
15
- # Load environment variables - supports both local .env and Hugging Face Spaces
16
  try:
17
  load_dotenv('api_keys.env')
18
  except:
19
- pass # Gracefully handle missing .env file
20
 
21
- # Initialize OpenAI client with API key validation
22
  openai_api_key = os.getenv("OPENAI_API_KEY")
23
  if not openai_api_key:
24
  raise ValueError("OPENAI_API_KEY not found. Please set it in your environment variables or .env file.")
25
 
26
- # Configure OpenAI client with settings optimized for Hugging Face Spaces
27
  client = OpenAI(
28
  api_key=openai_api_key,
29
- timeout=60.0, # Longer timeout for HF Spaces network
30
- max_retries=3 # Retry on connection failures
31
  )
32
 
33
- # Explanation complexity levels with clear descriptions
34
  EXPLANATION_LEVELS = {
35
  1: "like I'm 5 years old - use simple words and analogies",
36
  2: "like I'm 10 years old - basic concepts with examples",
@@ -39,10 +38,8 @@ EXPLANATION_LEVELS = {
39
  5: "like an expert in the field - professional level with technical depth",
40
  }
41
 
42
- # Supported languages for explanations
43
  LANGUAGES = ["English", "Russian", "German", "Spanish", "French", "Italian"]
44
 
45
- # Example questions for quick selection
46
  EXAMPLE_QUESTIONS = [
47
  "Why is the sky blue?",
48
  "How does the internet work?",
@@ -51,34 +48,28 @@ EXAMPLE_QUESTIONS = [
51
 
52
  def explain_concept(question, level, language):
53
  """
54
- Generate a comprehensive explanation for a given concept at the specified complexity level.
55
- Supports streaming for real-time response generation.
56
 
57
  Args:
58
- question (str): The concept or question to explain
59
- level (int): Complexity level (1-5)
60
- language (str): Language for the explanation
61
 
62
  Yields:
63
- str: The generated explanation chunks or error message
64
  """
65
- # Input validation
66
  if not question.strip():
67
  yield "❌ Please enter a concept to explain."
68
  return
69
 
70
- # Get explanation level description
71
  level_desc = EXPLANATION_LEVELS.get(level, "clearly and comprehensively")
72
 
73
- # Language validation with fallback
74
  if language not in LANGUAGES:
75
  language = "English"
76
 
77
- # Professional system prompt for focused explanations
78
  system_prompt = f"""You are an expert educator. Explain the given concept {level_desc} in {language}. Provide a clear, accurate response in under 200 words. Ensure you use bold text to emphasize the key points. Do not include introductions, conclusions, or extra commentary - just the explanation."""
79
 
80
  try:
81
- # Generate streaming explanation using OpenAI API
82
  stream = client.chat.completions.create(
83
  model="gpt-4.1-mini",
84
  messages=[
@@ -87,10 +78,9 @@ def explain_concept(question, level, language):
87
  ],
88
  temperature=0.7,
89
  max_tokens=1000,
90
- stream=True # Enable streaming
91
  )
92
 
93
- # Proper word-by-word streaming with partial accumulation
94
  partial = ""
95
  for chunk in stream:
96
  delta = getattr(chunk.choices[0].delta, "content", None)
@@ -99,11 +89,9 @@ def explain_concept(question, level, language):
99
  yield partial
100
 
101
  except Exception as e:
102
- # Provide more specific error messages for common issues
103
  error_msg = str(e).lower()
104
  error_type = type(e).__name__
105
 
106
- # Log full error for debugging (visible in HF Spaces logs)
107
  print(f"❌ API Error [{error_type}]: {str(e)}")
108
 
109
  if "connection" in error_msg or "timeout" in error_msg or "ssl" in error_msg:
@@ -128,39 +116,44 @@ def explain_concept(question, level, language):
128
  else:
129
  yield f"❌ **Error**: `[{error_type}]` {str(e)}"
130
 
131
- # Create enhanced Gradio interface with modern styling
132
  with gr.Blocks(
133
- theme=gr.themes.Soft(), # Modern, clean theme
134
  title="AI Concept Explainer",
135
  css="""
136
  .gradio-container {
137
  max-width: 900px !important;
138
  margin: auto !important;
139
  }
 
140
  .explanation-box {
141
  background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%);
142
  color: #222;
143
  padding: 20px;
144
  border-radius: 10px;
145
  margin: 10px 0;
 
 
 
 
 
 
 
146
  }
147
  """
148
  ) as app:
149
- # Header with description
150
  gr.Markdown("""
151
  # 🧠 AI Concept Explainer
152
 
153
  Get clear, personalized explanations of any concept at your preferred complexity level and language.
154
  """)
155
 
156
- # Example selection section
157
  gr.Markdown("### πŸ’‘ Quick Examples")
158
  with gr.Row():
159
  example_btn1 = gr.Button("Why is the sky blue?", size="sm", variant="secondary")
160
  example_btn2 = gr.Button("How does the internet work?", size="sm", variant="secondary")
161
  example_btn3 = gr.Button("What is artificial intelligence?", size="sm", variant="secondary")
162
 
163
- # Input section with improved layout
164
  with gr.Row():
165
  with gr.Column(scale=2):
166
  question = gr.Textbox(
@@ -184,25 +177,22 @@ with gr.Blocks(
184
  info="Choose your preferred language"
185
  )
186
 
187
- # Action button with enhanced styling
188
  explain_btn = gr.Button(
189
  "πŸš€ Explain Concept",
190
  variant="primary",
191
  size="lg"
192
  )
193
 
194
- # Output section with better formatting
195
  output = gr.Markdown(
196
  label="πŸ“ Explanation",
197
  elem_classes=["explanation-box"]
198
  )
199
 
200
- # Connect example buttons to populate question field
201
  example_btn1.click(lambda: EXAMPLE_QUESTIONS[0], outputs=question)
202
  example_btn2.click(lambda: EXAMPLE_QUESTIONS[1], outputs=question)
203
  example_btn3.click(lambda: EXAMPLE_QUESTIONS[2], outputs=question)
204
 
205
- # Connect button click to explanation function with streaming
206
  explain_btn.click(
207
  explain_concept,
208
  inputs=[question, level, language],
@@ -210,52 +200,32 @@ with gr.Blocks(
210
  )
211
 
212
  def signal_handler(signum, frame):
213
- """Handle graceful shutdown signals for local development."""
214
  print(f"Received signal {signum}, shutting down gracefully...")
215
  exit(0)
216
 
217
  if __name__ == "__main__":
218
- # Detect environment - Hugging Face Spaces vs local development
219
  is_space = os.getenv('SPACE_ID') is not None
220
 
221
- # Set up signal handler for local development only
222
  if not is_space:
223
  signal.signal(signal.SIGINT, signal_handler)
224
 
225
  print("πŸš€ Starting AI Concept Explainer...")
226
- print(f"Environment: {'Hugging Face Spaces' if is_space else 'Local Development'}")
227
  print(f"API Key configured: {'βœ… Yes' if openai_api_key else '❌ No'}")
228
- if openai_api_key:
229
- print(f"API Key starts with: {openai_api_key[:10]}...")
230
-
231
- # Test OpenAI connection at startup (HF Spaces only)
232
- if is_space:
233
- try:
234
- print("\nπŸ” Testing OpenAI API connection...")
235
- # Simple API test
236
- test_models = client.models.list()
237
- print("βœ… OpenAI API connection successful!")
238
- except Exception as e:
239
- print(f"⚠️ WARNING: OpenAI API connection test failed!")
240
- print(f" Error: [{type(e).__name__}] {str(e)}")
241
- print(f" The app will start, but API calls may fail.")
242
- print(f" Check: 1) OPENAI_API_KEY secret is set, 2) API key is valid")
243
 
244
  if is_space:
245
- # Production configuration for Hugging Face Spaces
246
  app.launch(
247
- server_name="0.0.0.0", # Accept external connections
248
- server_port=7860, # Standard Spaces port
249
- show_error=True, # Display errors in UI
250
- quiet=False # Show startup logs
251
  )
252
  else:
253
- # Development configuration for local use
254
  app.launch(
255
- share=False, # No public sharing
256
- server_name="127.0.0.1", # Localhost only
257
- server_port=7860, # Consistent port
258
- inbrowser=True, # Auto-open browser
259
- show_error=True, # Display errors in UI
260
- quiet=False # Show startup logs
261
  )
 
12
  from openai import OpenAI
13
  from dotenv import load_dotenv
14
 
15
+ # Load environment variables
16
  try:
17
  load_dotenv('api_keys.env')
18
  except:
19
+ pass
20
 
21
+ # Initialize OpenAI client
22
  openai_api_key = os.getenv("OPENAI_API_KEY")
23
  if not openai_api_key:
24
  raise ValueError("OPENAI_API_KEY not found. Please set it in your environment variables or .env file.")
25
 
 
26
  client = OpenAI(
27
  api_key=openai_api_key,
28
+ timeout=60.0,
29
+ max_retries=3
30
  )
31
 
32
+ # Configuration
33
  EXPLANATION_LEVELS = {
34
  1: "like I'm 5 years old - use simple words and analogies",
35
  2: "like I'm 10 years old - basic concepts with examples",
 
38
  5: "like an expert in the field - professional level with technical depth",
39
  }
40
 
 
41
  LANGUAGES = ["English", "Russian", "German", "Spanish", "French", "Italian"]
42
 
 
43
  EXAMPLE_QUESTIONS = [
44
  "Why is the sky blue?",
45
  "How does the internet work?",
 
48
 
49
  def explain_concept(question, level, language):
50
  """
51
+ Generate an explanation for a given concept with streaming.
 
52
 
53
  Args:
54
+ question: The concept or question to explain
55
+ level: Complexity level (1-5)
56
+ language: Language for the explanation
57
 
58
  Yields:
59
+ Explanation text chunks or error message
60
  """
 
61
  if not question.strip():
62
  yield "❌ Please enter a concept to explain."
63
  return
64
 
 
65
  level_desc = EXPLANATION_LEVELS.get(level, "clearly and comprehensively")
66
 
 
67
  if language not in LANGUAGES:
68
  language = "English"
69
 
 
70
  system_prompt = f"""You are an expert educator. Explain the given concept {level_desc} in {language}. Provide a clear, accurate response in under 200 words. Ensure you use bold text to emphasize the key points. Do not include introductions, conclusions, or extra commentary - just the explanation."""
71
 
72
  try:
 
73
  stream = client.chat.completions.create(
74
  model="gpt-4.1-mini",
75
  messages=[
 
78
  ],
79
  temperature=0.7,
80
  max_tokens=1000,
81
+ stream=True
82
  )
83
 
 
84
  partial = ""
85
  for chunk in stream:
86
  delta = getattr(chunk.choices[0].delta, "content", None)
 
89
  yield partial
90
 
91
  except Exception as e:
 
92
  error_msg = str(e).lower()
93
  error_type = type(e).__name__
94
 
 
95
  print(f"❌ API Error [{error_type}]: {str(e)}")
96
 
97
  if "connection" in error_msg or "timeout" in error_msg or "ssl" in error_msg:
 
116
  else:
117
  yield f"❌ **Error**: `[{error_type}]` {str(e)}"
118
 
119
+ # Build Gradio interface
120
  with gr.Blocks(
121
+ theme=gr.themes.Soft(),
122
  title="AI Concept Explainer",
123
  css="""
124
  .gradio-container {
125
  max-width: 900px !important;
126
  margin: auto !important;
127
  }
128
+ /* Light mode styling */
129
  .explanation-box {
130
  background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%);
131
  color: #222;
132
  padding: 20px;
133
  border-radius: 10px;
134
  margin: 10px 0;
135
+ border: 1px solid #d0d7f7;
136
+ }
137
+ /* Dark mode styling */
138
+ .dark .explanation-box {
139
+ background: linear-gradient(135deg, #1e293b 0%, #312e81 100%);
140
+ color: #e2e8f0;
141
+ border: 1px solid #475569;
142
  }
143
  """
144
  ) as app:
 
145
  gr.Markdown("""
146
  # 🧠 AI Concept Explainer
147
 
148
  Get clear, personalized explanations of any concept at your preferred complexity level and language.
149
  """)
150
 
 
151
  gr.Markdown("### πŸ’‘ Quick Examples")
152
  with gr.Row():
153
  example_btn1 = gr.Button("Why is the sky blue?", size="sm", variant="secondary")
154
  example_btn2 = gr.Button("How does the internet work?", size="sm", variant="secondary")
155
  example_btn3 = gr.Button("What is artificial intelligence?", size="sm", variant="secondary")
156
 
 
157
  with gr.Row():
158
  with gr.Column(scale=2):
159
  question = gr.Textbox(
 
177
  info="Choose your preferred language"
178
  )
179
 
 
180
  explain_btn = gr.Button(
181
  "πŸš€ Explain Concept",
182
  variant="primary",
183
  size="lg"
184
  )
185
 
 
186
  output = gr.Markdown(
187
  label="πŸ“ Explanation",
188
  elem_classes=["explanation-box"]
189
  )
190
 
191
+ # Wire up event handlers
192
  example_btn1.click(lambda: EXAMPLE_QUESTIONS[0], outputs=question)
193
  example_btn2.click(lambda: EXAMPLE_QUESTIONS[1], outputs=question)
194
  example_btn3.click(lambda: EXAMPLE_QUESTIONS[2], outputs=question)
195
 
 
196
  explain_btn.click(
197
  explain_concept,
198
  inputs=[question, level, language],
 
200
  )
201
 
202
  def signal_handler(signum, frame):
203
+ """Handle graceful shutdown for local development."""
204
  print(f"Received signal {signum}, shutting down gracefully...")
205
  exit(0)
206
 
207
  if __name__ == "__main__":
 
208
  is_space = os.getenv('SPACE_ID') is not None
209
 
 
210
  if not is_space:
211
  signal.signal(signal.SIGINT, signal_handler)
212
 
213
  print("πŸš€ Starting AI Concept Explainer...")
 
214
  print(f"API Key configured: {'βœ… Yes' if openai_api_key else '❌ No'}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
  if is_space:
 
217
  app.launch(
218
+ server_name="0.0.0.0",
219
+ server_port=7860,
220
+ show_error=True,
221
+ quiet=False
222
  )
223
  else:
 
224
  app.launch(
225
+ share=False,
226
+ server_name="127.0.0.1",
227
+ server_port=7860,
228
+ inbrowser=True,
229
+ show_error=True,
230
+ quiet=False
231
  )