tatianija commited on
Commit
5cd098a
·
verified ·
1 Parent(s): 3abb608

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +311 -39
app.py CHANGED
@@ -12,6 +12,9 @@ from huggingface_hub import InferenceClient
12
  import base64
13
  from PIL import Image
14
  import io
 
 
 
15
 
16
  # --- Constants ---
17
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
@@ -21,6 +24,96 @@ cached_answers = {}
21
  cached_questions = []
22
  processing_status = {"is_processing": False, "progress": 0, "total": 0}
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  # --- Image Processing Tool ---
25
  class ImageAnalysisTool:
26
  def __init__(self, model_name: str = "microsoft/Florence-2-large"):
@@ -111,6 +204,7 @@ class IntelligentAgent:
111
  self.client = InferenceClient(model=model_name, provider="sambanova")
112
  self.image_tool = ImageAnalysisTool()
113
  self.audio_tool = AudioTranscriptionTool()
 
114
  self.debug = debug
115
  if self.debug:
116
  print(f"IntelligentAgent initialized with model: {model_name}")
@@ -148,11 +242,108 @@ class IntelligentAgent:
148
  print(f"Both chat completion and text generation failed: {e}")
149
  raise e
150
 
151
- def _process_media_files(self, image_files: List[str] = None, audio_files: List[str] = None) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  """
153
- Process attached media files and return their content as text.
154
  """
155
- media_content = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  # Process images
158
  if image_files:
@@ -161,15 +352,15 @@ class IntelligentAgent:
161
  try:
162
  # Analyze the image
163
  image_description = self.image_tool.analyze_image(image_file)
164
- media_content.append(f"Image Analysis: {image_description}")
165
 
166
  # Try to extract text from image
167
  extracted_text = self.image_tool.extract_text_from_image(image_file)
168
  if extracted_text and "No text found" not in extracted_text:
169
- media_content.append(f"Text from Image: {extracted_text}")
170
 
171
  except Exception as e:
172
- media_content.append(f"Error processing image {image_file}: {e}")
173
 
174
  # Process audio files
175
  if audio_files:
@@ -178,16 +369,16 @@ class IntelligentAgent:
178
  try:
179
  # Transcribe the audio
180
  transcription = self.audio_tool.transcribe_audio(audio_file)
181
- media_content.append(f"Audio Transcription: {transcription}")
182
 
183
  except Exception as e:
184
- media_content.append(f"Error processing audio {audio_file}: {e}")
185
 
186
- return "\n\n".join(media_content) if media_content else ""
187
 
188
- def _should_search(self, question: str, media_context: str = "") -> bool:
189
  """
190
- Use LLM to determine if search is needed for the question, considering media context.
191
  Returns True if search is recommended, False otherwise.
192
  """
193
  decision_prompt = f"""Analyze this question and decide if it requires real-time information, recent data, or specific facts that might not be in your training data.
@@ -208,18 +399,20 @@ SEARCH IS NOT NEEDED for:
208
  - Definitions of well-established concepts
209
  - How-to instructions for common tasks
210
  - Creative writing or opinion-based responses
211
- - Questions that can be answered from attached media content
 
 
212
 
213
  Question: "{question}"
214
 
215
- {f"Media Context Available: {media_context[:500]}..." if media_context else "No media context available."}
216
 
217
  Respond with only "SEARCH" or "NO_SEARCH" followed by a brief reason (max 20 words).
218
 
219
  Example responses:
220
  - "SEARCH - Current weather data needed"
221
  - "NO_SEARCH - Mathematical concept, general knowledge sufficient"
222
- - "NO_SEARCH - Can be answered from attached image content"
223
  """
224
 
225
  try:
@@ -236,15 +429,15 @@ Example responses:
236
 
237
  except Exception as e:
238
  if self.debug:
239
- print(f"Error in search decision: {e}, defaulting to search")
240
- # Default to search if decision fails
241
- return True
242
 
243
- def _answer_with_llm(self, question: str, media_context: str = "") -> str:
244
  """
245
- Generate answer using LLM without search, considering media context.
246
  """
247
- context_section = f"\n\nMedia Context:\n{media_context}" if media_context else ""
248
 
249
  answer_prompt = f"""You are a general AI assistant. I will ask you a question. YOUR ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string. Do not add a dot after the numbers.
250
 
@@ -261,9 +454,9 @@ Answer:"""
261
  except Exception as e:
