Spaces:
Sleeping
Sleeping
gabriel-melki
commited on
Commit
·
f1eb571
1
Parent(s):
b4b094c
Package modification
Browse files- __pycache__/agent.cpython-313.pyc +0 -0
- __pycache__/prompt.cpython-313.pyc +0 -0
- agent.py +43 -3
- app.py +6 -203
- prompt.py +8 -6
- requirements.txt +2 -3
- submission.py +198 -0
- tools/audio_processing_tools.py +11 -0
- tools/image_processing_tools.py +43 -19
- tools/youtube_tools.py +6 -7
__pycache__/agent.cpython-313.pyc
DELETED
|
Binary file (1.09 kB)
|
|
|
__pycache__/prompt.cpython-313.pyc
DELETED
|
Binary file (4 kB)
|
|
|
agent.py
CHANGED
|
@@ -1,11 +1,51 @@
|
|
|
|
|
|
|
|
| 1 |
from smolagents import CodeAgent
|
| 2 |
from prompt import get_prompt
|
| 3 |
|
| 4 |
class QuestionAnsweringAgent(CodeAgent):
|
| 5 |
def __init__(self, *args, **kwargs):
|
| 6 |
super().__init__(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
def __call__(self, question_text, file_name) -> str:
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import glob
|
| 3 |
from smolagents import CodeAgent
|
| 4 |
from prompt import get_prompt
|
| 5 |
|
| 6 |
class QuestionAnsweringAgent(CodeAgent):
|
| 7 |
def __init__(self, *args, **kwargs):
|
| 8 |
super().__init__(*args, **kwargs)
|
| 9 |
+
|
| 10 |
+
def get_current_files(self):
|
| 11 |
+
"""Get a set of all files in the current working directory"""
|
| 12 |
+
try:
|
| 13 |
+
# Get all files in current directory (including hidden files)
|
| 14 |
+
all_files = set()
|
| 15 |
+
for pattern in ['*', '.*']:
|
| 16 |
+
all_files.update(glob.glob(pattern))
|
| 17 |
+
# Filter to only include actual files (not directories)
|
| 18 |
+
files = {f for f in all_files if os.path.isfile(f)}
|
| 19 |
+
return files
|
| 20 |
+
except Exception as e:
|
| 21 |
+
print(f"Error getting current files: {e}")
|
| 22 |
+
return set()
|
| 23 |
+
|
| 24 |
+
def cleanup_created_files(self, files_before):
|
| 25 |
+
"""Remove files that were created during execution"""
|
| 26 |
+
try:
|
| 27 |
+
files_after = self.get_current_files()
|
| 28 |
+
newly_created_files = files_after - files_before
|
| 29 |
+
|
| 30 |
+
for file_path in newly_created_files:
|
| 31 |
+
try:
|
| 32 |
+
if os.path.exists(file_path):
|
| 33 |
+
os.remove(file_path)
|
| 34 |
+
print(f"Cleaned up file: {file_path}")
|
| 35 |
+
except Exception as e:
|
| 36 |
+
print(f"Error cleaning up file {file_path}: {e}")
|
| 37 |
+
|
| 38 |
+
except Exception as e:
|
| 39 |
+
print(f"Error during cleanup: {e}")
|
| 40 |
|
| 41 |
def __call__(self, question_text, file_name) -> str:
|
| 42 |
+
# Take snapshot of files before execution
|
| 43 |
+
files_before = self.get_current_files()
|
| 44 |
+
|
| 45 |
+
try:
|
| 46 |
+
enhanced_question = get_prompt(question_text, file_name)
|
| 47 |
+
response = self.run(enhanced_question, reset=True)
|
| 48 |
+
return response
|
| 49 |
+
finally:
|
| 50 |
+
# Always clean up files, even if there's an exception
|
| 51 |
+
self.cleanup_created_files(files_before)
|
app.py
CHANGED
|
@@ -1,8 +1,3 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import gradio as gr
|
| 3 |
-
import requests
|
| 4 |
-
import pandas as pd
|
| 5 |
-
|
| 6 |
from smolagents import (
|
| 7 |
InferenceClientModel,
|
| 8 |
FinalAnswerTool
|
|
@@ -15,14 +10,14 @@ from tools.image_processing_tools import ask_question_about_image
|
|
| 15 |
|
| 16 |
from agent import QuestionAnsweringAgent
|
| 17 |
|
| 18 |
-
|
| 19 |
|
| 20 |
model = InferenceClientModel(
|
| 21 |
provider="auto",
|
| 22 |
model_id="Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
| 23 |
temperature=0,
|
| 24 |
top_p=1.0,
|
| 25 |
-
seed=42
|
| 26 |
)
|
| 27 |
|
| 28 |
agent_tools = [
|
|
@@ -37,204 +32,12 @@ agent = QuestionAnsweringAgent(
|
|
| 37 |
name="question_answering_expert",
|
| 38 |
model=model,
|
| 39 |
tools=agent_tools,
|
| 40 |
-
add_base_tools=True,
|
| 41 |
-
planning_interval=None,
|
| 42 |
-
additional_authorized_imports=["bs4"],
|
| 43 |
max_steps=10,
|
| 44 |
verbosity_level=2, # For better debugging
|
| 45 |
)
|
| 46 |
-
|
| 47 |
-
def run_and_submit_all( profile: gr.OAuthProfile | None):
|
| 48 |
-
"""
|
| 49 |
-
Fetches all questions, runs the BasicAgent on them, submits all answers,
|
| 50 |
-
and displays the results.
|
| 51 |
-
"""
|
| 52 |
-
# --- Determine HF Space Runtime URL and Repo URL ---
|
| 53 |
-
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
| 54 |
-
|
| 55 |
-
if profile:
|
| 56 |
-
username= f"{profile.username}"
|
| 57 |
-
print(f"User logged in: {username}")
|
| 58 |
-
else:
|
| 59 |
-
print("User not logged in.")
|
| 60 |
-
return "Please Login to Hugging Face with the button.", None
|
| 61 |
-
|
| 62 |
-
api_url = DEFAULT_API_URL
|
| 63 |
-
questions_url = f"{api_url}/questions"
|
| 64 |
-
submit_url = f"{api_url}/submit"
|
| 65 |
-
|
| 66 |
-
# 1. Instantiate Agent ( modify this part to create your agent)
|
| 67 |
-
try:
|
| 68 |
-
# Use the global agent that was already initialized
|
| 69 |
-
pass # agent is already defined globally
|
| 70 |
-
except Exception as e:
|
| 71 |
-
print(f"Error instantiating agent: {e}")
|
| 72 |
-
return f"Error initializing agent: {e}", None
|
| 73 |
-
# In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
|
| 74 |
-
agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
|
| 75 |
-
print(agent_code)
|
| 76 |
-
|
| 77 |
-
# 2. Fetch Questions
|
| 78 |
-
print(f"Fetching questions from: {questions_url}")
|
| 79 |
-
try:
|
| 80 |
-
response = requests.get(questions_url, timeout=15)
|
| 81 |
-
response.raise_for_status()
|
| 82 |
-
questions_data = response.json()
|
| 83 |
-
if not questions_data:
|
| 84 |
-
print("Fetched questions list is empty.")
|
| 85 |
-
return "Fetched questions list is empty or invalid format.", None
|
| 86 |
-
print(f"Fetched {len(questions_data)} questions.")
|
| 87 |
-
except requests.exceptions.RequestException as e:
|
| 88 |
-
print(f"Error fetching questions: {e}")
|
| 89 |
-
return f"Error fetching questions: {e}", None
|
| 90 |
-
except requests.exceptions.JSONDecodeError as e:
|
| 91 |
-
print(f"Error decoding JSON response from questions endpoint: {e}")
|
| 92 |
-
print(f"Response text: {response.text[:500]}")
|
| 93 |
-
return f"Error decoding server response for questions: {e}", None
|
| 94 |
-
except Exception as e:
|
| 95 |
-
print(f"An unexpected error occurred fetching questions: {e}")
|
| 96 |
-
return f"An unexpected error occurred fetching questions: {e}", None
|
| 97 |
-
|
| 98 |
-
# 3. Run your Agent
|
| 99 |
-
results_log = []
|
| 100 |
-
answers_payload = []
|
| 101 |
-
print(f"Running agent on {len(questions_data)} questions...")
|
| 102 |
-
for item in questions_data[:]:
|
| 103 |
-
task_id = item.get("task_id")
|
| 104 |
-
question_text = item.get("question")
|
| 105 |
-
file_name = item.get("file_name")
|
| 106 |
-
if not task_id or question_text is None:
|
| 107 |
-
print(f"Skipping item with missing task_id or question: {item}")
|
| 108 |
-
continue
|
| 109 |
-
try:
|
| 110 |
-
submitted_answer = agent(question_text, file_name)
|
| 111 |
-
|
| 112 |
-
# Log the execution timeline
|
| 113 |
-
print(f"Task {task_id} execution steps: {len(agent.memory.steps)}")
|
| 114 |
-
timeline_details = []
|
| 115 |
-
for i, step in enumerate(agent.memory.steps):
|
| 116 |
-
step_type = type(step).__name__
|
| 117 |
-
print(f" Step {i+1}: {step_type}")
|
| 118 |
-
timeline_details.append(f"Step {i+1}: {step_type}")
|
| 119 |
-
|
| 120 |
-
# Add timeline information to results
|
| 121 |
-
timeline_summary = "; ".join(timeline_details)
|
| 122 |
-
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 123 |
-
results_log.append({
|
| 124 |
-
"Task ID": task_id,
|
| 125 |
-
"Question": question_text,
|
| 126 |
-
"Submitted Answer": submitted_answer,
|
| 127 |
-
"Timeline": timeline_summary,
|
| 128 |
-
"Total Steps": len(agent.memory.steps)
|
| 129 |
-
})
|
| 130 |
-
except Exception as e:
|
| 131 |
-
print(f"Error running agent on task {task_id}: {e}")
|
| 132 |
-
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
|
| 133 |
-
|
| 134 |
-
if not answers_payload:
|
| 135 |
-
print("Agent did not produce any answers to submit.")
|
| 136 |
-
return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
|
| 137 |
-
|
| 138 |
-
# 4. Prepare Submission
|
| 139 |
-
submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 140 |
-
status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
|
| 141 |
-
print(status_update)
|
| 142 |
-
|
| 143 |
-
# 5. Submit
|
| 144 |
-
print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
|
| 145 |
-
try:
|
| 146 |
-
response = requests.post(submit_url, json=submission_data, timeout=60)
|
| 147 |
-
response.raise_for_status()
|
| 148 |
-
result_data = response.json()
|
| 149 |
-
final_status = (
|
| 150 |
-
f"Submission Successful!\n"
|
| 151 |
-
f"User: {result_data.get('username')}\n"
|
| 152 |
-
f"Overall Score: {result_data.get('score', 'N/A')}% "
|
| 153 |
-
f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
|
| 154 |
-
f"Message: {result_data.get('message', 'No message received.')}"
|
| 155 |
-
)
|
| 156 |
-
print("Submission successful.")
|
| 157 |
-
results_df = pd.DataFrame(results_log)
|
| 158 |
-
return final_status, results_df
|
| 159 |
-
except requests.exceptions.HTTPError as e:
|
| 160 |
-
error_detail = f"Server responded with status {e.response.status_code}."
|
| 161 |
-
try:
|
| 162 |
-
error_json = e.response.json()
|
| 163 |
-
error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
|
| 164 |
-
except requests.exceptions.JSONDecodeError:
|
| 165 |
-
error_detail += f" Response: {e.response.text[:500]}"
|
| 166 |
-
status_message = f"Submission Failed: {error_detail}"
|
| 167 |
-
print(status_message)
|
| 168 |
-
results_df = pd.DataFrame(results_log)
|
| 169 |
-
return status_message, results_df
|
| 170 |
-
except requests.exceptions.Timeout:
|
| 171 |
-
status_message = "Submission Failed: The request timed out."
|
| 172 |
-
print(status_message)
|
| 173 |
-
results_df = pd.DataFrame(results_log)
|
| 174 |
-
return status_message, results_df
|
| 175 |
-
except requests.exceptions.RequestException as e:
|
| 176 |
-
status_message = f"Submission Failed: Network error - {e}"
|
| 177 |
-
print(status_message)
|
| 178 |
-
results_df = pd.DataFrame(results_log)
|
| 179 |
-
return status_message, results_df
|
| 180 |
-
except Exception as e:
|
| 181 |
-
status_message = f"An unexpected error occurred during submission: {e}"
|
| 182 |
-
print(status_message)
|
| 183 |
-
results_df = pd.DataFrame(results_log)
|
| 184 |
-
return status_message, results_df
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
# --- Build Gradio Interface using Blocks ---
|
| 188 |
-
with gr.Blocks() as demo:
|
| 189 |
-
gr.Markdown("# Basic Agent Evaluation Runner")
|
| 190 |
-
gr.Markdown(
|
| 191 |
-
"""
|
| 192 |
-
**Instructions:**
|
| 193 |
-
|
| 194 |
-
1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
|
| 195 |
-
2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
|
| 196 |
-
3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
|
| 197 |
-
|
| 198 |
-
---
|
| 199 |
-
**Disclaimers:**
|
| 200 |
-
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).
|
| 201 |
-
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.
|
| 202 |
-
"""
|
| 203 |
-
)
|
| 204 |
-
|
| 205 |
-
gr.LoginButton()
|
| 206 |
-
|
| 207 |
-
run_button = gr.Button("Run Evaluation & Submit All Answers")
|
| 208 |
-
|
| 209 |
-
status_output = gr.Textbox(label="Run Status / Submission Result", lines=5, interactive=False)
|
| 210 |
-
# Removed max_rows=10 from DataFrame constructor
|
| 211 |
-
results_table = gr.DataFrame(label="Questions and Agent Answers", wrap=True)
|
| 212 |
-
|
| 213 |
-
run_button.click(
|
| 214 |
-
fn=run_and_submit_all,
|
| 215 |
-
outputs=[status_output, results_table]
|
| 216 |
-
)
|
| 217 |
|
| 218 |
if __name__ == "__main__":
|
| 219 |
-
|
| 220 |
-
# Check for SPACE_HOST and SPACE_ID at startup for information
|
| 221 |
-
space_host_startup = os.getenv("SPACE_HOST")
|
| 222 |
-
space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
|
| 223 |
-
|
| 224 |
-
if space_host_startup:
|
| 225 |
-
print(f"✅ SPACE_HOST found: {space_host_startup}")
|
| 226 |
-
print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
|
| 227 |
-
else:
|
| 228 |
-
print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
|
| 229 |
-
|
| 230 |
-
if space_id_startup: # Print repo URLs if SPACE_ID is found
|
| 231 |
-
print(f"✅ SPACE_ID found: {space_id_startup}")
|
| 232 |
-
print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
|
| 233 |
-
print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
|
| 234 |
-
else:
|
| 235 |
-
print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
|
| 236 |
-
|
| 237 |
-
print("-"*(60 + len(" App Starting ")) + "\n")
|
| 238 |
-
|
| 239 |
-
print("Launching Gradio Interface for Basic Agent Evaluation...")
|
| 240 |
-
demo.launch(debug=True, share=False)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from smolagents import (
|
| 2 |
InferenceClientModel,
|
| 3 |
FinalAnswerTool
|
|
|
|
| 10 |
|
| 11 |
from agent import QuestionAnsweringAgent
|
| 12 |
|
| 13 |
+
from submission import build_gradio_interface
|
| 14 |
|
| 15 |
model = InferenceClientModel(
|
| 16 |
provider="auto",
|
| 17 |
model_id="Qwen/Qwen3-Coder-30B-A3B-Instruct",
|
| 18 |
temperature=0,
|
| 19 |
top_p=1.0,
|
| 20 |
+
seed=42
|
| 21 |
)
|
| 22 |
|
| 23 |
agent_tools = [
|
|
|
|
| 32 |
name="question_answering_expert",
|
| 33 |
model=model,
|
| 34 |
tools=agent_tools,
|
| 35 |
+
add_base_tools=True,
|
| 36 |
+
planning_interval=None,
|
| 37 |
+
additional_authorized_imports=["os", "bs4", "PIL", "transformers", "torch", "requests", "glob"],
|
| 38 |
max_steps=10,
|
| 39 |
verbosity_level=2, # For better debugging
|
| 40 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
|
| 42 |
if __name__ == "__main__":
|
| 43 |
+
build_gradio_interface(agent)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
prompt.py
CHANGED
|
@@ -7,13 +7,15 @@ def get_prompt(question_text, file_name):
|
|
| 7 |
NEVER include words like: career, albums, list, biography, years, numbers, prepositions, or date ranges.
|
| 8 |
Examples:
|
| 9 |
- "Lionel Messi career" → use: wikipedia_summary("Lionel Messi")
|
| 10 |
-
- "Mercedes Sosa discography" → use: wikipedia_summary("Mercedes Sosa")
|
| 11 |
- "Battle of Hastings timeline" → use: wikipedia_summary("Battle of Hastings")
|
| 12 |
- "Population of Paris in 2010" → use: wikipedia_summary("Paris")
|
| 13 |
- If necessary, visit the wikipedia page listed in the wikipedia summary tool to read the full content. You will find the page url in the output of the wikipedia summary tool at the end after the **Read more:** section. Use the `read_wikipedia_page` tool to visit the page.
|
| 14 |
- When using the `read_wikipedia_page` tool, you may find tables in the page. To analyze the tables, please use a code snippet to read the tables into a pandas dataframe and analyze the data.
|
| 15 |
-
- If necessary,
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
| 17 |
- If the task requires reading, listening, or analyzing a file, you must use the file specified after the question, NOT the file name mentioned casually inside the question text.
|
| 18 |
- Comma separated lists MUST contain a single space after each comma.
|
| 19 |
- If you are asked for a number, don't use comma to write your number, nor use units such as $$ or percent sign unless specified otherwise.
|
|
@@ -37,8 +39,8 @@ def get_prompt(question_text, file_name):
|
|
| 37 |
-- beginning of question --
|
| 38 |
{question_text}
|
| 39 |
-- end of question --
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
|
| 44 |
return PROMPT
|
|
|
|
| 7 |
NEVER include words like: career, albums, list, biography, years, numbers, prepositions, or date ranges.
|
| 8 |
Examples:
|
| 9 |
- "Lionel Messi career" → use: wikipedia_summary("Lionel Messi")
|
|
|
|
| 10 |
- "Battle of Hastings timeline" → use: wikipedia_summary("Battle of Hastings")
|
| 11 |
- "Population of Paris in 2010" → use: wikipedia_summary("Paris")
|
| 12 |
- If necessary, visit the wikipedia page listed in the wikipedia summary tool to read the full content. You will find the page url in the output of the wikipedia summary tool at the end after the **Read more:** section. Use the `read_wikipedia_page` tool to visit the page.
|
| 13 |
- When using the `read_wikipedia_page` tool, you may find tables in the page. To analyze the tables, please use a code snippet to read the tables into a pandas dataframe and analyze the data.
|
| 14 |
+
- If necessary, download a youtube video using the `download_youtube_url_audio` or `download_youtube_url_images` tool to find possible sources of information. For the parameter `num_images`, use a large number if you need to have comprehensive information about the video.
|
| 15 |
+
- If necessary, analyze the audio or images downloaded from youtube using the `ask_question_about_image` tool to find possible sources of information.
|
| 16 |
+
- If necessary, perform a web search using the `web_search` tool to find possible sources of information.
|
| 17 |
+
- If necessary, please analyze the images downloaded using the `ask_question_about_image` tool to find possible sources of information.
|
| 18 |
+
- If the web search only returns titles and short snippets, you MUST visit the actual webpage using the `visit_webpage` tool to read the full content before answering.
|
| 19 |
- If the task requires reading, listening, or analyzing a file, you must use the file specified after the question, NOT the file name mentioned casually inside the question text.
|
| 20 |
- Comma separated lists MUST contain a single space after each comma.
|
| 21 |
- If you are asked for a number, don't use comma to write your number, nor use units such as $$ or percent sign unless specified otherwise.
|
|
|
|
| 39 |
-- beginning of question --
|
| 40 |
{question_text}
|
| 41 |
-- end of question --
|
| 42 |
+
|
| 43 |
+
IMPORTANT: If the question mentions the need to use a file, the file name is provided below.
|
| 44 |
+
file_name: {file_name}"""
|
| 45 |
|
| 46 |
return PROMPT
|
requirements.txt
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
gradio[oauth]==5.44.1
|
| 2 |
requests==2.32.5
|
| 3 |
-
smolagents==1.21.3
|
| 4 |
duckduckgo-search==8.1.1
|
| 5 |
ddgs==9.5.5
|
| 6 |
markdownify==0.11.0
|
|
@@ -11,5 +11,4 @@ beautifulsoup4==4.12.3
|
|
| 11 |
langchain_community==0.3.2
|
| 12 |
wikipedia==1.4.0
|
| 13 |
tabulate==0.9.0
|
| 14 |
-
yt-dlp==2025.9.5
|
| 15 |
-
transformers==4.46.1
|
|
|
|
| 1 |
gradio[oauth]==5.44.1
|
| 2 |
requests==2.32.5
|
| 3 |
+
smolagents[transformers]==1.21.3
|
| 4 |
duckduckgo-search==8.1.1
|
| 5 |
ddgs==9.5.5
|
| 6 |
markdownify==0.11.0
|
|
|
|
| 11 |
langchain_community==0.3.2
|
| 12 |
wikipedia==1.4.0
|
| 13 |
tabulate==0.9.0
|
| 14 |
+
yt-dlp==2025.9.5
|
|
|
submission.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import gradio as gr
|
| 3 |
+
import requests
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
| 9 |
+
SELECTED_QUESTIONS = [3]
|
| 10 |
+
def run_and_submit_all(agent, profile: gr.OAuthProfile | None):
|
| 11 |
+
"""
|
| 12 |
+
Fetches all questions, runs the BasicAgent on them, submits all answers,
|
| 13 |
+
and displays the results.
|
| 14 |
+
"""
|
| 15 |
+
# --- Determine HF Space Runtime URL and Repo URL ---
|
| 16 |
+
space_id = os.getenv("SPACE_ID") # Get the SPACE_ID for sending link to the code
|
| 17 |
+
|
| 18 |
+
if profile:
|
| 19 |
+
username= f"{profile.username}"
|
| 20 |
+
print(f"User logged in: {username}")
|
| 21 |
+
else:
|
| 22 |
+
print("User not logged in.")
|
| 23 |
+
return "Please Login to Hugging Face with the button.", None
|
| 24 |
+
|
| 25 |
+
api_url = DEFAULT_API_URL
|
| 26 |
+
questions_url = f"{api_url}/questions"
|
| 27 |
+
submit_url = f"{api_url}/submit"
|
| 28 |
+
|
| 29 |
+
# 1. Instantiate Agent ( modify this part to create your agent)
|
| 30 |
+
try:
|
| 31 |
+
agent = agent
|
| 32 |
+
except Exception as e:
|
| 33 |
+
print(f"Error instantiating agent: {e}")
|
| 34 |
+
return f"Error initializing agent: {e}", None
|
| 35 |
+
# In the case of an app running as a hugging Face space, this link points toward your codebase ( usefull for others so please keep it public)
|
| 36 |
+
agent_code = f"https://huggingface.co/spaces/{space_id}/tree/main"
|
| 37 |
+
print(agent_code)
|
| 38 |
+
|
| 39 |
+
# 2. Fetch Questions
|
| 40 |
+
print(f"Fetching questions from: {questions_url}")
|
| 41 |
+
try:
|
| 42 |
+
response = requests.get(questions_url, timeout=15)
|
| 43 |
+
response.raise_for_status()
|
| 44 |
+
questions_data = response.json()
|
| 45 |
+
if not questions_data:
|
| 46 |
+
print("Fetched questions list is empty.")
|
| 47 |
+
return "Fetched questions list is empty or invalid format.", None
|
| 48 |
+
print(f"Fetched {len(questions_data)} questions.")
|
| 49 |
+
except requests.exceptions.RequestException as e:
|
| 50 |
+
print(f"Error fetching questions: {e}")
|
| 51 |
+
return f"Error fetching questions: {e}", None
|
| 52 |
+
except requests.exceptions.JSONDecodeError as e:
|
| 53 |
+
print(f"Error decoding JSON response from questions endpoint: {e}")
|
| 54 |
+
print(f"Response text: {response.text[:500]}")
|
| 55 |
+
return f"Error decoding server response for questions: {e}", None
|
| 56 |
+
except Exception as e:
|
| 57 |
+
print(f"An unexpected error occurred fetching questions: {e}")
|
| 58 |
+
return f"An unexpected error occurred fetching questions: {e}", None
|
| 59 |
+
|
| 60 |
+
# 3. Run your Agent
|
| 61 |
+
results_log = []
|
| 62 |
+
answers_payload = []
|
| 63 |
+
is_correct_answers = []
|
| 64 |
+
print(f"Running agent on {len(questions_data)} questions...")
|
| 65 |
+
for item in np.array(questions_data).take(SELECTED_QUESTIONS):
|
| 66 |
+
task_id = item.get("task_id")
|
| 67 |
+
question_text = item.get("question")
|
| 68 |
+
file_name = item.get("file_name")
|
| 69 |
+
if not task_id or question_text is None:
|
| 70 |
+
print(f"Skipping item with missing task_id or question: {item}")
|
| 71 |
+
continue
|
| 72 |
+
try:
|
| 73 |
+
submitted_answer = agent(question_text, file_name)
|
| 74 |
+
answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
|
| 75 |
+
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
|
| 76 |
+
except Exception as e:
|
| 77 |
+
print(f"Error running agent on task {task_id}: {e}")
|
| 78 |
+
results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": f"AGENT ERROR: {e}"})
|
| 79 |
+
individual_submission_data = {
|
| 80 |
+
"username": username.strip(),
|
| 81 |
+
"agent_code": agent_code,
|
| 82 |
+
"answers": [{"task_id": task_id, "submitted_answer": submitted_answer}]
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
individual_response = requests.post(submit_url, json=individual_submission_data, timeout=60)
|
| 86 |
+
individual_response.raise_for_status()
|
| 87 |
+
individual_result_data = individual_response.json()
|
| 88 |
+
is_correct_answers.append(True if individual_result_data.get("correct_count", 0) == 1 else False)
|
| 89 |
+
if not answers_payload:
|
| 90 |
+
print("Agent did not produce any answers to submit.")
|
| 91 |
+
return "Agent did not produce any answers to submit.", pd.DataFrame(results_log)
|
| 92 |
+
|
| 93 |
+
# 4. Prepare Submission
|
| 94 |
+
submission_data = {"username": username.strip(), "agent_code": agent_code, "answers": answers_payload}
|
| 95 |
+
status_update = f"Agent finished. Submitting {len(answers_payload)} answers for user '{username}'..."
|
| 96 |
+
print(status_update)
|
| 97 |
+
|
| 98 |
+
# 5. Submit
|
| 99 |
+
print(f"Submitting {len(answers_payload)} answers to: {submit_url}")
|
| 100 |
+
try:
|
| 101 |
+
response = requests.post(submit_url, json=submission_data, timeout=60)
|
| 102 |
+
response.raise_for_status()
|
| 103 |
+
result_data = response.json()
|
| 104 |
+
final_status = (
|
| 105 |
+
f"Submission Successful!\n"
|
| 106 |
+
f"User: {result_data.get('username')}\n"
|
| 107 |
+
f"Overall Score: {result_data.get('score', 'N/A')}% "
|
| 108 |
+
f"({result_data.get('correct_count', '?')}/{result_data.get('total_attempted', '?')} correct)\n"
|
| 109 |
+
f"Message: {result_data.get('message', 'No message received.')}"
|
| 110 |
+
)
|
| 111 |
+
print("Submission successful.")
|
| 112 |
+
results_df = pd.DataFrame(results_log)
|
| 113 |
+
results_df["Is Correct"] = is_correct_answers
|
| 114 |
+
return final_status, results_df
|
| 115 |
+
except requests.exceptions.HTTPError as e:
|
| 116 |
+
error_detail = f"Server responded with status {e.response.status_code}."
|
| 117 |
+
try:
|
| 118 |
+
error_json = e.response.json()
|
| 119 |
+
error_detail += f" Detail: {error_json.get('detail', e.response.text)}"
|
| 120 |
+
except requests.exceptions.JSONDecodeError:
|
| 121 |
+
error_detail += f" Response: {e.response.text[:500]}"
|
| 122 |
+
status_message = f"Submission Failed: {error_detail}"
|
| 123 |
+
print(status_message)
|
| 124 |
+
results_df = pd.DataFrame(results_log)
|
| 125 |
+
results_df["Is Correct"] = is_correct_answers
|
| 126 |
+
return status_message, results_df
|
| 127 |
+
except requests.exceptions.Timeout:
|
| 128 |
+
status_message = "Submission Failed: The request timed out."
|
| 129 |
+
print(status_message)
|
| 130 |
+
results_df = pd.DataFrame(results_log)
|
| 131 |
+
return status_message, results_df
|
| 132 |
+
except requests.exceptions.RequestException as e:
|
| 133 |
+
status_message = f"Submission Failed: Network error - {e}"
|
| 134 |
+
print(status_message)
|
| 135 |
+
results_df = pd.DataFrame(results_log)
|
| 136 |
+
return status_message, results_df
|
| 137 |
+
except Exception as e:
|
| 138 |
+
status_message = f"An unexpected error occurred during submission: {e}"
|
| 139 |
+
print(status_message)
|
| 140 |
+
results_df = pd.DataFrame(results_log)
|
| 141 |
+
return status_message, results_df
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def build_gradio_interface(agent):
|
| 145 |
+
# --- Build Gradio Interface using Blocks ---
|
| 146 |
+
with gr.Blocks() as demo:
|
| 147 |
+
gr.Markdown("# Basic Agent Evaluation Runner")
|
| 148 |
+
gr.Markdown(
|
| 149 |
+
"""
|
| 150 |
+
**Instructions:**
|
| 151 |
+
1. Please clone this space, then modify the code to define your agent's logic, the tools, the necessary packages, etc ...
|
| 152 |
+
2. Log in to your Hugging Face account using the button below. This uses your HF username for submission.
|
| 153 |
+
3. Click 'Run Evaluation & Submit All Answers' to fetch questions, run your agent, submit answers, and see the score.
|
| 154 |
+
---
|
| 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 |
+
def run_with_login_state(profile: gr.OAuthProfile):
|
| 170 |
+
return run_and_submit_all(agent, profile)
|
| 171 |
+
|
| 172 |
+
run_button.click(
|
| 173 |
+
fn=run_with_login_state,
|
| 174 |
+
outputs=[status_output, results_table]
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
print("\n" + "-"*30 + " App Starting " + "-"*30)
|
| 178 |
+
# Check for SPACE_HOST and SPACE_ID at startup for information
|
| 179 |
+
space_host_startup = os.getenv("SPACE_HOST")
|
| 180 |
+
space_id_startup = os.getenv("SPACE_ID") # Get SPACE_ID at startup
|
| 181 |
+
|
| 182 |
+
if space_host_startup:
|
| 183 |
+
print(f"✅ SPACE_HOST found: {space_host_startup}")
|
| 184 |
+
print(f" Runtime URL should be: https://{space_host_startup}.hf.space")
|
| 185 |
+
else:
|
| 186 |
+
print("ℹ️ SPACE_HOST environment variable not found (running locally?).")
|
| 187 |
+
|
| 188 |
+
if space_id_startup: # Print repo URLs if SPACE_ID is found
|
| 189 |
+
print(f"✅ SPACE_ID found: {space_id_startup}")
|
| 190 |
+
print(f" Repo URL: https://huggingface.co/spaces/{space_id_startup}")
|
| 191 |
+
print(f" Repo Tree URL: https://huggingface.co/spaces/{space_id_startup}/tree/main")
|
| 192 |
+
else:
|
| 193 |
+
print("ℹ️ SPACE_ID environment variable not found (running locally?). Repo URL cannot be determined.")
|
| 194 |
+
|
| 195 |
+
print("-"*(60 + len(" App Starting ")) + "\n")
|
| 196 |
+
|
| 197 |
+
print("Launching Gradio Interface for Basic Agent Evaluation...")
|
| 198 |
+
demo.launch(debug=True, share=False)
|
tools/audio_processing_tools.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from whisper import load_model
|
| 2 |
+
from smolagents.tools import tool
|
| 3 |
+
|
| 4 |
+
@tool
|
| 5 |
+
def ask_question_about_audio(question: str, path_to_audio: str) -> str:
|
| 6 |
+
"""
|
| 7 |
+
Ask a question about an audio and return the answer.
|
| 8 |
+
"""
|
| 9 |
+
model = load_model("base")
|
| 10 |
+
res = model.transcribe(path_to_audio)
|
| 11 |
+
return res["text"]
|
tools/image_processing_tools.py
CHANGED
|
@@ -1,6 +1,24 @@
|
|
| 1 |
from PIL import Image
|
| 2 |
-
from transformers import
|
| 3 |
from smolagents.tools import tool
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
@tool
|
| 6 |
def ask_question_about_image(question: str, path_to_image: str) -> str:
|
|
@@ -12,24 +30,30 @@ def ask_question_about_image(question: str, path_to_image: str) -> str:
|
|
| 12 |
Returns:
|
| 13 |
A string with the answer to the question.
|
| 14 |
"""
|
| 15 |
-
|
|
|
|
| 16 |
|
| 17 |
-
|
|
|
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
|
|
|
|
| 1 |
from PIL import Image
|
| 2 |
+
from transformers import BlipProcessor, BlipForQuestionAnswering
|
| 3 |
from smolagents.tools import tool
|
| 4 |
+
import torch
|
| 5 |
+
import requests
|
| 6 |
+
import os
|
| 7 |
+
|
| 8 |
+
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
| 9 |
+
|
| 10 |
+
def _download_file(file_name: str) -> None:
|
| 11 |
+
"""Download file if it doesn't exist locally"""
|
| 12 |
+
try:
|
| 13 |
+
# Try to open the file to check if it exists
|
| 14 |
+
with open(file_name, 'rb') as f:
|
| 15 |
+
pass # File exists, do nothing
|
| 16 |
+
except FileNotFoundError:
|
| 17 |
+
# File doesn't exist, download it
|
| 18 |
+
url = f"{DEFAULT_API_URL}/files/{file_name.split('.')[-2]}"
|
| 19 |
+
r = requests.get(url)
|
| 20 |
+
with open(file_name, "wb") as f:
|
| 21 |
+
f.write(r.content)
|
| 22 |
|
| 23 |
@tool
|
| 24 |
def ask_question_about_image(question: str, path_to_image: str) -> str:
|
|
|
|
| 30 |
Returns:
|
| 31 |
A string with the answer to the question.
|
| 32 |
"""
|
| 33 |
+
# Download the file if it doesn't exist
|
| 34 |
+
_download_file(path_to_image)
|
| 35 |
|
| 36 |
+
# Check if CUDA is available and use GPU if possible, otherwise use CPU
|
| 37 |
+
device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
| 38 |
|
| 39 |
+
# Load the processor and model (using BLIP for more stable VQA)
|
| 40 |
+
processor = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base")
|
| 41 |
+
model = BlipForQuestionAnswering.from_pretrained("Salesforce/blip-vqa-base")
|
| 42 |
+
model = model.to(device)
|
| 43 |
+
|
| 44 |
+
# Load and process the image
|
| 45 |
+
image = Image.open(path_to_image).convert('RGB')
|
| 46 |
+
|
| 47 |
+
# Process the inputs
|
| 48 |
+
inputs = processor(image, question, return_tensors="pt")
|
| 49 |
+
inputs = {k: v.to(device) for k, v in inputs.items()}
|
| 50 |
+
|
| 51 |
+
# Generate the answer
|
| 52 |
+
with torch.no_grad():
|
| 53 |
+
outputs = model.generate(**inputs, max_length=50, num_beams=5)
|
| 54 |
+
|
| 55 |
+
# Decode and return the answer
|
| 56 |
+
answer = processor.decode(outputs[0], skip_special_tokens=True)
|
| 57 |
+
|
| 58 |
+
return answer
|
| 59 |
|
tools/youtube_tools.py
CHANGED
|
@@ -3,8 +3,6 @@ import os
|
|
| 3 |
import subprocess
|
| 4 |
from yt_dlp import YoutubeDL
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
from smolagents.tools import tool
|
| 9 |
|
| 10 |
# Use FFmpeg to extract frames from the video
|
|
@@ -54,13 +52,13 @@ def extract_frames_with_ffmpeg(video_path: str, num_frames: int) -> [str]:
|
|
| 54 |
@tool
|
| 55 |
def download_youtube_url_audio(url: str) -> str:
|
| 56 |
"""
|
| 57 |
-
Download a YouTube video and return the path to the downloaded file.
|
| 58 |
|
| 59 |
Args:
|
| 60 |
url (str): The URL of the YouTube video to download.
|
| 61 |
|
| 62 |
Returns:
|
| 63 |
-
str: The path to the downloaded file.
|
| 64 |
"""
|
| 65 |
ydl_audio_opts = {
|
| 66 |
'format': 'bestaudio/best',
|
|
@@ -76,20 +74,21 @@ def download_youtube_url_audio(url: str) -> str:
|
|
| 76 |
|
| 77 |
with YoutubeDL(ydl_audio_opts) as ydl:
|
| 78 |
file_path = ydl.extract_info(url)
|
|
|
|
| 79 |
return file_path['requested_downloads'][0]['filepath']
|
| 80 |
|
| 81 |
|
| 82 |
@tool
|
| 83 |
def download_youtube_url_images(url: str, num_images: int = 3) -> str:
|
| 84 |
"""
|
| 85 |
-
Download a YouTube video and return the path to the downloaded
|
| 86 |
|
| 87 |
Args:
|
| 88 |
url (str): The URL of the YouTube video to download.
|
| 89 |
-
num_images (int): The number of images to download.
|
| 90 |
|
| 91 |
Returns:
|
| 92 |
-
str: The different paths to the downloaded
|
| 93 |
"""
|
| 94 |
# First, download the video
|
| 95 |
ydl_images_opts = {
|
|
|
|
| 3 |
import subprocess
|
| 4 |
from yt_dlp import YoutubeDL
|
| 5 |
|
|
|
|
|
|
|
| 6 |
from smolagents.tools import tool
|
| 7 |
|
| 8 |
# Use FFmpeg to extract frames from the video
|
|
|
|
| 52 |
@tool
|
| 53 |
def download_youtube_url_audio(url: str) -> str:
|
| 54 |
"""
|
| 55 |
+
Download a YouTube video using the url, extract the audio and return the path to the downloaded file.
|
| 56 |
|
| 57 |
Args:
|
| 58 |
url (str): The URL of the YouTube video to download.
|
| 59 |
|
| 60 |
Returns:
|
| 61 |
+
str: The path to the downloaded audio file.
|
| 62 |
"""
|
| 63 |
ydl_audio_opts = {
|
| 64 |
'format': 'bestaudio/best',
|
|
|
|
| 74 |
|
| 75 |
with YoutubeDL(ydl_audio_opts) as ydl:
|
| 76 |
file_path = ydl.extract_info(url)
|
| 77 |
+
|
| 78 |
return file_path['requested_downloads'][0]['filepath']
|
| 79 |
|
| 80 |
|
| 81 |
@tool
|
| 82 |
def download_youtube_url_images(url: str, num_images: int = 3) -> str:
|
| 83 |
"""
|
| 84 |
+
Download a YouTube video using the url, extract the frames and return the path to the downloaded files.
|
| 85 |
|
| 86 |
Args:
|
| 87 |
url (str): The URL of the YouTube video to download.
|
| 88 |
+
num_images (int): The number of images to download. The images are extracted from the video at regular intervals.
|
| 89 |
|
| 90 |
Returns:
|
| 91 |
+
str: The different paths to the downloaded frames, separated by newlines.
|
| 92 |
"""
|
| 93 |
# First, download the video
|
| 94 |
ydl_images_opts = {
|