Surajgjadhav commited on
Commit
f6ac7a6
·
verified ·
1 Parent(s): 81917a3

feat: Added langgraph agent

Browse files
Files changed (5) hide show
  1. agent.py +113 -0
  2. app.py +63 -33
  3. requirements.txt +10 -1
  4. retriever.py +215 -0
  5. tools.py +96 -0
agent.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langgraph.prebuilt import ToolNode
2
+ from retriever import (
3
+ get_file,
4
+ extract_image_info,
5
+ file_retriever_tool,
6
+ fetch_text_from_url,
7
+ excel_data_retriever,
8
+ download_file_from_url,
9
+ image_decoder,
10
+ csv_data_retriever,
11
+ )
12
+ from tools import (
13
+ search_tool,
14
+ calc,
15
+ wiki_search,
16
+ arxiv_search,
17
+ run_python_code,
18
+ get_image_captioning,
19
+ )
20
+ from typing import List, TypedDict, Annotated, Optional
21
+ from langchain_core.messages import AnyMessage, SystemMessage
22
+ from langgraph.graph.message import add_messages
23
+ from langgraph.graph import START, StateGraph
24
+ from langgraph.prebuilt import tools_condition
25
+ from langchain_huggingface import HuggingFaceEndpoint, ChatHuggingFace
26
+
27
+ MODEL_NAME = "Qwen/Qwen3-Next-80B-A3B-Thinking"
28
+
29
+ SYSTEM_PROMPT = """
30
+ You are a literary data assistant.
31
+
32
+ ## Capabilities
33
+
34
+ - `fetch_text_from_url`: loads document text from a URL into the conversation.
35
+ - `search_tool`: search tool to access information from internet
36
+ - `calc`: Calulate expression
37
+ - `run_python_code`: execute given python code and return result of execution
38
+ - `wiki_search`: search documets on Wikipedia
39
+ - `arxiv_search`: Search research papaers on arxiv
40
+ - `download_file_from_url`: download and stoere file from url
41
+ - `extract_image_info`: extract information from image
42
+ - `file_retriever_tool`: extract file from GAIA API for given task_id
43
+ - `fetch_text_from_url`: fetch textual info from URL
44
+ - `excel_data_retriever`: retreieve data from excel file
45
+ - `image_decoder`: convert image from url to base64 decoded image
46
+ - `get_image_captioning`: get captioning for gibven image urls
47
+ - `csv_data_retriever`: retrieve data from csv file
48
+ """
49
+
50
+ llm = HuggingFaceEndpoint(
51
+ repo_id="Qwen/Qwen2.5-Coder-32B-Instruct",
52
+ )
53
+
54
+
55
+ class AgentState(TypedDict):
56
+ # The document provided
57
+ input_file: Optional[str] # Contains file path (PDF/PNG)
58
+ file_name: Optional[str]
59
+ task_id: Optional[str]
60
+ messages: Annotated[list[AnyMessage], add_messages]
61
+
62
+
63
+ tools = [
64
+ search_tool,
65
+ calc,
66
+ run_python_code,
67
+ wiki_search,
68
+ arxiv_search,
69
+ download_file_from_url,
70
+ extract_image_info,
71
+ file_retriever_tool,
72
+ fetch_text_from_url,
73
+ excel_data_retriever,
74
+ image_decoder,
75
+ get_image_captioning,
76
+ csv_data_retriever,
77
+ ]
78
+
79
+ tool_node = ToolNode(tools)
80
+
81
+
82
+ def assistant(state: AgentState):
83
+ sys_msg = SystemMessage(content=SYSTEM_PROMPT)
84
+ return {
85
+ "messages": [chat_with_tools.invoke([sys_msg] + state["messages"])],
86
+ "input_file": state["input_file"],
87
+ "file_name": state["file_name"],
88
+ "task_id": state["task_id"],
89
+ }
90
+
91
+
92
+ chat = ChatHuggingFace(llm=llm, verbose=True)
93
+ chat_with_tools = chat.bind_tools(tools)
94
+
95
+
96
+ def build_agent():
97
+ ## The graph
98
+ builder = StateGraph(AgentState)
99
+
100
+ # Define nodes: these do the work
101
+ builder.add_node("assistant", assistant)
102
+ builder.add_node("tools", ToolNode(tools))
103
+
104
+ # Define edges: these determine how the control flow moves
105
+ builder.add_edge(START, "assistant")
106
+ builder.add_conditional_edges(
107
+ "assistant",
108
+ # If the latest message requires a tool, route to tools
109
+ # Otherwise, provide a direct response
110
+ tools_condition,
111
+ )
112
+ builder.add_edge("tools", "assistant")
113
+ return builder.compile()
app.py CHANGED
@@ -3,32 +3,43 @@ import gradio as gr
3
  import requests