262
  return f"Sorry, I encountered an error generating the response: {e}"
263
 
264
- def _answer_with_search(self, question: str, media_context: str = "") -> str:
265
  """
266
- Generate answer using search results and LLM, considering media context.
267
  """
268
  try:
269
  # Perform search
@@ -274,7 +467,7 @@ Answer:"""
274
  print(f"Search results type: {type(search_results)}")
275
 
276
  if not search_results:
277
- return "No search results found. Let me try to answer based on my knowledge:\n\n" + self._answer_with_llm(question, media_context)
278
 
279
  # Format search results - handle different result formats
280
  if isinstance(search_results, str):
@@ -295,8 +488,8 @@ Answer:"""
295
 
296
  search_context = "\n\n".join(formatted_results)
297
 
298
- # Generate answer using search context and media context
299
- context_section = f"\n\nMedia Context:\n{media_context}" if media_context else ""
300
 
301
  answer_prompt = f"""You are a general AI assistant. I will ask you a question. Based on the search results and context section below, provide an answer to the question. If the search results don't fully answer the question, you can supplement with your general knowledge. Do not add dot if your answer is a number.
302
  Your ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
@@ -336,11 +529,60 @@ Answer:"""
336
  return "Search completed but no usable results found."
337
 
338
  except Exception as e:
339
- return f"Search failed: {e}. Let me try to answer based on my knowledge:\n\n" + self._answer_with_llm(question, media_context)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
 
341
  def __call__(self, question: str, image_files: List[str] = None, audio_files: List[str] = None) -> str:
342
  """
343
- Main entry point - process media files, decide whether to search, and generate appropriate response.
344
  """
345
  if self.debug:
346
  print(f"Agent received question: {question}")
@@ -353,20 +595,20 @@ Answer:"""
353
 
354
  try:
355
  # Process media files first
356
- media_context = self._process_media_files(image_files, audio_files)
357
 
358
- if self.debug and media_context:
359
- print(f"Media context: {media_context[:200]}...")
360
 
361
  # Decide whether to search
362
- if self._should_search(question, media_context):
363
  if self.debug:
364
  print("Using search-based approach")
365
- answer = self._answer_with_search(question, media_context)
366
  else:
367
  if self.debug:
368
  print("Using LLM-only approach")
369
- answer = self._answer_with_llm(question, media_context)
370
 
371
  except Exception as e:
372
  answer = f"Sorry, I encountered an error: {e}"
@@ -399,13 +641,40 @@ def fetch_questions() -> Tuple[str, Optional[pd.DataFrame]]:
399
  # Create DataFrame for display
400
  display_data = []
401
  for item in questions_data:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  display_data.append({
403
  "Task ID": item.get("task_id", "Unknown"),
404
- "Question": item.get("question", "")
 
 
405
  })
406
 
407
  df = pd.DataFrame(display_data)
408
- status_msg = f"Successfully fetched {len(questions_data)} questions. Ready to generate answers."
 
 
409
 
410
  return status_msg, df
411
 
@@ -431,18 +700,19 @@ def generate_answers_async(model_name: str = "meta-llama/Llama-3.1-8B-Instruct",
431
  agent = IntelligentAgent(debug=True, model_name=model_name)
432
  cached_answers = {}
433
 
434
- for i, item in enumerate(cached_questions):
435
  if not processing_status["is_processing"]: # Check if cancelled
436
  break
437
 
438
- task_id = item.get("task_id")
439
- question_text = item.get("question")
440
 
441
  if not task_id or question_text is None:
442
  continue
443
 
444
  try:
445
- answer = agent(question_text)
 
446
  cached_answers[task_id] = {
447
  "question": question_text,
448
  "answer": answer
@@ -487,6 +757,8 @@ def start_answer_generation(model_choice: str):
487
  thread.start()
488
 
489
  return f"Answer generation started using {model_choice}. Check progress."
 
 
490
  def get_generation_progress():
491
  """
492
  Get the current progress of answer generation.
 
12
  import base64
13
  from PIL import Image
14
  import io
15
+ import tempfile
16
+ import urllib.parse
17
+ from pathlib import Path
18
 
19
  # --- Constants ---
20
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
24
  cached_questions = []
25
  processing_status = {"is_processing": False, "progress": 0, "total": 0}
26
 
