Pycrolis commited on
Commit
e9e487d
·
2 Parent(s): efcf2db 4c07abc

Merge branch 'feat/add-more-tools'

Browse files
.gitattributes CHANGED
@@ -33,3 +33,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.mp3 filter=lfs diff=lfs merge=lfs -text
37
+ *.xlsx filter=lfs diff=lfs merge=lfs -text
ShrewdAgent.py CHANGED
@@ -13,7 +13,12 @@ from langgraph.pregel import PregelProtocol
13
  from loguru import logger
14
  from pydantic import SecretStr
15
 
 
 
 
16
  from tools.produce_classifier import produce_classifier
 
 
17
  from tools.web_page_information_extractor import web_page_information_extractor
18
  from tools.wikipedia_search import wikipedia_search
19
  from tools.youtube_transcript import youtube_transcript
@@ -44,6 +49,11 @@ class ShrewdAgent:
44
  web_page_information_extractor,
45
  youtube_transcript,
46
  produce_classifier,
 
 
 
 
 
47
  ]
48
  self.llm = ChatOpenAI(
49
  model="gpt-4o-mini",
@@ -60,7 +70,7 @@ class ShrewdAgent:
60
  logger.info("ShrewdAgent initialized.")
61
 
62
  def __call__(self, question: str) -> str:
63
- logger.info(f"Agent received question: {question}")
64
  accumulated_response = []
65
  try:
66
  for chunk in self.agent.stream(
@@ -72,10 +82,10 @@ class ShrewdAgent:
72
  ):
73
  assistant = chunk.get("assistant")
74
  if assistant:
75
- logger.debug(f"{assistant.get('messages')[0].pretty_repr()}")
76
  tools = chunk.get("tools")
77
  if tools:
78
- logger.debug(f"{tools.get('messages')[0].pretty_repr()}")
79
  accumulated_response.append(chunk)
80
 
81
  except GraphRecursionError as e:
 
13
  from loguru import logger
14
  from pydantic import SecretStr
15
 
16
+ from tools.excel_to_text import excel_to_text
17
+ from tools.execute_python_code_from_file import execute_python_code_from_file
18
+ from tools.maths import add_integers
19
  from tools.produce_classifier import produce_classifier
20
+ from tools.sort_words_alphabetically import sort_words_alphabetically
21
+ from tools.transcribe_audio import transcribe_audio
22
  from tools.web_page_information_extractor import web_page_information_extractor
23
  from tools.wikipedia_search import wikipedia_search
24
  from tools.youtube_transcript import youtube_transcript
 
49
  web_page_information_extractor,
50
  youtube_transcript,
51
  produce_classifier,
52
+ sort_words_alphabetically,
53
+ excel_to_text,
54
+ execute_python_code_from_file,
55
+ add_integers,
56
+ transcribe_audio,
57
  ]
58
  self.llm = ChatOpenAI(
59
  model="gpt-4o-mini",
 
70
  logger.info("ShrewdAgent initialized.")
71
 
72
  def __call__(self, question: str) -> str:
73
+ logger.info(f"Agent received question:\n{question}")
74
  accumulated_response = []
75
  try:
76
  for chunk in self.agent.stream(
 
82
  ):
83
  assistant = chunk.get("assistant")
84
  if assistant:
85
+ logger.debug(f"\n{assistant.get('messages')[0].pretty_repr()}")
86
  tools = chunk.get("tools")
87
  if tools:
88
+ logger.debug(f"\n{tools.get('messages')[0].pretty_repr()}")
89
  accumulated_response.append(chunk)
90
 
91
  except GraphRecursionError as e:
app.py CHANGED
@@ -78,11 +78,13 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
78
  for item in questions_data:
79
  task_id = item.get("task_id")
80
  question_text = item.get("question")
 
81
  if not task_id or question_text is None:
82
  print(f"Skipping item with missing task_id or question: {item}")
83
  continue
84
  try:
85
- submitted_answer = agent(question_text)
 
86
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
87
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
88
  except Exception as e:
@@ -173,6 +175,14 @@ with gr.Blocks() as demo:
173
  outputs=[status_output, results_table]
174
  )
175
 
 
 
 
 
 
 
 
 
176
  if __name__ == "__main__":
177
  print("\n" + "-"*30 + " App Starting " + "-"*30)
178
  # Check for SPACE_HOST and SPACE_ID at startup for information
 
78
  for item in questions_data:
79
  task_id = item.get("task_id")
80
  question_text = item.get("question")
81
+ file_name = item.get("file_name")
82
  if not task_id or question_text is None:
83
  print(f"Skipping item with missing task_id or question: {item}")
84
  continue
85
  try:
86
+ question_with_attachment = compute_question_with_attachment(question_text, task_id, file_name)
87
+ submitted_answer = agent(question_with_attachment)
88
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
89
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
90
  except Exception as e:
 
