from django.http import JsonResponse from django.views.decorators.http import require_GET try: from pxy_contracts.version import SPEC_VERSION except Exception: SPEC_VERSION = '0.1.0' @require_GET def agents_list(request): base = request.build_absolute_uri('/')[:-1] # absolute base, no trailing slash agents = [ { 'agent': 'sami', 'name': 'SAMI-Agent', 'version': '1.0.0', 'spec_version': SPEC_VERSION, 'contracts_url': f'{base}/api/contracts/sami.json', 'execute_url': f'{base}/api/agents/execute', 'description': 'Urban scaling (β,R²) + SAMI residuals + chart', }, { 'agent': 'sites', 'name': 'Sites-Agent', 'version': '1.0.0', 'spec_version': SPEC_VERSION, 'contracts_url': f'{base}/api/contracts/sites.json', 'execute_url': f'{base}/api/agents/execute', 'description': 'Site scoring (access, demand, competition) with maps', }, ] return JsonResponse({'agents': agents}) import json import requests from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from django.http import JsonResponse from django.conf import settings # Internal base where your existing APIs live (same host in MVP) AGENTS_INTERNAL_BASE = getattr(settings, "AGENTS_INTERNAL_BASE", "") # empty = use same host via relative path @csrf_exempt @require_POST def agents_execute(request): """ POST /api/agents/execute Body: { "agent": "sami"|"sites", "payload": {...} } Proxies to: /api/sami/run or /api/sites/search """ try: body = json.loads(request.body.decode("utf-8")) agent = (body.get("agent") or "").strip().lower() payload = body.get("payload") if agent not in ("sami", "sites"): return JsonResponse( {"code": "AGENT_NOT_FOUND", "message": f"unknown agent '{agent}'"}, status=404, ) if payload is None: return JsonResponse( {"code": "BAD_REQUEST", "message": "missing 'payload'"}, status=400, ) # Resolve proxy target if agent == "sami": path = "/api/sami/run" else: path = "/api/sites/search" url = (AGENTS_INTERNAL_BASE or "").rstrip("/") + path # Absolute self-call (same container) using the request host if not url.startswith("http"): base = request.build_absolute_uri("/")[:-1] url = f"{base}{path}" r = requests.post(url, json=payload, timeout=90) # Pass through JSON and status code return JsonResponse(r.json(), status=r.status_code, safe=False) except requests.Timeout: return JsonResponse( {"code": "UPSTREAM_TIMEOUT", "message": "agent upstream timed out"}, status=504, ) except ValueError as ve: return JsonResponse( {"code": "BAD_JSON", "message": str(ve)}, status=400, ) except Exception as e: return JsonResponse( {"code": "AGENT_EXEC_ERROR", "message": str(e)}, status=500, )