4
  import inspect
5
  import pandas as pd
 
 
6
 
7
  # (Keep Constants as is)
8
  # --- Constants ---
9
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
10
 
 
11
  # --- Basic Agent Definition ---
12
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
13
  class BasicAgent:
14
  def __init__(self):
15
  print("BasicAgent initialized.")
 
 
16
  def __call__(self, question: str) -> str:
17
  print(f"Agent received question (first 50 chars): {question[:50]}...")
18
- fixed_answer = "This is a default answer."
19
- print(f"Agent returning fixed answer: {fixed_answer}")
20
- return fixed_answer
 
 
 
 
 
21
 
22
- def run_and_submit_all( profile: gr.OAuthProfile | None):
 
23
  """
24
  Fetches all questions, runs the BasicAgent on them, submits all answers,
25
  and displays the results.
26
  """
27
  # --- Determine HF Space Runtime URL and Repo URL ---
28
- space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
29
 
30
  if profile:
31
- username= f"{profile.username}"
32
  print(f"User logged in: {username}")
33
  else:
34
  print("User not logged in.")
@@ -55,16 +66,16 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
55
  response.raise_for_status()
56
  questions_data = response.json()
57
  if not questions_data:
58
- print("Fetched questions list is empty.")
59
- return "Fetched questions list is empty or invalid format.", None
60
  print(f"Fetched {len(questions_data)} questions.")
61
  except requests.exceptions.RequestException as e:
62
  print(f"Error fetching questions: {e}")
63
  return f"Error fetching questions: {e}", None
64
  except requests.exceptions.JSONDecodeError as e:
65
- print(f"Error decoding JSON response from questions endpoint: {e}")
66
- print(f"Response text: {response.text[:500]}")
67
- return f"Error decoding server response for questions: {e}", None
68
  except Exception as e:
69
  print(f"An unexpected error occurred fetching questions: {e}")
70
  return f"An unexpected error occurred fetching questions: {e}", None
@@ -81,18 +92,36 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
81
  continue
82
  try:
83
  submitted_answer = agent(question_text)
84
- answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
85
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
 
 
 
 
 
 
 
 
86
  except Exception as e:
87
- print(f"Error running agent on task {task_id}: {e}")
88
- results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
 
 
 
 
 
 
89
 
90
  if not answers_payload:
91
  print("Agent did not produce any answers to submit.")
92
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
93
 
94
- # 4. Prepare Submission
95
- submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
 
 
 
 
96
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
97
  print(status_update)
98
 
@@ -143,8 +172,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
143
  # --- Build Gradio Interface using Blocks ---
144
  with gr.Blocks() as demo:
145
  gr.Markdown("# Basic Agent Evaluation Runner")
146
- gr.Markdown(
147
- """
148
  **Instructions:**
149
 
150
  1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
@@ -155,27 +183,25 @@ with gr.Blocks() as demo:
155
  **Disclaimers:**
156
  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).
157
  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.
158
- """
159
- )
160
 
161
  gr.LoginButton()
162
 
163
  run_button = gr.Button("Run Evaluation & Submit All Answers")
164
 
165
- status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
 
 
166
  # Removed max_rows=10 from DataFrame constructor
167
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
168
 
169
- run_button.click(
170
- fn=run_and_submit_all,
171
- outputs=[status_output, results_table]
172
- )
173
 
174
  if __name__ == "__main__":
175
- print("\n" + "-"*30 + " App Starting " + "-"*30)
176
  # Check for SPACE_HOST and SPACE_ID at startup for information
177
  space_host_startup = os.getenv("SPACE_HOST")
178
- space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
179
 
180
  if space_host_startup:
181
  print(f"✅ SPACE_HOST found: {space_host_startup}")
