aixtools 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aixtools might be problematic. Click here for more details.
- aixtools/_version.py +2 -2
- aixtools/a2a/app.py +20 -0
- aixtools/a2a/auth_middleware.py +46 -0
- aixtools/a2a/google_sdk/remote_agent_connection.py +1 -1
- aixtools/a2a/google_sdk/utils.py +17 -4
- aixtools/auth/auth.py +4 -2
- {aixtools-0.3.2.dist-info → aixtools-0.3.4.dist-info}/METADATA +1 -1
- {aixtools-0.3.2.dist-info → aixtools-0.3.4.dist-info}/RECORD +11 -10
- {aixtools-0.3.2.dist-info → aixtools-0.3.4.dist-info}/WHEEL +0 -0
- {aixtools-0.3.2.dist-info → aixtools-0.3.4.dist-info}/entry_points.txt +0 -0
- {aixtools-0.3.2.dist-info → aixtools-0.3.4.dist-info}/top_level.txt +0 -0
aixtools/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.3.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 3,
|
|
31
|
+
__version__ = version = '0.3.4'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 3, 4)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
aixtools/a2a/app.py
CHANGED
|
@@ -6,6 +6,9 @@ import json
|
|
|
6
6
|
from functools import partial
|
|
7
7
|
from typing import assert_never
|
|
8
8
|
|
|
9
|
+
from a2a.server.apps import A2AStarletteApplication
|
|
10
|
+
from a2a.server.request_handlers import RequestHandler
|
|
11
|
+
from a2a.types import AgentCard
|
|
9
12
|
from fasta2a.applications import FastA2A
|
|
10
13
|
from fasta2a.broker import InMemoryBroker
|
|
11
14
|
from fasta2a.schema import Part, TaskSendParams
|
|
@@ -26,6 +29,8 @@ from starlette.exceptions import HTTPException
|
|
|
26
29
|
from starlette.requests import Request
|
|
27
30
|
from starlette.responses import RedirectResponse
|
|
28
31
|
|
|
32
|
+
from aixtools.a2a.auth_middleware import AuthMiddleware
|
|
33
|
+
from aixtools.auth.auth import AccessTokenAuthProvider
|
|
29
34
|
from aixtools.context import session_id_var, user_id_var
|
|
30
35
|
|
|
31
36
|
|
|
@@ -124,3 +129,18 @@ def fix_a2a_docs_pages(app: Starlette) -> None:
|
|
|
124
129
|
|
|
125
130
|
app.router.add_route("/.well-known/agent.json", redirect_to_sub_agent, methods=["GET"])
|
|
126
131
|
app.router.add_route("/", redirect_to_sub_agent, methods=["POST"])
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def build_a2a_starlette_app(
|
|
135
|
+
public_agent_card: AgentCard, request_handler: RequestHandler, auth_provider: AccessTokenAuthProvider | None = None
|
|
136
|
+
) -> Starlette:
|
|
137
|
+
"""Builds a A2AStarletteApplication with the given auth provider."""
|
|
138
|
+
server = A2AStarletteApplication(
|
|
139
|
+
agent_card=public_agent_card,
|
|
140
|
+
http_handler=request_handler,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
app = server.build()
|
|
144
|
+
if auth_provider:
|
|
145
|
+
app.add_middleware(AuthMiddleware, provider=auth_provider)
|
|
146
|
+
return app
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"Auth module managing user authentication for A2A server"
|
|
2
|
+
|
|
3
|
+
from logging import getLogger
|
|
4
|
+
|
|
5
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
6
|
+
from starlette.requests import Request
|
|
7
|
+
|
|
8
|
+
from aixtools.auth.auth import AccessTokenAuthProvider, AuthTokenError
|
|
9
|
+
from aixtools.utils import config
|
|
10
|
+
|
|
11
|
+
logger = getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AuthMiddleware(BaseHTTPMiddleware): # pylint: disable=too-few-public-methods
|
|
15
|
+
"""
|
|
16
|
+
Middleware that enforces access token authentication for A2A route.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
AGENT_CARD_PATH_SUFFIXES = [".well-known/agent.json", ".well-known/agent-card.json"]
|
|
20
|
+
APP_DEFAULT_SCOPE = config.APP_DEFAULT_SCOPE
|
|
21
|
+
|
|
22
|
+
def __init__(self, app, provider: AccessTokenAuthProvider):
|
|
23
|
+
super().__init__(app)
|
|
24
|
+
self.provider = provider
|
|
25
|
+
|
|
26
|
+
async def dispatch(self, request: Request, call_next):
|
|
27
|
+
"""
|
|
28
|
+
Auth middleware function that checks whether the request has a valid access token.
|
|
29
|
+
|
|
30
|
+
:param request: The incoming http request
|
|
31
|
+
:param call_next: The next function in the chain
|
|
32
|
+
:raises HTTPException: If an access token isn't valid or missing
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
path = request.url.path
|
|
36
|
+
|
|
37
|
+
# allow agent cards to pass
|
|
38
|
+
if any(path.endswith(suffix) for suffix in self.AGENT_CARD_PATH_SUFFIXES):
|
|
39
|
+
return await call_next(request)
|
|
40
|
+
|
|
41
|
+
auth_header = request.headers.get("Authorization") or request.headers.get("authorization")
|
|
42
|
+
try:
|
|
43
|
+
await self.provider.verify_auth_header(auth_header)
|
|
44
|
+
return await call_next(request)
|
|
45
|
+
except AuthTokenError as e:
|
|
46
|
+
raise e.to_http_exception(self.APP_DEFAULT_SCOPE)
|
aixtools/a2a/google_sdk/utils.py
CHANGED
|
@@ -5,7 +5,7 @@ import asyncio
|
|
|
5
5
|
import httpx
|
|
6
6
|
from a2a.client import A2ACardResolver, ClientConfig, ClientFactory
|
|
7
7
|
from a2a.server.agent_execution import RequestContext
|
|
8
|
-
from a2a.types import AgentCard
|
|
8
|
+
from a2a.types import AgentCard, PushNotificationConfig
|
|
9
9
|
from a2a.utils import AGENT_CARD_WELL_KNOWN_PATH, PREV_AGENT_CARD_WELL_KNOWN_PATH
|
|
10
10
|
|
|
11
11
|
from aixtools.a2a.google_sdk.remote_agent_connection import RemoteAgentConnection
|
|
@@ -42,9 +42,15 @@ async def get_agent_card(client: httpx.AsyncClient, address: str) -> AgentCard:
|
|
|
42
42
|
class _AgentCardResolver:
|
|
43
43
|
"""Helper class to resolve and manage agent cards and their connections."""
|
|
44
44
|
|
|
45
|
-
def __init__(
|
|
45
|
+
def __init__(
|
|
46
|
+
self, client: httpx.AsyncClient, push_notification_configs: list[PushNotificationConfig] | None = None
|
|
47
|
+
):
|
|
46
48
|
self._httpx_client = client
|
|
47
|
-
self._a2a_client_factory = ClientFactory(
|
|
49
|
+
self._a2a_client_factory = ClientFactory(
|
|
50
|
+
ClientConfig(
|
|
51
|
+
httpx_client=self._httpx_client, polling=True, push_notification_configs=push_notification_configs or []
|
|
52
|
+
)
|
|
53
|
+
)
|
|
48
54
|
self.clients: dict[str, RemoteAgentConnection] = {}
|
|
49
55
|
|
|
50
56
|
def register_agent_card(self, card: AgentCard):
|
|
@@ -72,13 +78,20 @@ async def get_a2a_clients(
|
|
|
72
78
|
agent_hosts: list[str],
|
|
73
79
|
session_id_tuple: SessionIdTuple,
|
|
74
80
|
auth_token: str = None,
|
|
81
|
+
push_notification_config_url: str | None = None,
|
|
75
82
|
*,
|
|
76
83
|
timeout: float = DEFAULT_A2A_TIMEOUT,
|
|
77
84
|
) -> dict[str, RemoteAgentConnection]:
|
|
78
85
|
"""Get A2A clients for all agents defined in the configuration."""
|
|
86
|
+
push_notification_configs = (
|
|
87
|
+
[PushNotificationConfig(url=push_notification_config_url)] if push_notification_config_url else None
|
|
88
|
+
)
|
|
89
|
+
|
|
79
90
|
headers = create_session_headers(session_id_tuple, auth_token)
|
|
80
91
|
httpx_client = httpx.AsyncClient(headers=headers, timeout=timeout, follow_redirects=True)
|
|
81
|
-
clients = await _AgentCardResolver(
|
|
92
|
+
clients = await _AgentCardResolver(
|
|
93
|
+
httpx_client, push_notification_configs=push_notification_configs
|
|
94
|
+
).get_a2a_clients(agent_hosts)
|
|
82
95
|
for client in clients.values():
|
|
83
96
|
logger.info("Using A2A server at: %s", client.get_agent_card().url)
|
|
84
97
|
return clients
|
aixtools/auth/auth.py
CHANGED
|
@@ -120,6 +120,7 @@ class AccessTokenVerifier:
|
|
|
120
120
|
except InvalidSignatureError as e:
|
|
121
121
|
raise AuthTokenError(AuthTokenErrorCode.INVALID_SIGNATURE) from e
|
|
122
122
|
except jwt.exceptions.PyJWTError as e:
|
|
123
|
+
logger.exception("Unable to check JWT token: %s", token)
|
|
123
124
|
raise AuthTokenError(AuthTokenErrorCode.JWT_ERROR) from e
|
|
124
125
|
|
|
125
126
|
def authorize_claims(self, claims: dict, expected_scope: str):
|
|
@@ -148,10 +149,11 @@ class AccessTokenVerifier:
|
|
|
148
149
|
logger.info("Authorized JWT token, against %s", groups)
|
|
149
150
|
return
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
email = claims.get("email")
|
|
153
|
+
logger.warning("User %s group %s does not match configured groups %s", email, groups, self.authorized_groups)
|
|
152
154
|
raise AuthTokenError(
|
|
153
155
|
AuthTokenErrorCode.MISSING_GROUPS_ERROR,
|
|
154
|
-
f"
|
|
156
|
+
f"User {email} group {groups} does not match configured groups {self.authorized_groups}",
|
|
155
157
|
)
|
|
156
158
|
|
|
157
159
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
aixtools/__init__.py,sha256=9NGHm7LjsQmsvjTZvw6QFJexSvAU4bCoN_KBk9SCa00,260
|
|
2
|
-
aixtools/_version.py,sha256=
|
|
2
|
+
aixtools/_version.py,sha256=3nDaC5e0d_scBB1bUEKPlItbvbY0PmXNNyyOTNFNWNI,704
|
|
3
3
|
aixtools/app.py,sha256=JzQ0nrv_bjDQokllIlGHOV0HEb-V8N6k_nGQH-TEsVU,5227
|
|
4
4
|
aixtools/chainlit.md,sha256=yC37Ly57vjKyiIvK4oUvf4DYxZCwH7iocTlx7bLeGLU,761
|
|
5
5
|
aixtools/context.py,sha256=I_MD40ZnvRm5WPKAKqBUAdXIf8YaurkYUUHSVVy-QvU,598
|
|
@@ -17,11 +17,12 @@ aixtools/.chainlit/translations/nl.json,sha256=R3e-WxkQXAiuQgnnXjFWhwzpn1EA9xJ8g
|
|
|
17
17
|
aixtools/.chainlit/translations/ta.json,sha256=pxa2uLEEDjiGiT6MFcCJ_kNh5KoFViHFptcJjc79Llc,17224
|
|
18
18
|
aixtools/.chainlit/translations/te.json,sha256=0qGj-ODEHVOcxfVVX5IszS1QBCKSXuU1okANP_EbvBQ,16885
|
|
19
19
|
aixtools/.chainlit/translations/zh-CN.json,sha256=EWxhT2_6CW9z0F6SI2llr3RsaL2omH1QZWHVG2n5POA,8664
|
|
20
|
-
aixtools/a2a/app.py,sha256=
|
|
20
|
+
aixtools/a2a/app.py,sha256=ugx9FR8QadpEIgy79V6vx1zoamm6ldds6PYKePPQ3sA,5809
|
|
21
|
+
aixtools/a2a/auth_middleware.py,sha256=Q9JmM0JZyJinQBm_BbSizhKpUgIvyWXEegCUmm204AI,1635
|
|
21
22
|
aixtools/a2a/utils.py,sha256=EHr3IyyBJn23ni-JcfAf6i3VpQmPs0g1TSnAZazvY_8,4039
|
|
22
23
|
aixtools/a2a/google_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
23
|
-
aixtools/a2a/google_sdk/remote_agent_connection.py,sha256=
|
|
24
|
-
aixtools/a2a/google_sdk/utils.py,sha256=
|
|
24
|
+
aixtools/a2a/google_sdk/remote_agent_connection.py,sha256=wJXIHD76yL3nlI4b9IA7FQlpKRxrFDsSh78TIm1Fy4E,3329
|
|
25
|
+
aixtools/a2a/google_sdk/utils.py,sha256=eEjYBeSYS7U8RsRxC-rTiw0VXo25CIR9VyRoqdvzGY4,4859
|
|
25
26
|
aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py,sha256=xamLIKl-FeB8PElVKgiIKR-MhFKmPjywHmC_ow-fGAA,9904
|
|
26
27
|
aixtools/a2a/google_sdk/pydantic_ai_adapter/storage.py,sha256=nGoVL7MPoZJW7iVR71laqpUYP308yFKZIifJtvUgpiU,878
|
|
27
28
|
aixtools/agents/__init__.py,sha256=MAW196S2_G7uGqv-VNjvlOETRfuV44WlU1leO7SiR0A,282
|
|
@@ -33,7 +34,7 @@ aixtools/agents/nodes_to_str.py,sha256=UkOu5Nry827J4H_ohQU3tPBfJxtr3p6FfCfWoUy5u
|
|
|
33
34
|
aixtools/agents/print_nodes.py,sha256=wVTngNfqM0As845WTRz6G3Rei_Gr3HuBlvu-G_eXuig,1665
|
|
34
35
|
aixtools/agents/prompt.py,sha256=oZl6_3SelyoSysLpF6AAmLHLHhwyPYCtX8hJ2pRUnhw,7396
|
|
35
36
|
aixtools/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
-
aixtools/auth/auth.py,sha256=
|
|
37
|
+
aixtools/auth/auth.py,sha256=0GAelLK6MaLq-iHm4jE76UPvLplEfodlxb0VYRttLNk,8258
|
|
37
38
|
aixtools/compliance/__init__.py,sha256=vnw0zEdySIJWvDAJ8DCRRaWmY_agEOz1qlpAdhmtiuo,191
|
|
38
39
|
aixtools/compliance/private_data.py,sha256=OOM9mIp3_w0fNgj3VAEWBl7-jrPc19_Ls1pC5dfF5UY,5323
|
|
39
40
|
aixtools/db/__init__.py,sha256=b8vRhme3egV-aUZbAntnOaDkSXB8UT0Xy5oqQhU_z0Q,399
|
|
@@ -95,8 +96,8 @@ aixtools/utils/chainlit/cl_agent_show.py,sha256=vaRuowp4BRvhxEr5hw0zHEJ7iaSF_5bo
|
|
|
95
96
|
aixtools/utils/chainlit/cl_utils.py,sha256=fxaxdkcZg6uHdM8uztxdPowg3a2f7VR7B26VPY4t-3c,5738
|
|
96
97
|
aixtools/vault/__init__.py,sha256=fsr_NuX3GZ9WZ7dGfe0gp_5-z3URxAfwVRXw7Xyc0dU,141
|
|
97
98
|
aixtools/vault/vault.py,sha256=9dZLWdZQk9qN_Q9Djkofw9LUKnJqnrX5H0fGusVLBhA,6037
|
|
98
|
-
aixtools-0.3.
|
|
99
|
-
aixtools-0.3.
|
|
100
|
-
aixtools-0.3.
|
|
101
|
-
aixtools-0.3.
|
|
102
|
-
aixtools-0.3.
|
|
99
|
+
aixtools-0.3.4.dist-info/METADATA,sha256=tIIys8pgVbNONjTKYW0fVbbEtkhwQP8F18hYuPQff1c,28014
|
|
100
|
+
aixtools-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
101
|
+
aixtools-0.3.4.dist-info/entry_points.txt,sha256=q8412TG4T0S8K0SKeWp2vkVPIDYQs0jNoHqcQ7qxOiA,155
|
|
102
|
+
aixtools-0.3.4.dist-info/top_level.txt,sha256=wBn-rw9bCtxrR4AYEYgjilNCUVmKY0LWby9Zan2PRJM,9
|
|
103
|
+
aixtools-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|