ekabaruh commited on
Commit
288bc9b
·
verified ·
1 Parent(s): 4a65d4c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +7 -486
app.py CHANGED
@@ -3,500 +3,22 @@ import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
6
- import re
7
- import base64
8
- from io import BytesIO
9
- from typing import Dict, List, Any, Optional, Tuple
10
- from langchain_core.messages import AIMessage, HumanMessage
11
- from langchain_openai import ChatOpenAI
12
- from langgraph.graph import StateGraph, END
13
- from pydantic import BaseModel, Field
14
- import json
15
- import math
16
- from dotenv import load_dotenv
17
- from PIL import Image
18
- import pytesseract
19
- import youtube_dl
20
- from youtube_transcript_api import YouTubeTranscriptApi
21
-
22
- # Load environment variables from .env file if present
23
- load_dotenv()
24
 
25
  # (Keep Constants as is)
26
  # --- Constants ---
27
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
28
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
29
 
30
  # --- Basic Agent Definition ---
31
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
32
  class BasicAgent:
33
- """
34
- An agent designed for the Gaia Benchmark, implementing a multi-step reasoning approach.
35
-
36
- This agent:
37
- 1. Analyzes the question to determine its type
38
- 2. Performs step-by-step reasoning
39
- 3. Uses tools when needed (calculator, web search, image analysis, file processing)
40
- 4. Formats the final answer according to Gaia Benchmark requirements
41
- """
42
-
43
- def __init__(self, model_name: str = "gpt-4.1", api_key: str = None):
44
- """
45
- Initialize the BasicAgent with the specified LLM.
46
-
47
- Args:
48
- model_name: The name of the model to use (defaults to gpt-4.1)
49
- api_key: OpenAI API key (defaults to environment variable)
50
- """
51
- # Use the provided API key or fall back to the environment variable
52
- api_key = api_key or OPENAI_API_KEY
53
-
54
- if not api_key:
55
- print("Warning: No OpenAI API key provided. Make sure to set OPENAI_API_KEY environment variable.")
56
-
57
- self.llm = ChatOpenAI(
58
- model=model_name,
59
- temperature=0,
60
- api_key=api_key
61
- )
62
-
63
- # Initialize vision model if needed for image analysis
64
- try:
65
- self.vision_model = ChatOpenAI(
66
- model="gpt-4-vision-preview",
67
- temperature=0,
68
- api_key=api_key
69
- )
70
- print("Vision model initialized successfully")
71
- except Exception as e:
72
- print(f"Warning: Could not initialize vision model: {e}")
73
- self.vision_model = None
74
-
75
- print(f"BasicAgent initialized with model: {model_name}")
76
-
77
- def analyze_question(self, question: str):
78
- """Analyze the question to determine its type and approach."""
79
- prompt = [
80
- HumanMessage(content=f"""
81
- You are an expert problem analyzer. Examine the following question to determine its type
82
- and the tools needed to answer it effectively.
83
-
84
- Question: {question}
85
-
86
- Determine what tools are needed for this question:
87
- 1. Mathematical calculation? If yes, extract the mathematical expression.
88
- 2. Web search for factual information? If yes, determine the search query.
89
- 3. Image processing? Is there a reference to an image or video that needs analysis?
90
- 4. File processing? Is there a reference to an attached file (Excel, code, etc.)?
91
- 5. YouTube video? Is there a YouTube URL that needs transcript analysis?
92
- 6. Complex reasoning? Does this question require multi-step logical reasoning?
93
- 7. Backward text? Does the question contain text that needs to be reversed?
94
-
95
- Respond in JSON format:
96
- {{
97
- "thought": "your analysis of the question",
98
- "need_calculator": true/false,
99
- "math_expression": "expression to calculate (if needed, otherwise null)",
100
- "need_websearch": true/false,
101
- "search_query": "search query (if needed, otherwise null)",
102
- "need_image_processing": true/false,
103
- "need_file_processing": true/false,
104
- "file_type": "excel/code/other (if needed, otherwise null)",
105
- "has_youtube_url": true/false,
106
- "youtube_url": "URL (if found, otherwise null)",
107
- "need_complex_reasoning": true/false,
108
- "has_backward_text": true/false,
109
- "backward_text": "the text to reverse (if needed, otherwise null)"
110
- }}
111
- """)
112
- ]
113
-
114
- try:
115
- response = self.llm.invoke(prompt)
116
- try:
117
- result = json.loads(response.content)
118
- thought = f"Analysis: {result.get('thought', 'No analysis provided')}"
119
-
120
- # Extract all the analysis results
121
- need_calculator = result.get('need_calculator', False)
122
- math_expression = result.get('math_expression')
123
- need_websearch = result.get('need_websearch', False)
124
- search_query = result.get('search_query')
125
- need_image_processing = result.get('need_image_processing', False)
126
- need_file_processing = result.get('need_file_processing', False)
127
- file_type = result.get('file_type')
128
- has_youtube_url = result.get('has_youtube_url', False)
129
- youtube_url = result.get('youtube_url')
130
- need_complex_reasoning = result.get('need_complex_reasoning', False)
131
- has_backward_text = result.get('has_backward_text', False)
132
- backward_text = result.get('backward_text')
133
-
134
- return {
135
- "thought": thought,
136
- "need_calculator": need_calculator,
137
- "math_expression": math_expression,
138
- "need_websearch": need_websearch,
139
- "search_query": search_query,
140
- "need_image_processing": need_image_processing,
141
- "need_file_processing": need_file_processing,
142
- "file_type": file_type,
143
- "has_youtube_url": has_youtube_url,
144
- "youtube_url": youtube_url,
145
- "need_complex_reasoning": need_complex_reasoning,
146
- "has_backward_text": has_backward_text,
147
- "backward_text": backward_text
148
- }
149
- except json.JSONDecodeError as e:
150
- # Fallback in case of JSON parsing error
151
- return {
152
- "thought": f"Couldn't parse the analysis response as JSON: {e}",
153
- "need_calculator": False,
154
- "math_expression": None,
155
- "need_websearch": False,
156
- "search_query": None,
157
- "need_image_processing": False,
158
- "need_file_processing": False,
159
- "file_type": None,
160
- "has_youtube_url": False,
161
- "youtube_url": None,
162
- "need_complex_reasoning": False,
163
- "has_backward_text": False,
164
- "backward_text": None
165
- }
166
- except Exception as e:
167
- # Fallback for LLM invocation error
168
- return {
169
- "thought": f"Error invoking language model: {str(e)}",
170
- "need_calculator": False,
171
- "math_expression": None,
172
- "need_websearch": False,
173
- "search_query": None,
174
- "need_image_processing": False,
175
- "need_file_processing": False,
176
- "file_type": None,
177
- "has_youtube_url": False,
178
- "youtube_url": None,
179
- "need_complex_reasoning": False,
180
- "has_backward_text": False,
181
- "backward_text": None
182
- }
183
-
184
- def solve_math(self, math_expression):
185
- """Evaluate the mathematical expression."""
186
- try:
187
- # Simple math expression evaluator
188
- result = eval(math_expression)
189
- return f"Calculated {math_expression} = {result}", result
190
- except Exception as e:
191
- return f"Error calculating expression: {e}", None
192
-
193
- def web_search(self, query):
194
- """Perform a web search for factual information."""
195
- try:
196
- # Use DuckDuckGo search API (no key required)
197
- search_url = f"https://api.duckduckgo.com/?q={query}&format=json"
198
- response = requests.get(search_url, timeout=10)
199
- response.raise_for_status()
200
- search_data = response.json()
201
-
202
- # Extract and format the search results
203
- results = []
204
-
205
- # Abstract (main result)
206
- if search_data.get('Abstract'):
207
- results.append(f"Main result: {search_data.get('Abstract')}")
208
-
209
- # Related topics
210
- for topic in search_data.get('RelatedTopics', [])[:5]: # Limit to first 5 results
211
- if 'Text' in topic:
212
- results.append(topic['Text'])
213
-
214
- if not results:
215
- return f"No search results found for '{query}'", None
216
-
217
- search_results = "\n".join(results)
218
- return f"Search results for '{query}':\n{search_results}", search_results
219
- except requests.exceptions.RequestException as e:
220
- return f"Error during web search: {e}", None
221
- except Exception as e:
222
- return f"Error processing search results: {e}", None
223
-
224
- def process_youtube_video(self, youtube_url):
225
- """Extract and process information from a YouTube video."""
226
- try:
227
- # Extract video ID from URL
228
- video_id = None
229
- if "youtu.be" in youtube_url:
230
- video_id = youtube_url.split("/")[-1].split("?")[0]
231
- elif "youtube.com/watch" in youtube_url:
232
- video_id = re.search(r"v=([^&]+)", youtube_url).group(1)
233
-
234
- if not video_id:
235
- return f"Could not extract video ID from URL: {youtube_url}", None
236
-
237
- # Get video transcript
238
- try:
239
- transcript_list = YouTubeTranscriptApi.get_transcript(video_id)
240
- transcript_text = " ".join([item['text'] for item in transcript_list])
241
-
242
- # Get video metadata
243
- with youtube_dl.YoutubeDL({'quiet': True}) as ydl:
244
- info = ydl.extract_info(youtube_url, download=False)
245
- title = info.get('title', 'Unknown title')
246
- description = info.get('description', 'No description')
247
-
248
- video_info = {
249
- 'title': title,
250
- 'description': description,
251
- 'transcript': transcript_text
252
- }
253
-
254
- return f"Successfully extracted information from YouTube video: {title}", video_info
255
- except Exception as e:
256
- # If transcript fails, try to get just metadata
257
- try:
258
- with youtube_dl.YoutubeDL({'quiet': True}) as ydl:
259
- info = ydl.extract_info(youtube_url, download=False)
260
- title = info.get('title', 'Unknown title')
261
- description = info.get('description', 'No description')
262
-
263
- video_info = {
264
- 'title': title,
265
- 'description': description,
266
- 'transcript': "Transcript unavailable"
267
- }
268
-
269
- return f"Extracted partial information from YouTube video (no transcript): {title}", video_info
270
- except Exception as e2:
271
- return f"Error getting YouTube video information: {e}, then {e2}", None
272
- except Exception as e:
273
- return f"Error processing YouTube video: {e}", None
274
-
275
- def process_backward_text(self, backward_text):
276
- """Reverse backward text to get the intended meaning."""
277
- try:
278
- forward_text = backward_text[::-1]
279
- return f"Reversed text: '{backward_text}' → '{forward_text}'", forward_text
280
- except Exception as e:
281
- return f"Error reversing text: {e}", None
282
-
283
- def reasoning(self, question, context=""):
284
- """Perform step-by-step reasoning to solve the problem."""
285
- prompt = [
286
- HumanMessage(content=f"""
287
- You are a general AI assistant. Solve the following question using careful step-by-step reasoning.
288
-
289
- Question: {question}
290
-
291
- {context}
292
-
293
- Provide a detailed step-by-step solution showing your thought process. Be methodical
294
- and thorough. DO NOT include a final answer yet, just your reasoning.
295
- """)
296
- ]
297
-
298
- try:
299
- response = self.llm.invoke(prompt)
300
- return response.content
301
- except Exception as e:
302
- return f"Error during reasoning step: {str(e)}"
303
-
304
- def complex_reasoning(self, question, context=""):
305
- """Perform more intensive reasoning for complex questions."""
306
- prompt = [
307
- HumanMessage(content=f"""
308
- You are an advanced reasoning agent. Solve the following complex question using multi-step reasoning.
309
- Break down the problem into logical steps and consider different approaches.
310
-
311
- Question: {question}
312
-
313
- {context}
314
-
315
- Start by analyzing what the question is really asking for. Consider different interpretations if ambiguous.
316
- Identify key entities, relationships, or constraints mentioned in the question.
317
- Work through the solution step-by-step, making your reasoning explicit at each stage.
318
- If appropriate, use diagrams, tables, or mathematical expressions to organize your reasoning.
319
-
320
- Provide your detailed multi-step reasoning. DO NOT include a final answer yet.
321
- """)
322
- ]
323
-
324
- try:
325
- response = self.llm.invoke(prompt)
326
- first_pass = response.content
327
-
328
- # Follow-up with a more focused analysis
329
- prompt = [
330
- HumanMessage(content=f"""
331
- You are an advanced reasoning agent. Review and refine the following reasoning:
332
-
333
- Question: {question}
334
-
335
- Initial reasoning:
336
- {first_pass}
337
-
338
- Identify any gaps, inconsistencies, or areas where the reasoning could be improved.
339
- Apply critical thinking to ensure the logic is sound and addresses all aspects of the question.
340
- Offer improved or alternative reasoning paths if appropriate.
341
-
342
- Provide your refined reasoning. DO NOT include a final answer yet.
343
- """)
344
- ]
345
-
346
- try:
347
- response = self.llm.invoke(prompt)
348
- return f"{first_pass}\n\nRefined analysis:\n{response.content}"
349
- except Exception as e:
350
- return first_pass
351
- except Exception as e:
352
- return f"Error during complex reasoning step: {str(e)}"
353
-
354
- def format_answer(self, question, thoughts, answer_context=None):
355
- """Format the final answer according to Gaia Benchmark requirements."""
356
- all_thoughts = "\n".join(thoughts)
357
-
358
- context_prompt = ""
359
- if answer_context:
360
- context_prompt = f"""
361
- Additional context for final answer formulation:
362
- {answer_context}
363
- """
364
-
365
- prompt = [
366
- HumanMessage(content=f"""
367
- You are a general AI assistant. Based on the following reasoning, provide a concise
368
- final answer to the question. The final answer format is extremely important.
369
-
370
- Question: {question}
371
-
372
- Reasoning:
373
- {all_thoughts}
374
-
375
- {context_prompt}
376
-
377
- Format your answer following these strict rules:
378
- - For numbers: No commas in large numbers, no units unless explicitly requested.
379
- - For strings: Minimal, no articles, no abbreviations.
380
- - For lists: Comma-separated values.
381
- - Use digits in words unless otherwise stated.
382
- - If the answer is a country code, use the standard 3-letter format (e.g., "USA", "GBR").
383
- - For names, provide exactly what was asked for (first name, last name, or full name).
384
-
385
- IMPORTANT: Your answer must be concise, precise and EXACT. Do not include explanations.
386
- Do not include a prefix like "FINAL ANSWER:" in your response. Just provide the exact answer.
387
-
388
- For example, if the answer is "5", just respond with "5".
389
- If the answer is "New York", just respond with "New York".
390
- """)
391
- ]
392
-
393
- try:
394
- response = self.llm.invoke(prompt)
395
- answer = response.content.strip()
396
-
397
- # Remove any "FINAL ANSWER:" prefix if it exists
398
- if "FINAL ANSWER:" in answer:
399
- parts = answer.split("FINAL ANSWER:")
400
- answer = parts[1].strip()
401
-
402
- return answer
403
- except Exception as e:
404
- return f"Error formatting answer: {str(e)}"
405
-
406
  def __call__(self, question: str) -> str:
