A2A Protocol

This commit is contained in:
2026-10-21 08:23:55 -03:00
parent b08affa48c
commit 3765c3f43a
32 changed files with 2141 additions and 12 deletions

View File

@@ -0,0 +1,7 @@
FROM python:3.12-slim
WORKDIR /app
COPY libs/agent_framework /agent_framework
COPY apps/agent_gateway /app
RUN pip install --no-cache-dir -e '/agent_framework[a2a]' -r requirements.txt
ENV ENABLE_A2A=true
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8010"]

View File

@@ -71,3 +71,13 @@ O Gateway emite eventos de observabilidade:
- `IC.GLOBAL_GATEWAY_COMPLETED`
- `NOC.005` em falhas
- `NOC.006` em conclusão HTTP
## A2A opcional
A fachada A2A 1.0, a compatibilidade 0.3 e o catálogo misto de backends são
explicados em [Integração A2A](../../docs/A2A_IMPLEMENTACAO.md).
A ativação é opcional (`ENABLE_A2A=false` por padrão).
A fachada também oferece [Tasks A2A assíncronas e streaming](../../docs/A2A_ASSINCRONO.md),
com envio imediato, consulta, reconexão e cancelamento. O template de backend
pode publicar o mesmo protocolo diretamente.

View File

@@ -219,6 +219,10 @@ import asyncio
@app.get("/gateway/events/{session_id:path}")
async def gateway_events(session_id: str):
existing = await session_store.get(session_id)
if existing and existing.active_backend and registry.get(existing.active_backend).protocol == "a2a":
raise HTTPException(status_code=409, detail="A2A backend: use message/send; native event proxy is unavailable")
async def stream():
yield (
"event: connected\n"
@@ -250,6 +254,9 @@ async def gateway_events(session_id: str):
return
backend = registry.get(backend_id)
if backend.protocol == "a2a":
yield 'event: error\ndata: {"message":"Native event proxy is unavailable for A2A backends"}\n\n'
return
backend_base_url = (
getattr(backend, "base_url", None)
@@ -325,4 +332,17 @@ async def gateway_events(session_id: str):
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
},
)
)
# Opt-in: existing deployments do not import or require the A2A SDK.
if settings.ENABLE_A2A:
from agent_framework.a2a.server import install_a2a
async def _a2a_gateway_handler(data: dict):
return await gateway_message(GatewayRequest(**data))
install_a2a(
app, _a2a_gateway_handler, registry, settings.A2A_PUBLIC_URL,
settings.A2A_SECURITY_SCHEMES or None, settings.A2A_SECURITY or None,
task_mode=settings.A2A_TASK_MODE, timeout_seconds=settings.A2A_TASK_TIMEOUT_SECONDS,
)

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
from functools import lru_cache
from typing import Literal
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
@@ -16,6 +17,13 @@ class GatewaySettings(BaseSettings):
API_PORT: int = 8010
CORS_ORIGINS: str = "http://localhost:5173"
ENABLE_A2A: bool = False
A2A_TASK_MODE: bool = True
A2A_TASK_TIMEOUT_SECONDS: float = Field(default=120.0, gt=0)
A2A_PUBLIC_URL: str = "http://localhost:8010"
A2A_SECURITY_SCHEMES: dict = Field(default_factory=dict)
A2A_SECURITY: list[dict[str, list[str]]] = Field(default_factory=list)
BACKENDS_CONFIG_PATH: str = "./config/backends.yaml"
GLOBAL_ROUTING_MODE: Literal["router", "supervisor", "hybrid"] = "hybrid"
GLOBAL_KEEP_ACTIVE_BACKEND: bool = True

View File

@@ -0,0 +1,27 @@
#default_backend: native
#backends:
# native:
# url: http://localhost:8000
# description: Agente nativo do framework
# keywords: [conta, fatura]
# # protocol omitido equivale a native
# external:
# protocol: a2a
# a2a_version: '1.0' # ou '0.3'
# url: https://external-agent.example
# a2a_rpc_path: /a2a # endpoint definido pelo operador a partir do Agent Card
# a2a_agent_card_path: /.well-known/agent-card.json
# description: Agente externo A2A
# keywords: [suporte]
# a2a_headers_env:
# Authorization: EXTERNAL_AGENT_AUTHORIZATION # valor completo: Bearer <token>
default_backend: agent_template
backends:
agent_template:
protocol: a2a
a2a_version: "1.0"
url: http://localhost:8000
a2a_rpc_path: /a2a
a2a_agent_card_path: /.well-known/agent-card.json