From 3204053681a83ecee6d2e7bc2bf1e1200cb02636 Mon Sep 17 00:00:00 2001 From: Ekaropolus Date: Thu, 18 Sep 2025 14:41:55 -0600 Subject: [PATCH] agent list with post --- pxy_agents_coral/views.py | 106 +++++++++++++++++++++----------------- 1 file changed, 58 insertions(+), 48 deletions(-) diff --git a/pxy_agents_coral/views.py b/pxy_agents_coral/views.py index 138ad00..935d978 100644 --- a/pxy_agents_coral/views.py +++ b/pxy_agents_coral/views.py @@ -1,46 +1,56 @@ -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}) +# polisplexity/pxy_agents_coral/views.py +from __future__ import annotations 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 +from django.http import JsonResponse +from django.views.decorators.csrf import csrf_exempt +from django.views.decorators.http import require_http_methods, require_POST + +# Contracts spec version (best-effort import) +try: + from pxy_contracts.version import SPEC_VERSION +except Exception: + SPEC_VERSION = "0.1.0" + +# Where to call your internal APIs from inside the container. +# In prod you likely want: "http://127.0.0.1:8002" +AGENTS_INTERNAL_BASE = getattr(settings, "AGENTS_INTERNAL_BASE", "") + + +@csrf_exempt +@require_http_methods(["GET", "POST"]) # accept both because the bot posts by default +def agents_list(request): + """ + List available agents (SAMI, Sites) with their contracts & execute endpoints. + Accepts GET and POST (POST to support simple bot routers that only POST). + """ + 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}) + @csrf_exempt @require_POST @@ -51,9 +61,10 @@ def agents_execute(request): Proxies to: /api/sami/run or /api/sites/search """ try: - body = json.loads(request.body.decode("utf-8")) + body = json.loads(request.body.decode("utf-8") or "{}") 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}'"}, @@ -66,19 +77,19 @@ def agents_execute(request): ) # Resolve proxy target - if agent == "sami": - path = "/api/sami/run" - else: - path = "/api/sites/search" + path = "/api/sami/run" if agent == "sami" else "/api/sites/search" + # Prefer explicit internal base; otherwise same-host absolute URL 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}" + # Proxy r = requests.post(url, json=payload, timeout=90) - # Pass through JSON and status code + + # Pass through JSON (may be list/dict) & status code + # Use safe=False because upstream could return a list return JsonResponse(r.json(), status=r.status_code, safe=False) except requests.Timeout: @@ -87,11 +98,10 @@ def agents_execute(request): status=504, ) except ValueError as ve: - return JsonResponse( - {"code": "BAD_JSON", "message": str(ve)}, - status=400, - ) + # Bad JSON in request or from upstream + return JsonResponse({"code": "BAD_JSON", "message": str(ve)}, status=400) except Exception as e: + # Last-resort error envelope return JsonResponse( {"code": "AGENT_EXEC_ERROR", "message": str(e)}, status=500,