spatial-atlas / src /fieldwork /handler.py
Arun0808's picture
Spatial Atlas v1.0: spatial-aware research agent for AgentBeats Challenge
77a8cec
Raw
History Blame Contribute Delete
3.61 kB
"""
Spatial Atlas — FieldWorkArena Domain Handler
Main entry point for FieldWorkArena tasks. Orchestrates:
1. Goal parsing (structured task extraction)
2. Multimodal file processing (images, PDFs, videos, text)
3. Spatial scene graph construction
4. Entropy-guided reasoning
5. Output format matching
Receives: text goal + file attachments from green agent
Returns: formatted answer text
"""
import logging
from a2a.server.tasks import TaskUpdater
from a2a.types import TaskState
from a2a.utils import new_agent_text_message
from config import Config
from llm import LLMClient
from fieldwork.parser import GoalParser
from fieldwork.vision import VisionPipeline
from fieldwork.spatial import SpatialAnalyzer
from fieldwork.reasoner import FieldWorkReasoner
from fieldwork.formatter import AnswerFormatter
logger = logging.getLogger("spatial-atlas.fieldwork")
class FieldWorkHandler:
"""Handle FieldWorkArena benchmark tasks."""
def __init__(self, config: Config, llm: LLMClient):
self.config = config
self.llm = llm
self.parser = GoalParser()
self.vision = VisionPipeline(llm, max_video_frames=config.max_video_frames)
self.spatial = SpatialAnalyzer(llm)
self.reasoner = FieldWorkReasoner(config, llm)
self.formatter = AnswerFormatter()
async def handle(
self,
text: str,
file_parts: list[tuple[str, str, str | bytes]],
updater: TaskUpdater,
) -> str:
"""
Process a FieldWorkArena task end-to-end.
Args:
text: Goal text from green agent (# Question / # Input Data / # Output Format)
file_parts: List of (name, mime_type, data) for attached files
updater: A2A task updater for progress reporting
Returns:
Formatted answer string
"""
# 1. Parse the goal string
task = self.parser.parse(text)
logger.info(f"Task parsed: query='{task.query[:80]}...', files={len(file_parts)}")
# 2. Process all file attachments into text context
await updater.update_status(
TaskState.working,
new_agent_text_message(
f"Processing {len(file_parts)} input file(s)..."
),
)
file_contexts = []
for name, mime, data in file_parts:
context = await self.vision.process_file(name, mime, data)
file_contexts.append(context)
logger.info(f"Processed {len(file_contexts)} files into context")
# 3. Build spatial scene graph
await updater.update_status(
TaskState.working,
new_agent_text_message("Building spatial scene graph..."),
)
scene = await self.spatial.build_scene(task.query, file_contexts)
logger.info(
f"Scene: {scene.entity_count} entities, "
f"{len(scene.relations)} relations, "
f"{scene.violation_count} violations"
)
# 4. Reason over evidence to produce answer
await updater.update_status(
TaskState.working,
new_agent_text_message("Reasoning over evidence..."),
)
answer = await self.reasoner.reason(
query=task.query,
file_contexts=file_contexts,
scene=scene,
output_format=task.output_format,
)
# 5. Format answer to match expected output
formatted = self.formatter.format_answer(answer, task.output_format)
logger.info(f"Answer formatted ({len(formatted)} chars): {formatted[:200]}...")
return formatted