27
+ # --- File Download Utility ---
28
+ def download_attachment(url: str, temp_dir: str) -> Optional[str]:
29
+ """
30
+ Download an attachment from URL to a temporary directory.
31
+ Returns the local file path if successful, None otherwise.
32
+ """
33
+ try:
34
+ response = requests.get(url, timeout=30)
35
+ response.raise_for_status()
36
+
37
+ # Extract filename from URL or create one based on content type
38
+ parsed_url = urllib.parse.urlparse(url)
39
+ filename = os.path.basename(parsed_url.path)
40
+
41
+ if not filename or '.' not in filename:
42
+ # Try to determine extension from content type
43
+ content_type = response.headers.get('content-type', '').lower()
44
+ if 'image' in content_type:
45
+ if 'jpeg' in content_type or 'jpg' in content_type:
46
+ filename = f"attachment_{int(time.time())}.jpg"
47
+ elif 'png' in content_type:
48
+ filename = f"attachment_{int(time.time())}.png"
49
+ else:
50
+ filename = f"attachment_{int(time.time())}.img"
51
+ elif 'audio' in content_type:
52
+ if 'mp3' in content_type:
53
+ filename = f"attachment_{int(time.time())}.mp3"
54
+ elif 'wav' in content_type:
55
+ filename = f"attachment_{int(time.time())}.wav"
56
+ else:
57
+ filename = f"attachment_{int(time.time())}.audio"
58
+ elif 'python' in content_type or 'text' in content_type:
59
+ filename = f"attachment_{int(time.time())}.py"
60
+ else:
61
+ filename = f"attachment_{int(time.time())}.file"
62
+
63
+ file_path = os.path.join(temp_dir, filename)
64
+
65
+ with open(file_path, 'wb') as f:
66
+ f.write(response.content)
67
+
68
+ print(f"Downloaded attachment: {url} -> {file_path}")
69
+ return file_path
70
+
71
+ except Exception as e:
72
+ print(f"Failed to download attachment {url}: {e}")
73
+ return None
74
+
75
+ # --- Code Processing Tool ---
76
+ class CodeAnalysisTool:
77
+ def __init__(self, model_name: str = "meta-llama/Llama-3.1-8B-Instruct"):
78
+ self.client = InferenceClient(model=model_name, provider="sambanova")
79
+
80
+ def analyze_code(self, code_path: str) -> str:
81
+ """
82
+ Analyze Python code and return insights.
83
+ """
84
+ try:
85
+ with open(code_path, 'r', encoding='utf-8') as f:
86
+ code_content = f.read()
87
+
88
+ # Limit code length for analysis
89
+ if len(code_content) > 5000:
90
+ code_content = code_content[:5000] + "\n... (truncated)"
91
+
92
+ analysis_prompt = f"""Analyze this Python code and provide a concise summary of:
93
+ 1. What the code does (main functionality)
94
+ 2. Key functions/classes
95
+ 3. Any notable patterns or issues
96
+ 4. Input/output behavior if applicable
97
+
98
+ Code:
99
+ ```python
100
+ {code_content}
101
+ ```
102
+
103
+ Provide a brief, focused analysis:"""
104
+
105
+ messages = [{"role": "user", "content": analysis_prompt}]
106
+ response = self.client.chat_completion(
107
+ messages=messages,
108
+ max_tokens=500,
109
+ temperature=0.3
110
+ )
111
+
112
+ return response.choices[0].message.content.strip()
113
+
114
+ except Exception as e:
115
+ return f"Code analysis failed: {e}"
116
+
117
  # --- Image Processing Tool ---
118
  class ImageAnalysisTool:
119
  def __init__(self, model_name: str = "microsoft/Florence-2-large"):
 
204
  self.client = InferenceClient(model=model_name, provider="sambanova")
205
  self.image_tool = ImageAnalysisTool()
206
  self.audio_tool = AudioTranscriptionTool()
207
+ self.code_tool = CodeAnalysisTool(model_name)
208
  self.debug = debug
209
  if self.debug:
210
  print(f"IntelligentAgent initialized with model: {model_name}")
 
242
  print(f"Both chat completion and text generation failed: {e}")
243
  raise e
244
 