@@ -183,14 +209,18 @@ if __name__ == "__main__":
183
  else:
184
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
185
 
186
- if space_id_startup: # Print repo URLs if SPACE_ID is found
187
  print(f"✅ SPACE_ID found: {space_id_startup}")
188
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
189
- print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
 
 
190
  else:
191
- print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
 
 
192
 
193
- print("-"*(60 + len(" App Starting ")) + "\n")
194
 
195
  print("Launching Gradio Interface for Basic Agent Evaluation...")
196
- demo.launch(debug=True, share=False)
 
3
  import requests
4
  import inspect
5
  import pandas as pd
6
+ from agent import build_agent
7
+ from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage
8
 
9
  # (Keep Constants as is)
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
13
+
14
  # --- Basic Agent Definition ---
15
  # ----- THIS IS WERE YOU CAN BUILD WHAT YOU WANT ------
16
  class BasicAgent:
17
  def __init__(self):
18
  print("BasicAgent initialized.")
19
+ self.agent = build_agent()
20
+
21
  def __call__(self, question: str) -> str:
22
  print(f"Agent received question (first 50 chars): {question[:50]}...")
23
+ messages = [HumanMessage(content=question)]
24
+ fixed_answer = self.agent.invoke(messages)
25
+ if not isinstance(fixed_answer, dict):
26
+ return "Graph returned an unexpected result format."
27
+
28
+ if "messages" in fixed_answer:
29
+ return fixed_answer["messages"][-1].content
30
+ return f"Graph returned: {fixed_answer} (missing 'messages')"
31
 
32
+
33
+ def run_and_submit_all(profile: gr.OAuthProfile | None):
34
  """
35
  Fetches all questions, runs the BasicAgent on them, submits all answers,
36
  and displays the results.
37
  """
38
  # --- Determine HF Space Runtime URL and Repo URL ---
39
+ space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
40
 
41
  if profile:
42
+ username = f"{profile.username}"
43
  print(f"User logged in: {username}")
44
  else:
45
  print("User not logged in.")
 
66
  response.raise_for_status()
67
  questions_data = response.json()
68
  if not questions_data:
69
+ print("Fetched questions list is empty.")
70
+ return "Fetched questions list is empty or invalid format.", None
71
  print(f"Fetched {len(questions_data)} questions.")
72
  except requests.exceptions.RequestException as e:
73
  print(f"Error fetching questions: {e}")
74
  return f"Error fetching questions: {e}", None
75
  except requests.exceptions.JSONDecodeError as e:
76
+ print(f"Error decoding JSON response from questions endpoint: {e}")
77
+ print(f"Response text: {response.text[:500]}")
78
+ return f"Error decoding server response for questions: {e}", None
79
  except Exception as e:
80
  print(f"An unexpected error occurred fetching questions: {e}")
81
  return f"An unexpected error occurred fetching questions: {e}", None
 
92
  continue
93
  try:
94
  submitted_answer = agent(question_text)
95
+ answers_payload.append(
96
+ {"task_id": task_id, "submitted_answer": submitted_answer}
97
+ )
98
+ results_log.append(
99
+ {
100
+ "Task ID": task_id,
101
+ "Question": question_text,
102
+ "Submitted Answer": submitted_answer,
103
+ }
104
+ )
105
  except Exception as e:
106
+ print(f"Error running agent on task {task_id}: {e}")
107
+ results_log.append(
108
+ {
109
+ "Task ID": task_id,
110
+ "Question": question_text,
111
+ "Submitted Answer": f"AGENT ERROR: {e}",
112
+ }
113
+ )
114
 
115
  if not answers_payload:
116
  print("Agent did not produce any answers to submit.")
117
  return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
118
 
119
+ # 4. Prepare Submission
120
+ submission_data = {
121
+ "username": username.strip(),
122
+ "agent_code": agent_code,
123
+ "answers": answers_payload,
124
+ }
125
  status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
126
  print(status_update)
127
 
 
172
  # --- Build Gradio Interface using Blocks ---
173
  with gr.Blocks() as demo:
174
  gr.Markdown("# Basic Agent Evaluation Runner")
175
+ gr.Markdown("""
 
176
  **Instructions:**
177
 
178
  1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
 
183
  **Disclaimers:**
184
  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).
185
  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.
186
+ """)
 
