AlbertoFor commited on
Commit
6a809e4
·
1 Parent(s): c657a71

Final version of tools

Browse files
app.py CHANGED
@@ -16,7 +16,7 @@ from io import BytesIO
16
  import PyPDF2
17
  import base64
18
  from langchain_google_genai import ChatGoogleGenerativeAI
19
- from langchain_openai import AzureChatOpenAI
20
  from langchain_core.tools import tool
21
  from dotenv import load_dotenv
22
  import time
@@ -36,6 +36,7 @@ from tools.answer_excel import AnswerExcelTool
36
  from contextlib import redirect_stdout
37
  from tools.chess_tool import ChessTool
38
  from tools.audio_tool import AudioTool
 
39
 
40
  load_dotenv(".env", override=True)
41
  BRAVE_API_KEY = os.getenv("BRAVE_API")
@@ -55,18 +56,15 @@ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
55
 
56
  # --- Basic Agent Definition ---
57
  class BasicAgent:
58
- def __init__(self):
59
-
60
- # tools initialization
61
- #internet_search = DuckDuckGoSearchRun()
62
-
63
  tools = [CodeGenTool(), PythonExecutionTool(temp_dir="./"), YoutubeTranscriptTool(),
64
  AnswerQuestionFromFileTool(), AnswerQuestionTool(), DownloadFile(),
65
- ReverseString(), WebSearchTool(), WikipediaTool(), AnswerExcelTool(), ChessTool(), AudioTool()]
66
 
67
- llm = ChatGoogleGenerativeAI(
68
- model="gemini-2.0-flash",
69
- temperature=0)
 
70
  self.llm_with_tools = llm.bind_tools(tools)
71
 
72
  builder = StateGraph(State)
@@ -101,13 +99,13 @@ class BasicAgent:
101
  self.react_graph = builder.compile()
102
 
103
 
104
- def __call__(self, question: str, file_name: Optional[str]) -> str:
105
  print(f"Agent received question (first 50 chars): {question[:50]}...")
106
 
107
  messages = [HumanMessage(question)]
108
  messages = self.react_graph.invoke({"messages": messages, "file_path": file_name, "question": question})
109
 
110
- with open(f'messages_{file_name}.txt', 'w', encoding='utf-8') as out:
111
  with redirect_stdout(out):
112
  for m in messages['messages']:
113
  m.pretty_print()
@@ -126,36 +124,55 @@ class BasicAgent:
126
  file_name = None
127
 
128
  prompt = f"""
129
- You are a general AI assistant. I will ask you a question. Report your thoughts, and finish your answer with the following template: FINAL ANSWER: [YOUR FINAL ANSWER].
130
- YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings.
131
- 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.
132
-
133
- You should read the prompt thoroughly. For example, if they ask you for athletes with the least number of athletes, you must be careful to what they ask (in case of tie, the country which is the first in alphabetical order.)
134
 
135
- You MUST ALWAYS PICK WIKIPEDIA TOOL BEFORE WEB SEARCH.
136
-
137
- 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.
138
-
139
- YOU SHOULD **NEVER** MAKE ANY ASSUMPTION AND USE THE TOOLS PROVIDED!
140
-
141
- You are given this file: {file_name} with the extension: {file_extension}.
142
- If a file is provided, the FIRST thing you MUST do is call the download_file tool!!
143
- The format must be {DEFAULT_API_URL}/files/{file_name}
144
- DO NOT PASS THE EXTENSION!!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  """
146
 
147
  sys_msg = SystemMessage(content=prompt)
148
 
149
- time.sleep(5)
150
  return {"messages": [self.llm_with_tools.invoke([sys_msg] + state["messages"])]}
151
 
152
  def final_answer(state: State):
153
  system_prompt = f"""
154
- You will be given an answer and a question. You MUST remove EVERYTHING not needed from the answer and answer the question exactly.
155
  That is if you are being asked the number of something, you must not return the thought process, but just the number X.
156
 
157
- You must be VERY CAREFUL!! Of what the question asks.
158
- For example if they ask you to give the full name of a city without abbreviations you should stick to it (for example, St. Petersburg should be Saint Petersburg).
 
 
 
159
  """
160
 
