Spaces:
Running
Running
| """Build reproducible bootstrap datasets for Places intent and retrieval. | |
| The generated examples are synthetic and contain no user data. They exercise | |
| open-vocabulary categories, spelling variation, contextual slots, and hard | |
| retrieval negatives. They are intended as a reviewed seed corpus; production | |
| behavioral data should be anonymized, adjudicated, and added before release. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| import json | |
| from dataclasses import dataclass, replace | |
| from pathlib import Path | |
| from typing import Iterable, Sequence | |
| try: | |
| from scripts.train_place_intent_bert import validate_training_example | |
| from scripts.train_place_retriever import _read_training_rows | |
| except ModuleNotFoundError: # Supports ``python scripts/build_...py``. | |
| from train_place_intent_bert import validate_training_example | |
| from train_place_retriever import _read_training_rows | |
| ROOT = Path(__file__).resolve().parents[1] | |
| DATA_ROOT = ROOT / "data" / "training" | |
| INTENT_ROOT = DATA_ROOT / "places_intent_v1" | |
| RETRIEVAL_ROOT = DATA_ROOT / "places_retrieval_v1" | |
| SPLITS = ("train", "validation", "test") | |
| INTENT_CONCEPTS: dict[str, tuple[str, ...]] = { | |
| "train": ( | |
| "donas artesanales", | |
| "bubble tea", | |
| "salón de té", | |
| "tacos de birria", | |
| "ramen", | |
| "comida coreana", | |
| "restaurante vegano", | |
| "cafetería de libros", | |
| "espacio de coworking", | |
| "jardín botánico", | |
| "mirador panorámico", | |
| "sendero natural", | |
| "museo de ciencia", | |
| "galería de arte", | |
| "biblioteca pública", | |
| "cine independiente", | |
| "café de juegos de mesa", | |
| "arcade", | |
| "karaoke", | |
| "muro de escalada", | |
| "estudio de yoga", | |
| "mercado artesanal", | |
| "heladería italiana", | |
| "hostal juvenil", | |
| "bar con música en vivo", | |
| "cafetería de especialidad", | |
| "marisquería", | |
| "desayunos chiapanecos", | |
| ), | |
| "validation": ( | |
| "panadería sin gluten", | |
| "restaurante etíope", | |
| "casa de jazz", | |
| "pista de patinaje", | |
| "chocolatería", | |
| "vivero de orquídeas", | |
| ), | |
| "test": ( | |
| "tienda de cómics", | |
| "temazcal", | |
| "taller de cerámica", | |
| "restaurante de comida libanesa", | |
| "parque para perros", | |
| "observatorio astronómico", | |
| ), | |
| } | |
| # Controlled orthographic and colloquial variants stay inside the CATEGORY | |
| # span. They teach boundary detection without mapping the phrase to a closed | |
| # class, and every concept remains confined to one split. | |
| INTENT_CATEGORY_VARIANTS = { | |
| "donas artesanales": "donas artensanales", | |
| "bubble tea": "buble tea", | |
| "salón de té": "salon de te", | |
| "tacos de birria": "tacos d birria", | |
| "cafetería de libros": "cafetría con libros", | |
| "espacio de coworking": "espacio de co-working", | |
| "jardín botánico": "jardin botanico", | |
| "museo de ciencia": "museo d ciencia", | |
| "café de juegos de mesa": "cafe con juegos de mesa", | |
| "heladería italiana": "eladería italiana", | |
| "cafetería de especialidad": "cafeteria de especialidad", | |
| "panadería sin gluten": "panaderia sin glúten", | |
| "restaurante etíope": "resturante etíope", | |
| "pista de patinaje": "pista para patinar", | |
| "tienda de cómics": "tienda de comics", | |
| "taller de cerámica": "tayer de ceramica", | |
| "restaurante de comida libanesa": "resturante libanés", | |
| "observatorio astronómico": "observatorio astronomico", | |
| } | |
| PREFERENCES = ( | |
| "tranquilo", | |
| "con terraza", | |
| "pet friendly", | |
| "económico", | |
| "romántico", | |
| "con wifi", | |
| "familiar", | |
| "al aire libre", | |
| "accesible", | |
| "abierto de noche", | |
| ) | |
| EXCLUSIONS = ( | |
| "ruido", | |
| "filas largas", | |
| "música fuerte", | |
| "humo", | |
| "alcohol", | |
| "aglomeraciones", | |
| ) | |
| LOCATIONS = ( | |
| "Centro Histórico", | |
| "Parque Central", | |
| "Plaza del Sol", | |
| "Universidad Autónoma", | |
| "Barrio de Guadalupe", | |
| "Terminal de Autobuses", | |
| ) | |
| RADII = ("500 m", "1 km", "2 km", "3 km", "750 metros") | |
| REFERENCES = ( | |
| "Casa Azul", | |
| "La Estación", | |
| "Punto Cero", | |
| "El Farol", | |
| "Café Nómada", | |
| "La Casona", | |
| "Patio Central", | |
| "Foro Once", | |
| ) | |
| INTENT_EXTRAS: dict[str, tuple[tuple[str, tuple[tuple[str, str], ...]], ...]] = { | |
| "train": ( | |
| ("Dame algunas opciones para salir.", ()), | |
| ("Quiero algo parecido a Casa Azul.", (("REFERENCE", "Casa Azul"),)), | |
| ( | |
| "Algo como Café Nómada pero sin música.", | |
| (("REFERENCE", "Café Nómada"), ("EXCLUSION", "música")), | |
| ), | |
| ("Que sea silencioso y con wifi.", (("PREFERENCE", "silencioso"), ("PREFERENCE", "con wifi"))), | |
| ("No quiero lugares con humo.", (("EXCLUSION", "humo"),)), | |
| ("Cerca del Parque Bicentenario.", (("LOCATION", "Parque Bicentenario"),)), | |
| ("A no más de 3 km.", (("RADIUS", "3 km"),)), | |
| ( | |
| "Parecido a La Estación cerca de Plaza Ámbar.", | |
| (("REFERENCE", "La Estación"), ("LOCATION", "Plaza Ámbar")), | |
| ), | |
| ), | |
| "validation": ( | |
| ("Sorpréndeme con algún lugar distinto.", ()), | |
| ("Algo similar a Casa Frida.", (("REFERENCE", "Casa Frida"),)), | |
| ("Sin niños y sin música alta.", (("EXCLUSION", "niños"), ("EXCLUSION", "música alta"))), | |
| ("Que tenga enchufes y sea silencioso.", (("PREFERENCE", "enchufes"), ("PREFERENCE", "silencioso"))), | |
| ("Alrededor del Teatro de la Ciudad.", (("LOCATION", "Teatro de la Ciudad"),)), | |
| ("En un radio de 1200 metros.", (("RADIUS", "1200 metros"),)), | |
| ( | |
| "Como El Farol pero cerca del Barrio Antiguo.", | |
| (("REFERENCE", "El Farol"), ("LOCATION", "Barrio Antiguo")), | |
| ), | |
| ("Busco algo para pasar la tarde.", ()), | |
| ), | |
| "test": ( | |
| ("No sé qué quiero, enséñame lugares.", ()), | |
| ("Uno como La Casona.", (("REFERENCE", "La Casona"),)), | |
| ("Sin escaleras y sin demasiado ruido.", (("EXCLUSION", "escaleras"), ("EXCLUSION", "demasiado ruido"))), | |
| ("Que acepte mascotas y tenga estacionamiento.", (("PREFERENCE", "acepte mascotas"), ("PREFERENCE", "estacionamiento"))), | |
| ("Por la zona del Museo Regional.", (("LOCATION", "Museo Regional"),)), | |
| ("Máximo a 5 kilómetros.", (("RADIUS", "5 kilómetros"),)), | |
| ( | |
| "Parecido a Punto Cero alrededor de la Glorieta de la Paz.", | |
| (("REFERENCE", "Punto Cero"), ("LOCATION", "Glorieta de la Paz")), | |
| ), | |
| ("Quiero conocer algo nuevo hoy.", ()), | |
| ), | |
| } | |
| class PlaceSpec: | |
| split: str | |
| family: str | |
| name: str | |
| category: str | |
| description: str | |
| tags: str | |
| tag_families: str | |
| query: str | |
| concept_key: str | None = None | |
| PLACE_SPECS: tuple[PlaceSpec, ...] = ( | |
| PlaceSpec("train", "sweets", "Donas del Parque", "donut_shop", "Donas glaseadas y rellenas horneadas cada mañana.", "donas postres café", "comida ambiente", "se me antojan donas glaseadas"), | |
| PlaceSpec("train", "drinks", "Boba Nube", "bubble_tea_shop", "Tés fríos con tapioca, frutas y opciones sin lácteos.", "bubble tea tapioca bebidas", "comida preferencias", "dónde venden boba o té con tapioca"), | |
| PlaceSpec("train", "drinks", "Casa Té Jazmín", "tea_house", "Salón de té tranquilo con infusiones y repostería ligera.", "té infusiones tranquilo", "comida ambiente", "quiero un salón de té silencioso"), | |
| PlaceSpec("train", "food", "Birria del Barrio", "birria_restaurant", "Tacos y consomé de birria preparados al estilo tradicional.", "birria tacos consomé", "comida ocasión", "busco tacos de birria con consomé"), | |
| PlaceSpec("train", "food", "Ramen Neko", "ramen_restaurant", "Ramen japonés con caldos largos y alternativas vegetarianas.", "ramen japonés fideos", "comida preferencias", "quiero un buen ramen japonés"), | |
| PlaceSpec("train", "food", "Seúl Mesa", "korean_restaurant", "Cocina coreana con bibimbap, kimchi y parrilla de mesa.", "comida coreana kimchi bibimbap", "comida cultura", "dónde puedo comer comida coreana"), | |
| PlaceSpec("train", "food", "Verde Raíz", "vegan_restaurant", "Platillos veganos de temporada con ingredientes locales.", "vegano saludable local", "comida preferencias", "restaurante vegano con comida completa"), | |
| PlaceSpec("train", "work", "Café Página", "book_cafe", "Cafetería con libreros, mesas de lectura y ambiente calmado.", "libros café lectura", "actividad ambiente", "cafetería para leer entre libros"), | |
| PlaceSpec("train", "work", "Nube Cowork", "coworking_space", "Espacio de trabajo con internet rápido, cabinas y salas por hora.", "coworking wifi trabajo remoto", "actividad servicios", "necesito coworking con wifi y cabina"), | |
| PlaceSpec("train", "nature", "Jardín Ceiba", "botanical_garden", "Colección de plantas tropicales con senderos interpretativos.", "jardín botánico plantas naturaleza", "actividad ambiente", "quiero visitar un jardín botánico"), | |
| PlaceSpec("train", "nature", "Mirador del Cañón", "viewpoint", "Mirador al aire libre con vista panorámica del valle.", "mirador paisaje fotografía", "actividad ambiente", "un mirador para ver el atardecer"), | |
| PlaceSpec("train", "nature", "Sendero Encino", "nature_trail", "Ruta arbolada para caminata moderada y observación de aves.", "senderismo aves bosque", "actividad ambiente", "sendero natural para caminar"), | |
| PlaceSpec("train", "culture", "Museo Cosmos", "science_museum", "Exhibiciones interactivas de astronomía, física y tecnología.", "museo ciencia tecnología", "actividad cultura", "museo interactivo de ciencia"), | |
| PlaceSpec("train", "culture", "Galería Sur", "art_gallery", "Exposiciones temporales de artistas locales y arte contemporáneo.", "galería arte exposiciones", "actividad cultura", "galería de arte contemporáneo"), | |
| PlaceSpec("train", "culture", "Biblioteca Central", "public_library", "Acervo público, hemeroteca y salas silenciosas de estudio.", "biblioteca libros estudio", "actividad servicios", "biblioteca pública para estudiar"), | |
| PlaceSpec("train", "culture", "Cine Lumière", "independent_cinema", "Sala independiente con cine de autor y ciclos internacionales.", "cine independiente películas", "actividad cultura", "dónde proyectan cine independiente"), | |
| PlaceSpec("train", "entertainment", "Dados y Café", "board_game_cafe", "Juegos de mesa para grupos con bebidas y alimentos ligeros.", "juegos de mesa café grupos", "actividad ocasión", "café con juegos de mesa"), | |
| PlaceSpec("train", "entertainment", "Pixel Arcade", "arcade", "Máquinas arcade clásicas, simuladores y torneos casuales.", "arcade videojuegos retro", "actividad tecnología", "quiero jugar maquinitas arcade"), | |
| PlaceSpec("train", "entertainment", "Karaoke Luna", "karaoke_bar", "Salas privadas de karaoke con catálogo en español e inglés.", "karaoke música salas privadas", "actividad ocasión", "karaoke con sala privada"), | |
| PlaceSpec("train", "wellness", "Muro Alto", "climbing_gym", "Muros de escalada y boulder para principiantes y avanzados.", "escalada boulder ejercicio", "actividad deporte", "gimnasio con muro de escalada"), | |
| PlaceSpec("train", "wellness", "Yoga Amanecer", "yoga_studio", "Clases de yoga suave, restaurativo y meditación guiada.", "yoga meditación bienestar", "actividad ambiente", "estudio para clases de yoga"), | |
| PlaceSpec("train", "shopping", "Mercado Manos", "artisan_market", "Mercado de textiles, cerámica y productos de artesanos locales.", "artesanías textiles cerámica", "compras cultura", "mercado de artesanías locales"), | |
| PlaceSpec("train", "sweets", "Gelato Chiapas", "ice_cream_shop", "Helado italiano elaborado con frutas regionales.", "helado gelato postres", "comida preferencias", "heladería de gelato artesanal"), | |
| PlaceSpec("train", "lodging", "Hostal Viajero", "hostel", "Hospedaje económico con dormitorios y cocina compartida.", "hostal económico viajeros", "alojamiento servicios", "hostal barato para mochileros"), | |
| PlaceSpec("train", "nightlife", "Foro Sonoro", "live_music_bar", "Bar con escenario para bandas locales y conciertos pequeños.", "música en vivo bar conciertos", "actividad ambiente", "bar con música en vivo"), | |
| PlaceSpec("train", "drinks", "Café Altura", "specialty_coffee_shop", "Café de especialidad con granos regionales y métodos filtrados.", "café especialidad métodos", "comida preferencias", "cafetería de especialidad con buen espresso"), | |
| PlaceSpec("train", "food", "Mar Azul", "seafood_restaurant", "Mariscos frescos, ceviches y pescados a la plancha.", "mariscos ceviche pescado", "comida ocasión", "marisquería para comer ceviche"), | |
| PlaceSpec("train", "food", "Cocina Zoque", "regional_breakfast_restaurant", "Desayunos regionales con tamales, tascalate y huevos.", "desayuno chiapaneco regional", "comida cultura", "lugar de desayunos chiapanecos"), | |
| PlaceSpec("train", "sweets", "Pan Libre", "gluten_free_bakery", "Panadería dedicada a panes y postres sin gluten.", "panadería sin gluten postres", "comida preferencias", "panadería con opciones sin gluten"), | |
| PlaceSpec("train", "food", "Addis Mesa", "ethiopian_restaurant", "Cocina etíope para compartir con injera y guisos especiados.", "comida etíope injera", "comida cultura", "quiero probar injera y comida etíope"), | |
| PlaceSpec("train", "nightlife", "Jazz Sótano", "jazz_club", "Club íntimo con presentaciones de jazz y sesiones improvisadas.", "jazz música en vivo", "actividad ambiente", "casa de jazz con música en vivo"), | |
| PlaceSpec("train", "entertainment", "Rueda Libre", "skating_rink", "Pista cubierta para patines con renta de equipo.", "patinaje pista ejercicio", "actividad deporte", "pista para patinar bajo techo"), | |
| PlaceSpec("train", "sweets", "Cacao Taller", "chocolate_shop", "Chocolatería con cacao local, bebidas y bombones artesanales.", "chocolate cacao bombones", "comida cultura", "chocolatería artesanal con cacao local"), | |
| PlaceSpec("train", "shopping", "Vivero Orquídea", "plant_nursery", "Vivero especializado en orquídeas, plantas y asesoría.", "vivero orquídeas plantas", "compras naturaleza", "vivero donde vendan orquídeas"), | |
| PlaceSpec("train", "shopping", "Comic Planet", "comic_book_store", "Tienda de cómics, manga, novelas gráficas y coleccionables.", "cómics manga coleccionables", "compras cultura", "tienda de comics y manga"), | |
| PlaceSpec("train", "wellness", "Temazcal Tierra", "temazcal", "Baño de vapor tradicional con sesiones guiadas de bienestar.", "temazcal vapor bienestar", "actividad cultura", "quiero una sesión de temazcal"), | |
| PlaceSpec("train", "culture", "Barro Vivo", "pottery_studio", "Taller de cerámica con clases, torno y pintura de piezas.", "cerámica barro talleres", "actividad cultura", "taller para aprender cerámica"), | |
| PlaceSpec("train", "food", "Cedro Libanés", "lebanese_restaurant", "Cocina libanesa con hummus, falafel y pan recién hecho.", "comida libanesa hummus falafel", "comida cultura", "restaurante libanés con falafel"), | |
| PlaceSpec("train", "nature", "Huellas", "dog_park", "Parque cercado con áreas de juego y bebederos para perros.", "parque perros mascotas", "actividad ambiente", "parque cercado para llevar a mi perro"), | |
| PlaceSpec("train", "culture", "Observatorio Sierra", "astronomical_observatory", "Observación nocturna con telescopios y charlas de astronomía.", "observatorio estrellas telescopio", "actividad cultura", "observatorio para ver estrellas"), | |
| PlaceSpec("train", "food", "Taquería Nocturna", "taco_shop", "Tacos al carbón y guisos servidos hasta la madrugada.", "tacos nocturno económico", "comida ambiente", "taquería abierta de madrugada"), | |
| PlaceSpec("train", "nightlife", "Agave Casa", "mezcal_bar", "Mezcalería con degustaciones guiadas de productores regionales.", "mezcal degustación agave", "comida cultura", "mezcalería con degustación"), | |
| PlaceSpec("train", "shopping", "Librería Andante", "bookstore", "Librería independiente con narrativa, ensayo y presentaciones.", "librería libros lectura", "compras cultura", "librería independiente con libros"), | |
| PlaceSpec("train", "culture", "Fototeca Luz", "photography_museum", "Archivo y exposiciones dedicadas a fotografía histórica.", "fotografía museo exposiciones", "actividad cultura", "museo dedicado a fotografía"), | |
| PlaceSpec("train", "entertainment", "Casa Salsa", "dance_studio", "Clases sociales de salsa, bachata y ritmos latinos.", "baile salsa bachata", "actividad deporte", "clases de salsa para principiantes"), | |
| PlaceSpec("train", "wellness", "Cancha Urbana", "sports_centre", "Canchas multiusos para básquetbol, voleibol y fútbol rápido.", "canchas deportes ejercicio", "actividad deporte", "centro deportivo con canchas"), | |
| PlaceSpec("train", "family", "Ludoteca Peques", "children_activity_centre", "Juegos educativos, lectura y actividades para niñas y niños.", "ludoteca niños juegos", "actividad ocasión", "ludoteca con actividades infantiles"), | |
| PlaceSpec("train", "nightlife", "Azotea 360", "rooftop_bar", "Terraza en azotea con coctelería y vista de la ciudad.", "rooftop terraza cocteles", "ambiente ocasión", "bar de azotea con vista"), | |
| PlaceSpec("validation", "food", "Pupusas Centroamérica", "pupusa_restaurant", "Pupusas de queso, frijol y chicharrón hechas al momento.", "pupusas comida salvadoreña", "comida cultura", "dónde comer pupusas salvadoreñas"), | |
| PlaceSpec("validation", "sweets", "Crêpe Maison", "creperie", "Crepas dulces y saladas con café y fruta fresca.", "crepas postres café", "comida ambiente", "crepería con opciones saladas"), | |
| PlaceSpec("validation", "culture", "Planetario Orión", "planetarium", "Proyecciones del cielo y actividades educativas de astronomía.", "planetario estrellas ciencia", "actividad cultura", "planetario con funciones de astronomía"), | |
| PlaceSpec("validation", "wellness", "Aqua Centro", "aquatic_centre", "Alberca techada con carriles y clases de natación.", "natación alberca ejercicio", "actividad deporte", "centro acuático con alberca techada"), | |
| PlaceSpec("validation", "entertainment", "Clave Oculta", "escape_room", "Salas de escape con acertijos para equipos pequeños.", "escape room acertijos grupos", "actividad ocasión", "quiero un escape room para amigos"), | |
| PlaceSpec("validation", "shopping", "Vinilo Sur", "record_store", "Discos de vinilo nuevos y usados de diversos géneros.", "vinilos discos música", "compras cultura", "tienda de discos de vinilo"), | |
| PlaceSpec("validation", "wellness", "Centro Presente", "meditation_centre", "Meditación guiada y prácticas de respiración en silencio.", "meditación respiración silencio", "actividad ambiente", "centro tranquilo para meditar"), | |
| PlaceSpec("validation", "drinks", "Lúpulo Local", "craft_brewery", "Cervecería artesanal con recorridos y degustación de temporada.", "cerveza artesanal degustación", "comida actividad", "cervecería artesanal con degustación"), | |
| PlaceSpec("validation", "food", "Sushi Viajero", "conveyor_belt_sushi", "Sushi servido en banda transportadora con piezas preparadas al momento.", "sushi banda japonesa", "comida cultura", "sushi de banda transportadora"), | |
| PlaceSpec("validation", "shopping", "Cero Residuo", "zero_waste_store", "Tienda a granel de alimentos y productos de limpieza reutilizables.", "granel ecológico reutilizable", "compras preferencias", "tienda sin empaques para comprar a granel"), | |
| PlaceSpec("validation", "entertainment", "Salto Alto", "trampoline_park", "Parque interior de trampolines con zonas para distintas edades.", "trampolines saltos familiar", "actividad deporte", "parque de trampolines bajo techo"), | |
| PlaceSpec("validation", "culture", "Teatro Bel Canto", "opera_house", "Recinto para ópera, recitales vocales y música de cámara.", "ópera teatro conciertos", "actividad cultura", "teatro donde presenten ópera"), | |
| PlaceSpec("validation", "nature", "Finca Aroma", "coffee_farm_tour", "Recorrido por cafetales con explicación del cultivo y tostado.", "café finca recorrido", "actividad cultura", "tour por una finca de café"), | |
| PlaceSpec("validation", "wellness", "Raqueta Sur", "squash_club", "Canchas de squash con renta de equipo y clases individuales.", "squash raqueta canchas", "actividad deporte", "club con canchas de squash"), | |
| PlaceSpec("validation", "work", "Fábrica Lab", "makerspace", "Taller compartido con impresoras 3D, electrónica y herramientas.", "makerspace impresión 3d electrónica", "actividad tecnología", "makerspace con impresora 3d"), | |
| PlaceSpec("validation", "culture", "Estudio Obturador", "photography_studio", "Estudio fotográfico con ciclorama, iluminación y renta por hora.", "fotografía estudio iluminación", "actividad servicios", "estudio de fotografía con ciclorama"), | |
| PlaceSpec("validation", "drinks", "Verso Café", "poetry_cafe", "Cafetería con lecturas de poesía y micrófono abierto.", "poesía café lectura", "actividad cultura", "café con lecturas de poesía"), | |
| PlaceSpec("validation", "wellness", "Baño Nube", "hammam_spa", "Baño de vapor tipo hammam con circuitos de relajación.", "hammam vapor spa", "actividad ambiente", "spa con baño tipo hammam"), | |
| PlaceSpec("validation", "shopping", "Cosecha Directa", "farmers_market", "Mercado semanal de frutas, verduras y productos de agricultores.", "mercado agricultores orgánico", "compras comida", "mercado de productores y agricultores"), | |
| PlaceSpec("validation", "food", "Taco Verde", "vegan_taco_shop", "Tacos veganos con guisos de setas, legumbres y salsas caseras.", "tacos veganos setas", "comida preferencias", "taquería completamente vegana"), | |
| PlaceSpec("test", "food", "Arepa Morena", "arepa_restaurant", "Arepas venezolanas rellenas y preparadas al momento.", "arepas comida venezolana", "comida cultura", "lugar de arepas venezolanas"), | |
| PlaceSpec("test", "drinks", "Michi Café", "cat_cafe", "Cafetería con zona separada para convivencia con gatos rescatados.", "gatos café mascotas", "ambiente ocasión", "cafetería donde pueda convivir con gatos"), | |
| PlaceSpec("test", "family", "Laboratorio Curioso", "children_science_centre", "Experimentos y talleres de ciencia para público infantil.", "ciencia niños experimentos", "actividad cultura", "talleres de ciencia para niños"), | |
| PlaceSpec("test", "nature", "Santuario Monarca", "butterfly_sanctuary", "Jardín de conservación y observación de mariposas.", "mariposas santuario naturaleza", "actividad ambiente", "santuario para observar mariposas"), | |
| PlaceSpec("test", "entertainment", "Risa Abierta", "comedy_club", "Escenario de stand up y noches de micrófono abierto.", "comedia stand up espectáculo", "actividad ambiente", "club con show de stand up"), | |
| PlaceSpec("test", "work", "Café Babel", "language_exchange_cafe", "Encuentros de conversación para practicar varios idiomas.", "idiomas intercambio café", "actividad cultura", "café con intercambio de idiomas"), | |
| PlaceSpec("test", "sweets", "Miga Verde", "vegan_bakery", "Pan dulce y pasteles veganos sin ingredientes animales.", "panadería vegana postres", "comida preferencias", "panadería con pasteles veganos"), | |
| PlaceSpec("test", "lodging", "Bosque Domo", "glamping", "Domos equipados para acampar con comodidad cerca del bosque.", "glamping camping naturaleza", "alojamiento ambiente", "glamping en domo cerca de naturaleza"), | |
| PlaceSpec("test", "food", "La Media Luna", "empanada_shop", "Empanadas horneadas con rellenos salados y dulces.", "empanadas horno comida", "comida ocasión", "lugar especializado en empanadas"), | |
| PlaceSpec("test", "nature", "Herpetario Verde", "reptile_house", "Centro de conservación con reptiles y visitas educativas.", "reptiles conservación educación", "actividad naturaleza", "herpetario para conocer reptiles"), | |
| PlaceSpec("test", "culture", "Museo del Barro", "ceramics_museum", "Colección histórica de alfarería y cerámica regional.", "museo cerámica alfarería", "actividad cultura", "museo sobre cerámica regional"), | |
| PlaceSpec("test", "wellness", "Pista Furia", "roller_derby_rink", "Pista y entrenamientos de roller derby para varios niveles.", "roller derby patines deporte", "actividad deporte", "dónde entrenar roller derby"), | |
| PlaceSpec("test", "work", "Voz Estudio", "podcast_studio", "Cabinas tratadas acústicamente para grabar y editar podcasts.", "podcast grabación cabina", "actividad tecnología", "estudio para grabar un podcast"), | |
| PlaceSpec("test", "drinks", "Invernadero Café", "indoor_garden_cafe", "Cafetería dentro de un jardín interior con abundantes plantas.", "café jardín interior plantas", "ambiente comida", "cafetería dentro de un invernadero"), | |
| PlaceSpec("test", "family", "Robot Peques", "robotics_workshop", "Talleres infantiles de robótica, programación y construcción.", "robótica niños programación", "actividad tecnología", "taller de robótica para niños"), | |
| PlaceSpec("test", "nature", "Remo Río", "kayak_rental", "Renta de kayaks con equipo de seguridad y rutas guiadas.", "kayak remo río", "actividad deporte", "renta de kayak con guía"), | |
| PlaceSpec("test", "shopping", "Quesería Sierra", "cheese_shop", "Tienda de quesos regionales con degustación y productos artesanales.", "quesos degustación artesanal", "compras comida", "tienda especializada en quesos"), | |
| PlaceSpec("test", "wellness", "Circo Aire", "circus_school", "Escuela de telas aéreas, acrobacia y equilibrio para principiantes.", "circo acrobacia telas", "actividad deporte", "escuela para aprender telas aéreas"), | |
| PlaceSpec("test", "nature", "Reserva Alas", "birdwatching_reserve", "Reserva natural con observatorios y recorridos para avistar aves.", "aves reserva observación", "actividad naturaleza", "reserva para observación de aves"), | |
| PlaceSpec("test", "nightlife", "Bar Cero", "alcohol_free_bar", "Bar nocturno de coctelería sin alcohol y música moderada.", "mocktails sin alcohol nocturno", "comida ambiente", "bar nocturno sin bebidas alcohólicas"), | |
| ) | |
| # Validation and test deliberately contain several queries for the same relevant | |
| # document. Validation supports epoch/model selection without inspecting test. | |
| # Test remains the final, harder holdout. | |
| # This is closer to production retrieval than a one-query/one-document lookup: | |
| # the model must survive slang, misspellings, implicit needs and contrastive | |
| # wording while ranking against the complete test corpus. Keep these variants | |
| # out of train to prevent evaluation leakage. | |
| RETRIEVAL_VALIDATION_QUERY_VARIANTS: dict[ | |
| str, | |
| tuple[tuple[str, tuple[str, ...]], ...], | |
| ] = { | |
| "Pupusas Centroamérica": ( | |
| ("se me antoja comida salvadoreña de masa rellena", ("implicit", "paraphrase")), | |
| ("unas pupusitas con curtido dónde", ("colloquial", "implicit")), | |
| ), | |
| "Crêpe Maison": ( | |
| ("quiero algo enrollado que pueda ser dulce o salado", ("implicit", "contrastive")), | |
| ("crepas pa cenar, no solo postre", ("colloquial", "contrastive")), | |
| ), | |
| "Planetario Orión": ( | |
| ("quiero ver el cielo proyectado sin salir de la ciudad", ("implicit", "paraphrase")), | |
| ("plan de estrellas y astronomía bajo techo", ("implicit", "constraint")), | |
| ), | |
| "Aqua Centro": ( | |
| ("donde hay carriles pa echarme unos largos", ("colloquial", "implicit")), | |
| ("alberca techada con clases para nadar", ("paraphrase", "constraint")), | |
| ), | |
| "Clave Oculta": ( | |
| ("plan con acertijos pa escapar con la banda", ("colloquial", "implicit")), | |
| ("cuarto temático donde resolvemos pistas", ("implicit", "paraphrase")), | |
| ), | |
| "Vinilo Sur": ( | |
| ("dónde consigo discos de los grandotes", ("colloquial", "implicit")), | |
| ("ando cazando elepés usados", ("colloquial", "paraphrase")), | |
| ), | |
| "Centro Presente": ( | |
| ("necesito respirar y apagar la mente un rato", ("implicit", "colloquial")), | |
| ("un lugar silencioso con meditación guiada", ("paraphrase", "constraint")), | |
| ), | |
| "Lúpulo Local": ( | |
| ("cheve artesanal y que expliquen cómo la hacen", ("colloquial", "implicit")), | |
| ("quiero degustar varias cervezas locales", ("paraphrase",)), | |
| ), | |
| "Sushi Viajero": ( | |
| ("sushi que vaya pasando frente a la mesa", ("implicit", "paraphrase")), | |
| ("japonés con banda de platitos", ("colloquial", "implicit")), | |
| ), | |
| "Cero Residuo": ( | |
| ("comprar despensa llevando mis propios frascos", ("implicit", "constraint")), | |
| ("tienda a granel sin tanto empaque", ("paraphrase", "constraint")), | |
| ), | |
| "Salto Alto": ( | |
| ("dónde llevo a brincar a los chamacos", ("colloquial", "implicit")), | |
| ("parque techado lleno de camas elásticas", ("paraphrase", "implicit")), | |
| ), | |
| "Teatro Bel Canto": ( | |
| ("quiero escuchar voces líricas en un teatro", ("implicit", "paraphrase")), | |
| ("algún recinto con bel canto y música de cámara", ("paraphrase",)), | |
| ), | |
| "Finca Aroma": ( | |
| ("quiero ver de dónde sale el café antes de la taza", ("implicit", "paraphrase")), | |
| ("paseo entre cafetales con tostado", ("implicit", "paraphrase")), | |
| ), | |
| "Raqueta Sur": ( | |
| ("dónde rentan raqueta y cancha de squash", ("constraint", "paraphrase")), | |
| ("quiero pegarle a la pelota contra la pared", ("implicit", "colloquial")), | |
| ), | |
| "Fábrica Lab": ( | |
| ("taller para fabricar prototipos con impresora 3d", ("implicit", "paraphrase")), | |
| ("necesito herramientas y electrónica compartidas", ("implicit", "constraint")), | |
| ), | |
| "Estudio Obturador": ( | |
| ("renta de ciclorama y luces por hora", ("constraint", "paraphrase")), | |
| ("lugar equipado pa una sesión de fotos", ("colloquial", "implicit")), | |
| ), | |
| "Verso Café": ( | |
| ("cafecito con micro abierto para leer versos", ("colloquial", "implicit")), | |
| ("dónde escucho poesía mientras tomo algo", ("implicit", "paraphrase")), | |
| ), | |
| "Baño Nube": ( | |
| ("quiero un baño de vapor árabe", ("implicit", "paraphrase")), | |
| ("spa con circuito de vapor tipo turco", ("paraphrase", "constraint")), | |
| ), | |
| "Cosecha Directa": ( | |
| ("verduras directo de quien las cosecha", ("implicit", "paraphrase")), | |
| ("mercadito semanal de productores", ("colloquial", "paraphrase")), | |
| ), | |
| "Taco Verde": ( | |
| ("tacos sin nada de origen animal", ("implicit", "constraint")), | |
| ("taquitos de setas, cero carne", ("colloquial", "contrastive")), | |
| ), | |
| } | |
| RETRIEVAL_TEST_QUERY_VARIANTS: dict[ | |
| str, | |
| tuple[tuple[str, tuple[str, ...]], ...], | |
| ] = { | |
| "Arepa Morena": ( | |
| ("traigo antojo de una reina pepiada", ("colloquial", "implicit")), | |
| ("onde venden arepitas chidas", ("colloquial", "misspelling")), | |
| ("algo venezolano relleno y hecho al momento", ("implicit", "paraphrase")), | |
| ), | |
| "Michi Café": ( | |
| ("un cafecito pa echar el rato con michis", ("colloquial", "implicit")), | |
| ("cafeteria con gatitos rescatados", ("misspelling", "paraphrase")), | |
| ("quiero tomar algo rodeado de mininos", ("implicit", "paraphrase")), | |
| ), | |
| "Laboratorio Curioso": ( | |
| ("algo chido pa que los peques hagan experimentos", ("colloquial", "implicit")), | |
| ("museo no, mejor talleres cientificos para morritos", ("contrastive", "colloquial")), | |
| ("donde llevo a mi hijo a aprender ciencia jugando", ("implicit", "paraphrase")), | |
| ), | |
| "Santuario Monarca": ( | |
| ("quiero ver un buen de mariposas en un jardin", ("colloquial", "misspelling")), | |
| ("algún lugar de conservación de monarcas", ("implicit", "paraphrase")), | |
| ("plan tranqui entre plantas y maripositas", ("colloquial", "implicit")), | |
| ), | |
| "Risa Abierta": ( | |
| ("donde hay standap esta noche", ("misspelling", "colloquial")), | |
| ("quiero echarme unas risas con micro abierto", ("colloquial", "implicit")), | |
| ("un plan de comedia en vivo, no cine", ("contrastive", "paraphrase")), | |
| ), | |
| "Café Babel": ( | |
| ("cafecito pa practicar inglés con la banda", ("colloquial", "implicit")), | |
| ("intercambio d idiomas en un lugar para tomar algo", ("misspelling", "paraphrase")), | |
| ("quiero conversar con gente en otras lenguas", ("implicit", "paraphrase")), | |
| ), | |
| "Miga Verde": ( | |
| ("algo dulce pero cero ingredientes animales", ("implicit", "contrastive")), | |
| ("panecito vegano pa'l antojo", ("colloquial", "paraphrase")), | |
| ("pasteles sin leche ni huevo, qué hay", ("implicit", "constraint")), | |
| ), | |
| "Bosque Domo": ( | |
| ("acampar sí pero sin sufrirle", ("colloquial", "implicit")), | |
| ("un domito cómodo en el bosque", ("colloquial", "paraphrase")), | |
| ("naturaleza con cama y techo, plan de finde", ("implicit", "colloquial")), | |
| ), | |
| "La Media Luna": ( | |
| ("se me antoja algo relleno y horneadito", ("implicit", "colloquial")), | |
| ("onde hay empanadas dulces y saladas", ("misspelling", "paraphrase")), | |
| ("un lugar de medias lunas no, de empanadas", ("contrastive", "adversarial")), | |
| ), | |
| "Herpetario Verde": ( | |
| ("quiero ver víboras y lagartos", ("implicit", "paraphrase")), | |
| ("un plan educativo con animalitos de sangre fría", ("implicit", "paraphrase")), | |
| ("donde conocen los peques a los reptiles", ("colloquial", "implicit")), | |
| ), | |
| "Museo del Barro": ( | |
| ("quiero ver piezas antiguas hechas de barro", ("implicit", "paraphrase")), | |
| ("museo d alfareria de la region", ("misspelling", "paraphrase")), | |
| ("un plan cultural sobre vasijas y cerámica", ("implicit", "paraphrase")), | |
| ), | |
| "Pista Furia": ( | |
| ("dónde entrenan ese deporte rudo en patines", ("implicit", "paraphrase")), | |
| ("quiero darle al roler derbi", ("misspelling", "colloquial")), | |
| ("pista para aprender derby sobre ruedas", ("paraphrase", "implicit")), | |
| ), | |
| "Voz Estudio": ( | |
| ("necesito una cabina que no meta ruido pa grabar", ("colloquial", "implicit")), | |
| ("donde puedo producir mi podcast", ("paraphrase",)), | |
| ("un estudio con tratamiento acustico y edición", ("misspelling", "implicit")), | |
| ), | |
| "Invernadero Café": ( | |
| ("cafecito entre un montón de plantas", ("colloquial", "implicit")), | |
| ("un lugar verde para tomar café sin estar afuera", ("contrastive", "implicit")), | |
| ("cafeteria tipo jardin techado", ("misspelling", "paraphrase")), | |
| ), | |
| "Robot Peques": ( | |
| ("algo pa que mi morrito arme robots", ("colloquial", "implicit")), | |
| ("tayer infantil de programacion y electronica", ("misspelling", "paraphrase")), | |
| ("donde enseñan tecnología construyendo cosas", ("implicit", "paraphrase")), | |
| ), | |
| "Remo Río": ( | |
| ("quiero remar pero necesito que me presten todo", ("implicit", "constraint")), | |
| ("renta de kayak pa novatos con alguien que guíe", ("colloquial", "constraint")), | |
| ("plan en el río con remo y equipo seguro", ("implicit", "paraphrase")), | |
| ), | |
| "Quesería Sierra": ( | |
| ("quiero probar quesitos de la región", ("colloquial", "implicit")), | |
| ("una tienda donde den degustacion de queso artesanal", ("misspelling", "paraphrase")), | |
| ("ando buscando lácteos locales, sobre todo quesos", ("implicit", "paraphrase")), | |
| ), | |
| "Circo Aire": ( | |
| ("quiero aprender a colgarme de telas sin matarme", ("colloquial", "implicit")), | |
| ("clases d acrobacia aerea pa principiantes", ("misspelling", "colloquial")), | |
| ("una escuela de circo con equilibrio y telas", ("paraphrase",)), | |
| ), | |
| "Reserva Alas": ( | |
| ("plan para pajarear con binoculares", ("colloquial", "implicit")), | |
| ("un sitio con observatorios para avistar plumíferos", ("paraphrase", "implicit")), | |
| ("quiero caminar por una reserva viendo aves", ("paraphrase",)), | |
| ), | |
| "Bar Cero": ( | |
| ("quiero salir de noche sin ponerme peda", ("colloquial", "implicit")), | |
| ("bar con tragos cero alcohol y ambiente tranqui", ("colloquial", "constraint")), | |
| ("mocktels y musiquita pero nada de chela", ("misspelling", "colloquial", "constraint")), | |
| ), | |
| } | |
| def main() -> None: | |
| INTENT_ROOT.mkdir(parents=True, exist_ok=True) | |
| RETRIEVAL_ROOT.mkdir(parents=True, exist_ok=True) | |
| manifest: dict[str, object] = { | |
| "schema_version": 1, | |
| "license": "Proprietary - Frimeet internal training data", | |
| "provenance": "Synthetic bootstrap corpus; contains no production user data", | |
| "intent": {}, | |
| "retrieval": {}, | |
| } | |
| for split in SPLITS: | |
| intent_records = _intent_records(split) | |
| retrieval_records = _retrieval_records(split) | |
| intent_path = INTENT_ROOT / f"{split}.jsonl" | |
| retrieval_path = RETRIEVAL_ROOT / f"{split}.jsonl" | |
| _write_jsonl(intent_path, intent_records) | |
| _write_jsonl(retrieval_path, retrieval_records) | |
| manifest["intent"][split] = _file_manifest(intent_path, intent_records) # type: ignore[index] | |
| manifest["retrieval"][split] = _file_manifest(retrieval_path, retrieval_records) # type: ignore[index] | |
| _validate_cross_split_uniqueness() | |
| _validate_generated_files() | |
| (DATA_ROOT / "manifest.json").write_text( | |
| json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", | |
| encoding="utf-8", | |
| ) | |
| print(json.dumps(manifest, ensure_ascii=False, indent=2)) | |
| def _intent_records(split: str) -> list[dict[str, object]]: | |
| records: list[dict[str, object]] = [] | |
| for index, concept in enumerate(INTENT_CONCEPTS[split]): | |
| concept_variant = INTENT_CATEGORY_VARIANTS.get(concept, concept) | |
| preference = PREFERENCES[index % len(PREFERENCES)] | |
| exclusion = EXCLUSIONS[index % len(EXCLUSIONS)] | |
| location = LOCATIONS[index % len(LOCATIONS)] | |
| radius = RADII[index % len(RADII)] | |
| records.extend( | |
| ( | |
| _annotated(f"Busco {concept}.", (("CATEGORY", concept),)), | |
| _annotated( | |
| f"Quiero {concept_variant} {preference}.", | |
| (("CATEGORY", concept_variant), ("PREFERENCE", preference)), | |
| ), | |
| _annotated( | |
| f"Recomiéndame {concept} sin {exclusion} cerca de {location}.", | |
| ( | |
| ("CATEGORY", concept), | |
| ("EXCLUSION", exclusion), | |
| ("LOCATION", location), | |
| ), | |
| ), | |
| _annotated( | |
| f"¿Hay {concept_variant} a menos de {radius} de {location}?", | |
| ( | |
| ("CATEGORY", concept_variant), | |
| ("RADIUS", radius), | |
| ("LOCATION", location), | |
| ), | |
| ), | |
| ) | |
| ) | |
| reference_examples = 12 if split == "train" else 2 | |
| if index < reference_examples: | |
| reference = REFERENCES[index % len(REFERENCES)] | |
| records.append( | |
| _annotated( | |
| f"Busco {concept_variant} parecido a {reference}.", | |
| (("CATEGORY", concept_variant), ("REFERENCE", reference)), | |
| ) | |
| ) | |
| records.extend(_annotated(text, spans) for text, spans in INTENT_EXTRAS[split]) | |
| return records | |
| def _annotated( | |
| text: str, | |
| annotations: Sequence[tuple[str, str]], | |
| ) -> dict[str, object]: | |
| search_from: dict[str, int] = {} | |
| spans: list[dict[str, object]] = [] | |
| for slot, value in annotations: | |
| start = text.index(value, search_from.get(value, 0)) | |
| end = start + len(value) | |
| search_from[value] = end | |
| spans.append({"start": start, "end": end, "slot": slot}) | |
| payload: dict[str, object] = { | |
| "text": text, | |
| "spans": sorted(spans, key=lambda span: (int(span["start"]), int(span["end"]))), | |
| } | |
| validate_training_example(payload) | |
| return payload | |
| def _retrieval_records(split: str) -> list[dict[str, object]]: | |
| specs = _expanded_place_specs(split) | |
| documents = {spec.name: _place_document(spec) for spec in specs} | |
| records: list[dict[str, object]] = [] | |
| for index, spec in enumerate(specs): | |
| same_family = [ | |
| candidate | |
| for candidate in specs | |
| if candidate.name != spec.name | |
| and candidate.family == spec.family | |
| and _concept_key(candidate) != _concept_key(spec) | |
| ] | |
| other_family = [ | |
| candidate | |
| for candidate in specs | |
| if candidate.name != spec.name | |
| and candidate.family != spec.family | |
| and _concept_key(candidate) != _concept_key(spec) | |
| ] | |
| rotated = other_family[index % len(other_family) :] + other_family[: index % len(other_family)] | |
| negative_limit = 3 if split == "train" else 7 | |
| negative_specs = _unique_specs((*same_family, *rotated))[:negative_limit] | |
| if len(negative_specs) < negative_limit: | |
| raise RuntimeError(f"Not enough hard negatives for {spec.name}") | |
| query_variants = [(spec.query, ("canonical",))] | |
| if split == "validation": | |
| query_variants.extend(RETRIEVAL_VALIDATION_QUERY_VARIANTS[spec.name]) | |
| elif split == "test": | |
| query_variants.extend(RETRIEVAL_TEST_QUERY_VARIANTS[spec.name]) | |
| for query, challenge_tags in query_variants: | |
| record: dict[str, object] = { | |
| "query": query, | |
| "positive": documents[spec.name], | |
| "hard_negatives": [documents[item.name] for item in negative_specs], | |
| } | |
| if split != "train": | |
| record["challenge_tags"] = list(challenge_tags) | |
| records.append(record) | |
| return records | |
| def _expanded_place_specs(split: str) -> list[PlaceSpec]: | |
| """Create unique, discriminative venue documents without split leakage.""" | |
| expanded: list[PlaceSpec] = [] | |
| base_specs = [item for item in PLACE_SPECS if item.split == split] | |
| variants = 3 if split == "train" else 1 | |
| for index, spec in enumerate(base_specs): | |
| expanded.append(replace(spec, concept_key=spec.name)) | |
| if variants >= 2: | |
| expanded.append( | |
| replace( | |
| spec, | |
| name=f"{spec.name} Norte", | |
| description=( | |
| f"{spec.description} Esta sede ofrece acceso sin escalones, " | |
| "señalización clara y un ambiente tranquilo." | |
| ), | |
| tags=f"{spec.tags} accesible tranquilo", | |
| query=( | |
| f"{spec.query}; prefiero acceso sin escalones y ambiente tranquilo" | |
| ), | |
| concept_key=spec.name, | |
| ) | |
| ) | |
| if variants >= 3: | |
| expanded.append( | |
| replace( | |
| spec, | |
| name=f"{spec.name} Jardín", | |
| description=( | |
| f"{spec.description} Esta sede cuenta con terraza, mesas al " | |
| "aire libre y alternativas de precio moderado." | |
| ), | |
| tags=f"{spec.tags} terraza aire libre económico", | |
| query=( | |
| f"{spec.query}; ideal si tiene terraza y precios moderados" | |
| ), | |
| concept_key=spec.name, | |
| ) | |
| ) | |
| # Sixteen extra train-only documents reach 160 training pairs while | |
| # validation/test keep exactly one document per unseen concept. This | |
| # avoids ambiguous evaluation qrels caused by sibling venues. | |
| if split == "train" and index < 16: | |
| expanded.append( | |
| replace( | |
| spec, | |
| name=f"{spec.name} Plaza", | |
| description=( | |
| f"{spec.description} Esta sede acepta reservaciones, tiene " | |
| "área para grupos y mantiene horario extendido." | |
| ), | |
| tags=f"{spec.tags} reservaciones grupos horario extendido", | |
| query=( | |
| f"{spec.query}; necesito reservar para un grupo y llegar tarde" | |
| ), | |
| concept_key=spec.name, | |
| ) | |
| ) | |
| return expanded | |
| def _concept_key(spec: PlaceSpec) -> str: | |
| return spec.concept_key or spec.name | |
| def _place_document(spec: PlaceSpec) -> str: | |
| return ( | |
| f"Nombre: {spec.name}. " | |
| f"Tipo registrado: {spec.category.replace('_', ' ')}. " | |
| f"Descripcion: {spec.description} " | |
| f"Etiquetas: {spec.tags}. " | |
| f"Familias de etiquetas: {spec.tag_families}." | |
| ) | |
| def _unique_specs(specs: Iterable[PlaceSpec]) -> list[PlaceSpec]: | |
| unique: list[PlaceSpec] = [] | |
| seen: set[str] = set() | |
| for spec in specs: | |
| if spec.name not in seen: | |
| unique.append(spec) | |
| seen.add(spec.name) | |
| return unique | |
| def _write_jsonl(path: Path, records: Sequence[dict[str, object]]) -> None: | |
| path.write_text( | |
| "".join( | |
| json.dumps(record, ensure_ascii=False, separators=(",", ":")) + "\n" | |
| for record in records | |
| ), | |
| encoding="utf-8", | |
| ) | |
| def _file_manifest( | |
| path: Path, | |
| records: Sequence[dict[str, object]], | |
| ) -> dict[str, object]: | |
| return { | |
| "path": path.relative_to(ROOT).as_posix(), | |
| "records": len(records), | |
| "sha256": hashlib.sha256(path.read_bytes()).hexdigest(), | |
| } | |
| def _validate_cross_split_uniqueness() -> None: | |
| intent_seen: dict[str, str] = {} | |
| retrieval_seen: dict[str, str] = {} | |
| positive_seen: dict[str, str] = {} | |
| for split in SPLITS: | |
| for record in _intent_records(split): | |
| text = str(record["text"]).casefold() | |
| if text in intent_seen: | |
| raise ValueError(f"Duplicate intent text in {intent_seen[text]} and {split}: {text}") | |
| intent_seen[text] = split | |
| for record in _retrieval_records(split): | |
| query = str(record["query"]).casefold() | |
| positive = str(record["positive"]) | |
| if query in retrieval_seen: | |
| raise ValueError(f"Duplicate retrieval query in {retrieval_seen[query]} and {split}: {query}") | |
| positive_owner = positive_seen.get(positive) | |
| if positive_owner is not None and positive_owner != split: | |
| raise ValueError( | |
| f"Duplicate positive document in {positive_owner} and {split}" | |
| ) | |
| retrieval_seen[query] = split | |
| positive_seen[positive] = split | |
| def _validate_generated_files() -> None: | |
| for split in SPLITS: | |
| _read_training_rows(RETRIEVAL_ROOT / f"{split}.jsonl") | |
| if __name__ == "__main__": | |
| main() | |