187
 
188
  gr.LoginButton()
189
 
190
  run_button = gr.Button("Run Evaluation & Submit All Answers")
191
 
192
+ status_output = gr.Textbox(
193
+ label="Run Status / Submission Result", lines=5, interactive=False
194
+ )
195
  # Removed max_rows=10 from DataFrame constructor
196
  results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
197
 
198
+ run_button.click(fn=run_and_submit_all, outputs=[status_output, results_table])
 
 
 
199
 
200
  if __name__ == "__main__":
201
+ print("\n" + "-" * 30 + " App Starting " + "-" * 30)
202
  # Check for SPACE_HOST and SPACE_ID at startup for information
203
  space_host_startup = os.getenv("SPACE_HOST")
204
+ space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
205
 
206
  if space_host_startup:
207
  print(f"✅ SPACE_HOST found: {space_host_startup}")
 
209
  else:
210
  print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
211
 
212
+ if space_id_startup: # Print repo URLs if SPACE_ID is found
213
  print(f"✅ SPACE_ID found: {space_id_startup}")
214
  print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
215
+ print(
216
+ f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main"
217
+ )
218
  else:
219
+ print(
220
+ "ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined."
221
+ )
222
 
223
+ print("-" * (60 + len(" App Starting ")) + "\n")
224
 
225
  print("Launching Gradio Interface for Basic Agent Evaluation...")
