spatial-atlas / src /fieldwork /parser.py
Arun0808's picture
Spatial Atlas v1.0: spatial-aware research agent for AgentBeats Challenge
77a8cec
Raw
History Blame Contribute Delete
2.56 kB
"""
Spatial Atlas — FieldWorkArena Goal Parser
Parses the structured goal string sent by the FWA green agent into
a structured task object: query, input files, and output format.
Goal format:
# Question
{query}
# Input Data
{file1.jpg}
{file2.pdf}
# Output Format
{expected format description}
"""
import logging
import re
from dataclasses import dataclass, field
logger = logging.getLogger("spatial-atlas.fieldwork.parser")
@dataclass
class FieldWorkTask:
query: str
input_files: list[str] = field(default_factory=list)
output_format: str = ""
raw_goal: str = ""
class GoalParser:
"""Parse FieldWorkArena goal strings into structured tasks."""
# Section header patterns
SECTION_PATTERN = re.compile(
r"^#\s+(Question|Input Data|Output Format)\s*$",
re.MULTILINE | re.IGNORECASE,
)
def parse(self, goal_text: str) -> FieldWorkTask:
"""Parse a goal string into a FieldWorkTask."""
task = FieldWorkTask(raw_goal=goal_text, query="")
# Split into sections
sections = self._split_sections(goal_text)
task.query = sections.get("question", "").strip()
task.output_format = sections.get("output format", "").strip()
# Parse input data section into file names
input_data = sections.get("input data", "").strip()
if input_data:
task.input_files = [
line.strip()
for line in input_data.splitlines()
if line.strip()
]
# Fallback: if no structured sections found, treat entire text as query
if not task.query:
task.query = goal_text.strip()
logger.warning("No structured goal format detected, using full text as query")
logger.info(
f"Parsed task: query={task.query[:100]}..., "
f"files={len(task.input_files)}, "
f"format={task.output_format[:50]}..."
)
return task
def _split_sections(self, text: str) -> dict[str, str]:
"""Split goal text by # headers into named sections."""
sections: dict[str, str] = {}
matches = list(self.SECTION_PATTERN.finditer(text))
if not matches:
return sections
for i, match in enumerate(matches):
section_name = match.group(1).lower()
start = match.end()
end = matches[i + 1].start() if i + 1 < len(matches) else len(text)
sections[section_name] = text[start:end]
return sections