Making formatter more intelligen
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
097d0861c4
commit
e29b29bf0a
@ -1,84 +1,117 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations #
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
|
from typing import Any, Dict, Tuple
|
||||||
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
|
||||||
|
|
||||||
def _post_json(url: str, payload: dict, timeout: float = 60.0) -> dict:
|
# ---------- helpers ----------
|
||||||
r = requests.post(url, json=payload, timeout=timeout)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
def _base(request: HttpRequest) -> str:
|
def _base(request: HttpRequest) -> str:
|
||||||
return request.build_absolute_uri("/")[:-1]
|
return request.build_absolute_uri("/")[:-1]
|
||||||
|
|
||||||
|
def _load_body(request: HttpRequest) -> Dict[str, Any]:
|
||||||
|
try:
|
||||||
|
raw = (request.body or b"").decode("utf-8")
|
||||||
|
return json.loads(raw or "{}")
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def _extract_payload(body: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
||||||
|
"""
|
||||||
|
Returns (payload, src) where src is 'payload', 'args_raw', or 'empty'.
|
||||||
|
Accepts:
|
||||||
|
1) {"payload": {...}}
|
||||||
|
2) full canonical envelope with .input.args_raw="/sami {...}"
|
||||||
|
"""
|
||||||
|
# 1) direct payload
|
||||||
|
if isinstance(body.get("payload"), dict):
|
||||||
|
return body["payload"], "payload"
|
||||||
|
|
||||||
|
# 2) canonical envelope: parse JSON after the command in args_raw
|
||||||
|
args_raw = (body.get("input", {}) or {}).get("args_raw") or ""
|
||||||
|
# strip leading "/sami " or "/sites "
|
||||||
|
cleaned = re.sub(r"^/\w+\s*", "", args_raw).strip()
|
||||||
|
if cleaned:
|
||||||
|
try:
|
||||||
|
return json.loads(cleaned), "args_raw"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return {}, "empty"
|
||||||
|
|
||||||
|
def _post_execute(request: HttpRequest, agent: str, payload: Dict[str, Any], timeout: float = 30.0):
|
||||||
|
url = f"{_base(request)}/api/agents/execute"
|
||||||
|
try:
|
||||||
|
r = requests.post(url, json={"agent": agent, "payload": payload}, timeout=timeout)
|
||||||
|
# try parse json regardless of status
|
||||||
|
try:
|
||||||
|
data = r.json()
|
||||||
|
except Exception:
|
||||||
|
data = {"code": "NON_JSON", "message": r.text[:2000]}
|
||||||
|
return r.status_code, data
|
||||||
|
except requests.Timeout:
|
||||||
|
return 504, {"code": "UPSTREAM_TIMEOUT", "message": "agent upstream timed out"}
|
||||||
|
except Exception as e:
|
||||||
|
return 500, {"code": "EXEC_ERROR", "message": str(e)}
|
||||||
|
|
||||||
|
# ---------- text builders ----------
|
||||||
|
def _text_sami(data: Dict[str, Any]) -> str:
|
||||||
|
if "beta" in data and "r2" in data:
|
||||||
|
lines = [f"SAMI run: β={data['beta']:.3f}, R²={data['r2']:.3f}"]
|
||||||
|
resid = data.get("residuals") or []
|
||||||
|
top = sorted(resid, key=lambda x: x.get("rank", 1e9))[:3]
|
||||||
|
for c in top:
|
||||||
|
lines.append(f"{c.get('rank')}. {c.get('city')}: {c.get('sami',0):+0.2f}")
|
||||||
|
if data.get("share_url"):
|
||||||
|
lines += ["", data["share_url"]]
|
||||||
|
return "\n".join(lines)
|
||||||
|
if data.get("code"):
|
||||||
|
return f"⚠️ {data.get('code')}: {data.get('message','')}"
|
||||||
|
return "SAMI results ready."
|
||||||
|
|
||||||
|
def _text_sites(data: Dict[str, Any]) -> str:
|
||||||
|
if isinstance(data.get("candidates"), list):
|
||||||
|
city = data.get("city", "?")
|
||||||
|
business = data.get("business", "?")
|
||||||
|
lines = [f"Top sites for {business} in {city}:"]
|
||||||
|
for i, c in enumerate(data["candidates"][:3], 1):
|
||||||
|
lat = c.get("lat", 0); lon = c.get("lon", 0); sc = c.get("score", 0)
|
||||||
|
lines.append(f"{i}. score={sc:.2f} @ ({lat:.5f},{lon:.5f})")
|
||||||
|
for k in ("share_url", "isochrones_geojson_url", "candidates_geojson_url"):
|
||||||
|
if data.get(k): lines.append(data[k])
|
||||||
|
return "\n".join(lines)
|
||||||
|
if data.get("code"):
|
||||||
|
return f"⚠️ {data.get('code')}: {data.get('message','')}"
|
||||||
|
return "Site scoring ready."
|
||||||
|
|
||||||
|
# ---------- views ----------
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_http_methods(["GET", "POST"])
|
||||||
def format_sami(request: HttpRequest):
|
def format_sami(request: HttpRequest):
|
||||||
"""
|
body = _load_body(request)
|
||||||
Body: {"payload": {...}} where payload matches SAMI RunRequest.
|
payload, src = _extract_payload(body)
|
||||||
Calls /api/agents/execute and returns {"text": "...", ...original...}
|
status, data = _post_execute(request, "sami", payload, timeout=30.0)
|
||||||
"""
|
# add echo + text
|
||||||
|
data = data if isinstance(data, dict) else {"result": data}
|
||||||
|
data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())})
|
||||||
try:
|
try:
|
||||||
body = json.loads(request.body.decode("utf-8") or "{}")
|
data["text"] = _text_sami(data)
|
||||||
except Exception:
|
|
||||||
body = {}
|
|
||||||
payload = body.get("payload") or {}
|
|
||||||
|
|
||||||
data = _post_json(f"{_base(request)}/api/agents/execute",
|
|
||||||
{"agent": "sami", "payload": payload})
|
|
||||||
|
|
||||||
# Build text
|
|
||||||
try:
|
|
||||||
beta = data.get("beta"); r2 = data.get("r2")
|
|
||||||
resid = data.get("residuals") or []
|
|
||||||
top = sorted(resid, key=lambda x: x.get("rank", 1e9))[:3]
|
|
||||||
lines = []
|
|
||||||
if beta is not None and r2 is not None:
|
|
||||||
lines.append(f"SAMI run: β={beta:.3f}, R²={r2:.3f}")
|
|
||||||
for c in top:
|
|
||||||
lines.append(f"{c.get('rank')}. {c.get('city')}: {c.get('sami', 0):+0.2f}")
|
|
||||||
if data.get("share_url"):
|
|
||||||
lines.append("")
|
|
||||||
lines.append(data["share_url"])
|
|
||||||
text = "\n".join(lines) if lines else "SAMI results ready."
|
|
||||||
data["text"] = text
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
return JsonResponse(data, status=status, safe=False)
|
||||||
return JsonResponse(data, safe=False)
|
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
@require_http_methods(["GET", "POST"])
|
@require_http_methods(["GET", "POST"])
|
||||||
def format_sites(request: HttpRequest):
|
def format_sites(request: HttpRequest):
|
||||||
"""
|
body = _load_body(request)
|
||||||
Body: {"payload": {...}} where payload matches SiteSearchRequest.
|
payload, src = _extract_payload(body)
|
||||||
Calls /api/agents/execute and returns {"text": "...", ...original...}
|
status, data = _post_execute(request, "sites", payload, timeout=30.0)
|
||||||
"""
|
data = data if isinstance(data, dict) else {"result": data}
|
||||||
|
data.setdefault("_echo", {"src": src, "payload_keys": list(payload.keys())})
|
||||||
try:
|
try:
|
||||||
body = json.loads(request.body.decode("utf-8") or "{}")
|
data["text"] = _text_sites(data)
|
||||||
except Exception:
|
|
||||||
body = {}
|
|
||||||
payload = body.get("payload") or {}
|
|
||||||
|
|
||||||
data = _post_json(f"{_base(request)}/api/agents/execute",
|
|
||||||
{"agent": "sites", "payload": payload})
|
|
||||||
|
|
||||||
# Build text
|
|
||||||
try:
|
|
||||||
city = data.get("city"); business = data.get("business")
|
|
||||||
cands = data.get("candidates") or []
|
|
||||||
top = cands[:3]
|
|
||||||
lines = [f"Top sites for {business} in {city}:"]
|
|
||||||
for i, c in enumerate(top, 1):
|
|
||||||
lines.append(f"{i}. score={c.get('score',0):.2f} @ ({c.get('lat',0):.5f},{c.get('lon',0):.5f})")
|
|
||||||
# include useful links if present
|
|
||||||
for k in ("share_url", "isochrones_geojson_url", "candidates_geojson_url"):
|
|
||||||
if data.get(k):
|
|
||||||
lines.append(data[k])
|
|
||||||
data["text"] = "\n".join(lines)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
return JsonResponse(data, status=status, safe=False)
|
||||||
return JsonResponse(data, safe=False)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user