feministmystique commited on
Commit
18a553c
·
verified ·
1 Parent(s): de430ac

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +121 -60
src/streamlit_app.py CHANGED
@@ -10,6 +10,9 @@ import json
10
  import logging
11
  from typing import Optional, Dict, Any
12
  import uuid
 
 
 
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO)
@@ -38,23 +41,11 @@ WORD_LIMIT = 180
38
 
39
  class ConfigManager:
40
  def __init__(self):
41
- self.hf_token = os.environ.get("HF_TOKEN") or "mock-hf-token"
42
- self.google_creds_json = os.environ.get("GOOGLE_SHEETS_CREDENTIALS")
43
- self.google_sheets_id = os.environ.get("GOOGLE_SHEETS_ID") or "mock-sheet-id"
44
 
45
  missing_vars = []
46
- if not os.environ.get("HF_TOKEN"):
47
- missing_vars.append("HF_TOKEN (using mock)")
48
- if not self.google_creds_json:
49
- missing_vars.append("GOOGLE_SHEETS_CREDENTIALS (Google integration disabled)")
50
- if not os.environ.get("GOOGLE_SHEETS_ID"):
51
- missing_vars.append("GOOGLE_SHEETS_ID (using mock)")
52
-
53
- if missing_vars:
54
- st.warning(
55
- f"⚠ Missing environment variables: {', '.join(missing_vars)}. "
56
- "Some features may be disabled or mocked."
57
- )
58
 
59
  def get_google_client(self):
60
  if not self.google_creds_json:
@@ -75,55 +66,52 @@ class ConfigManager:
75
 
76
  class GoogleSheetsLogger:
77
  """Handles Google Sheets logging functionality"""
78
-
79
  def __init__(self, config: ConfigManager):
80
  self.config = config
81
- self._sheet = None
82
-
83
- @st.cache_resource
84
- def _init_google_sheets(_self):
85
- """Initialize Google Sheets connection with caching"""
86
  try:
87
- creds_dict = json.loads(_self.config.google_sheets_credentials)
 
 
 
 
88
  scope = [
89
  "https://www.googleapis.com/auth/spreadsheets",
90
  "https://www.googleapis.com/auth/drive"
91
  ]
92
  creds = Credentials.from_service_account_info(creds_dict, scopes=scope)
93
  client = gspread.authorize(creds)
94
- spreadsheet = client.open_by_key(_self.config.google_sheets_id)
95
  sheet = spreadsheet.sheet1
96
-
97
  # Initialize headers if not present
98
  headers = sheet.row_values(1)
99
  if not headers:
100
  sheet.append_row([
101
- "Timestamp", "Session_ID", "Button_Clicked",
102
  "Question", "Subject", "Help_Click_Count", "Retry_Count"
103
  ])
104
-
105
  return sheet
 
106
  except Exception as e:
107
  logger.error(f"Error connecting to Google Sheets: {e}")
108
- st.error(f"Error connecting to Google Sheets: {e}")
109
  return None
110
-
111
- def log_button_click(self, button_name: str, question: str, subject: str,
112
- help_clicks: int, retry_count: int):
113
- """Log button click to Google Sheets"""
114
  try:
115
- sheet = self._init_google_sheets()
116
  if not sheet:
117
- logger.error("Sheet is None, cannot log")
118
  return
119
-
120
- # Generate session ID if not exists
121
  if "session_id" not in st.session_state:
122
  st.session_state.session_id = str(uuid.uuid4())
123
-
124
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
125
-
126
- # Log the data
127
  sheet.append_row([
128
  timestamp,
129
  st.session_state.session_id,
@@ -133,16 +121,17 @@ class GoogleSheetsLogger:
133
  help_clicks,
134
  retry_count
135
  ])
136
- logger.info(f"Successfully logged button click: {button_name} for question: {question[:50]}...")
137
  except Exception as e:
138
  logger.error(f"Error logging to Google Sheets: {e}")
139
- # Don't show error to user for logging failures
140
 
141
  class AIAssistant:
142
  """Handles AI model interactions"""
143
 
144
  def __init__(self, config: ConfigManager):