245
+ def _detect_and_download_attachments(self, question_data: dict) -> Tuple[List[str], List[str], List[str]]:
246
+ """
247
+ Detect and download attachments from question data.
248
+ Returns (image_files, audio_files, code_files)
249
+ """
250
+ image_files = []
251
+ audio_files = []
252
+ code_files = []
253
+
254
+ # Create temporary directory for downloads
255
+ temp_dir = tempfile.mkdtemp(prefix="agent_attachments_")
256
+
257
+ # Check for attachments in various fields
258
+ attachments = []
259
+
260
+ # Common fields where attachments might be found
261
+ attachment_fields = ['attachments', 'files', 'media', 'resources']
262
+
263
+ for field in attachment_fields:
264
+ if field in question_data:
265
+ field_data = question_data[field]
266
+ if isinstance(field_data, list):
267
+ attachments.extend(field_data)
268
+ elif isinstance(field_data, str):
269
+ attachments.append(field_data)
270
+
271
+ # Also check if the question text contains URLs
272
+ question_text = question_data.get('question', '')
273
+ if 'http' in question_text:
274
+ import re
275
+ urls = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', question_text)
276
+ attachments.extend(urls)
277
+
278
+ # Download and categorize attachments
279
+ for attachment in attachments:
280
+ if isinstance(attachment, dict):
281
+ url = attachment.get('url') or attachment.get('link') or attachment.get('file_url')
282
+ file_type = attachment.get('type', '').lower()
283
+ else:
284
+ url = attachment
285
+ file_type = ''
286
+
287
+ if not url:
288
+ continue
289
+
290
+ # Download the file
291
+ file_path = download_attachment(url, temp_dir)
292
+ if not file_path:
293
+ continue
294
+
295
+ # Categorize based on extension or type
296
+ file_ext = Path(file_path).suffix.lower()
297
+
298
+ if file_type:
299
+ if 'image' in file_type or file_ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']:
300
+ image_files.append(file_path)
301
+ elif 'audio' in file_type or file_ext in ['.mp3', '.wav', '.m4a', '.ogg', '.flac']:
302
+ audio_files.append(file_path)
303
+ elif 'python' in file_type or 'code' in file_type or file_ext in ['.py', '.txt']:
304
+ code_files.append(file_path)
305
+ else:
306
+ # Auto-detect based on extension
307
+ if file_ext in ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp']:
308
+ image_files.append(file_path)
309
+ elif file_ext in ['.mp3', '.wav', '.m4a', '.ogg', '.flac']:
310
+ audio_files.append(file_path)
311
+ elif file_ext in ['.py', '.txt']:
312
+ code_files.append(file_path)
313
+
314
+ if self.debug:
315
+ print(f"Downloaded attachments: {len(image_files)} images, {len(audio_files)} audio, {len(code_files)} code files")
316
+
317
+ return image_files, audio_files, code_files
318
+
319
+ def _process_attachments(self, image_files: List[str] = None, audio_files: List[str] = None, code_files: List[str] = None) -> str:
320
  """
321
+ Process all types of attachments and return their content as text.
322
  """
323
+ attachment_content = []
324
+
325
+ # Process code files
326
+ if code_files:
327
+ for code_file in code_files:
328
+ if code_file and os.path.exists(code_file):
329
+ try:
330
+ # First, include the raw code content (truncated)
331
+ with open(code_file, 'r', encoding='utf-8') as f:
332
+ code_content = f.read()
333
+
334
+ if len(code_content) > 1000:
335
+ code_preview = code_content[:1000] + "\n... (truncated)"
336
+ else:
337
+ code_preview = code_content
338
+
339
+ attachment_content.append(f"Code File Content:\n```python\n{code_preview}\n```")
340
+
341
+ # Then add analysis
342
+ code_analysis = self.code_tool.analyze_code(code_file)
343
+ attachment_content.append(f"Code Analysis: {code_analysis}")
344
+
345
+ except Exception as e:
346
+ attachment_content.append(f"Error processing code file {code_file}: {e}")
347
 
348
  # Process images
349
  if image_files:
 
352
  try:
353
  # Analyze the image
354
  image_description = self.image_tool.analyze_image(image_file)
355
+ attachment_content.append(f"Image Analysis: {image_description}")
356
 
357
  # Try to extract text from image
358
  extracted_text = self.image_tool.extract_text_from_image(image_file)
359
  if extracted_text and "No text found" not in extracted_text:
360
+ attachment_content.append(f"Text from Image: {extracted_text}")
361
 
362
  except Exception as e:
363
+ attachment_content.append(f"Error processing image {image_file}: {e}")
364
 
365
  # Process audio files
366
  if audio_files:
 
369
  try:
370
  # Transcribe the audio
371
  transcription = self.audio_tool.transcribe_audio(audio_file)
