22# SPDX-License-Identifier: Apache-2.0
33
44from typing import Annotated
5- from urllib .parse import urljoin , urlparse
5+ from urllib .parse import urljoin
66from uuid import UUID
77
88import fastapi
99import fastapi .responses
10- from a2a .types import AgentCard , TransportProtocol
10+ from a2a .server .apps import A2AFastAPIApplication
11+ from a2a .server .apps .rest .rest_adapter import RESTAdapter
12+ from a2a .types import AgentCard , AgentInterface , TransportProtocol
1113from a2a .utils import AGENT_CARD_WELL_KNOWN_PATH
12- from fastapi import Depends , Request
14+ from fastapi import Depends , HTTPException , Request
1315
1416from beeai_server .api .dependencies import (
1517 A2AProxyServiceDependency ,
1921from beeai_server .domain .models .permissions import AuthorizedUser
2022from beeai_server .service_layer .services .a2a import A2AServerResponse
2123
22- _SUPPORTED_TRANSPORTS = [TransportProtocol .jsonrpc , TransportProtocol .http_json ]
23-
24-
2524router = fastapi .APIRouter ()
2625
2726
28- def _create_proxy_url (url : str , * , proxy_base : str ) -> str :
29- return urljoin (proxy_base , urlparse (url ).path .lstrip ("/" ))
30-
31-
3227def create_proxy_agent_card (agent_card : AgentCard , * , provider_id : UUID , request : Request ) -> AgentCard :
33- proxy_base = str (request .url_for (proxy_request .__name__ , provider_id = provider_id , path = "" ))
34- proxy_interfaces = (
35- [
36- interface .model_copy (update = {"url" : _create_proxy_url (interface .url , proxy_base = proxy_base )})
37- for interface in agent_card .additional_interfaces
38- if interface .transport in _SUPPORTED_TRANSPORTS
39- ]
40- if agent_card .additional_interfaces is not None
41- else None
28+ proxy_base = str (request .url_for (a2a_proxy_jsonrpc_transport .__name__ , provider_id = provider_id ))
29+ return agent_card .model_copy (
30+ update = {
31+ "preferred_transport" : TransportProtocol .jsonrpc ,
32+ "url" : proxy_base ,
33+ "additional_interfaces" : [
34+ AgentInterface (transport = TransportProtocol .http_json , url = urljoin (proxy_base , "http" )),
35+ AgentInterface (transport = TransportProtocol .jsonrpc , url = proxy_base ),
36+ ],
37+ }
4238 )
43- if agent_card .preferred_transport in _SUPPORTED_TRANSPORTS :
44- return agent_card .model_copy (
45- update = {
46- "url" : _create_proxy_url (agent_card .url , proxy_base = proxy_base ),
47- "additional_interfaces" : proxy_interfaces ,
48- }
49- )
50- elif proxy_interfaces :
51- interface = proxy_interfaces [0 ]
52- return agent_card .model_copy (
53- update = {
54- "url" : interface .url ,
55- "preferred_transport" : interface .transport ,
56- "additional_interfaces" : proxy_interfaces ,
57- }
58- )
59- else :
60- raise RuntimeError ("Provider doesn't have any transport supported by the proxy." )
6139
6240
6341def _to_fastapi (response : A2AServerResponse ):
@@ -79,15 +57,45 @@ async def get_agent_card(
7957 return create_proxy_agent_card (provider .agent_card , provider_id = provider .id , request = request )
8058
8159
82- @router .api_route ("/{provider_id}" , methods = ["GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "HEAD" , "OPTIONS" ])
83- @router .api_route ("/{provider_id}/{path:path}" , methods = ["GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "HEAD" , "OPTIONS" ])
84- async def proxy_request (
60+ @router .post ("/{provider_id}" )
61+ @router .post ("/{provider_id}/" )
62+ async def a2a_proxy_jsonrpc_transport (
63+ provider_id : UUID ,
64+ request : fastapi .requests .Request ,
65+ a2a_proxy : A2AProxyServiceDependency ,
66+ provider_service : ProviderServiceDependency ,
67+ user : Annotated [AuthorizedUser , Depends (RequiresPermissions (a2a_proxy = {"*" }))],
68+ ):
69+ provider = await provider_service .get_provider (provider_id = provider_id )
70+ agent_card = create_proxy_agent_card (provider .agent_card , provider_id = provider .id , request = request )
71+
72+ handler = await a2a_proxy .get_request_handler (provider = provider , user = user .user )
73+ app = A2AFastAPIApplication (agent_card = agent_card , http_handler = handler )
74+ return await app ._handle_requests (request )
75+
76+
77+ @router .api_route ("/{provider_id}/http" , methods = ["GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "HEAD" , "OPTIONS" ])
78+ @router .api_route (
79+ "/{provider_id}/http/{path:path}" , methods = ["GET" , "POST" , "PUT" , "DELETE" , "PATCH" , "HEAD" , "OPTIONS" ]
80+ )
81+ async def a2a_proxy_http_transport (
8582 provider_id : UUID ,
8683 request : fastapi .requests .Request ,
8784 a2a_proxy : A2AProxyServiceDependency ,
88- _ : Annotated [AuthorizedUser , Depends (RequiresPermissions (a2a_proxy = {"*" }))],
85+ provider_service : ProviderServiceDependency ,
86+ user : Annotated [AuthorizedUser , Depends (RequiresPermissions (a2a_proxy = {"*" }))],
8987 path : str = "" ,
9088):
91- client = await a2a_proxy .get_proxy_client (provider_id = provider_id )
92- response = await client .send_request (method = request .method , url = f"/{ path } " , content = request .stream ())
93- return _to_fastapi (response )
89+ provider = await provider_service .get_provider (provider_id = provider_id )
90+ agent_card = create_proxy_agent_card (provider .agent_card , provider_id = provider .id , request = request )
91+
92+ handler = await a2a_proxy .get_request_handler (provider = provider , user = user .user )
93+ adapter = RESTAdapter (agent_card = agent_card , http_handler = handler )
94+
95+ if not (handler := adapter .routes ().get ((f"/{ path .rstrip ('/' )} " , request .method ), None )):
96+ raise HTTPException (status_code = 404 , detail = "Not found" )
97+
98+ return await handler (request )
99+
100+
101+ # TODO: extra a2a routes are not supported
0 commit comments