feministmystique commited on
Commit
cd4d40d
·
verified ·
1 Parent(s): 5c39a0d

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +51 -113
src/streamlit_app.py CHANGED
@@ -1,19 +1,16 @@
1
- import os, pathlib
 
2
 
3
-
4
-
5
- os.environ["HOME"] = "/tmp"
6
  cfg_dir = pathlib.Path("/tmp/.streamlit")
7
  cfg_dir.mkdir(parents=True, exist_ok=True)
8
-
9
  (cfg_dir / "config.toml").write_text(
10
- "[server]\nheadless = true\nport = 7860\naddress = \"0.0.0.0\"\n\n"
11
- "[browser]\ngatherUsageStats = false\n",
12
  encoding="utf-8",
13
  )
14
- import streamlit as st
15
 
16
  import time
 
17
  from huggingface_hub import InferenceClient
18
  import re
19
  import gspread
@@ -27,18 +24,15 @@ from huggingface_hub.utils import HfHubHTTPError
27
  import httpx
28
 
29
 
30
- # Configure logging
31
  logging.basicConfig(level=logging.INFO)
32
  logger = logging.getLogger(__name__)
33
 
34
- # Set Streamlit page config early
35
  st.set_page_config(
36
  page_title="Interactive Help Interface",
37
  layout="centered",
38
  initial_sidebar_state="collapsed"
39
  )
40
 
