|
|
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. |
|
|
|
|
|
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. |
|
|
|
|
|
------------------------------------------------------ |
|
|
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 **<Int>** |
|
|
- 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" |
|
|
|
|
|
answer = str(answer).strip() |
|
|
match = re.search(r'boxed\{([^}]+)\}', answer) |
|
|
if match: |
|
|
answer = match.group(1) |
|
|
|
|
|
answer = re.sub(r'[\$\{\}\\]', '', answer) |
|
|
answer = re.sub(r'[^A-Za-z0-9,\.\-\+/ ]', ' ', answer) |
|
|
answer = " ".join(answer.split()) |
|
|
|
|
|
if not answer: |
|
|
return "I am unable to answer" |
|
|
|
|
|
return answer |