from __future__ import annotations import json import os import subprocess from functools import lru_cache from typing import Any from news_mcp.entity_normalize import normalize_entity @lru_cache(maxsize=1024) def resolve_entity_via_trends(entity: str) -> dict[str, Any]: """Resolve a normalized entity through trends-mcp, falling back cleanly. The input is normalized first using the same local normalization rules used everywhere else in news-mcp, so query and storage paths stay aligned. """ normalized = normalize_entity(entity) if not normalized: return { "raw": entity, "normalized": "", "canonical_label": "", "mid": None, "type": None, "source": "empty", } config = os.getenv("MCPORTER_CONFIG", os.path.expanduser("~/.openclaw/workspace/config/mcporter.json")) command = [ "mcporter", "--config", config, "call", "trends.resolve_entity", f"keyword={normalized}", ] try: proc = subprocess.run(command, capture_output=True, text=True, timeout=20, check=False) if proc.returncode == 0 and proc.stdout.strip(): payload = json.loads(proc.stdout) return { "raw": entity, "normalized": normalized, "canonical_label": payload.get("canonical_label") or normalized, "mid": payload.get("mid"), "type": payload.get("type"), "candidates": payload.get("candidates", []), "source": "trends-mcp", } except Exception: pass # Conservative fallback: keep the local normalized form and leave MID unset. return { "raw": entity, "normalized": normalized, "canonical_label": normalized, "mid": None, "type": None, "source": "fallback", }