gaia-final-assignment / tools /text_transform.py
ykumar2020's picture
Publish verified modular GAIA agent source
c641d5f verified
Raw
History Blame Contribute Delete
3.72 kB
"""Deterministic text transformations."""
from __future__ import annotations
import ast
import codecs
import operator
import re
_ARITHMETIC = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
ast.FloorDiv: operator.floordiv,
ast.Mod: operator.mod,
ast.Pow: operator.pow,
ast.USub: operator.neg,
ast.UAdd: operator.pos,
}
_OPPOSITES = {
"left": "right",
"right": "left",
"up": "down",
"down": "up",
"true": "false",
"false": "true",
"yes": "no",
"no": "yes",
"open": "closed",
"closed": "open",
}
def _safe_arithmetic(expression: str) -> int | float:
def evaluate(node):
if isinstance(node, ast.Expression):
return evaluate(node.body)
if isinstance(node, ast.Constant) and isinstance(node.value, (int, float)):
return node.value
if isinstance(node, ast.BinOp) and type(node.op) in _ARITHMETIC:
return _ARITHMETIC[type(node.op)](evaluate(node.left), evaluate(node.right))
if isinstance(node, ast.UnaryOp) and type(node.op) in _ARITHMETIC:
return _ARITHMETIC[type(node.op)](evaluate(node.operand))
raise ValueError("Unsafe arithmetic expression")
return evaluate(ast.parse(expression, mode="eval"))
def solve_text_transformation(question: str) -> str | None:
"""Solve transformations only when the operation is unambiguous."""
reversed_question = question[::-1]
lower_reversed = reversed_question.lower()
if "write" in lower_reversed and "answer" in lower_reversed:
# The reversed prompt itself contains the literal instruction and answer.
quoted = re.findall(r'["β€œ]([^"”]+)["”]', reversed_question)
if quoted:
literal = quoted[-1]
if "opposite" in lower_reversed:
return _OPPOSITES.get(literal.casefold())
return literal
match = re.search(r"write\s+(?:the\s+)?(?:word\s+)?([a-z-]+)", lower_reversed)
if match:
return match.group(1)
quoted = re.findall(r'["β€œ]([^"”]+)["”]', question)
source = quoted[0] if quoted else None
lowered = question.lower()
if source and re.search(r"\breverse\b", lowered):
return source[::-1]
if source and re.search(r"\buppercase\b", lowered):
return source.upper()
if source and re.search(r"\blowercase\b", lowered):
return source.lower()
if source and re.search(r"\balphabeti[sz]e\b", lowered):
return " ".join(sorted(source.split(), key=str.casefold))
if source and re.search(r"\brot\s*-?13\b", lowered):
return codecs.decode(source, "rot_13")
if source and re.search(r"\bsort\b.*\bnumeric", lowered):
numbers = re.findall(r"[-+]?\d+(?:\.\d+)?", source)
return ", ".join(sorted(numbers, key=float))
arithmetic = re.search(
r"(?:calculate|compute|evaluate|what is)\s+([\d\s+*/().%-]+)\??\s*$",
question,
re.IGNORECASE,
)
if arithmetic:
result = _safe_arithmetic(arithmetic.group(1).strip())
return (
str(int(result))
if isinstance(result, float) and result.is_integer()
else str(result)
)
extraction = re.search(
r"extract\s+the\s+(\d+)(?:st|nd|rd|th)\s+word\s+from\s+[\"β€œ]([^\"”]+)",
question,
re.IGNORECASE,
)
if extraction:
index = int(extraction.group(1)) - 1
words = extraction.group(2).split()
return words[index] if 0 <= index < len(words) else None
return None