372
+ attachment_content.append(f"Audio Transcription: {transcription}")
373
 
374
  except Exception as e:
375
+ attachment_content.append(f"Error processing audio {audio_file}: {e}")
376
 
377
+ return "\n\n".join(attachment_content) if attachment_content else ""
378
 
379
+ def _should_search(self, question: str, attachment_context: str = "") -> bool:
380
  """
381
+ Use LLM to determine if search is needed for the question, considering attachment context.
382
  Returns True if search is recommended, False otherwise.
383
  """
384
  decision_prompt = f"""Analyze this question and decide if it requires real-time information, recent data, or specific facts that might not be in your training data.
 
399
  - Definitions of well-established concepts
400
  - How-to instructions for common tasks
401
  - Creative writing or opinion-based responses
402
+ - Questions that can be answered from attached files (code, images, audio)
403
+ - Code analysis, debugging, or explanation questions
404
+ - Questions about uploaded content
405
 
406
  Question: "{question}"
407
 
408
+ {f"Attachment Context Available: {attachment_context[:500]}..." if attachment_context else "No attachment context available."}
409
 
410
  Respond with only "SEARCH" or "NO_SEARCH" followed by a brief reason (max 20 words).
411
 
412
  Example responses:
413
  - "SEARCH - Current weather data needed"
414
  - "NO_SEARCH - Mathematical concept, general knowledge sufficient"
415
+ - "NO_SEARCH - Can be answered from attached code/image content"
416
  """
417
 
418
  try:
 
429
 
430
  except Exception as e:
431
  if self.debug:
432
+ print(f"Error in search decision: {e}, defaulting to no search for attachment questions")
433
+ # Default to no search if decision fails and there are attachments
434
+ return len(attachment_context) == 0
435
 
436
+ def _answer_with_llm(self, question: str, attachment_context: str = "") -> str:
437
  """
438
+ Generate answer using LLM without search, considering attachment context.
439
  """
440
+ context_section = f"\n\nAttachment Context:\n{attachment_context}" if attachment_context else ""
441
 
442
  answer_prompt = f"""You are a general AI assistant. I will ask you a question. YOUR ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string. Do not add a dot after the numbers.
443
 
 
454
  except Exception as e:
455
  return f"Sorry, I encountered an error generating the response: {e}"
456
 
457
+ def _answer_with_search(self, question: str, attachment_context: str = "") -> str:
458
  """
459
+ Generate answer using search results and LLM, considering attachment context.
460
  """
461
  try:
462
  # Perform search
 
467
  print(f"Search results type: {type(search_results)}")
468
 
469
  if not search_results:
470
+ return "No search results found. Let me try to answer based on my knowledge:\n\n" + self._answer_with_llm(question, attachment_context)
471
 
472
  # Format search results - handle different result formats
473
  if isinstance(search_results, str):
 
488
 
489
  search_context = "\n\n".join(formatted_results)
490
 
491
+ # Generate answer using search context and attachment context
492
+ context_section = f"\n\nAttachment Context:\n{attachment_context}" if attachment_context else ""
493
 
494
  answer_prompt = f"""You are a general AI assistant. I will ask you a question. Based on the search results and context section below, provide an answer to the question. If the search results don't fully answer the question, you can supplement with your general knowledge. Do not add dot if your answer is a number.
495
  Your ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise. If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.
 
529
  return "Search completed but no usable results found."
530
 
531
  except Exception as e:
