Internet of Agent Initial part
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
db61f56a24
commit
d4b56e1a2c
4
.gitignore
vendored
4
.gitignore
vendored
@ -32,3 +32,7 @@ docker-compose.override.yml
|
|||||||
docker-compose.override.yml
|
docker-compose.override.yml
|
||||||
pxy_meta_pages.zip
|
pxy_meta_pages.zip
|
||||||
pxy_openai.zip
|
pxy_openai.zip
|
||||||
|
pxy_bots.zip
|
||||||
|
pxy_bots (2).zip
|
||||||
|
pxy_bots.zip
|
||||||
|
pxy_bots.zip
|
||||||
|
@ -59,6 +59,7 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
"pxy_api",
|
"pxy_api",
|
||||||
|
'pxy_agents_coral',
|
||||||
|
|
||||||
|
|
||||||
# Third-party apps
|
# Third-party apps
|
||||||
@ -215,3 +216,5 @@ REST_FRAMEWORK = {
|
|||||||
# Manejo de errores uniforme
|
# Manejo de errores uniforme
|
||||||
"EXCEPTION_HANDLER": "pxy_api.exceptions.envelope_exception_handler",
|
"EXCEPTION_HANDLER": "pxy_api.exceptions.envelope_exception_handler",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AGENTS_INTERNAL_BASE = "http://127.0.0.1:8000"
|
||||||
|
@ -50,6 +50,10 @@ urlpatterns = [
|
|||||||
|
|
||||||
path("", include("pxy_openai.urls")),
|
path("", include("pxy_openai.urls")),
|
||||||
|
|
||||||
|
path("", include("pxy_contracts.urls")),
|
||||||
|
|
||||||
|
path('', include('pxy_agents_coral.urls')),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
0
pxy_agents_coral/__init__.py
Normal file
0
pxy_agents_coral/__init__.py
Normal file
3
pxy_agents_coral/admin.py
Normal file
3
pxy_agents_coral/admin.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
6
pxy_agents_coral/apps.py
Normal file
6
pxy_agents_coral/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class PxyAgentsCoralConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'pxy_agents_coral'
|
0
pxy_agents_coral/migrations/__init__.py
Normal file
0
pxy_agents_coral/migrations/__init__.py
Normal file
3
pxy_agents_coral/models.py
Normal file
3
pxy_agents_coral/models.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.db import models
|
||||||
|
|
||||||
|
# Create your models here.
|
3
pxy_agents_coral/tests.py
Normal file
3
pxy_agents_coral/tests.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
7
pxy_agents_coral/urls.py
Normal file
7
pxy_agents_coral/urls.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/agents/list', views.agents_list, name='agents_list'),
|
||||||
|
path('api/agents/execute', views.agents_execute, name='agents_execute'),
|
||||||
|
]
|
98
pxy_agents_coral/views.py
Normal file
98
pxy_agents_coral/views.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
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,
|
||||||
|
)
|
BIN
pxy_bots (2).zip
BIN
pxy_bots (2).zip
Binary file not shown.
BIN
pxy_bots.zip
BIN
pxy_bots.zip
Binary file not shown.
7
pxy_contracts/urls.py
Normal file
7
pxy_contracts/urls.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("api/contracts/sami.json", views.sami_contracts, name="contracts_sami"),
|
||||||
|
path("api/contracts/sites.json", views.sites_contracts, name="contracts_sites"),
|
||||||
|
]
|
@ -1,3 +1,40 @@
|
|||||||
from django.shortcuts import render
|
from django.http import JsonResponse
|
||||||
|
from django.views.decorators.http import require_GET
|
||||||
|
|
||||||
# Create your views here.
|
# Versión del contrato
|
||||||
|
try:
|
||||||
|
from .version import SPEC_VERSION
|
||||||
|
except Exception:
|
||||||
|
SPEC_VERSION = "0.1.0"
|
||||||
|
|
||||||
|
# Modelos Pydantic
|
||||||
|
from .contracts.sami import SAMIRunRequest, SAMIRunResponse
|
||||||
|
from .contracts.sites import SiteSearchRequest, SiteSearchResponse
|
||||||
|
|
||||||
|
|
||||||
|
def _schema_of(model_cls):
|
||||||
|
"""Devuelve JSONSchema para Pydantic v2 o v1."""
|
||||||
|
try:
|
||||||
|
return model_cls.model_json_schema() # Pydantic v2
|
||||||
|
except Exception:
|
||||||
|
return model_cls.schema() # Pydantic v1
|
||||||
|
|
||||||
|
|
||||||
|
@require_GET
|
||||||
|
def sami_contracts(request):
|
||||||
|
"""GET /api/contracts/sami.json"""
|
||||||
|
return JsonResponse({
|
||||||
|
"spec_version": SPEC_VERSION,
|
||||||
|
"request": _schema_of(SAMIRunRequest),
|
||||||
|
"response": _schema_of(SAMIRunResponse),
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@require_GET
|
||||||
|
def sites_contracts(request):
|
||||||
|
"""GET /api/contracts/sites.json"""
|
||||||
|
return JsonResponse({
|
||||||
|
"spec_version": SPEC_VERSION,
|
||||||
|
"request": _schema_of(SiteSearchRequest),
|
||||||
|
"response": _schema_of(SiteSearchResponse),
|
||||||
|
})
|
||||||
|
@ -113,8 +113,16 @@ EXEMPT_URLS += [
|
|||||||
re.compile(r"^api/openai/voice_chat$"),
|
re.compile(r"^api/openai/voice_chat$"),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Contracts (public for interop/coral tooling)
|
||||||
|
EXEMPT_URLS += [
|
||||||
|
re.compile(r"^api/contracts/(sami|sites)\.json$"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Coral-style agents catalog & execute (public for MVP)
|
||||||
|
EXEMPT_URLS += [
|
||||||
|
re.compile(r"^api/agents/list$"),
|
||||||
|
re.compile(r"^api/agents/execute$"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user