Spaces:
Running
Running
Merge branch 'feat/add-more-tools'
Browse files- .gitattributes +2 -0
- ShrewdAgent.py +13 -3
- app.py +11 -1
- data/7bd855d8-463d-4ed5-93ca-5fe35145f733.xlsx +3 -0
- data/99c9cc74-fdc8-46c6-8f8d-3ce2d3bfeea3.mp3 +3 -0
- data/f918266a-b3e0-4914-865d-4faa564f1aef.py +36 -0
- requirements.txt +3 -1
- tools/excel_to_text.py +32 -0
- tools/execute_python_code_from_file.py +38 -0
- tools/load_file.py +22 -0
- tools/maths.py +33 -0
- tools/sort_words_alphabetically.py +26 -0
- tools/transcribe_audio.py +42 -0
.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
|
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 |
-
|
|
|
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"))
|