161
  human_prompt = f"""
@@ -234,15 +251,14 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
234
  print(f"Running agent on {len(questions_data)} questions...")
235
  for item in questions_data:
236
  task_id = item.get("task_id")
237
- #if task_id != "99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3":
238
- # continue
239
  question_text = item.get("question")
240
  file_name = item.get("file_name")
241
  if not task_id or question_text is None:
242
  print(f"Skipping item with missing task_id or question: {item}")
243
  continue
244
  try:
245
- submitted_answer = agent(question_text, file_name)
 
246
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
247
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
248
  except Exception as e:
 
16
  import PyPDF2
17
  import base64
18
  from langchain_google_genai import ChatGoogleGenerativeAI
19
+ from langchain_openai import ChatOpenAI
20
  from langchain_core.tools import tool
21
  from dotenv import load_dotenv
22
  import time
 
36
  from contextlib import redirect_stdout
37
  from tools.chess_tool import ChessTool
38
  from tools.audio_tool import AudioTool
39
+ from tools.fetch_web_page import FetchWebPageTool
40
 
41
  load_dotenv(".env", override=True)
42
  BRAVE_API_KEY = os.getenv("BRAVE_API")
 
56
 
57
  # --- Basic Agent Definition ---
58
  class BasicAgent:
59
+ def __init__(self):
 
 
 
 
60
  tools = [CodeGenTool(), PythonExecutionTool(temp_dir="./"), YoutubeTranscriptTool(),
61
  AnswerQuestionFromFileTool(), AnswerQuestionTool(), DownloadFile(),
62
+ ReverseString(), WebSearchTool(), WikipediaTool(), AnswerExcelTool(), ChessTool(), AudioTool(), FetchWebPageTool()]
63
 
64
+ #llm = ChatGoogleGenerativeAI(
65
+ # model="gemini-2.0-flash",
66
+ # temperature=0)
67
+ llm = ChatOpenAI(model="gpt-4.1-mini", temperature=0)
68
  self.llm_with_tools = llm.bind_tools(tools)
69
 
70
  builder = StateGraph(State)
 
99
  self.react_graph = builder.compile()
100
 
101
 
102
+ def __call__(self, question: str, task_id: str, file_name: Optional[str]) -> str:
103
  print(f"Agent received question (first 50 chars): {question[:50]}...")
104
 
105
  messages = [HumanMessage(question)]
106
  messages = self.react_graph.invoke({"messages": messages, "file_path": file_name, "question": question})
107
 
108
+ with open(f'messages_{task_id}.txt', 'w', encoding='utf-8') as out:
109
  with redirect_stdout(out):
110
  for m in messages['messages']:
111
  m.pretty_print()
 
124
  file_name = None
125
 
126
  prompt = f"""
127
+ You are a general AI assistant. When I ask you a question:
 
 
 
 
128
 
129
+ Share your reasoning process clearly.
130
+
131
+ End with the exact template:
132
+ FINAL ANSWER: [YOUR FINAL ANSWER]
133
+
134
+ -------------------------------------------
135
+ Guidelines for FINAL ANSWER:
136
+
137
+ - Use a single number, a minimal phrase, or a comma-separated list of numbers and/or strings.
138
+
139
+ - For numbers, do not use commas, currency symbols, or percentage signs unless explicitly requested.
140
+
141
+ - For strings, avoid articles and abbreviations (e.g., no city abbreviations). Write digits in full text unless otherwise specified.
142
+
143
+ - Do not change capitalization of the terms you see unless it explicitly specified.
144
+
145
+ NEVER REPEAT THE SAME SEARCH MORE THAN ONCE, EVEN WITH SIMILAR TERMS. If you didn't find anything on the first go, it means there's nothing with that search query available.
146
+ If you can't find an answer just say you can't find it without repeating the same thing over and over.
147
+
148
+ Always read the prompt carefully.
149
+
150
+ Start with Wikipedia when searching for information. If Wikipedia doesn't have the answer, then use the web search tool. Use every available resource to find the correct answer.
151
+
152
+ IMPORTANT: Never make assumptions. Always use the provided tools!! If you are asked a question you think you know without using any tool, do not answer but invoke the answer_question_tool provided the WHOLE question in input.
153
+
154
+ NOTE: the question about the actor is tricky: they want to know who Bartłomiej played in Magda M.
155
+
156
+ If a file is provided (named {file_name} with extension {file_extension}), your first action MUST BE TO CALL the download_file tool with the URL:
157
+ {DEFAULT_API_URL}/files/{file_name}
158
+ Do **NOT** include the file extension in the URL and send WITHOUT MODIFICATION.
159
  """
160
 
161
  sys_msg = SystemMessage(content=prompt)
162
 
163
+ time.sleep(40)
164
  return {"messages": [self.llm_with_tools.invoke([sys_msg] + state["messages"])]}
165
 
166
  def final_answer(state: State):
167
  system_prompt = f"""
168
+ You will be given an answer and a question. You MUST remove EVERYTHING not needed from the answer and answer the question exactly without reporting "FINAL ANSWER".
169
  That is if you are being asked the number of something, you must not return the thought process, but just the number X.
170
 
171
+ You must be VERY CAREFUL of what the question asks!!!
172
+ For example:
173
+ if they ask you to give the full name of a city without abbreviations you should stick to it (for example, St. Petersburg should be Saint Petersburg).
174
+ if the first name is asked, you MUST return the first name only (Claus and not Claus Peter)!
175
+ Remove full stops at the end, they are not needed. If you return something comma separated, there must always be a space between the comma and the next letter. Always!!
176
  """
177
 
178
  human_prompt = f"""
 
251
  print(f"Running agent on {len(questions_data)} questions...")
252
  for item in questions_data:
253
  task_id = item.get("task_id")
 
 
254
  question_text = item.get("question")
255
  file_name = item.get("file_name")
256
  if not task_id or question_text is None:
257
  print(f"Skipping item with missing task_id or question: {item}")
258
  continue
259
  try:
260
+ submitted_answer = agent(question_text, task_id, file_name)
261
+
262
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
263
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
264
  except Exception as e:
tools/answer_excel.py CHANGED
@@ -6,12 +6,12 @@ from langchain.agents.agent_types import AgentType
6
 
7
  class AnswerExcelTool(BaseTool):
8
  name : str = "answer_excel_tool"
9
- description: str = "Given the path to a file containing an excel file and a query, this tool tries to get an answer by querying the excel file."
10
 
11
- def _run(query: str, file_path: str) -> str:
12
  df = pd.read_excel(file_path)
13
 
14
- llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0),
15
 
16
  agent_executor = create_pandas_dataframe_agent(
17
  llm,
 
6
 
7
  class AnswerExcelTool(BaseTool):
8
  name : str = "answer_excel_tool"
9
+ description: str = "Given the path to a file containing an excel file and a query, this tool tries to get an answer by querying the excel file. Provide the whole question in input. Another agent will later break down the task."
10
 
11
+ def _run(self, query: str, file_path: str) -> str:
12
  df = pd.read_excel(file_path)
13
 
14
+ llm = ChatGoogleGenerativeAI(model="gemini-2.0-flash", temperature=0)
15
 
16
  agent_executor = create_pandas_dataframe_agent(
17
  llm,
tools/answer_question.py CHANGED
@@ -2,28 +2,31 @@ from langchain_google_genai import ChatGoogleGenerativeAI
2
  from pydantic import PrivateAttr
3
  from langchain_core.tools.base import BaseTool
4
  from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
 
 
 
5
 
6
  class AnswerQuestionTool(BaseTool):
7
  name : str = "answer_question_tool"
8
- description: str = "Use this tool to answer any elementary question that you can solve without needing access to any external tool. Simply provide the question in input, reporting the whole question including desired output format."
9
  _llm = PrivateAttr()
10
  _system_prompt = PrivateAttr()
11
 
12
  def __init__(self):
13
  super().__init__()
14
- self._llm = ChatGoogleGenerativeAI(
15
- model="gemini-2.0-flash",
16
- temperature=0)
17
-
 
18
 
19
  self._system_prompt = SystemMessage("""You are a helpful assistant.
20
  You will be given a question and you will have to answer that question.
21
- You MUST NOT apologise, explain your reasoning nor nothing else.
22
- You MUST answer the question and provide the answer in the REQUIRED FORMAT.
23
- YOU MUST ABSOLUTELY NOT MAKE ANY KIND OF ASSUMPTION!! If you don't know an answer, say you don't know!
24
- If the format is incorrect, the answer is considered wrong.
25
 
26
- If you are given a list or a transcript and you need to do something on a list of object, think thoroughly on how you should return the output!
 
 
27
  """)
28
 
29
  def _run(self, question: str) -> str:
@@ -33,6 +36,18 @@ class AnswerQuestionTool(BaseTool):
33
  ]
34
  )
35
 
36
- response = self._llm.invoke([self._system_prompt, human_message])
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
  return response
 
2
  from pydantic import PrivateAttr
3
  from langchain_core.tools.base import BaseTool
4
  from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
5
+ from langchain_openai import ChatOpenAI
6
+ import time
7
+ from openai import OpenAI
8
 
9
  class AnswerQuestionTool(BaseTool):
10
  name : str = "answer_question_tool"
11
+ description: str = "Use this tool to answer any elementary question that you can solve without needing access to any external tool. Simply provide the question in input, reporting the whole question including desired output format. You can use this tool for example for vegetable classification."
12
  _llm = PrivateAttr()
13
  _system_prompt = PrivateAttr()
14
 
15
  def __init__(self):
16
  super().__init__()
17
+ #self._llm = ChatGoogleGenerativeAI(
18
+ # model="gemini-2.0-flash",
19
+ # temperature=0)
20
+ #self._llm = ChatOpenAI(model="o4-mini", temperature=0)
21
+
22
 
23
  self._system_prompt = SystemMessage("""You are a helpful assistant.
24
  You will be given a question and you will have to answer that question.
25
+ Provide also the reasoning for your answer as well as your final answer.
 
 
 
26
 
27
+ When provided with a list you must stick with the exact terms provided in the list and not make any modification.
28
+ Green beans, corn and zucchini are NOT VEGEATABLES BOTANICALLY!
29
+ Let's think step by step.
30
  """)
31
 
32
  def _run(self, question: str) -> str:
 
36
  ]
37
  )
38
 
39
+ time.sleep(5)
40
+ client = OpenAI()
41
+ response = client.responses.create(
42
+ model="o4-mini",
43
+ messages = [
44
+ {
45
+ "role": "system", "content": self._system_prompt.text()
46
+ },
47
+ {
48
+ "role": "user", "content": question
49
+ }]
50
+ )
51
+ #response = self._llm.invoke([self._system_prompt, human_message])
52
 
53
  return response
tools/answer_question_from_file.py CHANGED
@@ -5,6 +5,7 @@ from pydantic import PrivateAttr
5
  import os
6
  from dotenv import load_dotenv
7
  import whisper
 
8
 
9
  load_dotenv(".env", override=True)
10
 
@@ -23,7 +24,6 @@ class AnswerQuestionFromFileTool(BaseTool):
23
 
24
  Args:
25
  The question that needs to be answered.
26
- The file from which you want to get some information.
27
  The file extension of the file that is being processed.
28
  """
29
  _llm = PrivateAttr()
@@ -35,11 +35,18 @@ class AnswerQuestionFromFileTool(BaseTool):
35
  temperature=0)
36
 
37
 
38
- def _run(self, question: str, encoded_file: str, file_extension: str) -> str:
 
 
 
 
39
  if file_extension in ["png", "jpg"]:
 
 
40
  message = {"type": "image_url", "image_url": f"data:image/png;base64,{encoded_file}"}
41
  elif file_extension == "pdf":
42
- message = {"type": "image_url", # Assuming the LLM accepts PDF under this key, you might need to verify this
 
43
  "image_url": f"data:application/pdf;base64,{encoded_file}"
44
  }
45
  else:
 
5
  import os
6
  from dotenv import load_dotenv
7
  import whisper
8
+ import base64
9
 
10
  load_dotenv(".env", override=True)
11
 
 
24
 
25
  Args:
26
  The question that needs to be answered.
 
27
  The file extension of the file that is being processed.
28
  """
29
  _llm = PrivateAttr()
 
35
  temperature=0)
36
 
37
 
38
+ def _run(self, question: str, file_name: str, file_extension: str) -> str:
39
+
40
+ with open(file_name, "rb") as f:
41
+ file = f.read()
42
+
43
  if file_extension in ["png", "jpg"]:
44
+ encoded_file = base64.b64encode(file).decode("utf-8")
45
+
46
  message = {"type": "image_url", "image_url": f"data:image/png;base64,{encoded_file}"}
47
  elif file_extension == "pdf":
48
+ encoded_file = base64.b64encode(file).decode("utf-8")
49
+ message = {"type": "image_url",
50
  "image_url": f"data:application/pdf;base64,{encoded_file}"
51
  }
52
  else:
tools/audio_tool.py CHANGED
@@ -6,6 +6,8 @@ from pathlib import Path
6
  import os
7
  from transformers import pipeline
8
  import torch
 
 
9
 
10
  class AudioTool(BaseTool):
11
  name : str = "answer_question_audio_tool"
@@ -13,13 +15,16 @@ class AudioTool(BaseTool):
13
 
14
  def _run(self, query: str, file_path: str) -> str:
15
  try:
16
- pipe = pipeline(
17
- task="automatic-speech-recognition",
18
- model="openai/whisper-base",
19
- torch_dtype=torch.float32,
20
- device=0
21
- )
22
- result = pipe(str(Path("./") / Path(file_path)))
 
 
 
23
  except Exception as e:
24
  print("Exception", e)
25
 
@@ -32,17 +37,16 @@ class AudioTool(BaseTool):
32
 
33
  1. Carefully read the query multiple times to ensure you fully grasp what is being asked.
34
 
35
- 2. Start your response by listing, in clear bullet points, each precise requirement implied by the user's instructions (e.g., which portions of the transcript to use, what to include or exclude, and any specific formatting).
36
 
37
- 3. After restating the requirements, fulfill the request exactly as specified. Follow all content and formatting rules without deviation (for instance, “list only names,” “omit quantities,” “use comma-separated values,” “alphabetize,” etc.).
38
 
39
  4. Ensure that your final answer adheres strictly to the user's criteria and contains nothing beyond what was requested.
40
 
41
- Always prioritize accuracy and strict adherence to the user's stated needs before providing the answer.""")
42
 
43
- llm = ChatGoogleGenerativeAI(
44
- model="gemini-2.0-flash",
45
- temperature=0)
46
 
47
  response = llm.invoke([system_message, human_message])
48
 
 
6
  import os
7
  from transformers import pipeline
8
  import torch
9
+ from langchain_openai import ChatOpenAI
10
+ import time
11
 
12
  class AudioTool(BaseTool):
13
  name : str = "answer_question_audio_tool"
 
15
 
16
  def _run(self, query: str, file_path: str) -> str:
17
  try:
18
+ #pipe = pipeline(
19
+ # task="automatic-speech-recognition",
20
+ # model="openai/whisper-base",
21
+ # torch_dtype=torch.float32,
22
+ # device=0,
23
+ # return_timestamps=True
24
+ #)
25
+ #result = pipe(str(Path("./") / Path(file_path)), return_timestamps=True)
26
+ model = whisper.load_model("base")
27
+ result = model.transcribe(audio=str(Path("./") / Path(file_path)), language='en')
28
  except Exception as e:
29
  print("Exception", e)
30
 
 
37
 
38
  1. Carefully read the query multiple times to ensure you fully grasp what is being asked.
39
 
40
+ 2. Start by thinking, in clear bullet points, each precise requirement implied by the user's instructions (e.g., which portions of the transcript to use, what to include or exclude, and any specific formatting).
41
 
42
+ 3. After thinking more about the requirements, fulfill the request exactly as specified. Follow all content and formatting rules without deviation (for instance, “list only names,” “omit quantities,” “use comma-separated values,” “alphabetize,” etc.).
43
 
44
  4. Ensure that your final answer adheres strictly to the user's criteria and contains nothing beyond what was requested.
45
 
46
+ Always prioritize accuracy and strict adherence to the user's stated needs before providing the answer. REPLY ONLY WITH WHAT THE HUMAN ASKED. Return only the final answer!""")
47
 
48
+ time.sleep(5)
49
+ llm = ChatOpenAI(model="gpt-4.1-mini", temperature=0)
 
50
 
51
  response = llm.invoke([system_message, human_message])
52
 
tools/chess_tool.py CHANGED
@@ -9,7 +9,7 @@ class ChessTool(BaseTool):
9
 
10
  def _run(self, img_path: str, color_turn: str) -> str:
11
  # Get the FEN string
12
- #fen = predict_fen("./downloaded_files/image.png")
13
 