407
- """
408
- Process the question and return a formatted response according to Gaia requirements.
409
- """
410
  print(f"Agent received question (first 50 chars): {question[:50]}...")
411
-
412
- # Initialize an empty list to collect thoughts
413
- thoughts = []
414
-
415
- try:
416
- # Step 1: Analyze the question
417
- analysis = self.analyze_question(question)
418
- thoughts.append(analysis["thought"])
419
-
420
- # Initialize context variables
421
- all_context = ""
422
- answer_context = ""
423
-
424
- # Step 2: Process backward text if needed
425
- if analysis["has_backward_text"] and analysis["backward_text"]:
426
- backward_thought, forward_text = self.process_backward_text(analysis["backward_text"])
427
- thoughts.append(backward_thought)
428
- if forward_text:
429
- all_context += f"\nBackward text analysis:\n{backward_text} (backward) = {forward_text} (forward)\n"
430
- # Replace the backward text in the question with forward text for further processing
431
- question = question.replace(analysis["backward_text"], forward_text)
432
-
433
- # Step 3: Calculate if needed
434
- if analysis["need_calculator"] and analysis["math_expression"]:
435
- calc_thought, calc_result = self.solve_math(analysis["math_expression"])
436
- thoughts.append(calc_thought)
437
- if calc_result is not None:
438
- all_context += f"\nMathematical calculation:\nExpression: {analysis['math_expression']}\nResult: {calc_result}\n"
439
-
440
- # Step 4: Perform web search if needed
441
- if analysis["need_websearch"] and analysis["search_query"]:
442
- search_thought, search_result = self.web_search(analysis["search_query"])
443
- thoughts.append(search_thought)
444
- if search_result:
445
- all_context += f"\nWeb search results:\nQuery: {analysis['search_query']}\nResults: {search_result}\n"
446
-
447
- # Step 5: Process YouTube video if present
448
- if analysis["has_youtube_url"] and analysis["youtube_url"]:
449
- youtube_thought, youtube_info = self.process_youtube_video(analysis["youtube_url"])
450
- thoughts.append(youtube_thought)
451
- if youtube_info:
452
- video_context = f"\nYouTube video information:\nTitle: {youtube_info.get('title')}\n"
453
-
454
- # Add description if not too long
455
- description = youtube_info.get('description', '')
456
- if description and len(description) > 500:
457
- description = description[:500] + "... [truncated]"
458
- video_context += f"Description: {description}\n"
459
-
460
- # Add transcript if available (truncated if too long)
461
- transcript = youtube_info.get('transcript', '')
462
- if transcript and transcript != "Transcript unavailable":
463
- if len(transcript) > 1000:
464
- transcript = transcript[:1000] + "... [truncated]"
465
- video_context += f"Transcript excerpt: {transcript}\n"
466
-
467
- all_context += video_context
468
-
469
- # Step 6: Handle image/file processing references with appropriate messages
470
- if analysis["need_image_processing"]:
471
- image_thought = "Note: Referenced image not available for processing."
472
- thoughts.append(image_thought)
473
- all_context += "\n" + image_thought + "\n"
474
- answer_context += "\nIf the question references an image or visual content that is not available, respond with an appropriate message indicating you cannot access the image.\n"
475
-
476
- if analysis["need_file_processing"]:
477
- file_type = analysis["file_type"] or "file"
478
- file_thought = f"Note: Referenced {file_type} not available for processing."
479
- thoughts.append(file_thought)
480
- all_context += "\n" + file_thought + "\n"
481
- answer_context += f"\nIf the question references a {file_type} that is not available, respond with an appropriate message indicating you cannot access the {file_type}.\n"
482
-
483
- # Step 7: Perform reasoning (complex or standard)
484
- if analysis["need_complex_reasoning"]:
485
- reasoning_output = self.complex_reasoning(question, all_context)
486
- else:
487
- reasoning_output = self.reasoning(question, all_context)
488
-
489
- thoughts.append(reasoning_output)
490
-
491
- # Step 8: Format final answer
492
- final_answer = self.format_answer(question, thoughts, answer_context)
493
-
494
- print(f"Agent returning answer: {final_answer}")
495
- return final_answer
496
- except Exception as e:
497
- print(f"Error running agent workflow: {e}")
498
- # Fallback response - return just the error message without the prefix
499
- return "Error"
500
 
