from smolagents import LiteLLMModel from src.settings import Settings from src.utils import InputTokenRateLimiter from smolagents.tools import Tool from litellm import completion import os import re settings = Settings() print(settings.llm_model_id) class FinalAnswerTool(Tool): name = "final_answer" description = """ The final answer MUST be produced ONLY by calling final_answer. The agent MUST NOT output explanations after final_answer. Your answer should be precise. No sentences. - **CRITICAL TEXT RULE: Do not use short forms (e.g., abbreviations like 'St.' for 'Saint' or 'Dr.' for 'Doctor') in the string passed to final_answer.** Preserve full, unabbreviated words for all names and locations. For numerical questions: - If the question requires a single numeric answer → compute it and pass the numeric string to final_answer. No long sentences or thought process. Just the precise answer. - If rounding is required → apply the rounding before sending to final_answer. - If the question asks for "Round your result to the nearest 1000 hours?" STEP 1: Compute total time in hours. STEP 2: Divide the final answer from step 1 by 1000 to convert to "thousands of hours". STEP 3: Round to the nearest integer. STEP 4: Send ONLY that integer to final_answer. - If the final answer is Research. Do not capitalize. Just keep the answer as "research" - If the answer includes multiple options (e.g., b,e) → pass the exact options separated by a comma and a single space (e.g., b, e) to final_answer. ------------------------------------------------------ WHEN MAX STEPS ARE REACHED ------------------------------------------------------ Even if reasoning is incomplete, the LAST step MUST ALWAYS be a final_answer tool call using the best available computed value. Never end the run without calling final_answer. FAILSAFE -------- If input is empty or unusable → output: I am unable to answer ------------------------------------------------------ PROHIBITIONS ------------------------------------------------------ - No markdown. - No bold text. - No asterisks like **** - No units unless required. - No surrounding text. - No explanations after final_answer. - No answering directly without using final_answer. FORMAT CLEANING --------------- - Remove markdown symbols. - Remove LaTeX ($, \, { }). - If boxed{...} appears, extract content inside. - Keep digits, letters, decimal points, hyphens, slashes, commas, spaces. - Collapse extra whitespace. """ inputs = { "answer": {"type": "string", "description": "The final, correctly formatted answer string."} } output_type = "string" def forward(self, answer: str) -> str: if not answer or str(answer).strip() == "": return "I am unable to answer" # Fallback string for submission answer = str(answer).strip() match = re.search(r'boxed\{([^}]+)\}', answer) if match: answer = match.group(1) # Remove LaTeX symbols and keep letters, digits, commas answer = re.sub(r'[\$\{\}\\]', '', answer) answer = re.sub(r'[^A-Za-z0-9,\.\-\+/ ]', ' ', answer) answer = " ".join(answer.split()) # Remove extra spaces if not answer: return "I am unable to answer" return answer