DakshChaudhary commited on
Commit
1b34f03
·
1 Parent(s): 24d5bb0

Refactored + Added prompts + Added Tools (Calculator, FileDownloader, ImageReader, Pandas, WebSearch) + NebiusAI inference added

Browse files
__pycache__/agent.cpython-313.pyc ADDED
Binary file (2.5 kB). View file
 
agent.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import inspect
3
+ import logging
4
+ from llama_index.core.agent import ReActAgent
5
+ from agent_models.models import get_language_model
6
+ from agent_tools.WebSearchTool import web_search_tools
7
+ from agent_tools.FileDownloaderTool import get_downloader_tool
8
+ from agent_tools.ImageReaderTool import get_image_interpreter_tool
9
+ from agent_tools.CalculatorTool import get_calculator_tool
10
+ from agent_tools.PandasTool import get_pandas_tool
11
+ from agent_prompts.SystemPrompt import gaia_system_prompt
12
+
13
+ #Logging
14
+ logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
15
+ logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
16
+
17
+
18
+ # Agent Definition
19
+ class GaiaAgent:
20
+ def __init__(self):
21
+ # print(f"ReAct Agent signature--------------------------------------- \n {inspect.signature(ReActAgent.from_tools)} \n ReAct Agent signature--------------------------------------- \n" )
22
+ list_of_search_tools = web_search_tools()
23
+ list_of_other_tools = [get_downloader_tool(), get_image_interpreter_tool(), get_calculator_tool(), get_pandas_tool()]
24
+
25
+ self.llm = get_language_model()
26
+ self.tools = list_of_search_tools + list_of_other_tools
27
+ self.agent = ReActAgent.from_tools(tools=self.tools, llm=self.llm, context=gaia_system_prompt, verbose=True)
28
+
29
+ async def __call__(self, question: str) -> str:
30
+ response_object = self.agent.chat(question)
31
+ full_response_text = str(response_object)
32
+
33
+ final_answer_prefix = "FINAL ANSWER:"
34
+ if final_answer_prefix in full_response_text:
35
+ clean_answer = full_response_text.split(final_answer_prefix, 1)[1].strip()
36
+ return clean_answer
37
+ else:
38
+ return full_response_text
agent_models/__pycache__/models.cpython-313.pyc ADDED
Binary file (1.65 kB). View file
 
agent_models/models.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from llama_index.llms.nebius import NebiusLLM
3
+ from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
4
+ from openai import OpenAI
5
+
6
+ # NebiusAI API Key
7
+ os.environ["NEBIUS_API_KEY"] = ""
8
+
9
+ # Model config variable
10
+ llm_Id = "Qwen/Qwen2.5-32B-Instruct"
11
+
12
+ def get_language_model():
13
+ '''Initializes and returns the LLM for Agent's core functionality'''
14
+ return NebiusLLM(api_key=os.getenv("NEBIUS_API_KEY"), model=llm_Id) # To use HuggingFaceInferenceAPI -> return HuggingFaceInferenceAPI(model_name=llm_Id)
15
+
16
+ def get_vision_model_client():
17
+ '''Initializes and returns the vision model for analyzing images'''
18
+
19
+ return OpenAI(
20
+ api_key=os.getenv("NEBIUS_API_KEY"),
21
+ base_url="https://api.studio.nebius.com/v1/"
22
+ )
agent_prompts/SystemPrompt.py CHANGED
@@ -49,4 +49,12 @@ Action: calculate["20 divided by 80 times 100"]
49
  Observation: 20/80 × 100 = 25.
50
  Thought: That is twenty‑five percent.
51
  FINAL ANSWER: twenty-five percent
52
- '''
 
 
 
 
 
 
 
 
 
49
  Observation: 20/80 × 100 = 25.
50
  Thought: That is twenty‑five percent.
51
  FINAL ANSWER: twenty-five percent
52
+ '''
53
+
54
+
55
+ vision_model_system_prompt = '''
56
+ You are an expert image analyst. Describe the contents of this image in detail. Focus on the minute details present in the image that might be important to the overall context.
57
+ If it is a chess board, describe the exact position of all pieces for both black and white (Pay very close attention to the numbers and letters to determine position).
58
+
59
+ You MUST ALWAYS VERIFY that your output is correct before sending it to the user.
60
+ '''
agent_prompts/__pycache__/SystemPrompt.cpython-313.pyc CHANGED
Binary files a/agent_prompts/__pycache__/SystemPrompt.cpython-313.pyc and b/agent_prompts/__pycache__/SystemPrompt.cpython-313.pyc differ
 