145
- self.client = InferenceClient(token=config.hf_token)
 
 
146
  self.base_prompt = f"""
147
  You are an AI assistant designed to support high school students in the subject of {SUBJECT}.
148
  Your role is to offer friendly, helpful, concise, in-depth guidance, just like a supportive teacher would.
@@ -171,32 +160,73 @@ Please follow these guidelines:
171
  }
172
 
173
  def generate_response(self, button_name: str, question: str, retry_count: int = 0) -> str:
174
- """Generate AI response based on button type and question"""
175
  try:
176
- prompt = self.base_prompt + self.prompt_templates[button_name]
 
177
  if retry_count > 0:
178
- prompt += f"\nPlease provide a different explanation. This is attempt {retry_count + 1}."
 
179
 
180
- # Send prompt to HuggingFace Inference API
181
- full_prompt = f"{prompt}\n\nQuestion:\n{question}"
182
 
183
- response = self.client.text_generation(
184
- model=MODEL,
185
- prompt=full_prompt,
186
- max_new_tokens=300,
187
- temperature=0.7,
188
- repetition_penalty=1.1,
189
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- # `response` will be a string, so just clean it
192
- cleaned_response = re.sub(r"<think>.*?</think>", "", response, flags=re.DOTALL)
193
- return cleaned_response.strip()
194
-
195
  except Exception as e:
196
- logger.error(f"Error generating AI response: {e}")
197
  return "Sorry, I encountered an error generating a response. Please try again."
198
 
199
 
 
 
 
200
  class SessionManager:
201
  """Manages Streamlit session state"""
202
 
@@ -666,6 +696,36 @@ class HelpInterface:
666
  st.success("🎉 Glad I could help! Feel free to ask for help anytime.")
667
  st.rerun()
668
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
  def main():
670
  """Main application entry point"""
671
  st.markdown("""
@@ -778,7 +838,8 @@ def main():
778
  </style>
779
  """, unsafe_allow_html=True)
780
 
781
- if __name__ == "__main__":
782
- main()
783
 
784
 
 
 
 
 
10
  import logging
11
  from typing import Optional, Dict, Any
12
  import uuid
13
+ from huggingface_hub.utils import HfHubHTTPError
14
+ import httpx
15
+
16
 
17
  # Configure logging
18
  logging.basicConfig(level=logging.INFO)
 
41
 
42
  class ConfigManager:
43
  def __init__(self):
44
+ self.hf_token = os.getenv("HF_TOKEN")
45
+ self.google_creds_json = os.getenv("GOOGLE_SHEETS_CREDENTIALS")
46
+ self.google_sheets_id = os.getenv("GOOGLE_SHEETS_ID")
47
 
48
  missing_vars = []
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
  def get_google_client(self):
51
  if not self.google_creds_json:
 
66
 
67
  class GoogleSheetsLogger:
68
  """Handles Google Sheets logging functionality"""
69
+
70
  def __init__(self, config: ConfigManager):
71
  self.config = config
72
+
73
+ @st.cache_resource(show_spinner=False)
74
+ def _init_google_sheets(creds_json: str, sheet_id: str):
75
+ """Initialize Google Sheets connection with caching based on inputs."""
 
76
  try:
77
+ if not creds_json or not sheet_id:
78
+ logger.info("Google Sheets disabled: missing creds_json or sheet_id.")
79
+ return None
80
+
81
+ creds_dict = json.loads(creds_json)
82
  scope = [
83
  "https://www.googleapis.com/auth/spreadsheets",
84
  "https://www.googleapis.com/auth/drive"
85
  ]
86
  creds = Credentials.from_service_account_info(creds_dict, scopes=scope)
87
  client = gspread.authorize(creds)
88
+ spreadsheet = client.open_by_key(sheet_id)
89
  sheet = spreadsheet.sheet1
90
+
91
  # Initialize headers if not present
92
  headers = sheet.row_values(1)
93
  if not headers:
94
  sheet.append_row([
95
+ "Timestamp", "Session_ID", "Button_Clicked",
96
  "Question", "Subject", "Help_Click_Count", "Retry_Count"
97
  ])
98
+
99
  return sheet
100
+
101
  except Exception as e:
102
  logger.error(f"Error connecting to Google Sheets: {e}")
103
+ # Do NOT crash the UI; just return None so logging becomes a no-op
104
  return None
105
+
106
+ def log_button_click(self, button_name, question, subject, help_clicks, retry_count):
 
 
107
  try:
108
+ sheet = _get_sheet(self.config.google_creds_json, self.config.google_sheets_id)
109
  if not sheet:
110
+ logger.info("Google Sheets not available; skipping log.")
111
  return
 
 
112
  if "session_id" not in st.session_state:
113
  st.session_state.session_id = str(uuid.uuid4())
 
114
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
 
 
115
  sheet.append_row([
116
  timestamp,
117
  st.session_state.session_id,
 
121
  help_clicks,
122
  retry_count
123
  ])
 
124
  except Exception as e:
125
  logger.error(f"Error logging to Google Sheets: {e}")
126
+
127
 
128
  class AIAssistant:
129
  """Handles AI model interactions"""
130
 
131
  def __init__(self, config: ConfigManager):
132
+ self.model = MODEL
133
+ self.client = InferenceClient(model=self.model, token=config.hf_token, timeout=60.0)
134
+
135
  self.base_prompt = f"""
