Spaces:
Sleeping
Sleeping
| """ | |
| echo/tools/research.py | |
| ---------------------- | |
| The world-grounding tool. Given a location + era, it returns real-world detail | |
| (cost of living, cultural moment, climate, notable events) that the Curator | |
| weaves in so an alternate life feels *anchored* rather than hallucinated. This | |
| grounding is the moment a judge thinks "wait, how does it know that?" | |
| ResearchTool is an interface with: | |
| * MockResearch — deterministic offline facts (for GPU-free testing), | |
| * WebResearch — wraps a real search call (web_search / an API) at deploy time. | |
| """ | |
| from __future__ import annotations | |
| from abc import ABC, abstractmethod | |
| class ResearchTool(ABC): | |
| def ground(self, location: str, year: int, theme: str = "") -> str: | |
| """Return a short bundle of grounding facts for location/year.""" | |
| ... | |
| class MockResearch(ResearchTool): | |
| """Offline, deterministic grounding. Enough to exercise the pipeline.""" | |
| _NOTES = { | |
| "Lisbon": "steep tiled streets, cheap pastéis, a tech-expat wave, the 28 tram", | |
| "Tokyo": "rail punctual to the second, conbini at every corner, quiet density", | |
| "Berlin": "late winters, techno after-hours, a city still half rebuilt", | |
| "São Paulo": "endless gray sprawl, helicopters over traffic, rain that floods", | |
| "Reykjavik": "winter dark by 3pm, geothermal pools, wool against the wind", | |
| "Montreal": "bilingual signs, brutal Februaries, bagels better than New York", | |
| "Nairobi": "matatus in bright livery, high-altitude light, a startup hum", | |
| "Hanoi": "scooters like a river, broth at dawn, humidity that never lifts", | |
| } | |
| def ground(self, location: str, year: int, theme: str = "") -> str: | |
| note = self._NOTES.get(location, "a place with its own particular light") | |
| return f"{location} (~{year}): {note}." | |
| class WebResearch(ResearchTool): | |
| """ | |
| Deploy-time grounding via a real search function injected at construction. | |
| `search_fn(query) -> str` keeps this decoupled from any specific API and | |
| lets the Gradio app pass in web_search or a cached corpus. | |
| """ | |
| def __init__(self, search_fn): | |
| self.search_fn = search_fn | |
| def ground(self, location: str, year: int, theme: str = "") -> str: | |
| q = f"{location} {year} daily life cost of living culture {theme}".strip() | |
| try: | |
| return str(self.search_fn(q))[:600] | |
| except Exception: | |
| return f"{location} (~{year})." | |