"""Klient HTTP do warstwy logicznej. Jedyny punkt styku prezentacji w dół. Przekazuje dane z formularza i odbiera opracowane wyniki. Prezentacja nie sięga bezpośrednio do bazy ani do silnika. """ from __future__ import annotations import os from typing import Any import httpx from app.config import settings def _auth_headers() -> dict[str, str]: """Token międzywarstwowy (LOG-32) — pusty, gdy ochrona wyłączona.""" token = os.getenv("INTERNAL_TOKEN", "") return {"X-Astrololo-Token": token} if token else {} class LogicClient: def __init__(self, base_url: str | None = None) -> None: self.base_url = (base_url or settings.logic_url).rstrip("/") def query(self, query: str, field: str, exact: bool, limit: int) -> dict[str, Any]: payload = {"query": query, "field": field, "exact": exact, "limit": limit} with httpx.Client(timeout=settings.http_timeout) as client: r = client.post(f"{self.base_url}/api/query", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json() def positions( self, when_utc_iso: str, lat: float, lon: float, objects: list[str] | None = None, house_system: str = "whole_sign", stations: bool = False, zodiac: str = "tropical", ) -> dict[str, Any]: """Pełny horoskop dla danego momentu — woła logic /chart/positions.""" payload = { "when_utc": when_utc_iso, "lat": lat, "lon": lon, "objects": objects, "house_system": house_system, "stations": stations, "zodiac": zodiac, } # stacje wymagają root-findów — dłuższy timeout with httpx.Client(timeout=max(settings.http_timeout, 60.0) if stations else settings.http_timeout) as client: r = client.post(f"{self.base_url}/chart/positions", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json() def report( self, when_utc_iso: str, lat: float, lon: float, limit: int = 5000, group: bool = False ) -> dict[str, Any]: """Sygnifikatory z obliczeń szukane w bazie — woła logic /chart/report.""" payload = {"when_utc": when_utc_iso, "lat": lat, "lon": lon, "limit": limit, "group": group} with httpx.Client(timeout=max(settings.http_timeout, 30.0)) as client: r = client.post(f"{self.base_url}/chart/report", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json() def prompt( self, profile: str, when_utc_iso: str, lat: float, lon: float, budget: str = "medium", from_date: str | None = None, to_date: str | None = None, ) -> dict[str, Any]: """Gotowy prompt do LLM z wyliczeń (LOG-29/30) — woła logic /chart/prompt.""" payload: dict[str, Any] = { "profile": profile, "when_utc": when_utc_iso, "lat": lat, "lon": lon, "budget": budget, } if from_date and to_date: payload["from_date"], payload["to_date"] = from_date, to_date with httpx.Client(timeout=max(settings.http_timeout, 60.0)) as client: r = client.post(f"{self.base_url}/chart/prompt", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json() def horoscope( self, profile: str, when_utc_iso: str, lat: float, lon: float, budget: str = "medium", provider: str | None = None, from_date: str | None = None, to_date: str | None = None, ) -> dict[str, Any]: """Napisany horoskop (LOG-31) — woła logic /chart/horoscope. Generowanie tekstu trwa (zwłaszcza na modelu lokalnym), stąd długi timeout. """ payload: dict[str, Any] = { "profile": profile, "when_utc": when_utc_iso, "lat": lat, "lon": lon, "budget": budget, } if provider: payload["provider"] = provider if from_date and to_date: payload["from_date"], payload["to_date"] = from_date, to_date with httpx.Client(timeout=max(settings.http_timeout, 300.0)) as client: r = client.post(f"{self.base_url}/chart/horoscope", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json() def timeline( self, when_utc_iso: str, lat: float, lon: float, from_date: str, to_date: str, interpret: bool = True, ) -> dict[str, Any]: """Oś czasu z technik (+interpretacje z bazy) — woła logic /chart/timeline.""" payload = { "when_utc": when_utc_iso, "lat": lat, "lon": lon, "from_date": from_date, "to_date": to_date, "interpret": interpret, } with httpx.Client(timeout=max(settings.http_timeout, 60.0)) as client: r = client.post(f"{self.base_url}/chart/timeline", json=payload, headers=_auth_headers()) r.raise_for_status() return r.json()