226
+ demo.launch(debug=True, share=False)
requirements.txt CHANGED
@@ -1,2 +1,11 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ requests
3
+ langchain-huggingface
4
+ langchain-community
5
+ langchain-core
6
+ langchain_openai
7
+ langgraph
8
+ ddgs
9
+ pandas
10
+ openpyxl
11
+ pytesseract
retriever.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ from langchain_core.tools import Tool, tool
3
+ from typing import Optional
4
+ from PIL import Image
5
+ from io import BytesIO
6
+ import base64
7
+ import mimetypes
8
+ import urllib.error
9
+ import urllib.request
10
+ from urllib.parse import urlparse
11
+ import tempfile
12
+ import os
13
+ import pytesseract
14
+ import uuid
15
+ import pandas as pd
16
+
17
+ local_filename = "downloaded_data.xlsx"
18
+
19
+
20
+ # GAIA File Retriver for task_id
21
+ def get_file(task_id: str) -> str:
22
+ """fetches the file from GAIA API for given task_id"""
23
+ response = requests.get(
24
+ f"https://agents-course-unit4-scoring.hf.space/files/{task_id}", timeout=120
25
+ )
26
+ return response.content
27
+
28
+
29
+ file_retriever_tool = Tool(
30
+ name="get_file",
31
+ description="fetches the file from Agent API for given task_id",
32
+ func=get_file,
33
+ )
34
+
35
+
36
+ @tool
37
+ def download_file_from_url(url: str, filename: Optional[str] = None) -> str:
38
+ """
39
+ Download a file from a URL and save it to a temporary location.
40
+ Args:
41
+ url (str): the URL of the file to download.
42
+ filename (str, optional): the name of the file. If not provided, a random name file will be created.
43
+ """
44
+ try:
45
+ # Parse URL to get filename if not provided
46
+ if not filename:
47
+ path = urlparse(url).path
48
+ filename = os.path.basename(path)
49
+ if not filename:
50
+ filename = f"downloaded_{uuid.uuid4().hex[:8]}"
51
+
52
+ # Create temporary file
53
+ temp_dir = tempfile.gettempdir()
54
+ filepath = os.path.join(temp_dir, filename)
55
+
56
+ # Download the file
57
+ response = requests.get(url, stream=True, timeout=120)
58
+ response.raise_for_status()
59
+
60
+ # Save the file
61
+ with open(filepath, "wb") as f:
62
+ for chunk in response.iter_content(chunk_size=8192):
63
+ f.write(chunk)
64
+
65
+ return f"File downloaded to {filepath}. You can read this file to process its contents."
66
+ except Exception as e:
67
+ return f"Error downloading file: {str(e)}"
68
+
69
+
70
+ # text file retriever
71
+ @tool
72
+ def fetch_text_from_url(url: str) -> str:
73
+ """Fetch the document from a URL"""
74
+ req = urllib.request.Request(
75
+ url,
76
+ headers={"User-Agent": "Mozilla/5.0 (compatible; quickstart-research/1.0)"},
77
+ )
78
+ try:
79
+ with urllib.request.urlopen(req, timeout=120) as resp:
80
+ raw = resp.read()
81
+ except urllib.error.URLError as e:
82
+ return f"Fetch failed: {e}"
83
+ text = raw.decode("utf-8", errors="replace")
84
+ return text
85
+
86
+
87
+ # YT Video Frame Retriever
88
+
89
+
90
+ # Image Retriever
91
+ @tool
92
+ def image_decoder(img_url: str) -> str:
93
+ """downaload image from url and generate base64 decoded image url to read in local
94
+ Args:
95
+ img_url: url of image to download
96
+ """
97
+ try:
98
+ buffered = BytesIO()
99
+ headers = {
100
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
101
+ }
102
+ response = requests.get(img_url, headers, timeout=120)
103
+ mime, _ = mimetypes.guess_type(img_url)
104
+ image = Image.open(BytesIO(response.content))
105
+ img_mime = (
106
+ mime if mime else f"image/{image.format}" if image.format else "image/png"
107
+ )
108
+ img_format = image.format if image.format else "PNG"
109
+ image.save(buffered, format=img_format)
110
+ img_base64 = base64.b64encode(buffered.getvalue()).decode("utf-8")
111
+ buffered.close()
112
+ return f"data:{img_mime};base64,{img_base64}"
113
+ except Exception as e:
114
+ raise Exception(f"Error fetching image details from {img_url}: {str(e)}")
115
+
116
+
117
+ # @tool
118
+ # def extract_image_info(image_url: str) -> dict:
119
+ # """
120
+ # Extract text from an image file using a multimodal model.
121
+
122
+ # Args:
123
+ # image_url: The URL of the image to analyze
124
+
125
+ # Returns:
126
+ # A dictionary containing the description, table, and table content
127
+ # """
128
+ # try:
129
+ # img_url = image_decoder(image_url)
130
+ # all_text = ""
131
+ # message = [
132
+ # HumanMessage(
133
+ # content=[
134
+ # {
135
+ # "type": "text",
136
+ # "text": (
137
+ # "Extract all the text from this image. "
138
+ # "Return only the extracted text, no explanations."
139
+ # ),
140
+ # },
141
+ # {
142
+ # "type": "image_url",
143
+ # "image_url": {"url": img_url},
144
+ # },
145
+ # ]
146
+ # )
147
+ # ]
148
+
149
+ # response = vision_llm.invoke(message)
150
+ # all_text += response.content + "\n\n"
151
+ # return all_text.strip()
152
+ # except Exception as e:
153
+ # return f"Error extracting image details from {image_url}: {str(e)}"
154
+
155
+
156
+ @tool
157
+ def extract_image_info(image_url: str) -> str:
158
+ """
159
+ Extract text from an image file using a OCR library pytesseract (if available).
160
+
161
+ Args:
162
+ image_url: The URL of the image to analyze
163
+
164
+ Returns:
165
+ A text containing the description of image
166
+ """
167
+ # 1. Fetch the image from the URL
168
+ response = requests.get(image_url, timeout=120)
169
+
170
+ # 2. Open the image from the downloaded bytes
171
+ img = Image.open(BytesIO(response.content))
172
+
173
+ # 3. Perform OCR
174
+ text = pytesseract.image_to_string(img)
175
+ return text
176
+
177
+
178
+ # Excel File Reader
179
+ @tool
180
+ def excel_data_retriever(file_path: str):
181
+ """Download and read the excel file using given excel file url.
182
+
183
+ Args:
184
+ file_path: path of excel file to process
185
+ """
186
+ try:
187
+ df = pd.read_excel(file_path)
188
+ df_json = df.to_json()
189
+ result = (
190
+ f"Excel file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
191
+ )
192
+ result += f"Detailed Info of records as given in JSON format: \n Here the JSON is created by columns, so each key in JOSN represent column names with its values\n {df_json}"
193
+ print(df.to_json())
194
+ return result
195
+ except Exception as e:
196
+ return f"Error reading excel file {file_path}: {str(e)}"
197
+
198
+
199
+ # CSV File Reader
200
+ @tool
201
+ def csv_data_retriever(file_path: str):
202
+ """Download and read the csv file using given csv file url.
203
+
204
+ Args:
205
+ file_path: path of csv file to process
206
+ """
207
+ try:
208
+ df = pd.read_csv(file_path)
209
+ df_json = df.to_json()
210
+ result = f"CSV file loaded with {len(df)} rows and {len(df.columns)} columns.\n"
211
+ result += f"Detailed Info of records as given in JSON format: \n Here the JSON is created by columns, so each key in JOSN represent column names with its values\n {df_json}"
212
+ print(df.to_json())
213
+ return result
214
+ except Exception as e:
215
+ return f"Error reading CSV file {file_path}: {str(e)}"
tools.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_community.tools import DuckDuckGoSearchRun
2
+ from langchain_community.document_loaders import (
3
+ WikipediaLoader,
4
+ ArxivLoader,
5
+ ImageCaptionLoader,
6
+ )
7
+ from langchain_core.tools import tool
8
+ from typing import List
9
+ import math
10
+
11
+ # Web_search Tool
12
+ search_tool = DuckDuckGoSearchRun()
13
+
14
+
15
+ # Calulator Tool
16
+ @tool(
17
+ "calculator",
18
+ description="Performs arithmetic calculations. Use this for any math problems.",
19
+ )
20
+ def calc(expression: str) -> str:
21
+ """Evaluate mathematical expressions.
22
+ Args:
23
+ expression: expression to evaluate
24
+ """
25
+ try:
26
+ cleaned = expression.replace("^", "**").replace(",", "")
27
+ safe_ns = {k: getattr(math, k) for k in dir(math) if not k.startswith("_")}
28
+ safe_ns["__builtins__"] = {}
29
+ result = eval(cleaned, safe_ns)
30
+ return str(result)
31
+ except Exception as e:
32
+ return f"Calculation error: {e}"
33
+
34
+
35
+ @tool
36
+ def wiki_search(query: str) -> str:
37
+ """
38
+ Search Wikipedia for a query and return maximum 2 results.
39
+ Args:
40
+ query: The search query.
41
+ """
42
+ search_docs = WikipediaLoader(query=query, load_max_docs=2).load()
43
+ formatted_search_docs = "\n\n----\n\n".join(
44
+ [
45
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content}\n</Document>'
46
+ for doc in search_docs
47
+ ]
48
+ )
49
+ return {"wiki_results": formatted_search_docs}
50
+
51
+
52
+ @tool
53
+ def arxiv_search(query: str) -> str:
54
+ """
55
+ Search Arxiv for a query and return maximum 3 result.
56
+ Args:
57
+ query: The search query.
58
+ """
59
+ search_docs = ArxivLoader(query=query, load_max_docs=3).load()
60
+ formatted_search_docs = "\n\n----\n\n".join(
61
+ [
62
+ f'<Document source="{doc.metadata["source"]}" page="{doc.metadata.get("page", "")}"/>\n{doc.page_content[:1000]}\n</Document>'
63
+ for doc in search_docs
64
+ ]
65
+ )
66
+ return {"arxiv_results": formatted_search_docs}
67
+
68
+
69
+ @tool
70
+ def run_python_code(code_str: str) -> str:
71
+ """Executes the provided Python code string and returns the result."""
72
+ try:
73
+ exec_globals = {}
74
+ exec(code_str, exec_globals)
75
+ return str(exec_globals.get("result", "Execution successful"))
76
+ except Exception as e:
77
+ return f"Error: {str(e)}"
78
+
79
+
80
+ @tool
81
+ def get_image_captioning(list_image_urls: List[str]) -> str:
82
+ """
83
+ generate captions for images
84
+
85
+ Args:
86
+ list_image_urls: list of image urls to process
87
+ """
88
+ try:
89
+ loader = ImageCaptionLoader(images=list_image_urls)
90
+ list_docs = loader.load()
91
+ result = "Captions for given images: \n"
92
+ for doc in list_docs:
93
+ result += f"Image: {doc.metadata.__str__()}, Caption:{doc.page_content}"
94
+ return result
95
+ except Exception as e:
96
+ return f"Image captioning failied: {str(e)}"