14
  if color_turn == "b":
15
  ranks = fen.split('/')
@@ -24,10 +24,18 @@ class ChessTool(BaseTool):
24
 
25
  fen = f"{final_fen} {color_turn} - - 0 1"
26
 
27
- fen = f"3r2k1/pp3pp1/4b2p/7Q/3n4/PqBBR2P/5PP1/6K1 {color_turn} - - 0 1"
 
28
 
29
- stockfish = Stockfish(path="C:/Users/FORMAGGA/Documents/personal/stockfish-windows-x86-64-avx2/stockfish/stockfish-windows-x86-64-avx2.exe")
30
 
31
- stockfish.set_fen_position(fen)
 
 
 
 
 
32
 
33
- return stockfish.get_best_move()
 
 
 
9
 
10
  def _run(self, img_path: str, color_turn: str) -> str:
11
  # Get the FEN string
12
+ fen = predict_fen("./downloaded_files/image.png")
13
 
14
  if color_turn == "b":
15
  ranks = fen.split('/')
 
24
 
25
  fen = f"{final_fen} {color_turn} - - 0 1"
26
 
27
+ try:
28
+ stockfish = Stockfish(path="C:/Users/FORMAGGA/Documents/personal/stockfish-windows-x86-64-avx2/stockfish/stockfish-windows-x86-64-avx2.exe")
29
 
30
+ stockfish.set_fen_position(fen)
31
 
32
+ next_move = str(stockfish.get_best_move())
33
+ except Exception as e:
34
+ print("Exception", e)
35
+ raise e
36
+
37
+ piece = stockfish.get_what_is_on_square(next_move[:2])
38
 
39
+ next_move_fen = piece.name + next_move[2:]
40
+
41
+ return next_move_fen
tools/code_exec.py CHANGED
@@ -23,7 +23,7 @@ class PythonExecutionTool(BaseTool):
23
 
24
  def __init__(
25
  self,
26
- python_executable: str = "python",
27
  timeout: int = 5,
28
  *,
29
  temp_dir: Optional[str] = None
 
23
 
24
  def __init__(
25
  self,
26
+ python_executable: str = "C:\\Users\\FORMAGGA\\Documents\\personal\\Final_Assignment_Template\\.venv\\Scripts\\python.exe",
27
  timeout: int = 5,
28
  *,
29
  temp_dir: Optional[str] = None
tools/download_file.py CHANGED
@@ -21,10 +21,7 @@ class DownloadFile(BaseTool):
21
 
22
  Output:
23
  IF the file is a document, image or audio:
24
- A Dict made of:
25
- 1\) The file in bytes
26
- 2\) The file in Base64 encoding
27
- 3\) The result of the call
28
 
29
  IF the file is a piece of code:
30
  A dict made of:
@@ -37,44 +34,40 @@ class DownloadFile(BaseTool):
37
 
38
  def _run(self, file_url: str, file_extension: str) -> dict:
39
  response = requests.get(file_url)
 
40
  if response.status_code == 200:
41
  msg = "File downloaded successfully!!"
42
  if file_extension in ["png", "jpg", "pdf"]:
43
  file = response.content
44
- b64_file = base64.b64encode(file).decode("utf-8")
45
  with open("downloaded_files/image.png", "wb") as f:
46
  f.write(file)
47
 
48
- return {
49
- "bytes": file,
50
- "base64": b64_file,
51
- "status": response.status_code,
52
- "path": "downloaded_files/image.png"
53
- }
54
  elif file_extension in ["mp3", "wav"]:
55
  res = response.content
56
  with open("downloaded_files/audio.mp3", mode="wb") as f:
57
  f.write(res)
58
 
59
- return {"transcript": "./downloaded_files/audio.mp3"}
60
 
61
  elif file_extension == "py":
62
  return {"text": response.text}
63
  elif file_extension == "xlsx":
64
  file_name = file_url.split("/")[-1]
65
 
66
- with open(f"./downloaded_files/{file_name}", "wb") as f:
67
  f.write(response.content)
68
 
69
- return {"dataframe_path": f"./downloaded_files/{file_name}"}
70
  else:
71
- return {"error_msg": "The file extension is not valid."}
72
  else:
73
  msg = "There was an error downloading the file."
74
  file = None
75
  b64_file = None
76
 
77
- return {"error_msg": msg}
78
 
79
 
80
 
 
21
 
22
  Output:
23
  IF the file is a document, image or audio:
24
+ A string with the path to the file.
 
 
 
25
 
26
  IF the file is a piece of code:
27
  A dict made of:
 
34
 
35
  def _run(self, file_url: str, file_extension: str) -> dict:
36
  response = requests.get(file_url)
37
+
38
  if response.status_code == 200:
39
  msg = "File downloaded successfully!!"
40
  if file_extension in ["png", "jpg", "pdf"]:
41
  file = response.content
42
+
43
  with open("downloaded_files/image.png", "wb") as f:
44
  f.write(file)
45
 
46
+ return "downloaded_files/image.png"
 
 
 
 
 
47
  elif file_extension in ["mp3", "wav"]:
48
  res = response.content
49
  with open("downloaded_files/audio.mp3", mode="wb") as f:
50
  f.write(res)
51
 
52
+ return f"./downloaded_files/audio.{file_extension}"
53
 
54
  elif file_extension == "py":
55
  return {"text": response.text}
56
  elif file_extension == "xlsx":
57
  file_name = file_url.split("/")[-1]
58
 
59
+ with open(f"./downloaded_files/{file_name}.xlsx", "wb") as f:
60
  f.write(response.content)
61
 
62
+ return f"./downloaded_files/{file_name}.xlsx"
63
  else:
64
+ return "The file extension is not valid."
65
  else:
66
  msg = "There was an error downloading the file."
67
  file = None
68
  b64_file = None
69
 
70
+ return msg
71
 
72
 
73
 
tools/fetch_web_page.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools.base import BaseTool
2
+ from typing import List
3
+ import requests
4
+
5
+ class FetchWebPageTool(BaseTool):
6
+ name : str = "fetch_web_page_tool"
7
+ description: str = "Provided the urls of 1 or more web pages, this tool returns the full content of the web page. This tool needs to be called AFTER calling the web_page_tool. It's important to fetch only pages which are useful to your task!"
8
+
9
+ def _run(self, urls: List[str]) -> List[str]:
10
+ pages = [requests.get(url).text for url in urls]
11
+
12
+ return pages
tools/web_search.py CHANGED
@@ -14,22 +14,22 @@ load_dotenv(".env", override=True)
14
 
15
  class WebSearchTool(BaseTool):
16
  name: str = "web_search_tool"
17
- description: str = "Perform a web search and extract concise factual answers. Use for online facts not in GAIA/Wikipedia—e.g. sports stats, Olympic participation, published papers, museum specimen locations, competition winners, and other up-to-date info."
18
  #_search: BraveSearch = PrivateAttr()
19
- _search: DuckDuckGoSearchResults = PrivateAttr()
20
 
21
  def __init__(self):
22
  super().__init__()
23
  #wrapper = DuckDuckGoSearchAPIWrapper(region="en", max_results=2)
24
  #self._search = DuckDuckGoSearchResults(api_wrapper=wrapper, output_format="json")
25
 
26
- self._search = TavilySearch(max_results=2)
27
 
28
  def _run_old(self, query: str) -> str:
29
  json_str = self._search.run(query) # list[Document]
30
  docs = json.loads(json_str)
31
  urls = [doc["link"] for doc in docs]
32
-
33
  pages = [requests.get(url) for url in urls]
34
 