41
- # Constants
42
  DEFAULT_QUESTIONS = [
43
  "Please familiarize yourself with all the buttons before completing the questions below.",
44
  "What is sin(π/6)?",
@@ -47,7 +41,6 @@ DEFAULT_QUESTIONS = [
47
  "Find the vertex (x,y) for a parabola with equation:\ny=3x^2 -6x + 1",
48
  "What are the solutions to y = x^2 + x - 6?"
49
  ]
50
- # text generation
51
  MODEL = "deepseek-ai/DeepSeek-R1-0528"
52
  SUBJECT = "Precalculus"
53
  WORD_LIMIT = 180
@@ -57,9 +50,6 @@ class ConfigManager:
57
  self.hf_token = os.getenv("HF_TOKEN")
58
  self.google_creds_json = os.getenv("GOOGLE_SHEETS_CREDENTIALS")
59
  self.google_sheets_id = os.getenv("GOOGLE_SHEETS_ID")
60
-
61
-
62
-
63
  missing_vars = []
64
 
65
  def get_google_client(self):
@@ -103,7 +93,6 @@ class GoogleSheetsLogger:
103
  spreadsheet = client.open_by_key(sheet_id)
104
  sheet = spreadsheet.sheet1
105
 
106
- # Initialize headers if not present
107
  headers = sheet.row_values(1)
108
  if not headers:
109
  sheet.append_row([
@@ -115,7 +104,6 @@ class GoogleSheetsLogger:
115
 
116
  except Exception as e:
117
  logger.error(f"Error connecting to Google Sheets: {e}")
118
- # Do NOT crash the UI; just return None so logging becomes a no-op
119
  return None
120
 
121
  def log_button_click(self, button_name, question, subject, help_clicks, retry_count):
@@ -141,17 +129,11 @@ class GoogleSheetsLogger:
141
 
142
 
143
  class AIAssistant:
144
- def __init__(self, config):
145
- self.model = os.getenv("HF_MODEL", "HuggingFaceH4/zephyr-7b-beta")
146
- token = os.getenv("HF_TOKEN")
147
- if os.getenv("SPACE_ID") or os.getenv("HF_SPACE_ID"):
148
- token = None
149
-
150
- self.client = InferenceClient(
151
- model=self.model,
152
- token=token,
153
- timeout=60.0,
154
- )
155
 
156
  self.base_prompt = f"""
157
  You are an AI assistant designed to support high school students in the subject of {SUBJECT}.
@@ -187,34 +169,46 @@ Please follow these guidelines:
187
  system_text += f"\nPlease provide a different explanation. This is attempt {retry_count + 1}."
188
  user_text = f"Question:\n{question}"
189
 
 
 
190
  try:
191
- messages = [
192
- {"role": "system", "content": system_text},
193
- {"role": "user", "content": user_text},
194
- ]
195
- chat = self.client.chat_completion(
196
- messages=messages,
197
- max_tokens=350,
198
- temperature=0.7,
199
- )
200
- text = ""
201
- try:
202
- choices = getattr(chat, "choices", None) or chat.get("choices", [])
203
- if choices:
204
- msg0 = choices[0].get("message") or {}
205
- text = msg0.get("content") or ""
206
- if not text:
207
- text = getattr(chat, "generated_text", None) or chat.get("generated_text", "") or ""
208
- except Exception:
209
- text = str(chat)
210
- except (HfHubHTTPError, ValueError, AttributeError):
211
- full_prompt = f"{system_text}\n\n{user_text}"
212
  text = self.client.text_generation(
213
  prompt=full_prompt,
214
  max_new_tokens=300,
215
  temperature=0.7,
216
  repetition_penalty=1.1,
 
 
 
 
 
 
 
 
217
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  except (httpx.ReadTimeout, httpx.ConnectTimeout):
219
  return "The model request timed out. Please try again."
220
 
@@ -233,16 +227,15 @@ class SessionManager:
233
 
234
  @staticmethod
235
  def initialize_session_state():
236
- """Initialize session state variables"""
237
  defaults = {
238
- "responses": {}, # Store responses per question
239
- "retry_counts": {}, # Store retry counts per question
240
- "help_clicks": {}, # Store help clicks per question
241
- "button_clicked": {}, # Store which button was clicked per question
242
- "show_sections": {}, # Store section visibility per question
243
- "help_dismissed": {}, # Store help dismissed state per question
244
- "loading_states": {}, # Store loading states per question
245
- "submitted": {}, # Store submission state per question
246
  "session_id": str(uuid.uuid4())
247
  }
248
 
@@ -252,14 +245,12 @@ class SessionManager:
252
 
253
  @staticmethod
254
  def get_question_state(question_id: str, key: str, default=None):
255
- """Get state for a specific question"""
256
  if key not in st.session_state:
257
  st.session_state[key] = {}
258
  return st.session_state[key].get(question_id, default)
259
 
260
  @staticmethod
261
  def set_question_state(question_id: str, key: str, value):
262
- """Set state for a specific question"""
263
  if key not in st.session_state:
264
  st.session_state[key] = {}
265
  st.session_state[key][question_id] = value
@@ -275,13 +266,9 @@ class HelpInterface:
275
  self.session_manager.initialize_session_state()
276
 
277
  def render_question(self, question: str, question_id: str, is_demo: bool = False):
278
- """Render a single question interface"""
279
- # Debug logging
280
  logger.info(f"Rendering question {question_id}, is_demo: {is_demo}")
281
 
282
- # Question container with enhanced styling
283
  if is_demo:
284
- # Demo question styling
285
  st.markdown(f"""
286
  <div style='
287
  background: linear-gradient(135deg, #fdcb6e 0%, #e17055 100%);
@@ -314,7 +301,6 @@ class HelpInterface:
314
  </div>
315
  """, unsafe_allow_html=True)
316
  else:
317
- # Regular question styling
318
  st.markdown(f"""
319
  <div style='
320
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -347,10 +333,8 @@ class HelpInterface:
347
  </div>
348
  """, unsafe_allow_html=True)
349
 
350
- # Check if question is submitted
351
  is_submitted = self.session_manager.get_question_state(question_id, "submitted", False)
352
 
353
- # Student response area with improved spacing - only show if not submitted
354
  if not is_submitted:
355
  st.markdown("<div style='margin: 20px 0;'>", unsafe_allow_html=True)
356
  st.text_area(
@@ -362,7 +346,6 @@ class HelpInterface:
362
  )
363
  st.markdown("</div>", unsafe_allow_html=True)
364
 
365
- # Enhanced button section with better spacing and styling
366
  st.markdown("""
367
  <div style='
368
  margin: 30px 0 20px 0;
@@ -371,7 +354,6 @@ class HelpInterface:
371
  '>
372
  """, unsafe_allow_html=True)
373
 
374
- # Button layout with improved spacing
375
  col1, col2, col3 = st.columns([1, 0.2, 1])
376
 
377
  with col1:
@@ -404,11 +386,9 @@ class HelpInterface:
404
  if st.button("🔍 Need Help?", key=f"help_{question_id}", type="primary", use_container_width=True):
405
  self._toggle_help(question, question_id, is_demo)
406
  else:
407
- # Empty space to maintain layout when submitted
408
  st.markdown("<div style='height: 55px;'></div>", unsafe_allow_html=True)
409
 
410
  with col2:
411
- # Empty column for spacing
412
  st.markdown("<div style='height: 55px;'></div>", unsafe_allow_html=True)
413
 
414
  with col3:
@@ -441,7 +421,6 @@ class HelpInterface:
441
  if st.button("📝 Submit Answer", key=f"submit_{question_id}", use_container_width=True):
442
  self._handle_submit(question, question_id, is_demo)
443
  else:
444
- # Submitted state with enhanced styling
445
  st.markdown("""
446
  <div style='
447
  background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
@@ -462,25 +441,19 @@ class HelpInterface:
462
  </div>
463
  """, unsafe_allow_html=True)
464
 
465
- # Close button container
466
  st.markdown("</div>", unsafe_allow_html=True)
467
 
468
- # Render help section if active and not submitted
469
  if (self.session_manager.get_question_state(question_id, "show_sections", False) and
470
  not is_submitted):
471
  self._render_help_section(question, question_id, is_demo)
472
 
473
- # Add spacing between questions
474
  st.markdown("<div style='margin-bottom: 40px;'></div>", unsafe_allow_html=True)
475
 
476
  def _handle_submit(self, question: str, question_id: str, is_demo: bool = False):
477
- """Handle submit button click"""
478
  logger.info(f"Handle submit called for question {question_id}, is_demo: {is_demo}")
479
 
480
- # Set submission state
481
  self.session_manager.set_question_state(question_id, "submitted", True)
482
 
483
- # Log submit only if not demo
484
  if not is_demo:
485
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
486
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
@@ -495,7 +468,6 @@ class HelpInterface:
495
  st.rerun()
496
 
497
  def _toggle_help(self, question: str, question_id: str, is_demo: bool = False):
498
- """Toggle help section visibility"""
499
  logger.info(f"Toggle help called for question {question_id}, is_demo: {is_demo}")
500
 
501
  current_help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
@@ -504,11 +476,9 @@ class HelpInterface:
504
  current_show = self.session_manager.get_question_state(question_id, "show_sections", False)
505
  self.session_manager.set_question_state(question_id, "show_sections", not current_show)
506
 
507
- # Reset other states
508
  self.session_manager.set_question_state(question_id, "button_clicked", None)
509
  self.session_manager.set_question_state(question_id, "help_dismissed", False)
510
 
511
- # Log the help button click only if not demo
512
  if not is_demo:
513
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
514
  logger.info(f"Logging help click for question {question_id}")
@@ -520,7 +490,6 @@ class HelpInterface:
520
  logger.info(f"Skipping logging for demo question {question_id}")
521
 
522
  def _render_help_section(self, question: str, question_id: str, is_demo: bool = False):
523
- """Render the help section with options"""
524
  st.markdown("""
525
  <div style='
526
  background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 100%);
@@ -543,7 +512,6 @@ class HelpInterface:
543
  </div>
544
  """, unsafe_allow_html=True)
545
 
546
- # Help option buttons in a more compact layout
547
  col1, col2, col3 = st.columns(3)
548
 
549
  with col1:
@@ -558,21 +526,16 @@ class HelpInterface:
558
  if st.button("🤔 Who cares?", key=f"application_{question_id}", use_container_width=True):
559
  self._handle_help_button("Who cares?", question, question_id, is_demo)
560
 
561
- # Show response if available - NO GREEN BAR
562
  current_response = self.session_manager.get_question_state(question_id, "responses", "")
563
  current_button = self.session_manager.get_question_state(question_id, "button_clicked", None)
564
 
565
  if current_response and current_button:
566
- # Simply display the response without any container styling
567
  st.write(current_response)
568
-
569
  self._render_action_buttons(question, question_id, is_demo)
570
 
571
  def _handle_help_button(self, button_name: str, question: str, question_id: str, is_demo: bool = False):
572
- """Handle help button click and generate response"""
573
  logger.info(f"Handle help button called: {button_name} for question {question_id}, is_demo: {is_demo}")
574
 
575
- # Log button click only if not demo
576
  if not is_demo:
577
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
578
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
@@ -584,12 +547,9 @@ class HelpInterface:
584
  else:
585
  logger.info(f"Skipping logging for demo question {question_id}")
586
 
587
- # Generate response
588
  with st.spinner("Generating response..."):
589
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
590
  response = self.ai_assistant.generate_response(button_name, question, retry_count)
591
-
592
- # Store response and button clicked
593
  self.session_manager.set_question_state(question_id, "responses", response)
594
  self.session_manager.set_question_state(question_id, "button_clicked", button_name)
595
  self.session_manager.set_question_state(question_id, "retry_counts", 0)
@@ -597,7 +557,6 @@ class HelpInterface:
597
  st.rerun()
598
 
599
  def _render_action_buttons(self, question: str, question_id: str, is_demo: bool = False):
600
- """Render action buttons after showing response"""
601
  st.markdown("<div style='margin-top: 15px;'>", unsafe_allow_html=True)
602
  col1, col2, col3 = st.columns(3)
603
 
@@ -616,7 +575,6 @@ class HelpInterface:
616
  st.markdown("</div>", unsafe_allow_html=True)
617
 
618
  def _handle_retry(self, question: str, question_id: str, is_demo: bool = False):
619
- """Handle retry button click"""
620
  logger.info(f"Handle retry called for question {question_id}, is_demo: {is_demo}")
621
 
622
  current_retry = self.session_manager.get_question_state(question_id, "retry_counts", 0)
@@ -624,7 +582,6 @@ class HelpInterface:
624
 
625
  self.session_manager.set_question_state(question_id, "retry_counts", new_retry_count)
626
 
627
- # Log retry only if not demo
628
  if not is_demo:
629
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
630
  logger.info(f"Logging retry for question {question_id}")
@@ -634,7 +591,6 @@ class HelpInterface:
634
  else:
635
  logger.info(f"Skipping retry logging for demo question {question_id}")
636
 
637
- # Generate new response
638
  current_button = self.session_manager.get_question_state(question_id, "button_clicked", None)
639
  if current_button:
640
  with st.spinner("Generating alternative response..."):
@@ -643,10 +599,8 @@ class HelpInterface:
643
  st.rerun()
644
 
645
  def _handle_chatgpt_redirect(self, question: str, question_id: str, is_demo: bool = False):
646
- """Handle ChatGPT redirect"""
647
  logger.info(f"Handle ChatGPT redirect called for question {question_id}, is_demo: {is_demo}")
648
 
649
- # Log only if not demo
650
  if not is_demo:
651
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
652
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
@@ -675,10 +629,8 @@ class HelpInterface:
675
  """, unsafe_allow_html=True)
676
 
677
  def _handle_thank_you(self, question: str, question_id: str, is_demo: bool = False):
678
- """Handle thank you button click"""
679
  logger.info(f"Handle thank you called for question {question_id}, is_demo: {is_demo}")
680
 
681
- # Log only if not demo
682
  if not is_demo:
683
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
684
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
@@ -690,7 +642,6 @@ class HelpInterface:
690
  else:
691
  logger.info(f"Skipping thank you logging for demo question {question_id}")
692
 
693
- # Hide help section
694
  self.session_manager.set_question_state(question_id, "show_sections", False)
695
  self.session_manager.set_question_state(question_id, "help_dismissed", True)
696
 
@@ -728,7 +679,6 @@ def _get_sheet(_creds_json: str, _sheet_id: str):
728
 
729
 
730
  def main():
731
- """Main application entry point"""
732
  st.markdown("""
733
  <div style='
734
  text-align: center;
@@ -753,25 +703,19 @@ def main():
753
  </div>
754
  """.format(SUBJECT=SUBJECT), unsafe_allow_html=True)
755
 
756
- # Initialize configuration and interface
757
  config = ConfigManager()
758
  interface = HelpInterface(config)
759
 
760
- # Render questions with proper numbering
761
  question_number = 1
762
  for i, question in enumerate(DEFAULT_QUESTIONS):
763
  if i == 0:
764
- # First question is demo - no logging, special styling
765
  interface.render_question(question, "demo", is_demo=True)
766
  else:
767
- # Regular questions with logging and proper numbering (1, 2, 3, 4, 5)
768
  interface.render_question(question, str(question_number), is_demo=False)
769
  question_number += 1
770
 
771
- # Add custom CSS for enhanced styling
772
  st.markdown("""
773
  <style>
774
- /* Enhanced button styling */
775
  .stButton > button {
776
  height: 2.5em;
777
  font-size: 0.9rem;
@@ -786,13 +730,11 @@ def main():
786
  box-shadow: 0 6px 20px rgba(0,0,0,0.15);
787
  }
788
 
789
- /* Primary button styling */
790
  .stButton > button[kind="primary"] {
791
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
792
  color: white;
793
  }
794
 
795
- /* Text area styling */
796
  .stTextArea > div > div > textarea {
797
  border-radius: 10px;
798
  border: 2px solid #e0e4e7;
@@ -812,18 +754,15 @@ def main():
812
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
813
  }
814
 
815
- /* Remove default spacing */
816
  .block-container {
817
  padding-top: 2rem;
818
  padding-bottom: 2rem;
819
  }
820
 
821
- /* Custom spacing */
822
  .stMarkdown {
823
  margin-bottom: 0;
824
  }
825
 
826
- /* Success message styling */
827
  .stSuccess {
828
  background: linear-gradient(135deg, #00b894 0%, #00a085 100%);
829
  border: none;
@@ -832,7 +771,6 @@ def main():
832
  color: white;
833
  }
834
 
835
- /* Spinner styling */
836
  .stSpinner {
837
  color: #667eea;
838
  }
 
1
+ import os
2
+ import pathlib
3
 
4
+ os.environ["HOME"] = "/tmp"
 
 
5
  cfg_dir = pathlib.Path("/tmp/.streamlit")
6
  cfg_dir.mkdir(parents=True, exist_ok=True)
 
7
  (cfg_dir / "config.toml").write_text(
8
+ "[server]\nheadless = true\nport = 7860\naddress = \"0.0.0.0\"\n\n[browser]\ngatherUsageStats = false\n",
 
9
  encoding="utf-8",
10
  )
 
11
 
12
  import time
13
+ import streamlit as st
14
  from huggingface_hub import InferenceClient
15
  import re
16
  import gspread
 
24
  import httpx
25
 
26
 
 
27
  logging.basicConfig(level=logging.INFO)
28
  logger = logging.getLogger(__name__)
29
 
 
30
  st.set_page_config(
31
  page_title="Interactive Help Interface",
32
  layout="centered",
33
  initial_sidebar_state="collapsed"
34
  )
35
 
 
36
  DEFAULT_QUESTIONS = [
37
  "Please familiarize yourself with all the buttons before completing the questions below.",
38
  "What is sin(π/6)?",
 
41
  "Find the vertex (x,y) for a parabola with equation:\ny=3x^2 -6x + 1",
42
  "What are the solutions to y = x^2 + x - 6?"
43
  ]
 
44
  MODEL = "deepseek-ai/DeepSeek-R1-0528"
45
  SUBJECT = "Precalculus"
46
  WORD_LIMIT = 180
 
50
  self.hf_token = os.getenv("HF_TOKEN")
51
  self.google_creds_json = os.getenv("GOOGLE_SHEETS_CREDENTIALS")
52
  self.google_sheets_id = os.getenv("GOOGLE_SHEETS_ID")
 
 
 
53
  missing_vars = []
54
 
55
  def get_google_client(self):
 
93
  spreadsheet = client.open_by_key(sheet_id)
94
  sheet = spreadsheet.sheet1
95
 
 
96
  headers = sheet.row_values(1)
97
  if not headers:
98
  sheet.append_row([
 
104
 
105
  except Exception as e:
106
  logger.error(f"Error connecting to Google Sheets: {e}")
 
107
  return None
108
 
109
  def log_button_click(self, button_name, question, subject, help_clicks, retry_count):
 
129
 
130
 
131
  class AIAssistant:
132
+ """Handles AI model interactions"""
133
+
134
+ def __init__(self, config: ConfigManager):
135
+ self.model = MODEL
136
+ self.client = InferenceClient(model=self.model, token=config.hf_token, timeout=60.0)
 
 
 
 
 
 
137
 
138
  self.base_prompt = f"""
139
  You are an AI assistant designed to support high school students in the subject of {SUBJECT}.
 
169
  system_text += f"\nPlease provide a different explanation. This is attempt {retry_count + 1}."
170
  user_text = f"Question:\n{question}"
171
 
172
+ full_prompt = f"{system_text}\n\n{user_text}"
173
+
174
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
  text = self.client.text_generation(
176
  prompt=full_prompt,
177
  max_new_tokens=300,
178
  temperature=0.7,
179
  repetition_penalty=1.1,
180
+ model=self.model,
181
+ )
182
+ except (HfHubHTTPError, ValueError) as e:
183
+ msg = str(e)
184
+ unsupported = (
185
+ "Task 'text-generation' not supported" in msg
186
+ or "doesn't support task 'text-generation'" in msg
187
+ or "Available tasks: ['conversational']" in msg
188
  )
189
+ if unsupported:
190
+ messages = [
191
+ {"role": "system", "content": system_text},
192
+ {"role": "user", "content": user_text},
193
+ ]
194
+ chat = self.client.chat_completion(
195
+ messages=messages,
196
+ max_tokens=350,
197
+ temperature=0.7,
198
+ model=self.model,
199
+ )
200
+ text = ""
201
+ try:
202
+ choices = getattr(chat, "choices", None) or chat.get("choices", [])
203
+ if choices:
204
+ msg0 = choices[0].get("message") or {}
205
+ text = msg0.get("content") or ""
206
+ if not text:
207
+ text = getattr(chat, "generated_text", None) or chat.get("generated_text", "") or ""
208
+ except Exception:
209
+ text = str(chat)
210
+ else:
211
+ raise
212
  except (httpx.ReadTimeout, httpx.ConnectTimeout):
213
  return "The model request timed out. Please try again."
214
 
 
227
 
228
  @staticmethod
229
  def initialize_session_state():
 
230
  defaults = {
231
+ "responses": {},
232
+ "retry_counts": {},
233
+ "help_clicks": {},
234
+ "button_clicked": {},
235
+ "show_sections": {},
236
+ "help_dismissed": {},
237
+ "loading_states": {},
238
+ "submitted": {},
239
  "session_id": str(uuid.uuid4())
240
  }
241
 
 
245
 
246
  @staticmethod
247
  def get_question_state(question_id: str, key: str, default=None):
 
248
  if key not in st.session_state:
249
  st.session_state[key] = {}
250
  return st.session_state[key].get(question_id, default)
251
 
252
  @staticmethod
253
  def set_question_state(question_id: str, key: str, value):
 
254
  if key not in st.session_state:
255
  st.session_state[key] = {}
256
  st.session_state[key][question_id] = value
 
266
  self.session_manager.initialize_session_state()
267
 
268
  def render_question(self, question: str, question_id: str, is_demo: bool = False):
 
 
269
  logger.info(f"Rendering question {question_id}, is_demo: {is_demo}")
270
 
 
271
  if is_demo:
 
272
  st.markdown(f"""
273
  <div style='
274
  background: linear-gradient(135deg, #fdcb6e 0%, #e17055 100%);
 
301
  </div>
302
  """, unsafe_allow_html=True)
303
  else:
 
304
  st.markdown(f"""
305
  <div style='
306
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
333
  </div>
334
  """, unsafe_allow_html=True)
335
 
 
336
  is_submitted = self.session_manager.get_question_state(question_id, "submitted", False)
337
 
 
338
  if not is_submitted:
339
  st.markdown("<div style='margin: 20px 0;'>", unsafe_allow_html=True)
340
  st.text_area(
 
346
  )
347
  st.markdown("</div>", unsafe_allow_html=True)
348
 
 
349
  st.markdown("""
350
  <div style='
351
  margin: 30px 0 20px 0;
 
354
  '>
355
  """, unsafe_allow_html=True)
356
 
 
357
  col1, col2, col3 = st.columns([1, 0.2, 1])
358
 
359
  with col1:
 
386
  if st.button("🔍 Need Help?", key=f"help_{question_id}", type="primary", use_container_width=True):
387
  self._toggle_help(question, question_id, is_demo)
388
  else:
 
389
  st.markdown("<div style='height: 55px;'></div>", unsafe_allow_html=True)
390
 
391
  with col2:
 
392
  st.markdown("<div style='height: 55px;'></div>", unsafe_allow_html=True)
393
 
394
  with col3:
 
421
  if st.button("📝 Submit Answer", key=f"submit_{question_id}", use_container_width=True):
422
  self._handle_submit(question, question_id, is_demo)
423
  else:
 
424
  st.markdown("""
425
  <div style='
426
  background: linear-gradient(135deg, #48bb78 0%, #38a169 100%);
 
441
  </div>
442
  """, unsafe_allow_html=True)
443
 
 
444
  st.markdown("</div>", unsafe_allow_html=True)
445
 
 
446
  if (self.session_manager.get_question_state(question_id, "show_sections", False) and
447
  not is_submitted):
448
  self._render_help_section(question, question_id, is_demo)
449
 
 
450
  st.markdown("<div style='margin-bottom: 40px;'></div>", unsafe_allow_html=True)
451
 
452
  def _handle_submit(self, question: str, question_id: str, is_demo: bool = False):
 
453
  logger.info(f"Handle submit called for question {question_id}, is_demo: {is_demo}")
454
 
 
455
  self.session_manager.set_question_state(question_id, "submitted", True)
456
 
 
457
  if not is_demo:
458
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
459
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
 
468
  st.rerun()
469
 
470
  def _toggle_help(self, question: str, question_id: str, is_demo: bool = False):
 
471
  logger.info(f"Toggle help called for question {question_id}, is_demo: {is_demo}")
472
 
473
  current_help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
 
476
  current_show = self.session_manager.get_question_state(question_id, "show_sections", False)
477
  self.session_manager.set_question_state(question_id, "show_sections", not current_show)
478
 
 
479
  self.session_manager.set_question_state(question_id, "button_clicked", None)
480
  self.session_manager.set_question_state(question_id, "help_dismissed", False)
481
 
 
482
  if not is_demo:
483
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
484
  logger.info(f"Logging help click for question {question_id}")
 
490
  logger.info(f"Skipping logging for demo question {question_id}")
491
 
492
  def _render_help_section(self, question: str, question_id: str, is_demo: bool = False):
 
493
  st.markdown("""
494
  <div style='
495
  background: linear-gradient(135deg, #ffeaa7 0%, #fab1a0 100%);
 
512
  </div>
513
  """, unsafe_allow_html=True)
514
 
 
515
  col1, col2, col3 = st.columns(3)
516
 
517
  with col1:
 
526
  if st.button("🤔 Who cares?", key=f"application_{question_id}", use_container_width=True):
527
  self._handle_help_button("Who cares?", question, question_id, is_demo)
528
 
 
529
  current_response = self.session_manager.get_question_state(question_id, "responses", "")
530
  current_button = self.session_manager.get_question_state(question_id, "button_clicked", None)
531
 
532
  if current_response and current_button:
 
533
  st.write(current_response)
 
534
  self._render_action_buttons(question, question_id, is_demo)
535
 
536
  def _handle_help_button(self, button_name: str, question: str, question_id: str, is_demo: bool = False):
 
537
  logger.info(f"Handle help button called: {button_name} for question {question_id}, is_demo: {is_demo}")
538
 
 
539
  if not is_demo:
540
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
541
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
 
547
  else:
548
  logger.info(f"Skipping logging for demo question {question_id}")
549
 
 
550
  with st.spinner("Generating response..."):
551
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
552
  response = self.ai_assistant.generate_response(button_name, question, retry_count)
 
 
553
  self.session_manager.set_question_state(question_id, "responses", response)
554
  self.session_manager.set_question_state(question_id, "button_clicked", button_name)
555
  self.session_manager.set_question_state(question_id, "retry_counts", 0)
 
557
  st.rerun()
558
 
559
  def _render_action_buttons(self, question: str, question_id: str, is_demo: bool = False):
 
560
  st.markdown("<div style='margin-top: 15px;'>", unsafe_allow_html=True)
561
  col1, col2, col3 = st.columns(3)
562
 
 
575
  st.markdown("</div>", unsafe_allow_html=True)
576
 
577
  def _handle_retry(self, question: str, question_id: str, is_demo: bool = False):
 
578
  logger.info(f"Handle retry called for question {question_id}, is_demo: {is_demo}")
579
 
580
  current_retry = self.session_manager.get_question_state(question_id, "retry_counts", 0)
 
582
 
583
  self.session_manager.set_question_state(question_id, "retry_counts", new_retry_count)
584
 
 
585
  if not is_demo:
586
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
587
  logger.info(f"Logging retry for question {question_id}")
 
591
  else:
592
  logger.info(f"Skipping retry logging for demo question {question_id}")
593
 
 
594
  current_button = self.session_manager.get_question_state(question_id, "button_clicked", None)
595
  if current_button:
596
  with st.spinner("Generating alternative response..."):
 
599
  st.rerun()
600
 
601
  def _handle_chatgpt_redirect(self, question: str, question_id: str, is_demo: bool = False):
 
602
  logger.info(f"Handle ChatGPT redirect called for question {question_id}, is_demo: {is_demo}")
603
 
 
604
  if not is_demo:
605
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
606
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
 
629
  """, unsafe_allow_html=True)
630
 
631
  def _handle_thank_you(self, question: str, question_id: str, is_demo: bool = False):
 
632
  logger.info(f"Handle thank you called for question {question_id}, is_demo: {is_demo}")
633
 
 
634
  if not is_demo:
635
  help_clicks = self.session_manager.get_question_state(question_id, "help_clicks", 0)
636
  retry_count = self.session_manager.get_question_state(question_id, "retry_counts", 0)
 
642
  else:
643
  logger.info(f"Skipping thank you logging for demo question {question_id}")
644
 
 
645
  self.session_manager.set_question_state(question_id, "show_sections", False)
646
  self.session_manager.set_question_state(question_id, "help_dismissed", True)
647
 
 
679
 
680
 
681
  def main():
 
682
  st.markdown("""
683
  <div style='
684
  text-align: center;
 
703
  </div>
704
  """.format(SUBJECT=SUBJECT), unsafe_allow_html=True)
705
 
 
706
  config = ConfigManager()
707
  interface = HelpInterface(config)
708
 
 
709
  question_number = 1
710
  for i, question in enumerate(DEFAULT_QUESTIONS):
711
  if i == 0:
 
712
  interface.render_question(question, "demo", is_demo=True)
713
  else:
 
714
  interface.render_question(question, str(question_number), is_demo=False)
715
  question_number += 1
716
 
 
717
  st.markdown("""
718
  <style>
 
719
  .stButton > button {
720
  height: 2.5em;
721
  font-size: 0.9rem;
 
730
  box-shadow: 0 6px 20px rgba(0,0,0,0.15);
731
  }
732
 
 
733
  .stButton > button[kind="primary"] {
734
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
735
  color: white;
736
  }
737
 
 
738
  .stTextArea > div > div > textarea {
739
  border-radius: 10px;
740
  border: 2px solid #e0e4e7;
 
754
  box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
755
  }
756
 
 
757
  .block-container {
758
  padding-top: 2rem;
759
  padding-bottom: 2rem;
760
  }
761
 
 
762
  .stMarkdown {
763
  margin-bottom: 0;
764
  }
765
 
 
766
  .stSuccess {
767
  background: linear-gradient(135deg, #00b894 0%, #00a085 100%);
768
  border: none;
 
771
  color: white;
772
  }
773
 
 
774
  .stSpinner {
775
  color: #667eea;
776
  }