Princekumar commited on
Commit
4223c89
·
1 Parent(s): 1d256d0

Final agent working with 40% correct answers

Browse files
Files changed (5) hide show
  1. app.py +32 -22
  2. helpers.py +117 -69
  3. llm.py +0 -37
  4. requirements.txt +20 -7
  5. tools.py +328 -201
app.py CHANGED
@@ -1,36 +1,45 @@
1
  import os
2
  import gradio as gr
3
  import requests
4
- import inspect
5
  import pandas as pd
 
6
  from smolagents import CodeAgent
7
  from helpers import download_file_from_url
8
- from llm import model
9
  from prompts import SYSTEM_PROMPT
10
  from tools import agent_tools
11
- from dotenv import load_dotenv
12
-
13
- load_dotenv()
14
 
15
- # (Keep Constants as is)
16
- # --- Constants ---
17
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
18
 
 
 
19
 
20
- # --- Basic Agent Definition ---
21
- # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
22
  class BasicAgent:
23
  def __init__(self):
24
- print("BasicAgent initialized.")
25
- agent = CodeAgent(model=model, tools=agent_tools, planning_interval=3)
26
- self.agent = agent
27
- self.agent.system_prompt = SYSTEM_PROMPT + "\n" + self.agent.system_prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  def __call__(self, question: str) -> str:
30
  print(f"Agent received question (first 50 chars): {question[:50]}...")
31
- fixed_answer = self.agent.run(question)
32
- print(f"Agent returning fixed answer: {fixed_answer}")
33
- return fixed_answer
34
 
35
 
36
  def run_and_submit_all(profile: gr.OAuthProfile | None):
@@ -51,7 +60,7 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
51
  api_url = DEFAULT_API_URL
52
  questions_url = f"{api_url}/questions"
53
  submit_url = f"{api_url}/submit"
54
- file_download_url = f"{api_url}/files"
55
 
56
  # 1. Instantiate Agent ( modify this part to create your agent)
57
  try:
@@ -91,15 +100,16 @@ def run_and_submit_all(profile: gr.OAuthProfile | None):
91
  for item in questions_data:
92
  task_id = item.get("task_id")
93
  question_text = item.get("question")
94
- file_name = item.get("file_name")
95
- if file_name:
96
- file_url = f"{file_download_url}/{task_id}"
97
- file_path = download_file_from_url(file_url, file_name)
98
- question_text = f"{question_text} (File: {file_path})"
99
  if not task_id or question_text is None:
100
  print(f"Skipping item with missing task_id or question: {item}")
101
  continue
 
102
  try:
 
 
 
 
103
  submitted_answer = agent(question_text)