501
  def run_and_submit_all( profile: gr.OAuthProfile | None):
502
  """
@@ -519,7 +41,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
519
 
520
  # 1. Instantiate Agent ( modify this part to create your agent)
521
  try:
522
- agent = BasicAgent()
523
  except Exception as e:
524
  print(f"Error instantiating agent: {e}")
525
  return f"Error initializing agent: {e}", None
@@ -589,7 +111,6 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
589
  f"Message: {result_data.get('message', 'No message received.')}"
590
  )
591
  print("Submission successful.")
592
- print(result_data)
593
  results_df = pd.DataFrame(results_log)
594
  return final_status, results_df
595
  except requests.exceptions.HTTPError as e:
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ from langgraph_agent import LangGraphAgent
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  # (Keep Constants as is)
9
  # --- Constants ---
10
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
11
 
12
  # --- Basic Agent Definition ---
13
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
14
  class BasicAgent:
15
+ def __init__(self):
16
+ print("BasicAgent initialized.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  def __call__(self, question: str) -> str:
 
 
 
18
  print(f"Agent received question (first 50 chars): {question[:50]}...")
19
+ fixed_answer = "This is a default answer."
20
+ print(f"Agent returning fixed answer: {fixed_answer}")
21
+ return fixed_answer
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  def run_and_submit_all( profile: gr.OAuthProfile | None):
24
  """
 
41
 
42
  # 1. Instantiate Agent ( modify this part to create your agent)
43
  try:
44
+ agent = LangGraphAgent()
45
  except Exception as e:
46
  print(f"Error instantiating agent: {e}")
47
  return f"Error initializing agent: {e}", None
 
111
  f"Message: {result_data.get('message', 'No message received.')}"
112
  )
113
  print("Submission successful.")
 
114
  results_df = pd.DataFrame(results_log)
115
  return final_status, results_df
116
  except requests.exceptions.HTTPError as e: