Better formater for calling agent sites
This commit is contained in:
parent
d7ab546650
commit
caf60c967e
@ -2,7 +2,8 @@ from __future__ import annotations #
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import requests
|
import requests
|
||||||
from typing import Any, Dict, Tuple
|
from typing import Any, Dict, Tuple, Optional, List
|
||||||
|
from django.conf import settings
|
||||||
from django.http import JsonResponse, HttpRequest
|
from django.http import JsonResponse, HttpRequest
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
@ -18,6 +19,131 @@ def _load_body(request: HttpRequest) -> Dict[str, Any]:
|
|||||||
except Exception:
|
except Exception:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
_SITES_DEFAULTS = {
|
||||||
|
"city": "CDMX",
|
||||||
|
"business": "all",
|
||||||
|
"time_bands": [10, 20],
|
||||||
|
"center_by_city": {
|
||||||
|
"CDMX": (19.4326, -99.1332),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def _parse_time_bands(raw: str) -> list[int]:
|
||||||
|
bands: list[int] = []
|
||||||
|
for part in re.split(r"[,\s]+", (raw or "").strip()):
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
val = int(part)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
if val > 0:
|
||||||
|
bands.append(val)
|
||||||
|
return bands
|
||||||
|
|
||||||
|
def _parse_latlon(raw: str) -> Optional[List[float]]:
|
||||||
|
parts = [p.strip() for p in (raw or "").split(",")]
|
||||||
|
if len(parts) != 2:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
lat = float(parts[0])
|
||||||
|
lon = float(parts[1])
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
return [lat, lon]
|
||||||
|
|
||||||
|
def _parse_sites_shorthand(body: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
||||||
|
args_raw = (body.get("input", {}) or {}).get("args_raw") or ""
|
||||||
|
cleaned = re.sub(r"^/\w+\s*", "", args_raw).strip()
|
||||||
|
if not cleaned:
|
||||||
|
return {}, "empty"
|
||||||
|
|
||||||
|
payload: Dict[str, Any] = {}
|
||||||
|
time_bands: list[int] = []
|
||||||
|
lat_val = None
|
||||||
|
lon_val = None
|
||||||
|
|
||||||
|
for tok in cleaned.split():
|
||||||
|
key = None
|
||||||
|
val = None
|
||||||
|
if "=" in tok:
|
||||||
|
key, val = tok.split("=", 1)
|
||||||
|
elif ":" in tok:
|
||||||
|
key, val = tok.split(":", 1)
|
||||||
|
|
||||||
|
if key is not None:
|
||||||
|
key = key.strip().lower()
|
||||||
|
val = (val or "").strip()
|
||||||
|
if key in {"city", "c"}:
|
||||||
|
payload["city"] = val
|
||||||
|
elif key in {"business", "biz", "b", "category", "cat"}:
|
||||||
|
payload["business"] = val
|
||||||
|
elif key in {"time", "times", "band", "bands", "time_bands", "tb"}:
|
||||||
|
bands = _parse_time_bands(val)
|
||||||
|
if bands:
|
||||||
|
time_bands.extend(bands)
|
||||||
|
elif key in {"max", "max_candidates", "k", "top"}:
|
||||||
|
try:
|
||||||
|
payload["max_candidates"] = max(1, int(val))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
elif key in {"center", "ctr"}:
|
||||||
|
latlon = _parse_latlon(val)
|
||||||
|
if latlon:
|
||||||
|
payload["center"] = latlon
|
||||||
|
elif key in {"lat", "latitude"}:
|
||||||
|
try:
|
||||||
|
lat_val = float(val)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
elif key in {"lon", "lng", "longitude"}:
|
||||||
|
try:
|
||||||
|
lon_val = float(val)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
continue
|
||||||
|
|
||||||
|
if re.fullmatch(r"\d+(?:,\d+)*", tok):
|
||||||
|
bands = _parse_time_bands(tok)
|
||||||
|
if bands:
|
||||||
|
time_bands.extend(bands)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if tok.isalpha() and tok.isupper() and "city" not in payload:
|
||||||
|
payload["city"] = tok
|
||||||
|
continue
|
||||||
|
|
||||||
|
if "business" not in payload:
|
||||||
|
payload["business"] = tok
|
||||||
|
else:
|
||||||
|
payload["business"] = f"{payload['business']}_{tok}"
|
||||||
|
|
||||||
|
if time_bands:
|
||||||
|
payload["time_bands"] = time_bands
|
||||||
|
if lat_val is not None and lon_val is not None and "center" not in payload:
|
||||||
|
payload["center"] = [lat_val, lon_val]
|
||||||
|
|
||||||
|
return payload, "shorthand"
|
||||||
|
|
||||||
|
def _apply_sites_defaults(payload: Dict[str, Any], body: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
out = dict(payload or {})
|
||||||
|
loc = (body.get("input") or {}).get("location") or {}
|
||||||
|
if "center" not in out and loc.get("lat") is not None and loc.get("lon") is not None:
|
||||||
|
out["center"] = [loc.get("lat"), loc.get("lon")]
|
||||||
|
|
||||||
|
city = (out.get("city") or _SITES_DEFAULTS["city"]).strip()
|
||||||
|
out["city"] = city
|
||||||
|
if not out.get("business"):
|
||||||
|
out["business"] = _SITES_DEFAULTS["business"]
|
||||||
|
if not out.get("time_bands"):
|
||||||
|
out["time_bands"] = list(_SITES_DEFAULTS["time_bands"])
|
||||||
|
|
||||||
|
if "center" not in out:
|
||||||
|
fallback = _SITES_DEFAULTS["center_by_city"].get(city.upper())
|
||||||
|
if fallback:
|
||||||
|
out["center"] = [fallback[0], fallback[1]]
|
||||||
|
return out
|
||||||
|
|
||||||
def _extract_payload(body: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
def _extract_payload(body: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
||||||
"""
|
"""
|
||||||
Returns (payload, src) where src is 'payload', 'args_raw', or 'empty'.
|
Returns (payload, src) where src is 'payload', 'args_raw', or 'empty'.
|
||||||
@ -41,8 +167,19 @@ def _extract_payload(body: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
|||||||
|
|
||||||
return {}, "empty"
|
return {}, "empty"
|
||||||
|
|
||||||
|
def _resolve_internal_base(request: HttpRequest) -> str:
|
||||||
|
base = (getattr(settings, "AGENTS_INTERNAL_BASE", "") or "").strip()
|
||||||
|
if base:
|
||||||
|
return base.rstrip("/")
|
||||||
|
host = (request.get_host() or "").lower()
|
||||||
|
if host.endswith(":8011"):
|
||||||
|
return "http://127.0.0.1:8000"
|
||||||
|
if host.endswith(":8010"):
|
||||||
|
return "http://127.0.0.1:8002"
|
||||||
|
return "http://127.0.0.1:8002"
|
||||||
|
|
||||||
def _post_execute(request: HttpRequest, agent: str, payload: Dict[str, Any], timeout: float = 30.0):
|
def _post_execute(request: HttpRequest, agent: str, payload: Dict[str, Any], timeout: float = 30.0):
|
||||||
url = f"{_base(request)}/api/agents/execute"
|
url = f"{_resolve_internal_base(request)}/api/agents/execute"
|
||||||
try:
|
try:
|
||||||
r = requests.post(url, json={"agent": agent, "payload": payload}, timeout=timeout)
|
r = requests.post(url, json={"agent": agent, "payload": payload}, timeout=timeout)
|
||||||
# try parse json regardless of status
|
# try parse json regardless of status
|
||||||
@ -107,6 +244,9 @@ def format_sami(request: HttpRequest):
|
|||||||
def format_sites(request: HttpRequest):
|
def format_sites(request: HttpRequest):
|
||||||
body = _load_body(request)
|
body = _load_body(request)
|
||||||
payload, src = _extract_payload(body)
|
payload, src = _extract_payload(body)
|
||||||
|
if not payload and src == "empty":
|
||||||
|
payload, src = _parse_sites_shorthand(body)
|
||||||
|
payload = _apply_sites_defaults(payload, body)
|
||||||
status, data = _post_execute(request, "sites", payload, timeout=30.0)
|
status, data = _post_execute(request, "sites", payload, timeout=30.0)
|
||||||
data = data if isinstance(data, dict) else {"result": data}
|
data = data if isinstance(data, dict) else {"result": data}
|
||||||
data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())})
|
data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())})
|
||||||
|
|||||||
@ -25,6 +25,21 @@ except Exception:
|
|||||||
# For the generic /api/agents/execute proxy (kept for compatibility)
|
# For the generic /api/agents/execute proxy (kept for compatibility)
|
||||||
AGENTS_INTERNAL_BASE = getattr(settings, "AGENTS_INTERNAL_BASE", "")
|
AGENTS_INTERNAL_BASE = getattr(settings, "AGENTS_INTERNAL_BASE", "")
|
||||||
|
|
||||||
|
def _resolve_internal_base(request: HttpRequest) -> str:
|
||||||
|
base = (AGENTS_INTERNAL_BASE or "").strip()
|
||||||
|
if base:
|
||||||
|
return base.rstrip("/")
|
||||||
|
host = (request.get_host() or "").lower()
|
||||||
|
if host.endswith(":8011"):
|
||||||
|
return "http://127.0.0.1:8000"
|
||||||
|
if host.endswith(":8010"):
|
||||||
|
return "http://127.0.0.1:8002"
|
||||||
|
if host.endswith(":8000"):
|
||||||
|
return "http://127.0.0.1:8000"
|
||||||
|
if host.endswith(":8002"):
|
||||||
|
return "http://127.0.0.1:8002"
|
||||||
|
return "http://127.0.0.1:8002"
|
||||||
|
|
||||||
# For the formatter endpoints we *force* an internal base and never guess from Host.
|
# For the formatter endpoints we *force* an internal base and never guess from Host.
|
||||||
# Set in .env: AGENTS_INTERNAL_BASE=http://127.0.0.1:8002
|
# Set in .env: AGENTS_INTERNAL_BASE=http://127.0.0.1:8002
|
||||||
# Fallback keeps you safe even if env is missing/misread.
|
# Fallback keeps you safe even if env is missing/misread.
|
||||||
@ -188,7 +203,7 @@ def agents_execute(request: HttpRequest):
|
|||||||
return JsonResponse({"code": "BAD_REQUEST", "message": "missing 'payload'"}, status=400)
|
return JsonResponse({"code": "BAD_REQUEST", "message": "missing 'payload'"}, status=400)
|
||||||
|
|
||||||
path = "/api/sami/run" if agent == "sami" else "/api/sites/search"
|
path = "/api/sami/run" if agent == "sami" else "/api/sites/search"
|
||||||
base = (AGENTS_INTERNAL_BASE or "http://127.0.0.1:8002").rstrip("/")
|
base = _resolve_internal_base(request)
|
||||||
url = f"{base}{path}"
|
url = f"{base}{path}"
|
||||||
|
|
||||||
r = requests.post(url, json=payload, timeout=90)
|
r = requests.post(url, json=payload, timeout=90)
|
||||||
@ -255,4 +270,3 @@ def agents_health(request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
data["checks"]["sites"] = {"ok": False, "error": str(e)}
|
data["checks"]["sites"] = {"ok": False, "error": str(e)}
|
||||||
return JsonResponse(data)
|
return JsonResponse(data)
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user