agent_tools/CalculatorTool.py ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numexpr
2
+ from llama_index.core.tools import FunctionTool
3
+
4
+ def calculate(expression: str) -> str:
5
+ """
6
+ A safe calculator that evaluates a mathematical string expression.
7
+ This tool can handle complex expressions with parentheses, addition,
8
+ subtraction, multiplication, and division.
9
+ Args:
10
+ expression (str): The mathematical expression to evaluate (e.g., "2 * (3 + 4)").
11
+ """
12
+ print(f"Calculating expression: {expression}")
13
+ try:
14
+ # Use the numexpr library to safely evaluate the string.
15
+ result = numexpr.evaluate(expression).item()
16
+
17
+ # Return the result as a string for the agent to process.
18
+ return str(result)
19
+ except Exception as e:
20
+ # If the expression is invalid, return a descriptive error.
21
+ return f"Error: Invalid mathematical expression. Please check your syntax. Details: {e}"
22
+
23
+ def get_calculator_tool() -> FunctionTool:
24
+ """Initializes and returns our custom-built, safe calculator tool."""
25
+ return FunctionTool.from_defaults(
26
+ fn=calculate,
27
+ name="calculator",
28
+ description="A tool for evaluating mathematical expressions. Use this for any math calculations, like addition, subtraction, multiplication, division, etc. Example input: '(25 * 4) + 15 - 5'"
29
+ )
agent_tools/FileDownloaderTool.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+ from llama_index.core.tools import FunctionTool
4
+
5
+ BASE_API_URL = "https://agents-course-unit4-scoring.hf.space"
6
+
7
+ def download_file(task_id: str, file_name: str) -> str:
8
+ """
9
+ Downloads a file for a given task_id from the GAIA API.
10
+ Args:
11
+ task_id (str): The ID of the task to download the file for.
12
+ file_name (str): The name to save the file as.
13
+ Returns:
14
+ str: The local path to the downloaded file.
15
+ """
16
+
17
+ print(f"Attempting to download file for task: {task_id}")
18
+
19
+ # 1. Construct the full URL for the file endpoint.
20
+ file_url = f"{BASE_API_URL}/files/{task_id}"
21
+
22
+ # 2. Define the local directory to save downloads.
23
+ download_dir = "downloads"
24
+ os.makedirs(download_dir, exist_ok=True)
25
+
26
+ # 3. Construct the full local path for the file.
27
+ local_filepath = os.path.join(download_dir, file_name)
28
+
29
+ # 4. Make a GET request to the file_url.
30
+ try:
31
+ response = requests.get(file_url, timeout=20)
32
+ # This will raise an exception for bad status codes (like 404).
33
+ response.raise_for_status()
34
+
35
+ # 5. Save the content of the response to the local file.
36
+ with open(local_filepath, 'wb') as f:
37
+ f.write(response.content)
38
+
39
+ print(f"Successfully downloaded file to: {local_filepath}")
40
+
41
+ # 6. Return the local file path.
42
+ return local_filepath
43
+
44
+ except requests.exceptions.RequestException as e:
45
+ error_message = f"Failed to download file for task {task_id}. Error: {e}"
46
+ print(error_message)
47
+ return error_message
48
+
49
+
50
+ # Wrapper function to create the tool
51
+ def get_downloader_tool() -> FunctionTool:
52
+ return FunctionTool.from_defaults(
53
+ fn=download_file,
54
+ name="file_downloader",
55
+ description="A tool to download files associated with a specific task ID. Use this when a question mentions an image, audio file, or other document."
56
+ )
agent_tools/ImageReaderTool.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ from llama_index.core.tools import FunctionTool
4
+ from llama_index.readers.file.image import ImageReader
5
+ from agent_models.models import get_vision_model_client
6
+ from agent_prompts.SystemPrompt import vision_model_system_prompt
7
+
8
+ def get_image_description(image_path: str) -> str:
9
+ """
10
+ Analyzes a local image and returns a text description. This tool is used to "see" what is in an image file.
11
+ Args:
12
+ image_path (str): The local file path of the image to analyze.
13
+ """
14
+ try:
15
+ print(f"Analyzing image at path: {image_path}")
16
+
17
+ # Read and encode the image
18
+ with open(image_path, "rb") as img_file:
19
+ b64_image = base64.b64encode(img_file.read()).decode("utf-8")
20
+ b64_url = f"data:image/png;base64,{b64_image}"
21
+
22
+ # Get Nebius client
23
+ client = get_vision_model_client()
24
+
25
+ # Call Nebius API
26
+ response = client.chat.completions.create(
27
+ model="Qwen/Qwen2-VL-72B-Instruct",
28
+ messages=[
29
+ {
30
+ "role": "system",
31
+ "content": vision_model_system_prompt
32
+ },
33
+ {
34
+ "role": "user",
35
+ "content": [
36
+ {"type": "text", "text": "Here is an image."},
37
+ {"type": "image_url", "image_url": {"url": b64_url}}
38
+ ]
39
+ }
40
+ ]
41
+ )
42
+
43
+ description = response.choices[0].message.content
44
+ print(f"Vision model response: {description}")
45
+ return description
46
+
47
+ except Exception as e:
48
+ return f"Error analyzing image: {e}"
49
+
50
+ # Wrapper function to create the tool for our agent
51
+ def get_image_interpreter_tool() -> FunctionTool:
52
+ return FunctionTool.from_defaults(
53
+ fn=get_image_description,
54
+ name="image_interpreter",
55
+ description="A tool to analyze an image from a local file path and return a detailed text description. Use this to 'see' what is in an image file that has already been downloaded."
56
+ )
agent_tools/PandasTool.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from llama_index.experimental.query_engine import PandasQueryEngine
3
+ from llama_index.core.tools import FunctionTool
4
+ from agent_models.models import get_language_model
5
+
6
+ def analyze_spreadsheet(file_path: str, question: str) -> str:
7
+ """
8
+ Analyzes a spreadsheet (Excel/CSV) to answer a specific question about its data.
9
+ This is a powerful tool for data analysis on structured files.
10
+ Args:
11
+ file_path (str): The local path to the .xlsx or .csv file.
12
+ question (str): The question to ask about the data in the file.
13
+ """
14
+ print(f"Analyzing spreadsheet '{file_path}' for question: '{question}'")
15
+ try:
16
+ # Load the spreadsheet into a Pandas DataFrame
17
+ df = pd.read_excel(file_path)
18
+
19
+ # The PandasQueryEngine needs a LLM to translate natural language into Pandas commands.
20
+ llm = get_language_model()
21
+
22
+ # Create the query engine
23
+ query_engine = PandasQueryEngine(df=df, llm=llm, verbose=True)
24
+
25
+ # Ask the question and get the response
26
+ response = query_engine.query(question)
27
+
28
+ return str(response)
29
+
30
+ except Exception as e:
31
+ return f"Error analyzing spreadsheet: {e}"
32
+
33
+ # Wrapper function to create the LlamaIndex tool
34
+ def get_pandas_tool() -> FunctionTool:
35
+ return FunctionTool.from_defaults(
36
+ fn=analyze_spreadsheet,
37
+ name="spreadsheet_analyzer",
38
+ description="A tool to answer questions about data stored in a spreadsheet (.xlsx or .csv). It takes two arguments: the 'file_path' to the spreadsheet and the 'question' to ask about the data."
39
+ )
agent_tools/WebSearchTool.py CHANGED
@@ -6,6 +6,6 @@ from llama_index.tools.tavily_research import TavilyToolSpec
6
  def web_search_tools()->List[FunctionTool]:
7
  "Tool to search the web with Tavily (search + scraping)"
8
 
9
- os.environ["TAVILY_API_KEY"] = "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"
10
  tavily_spec = TavilyToolSpec(api_key=os.getenv("TAVILY_API_KEY"))
11
  return tavily_spec.to_tool_list()
 
6
  def web_search_tools()->List[FunctionTool]:
7
  "Tool to search the web with Tavily (search + scraping)"
8
 
9
+ os.environ["TAVILY_API_KEY"] = ""
10
  tavily_spec = TavilyToolSpec(api_key=os.getenv("TAVILY_API_KEY"))
11
  return tavily_spec.to_tool_list()
agent_tools/__pycache__/CalculatorTool.cpython-313.pyc ADDED
Binary file (1.65 kB). View file
 
agent_tools/__pycache__/FileDownloaderTool.cpython-313.pyc ADDED
Binary file (2.36 kB). View file
 
agent_tools/__pycache__/ImageReaderTool.cpython-313.pyc ADDED
Binary file (2.56 kB). View file
 
agent_tools/__pycache__/PandasTool.cpython-313.pyc ADDED
Binary file (1.91 kB). View file
 
agent_tools/__pycache__/WebSearchTool.cpython-313.pyc CHANGED
Binary files a/agent_tools/__pycache__/WebSearchTool.cpython-313.pyc and b/agent_tools/__pycache__/WebSearchTool.cpython-313.pyc differ
 
app.py CHANGED
@@ -1,35 +1,23 @@
1
- import logging
2
- import sys
3
- # logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
4
- # logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
5
-
6
  import os
7
- import gradio as gr
8
- import requests
9
  import inspect
 
 
 
10
  import pandas as pd
11
- from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI
12
- from llama_index.core.agent import ReActAgent
13
- from agent_tools.WebSearchTool import web_search_tools
14
- from agent_prompts.SystemPrompt import gaia_system_prompt
15
 
16
- # --- Constants ---
17
- DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
18
 
19
- # --- Basic Agent Definition ---
20
- class BasicAgent:
21
- def __init__(self):
22
- self.llm = HuggingFaceInferenceAPI(model_name = "Qwen/Qwen2.5-72B-Instruct")
23
- # print(f"ReAct Agent signature--------------------------------------- \n {inspect.signature(ReActAgent.from_tools)} \n ReAct Agent signature--------------------------------------- \n" )
24
- self.agent = ReActAgent.from_tools(tools=web_search_tools(), llm=self.llm, context=gaia_system_prompt, verbose=True)
25
-
26
- async def __call__(self, question: str) -> str:
27
- response = self.agent.chat(question)
28
- return str(response)
29
 
 
30
  async def run_and_submit_all( profile: gr.OAuthProfile | None):
31
  """
32
- Fetches all questions, runs the BasicAgent on them, submits all answers, and displays the results.
33
  """
34
  # --- Determine HF Space Runtime URL and Repo URL ---
35
  space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
@@ -41,13 +29,13 @@ async def run_and_submit_all( profile: gr.OAuthProfile | None):
41
  print("User not logged in.")
42
  return "Please Login to Hugging Face with the button.", None
43
 
44
- api_url = DEFAULT_API_URL
45
  questions_url = f"{api_url}/questions"
46
  submit_url = f"{api_url}/submit"
47
 
48
- # 1. Instantiate Agent ( modify this part to create your agent)
49
  try:
50
- agent = BasicAgent()
51
  except Exception as e:
52
  print(f"Error instantiating agent: {e}")
53
  return f"Error initializing agent: {e}", None
@@ -55,36 +43,6 @@ async def run_and_submit_all( profile: gr.OAuthProfile | None):
55
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
56
  print(agent_code)
57
 
58
- # 2. Test Case
59
- test_question = "How many studio albums were published by Mercedes Sosa between 2000 and 2009 (included)?"
60
- print(f"--- RUNNING SINGLE TEST ---")
61
- print(f"Question: {test_question}")
62
-
63
- # 3. Run the agent and display the result
64
- try:
65
- # This calls your agent's __call__ method
66
- submitted_answer = await agent(test_question)
67
-
68
- # Print the final answer clearly in the terminal
69
- print("-" * 50)
70
- print(f">>> AGENT'S FINAL ANSWER: {submitted_answer}")
71
- print("-" * 50)
72
-
73
- # Prepare a simple status message and table for the Gradio UI
74
- status_message = "Test Finished Successfully."
75
- results_df = pd.DataFrame([
76
- {"Question": test_question, "Submitted Answer": submitted_answer}
77
- ])
78
-
79
- except Exception as e:
80
- print(f"Error during single test run: {e}")
81
- status_message = f"Agent returned an error: {e}"
82
- results_df = pd.DataFrame()
83
-
84
- # 4. Return early to prevent fetching all questions or submitting
85
- return status_message, results_df
86
-
87
- '''
88
  # 2. Fetch Questions
89
  print(f"Fetching questions from: {questions_url}")
90
  try:
@@ -113,11 +71,18 @@ async def run_and_submit_all( profile: gr.OAuthProfile | None):
113
  for item in questions_data:
114
  task_id = item.get("task_id")
115
  question_text = item.get("question")
 
 
 
 
 
 
 
116
  if not task_id or question_text is None:
117
  print(f"Skipping item with missing task_id or question: {item}")
118
  continue
119
  try:
120
- submitted_answer = agent(question_text)
121
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
122
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
123
  except Exception as e:
@@ -174,24 +139,15 @@ async def run_and_submit_all( profile: gr.OAuthProfile | None):
174
  status_message = f"An unexpected error occurred during submission: {e}"
175
  print(status_message)
176
  results_df = pd.DataFrame(results_log)
177
- return status_message, results_df
178
- '''
179
 