35
  res = "\n\n---\n\n".join(
@@ -44,9 +44,9 @@ class WebSearchTool(BaseTool):
44
 
45
  return res
46
 
47
- def _run(self, query: str) -> str:
48
  # import pdb;pdb.set_trace()
49
  search_results = []
50
- search_results.append(self._search.invoke(query))
51
- # print(f"Search results: {search_results} \n type: {type(search_results)}")
52
- return str(search_results)
 
14
 
15
  class WebSearchTool(BaseTool):
16
  name: str = "web_search_tool"
17
+ description: str = "Perform a web search and extract concise factual answers. The query should be concise, below 400 characters. Use for online facts not in GAIA/Wikipedia—e.g. sports stats, Olympic participation, published papers, museum specimen locations, competition winners, and other up-to-date info."
18
  #_search: BraveSearch = PrivateAttr()
19
+ _search: TavilySearch = PrivateAttr()
20
 
21
  def __init__(self):
22
  super().__init__()
23
  #wrapper = DuckDuckGoSearchAPIWrapper(region="en", max_results=2)
24
  #self._search = DuckDuckGoSearchResults(api_wrapper=wrapper, output_format="json")
25
 
26
+ self._search = TavilySearch(max_results=3, topic="general")
27
 
28
  def _run_old(self, query: str) -> str:
29
  json_str = self._search.run(query) # list[Document]
30
  docs = json.loads(json_str)
31
  urls = [doc["link"] for doc in docs]
32
+ print(urls)
33
  pages = [requests.get(url) for url in urls]
34
 
35
  res = "\n\n---\n\n".join(
 
44
 
45
  return res
46
 
47
+ def _run(self, query: str) -> dict:
48
  # import pdb;pdb.set_trace()
49
  search_results = []
50
+ search_results.append(self._search.run(query))
51
+
52
+ return search_results
tools/wikipedia.py CHANGED
@@ -5,11 +5,12 @@ from langchain_core.tools.base import BaseTool
5
  from langchain_community.document_loaders import WikipediaLoader
6
  import requests
7
  from bs4 import BeautifulSoup
 
8
 
9
 
10
  class WikipediaTool(BaseTool):
11
  name: str = "wikipedia_tool"
12
- description: str = "Search Wikipedia for a given query, retrieving the corresponding page's HTML content."
13
  #_wikipedia = PrivateAttr()
14
 
15
  def __init__(self):
@@ -18,33 +19,30 @@ class WikipediaTool(BaseTool):
18
 
19
 
20
  def _run(self, query: str):
21
- loader = WikipediaLoader(
22
- query=query,
23
- lang="en",
24
- load_max_docs=1, # number of pages to fetch
25
- load_all_available_meta=True
26
- )
27
- docs = loader.load()
28
- url = docs[0].metadata.get("source")
29
-
30
- resp = requests.get(url)
31
- resp.raise_for_status()
32
-
33
- html_text = resp.text
34
-
35
- soup = BeautifulSoup(html_text, 'html.parser')
36
- page_content = soup.find("div", class_="mw-parser-output")
37
- if not page_content:
38
  return ""
39
- try:
40
- # Decompose non relevant tags
41
- to_decompose = []
42
- for tag in page_content.find_all(["style", "sup", "script", "noscript", "img", "link", "figure"]):
 
 
 
 
 
43
  to_decompose.append(tag)
44
-
45
- for tag in to_decompose:
46
- tag.decompose()
47
- except Exception as e:
48
- print(e)
49
 
50
- return str(page_content)
 
5
  from langchain_community.document_loaders import WikipediaLoader
6
  import requests
7
  from bs4 import BeautifulSoup
8
+ import wikipedia
9
 
10
 
11
  class WikipediaTool(BaseTool):
12
  name: str = "wikipedia_tool"
13
+ description: str = "Search Wikipedia for a given query, retrieving the corresponding page's HTML content. The query should not contain any noise and ask for something specific."
14
  #_wikipedia = PrivateAttr()
15
 
16
  def __init__(self):
 
19
 
20
 
21
  def _run(self, query: str):
22
+ print(f"wikipedia_search_html called with query='{query}'")
23
+ # Step 1: Get Wikipedia HTML
24
+ page = wikipedia.page(query)
25
+ html = page.html()
26
+
27
+ # Step 2: Parse HTML
28
+ soup = BeautifulSoup(html, "html.parser")
29
+ content_div = soup.find("div", class_="mw-parser-output")
30
+ # content_div = soup.find("table", class_="wikitable")
31
+ if not content_div:
 
 
 
 
 
 
 
32
  return ""
33
+
34
+ # Step 3: Find all tags to remove (style, script, sup, infobox, etc.)
35
+ to_decompose = []
36
+ for tag in content_div.find_all():
37
+ tag_classes = tag.get("class", [])
38
+ if (
39
+ tag.name in ["style", "script", "sup"]
40
+ or any(cls in ["infobox", "navbox", "reference"] for cls in tag_classes)
41
+ ):
42
  to_decompose.append(tag)
43
+
44
+ # Remove them after collecting
45
+ for tag in to_decompose:
46
+ tag.decompose()
 
47
 
48
+ return str(content_div)