Phoenix21 commited on
Commit
c947ea7
·
verified ·
1 Parent(s): a004b34

ADDED FEATURES

Browse files
Files changed (1) hide show
  1. app.py +217 -253
app.py CHANGED
@@ -1,102 +1,149 @@
1
  # app.py
2
 
3
  import os
4
- import getpass
5
  import pandas as pd
6
  import chardet
7
  import logging
8
  import gradio as gr
 
9
 
10
  from sentence_transformers import SentenceTransformer, util, CrossEncoder
11
- from langchain_community.retrievers import BM25Retriever
12
- from smolagents import (
13
- CodeAgent,
14
- HfApiModel,
15
- DuckDuckGoSearchTool,
16
- Tool,
17
- ManagedAgent,
18
- LiteLLMModel
19
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
- # --------------------------------------------------------------------------------
22
- # Set up logging
23
- # --------------------------------------------------------------------------------
24
- logging.basicConfig(level=logging.DEBUG)
25
- logger = logging.getLogger("Daily Wellness AI Guru")
26
-
27
- # --------------------------------------------------------------------------------
28
- # Ensure Hugging Face API Token
29
- # --------------------------------------------------------------------------------
30
- # In a Hugging Face Space, you can set HF_API_TOKEN as a secret variable.
31
- # If it's not set, you could prompt for it locally, but in Spaces,
32
- # you typically wouldn't do getpass. We'll leave the logic here as fallback.
33
- if 'HF_API_TOKEN' not in os.environ or not os.environ['HF_API_TOKEN']:
34
- os.environ['HF_API_TOKEN'] = getpass.getpass('Enter your Hugging Face API Token: ')
35
- else:
36
- print("HF_API_TOKEN is already set.")
37
-
38
- # --------------------------------------------------------------------------------
39
- # CSV Loading and Processing
40
- # --------------------------------------------------------------------------------
41
- def load_csv(file_path):
42
- """
43
- Load and process a CSV file into two lists: questions and answers.
44
- """
45
  try:
46
- # Detect the encoding of the file
 
 
 
47
  with open(file_path, 'rb') as f:
48
  result = chardet.detect(f.read())
49
  encoding = result['encoding']
50
 
51
- # Load the CSV using the detected encoding
52
  data = pd.read_csv(file_path, encoding=encoding)
53
-
54
- # Validate that the required columns are present
55
  if 'Question' not in data.columns or 'Answers' not in data.columns:
56
- raise ValueError("The CSV file must contain 'Question' and 'Answers' columns.")
57
-
58
- # Drop any rows with missing values in 'Question' or 'Answers'
59
  data = data.dropna(subset=['Question', 'Answers'])
60
 
61
- # Extract questions and answers
62
- questions = data['Question'].tolist()
63
- answers = data['Answers'].tolist()
64
-
65
- logger.info(f"Loaded {len(questions)} questions and {len(answers)} answers from {file_path}")
66
- return questions, answers
67
  except Exception as e:
68
- logger.error(f"Error loading CSV file: {e}")
69
  return [], []
70
 
71
- # --------------------------------------------------------------------------------
72
- # Load the AIChatbot.csv file
73
- # --------------------------------------------------------------------------------
74
- file_path = "AIChatbot.csv" # Ensure this file is in the same directory as app.py
75
- corpus_questions, corpus_answers = load_csv(file_path)
76
-
77
  if not corpus_questions:
78
- raise ValueError(f"Failed to load questions from {file_path}.")
79
 
80
- # --------------------------------------------------------------------------------
81
- # Embedding Model
82
- # --------------------------------------------------------------------------------
83
  embedding_model_name = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
84
- embedding_model = SentenceTransformer(embedding_model_name)
85
- logger.info(f"Loaded sentence embedding model: {embedding_model_name}")
86
-
87
- # Encode Questions (for retrieval)
88
- question_embeddings = embedding_model.encode(corpus_questions, convert_to_tensor=True)
89
-
90
- # --------------------------------------------------------------------------------
91
- # Cross-Encoder for Re-Ranking
92
- # --------------------------------------------------------------------------------
93
- cross_encoder_model_name = "cross-encoder/ms-marco-MiniLM-L-6-v2"
94
- cross_encoder = CrossEncoder(cross_encoder_model_name)
95
- logger.info(f"Loaded cross-encoder model: {cross_encoder_model_name}")
96
-
97
- # --------------------------------------------------------------------------------
98
- # Retrieval + Re-ranking Class
99
- # --------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
100
  class EmbeddingRetriever:
101
  def __init__(self, questions, answers, embeddings, model, cross_encoder):
102
  self.questions = questions
@@ -105,204 +152,121 @@ class EmbeddingRetriever:
105
  self.model = model
106
  self.cross_encoder = cross_encoder
107
 
108
- def retrieve(self, query, top_k=3):
109
- # Compute query embedding
110
- query_embedding = self.model.encode(query, convert_to_tensor=True)
111
- scores = util.pytorch_cos_sim(query_embedding, self.embeddings)[0].cpu().tolist()
112
-
113
- # Combine data
114
- scored_data = list(zip(self.questions, self.answers, scores))
115
- # Sort by best scores
116
- scored_data = sorted(scored_data, key=lambda x: x[2], reverse=True)
117
- # Take top_k
118
- top_candidates = scored_data[:top_k]
119
-
120
- # Cross-encode re-rank
121
- cross_inputs = [[query, candidate[0]] for candidate in top_candidates]
122
- cross_scores = self.cross_encoder.predict(cross_inputs)
123
-
124
- reranked = sorted(
125
- zip(top_candidates, cross_scores),
126
- key=lambda x: x[1],
127
- reverse=True
128
- )
129
 
130
- # The best candidate
131
- best_candidate = reranked[0][0] # (question, answer, score)
132
- best_answer = best_candidate[1]
133
- return best_answer
134
-
135
- retriever = EmbeddingRetriever(
136
- questions=corpus_questions,
137
- answers=corpus_answers,
138
- embeddings=question_embeddings,
139
- model=embedding_model,
140
- cross_encoder=cross_encoder
141
- )
142
 
143
- # --------------------------------------------------------------------------------
144
- # Simple Answer Expander (Without custom sampling parameters)
145
- # --------------------------------------------------------------------------------
 
 
146
  class AnswerExpander:
147
- def __init__(self, model: HfApiModel):
148
- self.model = model
149
 
150
- def expand(self, question: str, short_answer: str) -> str:
151
- """
152
- Prompt the LLM to provide a more creative, brand-aligned answer.
153
- """
154
- prompt = (
155
- "You are Daily Wellness AI, a friendly and creative wellness expert. "
156
- "The user has a question about well-being. Provide an encouraging, day-to-day "
157
- "wellness perspective. Be gentle, uplifting, and brand-aligned.\n\n"
158
- f"Question: {question}\n"
159
- f"Current short answer: {short_answer}\n\n"
160
- "Please rephrase and expand with more detail, wellness tips, daily-life "
161
- "applications, and an optimistic tone. Keep it informal, friendly, and end "
162
- "with a short inspirational note.\n"
163
- )
164
  try:
165
- expanded_answer = self.model.run(prompt)
166
- return expanded_answer.strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  except Exception as e:
168
- logger.error(f"Failed to expand answer: {e}")
169
- return short_answer
170
-
171
- # NOTE: We are using a basic HfApiModel here (no custom sampling).
172
- expander_model = HfApiModel()
173
- answer_expander = AnswerExpander(expander_model)
174
-
175
- # --------------------------------------------------------------------------------
176
- # Enhanced Retriever Tool
177
- # --------------------------------------------------------------------------------
178
- from smolagents import Tool
179
-
180
- class RetrieverTool(Tool):
181
- name = "retriever_tool"
182
- description = "Uses semantic search + cross-encoder re-ranking to retrieve the best answer."
183
- inputs = {
184
- "query": {
185
- "type": "string",
186
- "description": "User query for retrieving relevant information.",
187
- }
188
- }
189
- output_type = "string"
190
-
191
- def __init__(self, retriever, expander):
192
- super().__init__()
193
- self.retriever = retriever
194
- self.expander = expander
195
-
196
- def forward(self, query):
197
- best_answer = self.retriever.retrieve(query, top_k=3)
198
- if best_answer:
199
- # If short, expand it
200
- if len(best_answer.strip()) < 80:
201
- logger.info("Answer is short. Expanding with LLM.")
202
- best_answer = self.expander.expand(query, best_answer)
203
- return best_answer
204
- return "No relevant information found."
205
-
206
- retriever_tool = RetrieverTool(retriever, answer_expander)
207
-
208
- # --------------------------------------------------------------------------------
209
- # DuckDuckGo (Web) Fallback
210
- # --------------------------------------------------------------------------------
211
- search_tool = DuckDuckGoSearchTool()
212
-
213
- # --------------------------------------------------------------------------------
214
- # Managed Agents
215
- # --------------------------------------------------------------------------------
216
- from smolagents import ManagedAgent, CodeAgent, LiteLLMModel
217
-
218
- retriever_agent = ManagedAgent(
219
- agent=CodeAgent(tools=[retriever_tool], model=LiteLLMModel("groq/llama3-8b-8192")),
220
- name="retriever_agent",
221
- description="Retrieves answers from the local knowledge base (CSV file)."
222
- )
223
 
224
- web_agent = ManagedAgent(
225
- agent=CodeAgent(tools=[search_tool], model=HfApiModel()),
226
- name="web_search_agent",
227
- description="Performs web searches if the local knowledge base doesn't have an answer."
228
- )
229
 
230
- # --------------------------------------------------------------------------------
231
- # Manager Agent to Orchestrate
232
- # --------------------------------------------------------------------------------
233
- manager_agent = CodeAgent(
234
- tools=[],
235
- model=HfApiModel(),
236
- managed_agents=[retriever_agent, web_agent],
237
- verbose=True
238
- )
239
 
240
- # --------------------------------------------------------------------------------
241
- # Gradio Interface
242
- # --------------------------------------------------------------------------------
243
- def gradio_interface(query):
244
  try:
245
- logger.info(f"User query: {query}")
246
-
247
- # 1) Query local knowledge base
248
- retriever_response = retriever_tool.forward(query)
249
- if retriever_response != "No relevant information found.":
250
- logger.info("Provided answer from local DB (possibly expanded).")
251
- return (
252
- f"Hello! This is **Daily Wellness AI**.\n\n"
253
- f"{retriever_response}\n\n"
254
- "Disclaimer: This is general wellness information, "
255
- "not a substitute for professional medical advice.\n\n"
256
- "Wishing you a calm and wonderful day!"
257
- )
258
-
259
- # 2) Fallback to Web if no relevant local info
260
- logger.info("Falling back to web search.")
261
- web_response = web_agent.run(query)
262
- if web_response:
263
- logger.info("Response retrieved from the web.")
264
- return (
265
- f"Hello! This is **Daily Wellness AI**.\n\n"
266
- f"{web_response.strip()}\n\n"
267
- "Disclaimer: This is general wellness information, "
268
- "not a substitute for professional medical advice.\n\n"
269
- "Wishing you a calm and wonderful day!"
270
- )
271
-
272
- # 3) Default fallback
273
- logger.info("No response found from any source.")
274
- return (
275
- "Hello! This is **Daily Wellness AI**.\n\n"
276
- "I'm sorry, I couldn't find an answer to your question. "
277
- "Please try rephrasing or ask something else.\n\n"
278
- "Take care, and have a wonderful day!"
279
  )
 
280
  except Exception as e:
281
- logger.error(f"Error processing query: {e}")
 
282
  return "**An error occurred while processing your request. Please try again later.**"
283
 
284
- # --------------------------------------------------------------------------------
285
- # Launch Gradio App
286
- # --------------------------------------------------------------------------------
287
  interface = gr.Interface(
288
  fn=gradio_interface,
289
  inputs=gr.Textbox(
290
- label="Ask Daily Wellness AI",
291
- placeholder="e.g., What is box breathing?"
 
292
  ),
293
  outputs=gr.Markdown(label="Answer from Daily Wellness AI"),
294
- title="Daily Wellness AI Guru Chatbot",
295
- description=(
296
- "Ask wellness-related questions to get detailed, creative answers from "
297
- "our knowledge base—expanded by an LLM if needed—or from the web. "
298
- "We aim to bring calm and positivity to your day."
299
- ),
300
- theme="compact"
 
 
301
  )
302
 
303
- def main():
304
- interface.launch(server_name="0.0.0.0", server_port=7860, debug=True)
305
-
306
- # If running in a local environment, we can also just call main()
307
  if __name__ == "__main__":
308
- main()
 
 
 
 
 
 
1
  # app.py
2
 
3
  import os
 
4
  import pandas as pd
5
  import chardet
6
  import logging
7
  import gradio as gr
8
+ from typing import Optional, List, Tuple, ClassVar, Dict
9
 
10
  from sentence_transformers import SentenceTransformer, util, CrossEncoder
11
+ from langchain.llms.base import LLM
12
+ import google.generativeai as genai
13
+
14
+ ###############################################################################
15
+ # 1) Logging Setup
16
+ ###############################################################################
17
+ logging.basicConfig(level=logging.INFO)
18
+ logger = logging.getLogger("Daily Wellness AI")
19
+
20
+ ###############################################################################
21
+ # 2) API Key Handling and Enhanced GeminiLLM Class
22
+ ###############################################################################
23
+ def clean_api_key(key: str) -> str:
24
+ """Remove non-ASCII characters and strip whitespace from the API key."""
25
+ return ''.join(c for c in key if ord(c) < 128).strip()
26
+
27
+ # Load the GEMINI API key from environment variables
28
+ gemini_api_key = os.environ.get("GEMINI_API_KEY")
29
+
30
+ if not gemini_api_key:
31
+ logger.error("GEMINI_API_KEY environment variable not set.")
32
+ raise EnvironmentError("Please set the GEMINI_API_KEY environment variable.")
33
+
34
+ gemini_api_key = clean_api_key(gemini_api_key)
35
+ logger.info("GEMINI API Key loaded successfully.")
36
+
37
+ # Configure Google Generative AI
38
+ try:
39
+ genai.configure(api_key=gemini_api_key)
40
+ logger.info("Configured Google Generative AI with provided API key.")
41
+ except Exception as e:
42
+ logger.error(f"Failed to configure Google Generative AI: {e}")
43
+ raise e
44
+
45
+ class GeminiLLM(LLM):
46
+ model_name: ClassVar[str] = "gemini-2.0-flash-exp"
47
+ temperature: float = 0.7
48
+ top_p: float = 0.95
49
+ top_k: int = 40
50
+ max_tokens: int = 2048
51
+
52
+ @property
53
+ def _llm_type(self) -> str:
54
+ return "custom_gemini"
55
+
56
+ def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
57
+ generation_config = {
58
+ "temperature": self.temperature,
59
+ "top_p": self.top_p,
60
+ "top_k": self.top_k,
61
+ "max_output_tokens": self.max_tokens,
62
+ }
63
+
64
+ try:
65
+ logger.debug(f"Initializing GenerativeModel with config: {generation_config}")
66
+ model = genai.GenerativeModel(
67
+ model_name=self.model_name,
68
+ generation_config=generation_config,
69
+ )
70
+ logger.debug("GenerativeModel initialized successfully.")
71
+
72
+ chat_session = model.start_chat(history=[])
73
+ logger.debug("Chat session started.")
74
+
75
+ response = chat_session.send_message(prompt)
76
+ logger.debug(f"Prompt sent to model: {prompt}")
77
+ logger.debug(f"Raw response received: {response.text}")
78
+
79
+ return response.text
80
+ except Exception as e:
81
+ logger.error(f"Error generating response with GeminiLLM: {e}")
82
+ logger.debug("Exception details:", exc_info=True)
83
+ raise e
84
+
85
+ # Instantiate the GeminiLLM globally
86
+ llm = GeminiLLM()
87
 
88
+ ###############################################################################
89
+ # 3) CSV Loading and Processing
90
+ ###############################################################################
91
+ def load_csv(file_path: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  try:
93
+ if not os.path.isfile(file_path):
94
+ logger.error(f"CSV file does not exist: {file_path}")
95
+ return [], []
96
+
97
  with open(file_path, 'rb') as f:
98
  result = chardet.detect(f.read())
99
  encoding = result['encoding']
100
 
 
101
  data = pd.read_csv(file_path, encoding=encoding)
 
 
102
  if 'Question' not in data.columns or 'Answers' not in data.columns:
103
+ raise ValueError("CSV must contain 'Question' and 'Answers' columns.")
 
 
104
  data = data.dropna(subset=['Question', 'Answers'])
105
 
106
+ logger.info(f"Loaded {len(data)} entries from {file_path}")
107
+ return data['Question'].tolist(), data['Answers'].tolist()
 
 
 
 
108
  except Exception as e:
109
+ logger.error(f"Error loading CSV: {e}")
110
  return [], []
111
 
112
+ # Path to your CSV file (ensure 'AIChatbot.csv' is in the repository)
113
+ csv_file_path = "AIChatbot.csv"
114
+ corpus_questions, corpus_answers = load_csv(csv_file_path)
 
 
 
115
  if not corpus_questions:
116
+ raise ValueError("Failed to load the knowledge base.")
117
 
118
+ ###############################################################################
119
+ # 4) Sentence Embeddings & Cross-Encoder
120
+ ###############################################################################
121
  embedding_model_name = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
122
+ try:
123
+ embedding_model = SentenceTransformer(embedding_model_name)
124
+ logger.info(f"Loaded embedding model: {embedding_model_name}")
125
+ except Exception as e:
126
+ logger.error(f"Failed to load embedding model: {e}")
127
+ raise e
128
+
129
+ try:
130
+ question_embeddings = embedding_model.encode(corpus_questions, convert_to_tensor=True)
131
+ logger.info("Encoded question embeddings successfully.")
132
+ except Exception as e:
133
+ logger.error(f"Failed to encode question embeddings: {e}")
134
+ raise e
135
+
136
+ cross_encoder_name = "cross-encoder/ms-marco-MiniLM-L-6-v2"
137
+ try:
138
+ cross_encoder = CrossEncoder(cross_encoder_name)
139
+ logger.info(f"Loaded cross-encoder model: {cross_encoder_name}")
140
+ except Exception as e:
141
+ logger.error(f"Failed to load cross-encoder model: {e}")
142
+ raise e
143
+
144
+ ###############################################################################
145
+ # 5) Retrieval + Re-Ranking
146
+ ###############################################################################
147
  class EmbeddingRetriever:
148
  def __init__(self, questions, answers, embeddings, model, cross_encoder):
149
  self.questions = questions
 
152
  self.model = model
153
  self.cross_encoder = cross_encoder
154
 
155
+ def retrieve(self, query: str, top_k: int = 3):
156
+ try:
157
+ query_embedding = self.model.encode(query, convert_to_tensor=True)
158
+ scores = util.pytorch_cos_sim(query_embedding, self.embeddings)[0].cpu().tolist()
159
+ scored_data = sorted(zip(self.questions, self.answers, scores), key=lambda x: x[2], reverse=True)[:top_k]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
 
161
+ cross_inputs = [[query, candidate[0]] for candidate in scored_data]
162
+ cross_scores = self.cross_encoder.predict(cross_inputs)
163
+
164
+ reranked = sorted(zip(scored_data, cross_scores), key=lambda x: x[1], reverse=True)
165
+ final_retrieved = [(entry[0][1], entry[1]) for entry in reranked]
166
+ logger.debug(f"Retrieved and reranked answers: {final_retrieved}")
167
+ return final_retrieved
168
+ except Exception as e:
169
+ logger.error(f"Error during retrieval: {e}")
170
+ logger.debug("Exception details:", exc_info=True)
171
+ return []
 
172
 
173
+ retriever = EmbeddingRetriever(corpus_questions, corpus_answers, question_embeddings, embedding_model, cross_encoder)
174
+
175
+ ###############################################################################
176
+ # 6) Answer Expansion
177
+ ###############################################################################
178
  class AnswerExpander:
179
+ def __init__(self, llm: GeminiLLM):
180
+ self.llm = llm
181
 
182
+ def expand(self, query: str, retrieved_answers: List[str]) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  try:
184
+ reference_block = "\n".join(f"- {idx+1}) {ans}" for idx, ans in enumerate(retrieved_answers, start=1))
185
+ prompt = (
186
+ f"You are Daily Wellness AI, a friendly wellness expert. Below are multiple "
187
+ f"potential answers retrieved from a local knowledge base. You have a user question.\n\n"
188
+ f"Question: {query}\n\n"
189
+ f"Retrieved Answers:\n{reference_block}\n\n"
190
+ "Please synthesize these references into a single cohesive, creative, "
191
+ "and brand-aligned response. Add practical tips and positivity, and end "
192
+ "with a short inspirational note.\n\n"
193
+ "Disclaimer: This is general wellness information, not a substitute for professional medical advice."
194
+ )
195
+ logger.debug(f"Generated prompt for answer expansion: {prompt}")
196
+ response = self.llm._call(prompt)
197
+ logger.debug(f"Expanded answer: {response}")
198
+ return response.strip()
199
  except Exception as e:
200
+ logger.error(f"Error expanding answer: {e}")
201
+ logger.debug("Exception details:", exc_info=True)
202
+ return "Sorry, an error occurred while generating a response."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
+ answer_expander = AnswerExpander(llm)
 
 
 
 
205
 
206
+ ###############################################################################
207
+ # 7) Query Handling
208
+ ###############################################################################
209
+ def handle_query(query: str) -> str:
210
+ if not query or not isinstance(query, str) or len(query.strip()) == 0:
211
+ return "Please provide a valid question."
 
 
 
212
 
 
 
 
 
213
  try:
214
+ retrieved = retriever.retrieve(query)
215
+ if not retrieved:
216
+ return "I'm sorry, I couldn't find an answer to your question."
217
+ responses = [ans[0] for ans in retrieved]
218
+ expanded_answer = answer_expander.expand(query, responses)
219
+ return expanded_answer
220
+ except Exception as e:
221
+ logger.error(f"Error handling query: {e}")
222
+ logger.debug("Exception details:", exc_info=True)
223
+ return "An error occurred while processing your request."
224
+
225
+ ###############################################################################
226
+ # 8) Gradio Interface
227
+ ###############################################################################
228
+ def gradio_interface(query: str):
229
+ try:
230
+ response = handle_query(query)
231
+ formatted_response = (
232
+ f"**Daily Wellness AI**\n\n"
233
+ f"{response}\n\n"
234
+ "Disclaimer: This is general wellness information, "
235
+ "not a substitute for professional medical advice.\n\n"
236
+ "Wishing you a calm and wonderful day!"
 
 
 
 
 
 
 
 
 
 
 
237
  )
238
+ return formatted_response
239
  except Exception as e:
240
+ logger.error(f"Error in Gradio interface: {e}")
241
+ logger.debug("Exception details:", exc_info=True)
242
  return "**An error occurred while processing your request. Please try again later.**"
243
 
 
 
 
244
  interface = gr.Interface(
245
  fn=gradio_interface,
246
  inputs=gr.Textbox(
247
+ lines=2,
248
+ placeholder="e.g., What is box breathing?",
249
+ label="Ask Daily Wellness AI"
250
  ),
251
  outputs=gr.Markdown(label="Answer from Daily Wellness AI"),
252
+ title="Daily Wellness AI",
253
+ description="Ask wellness-related questions and receive synthesized, creative answers.",
254
+ theme="default",
255
+ examples=[
256
+ "What is box breathing and how does it help reduce anxiety?",
257
+ "Provide a daily wellness schedule incorporating box breathing techniques.",
258
+ "What are some tips for maintaining good posture while working at a desk?"
259
+ ],
260
+ allow_flagging="never"
261
  )
262
 
263
+ ###############################################################################
264
+ # 9) Launch Gradio
265
+ ###############################################################################
 
266
  if __name__ == "__main__":
267
+ try:
268
+ # For Hugging Face Spaces, set share=False
269
+ interface.launch(server_name="0.0.0.0", server_port=7860, debug=False)
270
+ except Exception as e:
271
+ logger.error(f"Failed to launch Gradio interface: {e}")
272
+ logger.debug("Exception details:", exc_info=True)