aixtools 0.3.2__tar.gz → 0.3.4__tar.gz

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.

Files changed (103) hide show
  1. {aixtools-0.3.2 → aixtools-0.3.4}/PKG-INFO +1 -1
  2. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/_version.py +3 -3
  3. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/app.py +20 -0
  4. aixtools-0.3.4/aixtools/a2a/auth_middleware.py +46 -0
  5. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/google_sdk/remote_agent_connection.py +1 -1
  6. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/google_sdk/utils.py +17 -4
  7. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/auth/auth.py +4 -2
  8. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools.egg-info/SOURCES.txt +1 -0
  9. {aixtools-0.3.2 → aixtools-0.3.4}/README.md +0 -0
  10. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/config.toml +0 -0
  11. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/bn.json +0 -0
  12. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/en-US.json +0 -0
  13. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/gu.json +0 -0
  14. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/he-IL.json +0 -0
  15. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/hi.json +0 -0
  16. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/ja.json +0 -0
  17. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/kn.json +0 -0
  18. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/ml.json +0 -0
  19. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/mr.json +0 -0
  20. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/nl.json +0 -0
  21. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/ta.json +0 -0
  22. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/te.json +0 -0
  23. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/.chainlit/translations/zh-CN.json +0 -0
  24. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/__init__.py +0 -0
  25. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/google_sdk/__init__.py +0 -0
  26. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/google_sdk/pydantic_ai_adapter/agent_executor.py +0 -0
  27. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/google_sdk/pydantic_ai_adapter/storage.py +0 -0
  28. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/a2a/utils.py +0 -0
  29. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/__init__.py +0 -0
  30. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/agent.py +0 -0
  31. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/agent_batch.py +0 -0
  32. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/nodes_to_md.py +0 -0
  33. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/nodes_to_message.py +0 -0
  34. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/nodes_to_str.py +0 -0
  35. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/print_nodes.py +0 -0
  36. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/agents/prompt.py +0 -0
  37. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/app.py +0 -0
  38. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/auth/__init__.py +0 -0
  39. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/chainlit.md +0 -0
  40. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/compliance/__init__.py +0 -0
  41. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/compliance/private_data.py +0 -0
  42. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/context.py +0 -0
  43. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/db/__init__.py +0 -0
  44. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/db/database.py +0 -0
  45. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/db/vector_db.py +0 -0
  46. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/evals/__init__.py +0 -0
  47. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/evals/__main__.py +0 -0
  48. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/evals/dataset.py +0 -0
  49. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/evals/discovery.py +0 -0
  50. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/evals/run_evals.py +0 -0
  51. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/google/client.py +0 -0
  52. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/__init__.py +0 -0
  53. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/app.py +0 -0
  54. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/display.py +0 -0
  55. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/export.py +0 -0
  56. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/filters.py +0 -0
  57. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/log_utils.py +0 -0
  58. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/log_view/node_summary.py +0 -0
  59. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logfilters/__init__.py +0 -0
  60. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logfilters/context_filter.py +0 -0
  61. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/__init__.py +0 -0
  62. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/log_objects.py +0 -0
  63. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/logging_config.py +0 -0
  64. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/mcp_log_models.py +0 -0
  65. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/mcp_logger.py +0 -0
  66. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/model_patch_logging.py +0 -0
  67. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/logging/open_telemetry.py +0 -0
  68. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/__init__.py +0 -0
  69. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/client.py +0 -0
  70. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/example_client.py +0 -0
  71. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/example_server.py +0 -0
  72. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/exceptions.py +0 -0
  73. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/fast_mcp_log.py +0 -0
  74. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/faulty_mcp.py +0 -0
  75. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/middleware.py +0 -0
  76. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/mcp/server.py +0 -0
  77. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/model_patch/model_patch.py +0 -0
  78. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/server/__init__.py +0 -0
  79. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/server/app_mounter.py +0 -0
  80. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/server/path.py +0 -0
  81. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/server/utils.py +0 -0
  82. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/testing/__init__.py +0 -0
  83. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/testing/agent_mock.py +0 -0
  84. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/testing/aix_test_model.py +0 -0
  85. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/testing/mock_tool.py +0 -0
  86. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/testing/model_patch_cache.py +0 -0
  87. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/tools/doctor/__init__.py +0 -0
  88. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/tools/doctor/mcp_tool_doctor.py +0 -0
  89. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/tools/doctor/tool_doctor.py +0 -0
  90. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/tools/doctor/tool_recommendation.py +0 -0
  91. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/__init__.py +0 -0
  92. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/chainlit/cl_agent_show.py +0 -0
  93. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/chainlit/cl_utils.py +0 -0
  94. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/config.py +0 -0
  95. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/config_util.py +0 -0
  96. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/enum_with_description.py +0 -0
  97. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/files.py +0 -0
  98. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/persisted_dict.py +0 -0
  99. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/utils/utils.py +0 -0
  100. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/vault/__init__.py +0 -0
  101. {aixtools-0.3.2 → aixtools-0.3.4}/aixtools/vault/vault.py +0 -0
  102. {aixtools-0.3.2 → aixtools-0.3.4}/pyproject.toml +0 -0
  103. {aixtools-0.3.2 → aixtools-0.3.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aixtools
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: Tools for AI exploration and debugging
5
5
  Requires-Python: >=3.11.2
6
6
  Description-Content-Type: text/markdown
@@ -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.2'
32
- __version_tuple__ = version_tuple = (0, 3, 2)
31
+ __version__ = version = '0.3.4'
32
+ __version_tuple__ = version_tuple = (0, 3, 4)
33
33
 
34
- __commit_id__ = commit_id = 'g0ceaec30c'
34
+ __commit_id__ = commit_id = 'g1146585c6'
@@ -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)
@@ -63,7 +63,7 @@ class RemoteAgentConnection:
63
63
  self,
64
64
  message: Message,
65
65
  *,
66
- sleep_time: float = 0.2,
66
+ sleep_time: float = 2.0,
67
67
  max_iter=1000,
68
68
  on_task_submitted: Callable[[str], None] | None = None,
69
69
  ) -> Task | Message:
@@ -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__(self, client: httpx.AsyncClient):
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(ClientConfig(httpx_client=self._httpx_client, polling=True))
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(httpx_client).get_a2a_clients(agent_hosts)
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
@@ -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
- logger.error("Could not find any group in JWT token, matching: %s", self.authorized_groups)
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"Could not find any group in JWT token, matching: {self.authorized_groups}",
156
+ f"User {email} group {groups} does not match configured groups {self.authorized_groups}",
155
157
  )
156
158
 
157
159
 
@@ -20,6 +20,7 @@ aixtools/.chainlit/translations/ta.json
20
20
  aixtools/.chainlit/translations/te.json
21
21
  aixtools/.chainlit/translations/zh-CN.json
22
22
  aixtools/a2a/app.py
23
+ aixtools/a2a/auth_middleware.py
23
24
  aixtools/a2a/utils.py
24
25
  aixtools/a2a/google_sdk/__init__.py
25
26
  aixtools/a2a/google_sdk/remote_agent_connection.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes