feat(api): scaffolding FastAPI con TraceIdMiddleware, /health y routers stub
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
"""Router stub de /agents — se implementa en una task posterior."""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Router stub de /executions — se implementa en una task posterior."""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""Middleware de propagación de `trace_id` en cada request HTTP."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
|
||||||
|
from agentforge_core.observability.logging import bind_trace_id, clear_trace_id
|
||||||
|
|
||||||
|
|
||||||
|
class TraceIdMiddleware(BaseHTTPMiddleware):
|
||||||
|
"""Lee o genera `X-Trace-Id`, lo bind-ea al contexto de structlog y lo devuelve en la respuesta."""
|
||||||
|
|
||||||
|
async def dispatch(
|
||||||
|
self, request: Request, call_next: RequestResponseEndpoint
|
||||||
|
) -> Response:
|
||||||
|
trace_id = request.headers.get("X-Trace-Id") or str(uuid4())
|
||||||
|
bind_trace_id(trace_id)
|
||||||
|
try:
|
||||||
|
response = await call_next(request)
|
||||||
|
response.headers["X-Trace-Id"] = trace_id
|
||||||
|
return response
|
||||||
|
finally:
|
||||||
|
clear_trace_id()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Router stub de /policies — se implementa en una task posterior."""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"""Router stub de /violations — se implementa en una task posterior."""
|
||||||
|
|
||||||
|
from fastapi import APIRouter
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
"""Composición raíz de la aplicación FastAPI (agentforge-core)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from fastapi import FastAPI
|
||||||
|
|
||||||
|
from agentforge_core.api.middlewares import TraceIdMiddleware
|
||||||
|
from agentforge_core.config import Settings
|
||||||
|
from agentforge_core.observability.logging import configure_logging
|
||||||
|
|
||||||
|
|
||||||
|
def create_app() -> FastAPI:
|
||||||
|
"""Construye la app: logging, middleware de trace_id, /health y routers."""
|
||||||
|
settings = Settings()
|
||||||
|
configure_logging(level=settings.log_level)
|
||||||
|
app = FastAPI(
|
||||||
|
title="AgentForge Core",
|
||||||
|
version="0.1.0",
|
||||||
|
description="Plataforma de gobernanza de agentes IA — API REST.",
|
||||||
|
)
|
||||||
|
app.add_middleware(TraceIdMiddleware)
|
||||||
|
|
||||||
|
@app.get("/health", tags=["meta"])
|
||||||
|
async def health() -> dict[str, str]:
|
||||||
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
from agentforge_core.api import agents, executions, policies, violations
|
||||||
|
|
||||||
|
app.include_router(agents.router, prefix="/agents", tags=["agents"])
|
||||||
|
app.include_router(executions.router, prefix="/executions", tags=["executions"])
|
||||||
|
app.include_router(policies.router, prefix="/policies", tags=["policies"])
|
||||||
|
app.include_router(violations.router, prefix="/violations", tags=["violations"])
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
app = create_app()
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
"""Tests del endpoint /health y del TraceIdMiddleware."""
|
||||||
|
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from agentforge_core.main import create_app
|
||||||
|
|
||||||
|
|
||||||
|
def test_health_responde_ok() -> None:
|
||||||
|
client = TestClient(create_app())
|
||||||
|
r = client.get("/health")
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json() == {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_trace_id_header_se_devuelve_generado() -> None:
|
||||||
|
client = TestClient(create_app())
|
||||||
|
r = client.get("/health")
|
||||||
|
assert r.headers.get("x-trace-id")
|
||||||
|
|
||||||
|
|
||||||
|
def test_trace_id_header_se_propaga_si_viene_en_la_request() -> None:
|
||||||
|
client = TestClient(create_app())
|
||||||
|
r = client.get("/health", headers={"X-Trace-Id": "fixed-123"})
|
||||||
|
assert r.headers["x-trace-id"] == "fixed-123"
|
||||||
Reference in New Issue
Block a user