104
  answers_payload.append(
105
  {"task_id": task_id, "submitted_answer": submitted_answer}
 
1
  import os
2
  import gradio as gr
3
  import requests
 
4
  import pandas as pd
5
+ from dotenv import load_dotenv
6
  from smolagents import CodeAgent
7
  from helpers import download_file_from_url
 
8
  from prompts import SYSTEM_PROMPT
9
  from tools import agent_tools
10
+ from smolagents import LiteLLMModel
 
 
11
 
12
+ # Constants
 
13
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
14
 
15
+ load_dotenv() # Load environment variables from .env file
16
+
17
 
 
 
18
  class BasicAgent:
19
  def __init__(self):
20
+ print("Initializing the BasicAgent")
21
+
22
+ DEFAULT_MODEL = os.getenv("GEMINI_MODEL")
23
+
24
+ llm_model = LiteLLMModel(
25
+ model_id=DEFAULT_MODEL,
26
+ api_key=os.getenv("GEMINI_API_KEY"),
27
+ max_tokens=8192,
28
+ temperature=0.1,
29
+ )
30
+ # Initialize GeminiAgent
31
+ self.agent = CodeAgent(
32
+ model=llm_model,
33
+ tools=agent_tools,
34
+ )
35
+ self.agent.system_prompt += "\n" + SYSTEM_PROMPT
36
+ print("Smolagent initialized successfully")
37
 
38
  def __call__(self, question: str) -> str:
39
  print(f"Agent received question (first 50 chars): {question[:50]}...")
40
+ final_answer = self.agent.run(question)
41
+ print(f"Agent returning fixed answer: {final_answer}")
42
+ return final_answer
43
 
44
 
45
  def run_and_submit_all(profile: gr.OAuthProfile | None):
 
60
  api_url = DEFAULT_API_URL
61
  questions_url = f"{api_url}/questions"
62
  submit_url = f"{api_url}/submit"
63
+ file_url = f"{api_url}/files"
64
 
65
  # 1. Instantiate Agent ( modify this part to create your agent)
66
  try:
 
100
  for item in questions_data:
101
  task_id = item.get("task_id")
102
  question_text = item.get("question")
103
+ file_name = item.get("file_name", None)
 
 
 
 
104
  if not task_id or question_text is None:
105
  print(f"Skipping item with missing task_id or question: {item}")
106
  continue
107
+
108
  try:
109
+ if file_name:
110
+ task_file_url = file_url + f"/{task_id}"
111
+ file_path = download_file_from_url(url=task_file_url)
112
+ question_text += f"\n\n[File for this question: {file_name}] is saved at filepath : ({file_path})"
113
  submitted_answer = agent(question_text)
114
  answers_payload.append(
115
  {"task_id": task_id, "submitted_answer": submitted_answer}
helpers.py CHANGED
@@ -1,6 +1,4 @@
1
- import base64
2
  import os
3
- from litellm import completion, create_file
4
  import requests
5
  from dotenv import load_dotenv
6
 
@@ -9,71 +7,103 @@ load_dotenv()
9
  DEFAULT_MODEL = os.getenv("GEMINI_MODEL")
10
 
11
 
12
- def analyze_file_with_gemini(file_path: str, file_name: str) -> str:
13
- # 1. Read file and encode in base64
14
- try:
15
- with open(file_path, "rb") as f:
16
- content = f.read()
17
- mime_type = _get_mime_type(file_path)
18
- base64_data = base64.b64encode(content).decode("utf-8")
19
- except Exception as e:
20
- return f"Error reading file: {e}"
21
-
22
- file = create_file(
23
- file=base64_data,
24
- purpose="user_data",
25
- extra_body={"custom_llm_provider": "gemini"},
26
- api_key=os.getenv("GEMINI_API_KEY"),
27
- )
28
- # 2. Construct Gemini-style multimodal input
29
- prompt = (
30
- f"Analyze the following {mime_type} file and provide a detailed report. "
31
- "The file is encoded in base64 format. "
32
- "Please include any relevant information or insights."
33
- )
34
-
35
- try:
36
- response = completion(
37
- model=DEFAULT_MODEL,
38
- messages=[
39
- {
40
- "role": "user",
41
- "content": [
42
- {"type": "text", "text": prompt},
43
- {
44
- "type": "file",
45
- "file": {
46
- "file_id": file.id,
47
- "filename": file_name,
48
- "format": "audio/wav",
49
- },
50
- },
51
- ],
52
- },
53
- ],
54
- )
55
-
56
- return response.choices[0].message
57
- except Exception as e:
58
- return f"Error from Gemini: {e}"
59
-
60
-
61
- def _get_mime_type(file_path: str) -> str:
62
- if file_path.endswith(".png"):
63
- return "image/png"
64
- elif file_path.endswith(".jpg") or file_path.endswith(".jpeg"):
65
- return "image/jpeg"
66
- elif file_path.endswith(".mp3"):
67
- return "audio/mpeg"
68
- else:
69
- raise ValueError(
70
- "Unsupported file type: only .png, .jpg, .jpeg, .mp3 are supported"
71
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
 
74
  def download_file_from_url(url: str, save_dir: str = "./downloads") -> str:
75
  """
76
- Downloads a file from a public URL and saves it locally.
77
 
78
  Args:
79
  url (str): The direct URL to the file (must not be a blob: URL).
@@ -84,14 +114,32 @@ def download_file_from_url(url: str, save_dir: str = "./downloads") -> str:
84
  """
85
  try:
86
  os.makedirs(save_dir, exist_ok=True)
87
-
88
- # Get file name from the URL or fallback
89
- local_filename = url.split("/")[-1] or "downloaded_file"
90
- file_path = os.path.join(save_dir, local_filename)
91
-
92
- # Perform streaming download
93
  with requests.get(url, stream=True) as r:
94
  r.raise_for_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  with open(file_path, "wb") as f:
96
  for chunk in r.iter_content(chunk_size=8192):
97
  f.write(chunk)
 
 
1
  import os
 
2
  import requests
3
  from dotenv import load_dotenv
4
 
 
7
  DEFAULT_MODEL = os.getenv("GEMINI_MODEL")
8
 
9
 
10
+ # def analyze_file_with_gemini(file_path: str, file_name: str) -> str:
11
+ # # 1. Read file and encode in base64
12
+ # try:
13
+ # with open(file_path, "rb") as f:
14
+ # content = f.read()
15
+ # mime_type = _get_mime_type(file_path)
16
+ # base64_data = base64.b64encode(content).decode("utf-8")
17
+ # except Exception as e:
18
+ # return f"Error reading file: {e}"
19
+
20
+ # file = create_file(
21
+ # file=base64_data,
22
+ # purpose="user_data",
23
+ # extra_body={"custom_llm_provider": "gemini"},
24
+ # api_key=os.getenv("GEMINI_API_KEY"),
25
+ # )
26
+ # # 2. Construct Gemini-style multimodal input
27
+ # prompt = (
28
+ # f"Analyze the following {mime_type} file and provide a detailed report. "
29
+ # "The file is encoded in base64 format. "
30
+ # "Please include any relevant information or insights."
31
+ # )
32
+
33
+ # try:
34
+ # time.sleep(5)
35
+ # response = completion(
36
+ # model=DEFAULT_MODEL,
37
+ # messages=[
38
+ # {
39
+ # "role": "user",
40
+ # "content": [
41
+ # {"type": "text", "text": prompt},
42
+ # {
43
+ # "type": "file",
44
+ # "file": {
45
+ # "file_id": file.id,
46
+ # "filename": file_name,
47
+ # "format": "audio/wav",
48
+ # },
49
+ # },
50
+ # ],
51
+ # },
52
+ # ],
53
+ # )
54
+
55
+ # return response.choices[0].message
56
+ # except Exception as e:
57
+ # return f"Error from Gemini: {e}"
58
+
59
+
60
+ # def _get_mime_type(file_path: str) -> str:
61
+ # if file_path.endswith(".png"):
62
+ # return "image/png"
63
+ # elif file_path.endswith(".jpg") or file_path.endswith(".jpeg"):
64
+ # return "image/jpeg"
65
+ # elif file_path.endswith(".mp3"):
66
+ # return "audio/mpeg"
67
+ # else:
68
+ # raise ValueError(
69
+ # "Unsupported file type: only .png, .jpg, .jpeg, .mp3 are supported"
70
+ # )
71
+
72
+
73
+ # def download_file_from_url(url: str, save_dir: str = "./downloads") -> str:
74
+ # """
75
+ # Downloads a file from a public URL and saves it locally.
76
+
77
+ # Args:
78
+ # url (str): The direct URL to the file (must not be a blob: URL).
79
+ # save_dir (str): Directory to save the downloaded file (default: ./downloads).
80
+
81
+ # Returns:
82
+ # str: Full path to the downloaded file.
83
+ # """
84
+ # try:
85
+ # os.makedirs(save_dir, exist_ok=True)
86
+
87
+ # # Get file name from the URL or fallback
88
+ # local_filename = url.split("/")[-1] or "downloaded_file"
89
+ # file_path = os.path.join(save_dir, local_filename)
90
+
91
+ # # Perform streaming download
92
+ # with requests.get(url, stream=True) as r:
93
+ # r.raise_for_status()
94
+ # with open(file_path, "wb") as f:
95
+ # for chunk in r.iter_content(chunk_size=8192):
96
+ # f.write(chunk)
97
+
98
+ # return file_path
99
+ # except Exception as e:
100
+ # raise RuntimeError(f"Failed to download file from {url}: {e}")
101
+ import mimetypes
102
 
103
 
104
  def download_file_from_url(url: str, save_dir: str = "./downloads") -> str:
105
  """
106
+ Downloads a file from a public URL and saves it locally with the correct extension.
107
 
108
  Args:
109
  url (str): The direct URL to the file (must not be a blob: URL).
 
114
  """
115
  try:
116
  os.makedirs(save_dir, exist_ok=True)
 
 
 
 
 
 
117
  with requests.get(url, stream=True) as r:
118
  r.raise_for_status()
119
+
120
+ # Try to get filename from Content-Disposition header
121
+ cd = r.headers.get("content-disposition")
122
+ if cd and "filename=" in cd:
123
+ local_filename = cd.split("filename=")[-1].strip('"; ')
124
+ else:
125
+ # Fallback to URL
126
+ local_filename = url.split("/")[-1]
127
+
128
+ # If no extension, try to guess from Content-Type
129
+ if not os.path.splitext(local_filename)[1]:
130
+ content_type = r.headers.get("content-type")
131
+ ext = (
132
+ mimetypes.guess_extension(content_type.split(";")[0])
133
+ if content_type
134
+ else ""
135
+ )
136
+ if ext:
137
+ local_filename += ext
138
+ else:
139
+ local_filename += ".bin" # fallback
140
+
141
+ file_path = os.path.join(save_dir, local_filename)
142
+
143
  with open(file_path, "wb") as f:
144
  for chunk in r.iter_content(chunk_size=8192):
145
  f.write(chunk)
llm.py DELETED
@@ -1,37 +0,0 @@
1
- # llm.py
2
- import os
3
- import litellm
4
- from smolagents import LiteLLMModel
5
- from dotenv import load_dotenv
6
-
7
- load_dotenv()
8
- # Set default model
9
- DEFAULT_MODEL = os.getenv("GEMINI_MODEL")
10
-
11
-
12
- def chat_with_llm(messages, model=DEFAULT_MODEL):
13
- """
14
- messages: list of {"role": "user"/"system"/"assistant", "content": "..."}
15
- model: model string (e.g., "gemini-pro" or "gpt-3.5-turbo")
16
- """
17
- try:
18
- response = litellm.completion(
19
- model=model, messages=messages, api_key=os.getenv("GEMINI_API_KEY")
20
- )
21
- return response["choices"][0]["message"]["content"]
22
- except Exception as e:
23
- return f"[LLM Error] {e}"
24
-
25
-
26
- def ask_llm(prompt: str, model=DEFAULT_MODEL):
27
- """
28
- Simpler wrapper for single-turn prompts
29
- """
30
- return chat_with_llm([{"role": "user", "content": prompt}], model=model)
31
-
32
-
33
- model = LiteLLMModel(
34
- model_id=DEFAULT_MODEL,
35
- api_key=os.getenv("GEMINI_API_KEY"),
36
- max_tokens=8192,
37
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -1,8 +1,21 @@
1
  gradio
2
- requests
3
- smolagents
4
- smolagents[litellm]
5
- pytesseract
6
- pillow
7
- pytube
8
- python-dotenv
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ langchain>=0.1.0
3
+ langchain-core>=0.1.0
4
+ langchain-community>=0.0.10
5
+ langchain-google-community==2.0.7
6
+ langchain-google-genai>=0.0.6
7
+ google-generativeai>=0.3.0
8
+ python-dotenv>=1.0.0
9
+ google-api-python-client>=2.108.0
10
+ duckduckgo-search>=4.4
11
+ tiktoken>=0.5.2
12
+ google-cloud-speech>=2.24.0
13
+ requests>=2.31.0
14
+ pydub>=0.25.1
15
+ yt-dlp>=2023.12.30
16
+ smolagents>=0.1.3
17
+ wikipedia>=1.4.0
18
+ Pillow>=10.2.0
19
+ wikipedia-api>=0.6.0
20
+ openpyxl>=3.1.2
21
+ tabulate==0.9.0
tools.py CHANGED
@@ -1,79 +1,63 @@
1
  import base64
2
  import os
 
 
 
3
  from smolagents import Tool
4
  import math
5
  import datetime
6
  from PIL import Image
7
  import pandas as pd
8
  import litellm
 
9
  from prompts import SYSTEM_PROMPT
10
  from pytube import YouTube
11
  from PIL import Image
12
  import pytesseract
13
- from smolagents import DuckDuckGoSearchTool
14
  from dotenv import load_dotenv
 
 
 
 
 
 
15
 
16
  load_dotenv()
17
 
18
 
19
- class GeminiFileAnalyzerTool(Tool):
20
- name = "gemini_file_analyzer"
21
- description = "Analyze an image or audio file using Gemini via LiteLLM. Supports jpg, png, and mp3."
22
- inputs = {
23
- "file_path": {"type": "string", "description": "Path to image/audio file"},
24
- "file_name": {
 
25
  "type": "string",
26
- "description": "Name of the file (e.g., photo.jpg, audio.mp3)",
27
  },
 
28
  }
29
- output_type = "string"
30
 
31
- def forward(self, file_path: str, file_name: str):
32
- try:
33
- with open(file_path, "rb") as f:
34
- content = f.read()
35
- mime_type = self._get_mime_type(file_path)
36
- base64_data = base64.b64encode(content).decode("utf-8")
37
- except Exception as e:
38
- return f"Error reading file: {e}"
39
 
 
 
 
40
  try:
41
- file = litellm.create_file(
42
- file=base64_data,
43
- purpose="user_data",
44
- extra_body={"custom_llm_provider": "gemini"},
45
- api_key=os.getenv("GEMINI_API_KEY"),
46
- )
47
  except Exception as e:
48
  return f"Error uploading file: {e}"
49
 
50
- prompt = (
51
- f"Analyze the following {mime_type} file and provide a detailed report. "
52
- "The file is encoded in base64 format. "
53
- "Please include any relevant information or insights."
54
- )
55
-
56
  try:
57
- response = litellm.completion(
58
- model=os.getenv("GEMINI_MODEL", "gemini-pro-vision"),
59
- messages=[
60
- {
61
- "role": "user",
62
- "content": [
63
- {"type": "text", "text": prompt},
64
- {
65
- "type": "file",
66
- "file": {
67
- "file_id": file.id,
68
- "filename": file_name,
69
- "format": mime_type.split("/")[-1], # e.g., "mp3"
70
- },
71
- },
72
- ],
73
- },
74
- ],
75
  )
76
- return response["choices"][0]["message"]["content"]
77
  except Exception as e:
78
  return f"Error from Gemini: {e}"
79
 
@@ -90,36 +74,63 @@ class GeminiFileAnalyzerTool(Tool):
90
  )
91
 
92
 
93
- class ImageTextExtractorTool(Tool):
94
- name = "image_text_extractor"
95
- description = "Extract text from an image using OCR."
96
- inputs = {
97
- "image_path": {
98
- "type": "string",
99
- "description": "Path to the image file (jpg, png, etc.)",
100
- }
101
  }
102
- output_type = "string"
103
 
104
- def forward(self, image_path: str):
105
  try:
106
- image = Image.open(image_path)
107
- text = pytesseract.image_to_string(image)
108
- return text.strip() or "No text found in image."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  except Exception as e:
110
- return f"Error extracting text: {e}"
111
 
112
 
113
- class TableInspectorTool(Tool):
114
- name = "table_inspector"
115
- description = "Load a CSV or Excel file and return table info and summary stats in Markdown format."
116
- inputs = {
 
 
117
  "file_path": {
118
  "type": "string",
119
  "description": "Path to CSV or Excel file (.csv, .xls, .xlsx)",
120
  }
121
  }
122
- output_type = "string"
123
 
124
  def forward(self, file_path: str):
125
  try:
@@ -130,25 +141,7 @@ class TableInspectorTool(Tool):
130
  else:
131
  return "Unsupported file type. Only CSV and Excel (.xls/.xlsx) are supported."
132
 
133
- # Get basic info
134
- n_rows, n_cols = df.shape
135
- headers = list(df.columns)
136
- summary = (
137
- df.describe(include="all", datetime_is_numeric=True)
138
- .fillna("")
139
- .astype(str)
140
- )
141
-
142
- # Markdown output
143
- md = f"### File loaded: **{file_path}**\n"
144
- md += f"- Rows: **{n_rows}**\n"
145
- md += f"- Columns: **{n_cols}**\n"
146
- md += f"- Column Headers:\n"
147
- for col in headers:
148
- md += f" - `{col}`\n"
149
-
150
- md += "\n### Summary Statistics (markdown table):\n\n"
151
- md += summary.to_markdown()
152
 
153
  return md
154
 
@@ -157,63 +150,62 @@ class TableInspectorTool(Tool):
157
 
158
 
159
  class YouTubeVideoAnalyzerTool(Tool):
160
- name = "youtube_video_analyzer"
161
- description = "Given a YouTube URL, extracts metadata and comments, then analyzes it for summary, highlights, and visuals."
162
- inputs = {
 
 
163
  "url": {"type": "string", "description": "Full YouTube video URL"},
164
  "user_prompt": {
165
  "type": "string",
166
  "description": "What you want to analyze from the video content",
167
  },
168
  }
169
- output_type = "string"
170
 
171
  def forward(self, url: str, user_prompt: str):
172
  try:
173
- yt = YouTube(url)
174
- title = yt.title
175
- description = yt.description
176
- comments = yt.comments[:5] if yt.comments else []
177
-
178
- comment_text = (
179
- "\n".join([f"- {c}" for c in comments])
180
- if comments
181
- else "No comments found."
182
- )
183
-
184
- system_prompt = f"""You are an AI video analyzer. A user wants to analyze the following YouTube video.
185
-
186
- ### Title
187
- {title}
188
-
189
- ### Description
190
- {description or 'No description.'}
191
-
192
- ### Top Comments
193
- {comment_text}
194
-
195
- ### User Request
196
- {user_prompt}
197
-
198
- ### Instructions:
199
- - Identify the main topic of the video.
200
- - List any unique characteristics or production traits.
201
- - Mention key highlights or scenes if they are implied.
202
- - Give an overall summary based on description and social sentiment.
203
-
204
- Respond in structured markdown.
205
- """
206
-
207
- response = litellm.completion(
208
- api_key=os.getenv("GEMINI_API_KEY"),
209
- model=os.getenv("GEMINI_MODEL"),
210
- messages=[
211
- {"role": "system", "content": SYSTEM_PROMPT},
212
- {"role": "user", "content": system_prompt},
213
- ],
214
- )
215
-
216
- return response["choices"][0]["message"]["content"]
217
 
218
  except Exception as e:
219
  return f"Error analyzing video: {e}"
@@ -221,17 +213,17 @@ Respond in structured markdown.
221
 
222
  # --- Math Tools ---
223
  class CalculatorTool(Tool):
224
- name = "calculator"
225
- description = (
226
  "Evaluate a basic mathematical expression (supports +, -, *, /, **, %, etc.)."
227
  )
228
- inputs = {
229
  "expression": {
230
  "type": "string",
231
  "description": "A mathematical expression to evaluate",
232
  }
233
  }
234
- output_type = "number"
235
 
236
  def forward(self, expression: str):
237
  # Safely evaluate the expression using ast
@@ -262,26 +254,26 @@ class CalculatorTool(Tool):
262
 
263
  # Optionally, separate basic operations could be defined (e.g., add, subtract).
264
  class AddTool(Tool):
265
- name = "add"
266
- description = "Add two numbers together."
267
- inputs = {
268
  "a": {"type": "number", "description": "First number"},
269
  "b": {"type": "number", "description": "Second number"},
270
  }
271
- output_type = "number"
272
 
273
  def forward(self, a: float, b: float):
274
  return a + b
275
 
276
 
277
  class MultiplyTool(Tool):
278
- name = "multiply"
279
- description = "Multiply two numbers."
280
- inputs = {
281
  "a": {"type": "number", "description": "First number"},
282
  "b": {"type": "number", "description": "Second number"},
283
  }
284
- output_type = "number"
285
 
286
  def forward(self, a: float, b: float):
287
  return a * b
@@ -289,10 +281,12 @@ class MultiplyTool(Tool):
289
 
290
  # --- Date/Time Tools ---
291
  class DayOfWeekTool(Tool):
292
- name = "day_of_week"
293
- description = "Return the day of week for a given date (YYYY-MM-DD)."
294
- inputs = {"date": {"type": "string", "description": "Date in format YYYY-MM-DD"}}
295
- output_type = "string"
 
 
296
 
297
  def forward(self, date: str):
298
  year, month, day = map(int, date.split("-"))
@@ -301,13 +295,13 @@ class DayOfWeekTool(Tool):
301
 
302
 
303
  class AddDaysTool(Tool):
304
- name = "add_days"
305
- description = "Add a number of days to a date (YYYY-MM-DD)."
306
- inputs = {
307
  "date": {"type": "string", "description": "Start date (YYYY-MM-DD)"},
308
  "days": {"type": "integer", "description": "Number of days to add"},
309
  }
310
- output_type = "string"
311
 
312
  def forward(self, date: str, days: int):
313
  year, month, day = map(int, date.split("-"))
@@ -316,13 +310,15 @@ class AddDaysTool(Tool):
316
 
317
 
318
  class DateDiffTool(Tool):
319
- name = "date_diff"
320
- description = "Compute difference in days between two dates (YYYY-MM-DD)."
321
- inputs = {
 
 
322
  "start_date": {"type": "string", "description": "First date (YYYY-MM-DD)"},
323
  "end_date": {"type": "string", "description": "Second date (YYYY-MM-DD)"},
324
  }
325
- output_type = "integer"
326
 
327
  def forward(self, start_date: str, end_date: str):
328
  y1, m1, d1 = map(int, start_date.split("-"))
@@ -334,13 +330,13 @@ class DateDiffTool(Tool):
334
 
335
  # --- Unit Conversion Tools ---
336
  class TempConvertTool(Tool):
337
- name = "convert_temperature"
338
- description = "Convert temperature between Celsius and Fahrenheit."
339
- inputs = {
340
  "value": {"type": "number", "description": "Temperature value to convert"},
341
  "from_unit": {"type": "string", "description": "Unit of input ('C' or 'F')"},
342
  }
343
- output_type = "number"
344
 
345
  def forward(self, value: float, from_unit: str):
346
  unit = from_unit.strip().upper()
@@ -355,9 +351,11 @@ class TempConvertTool(Tool):
355
 
356
 
357
  class LengthConvertTool(Tool):
358
- name = "convert_length"
359
- description = "Convert length between kilometers, miles, meters, and feet."
360
- inputs = {
 
 
361
  "value": {"type": "number", "description": "Length value to convert"},
362
  "from_unit": {
363
  "type": "string",
@@ -368,7 +366,7 @@ class LengthConvertTool(Tool):
368
  "description": "Target unit ('km','mi','m','ft')",
369
  },
370
  }
371
- output_type = "number"
372
 
373
  def forward(self, value: float, from_unit: str, to_unit: str):
374
  u1 = from_unit.lower()
@@ -398,23 +396,25 @@ class LengthConvertTool(Tool):
398
 
399
  # --- Text Tools ---
400
  class WordCountTool(Tool):
401
- name = "word_count"
402
- description = "Count the number of words in a text string."
403
- inputs = {"text": {"type": "string", "description": "Input text"}}
404
- output_type = "integer"
405
 
406
  def forward(self, text: str):
407
  return len(text.split())
408
 
409
 
410
  class FindTextTool(Tool):
411
- name = "find_text"
412
- description = "Find occurrences of a substring in a text; returns count."
413
- inputs = {
 
 
414
  "text": {"type": "string", "description": "Text to search in"},
415
  "query": {"type": "string", "description": "Substring to search for"},
416
  }
417
- output_type = "integer"
418
 
419
  def forward(self, text: str, query: str):
420
  return text.count(query)
@@ -422,20 +422,26 @@ class FindTextTool(Tool):
422
 
423
  # --- List/Sequence Tools ---
424
  class SortListTool(Tool):
425
- name = "sort_list"
426
- description = "Sort a list of items (numbers or strings)."
427
- inputs = {"items": {"type": "array", "description": "List of items to sort"}}
428
- output_type = "array"
 
 
429
 
430
  def forward(self, items):
431
  return sorted(items)
432
 
433
 
434
  class UniqueListTool(Tool):
435
- name = "unique_list"
436
- description = "Return a list with duplicate items removed (preserving order)."
437
- inputs = {"items": {"type": "array", "description": "List of items"}}
438
- output_type = "array"
 
 
 
 
439
 
440
  def forward(self, items):
441
  seen = []
@@ -447,10 +453,12 @@ class UniqueListTool(Tool):
447
 
448
  # --- File I/O Tools ---
449
  class ReadFileTool(Tool):
450
- name = "read_file"
451
- description = "Read and return the contents of a text file."
452
- inputs = {"file_path": {"type": "string", "description": "Path to a text file"}}
453
- output_type = "string"
 
 
454
 
455
  def forward(self, file_path: str):
456
  try:
@@ -461,13 +469,13 @@ class ReadFileTool(Tool):
461
 
462
 
463
  class WriteFileTool(Tool):
464
- name = "write_file"
465
- description = "Write a string to a text file (overwrites if exists)."
466
- inputs = {
467
  "file_path": {"type": "string", "description": "Path to write the file"},
468
  "content": {"type": "string", "description": "Content to write"},
469
  }
470
- output_type = "string"
471
 
472
  def forward(self, file_path: str, content: str):
473
  with open(file_path, "w") as f:
@@ -477,10 +485,12 @@ class WriteFileTool(Tool):
477
 
478
  # --- Image Tool (stub) ---
479
  class ImageInfoTool(Tool):
480
- name = "image_info"
481
- description = "Load an image and report basic info (size and mode)."
482
- inputs = {"image_path": {"type": "string", "description": "Path to an image file"}}
483
- output_type = "string"
 
 
484
 
485
  def forward(self, image_path: str):
486
  try:
@@ -490,11 +500,129 @@ class ImageInfoTool(Tool):
490
  return f"Error loading image: {e}"
491
 
492
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  # List of all available tools
494
  agent_tools = [
495
- GeminiFileAnalyzerTool(),
496
- ImageTextExtractorTool(),
497
- TableInspectorTool(),
498
  YouTubeVideoAnalyzerTool(),
499
  CalculatorTool(),
500
  AddTool(),
@@ -508,8 +636,7 @@ agent_tools = [
508
  FindTextTool(),
509
  SortListTool(),
510
  UniqueListTool(),
511
- ReadFileTool(),
512
- WriteFileTool(),
513
- ImageInfoTool(),
514
- DuckDuckGoSearchTool(),
515
  ]
 
1
  import base64
2
  import os
3
+ from typing import ClassVar
4
+ from urllib.parse import urlparse
5
+ import requests
6
  from smolagents import Tool
7
  import math
8
  import datetime
9
  from PIL import Image
10
  import pandas as pd
11
  import litellm
12
+ import yt_dlp
13
  from prompts import SYSTEM_PROMPT
14
  from pytube import YouTube
15
  from PIL import Image
16
  import pytesseract
17
+ from smolagents import DuckDuckGoSearchTool, WikipediaSearchTool
18
  from dotenv import load_dotenv
19
+ import time
20
+ from langchain_google_community import GoogleSearchAPIWrapper
21
+ from google import genai
22
+ from langchain_community.document_loaders import WikipediaLoader
23
+ from langchain_community.document_loaders import ArxivLoader
24
+
25
 
26
  load_dotenv()
27
 
28
 
29
+ class AnyTypeFileAnalyzerTool(Tool):
30
+ name: ClassVar[str] = "any_type_file_analyzer_tool"
31
+ description: ClassVar[str] = (
32
+ "Analyze an image or audio mp3 file using Gemini. Supports jpg, png, and mp3."
33
+ )
34
+ inputs: ClassVar[dict] = {
35
+ "analysis_description": {
36
  "type": "string",
37
+ "description": "Describe what you want to analyze from the file",
38
  },
39
+ "file_path": {"type": "string", "description": "Path to image/audio file"},
40
  }
41
+ output_type: ClassVar[str] = "string"
42
 
43
+ def forward(self, analysis_description: str, file_path: str):
 
 
 
 
 
 
 
44
 
45
+ client = genai.Client(
46
+ api_key=os.getenv("GEMINI_API_KEY"),
47
+ )
48
  try:
49
+ file = client.files.upload(file=file_path)
 
 
 
 
 
50
  except Exception as e:
51
  return f"Error uploading file: {e}"
52
 
 
 
 
 
 
 
53
  try:
54
+ full_description = SYSTEM_PROMPT + f"\n\n{analysis_description}"
55
+ GENAI_MODEL = os.getenv("GENAI_MODEL")
56
+ response = client.models.generate_content(
57
+ model=GENAI_MODEL,
58
+ contents=[full_description, file],
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  )
60
+ return response.text
61
  except Exception as e:
62
  return f"Error from Gemini: {e}"
63
 
 
74
  )
75
 
76
 
77
+ class CodeFileReadTool(Tool):
78
+ name: ClassVar[str] = "read_code_file"
79
+ description: ClassVar[str] = (
80
+ "Read a code or text file (Python, JavaScript, Java, HTML, CSS, etc.) and return its content as a formatted code block with the correct language extension."
81
+ )
82
+ inputs: ClassVar[dict] = {
83
+ "file_path": {"type": "string", "description": "Path to the code or text file"},
 
84
  }
85
+ output_type: ClassVar[str] = "string"
86
 
87
+ def forward(self, file_path: str):
88
  try:
89
+ # Detect extension and map to language
90
+ ext = os.path.splitext(file_path)[-1].lower()
91
+ ext_to_lang = {
92
+ ".py": "python",
93
+ ".js": "javascript",
94
+ ".java": "java",
95
+ ".html": "html",
96
+ ".css": "css",
97
+ ".json": "json",
98
+ ".txt": "",
99
+ ".md": "markdown",
100
+ ".c": "c",
101
+ ".cpp": "cpp",
102
+ ".ts": "typescript",
103
+ ".sh": "bash",
104
+ ".xml": "xml",
105
+ ".yml": "yaml",
106
+ ".yaml": "yaml",
107
+ }
108
+ lang = ext_to_lang.get(ext, "")
109
+
110
+ with open(file_path, "r", encoding="utf-8") as f:
111
+ content = f.read()
112
+
113
+ # Format as markdown code block
114
+ if lang:
115
+ return f"```{lang}\n{content}\n```"
116
+ else:
117
+ return f"```\n{content}\n```"
118
  except Exception as e:
119
+ return f"Error reading file: {e}"
120
 
121
 
122
+ class ExcelAndCSVTableInspectorTool(Tool):
123
+ name: ClassVar[str] = "excel_csv_file_analyzer"
124
+ description: ClassVar[str] = (
125
+ "Load a CSV or Excel file and return table info and summary stats in Markdown format."
126
+ )
127
+ inputs: ClassVar[dict] = {
128
  "file_path": {
129
  "type": "string",
130
  "description": "Path to CSV or Excel file (.csv, .xls, .xlsx)",
131
  }
132
  }
133
+ output_type: ClassVar[str] = "string"
134
 
135
  def forward(self, file_path: str):
136
  try:
 
141
  else:
142
  return "Unsupported file type. Only CSV and Excel (.xls/.xlsx) are supported."
143
 
144
+ md = df.to_markdown(index=False, tablefmt="pipe")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  return md
147
 
 
150
 
151
 
152
  class YouTubeVideoAnalyzerTool(Tool):
153
+ name: ClassVar[str] = "youtube_video_analyzer"
154
+ description: ClassVar[str] = (
155
+ "Given a YouTube URL, extracts metadata, analyzes the video content, and answers user queries about it."
156
+ )
157
+ inputs: ClassVar[dict] = {
158
  "url": {"type": "string", "description": "Full YouTube video URL"},
159
  "user_prompt": {
160
  "type": "string",
161
  "description": "What you want to analyze from the video content",
162
  },
163
  }
164
+ output_type: ClassVar[str] = "string"
165
 
166
  def forward(self, url: str, user_prompt: str):
167
  try:
168
+ parsed_url = urlparse(url)
169
+ if (
170
+ parsed_url.scheme not in ["http", "https"]
171
+ or "youtube.com" not in parsed_url.netloc
172
+ ):
173
+ return "Invalid YouTube URL. Please provide a valid URL."
174
+
175
+ try:
176
+
177
+ user_final_prompt = """
178
+ You are an AI video analyzer. A user wants to analyze the following YouTube video.
179
+ Use your tools to extract information and analyze the video content.
180
+ ### User Request
181
+ {user_prompt}
182
+ ### Video URL
183
+ {url}
184
+ ### Instructions:
185
+ - Analyze the video content based on the user's request.
186
+ - Identify the main key thing needed to be analyzed.
187
+ - Provide the answer as per system prompt.
188
+ """
189
+
190
+ response = litellm.completion(
191
+ api_key=os.getenv("GEMINI_API_KEY"),
192
+ model=os.getenv("GEMINI_MODEL"),
193
+ messages=[
194
+ {"role": "system", "content": SYSTEM_PROMPT},
195
+ {
196
+ "role": "user",
197
+ "content": user_final_prompt.format(
198
+ user_prompt=user_prompt, url=url
199
+ ),
200
+ },
201
+ ],
202
+ )
203
+
204
+ return response["choices"][0]["message"]["content"]
205
+ except Exception as e:
206
+ if "Sign in" in str(e):
207
+ return "This video requires age verification or sign-in. Please provide a different video URL."
208
+ return f"Error accessing video: {str(e)}"
 
 
 
209
 
210
  except Exception as e:
211
  return f"Error analyzing video: {e}"
 
213
 
214
  # --- Math Tools ---
215
  class CalculatorTool(Tool):
216
+ name: ClassVar[str] = "calculator"
217
+ description: ClassVar[str] = (
218
  "Evaluate a basic mathematical expression (supports +, -, *, /, **, %, etc.)."
219
  )
220
+ inputs: ClassVar[dict] = {
221
  "expression": {
222
  "type": "string",
223
  "description": "A mathematical expression to evaluate",
224
  }
225
  }
226
+ output_type: ClassVar[str] = "number"
227
 
228
  def forward(self, expression: str):
229
  # Safely evaluate the expression using ast
 
254
 
255
  # Optionally, separate basic operations could be defined (e.g., add, subtract).
256
  class AddTool(Tool):
257
+ name: ClassVar[str] = "add"
258
+ description: ClassVar[str] = "Add two numbers together."
259
+ inputs: ClassVar[dict] = {
260
  "a": {"type": "number", "description": "First number"},
261
  "b": {"type": "number", "description": "Second number"},
262
  }
263
+ output_type: ClassVar[str] = "number"
264
 
265
  def forward(self, a: float, b: float):
266
  return a + b
267
 
268
 
269
  class MultiplyTool(Tool):
270
+ name: ClassVar[str] = "multiply"
271
+ description: ClassVar[str] = "Multiply two numbers."
272
+ inputs: ClassVar[dict] = {
273
  "a": {"type": "number", "description": "First number"},
274
  "b": {"type": "number", "description": "Second number"},
275
  }
276
+ output_type: ClassVar[str] = "number"
277
 
278
  def forward(self, a: float, b: float):
279
  return a * b
 
281
 
282
  # --- Date/Time Tools ---
283
  class DayOfWeekTool(Tool):
284
+ name: ClassVar[str] = "day_of_week"
285
+ description: ClassVar[str] = "Return the day of week for a given date (YYYY-MM-DD)."
286
+ inputs: ClassVar[dict] = {
287
+ "date": {"type": "string", "description": "Date in format YYYY-MM-DD"}
288
+ }
289
+ output_type: ClassVar[str] = "string"
290
 
291
  def forward(self, date: str):
292
  year, month, day = map(int, date.split("-"))
 
295
 
296
 
297
  class AddDaysTool(Tool):
298
+ name: ClassVar[str] = "add_days"
299
+ description: ClassVar[str] = "Add a number of days to a date (YYYY-MM-DD)."
300
+ inputs: ClassVar[dict] = {
301
  "date": {"type": "string", "description": "Start date (YYYY-MM-DD)"},
302
  "days": {"type": "integer", "description": "Number of days to add"},
303
  }
304
+ output_type: ClassVar[str] = "string"
305
 
306
  def forward(self, date: str, days: int):
307
  year, month, day = map(int, date.split("-"))
 
310
 
311
 
312
  class DateDiffTool(Tool):
313
+ name: ClassVar[str] = "date_diff"
314
+ description: ClassVar[str] = (
315
+ "Compute difference in days between two dates (YYYY-MM-DD)."
316
+ )
317
+ inputs: ClassVar[dict] = {
318
  "start_date": {"type": "string", "description": "First date (YYYY-MM-DD)"},
319
  "end_date": {"type": "string", "description": "Second date (YYYY-MM-DD)"},
320
  }
321
+ output_type: ClassVar[str] = "integer"
322
 
323
  def forward(self, start_date: str, end_date: str):
324
  y1, m1, d1 = map(int, start_date.split("-"))
 
330
 
331
  # --- Unit Conversion Tools ---
332
  class TempConvertTool(Tool):
333
+ name: ClassVar[str] = "convert_temperature"
334
+ description: ClassVar[str] = "Convert temperature between Celsius and Fahrenheit."
335
+ inputs: ClassVar[dict] = {
336
  "value": {"type": "number", "description": "Temperature value to convert"},
337
  "from_unit": {"type": "string", "description": "Unit of input ('C' or 'F')"},
338
  }
339
+ output_type: ClassVar[str] = "number"
340
 
341
  def forward(self, value: float, from_unit: str):
342
  unit = from_unit.strip().upper()
 
351
 
352
 
353
  class LengthConvertTool(Tool):
354
+ name: ClassVar[str] = "convert_length"
355
+ description: ClassVar[str] = (
356
+ "Convert length between kilometers, miles, meters, and feet."
357
+ )
358
+ inputs: ClassVar[dict] = {
359
  "value": {"type": "number", "description": "Length value to convert"},
360
  "from_unit": {
361
  "type": "string",
 
366
  "description": "Target unit ('km','mi','m','ft')",
367
  },
368
  }
369
+ output_type: ClassVar[str] = "number"
370
 
371
  def forward(self, value: float, from_unit: str, to_unit: str):
372
  u1 = from_unit.lower()
 
396
 
397
  # --- Text Tools ---
398
  class WordCountTool(Tool):
399
+ name: ClassVar[str] = "word_count"
400
+ description: ClassVar[str] = "Count the number of words in a text string."
401
+ inputs: ClassVar[dict] = {"text": {"type": "string", "description": "Input text"}}
402
+ output_type: ClassVar[str] = "integer"
403
 
404
  def forward(self, text: str):
405
  return len(text.split())
406
 
407
 
408
  class FindTextTool(Tool):
409
+ name: ClassVar[str] = "find_text"
410
+ description: ClassVar[str] = (
411
+ "Find occurrences of a substring in a text; returns count."
412
+ )
413
+ inputs: ClassVar[dict] = {
414
  "text": {"type": "string", "description": "Text to search in"},
415
  "query": {"type": "string", "description": "Substring to search for"},
416
  }
417
+ output_type: ClassVar[str] = "integer"
418
 
419
  def forward(self, text: str, query: str):
420
  return text.count(query)
 
422
 
423
  # --- List/Sequence Tools ---
424
  class SortListTool(Tool):
425
+ name: ClassVar[str] = "sort_list"
426
+ description: ClassVar[str] = "Sort a list of items (numbers or strings)."
427
+ inputs: ClassVar[dict] = {
428
+ "items": {"type": "array", "description": "List of items to sort"}
429
+ }
430
+ output_type: ClassVar[str] = "array"
431
 
432
  def forward(self, items):
433
  return sorted(items)
434
 
435
 
436
  class UniqueListTool(Tool):
437
+ name: ClassVar[str] = "unique_list"
438
+ description: ClassVar[str] = (
439
+ "Return a list with duplicate items removed (preserving order)."
440
+ )
441
+ inputs: ClassVar[dict] = {
442
+ "items": {"type": "array", "description": "List of items"}
443
+ }
444
+ output_type: ClassVar[str] = "array"
445
 
446
  def forward(self, items):
447
  seen = []
 
453
 
454
  # --- File I/O Tools ---
455
  class ReadFileTool(Tool):
456
+ name: ClassVar[str] = "read_file"
457
+ description: ClassVar[str] = "Read and return the contents of a text file."
458
+ inputs: ClassVar[dict] = {
459
+ "file_path": {"type": "string", "description": "Path to a text file"}
460
+ }
461
+ output_type: ClassVar[str] = "string"
462
 
463
  def forward(self, file_path: str):
464
  try:
 
469
 
470
 
471
  class WriteFileTool(Tool):
472
+ name: ClassVar[str] = "write_file"
473
+ description: ClassVar[str] = "Write a string to a text file (overwrites if exists)."
474
+ inputs: ClassVar[dict] = {
475
  "file_path": {"type": "string", "description": "Path to write the file"},
476
  "content": {"type": "string", "description": "Content to write"},
477
  }
478
+ output_type: ClassVar[str] = "string"
479
 
480
  def forward(self, file_path: str, content: str):
481
  with open(file_path, "w") as f:
 
485
 
486
  # --- Image Tool (stub) ---
487
  class ImageInfoTool(Tool):
488
+ name: ClassVar[str] = "image_info"
489
+ description: ClassVar[str] = "Load an image and report basic info (size and mode)."
490
+ inputs: ClassVar[dict] = {
491
+ "image_path": {"type": "string", "description": "Path to an image file"}
492
+ }
493
+ output_type: ClassVar[str] = "string"
494
 
495
  def forward(self, image_path: str):
496
  try:
 
500
  return f"Error loading image: {e}"
501
 
502
 
503
+ class WikipediaTool(Tool):
504
+ name: ClassVar[str] = "wikipedia_search_summary"
505
+ description: ClassVar[str] = "Search Wikipedia and return max 3 results"
506
+ inputs: ClassVar[dict] = {
507
+ "query": {
508
+ "type": "string",
509
+ "description": "Search Query for Wikipedia",
510
+ }
511
+ }
512
+ output_type: ClassVar[str] = "string"
513
+
514
+ def forward(self, query: str) -> str:
515
+ try:
516
+ search_docs = WikipediaLoader(query=query, load_max_docs=3).load()
517
+ formatted_search_docs = "\n\n---\n\n".join(
518
+ [
519
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
520
+ for doc in search_docs
521
+ ]
522
+ )
523
+ return {"wiki_results": formatted_search_docs}
524
+
525
+ except Exception as e:
526
+ return f"Error using Wikipedia API: {e}"
527
+
528
+
529
+ class ArvixSearchTool(Tool):
530
+ name: ClassVar[str] = "arvix_search"
531
+ description: ClassVar[str] = "Search Arvix for a query and return maximum 3 result"
532
+ inputs: ClassVar[dict] = {
533
+ "query": {
534
+ "type": "string",
535
+ "description": "Search Query for Arvix",
536
+ }
537
+ }
538
+ output_type: ClassVar[str] = "string"
539
+
540
+ def forward(self, query: str) -> str:
541
+ try:
542
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
543
+ formatted_search_docs = "\n\n---\n\n".join(
544
+ [
545
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
546
+ for doc in search_docs
547
+ ]
548
+ )
549
+ return {"arvix_results": formatted_search_docs}
550
+
551
+ except Exception as e:
552
+ return f"Error using Arvix Tool: {e}"
553
+
554
+
555
+ class GoogleSearchTool(Tool):
556
+ name: ClassVar[str] = "google_search"
557
+ description: ClassVar[str] = (
558
+ "Search the web using Google Search Engine and return results"
559
+ )
560
+ inputs: ClassVar[dict] = {
561
+ "query": {
562
+ "type": "string",
563
+ "description": "Search term to find information on Web",
564
+ }
565
+ }
566
+ output_type: ClassVar[str] = "string"
567
+
568
+ def forward(self, query: str) -> str:
569
+ try:
570
+ # Initialize Google Search API Wrapper
571
+ google_search = GoogleSearchAPIWrapper(
572
+ google_api_key=os.getenv("GOOGLE_API_KEY"),
573
+ google_cse_id=os.getenv("GOOGLE_CSE_ID"),
574
+ )
575
+
576
+ # Perform the search
577
+ results = google_search.results(query, num_results=5)
578
+ if not results:
579
+ return f"No results found for: '{query}'"
580
+
581
+ formatted = "\n\n".join(
582
+ f"{i+1}. **{r['title']}**\n{r['link']}\n{r['snippet']}"
583
+ for i, r in enumerate(results)
584
+ )
585
+
586
+ return f"**Search Results for '{query}':**\n\n{formatted}"
587
+
588
+ except Exception as e:
589
+ return f"Error using Google Search API: {e}"
590
+
591
+
592
+ class AdvanceGoogleAISearchTool(Tool):
593
+ name: ClassVar[str] = "google_ai_search"
594
+ description: ClassVar[str] = (
595
+ "Search the web using Google AI Search Engine and return results"
596
+ )
597
+ inputs: ClassVar[dict] = {
598
+ "query": {
599
+ "type": "string",
600
+ "description": "Search term to find information on Web",
601
+ }
602
+ }
603
+ output_type: ClassVar[str] = "string"
604
+
605
+ def forward(self, query: str) -> str:
606
+ try:
607
+ client = genai.Client(api_key=os.getenv("GEMINI_API_KEY"))
608
+ response = client.models.generate_content(
609
+ model=os.getenv("GENAI_MODEL"),
610
+ contents=[query],
611
+ )
612
+
613
+ if not response:
614
+ return f"No results found for: '{query}'"
615
+
616
+ return f"**Search Results for '{query}':**\n\n{response.text}"
617
+
618
+ except Exception as e:
619
+ return f"Error using Google AI Search API: {e}"
620
+
621
+
622
  # List of all available tools
623
  agent_tools = [
624
+ AnyTypeFileAnalyzerTool(),
625
+ ExcelAndCSVTableInspectorTool(),
 
626
  YouTubeVideoAnalyzerTool(),
627
  CalculatorTool(),
628
  AddTool(),
 
636
  FindTextTool(),
637
  SortListTool(),
638
  UniqueListTool(),
639
+ GoogleSearchTool(),
640
+ WikipediaSearchTool(),
641
+ AdvanceGoogleAISearchTool(),
 
642
  ]