"""지역명으로 맛집 후보 이름을 찾는다.""" import json from common.logger import LOG from services.llm.perplexity import DEFAULT_MAX_TOKENS, DEFAULT_MODEL, PerplexityError, call, read_usage from services.prompts.restaurant_search import RESPONSE_SCHEMA, SYSTEM_PROMPT, build_prompt MAX_RESULTS = 10 def _parse_names(payload: dict) -> list[str]: choices = payload.get("choices") or [] if not choices or not isinstance(choices[0], dict): return [] content = ((choices[0].get("message") or {}).get("content")) or "" try: doc = json.loads(content) except (json.JSONDecodeError, TypeError): LOG.w("[restaurant_discovery] 구조화 출력 파싱 실패") return [] names = doc.get("restaurants") if not isinstance(names, list): return [] return [str(n).strip() for n in names if str(n or "").strip()][:MAX_RESULTS] async def search_region_restaurants( region_label: str, *, model: str = DEFAULT_MODEL, client=None, ) -> list[str]: """지역명으로 맛집 상위 10곳의 이름만 받는다.""" if not (region_label or "").strip(): return [] body = { "model": model, "messages": [ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": build_prompt(region_label)}, ], "max_tokens": DEFAULT_MAX_TOKENS, "temperature": 0, "response_format": RESPONSE_SCHEMA, } try: payload = await call(body, client=client) except PerplexityError as ex: LOG.w(f"[restaurant_discovery] '{region_label}' 검색 실패: {ex}") return [] names = _parse_names(payload) usage = read_usage(payload) LOG.i( f"[restaurant_discovery] '{region_label}' {len(names)}곳 · " f"tokens in={usage.input_tokens} out={usage.output_tokens} · 약 ${usage.cost}" ) return names