| import os |
|
|
| from smolagents import ( |
| CodeAgent, |
| PythonInterpreterTool, |
| GoogleSearchTool, |
| VisitWebpageTool, |
| ) |
| from models import select_model |
|
|
| from typing import Optional |
|
|
|
|
| from tools_smolagent_audio import transcribe_audio |
| from tools_smolagent_browser import ( |
| wiki_search, |
| web_search, |
| arxiv_search, |
| website_scrape, |
| ) |
| from tools_smolagent_doc import ( |
| save_and_read_file, |
| download_file_from_url, |
| extract_text_from_image, |
| analyze_csv_file, |
| analyze_excel_file, |
| read_file, |
| ) |
| from tools_smolagent_img import ( |
| analyze_image, |
| transform_image, |
| draw_on_image, |
| generate_simple_image, |
| combine_images, |
| ) |
| from tools_smolagent_maths import ( |
| multiply, |
| add, |
| subtract, |
| divide, |
| modulus, |
| power, |
| |
| ) |
| from tools_smolagent_video import ( |
| review_youtube_video, |
| use_vision_model, |
| video_frames_to_images, |
| transcribe_youtube, |
| ) |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
|
|
| class BasicAgent: |
| def __init__( |
| self, |
| verbose=False, |
| name_model_provider="openrouter", |
| name_model="llama4", |
| ): |
| self.verbose = verbose |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| tools = [ |
| GoogleSearchTool(provider="serper"), |
| PythonInterpreterTool(), |
| save_and_read_file, |
| analyze_excel_file, |
| analyze_csv_file, |
| download_file_from_url, |
| extract_text_from_image, |
| read_file, |
| transcribe_audio, |
| wiki_search, |
| |
| arxiv_search, |
| website_scrape, |
| analyze_image, |
| transform_image, |
| draw_on_image, |
| generate_simple_image, |
| combine_images, |
| multiply, |
| add, |
| subtract, |
| divide, |
| modulus, |
| power, |
| |
| review_youtube_video, |
| use_vision_model, |
| video_frames_to_images, |
| transcribe_youtube, |
| ] |
| self.agent = CodeAgent( |
| tools=tools, |
| model=select_model(name_model_provider, name_model), |
| additional_authorized_imports=[ |
| "pandas", |
| "numpy", |
| "datetime", |
| "json", |
| "re", |
| "math", |
| "os", |
| "requests", |
| "csv", |
| "urllib", |
| "io", |
| "cv2", |
| ], |
| executor_type="local", |
| executor_kwargs={}, |
| verbosity_level=0, |
| ) |
| print("BasicAgent initialized.") |
|
|
| def __call__(self, question: str) -> str: |
| """ |
| Process a GAIA benchmark question and return the answer |
| |
| Args: |
| question: The question to answer |
| task_file_path: Optional path to a file associated with the question |
| |
| Returns: |
| The answer to the question |
| """ |
| try: |
| if self.verbose: |
| print(f"Processing question: {question}") |
|
|
| |
| context = question |
| if question.startswith(".") or ".rewsna eht sa" in question: |
| context = f""" This question appears to be in reversed text. Here's the reversed version: {question[::-1]} Now answer the question above. Remember to format your answer exactly as requested. """ |
|
|
| |
|
|
| full_prompt = f"""{context}. When answering, provide ONLY the precise answer requested. Do not include explanations, steps, reasoning, or additional text. Be direct and specific. GAIA benchmark requires exact matching answers. For example, if asked "What is the capital of France?", respond simply with "Paris".""" |
|
|
| |
|
|
| |
|
|
| |
| answer = self.agent.run(full_prompt) |
|
|
| |
| |
| answer = self._clean_answer(answer) |
|
|
| if self.verbose: |
| print(f"Generated answer: {answer}") |
|
|
| return answer |
|
|
| except Exception as e: |
| error_msg = f"Error answering question: {e}" |
| if self.verbose: |
| print(error_msg) |
| return error_msg |
|
|
| def answer_question( |
| self, question: str, task_file_path: Optional[str] = None |
| ) -> str: |
| """ |
| Process a GAIA benchmark question and return the answer |
| |
| Args: |
| question: The question to answer |
| task_file_path: Optional path to a file associated with the question |
| |
| Returns: |
| The answer to the question |
| """ |
| try: |
| if self.verbose: |
| print(f"Processing question: {question}") |
| if task_file_path: |
| print(f"With associated file: {task_file_path}") |
|
|
| |
| context = question |
| file_content = None |
|
|
| |
| if task_file_path: |
| try: |
| with open(task_file_path, "r") as f: |
| file_content = f.read() |
|
|
| |
| file_ext = os.path.splitext(task_file_path)[1].lower() |
|
|
| context = f""" Question: {question} This question has an associated file. Here is the file content: ```{file_ext} {file_content}```Analyze the file content above to answer the question.""" |
|
|
| except Exception as file_e: |
| context = f""" Question: {question} This question has an associated file at path: {task_file_path}. However, there was an error reading the file: {file_e}. You can still try to answer the question based on the information provided.""" |
|
|
| |
| |
| if question.startswith(".") or ".rewsna eht sa" in question: |
| context = f""" This question appears to be in reversed text. Here's the reversed version: {question[::-1]} Now answer the question above. Remember to format your answer exactly as requested. """ |
|
|
| |
|
|
| full_prompt = f"""{context}. When answering, provide ONLY the precise answer requested. Do not include explanations, steps, reasoning, or additional text. Be direct and specific. GAIA benchmark requires exact matching answers. For example, if asked "What is the capital of France?", respond simply with "Paris".""" |
|
|
| |
|
|
| |
|
|
| |
| answer = self.agent.run(full_prompt) |
|
|
| |
| |
| answer = self._clean_answer(answer) |
|
|
| if self.verbose: |
| print(f"Generated answer: {answer}") |
|
|
| return answer |
|
|
| except Exception as e: |
| error_msg = f"Error answering question: {e}" |
| if self.verbose: |
| print(error_msg) |
| return error_msg |
|
|
| def _clean_answer(self, answer: any) -> str: |
| """ |
| Clean up the answer to remove common prefixes and formatting |
| that models often add but that can cause exact match failures. |
| |
| Args: |
| answer: The raw answer from the model |
| |
| Returns: |
| The cleaned answer as a string |
| """ |
| |
| if not isinstance(answer, str): |
| |
| if isinstance(answer, float): |
| |
| |
| if answer.is_integer(): |
| formatted_answer = str(int(answer)) |
| else: |
| |
| if abs(answer) >= 1000: |
| formatted_answer = f"${answer:,.2f}" |
| else: |
| formatted_answer = str(answer) |
| return formatted_answer |
| elif isinstance(answer, int): |
| return str(answer) |
| else: |
| |
| return str(answer) |
|
|
| |
| |
| answer = answer.strip() |
|
|
| |
| prefixes_to_remove = [ |
| "The answer is ", |
| "Answer: ", |
| "Final answer: ", |
| "The result is ", |
| "To answer this question: ", |
| "Based on the information provided, ", |
| "According to the information: ", |
| ] |
|
|
| for prefix in prefixes_to_remove: |
| if answer.startswith(prefix): |
| answer = answer[len(prefix) :].strip() |
|
|
| |
| if (answer.startswith('"') and answer.endswith('"')) or ( |
| answer.startswith("'") and answer.endswith("'") |
| ): |
| answer = answer[1:-1].strip() |
|
|
| stuff_to_remove = "Stdout:" |
| if answer.startswith(stuff_to_remove): |
| answer = answer[len(stuff_to_remove) :].strip() |
|
|
| stuff_to_remove_bis = "Output: " |
| if answer.startswith(stuff_to_remove_bis): |
| answer = answer[len(stuff_to_remove_bis) :].strip() |
| return answer |
|
|