180
  # --- Build Gradio Interface using Blocks ---
181
  with gr.Blocks() as demo:
182
- gr.Markdown("# Basic Agent Evaluation Runner")
183
  gr.Markdown(
184
  """
185
- **Instructions:**
186
-
187
- 1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
188
- 2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
189
- 3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
190
-
191
- ---
192
  **Disclaimers:**
193
  Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
194
- This space provides a basic setup and is intentionally sub-optimal to encourage you to develop your own, more robust solution. For instance for the delay process of the submit button, a solution could be to cache the answers and submit in a seperate action or even to answer the questions in async.
195
  """
196
  )
197
 
 
 
 
 
 
 
1
  import os
2
+ import sys
 
3
  import inspect
4
+ import requests
5
+ import logging
6
+ import gradio as gr
7
  import pandas as pd
8
+ from agent import GaiaAgent
 
 
 
9
 
10
+ # Logging
11
+ logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
12
+ logging.getLogger().addHandler(logging.StreamHandler(stream=sys.stdout))
13
 
14
+ # Constants
15
+ BASE_API_URL = "https://agents-course-unit4-scoring.hf.space"
 
 
 
 
 
 
 
 
16
 
17
+ # Function to run GAIA dataset questions
18
  async def run_and_submit_all( profile: gr.OAuthProfile | None):
19
  """
20
+ Fetches all questions, runs the GaiaAgent on them, submits all answers, and displays the results.
21
  """
22
  # --- Determine HF Space Runtime URL and Repo URL ---
23
  space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
 
29
  print("User not logged in.")
30
  return "Please Login to Hugging Face with the button.", None
31
 
32
+ api_url = BASE_API_URL
33
  questions_url = f"{api_url}/questions"
34
  submit_url = f"{api_url}/submit"
35
 
36
+ # 1. Instantiate Agent
37
  try:
38
+ agent = GaiaAgent()
39
  except Exception as e:
40
  print(f"Error instantiating agent: {e}")
41
  return f"Error initializing agent: {e}", None
 
43
  agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
44
  print(agent_code)
45
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  # 2. Fetch Questions
47
  print(f"Fetching questions from: {questions_url}")
48
  try:
 
71
  for item in questions_data:
72
  task_id = item.get("task_id")
73
  question_text = item.get("question")
74
+ file_name = item.get("file_name")
75
+
76
+ full_input=f"""
77
+ Task ID: {task_id}
78
+ File Name: {file_name}
79
+ Question: {question_text}
80
+ """
81
  if not task_id or question_text is None:
82
  print(f"Skipping item with missing task_id or question: {item}")
83
  continue
84
  try:
85
+ submitted_answer = await agent(full_input)
86
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
87
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
88
  except Exception as e:
 
139
  status_message = f"An unexpected error occurred during submission: {e}"
140
  print(status_message)
141
  results_df = pd.DataFrame(results_log)
142
+ return status_message, results_df
 
143
 
144
  # --- Build Gradio Interface using Blocks ---
145
  with gr.Blocks() as demo:
146
+ gr.Markdown("# GAIA Agent Evaluation Runner")
147
  gr.Markdown(
148
  """
 
 
 
 
 
 
 
149
  **Disclaimers:**
150
  Once clicking on the "submit button, it can take quite some time ( this is the time for the agent to go through all the questions).
 
151
  """
152
  )
153
 
requirements.txt CHANGED
@@ -1,6 +1,14 @@
1
  gradio
2
  requests
3
  llama-index
4
- llama-index-llms-huggingface-api
5
  llama-index-tools-tavily-research
6
- tavily-python
 
 
 
 
 
 
 
 
 
1
  gradio
2
  requests
3
  llama-index
4
+ # llama-index-llms-huggingface-api
5
  llama-index-tools-tavily-research
6
+ llama-index-llms-nebius
7
+ llama-index-multi-modal-llms-nebius
8
+ tavily-python
9
+ llama-index-readers-file
10
+ openai
11
+ numexpr # calculator
12
+ pandas
13
+ openpyxl
14
+ llama-index-experimental