175
  outputs=[status_output, results_table]
176
  )
177
 
178
+
179
+ def compute_question_with_attachment(question: str, task_id: str, file_name: str) -> str:
180
+ if file_name:
181
+ return f"{question}\n\nAttached file: https://agents-course-unit4-scoring.hf.space/files/{task_id}"
182
+ else:
183
+ return question
184
+
185
+
186
  if __name__ == "__main__":
187
  print("\n" + "-"*30 + " App Starting " + "-"*30)
188
  # Check for SPACE_HOST and SPACE_ID at startup for information
data/7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:196b64ea8f72841cb0c4fc972ca82a28e74de926c402bb043305e12e05e012b7
3
+ size 5285
data/99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3.mp3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b218c951c1f888f0bbe6f46c080f57afc7c9348fffc7ba4da35749ff1e2ac40f
3
+ size 179304
data/f918266a-b3e0-4914-865d-4faa564f1aef.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ from random import randint
3
+
4
+
5
+ class UhOh(Exception):
6
+ pass
7
+
8
+ class Hmm:
9
+ def __init__(self):
10
+ self.value = randint(-100, 100)
11
+
12
+ def Yeah(self):
13
+ if self.value == 0:
14
+ return True
15
+ else:
16
+ raise UhOh()
17
+
18
+ def Okay():
19
+ while True:
20
+ yield Hmm()
21
+
22
+ def keep_trying(go, first_try=True):
23
+ maybe = next(go)
24
+ try:
25
+ if maybe.Yeah():
26
+ return maybe.value
27
+ except UhOh:
28
+ if first_try:
29
+ print("Working...")
30
+ print("Please wait patiently...")
31
+ time.sleep(0.1)
32
+ return keep_trying(go, first_try=False)
33
+
34
+ if __name__ == "__main__":
35
+ go = Okay()
36
+ print(f"{keep_trying(go)}")
requirements.txt CHANGED
@@ -11,4 +11,6 @@ beautifulsoup4~=4.13.4
11
  readability-lxml~=0.8.4.1
12
  youtube-transcript-api~=1.0.3
13
  wikipedia~=1.4.0
14
- langchain_tavily~=0.1.6
 
 
 
11
  readability-lxml~=0.8.4.1
12
  youtube-transcript-api~=1.0.3
13
  wikipedia~=1.4.0