136
  You are an AI assistant designed to support high school students in the subject of {SUBJECT}.
137
  Your role is to offer friendly, helpful, concise, in-depth guidance, just like a supportive teacher would.
 
160
  }
161
 
162
  def generate_response(self, button_name: str, question: str, retry_count: int = 0) -> str:
 
163
  try:
164
+ # Build prompts
165
+ system_text = self.base_prompt + self.prompt_templates.get(button_name, "")
166
  if retry_count > 0:
167
+ system_text += f"\nPlease provide a different explanation. This is attempt {retry_count + 1}."
168
+ user_text = f"Question:\n{question}"
169
 
170
+ full_prompt = f"{system_text}\n\n{user_text}" # still used for text_generation
 
171
 
172
+ try:
173
+ # Try classic text-generation first
174
+ text = self.client.text_generation(
175
+ prompt=full_prompt,
176
+ max_new_tokens=300,
177
+ temperature=0.7,
178
+ repetition_penalty=1.1,
179
+ model=self.model, # explicit even though client is bound
180
+ )
181
+ except (HfHubHTTPError, ValueError) as e:
182
+ # If the provider/model doesn't support text-generation, fall back to chat
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
+ # OpenAI-style chat interface
191
+ # Build messages: system + user
192
+ messages = [
193
+ {"role": "system", "content": system_text},
194
+ {"role": "user", "content": user_text},
195
+ ]
196
+ chat = self.client.chat_completion(
197
+ messages=messages,
198
+ max_tokens=350,
199
+ temperature=0.7,
200
+ model=self.model,
201
+ )
202
+ # Robust extraction
203
+ text = ""
204
+ try:
205
+ # chat.choices[0].message.content (OpenAI-like)
206
+ choices = getattr(chat, "choices", None) or chat.get("choices", [])
207
+ if choices:
208
+ msg0 = choices[0].get("message") or {}
209
+ text = msg0.get("content") or ""
210
+ if not text:
211
+ # Some providers return 'generated_text'
212
+ text = getattr(chat, "generated_text", None) or chat.get("generated_text", "") or ""
213
+ except Exception:
214
+ text = str(chat)
215
+ else:
216
+ raise
217
+ except (httpx.ReadTimeout, httpx.ConnectTimeout):
218
+ return "The model request timed out. Please try again."
219
 
220
+ cleaned = re.sub(r"<think>.*?</think>", "", text or "", flags=re.DOTALL)
221
+ return cleaned.strip() or "Sorry, I couldn't generate a response."
 
 
222
  except Exception as e:
223
+ logger.exception(f"Error generating AI response: {e}")
224
  return "Sorry, I encountered an error generating a response. Please try again."
225
 
226
 
227
+
228
+
229
+
230
  class SessionManager:
231
  """Manages Streamlit session state"""
232
 
 
696
  st.success("🎉 Glad I could help! Feel free to ask for help anytime.")
697
  st.rerun()
698
 
699
+ @st.cache_resource(show_spinner=False)
700
+ def _get_sheet(_creds_json: str, _sheet_id: str):
701
+ try:
702
+ if not _creds_json or not _sheet_id:
703
+ logger.info("Sheets disabled: missing creds or id.")
704
+ return None
705
+
706
+ creds_dict = json.loads(_creds_json)
707
+ scope = [
708
+ "https://www.googleapis.com/auth/spreadsheets",
709
+ "https://www.googleapis.com/auth/drive",
710
+ ]
711
+ creds = Credentials.from_service_account_info(creds_dict, scopes=scope)
712
+ client = gspread.authorize(creds)
713
+ spreadsheet = client.open_by_key(_sheet_id)
714
+ sheet = spreadsheet.sheet1
715
+
716
+ headers = sheet.row_values(1)
717
+ if not headers:
718
+ sheet.append_row([
719
+ "Timestamp","Session_ID","Button_Clicked",
720
+ "Question","Subject","Help_Click_Count","Retry_Count"
721
+ ])
722
+ return sheet
723
+ except Exception as e:
724
+ logger.error(f"Error connecting to Google Sheets: {e}")
725
+ return None
726
+
727
+
728
+
729
  def main():
730
  """Main application entry point"""
731
  st.markdown("""
 
838
  </style>
839
  """, unsafe_allow_html=True)
840
 
 
 
841
 
842
 
843
+
844
+ if __name__ == "__main__":
845
+ main()