Nicolas Fremondiere commited on
Commit
d919051
·
1 Parent(s): 81917a3

agent v_1

Browse files

All the common features are present
Video/audio/image analysis
Code analysis
Websearch/wikipedia search

.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .env
2
+ __pycache__
agent/agent.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from smolagents import Tool, tool
2
+ import os
3
+ import asyncio
4
+
5
+ from Final_Assignment_Template.agent.ressources_getter import save_file, \
6
+ get_prompt_with_file_content, extract_text_from_file, convert_excel_to_text, delete_tmp_files
7
+ from get_question.hf_api import get_file_by_task_id
8
+ from Final_Assignment_Template.agent.agent_websearch import create_wikipedia_agent
9
+ from Final_Assignment_Template.agent.agent_multimedia import create_multimedia_agent
10
+ from Final_Assignment_Template.agent.agent_code import create_code_analyzer_agent
11
+ from smolagents import ToolCallingAgent, LiteLLMModel
12
+ from dotenv import load_dotenv
13
+
14
+ load_dotenv()
15
+
16
+ agent_wikipedia = create_wikipedia_agent()
17
+ agent_multimedia = create_multimedia_agent()
18
+ agent_code = create_code_analyzer_agent()
19
+
20
+ class WikipediaAgentTool(Tool):
21
+ name = "WikipediaAgentTool"
22
+ description = "Delegates tasks to the WebSearch agent."
23
+ inputs = {"query": {"type": "string", "description": "Search query for Wikipedia or Web search."}}
24
+ output_type = "string"
25
+ def forward(self, query: str) -> str:
26
+ return agent_wikipedia.run(query)
27
+
28
+ class MediaAgentTool(Tool):
29
+ name = "MediaAgentTool"
30
+ description = """Delegates tasks to the Media agent. It can search for videos on YouTube to download them localy.
31
+ It can also analyze local video,image and music content."""
32
+ inputs = {"query": {"type": "string", "description": "Search query with a Youtube url or query with the file path."}}
33
+ output_type = "string"
34
+
35
+ def forward(self, query: str) -> str:
36
+ # Utilise l'agent vidéo pour rechercher sur YouTube
37
+ return agent_multimedia.run(query)
38
+
39
+
40
+ class CodeAgentTool(Tool):
41
+ name = "CodeAgentTool"
42
+ description = """Delegates tasks to the Code agent. It can execute Python code and return the output."""
43
+ inputs = {"query": {"type": "string", "description": "Query with file path of the code."}}
44
+ output_type = "string"
45
+
46
+ def forward(self, query: str) -> str:
47
+ # Utilise l'agent code pour exécuter du code Python
48
+ return agent_code.run(query)
49
+
50
+
51
+
52
+ gemini_flash_manager_model = LiteLLMModel(model_id="gemini/gemini-2.5-flash", api_key=os.getenv("GOOGLE_API_KEY"))
53
+
54
+
55
+ manager_agent = ToolCallingAgent(
56
+ tools=[WikipediaAgentTool(),MediaAgentTool(),CodeAgentTool(),extract_text_from_file,convert_excel_to_text,delete_tmp_files], # Les agents workers sont des outils pour le manager
57
+ model=gemini_flash_manager_model,
58
+ name="ManagerAgent",
59
+ description=(
60
+ "Je suis un agent manager. Mon rôle est de comprendre les requêtes complexes, "
61
+ "de les décomposer en tâches plus petites et de déléguer ces tâches "
62
+ "aux agents spécialisés (WikiAgent) pour obtenir la meilleure réponse. "
63
+ "Je coordonne leurs actions et synthétise les résultats."
64
+ "Après avoir répondu à la requête, je supprime les fichiers temporaires téléchargés pour économiser de l'espace disque."
65
+ "Si tu ne trouve pas une réponse satisfaisante sous 5 étapes ou que tu n'as pas les outils pour y répondre, renvoie une chaine de caractères vide."
66
+ )
67
+ )
68
+
69
+ # Pour interagir avec l'agent manager
70
+ async def run_multi_agent_system(prompt: str):
71
+ print(f"\nRequête au Manager : {prompt}")
72
+ return manager_agent.run(prompt)
73
+
74
+
75
+ class BasicAgent:
76
+ task_id:str
77
+ file_content_type:str
78
+ agent = manager_agent
79
+ def __init__(self):
80
+ print("BasicAgent initialized.")
81
+ def __call__(self, question: str, task_id :str) -> str:
82
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
83
+ file_bytes,file_name=get_file_by_task_id(task_id)
84
+ print(file_name)
85
+ file_path = save_file(file_bytes,file_name)
86
+ prompt = get_prompt_with_file_content(question,file_path)
87
+ answer = asyncio.run(run_multi_agent_system(prompt))
88
+ print(f"Agent returning fixed answer: {answer}")
89
+ return answer
90
+
91
+
92
+ # if __name__ == "__main__":
93
+ # agent = BasicAgent()
94
+ # question = "Review the chess position provided in the image. It is black's turn. Provide the correct next move for black which guarantees a win. Please provide your response in algebraic notation."
95
+ # task_id = "cca530fc-4052-43b2-b130-b30968d8aa44"
96
+ # response = agent(question,task_id)
97
+ # print(response)
agent/agent_code.py ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from dotenv import load_dotenv
4
+ from smolagents import CodeAgent, LiteLLMModel, tool
5
+
6
+ load_dotenv() # Charge les variables d'environnement
7
+ llm_model = LiteLLMModel(model_id="mistral/codestral-latest", api_key=os.getenv("MISTRAL_API_KEY"))
8
+
9
+
10
+ def create_code_analyzer_agent():
11
+ """
12
+ Crée un agent simple pour exécuter du code et obtenir sa sortie.
13
+
14
+ Returns:
15
+ CodeAgent: Agent configuré pour l'exécution de code
16
+ """
17
+ # Initialiser le modèle Mistral Codestral
18
+ model = LiteLLMModel(
19
+ model_id="mistral/codestral-latest",
20
+ temperature=0.1, # Déterministe pour l'exécution
21
+ max_tokens=2000,
22
+ )
23
+
24
+ # Instructions système simples et directes
25
+ system_prompt = """
26
+ Tu es un exécuteur de code Python. Ton rôle est simple : exécuter le code fourni et donner sa sortie finale.
27
+
28
+ Quand on te donne du code à analyser :
29
+ 1. Exécute directement le code Python
30
+ 2. Capture toute la sortie (print, résultats, etc.)
31
+ 3. Retourne la sortie finale
32
+
33
+ Sois concis et direct dans tes réponses. Concentre-toi uniquement sur l'exécution et le résultat.
34
+ """
35
+
36
+ code_analyzer_agent = CodeAgent(
37
+ model=model,
38
+ tools=[],
39
+ description=system_prompt,
40
+ max_steps=3, # Peu d'étapes nécessaires
41
+ )
42
+
43
+ return code_analyzer_agent
44
+
45
+
agent/agent_multimedia.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Fonction pour lire et encoder une image en base64
2
+ import base64
3
+ import os
4
+ from pathlib import Path
5
+
6
+ import yt_dlp
7
+ from dotenv import load_dotenv
8
+ from smolagents import tool, ToolCallingAgent, LiteLLMModel
9
+ import google.generativeai as genai
10
+ from ressources_getter import DOWNLOAD_FOLDER, delete_tmp_files, create_download_folder_if_not_exist
11
+ import mimetypes
12
+
13
+
14
+ load_dotenv()
15
+
16
+
17
+ @tool
18
+ def download_video_youtube(url : str)-> str | None:
19
+ """
20
+ Télécharge une vidéo YouTube dans un dossier local.
21
+ Args:
22
+ url (str): URL de la vidéo YouTube
23
+ Returns:
24
+ file_path (str): Chemin du fichier téléchargé ou None en cas d'erreur
25
+ """
26
+ try:
27
+ # Créer le dossier de téléchargement s'il n'existe pas
28
+ create_download_folder_if_not_exist()
29
+ file_path = f'{DOWNLOAD_FOLDER}/tmp_video.mp4'
30
+ # Configuration pour yt-dlp
31
+ ydl_opts = {
32
+ 'outtmpl': file_path, # Format du nom de fichier
33
+ 'format': 'best[height<=720]', # Qualité maximale 720p (ajustable)
34
+ 'noplaylist': True, # Télécharger seulement la vidéo, pas la playlist
35
+ }
36
+
37
+ # Télécharger la vidéo
38
+ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
39
+ print(f"Téléchargement de: {url}")
40
+ ydl.download([url])
41
+ print("Téléchargement terminé avec succès!")
42
+ return file_path
43
+
44
+ except Exception as e:
45
+ print(f"Erreur lors du téléchargement: {str(e)}")
46
+ return None
47
+
48
+
49
+ @tool
50
+ def analyze_media_content(media_path: str, user_prompt: str) -> str:
51
+ """
52
+ Analyse le contenu du média en utilisant Gemini 2.5 Flash via le SDK Google Generative AI.
53
+ (Ce n'est pas LiteLLM qui fait l'appel multimodal ici, mais le SDK direct pour gérer les parties vidéo).
54
+ Args:
55
+ media_path (str): Chemin du fichier local à analyser.
56
+ user_prompt (str): Requête de l'utilisateur pour guider l'analyse.
57
+ Returns:
58
+ str: Résultat de l'analyse du fichier.
59
+ """
60
+ try:
61
+
62
+
63
+ with open(media_path, "rb") as file:
64
+ file_bytes = file.read()
65
+ encoded_video = base64.b64encode(file_bytes).decode('utf-8')
66
+
67
+ # Déterminer le type MIME du fichier
68
+ mime_type, encoding = mimetypes.guess_type(media_path)
69
+
70
+ file_part = {
71
+ "inline_data": {
72
+ "mime_type": mime_type,
73
+ "data": encoded_video
74
+ }
75
+ }
76
+ genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) # Assure que l'API Key est configurée
77
+ model = genai.GenerativeModel('gemini-2.5-flash') # Utilise le modèle directement via le SDK
78
+ response = model.generate_content([user_prompt, file_part])
79
+ return response.text
80
+
81
+ except Exception as e:
82
+ return f"Erreur lors de l'analyse de la vidéo : {e}"
83
+
84
+
85
+ def create_multimedia_agent():
86
+ """
87
+ Crée un agent multimédia capable de télécharger et d'analyser des vidéos YouTube.
88
+ Il peut également analyser des fichiers multimédias locaux (vidéos, images, musique).
89
+ Returns:
90
+ BasicAgent: Instance de l'agent multimédia.
91
+ """
92
+ gemini_flash_vlm_model = LiteLLMModel(model_id="gemini/gemini-2.5-flash", api_key=os.getenv("GOOGLE_API_KEY"))
93
+
94
+ system_prompt = """Tu es un expert en analyse de vidéo,image et fichier son. Ton rôle est d'analyser les fichiers fournies en utilisant les outils à ta disposition.
95
+ Lorsque tu reçois un lien d'une vidéo youtube, utilise la fonction 'download_video_youtube' pour télécharger la vidéo.
96
+ Si tu as un fichier video,image ou son local, utilise la fonction 'analyze_media_content' pour analyser le contenu.
97
+ Utilise tes outils pour répondre aux questions de l'utilisateur concernant le contenu du fichier.
98
+ Fait bien attention à supprimer les fichiers temporaires téléchargées après analyse pour économiser de l'espace disque.
99
+ Fournis un réponse concise et sans points ou virgules.
100
+ """
101
+
102
+ # Crée l'agent en lui passant l'instance de LiteLLMModel
103
+ video_agent = ToolCallingAgent(
104
+ model=gemini_flash_vlm_model, # Passe l'instance LiteLLMModel ici
105
+ tools=[analyze_media_content, download_video_youtube,delete_tmp_files], # Liste des outils que l'agent peut utiliser
106
+ description=system_prompt,
107
+ )
108
+ return video_agent
agent/agent_websearch.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import wikipedia
2
+ import requests
3
+ from bs4 import BeautifulSoup
4
+ import pandas as pd
5
+ from typing import List, Dict, Any, Optional
6
+ import os
7
+ from dotenv import load_dotenv
8
+ from smolagents import CodeAgent, LiteLLMModel, tool, DuckDuckGoSearchTool
9
+ import re
10
+ from datetime import datetime
11
+
12
+ # Configuration de Wikipedia en français
13
+ wikipedia.set_lang("fr")
14
+
15
+ # Initialisation de l'outil de recherche DuckDuckGo
16
+ search_tool = DuckDuckGoSearchTool(5)
17
+
18
+ load_dotenv() # Charge les variables d'environnement
19
+
20
+ @tool
21
+ def search_internet(query: str) -> List[Dict[str, str]]:
22
+ """
23
+ Recherche des informations sur internet via DuckDuckGo.
24
+
25
+ Args:
26
+ query: Terme de recherche
27
+
28
+ Returns:
29
+ Liste de dictionnaires contenant les résultats de recherche
30
+ """
31
+ try:
32
+ # Utilisation de DuckDuckGoSearchTool
33
+ results = search_tool(query)
34
+ return results
35
+ except Exception as e:
36
+ return [{'error': f'Erreur lors de la recherche: {str(e)}'}]
37
+
38
+
39
+ @tool
40
+ def get_wikipedia_page(title: str, lang: str = "fr") -> Dict[str, Any]:
41
+ """
42
+ Récupère une page Wikipedia complète avec son contenu et ses tableaux sous forme d'un dictionnaire.
43
+
44
+ Args:
45
+ title: Titre de la page Wikipedia
46
+ lang: Langue de Wikipedia (par défaut: français)
47
+
48
+ Returns:
49
+ Dictionnaire contenant les informations de la page et ses tableaux
50
+ """
51
+ try:
52
+ wikipedia.set_lang(lang)
53
+ page = wikipedia.page(title)
54
+
55
+ # Récupération du HTML de la page pour extraire les tableaux
56
+ response = requests.get(page.url)
57
+ soup = BeautifulSoup(response.content, 'html.parser')
58
+
59
+ # Extraction des tableaux avec leurs titres
60
+ tables = []
61
+ wiki_tables = soup.find_all('table', class_='wikitable')
62
+
63
+ for i, table in enumerate(wiki_tables):
64
+ table_data = []
65
+ headers = []
66
+ table_title = ""
67
+
68
+ # Recherche du titre du tableau
69
+ # Chercher dans les éléments précédents pour trouver un titre
70
+ current_element = table.find_previous_sibling()
71
+ title_found = False
72
+
73
+ while current_element and not title_found:
74
+ if current_element.name in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
75
+ table_title = current_element.get_text(strip=True)
76
+ title_found = True
77
+ elif current_element.name == 'p':
78
+ # Parfois le titre est dans un paragraphe juste avant
79
+ text = current_element.get_text(strip=True)
80
+ if len(text) < 100 and text.endswith(':'):
81
+ table_title = text.rstrip(':')
82
+ title_found = True
83
+ elif current_element.name == 'caption':
84
+ # Certains tableaux ont une balise caption
85
+ table_title = current_element.get_text(strip=True)
86
+ title_found = True
87
+ current_element = current_element.find_previous_sibling()
88
+
89
+ # Vérifier si le tableau a une caption directe
90
+ caption = table.find('caption')
91
+ if caption and not table_title:
92
+ table_title = caption.get_text(strip=True)
93
+
94
+ # Si aucun titre trouvé, chercher dans les éléments suivants
95
+ if not table_title:
96
+ # Regarder les éléments th avec colspan ou les premiers éléments
97
+ first_row = table.find('tr')
98
+ if first_row:
99
+ first_cell = first_row.find(['th', 'td'])
100
+ if first_cell and first_cell.get('colspan'):
101
+ potential_title = first_cell.get_text(strip=True)
102
+ if len(potential_title) < 100:
103
+ table_title = potential_title
104
+
105
+ # Valeur par défaut si aucun titre trouvé
106
+ if not table_title:
107
+ table_title = f"Tableau {i + 1}"
108
+
109
+ # Extraction des en-têtes
110
+ header_row = table.find('tr')
111
+ if header_row:
112
+ for th in header_row.find_all(['th', 'td']):
113
+ headers.append(th.get_text(strip=True))
114
+
115
+ # Extraction des données
116
+ rows = table.find_all('tr')
117
+ data_rows = []
118
+
119
+ # Déterminer quelle ligne contient les données (pas les en-têtes)
120
+ start_row = 1 if headers else 0
121
+
122
+ for row in rows[start_row:]:
123
+ cells = row.find_all(['td', 'th'])
124
+ if cells:
125
+ row_data = {}
126
+ for j, cell in enumerate(cells):
127
+ header = headers[j] if j < len(headers) else f'Colonne_{j + 1}'
128
+ cell_text = cell.get_text(strip=True)
129
+ row_data[header] = cell_text
130
+ if any(row_data.values()): # Ignorer les lignes vides
131
+ data_rows.append(row_data)
132
+
133
+ tables.append({
134
+ 'table_index': i,
135
+ 'table_title': table_title,
136
+ 'headers': headers,
137
+ 'data': data_rows,
138
+ 'row_count': len(data_rows),
139
+ 'column_count': len(headers)
140
+ })
141
+
142
+ return {
143
+ 'title': page.title,
144
+ 'url': page.url,
145
+ 'summary': page.summary,
146
+ 'content': page.content,
147
+ 'links': page.links[:20], # Limite aux 20 premiers liens
148
+ 'categories': page.categories[:10], # Limite aux 10 premières catégories
149
+ 'images': page.images[:5] if page.images else [], # Limite aux 5 premières images
150
+ 'tables': tables,
151
+ 'table_count': len(tables),
152
+ 'page_length': len(page.content),
153
+ 'has_tables': len(tables) > 0
154
+ }
155
+ except wikipedia.exceptions.DisambiguationError as e:
156
+ return {
157
+ 'error': 'Ambiguité détectée',
158
+ 'options': e.options[:10], # Limite aux 10 premières options
159
+ 'type': 'disambiguation'
160
+ }
161
+ except wikipedia.exceptions.PageError:
162
+ return {
163
+ 'error': f'Page "{title}" non trouvée',
164
+ 'type': 'page_not_found'
165
+ }
166
+ except Exception as e:
167
+ return {
168
+ 'error': f'Erreur: {str(e)}',
169
+ 'type': 'general_error'
170
+ }
171
+
172
+
173
+ # Configuration du modèle LiteLLM
174
+ def create_wikipedia_agent():
175
+ """
176
+ Crée un agent Wikipedia avec tous les outils nécessaires.
177
+ """
178
+ # Initialisation du modèle (ajustez selon votre configuration)
179
+ model = LiteLLMModel(model_id="gemini/gemini-2.5-flash", api_key=os.getenv("GOOGLE_API_KEY"))
180
+
181
+ system_prompt = """
182
+ Tu es un agent de recherche d'information, tu es capable de trouver des informations sur Wikipédia ou par recherche internet.
183
+ Tu peux exécuter des recherches, récupérer des pages Wikipedia, extraire des tableaux.
184
+ Pour trouver un article, tu peux rechercher sur internet avec le plus de détails puis trouver le premier lien Wikipedia pertinent.
185
+ Si tu ne trouves pas d'outils spécifiques à une tâche, tu peux utiliser la recherche internet pour trouver des informations pertinentes.
186
+ Tu dois toujours fournir des réponses claires et concises, en citant les sources lorsque c'est possible.
187
+ """
188
+ # Création de l'agent avec les outils
189
+ return CodeAgent(
190
+ tools=[
191
+ search_internet,
192
+ get_wikipedia_page,
193
+ # get_featured_articles_by_year,
194
+ # search_wikipedia_category
195
+ ],
196
+ model=model,
197
+ description=system_prompt,
198
+ additional_authorized_imports=["wikipedia", "requests", "bs4", "pandas"]
199
+ )
agent/get_question/hf_api.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ import requests
4
+
5
+
6
+ # (Keep Constants as is)
7
+ # --- Constants ---
8
+ DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
9
+
10
+
11
+ def get_question() :
12
+ questions_url = f"{DEFAULT_API_URL}/questions"
13
+
14
+ print(f"Fetching questions from: {questions_url}")
15
+ try:
16
+ response = requests.get(questions_url, timeout=15)
17
+ response.raise_for_status()
18
+ questions_data = response.json()
19
+ if not questions_data:
20
+ print("Fetched questions list is empty.")
21
+ return "Fetched questions list is empty or invalid format.", None
22
+ print(f"Fetched {len(questions_data)} questions.")
23
+ return questions_data
24
+ except requests.exceptions.RequestException as e:
25
+ print(f"Error fetching questions: {e}")
26
+ return f"Error fetching questions: {e}", None
27
+ except requests.exceptions.JSONDecodeError as e:
28
+ print(f"Error decoding JSON response from questions endpoint: {e}")
29
+ print(f"Response text: {response.text[:500]}")
30
+ return f"Error decoding server response for questions: {e}", None
31
+ except Exception as e:
32
+ print(f"An unexpected error occurred fetching questions: {e}")
33
+ return f"An unexpected error occurred fetching questions: {e}", None
34
+
35
+
36
+ def get_random_question():
37
+ random_question_url = f"{DEFAULT_API_URL}/random-question"
38
+ print(f"Fetching a random question from: {random_question_url}")
39
+ try:
40
+ response = requests.get(random_question_url, timeout=15)
41
+ response.raise_for_status()
42
+ questions_data = response.json()
43
+ if not questions_data:
44
+ print("Fetched questions list is empty.")
45
+ return "Fetched questions list is empty or invalid format.", None
46
+ return questions_data
47
+ except requests.exceptions.RequestException as e:
48
+ print(f"Error fetching a random question: {e}")
49
+ return f"Error fetching a random question: {e}", None
50
+ except requests.exceptions.JSONDecodeError as e:
51
+ print(f"Error decoding JSON response from a random question endpoint: {e}")
52
+ print(f"Response text: {response.text[:500]}")
53
+ return f"Error decoding server response for a random question: {e}", None
54
+ except Exception as e:
55
+ print(f"An unexpected error occurred fetching a random question: {e}")
56
+ return f"An unexpected error occurred fetching a random question: {e}", None
57
+
58
+
59
+ def get_file_by_task_id(task_id:str):
60
+ file_by_task_id_url = f"{DEFAULT_API_URL}/files/{task_id}"
61
+ print(f"Fetching a file from: {file_by_task_id_url}")
62
+ try:
63
+ response = requests.get(file_by_task_id_url, timeout=15)
64
+
65
+ response.raise_for_status()
66
+ content_disp = response.headers.get("Content-Disposition")
67
+ file_name = re.findall('filename=(.+)', content_disp)
68
+ questions_data = response.content
69
+ if not questions_data or not file_name:
70
+ print("No file to fetch.")
71
+ return "No file to fetch.", None
72
+ return questions_data, file_name[0].replace('"', '').strip()
73
+ except requests.exceptions.RequestException as e:
74
+ print(f"Error fetching a file: {e}")
75
+ return f"Error fetching a file: {e}", None
76
+ except requests.exceptions.JSONDecodeError as e:
77
+ print(f"Error decoding JSON response from the file by task id endpoint: {e}")
78
+ print(f"Response text: {response.text[:500]}")
79
+ return f"Error decoding server response for the file by task id: {e}", None
80
+ except Exception as e:
81
+ print(f"An unexpected error occurred fetching a file: {e}")
82
+ return f"An unexpected error occurred fetching a file: {e}", None
agent/prompts.yaml ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ description: You are a GAIA benchmark AI assistant, very precise, no nonsense. Your sole purpose is to output the minimal, final answer in the format: [ANSWER]. You
2
+ must NEVER output explanations, intermediate steps, reasoning, or comments — only
3
+ the answer, strictly enclosed in `[ANSWER]`.
4
+ rules:
5
+ - id: format
6
+ description: Control output format and token usage.
7
+ constraints:
8
+ - limit_token_used: 65536
9
+ - output_only_final_answer: true
10
+ - wrap_answer_in_brackets: true
11
+ - no_whitespace_outside_brackets: true
12
+ - no_follow_ups_justifications_clarifications: true
13
+ - id: numerical_answers
14
+ description: Guidelines for numerical answers.
15
+ constraints:
16
+ - use_digits_only: true
17
+ - no_commas_symbols_units_unless_required: true
18
+ - no_approximate_words: true
19
+ - id: string_answers
20
+ description: Guidelines for string answers.
21
+ constraints:
22
+ - omit_articles: true
23
+ - use_full_words: true
24
+ - use_text_for_numbers_if_specified: true
25
+ - sort_sets_lists_alphabetically_if_not_specified: true
26
+ - id: lists
27
+ description: Guidelines for list answers.
28
+ constraints:
29
+ - output_comma_separated: true
30
+ - no_conjunctions: true
31
+ - sort_alphabetically_or_numerically: true
32
+ - no_braces_or_brackets_unless_asked: true
33
+ - id: sources
34
+ description: Guidelines for using sources like Wikipedia or web tools.
35
+ constraints:
36
+ - extract_only_precise_fact: true
37
+ - ignore_unrelated_content: true
38
+ - id: file_analysis
39
+ description: Guidelines for using the run_query_with_file tool.
40
+ constraints:
41
+ - use_run_query_with_file_tool: true
42
+ - append_taskid_to_url: true
43
+ - include_exact_answer_only: true
44
+ - no_summarizing_excessive_quoting_or_interpreting: true
45
+ - id: video
46
+ description: Guidelines for using the relevant video tool.
47
+ constraints:
48
+ - use_relevant_video_tool: true
49
+ - include_exact_answer_only: true
50
+ - no_summarizing_excessive_quoting_or_interpreting: true
51
+ - id: minimalism
52
+ description: General minimalism guidelines.
53
+ constraints:
54
+ - no_assumptions_unless_logically_demanded: true
55
+ - choose_narrowest_most_literal_interpretation: true
56
+ - output_unknown_if_answer_not_found: true
57
+ examples:
58
+ - question: What is 2 + 2?
59
+ answer: "[ANSWER]4"
60
+ - question: How many studio albums were published by Mercedes Sosa between 2000 and
61
+ 2009 (inclusive)? Use 2022 English Wikipedia.
62
+ answer: "[ANSWER]3"
63
+ - question: Given the following group table on set S = {a, b, c, d, e}, identify
64
+ any subset involved in counterexamples to commutativity.
65
+ answer: "[ANSWER]b, e"
66
+ - question: How many at bats did the Yankee with the most walks in the 1977 regular
67
+ season have that same season?
68
+ answer: "[ANSWER]519"
agent/ressources_getter.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import pandas as pd
6
+ from smolagents import tool
7
+
8
+ DOWNLOAD_FOLDER = "./download"
9
+
10
+
11
+ def create_download_folder_if_not_exist():
12
+ os.makedirs(DOWNLOAD_FOLDER,exist_ok=True)
13
+
14
+ def save_file(binary_file,file_name:str):
15
+ create_download_folder_if_not_exist()
16
+ if binary_file is not None and file_name is not None :
17
+ file_path = os.path.join(DOWNLOAD_FOLDER,file_name)
18
+ open(file_path, 'wb').write(binary_file)
19
+ return file_path
20
+ else :
21
+ print("Pas de fichier")
22
+ return None
23
+
24
+ def get_prompt_with_file_content(prompt:str,file_path:str):
25
+ full_prompt = prompt
26
+ if file_path:
27
+ file_extension = os.path.splitext(file_path)[1].lower()
28
+ print(f"--- Fichier fourni : {file_path} (Type: {file_extension}) ---")
29
+ full_prompt = (
30
+ f"{prompt}\n\n"
31
+ f"Voici le chemin du fichier joint : {file_path}\n"
32
+ )
33
+ return full_prompt
34
+
35
+ @tool
36
+ def extract_text_from_file(file_path: str) -> str:
37
+ """
38
+ Extrait le texte d'un fichier.
39
+ Ne fonctionne qu'avec les fichier texte (txt, csv, json,etc.).
40
+ Args:
41
+ file_path (str): Chemin du fichier à traiter.
42
+ Returns:
43
+ str: Contenu textuel extrait du fichier.
44
+ """
45
+ with open(file_path, 'r', encoding='utf-8') as f:
46
+ return f.read()
47
+
48
+ @tool
49
+ def convert_excel_to_text(file_path: str) -> str:
50
+ """
51
+ Convertit un fichier Excel en texte brut.Ne fonctionne qu'avec les fichiers Excel (.xlsx, .xls).
52
+ Args:
53
+ file_path (str): Chemin du fichier Excel à convertir.
54
+ Returns:
55
+ str: Contenu textuel extrait du fichier Excel.
56
+ """
57
+ try:
58
+ df = pd.read_excel(file_path)
59
+ return df.to_string(index=False)
60
+ except Exception as e:
61
+ return f"Erreur lors de la conversion du fichier Excel : {e}"
62
+
63
+ @tool
64
+ def delete_tmp_files() -> None:
65
+ """
66
+ Supprime les fichiers temporaires.
67
+ """
68
+ try:
69
+ folder = Path(DOWNLOAD_FOLDER)
70
+ for file in folder.iterdir():
71
+ if file.is_file():
72
+ file.unlink()
73
+ print(f"Fichier temporaire supprimé : {file.name}")
74
+ except Exception as e:
75
+ print(f"Erreur lors de la suppression du fichier : {e}")
app.py CHANGED
@@ -4,20 +4,12 @@ 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
  """
@@ -80,7 +72,7 @@ def run_and_submit_all( profile: gr.OAuthProfile | None):
80
  print(f"Skipping item with missing task_id or question: {item}")
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:
 
4
  import inspect
5
  import pandas as pd
6
 
7
+ from Final_Assignment_Template.agent.agent import BasicAgent
8
+
9
  # (Keep Constants as is)
10
  # --- Constants ---
11
  DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
12
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  def run_and_submit_all( profile: gr.OAuthProfile | None):
15
  """
 
72
  print(f"Skipping item with missing task_id or question: {item}")
73
  continue
74
  try:
75
+ submitted_answer = agent(question_text,task_id)
76
  answers_payload.append({"task_id": task_id, "submitted_answer": submitted_answer})
77
  results_log.append({"Task ID": task_id, "Question": question_text, "Submitted Answer": submitted_answer})
78
  except Exception as e:
requirements.txt CHANGED
@@ -1,2 +1,17 @@
1
  gradio
2
- requests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  gradio
2
+ requests
3
+ tools
4
+ hf_xet
5
+ wikipedia
6
+ langchain_community
7
+ langchain[google-genai]
8
+ smolagents[toolkit]
9
+ smolagents
10
+ litellm
11
+ python-dotenv
12
+ wikipedia-api
13
+ beautifulsoup4
14
+ duckduckgo-search
15
+ yt_dlp
16
+ google-generativeai
17
+ openpyxl