532
+ return f"Search failed: {e}. Let me try to answer based on my knowledge:\n\n" + self._answer_with_llm(question, attachment_context)
533
+
534
+ def process_question_with_attachments(self, question_data: dict) -> str:
535
+ """
536
+ Process a question that may have attachments.
537
+ """
538
+ question_text = question_data.get('question', '')
539
+
540
+ if self.debug:
541
+ print(f"Processing question with potential attachments: {question_text[:100]}...")
542
+
543
+ try:
544
+ # Detect and download attachments
545
+ image_files, audio_files, code_files = self._detect_and_download_attachments(question_data)
546
+
547
+ # Process attachments to get context
548
+ attachment_context = self._process_attachments(image_files, audio_files, code_files)
549
+
550
+ if self.debug and attachment_context:
551
+ print(f"Attachment context: {attachment_context[:200]}...")
552
+
553
+ # Decide whether to search
554
+ if self._should_search(question_text, attachment_context):
555
+ if self.debug:
556
+ print("Using search-based approach")
557
+ answer = self._answer_with_search(question_text, attachment_context)
558
+ else:
559
+ if self.debug:
560
+ print("Using LLM-only approach")
561
+ answer = self._answer_with_llm(question_text, attachment_context)
562
+
563
+ # Cleanup temporary files
564
+ if image_files or audio_files or code_files:
565
+ try:
566
+ all_files = image_files + audio_files + code_files
567
+ temp_dirs = set(os.path.dirname(f) for f in all_files)
568
+ for temp_dir in temp_dirs:
569
+ import shutil
570
+ shutil.rmtree(temp_dir, ignore_errors=True)
571
+ except Exception as cleanup_error:
572
+ if self.debug:
573
+ print(f"Cleanup error: {cleanup_error}")
574
+
575
+ except Exception as e:
576
+ answer = f"Sorry, I encountered an error: {e}"
577
+
578
+ if self.debug:
579
+ print(f"Agent returning answer: {answer[:100]}...")
580
+
581
+ return answer
582
 
583
  def __call__(self, question: str, image_files: List[str] = None, audio_files: List[str] = None) -> str:
584
  """
585
+ Main entry point for manual testing - process media files and generate response.
586
  """
587
  if self.debug:
588
  print(f"Agent received question: {question}")
 
595
 
596
  try:
597
  # Process media files first
598
+ attachment_context = self._process_attachments(image_files, audio_files, [])
599
 
600
+ if self.debug and attachment_context:
601
+ print(f"Media context: {attachment_context[:200]}...")
602
 
603
  # Decide whether to search
604
+ if self._should_search(question, attachment_context):
605
  if self.debug:
606
  print("Using search-based approach")
607
+ answer = self._answer_with_search(question, attachment_context)
608
  else:
609
  if self.debug:
610
  print("Using LLM-only approach")
611
+ answer = self._answer_with_llm(question, attachment_context)
612
 
613
  except Exception as e:
614
  answer = f"Sorry, I encountered an error: {e}"
 
641
  # Create DataFrame for display
642
  display_data = []
643
  for item in questions_data:
644
+ # Check for attachments
645
+ has_attachments = False
646
+ attachment_info = ""
647
+
648
+ # Check various fields for attachments
649
+ attachment_fields = ['attachments', 'files', 'media', 'resources']
650
+ for field in attachment_fields:
651
+ if field in item and item[field]:
652
+ has_attachments = True
653
+ if isinstance(item[field], list):
654
+ attachment_info += f"{len(item[field])} {field}, "
655
+ else:
656
+ attachment_info += f"{field}, "
657
+
658
+ # Check if question contains URLs
659
+ question_text = item.get("question", "")
660
+ if 'http' in question_text:
661
+ has_attachments = True
662
+ attachment_info += "URLs in text, "
663
+
664
+ if attachment_info:
665
+ attachment_info = attachment_info.rstrip(", ")
666
+
667
  display_data.append({
668
  "Task ID": item.get("task_id", "Unknown"),
669
+ "Question": question_text[:100] + "..." if len(question_text) > 100 else question_text,
670
+ "Has Attachments": "Yes" if has_attachments else "No",
671
+ "Attachment Info": attachment_info
672
  })
673
 
674
  df = pd.DataFrame(display_data)
675
+
676
+ attachment_count = sum(1 for item in display_data if item["Has Attachments"] == "Yes")
677
+ status_msg = f"Successfully fetched {len(questions_data)} questions. {attachment_count} questions have attachments. Ready to generate answers."
678
 
679
  return status_msg, df
680
 
 
700
  agent = IntelligentAgent(debug=True, model_name=model_name)
701
  cached_answers = {}
702
 
703
+ for i, question_data in enumerate(cached_questions):
704
  if not processing_status["is_processing"]: # Check if cancelled
705
  break
706
 
707
+ task_id = question_data.get("task_id")
708
+ question_text = question_data.get("question")
709
 
710
  if not task_id or question_text is None:
711
  continue
712
 
713
  try:
714
+ # Use the new method that handles attachments
715
+ answer = agent.process_question_with_attachments(question_data)
716
  cached_answers[task_id] = {
717
  "question": question_text,
718
  "answer": answer
 
757
  thread.start()
758
 
759
  return f"Answer generation started using {model_choice}. Check progress."
760
+
761
+
762
  def get_generation_progress():
763
  """
764
  Get the current progress of answer generation.