14
+ langchain_tavily~=0.1.6
15
+ rizaio~=0.11.0
16
+ openai-whisper~=20240930
tools/excel_to_text.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from langchain_core.tools import tool
3
+ from loguru import logger
4
+
5
+ from tools.load_file import load_file
6
+
7
+
8
+ @tool("excel_to_text_tool", parse_docstring=True)
9
+ def excel_to_text(path_or_url: str) -> str:
10
+ """
11
+ Read an Excel file and return a text representation of the table data.
12
+
13
+ Args:
14
+ path_or_url (str): Either a local file path to an Excel file or a URL pointing to an Excel file.
15
+
16
+ Returns:
17
+ str: Text representation of the table data
18
+ """
19
+ logger.info(f"use excel_to_text with param: {path_or_url}")
20
+ try:
21
+ excel = load_file(path_or_url)
22
+ df = pd.read_excel(excel)
23
+ return df.to_markdown(index=False)
24
+
25
+ except Exception as e:
26
+ return f"Error reading Excel file: {str(e)}"
27
+
28
+
29
+ if __name__ == "__main__":
30
+ print(excel_to_text.invoke("../data/7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx"))
31
+ print(
32
+ excel_to_text.invoke("https://agents-course-unit4-scoring.hf.space/files/7bd855d8-463d-4ed5-93ca-5fe35145f733"))
tools/execute_python_code_from_file.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from langchain_core.tools import tool, ToolException
2
+ from loguru import logger
3
+ from rizaio import Riza
4
+
5
+ from tools.load_file import load_file
6
+
7
+
8
+ @tool("execute_python_code_from_file_tool", parse_docstring=True)
9
+ def execute_python_code_from_file(path_or_url: str) -> str:
10
+ """
11
+ Executes a given Python code snippet.
12
+
13
+ Args:
14
+ path_or_url (str): A string containing the Python code to execute.
15
+
16
+ Returns:
17
+ str: The standard output resulting from the execution of the code.
18
+ """
19
+ logger.info(f"execute_python_code_from_file with param: {path_or_url}")
20
+ code = load_file(path_or_url).read().decode("utf-8")
21
+ logger.debug(f"Python code:\n{code}")
22
+ client = Riza()
23
+ output = client.command.exec(
24
+ language="python",
25
+ code=code,
26
+ )
27
+
28
+ if output.exit_code > 0:
29
+ raise ToolException(
30
+ f"Riza code execution returned a non-zero exit code. "
31
+ f"The output captured from stderr was:\n{output.stderr}"
32
+ )
33
+ return output.stdout
34
+
35
+
36
+ if __name__ == "__main__":
37
+ print(execute_python_code_from_file.invoke("../data/f918266a-b3e0-4914-865d-4faa564f1aef.py"))
38
+ print(execute_python_code_from_file.invoke("https://agents-course-unit4-scoring.hf.space/files/f918266a-b3e0-4914-865d-4faa564f1aef"))
tools/load_file.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ from urllib.parse import urlparse
3
+
4
+ import requests
5
+
6
+
7
+ def load_file(path_or_url: str) -> io.BytesIO:
8
+ if _is_url(path_or_url):
9
+ response = requests.get(path_or_url)
10
+ response.raise_for_status()
11
+ return io.BytesIO(response.content)
12
+ else:
13
+ with open(path_or_url, 'rb') as f:
14
+ return io.BytesIO(f.read())
15
+
16
+
17
+ def _is_url(path_or_url):
18
+ try:
19
+ result = urlparse(path_or_url)
20
+ return result.scheme in ('http', 'https')
21
+ except ValueError:
22
+ return False
tools/maths.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+
3
+ from langchain_core.tools import tool
4
+ from loguru import logger
5
+
6
+
7
+ @tool("add_integers_tool", parse_docstring=True)
8
+ def add_integers(numbers: List[int]) -> int:
9
+ """
10
+ Add a list of integers together and return their sum.
11
+
12
+ This tool takes a list of integers and calculates their sum. It's useful for
13
+ performing basic arithmetic operations on multiple numbers at once.
14
+
15
+ Args:
16
+ numbers (List[int]): A list of integers to be summed.
17
+
18
+ Returns:
19
+ int: The sum of all integers in the input list.
20
+ """
21
+ logger.info(f"use add_integers with param: {numbers}")
22
+ if not numbers:
23
+ raise ValueError("Input list cannot be empty")
24
+
25
+ try:
26
+ return sum(numbers)
27
+ except TypeError as e:
28
+ raise TypeError("All elements in the list must be integers") from e
29
+
30
+
31
+ if __name__ == "__main__":
32
+ result = add_integers.invoke({"numbers": [10577, 10037, 10107, 9888, 10089, 10061, 10089, 9917, 10031]})
33
+ print(result)
tools/sort_words_alphabetically.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List
2
+
3
+ from langchain_core.tools import tool
4
+ from loguru import logger
5
+
6
+
7
+ @tool("sort_words_alphabetically_tool", parse_docstring=True)
8
+ def sort_words_alphabetically(words: List[str]) -> str:
9
+ """Sort a list of words alphabetically and join them with commas.
10
+
11
+ Args:
12
+ words (List[str]): List of strings to be sorted
13
+
14
+ Returns:
15
+ str: A string containing all sorted words joined with commas
16
+ """
17
+ logger.info(f"use sort_words_alphabetically with param: {words}")
18
+ sorted_words = sorted(words)
19
+
20
+ result = ", ".join(sorted_words)
21
+
22
+ return result
23
+
24
+
25
+ if __name__ == "__main__":
26
+ print(sort_words_alphabetically.invoke({"words": ["broccoli", "celery", "sweet potatoes", "lettuce"]}))
tools/transcribe_audio.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tempfile
2
+
3
+ import whisper
4
+ from langchain_core.tools import tool
5
+ from loguru import logger
6
+
7
+ from tools.load_file import load_file
8
+
9
+
10
+ @tool("transcribe_audio_tool", parse_docstring=True)
11
+ def transcribe_audio(file_name: str) -> str:
12
+ """
13
+ Convert speech from an audio file or URL to text.
14
+
15
+ Args:
16
+ file_name (str): Either a local file path to an audio file or a URL pointing to an audio file.
17
+
18
+
19
+ Returns:
20
+ str: The transcribed text from the audio file
21
+ """
22
+ logger.info(f"use transcribe_audio_tool with param: {file_name}")
23
+ model_name = "base"
24
+
25
+ try:
26
+ audio_bytes = load_file(file_name)
27
+ with tempfile.NamedTemporaryFile() as tmp:
28
+ tmp.write(audio_bytes.getvalue())
29
+ tmp.flush()
30
+ model = whisper.load_model(model_name)
31
+ result = model.transcribe(tmp.name, fp16=False)
32
+ return result["text"]
33
+
34
+ except Exception as e:
35
+ print(f"Error transcribing audio: {str(e)}")
36
+ return ""
37
+
38
+
39
+ if __name__ == "__main__":
40
+ print(transcribe_audio.invoke("../data/99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3.mp3"))
41
+ print(transcribe_audio.invoke(
42
+ "https://agents-course-unit4-scoring.hf.space/files/99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3"))