import os import json def load_config(config_path="configs/config.json"): with open(config_path, "r", encoding="utf-8") as f: config = json.load(f) return config def load_prompts(path="configs/prompts.json"): with open(path, "r", encoding="utf-8") as f: return json.load(f)["system_prompts"] def load_md(file_path): with open(file_path, "r", encoding="utf-8") as f: text = f.read() return text def extract_title_from_md(text: str, default: str) -> str: """Extrae un título legible desde el contenido Markdown. Nueva heurística (pensada para tus .md normalizados): - Prioriza el primer encabezado Markdown que empiece por '#'. - Ignora líneas que sean solo año/número o termos genéricos como "Article". - Si no hay encabezado válido, usa la primera línea de texto no vacía que no sea solo año/número/genérico. - Si todo falla, devuelve ``default``. """ if not text: return default def _es_generico_ou_numero(line: str) -> bool: """Detecta líneas poco informativas: solo número/año o rótulos genéricos.""" val = line.strip() if not val: return True # Solo dígitos ("2007", "69", etc.) if val.isdigit(): return True # Año de 4 dígitos aislado if len(val) == 4 and val.isdigit(): return True lower = val.lower() genericos = { "article", "artigo", "artículo", "issue", "number", "número", } if lower in genericos: return True return False lines = [l.rstrip("\n") for l in text.splitlines()] # 1) Buscar primer heading "#" que no sea genérico/numérico for raw_line in lines: line = raw_line.strip() if not line: continue if line.startswith("#"): candidate = line.lstrip("#").strip() if candidate and not _es_generico_ou_numero(candidate): return candidate # 2) Si no hay heading válido, buscar primera línea de texto informativa for raw_line in lines: line = raw_line.strip() if not line: continue if not _es_generico_ou_numero(line): return line # 3) Fallback return default