nvidia-nat-a2a 1.5.0a20251229__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 nvidia-nat-a2a might be problematic. Click here for more details.

@@ -0,0 +1,122 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import uvicorn
19
+
20
+ from nat.builder.front_end import FrontEndBase
21
+ from nat.builder.workflow_builder import WorkflowBuilder
22
+ from nat.plugins.a2a.server.front_end_config import A2AFrontEndConfig
23
+ from nat.plugins.a2a.server.front_end_plugin_worker import A2AFrontEndPluginWorker
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class A2AFrontEndPlugin(FrontEndBase[A2AFrontEndConfig]):
29
+ """A2A front end plugin implementation.
30
+
31
+ Exposes NAT workflows as A2A-compliant remote agents that can be
32
+ discovered and invoked by other A2A agents and clients.
33
+ """
34
+
35
+ async def run(self) -> None:
36
+ """Run the A2A server.
37
+
38
+ This method:
39
+ 1. Builds the workflow
40
+ 2. Creates the agent card from configuration
41
+ 3. Creates the agent executor adapter
42
+ 4. Sets up the A2A server
43
+ 5. Starts the server with uvicorn
44
+ """
45
+ # Build the workflow
46
+ async with WorkflowBuilder.from_config(config=self.full_config) as builder:
47
+ workflow = await builder.build()
48
+
49
+ # Create worker instance
50
+ worker = self._get_worker_instance()
51
+
52
+ # Build agent card from configuration and workflow functions
53
+ agent_card = await worker.create_agent_card(workflow)
54
+
55
+ # Create agent executor adapter
56
+ agent_executor = worker.create_agent_executor(workflow, builder)
57
+
58
+ # Create A2A server
59
+ a2a_server = worker.create_a2a_server(agent_card, agent_executor)
60
+
61
+ # Start the server with proper cleanup
62
+ try:
63
+ logger.info(
64
+ "Starting A2A server '%s' at http://%s:%s",
65
+ self.front_end_config.name,
66
+ self.front_end_config.host,
67
+ self.front_end_config.port,
68
+ )
69
+ logger.info("Agent card available at: http://%s:%s/.well-known/agent-card.json",
70
+ self.front_end_config.host,
71
+ self.front_end_config.port)
72
+
73
+ # Build the ASGI app
74
+ app = a2a_server.build()
75
+
76
+ # Add OAuth2 validation middleware if configured
77
+ if self.front_end_config.server_auth:
78
+ from nat.plugins.a2a.server.oauth_middleware import OAuth2ValidationMiddleware
79
+
80
+ app.add_middleware(OAuth2ValidationMiddleware, config=self.front_end_config.server_auth)
81
+ logger.info(
82
+ "OAuth2 token validation enabled for A2A server (issuer=%s, scopes=%s)",
83
+ self.front_end_config.server_auth.issuer_url,
84
+ self.front_end_config.server_auth.scopes,
85
+ )
86
+
87
+ # Run with uvicorn
88
+ config = uvicorn.Config(
89
+ app,
90
+ host=self.front_end_config.host,
91
+ port=self.front_end_config.port,
92
+ log_level=self.front_end_config.log_level.lower(),
93
+ )
94
+ server = uvicorn.Server(config)
95
+ await server.serve()
96
+
97
+ except KeyboardInterrupt:
98
+ logger.info("A2A server shutdown requested (Ctrl+C). Shutting down gracefully.")
99
+ except Exception as e:
100
+ logger.error("A2A server error: %s", e, exc_info=True)
101
+ raise
102
+ finally:
103
+ # Ensure cleanup of resources (httpx client)
104
+ await worker.cleanup()
105
+ logger.info("A2A server resources cleaned up")
106
+
107
+ def _get_worker_instance(self) -> A2AFrontEndPluginWorker:
108
+ """Get an instance of the worker class.
109
+
110
+ Returns:
111
+ Worker instance configured with full config
112
+ """
113
+ # Check if custom worker class is specified
114
+ if self.front_end_config.runner_class:
115
+ module_name, class_name = self.front_end_config.runner_class.rsplit(".", 1)
116
+ import importlib
117
+ module = importlib.import_module(module_name)
118
+ worker_class = getattr(module, class_name)
119
+ return worker_class(self.full_config)
120
+
121
+ # Use default worker
122
+ return A2AFrontEndPluginWorker(self.full_config)
@@ -0,0 +1,306 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import httpx
19
+
20
+ from a2a.server.apps import A2AStarletteApplication
21
+ from a2a.server.request_handlers import DefaultRequestHandler
22
+ from a2a.server.tasks import BasePushNotificationSender
23
+ from a2a.server.tasks import InMemoryPushNotificationConfigStore
24
+ from a2a.server.tasks import InMemoryTaskStore
25
+ from a2a.types import AgentCapabilities
26
+ from a2a.types import AgentCard
27
+ from a2a.types import AgentSkill
28
+ from a2a.types import SecurityScheme
29
+ from nat.builder.function import Function
30
+ from nat.builder.workflow import Workflow
31
+ from nat.builder.workflow_builder import WorkflowBuilder
32
+ from nat.data_models.config import Config
33
+ from nat.plugins.a2a.server.agent_executor_adapter import NATWorkflowAgentExecutor
34
+ from nat.plugins.a2a.server.front_end_config import A2AFrontEndConfig
35
+ from nat.runtime.session import SessionManager
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class A2AFrontEndPluginWorker:
41
+ """Worker that handles A2A server setup and configuration."""
42
+
43
+ def __init__(self, config: Config):
44
+ """Initialize the A2A worker with configuration.
45
+
46
+ Args:
47
+ config: The full NAT configuration
48
+ """
49
+ self.full_config = config
50
+ self.front_end_config: A2AFrontEndConfig = config.general.front_end # type: ignore
51
+
52
+ # Max concurrency for handling A2A tasks (from configuration)
53
+ # This limits how many workflow invocations can run simultaneously
54
+ self.max_concurrency = self.front_end_config.max_concurrency
55
+
56
+ # HTTP client for push notifications (managed for cleanup)
57
+ self._httpx_client: httpx.AsyncClient | None = None
58
+
59
+ async def _get_all_functions(self, workflow: Workflow) -> dict[str, Function]:
60
+ """Get all functions from the workflow.
61
+
62
+ Args:
63
+ workflow: The NAT workflow
64
+
65
+ Returns:
66
+ Dict mapping function names to Function objects
67
+ """
68
+ functions: dict[str, Function] = {}
69
+
70
+ # Extract all functions from the workflow
71
+ functions.update(workflow.functions)
72
+ for function_group in workflow.function_groups.values():
73
+ functions.update(await function_group.get_accessible_functions())
74
+
75
+ return functions
76
+
77
+ async def _generate_security_schemes(
78
+ self, server_auth_config) -> tuple[dict[str, SecurityScheme], list[dict[str, list[str]]]]:
79
+ """Generate A2A security schemes from OAuth2ResourceServerConfig.
80
+
81
+ Args:
82
+ server_auth_config: OAuth2ResourceServerConfig
83
+
84
+ Returns:
85
+ Tuple of (security_schemes dict, security requirements list)
86
+ """
87
+ from a2a.types import AuthorizationCodeOAuthFlow
88
+ from a2a.types import OAuth2SecurityScheme
89
+ from a2a.types import OAuthFlows
90
+
91
+ # Resolve OAuth2 endpoints from configuration
92
+ auth_url, token_url = await self._resolve_oauth_endpoints(server_auth_config)
93
+
94
+ # Create scope descriptions
95
+ scope_descriptions = {scope: f"Permission: {scope}" for scope in server_auth_config.scopes}
96
+
97
+ # Build OAuth2 security scheme
98
+ security_schemes = {
99
+ "oauth2":
100
+ SecurityScheme(root=OAuth2SecurityScheme(
101
+ type="oauth2",
102
+ description="OAuth 2.0 authentication required to access this agent",
103
+ flows=OAuthFlows(authorizationCode=AuthorizationCodeOAuthFlow(
104
+ authorizationUrl=auth_url,
105
+ tokenUrl=token_url,
106
+ scopes=scope_descriptions,
107
+ )),
108
+ ))
109
+ }
110
+
111
+ # Security requirements (scopes needed)
112
+ security = [{"oauth2": server_auth_config.scopes}]
113
+
114
+ return security_schemes, security
115
+
116
+ async def _resolve_oauth_endpoints(self, server_auth_config) -> tuple[str, str]:
117
+ """Resolve authorization and token URLs from OAuth2 configuration.
118
+
119
+ Args:
120
+ server_auth_config: OAuth2ResourceServerConfig
121
+
122
+ Returns:
123
+ Tuple of (authorization_url, token_url)
124
+ """
125
+ import httpx
126
+
127
+ # If discovery URL is provided, use OIDC discovery
128
+ if server_auth_config.discovery_url:
129
+ try:
130
+ async with httpx.AsyncClient() as client:
131
+ response = await client.get(server_auth_config.discovery_url, timeout=5.0)
132
+ response.raise_for_status()
133
+ metadata = response.json()
134
+
135
+ auth_url = metadata.get("authorization_endpoint")
136
+ token_url = metadata.get("token_endpoint")
137
+
138
+ if auth_url and token_url:
139
+ logger.info("Resolved OAuth endpoints via discovery: %s", server_auth_config.discovery_url)
140
+ return auth_url, token_url
141
+ except Exception as e:
142
+ logger.warning("Failed to discover OAuth endpoints: %s", e)
143
+
144
+ # Fallback: derive from issuer URL (common convention)
145
+ issuer = server_auth_config.issuer_url.rstrip("/")
146
+ auth_url = f"{issuer}/oauth/authorize"
147
+ token_url = f"{issuer}/oauth/token"
148
+
149
+ logger.info("Using derived OAuth endpoints from issuer: %s", issuer)
150
+ return auth_url, token_url
151
+
152
+ async def create_agent_card(self, workflow: Workflow) -> AgentCard:
153
+ """Build AgentCard from configuration and workflow functions.
154
+
155
+ Skills are auto-generated from the workflow's functions, similar to how
156
+ MCP introspects and exposes functions as tools.
157
+
158
+ Args:
159
+ workflow: The NAT workflow to extract functions from
160
+
161
+ Returns:
162
+ AgentCard with agent metadata, capabilities, and auto-generated skills
163
+ """
164
+ config = self.front_end_config
165
+
166
+ # Build capabilities
167
+ capabilities = AgentCapabilities(
168
+ streaming=config.capabilities.streaming,
169
+ push_notifications=config.capabilities.push_notifications,
170
+ )
171
+
172
+ # Auto-generate skills from workflow functions
173
+ functions = await self._get_all_functions(workflow)
174
+ skills = []
175
+
176
+ for function_name, function in functions.items():
177
+ # Create skill from function metadata
178
+ skill_name = function_name.replace('_', ' ').replace('.', ' - ').title()
179
+ skill_description = function.description or f"Execute {function_name}"
180
+
181
+ skill = AgentSkill(
182
+ id=function_name,
183
+ name=skill_name,
184
+ description=skill_description,
185
+ tags=[], # Could be extended with function metadata
186
+ examples=[], # Could be extracted from function examples if available
187
+ )
188
+ skills.append(skill)
189
+
190
+ logger.info("Auto-generated %d skills from workflow functions", len(skills))
191
+
192
+ # Generate security schemes if server_auth is configured
193
+ security_schemes = None
194
+ security = None
195
+
196
+ if config.server_auth:
197
+ security_schemes, security = await self._generate_security_schemes(config.server_auth)
198
+ logger.info(
199
+ "Generated OAuth2 security schemes for agent (issuer=%s, scopes=%s)",
200
+ config.server_auth.issuer_url,
201
+ config.server_auth.scopes,
202
+ )
203
+
204
+ # Build agent card
205
+ agent_url = f"http://{config.host}:{config.port}/"
206
+ agent_card = AgentCard(
207
+ name=config.name,
208
+ description=config.description,
209
+ url=agent_url,
210
+ version=config.version,
211
+ default_input_modes=config.default_input_modes,
212
+ default_output_modes=config.default_output_modes,
213
+ capabilities=capabilities,
214
+ skills=skills,
215
+ security_schemes=security_schemes,
216
+ security=security,
217
+ )
218
+
219
+ logger.info("Created AgentCard for: %s v%s", config.name, config.version)
220
+ logger.info("Agent URL: %s", agent_url)
221
+ logger.info("Skills: %d", len(skills))
222
+ if security_schemes:
223
+ logger.info("Security: OAuth2 authentication required")
224
+
225
+ return agent_card
226
+
227
+ def create_agent_executor(self, workflow: Workflow, builder: WorkflowBuilder) -> NATWorkflowAgentExecutor:
228
+ """Create agent executor adapter for the workflow.
229
+
230
+ This creates a SessionManager to handle concurrent A2A task requests,
231
+ similar to how FastAPI handles multiple HTTP requests.
232
+
233
+ Args:
234
+ workflow: The NAT workflow to expose
235
+ builder: The workflow builder used to create the workflow
236
+
237
+ Returns:
238
+ NATWorkflowAgentExecutor that wraps the workflow with a SessionManager
239
+ """
240
+ # Create SessionManager to handle concurrent requests with proper limits
241
+ session_manager = SessionManager(
242
+ config=self.full_config,
243
+ shared_builder=builder,
244
+ shared_workflow=workflow,
245
+ max_concurrency=self.max_concurrency,
246
+ )
247
+
248
+ logger.info("Created SessionManager with max_concurrency=%d", self.max_concurrency)
249
+
250
+ return NATWorkflowAgentExecutor(session_manager)
251
+
252
+ def create_a2a_server(
253
+ self,
254
+ agent_card: AgentCard,
255
+ agent_executor: NATWorkflowAgentExecutor,
256
+ ) -> A2AStarletteApplication:
257
+ """Create A2A server with the agent executor.
258
+
259
+ Args:
260
+ agent_card: The agent card describing the agent
261
+ agent_executor: The executor that handles task processing
262
+
263
+ Returns:
264
+ Configured A2A Starlette application
265
+
266
+ Note:
267
+ The httpx client is stored in self._httpx_client for lifecycle management.
268
+ Call cleanup() during server shutdown to properly close the client.
269
+ """
270
+ # Create HTTP client for push notifications and store for cleanup
271
+ self._httpx_client = httpx.AsyncClient()
272
+
273
+ # Create push notification infrastructure
274
+ push_config_store = InMemoryPushNotificationConfigStore()
275
+ push_sender = BasePushNotificationSender(
276
+ httpx_client=self._httpx_client,
277
+ config_store=push_config_store,
278
+ )
279
+
280
+ # Create request handler
281
+ request_handler = DefaultRequestHandler(
282
+ agent_executor=agent_executor,
283
+ task_store=InMemoryTaskStore(),
284
+ push_config_store=push_config_store,
285
+ push_sender=push_sender,
286
+ )
287
+
288
+ # Create A2A server
289
+ server = A2AStarletteApplication(
290
+ agent_card=agent_card,
291
+ http_handler=request_handler,
292
+ )
293
+
294
+ logger.info("Created A2A server with DefaultRequestHandler")
295
+
296
+ return server
297
+
298
+ async def cleanup(self) -> None:
299
+ """Clean up resources, particularly the httpx client.
300
+
301
+ This should be called during server shutdown to prevent connection leaks.
302
+ """
303
+ if self._httpx_client is not None:
304
+ await self._httpx_client.aclose()
305
+ self._httpx_client = None
306
+ logger.info("Closed httpx client for push notifications")
@@ -0,0 +1,121 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """OAuth 2.0 token validation middleware for A2A servers."""
16
+
17
+ import logging
18
+
19
+ from starlette.middleware.base import BaseHTTPMiddleware
20
+ from starlette.requests import Request
21
+ from starlette.responses import JSONResponse
22
+
23
+ from nat.authentication.credential_validator.bearer_token_validator import BearerTokenValidator
24
+ from nat.authentication.oauth2.oauth2_resource_server_config import OAuth2ResourceServerConfig
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class OAuth2ValidationMiddleware(BaseHTTPMiddleware):
30
+ """OAuth2 Bearer token validation middleware for A2A servers.
31
+
32
+ Validates Bearer tokens using NAT's BearerTokenValidator which supports:
33
+ - JWT validation via JWKS (RFC 7519)
34
+ - Opaque token validation via introspection (RFC 7662)
35
+ - OIDC discovery
36
+ - Scope and audience enforcement
37
+
38
+ The middleware allows public access to the agent card discovery endpoint
39
+ (/.well-known/agent.json) and validates all other A2A requests.
40
+ """
41
+
42
+ def __init__(self, app, config: OAuth2ResourceServerConfig):
43
+ """Initialize OAuth2 validation middleware.
44
+
45
+ Args:
46
+ app: Starlette application
47
+ config: OAuth2 resource server configuration
48
+ """
49
+ super().__init__(app)
50
+
51
+ # Create validator using NAT's BearerTokenValidator
52
+ self.validator = BearerTokenValidator(
53
+ issuer=config.issuer_url,
54
+ audience=config.audience,
55
+ scopes=config.scopes,
56
+ jwks_uri=config.jwks_uri,
57
+ introspection_endpoint=config.introspection_endpoint,
58
+ discovery_url=config.discovery_url,
59
+ client_id=config.client_id,
60
+ client_secret=config.client_secret.get_secret_value() if config.client_secret else None,
61
+ )
62
+
63
+ logger.info(
64
+ "OAuth2 validation middleware initialized (issuer=%s, scopes=%s, audience=%s)",
65
+ config.issuer_url,
66
+ config.scopes,
67
+ config.audience,
68
+ )
69
+
70
+ async def dispatch(self, request: Request, call_next):
71
+ """Validate OAuth2 Bearer token for all requests except agent card discovery.
72
+
73
+ Args:
74
+ request: Incoming HTTP request
75
+ call_next: Next middleware/handler in chain
76
+
77
+ Returns:
78
+ HTTP response (either error or result from next handler)
79
+ """
80
+ # Public: Agent card discovery (per A2A spec)
81
+ if request.url.path == "/.well-known/agent-card.json":
82
+ logger.debug("Public access to agent card discovery")
83
+ return await call_next(request)
84
+
85
+ # Extract Bearer token
86
+ auth_header = request.headers.get("Authorization", "")
87
+ if not auth_header.startswith("Bearer "):
88
+ logger.warning("Missing or invalid Authorization header")
89
+ return JSONResponse({
90
+ "error": "unauthorized", "message": "Missing or invalid Bearer token"
91
+ },
92
+ status_code=401)
93
+
94
+ token = auth_header[7:] # Strip "Bearer "
95
+
96
+ # Validate token using NAT's validator
97
+ try:
98
+ result = await self.validator.verify(token)
99
+ except Exception as e:
100
+ logger.error(f"Token validation error: {e}")
101
+ return JSONResponse({"error": "invalid_token", "message": "Token validation failed"}, status_code=403)
102
+
103
+ # Check if token is active
104
+ if not result.active:
105
+ logger.warning("Token is not active")
106
+ return JSONResponse({"error": "invalid_token", "message": "Token is not active"}, status_code=403)
107
+
108
+ # Attach token info to request state for potential use by handlers
109
+ request.state.oauth_user = result.subject
110
+ request.state.oauth_scopes = result.scopes or []
111
+ request.state.oauth_client_id = result.client_id
112
+ request.state.oauth_token_info = result
113
+
114
+ logger.debug(
115
+ "Token validated successfully (user=%s, scopes=%s, client=%s)",
116
+ result.subject,
117
+ result.scopes,
118
+ result.client_id,
119
+ )
120
+
121
+ return await call_next(request)
@@ -0,0 +1,37 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Registration of A2A front end with NAT plugin system."""
16
+
17
+ from collections.abc import AsyncIterator
18
+
19
+ from nat.cli.register_workflow import register_front_end
20
+ from nat.data_models.config import Config
21
+ from nat.plugins.a2a.server.front_end_config import A2AFrontEndConfig
22
+
23
+
24
+ @register_front_end(config_type=A2AFrontEndConfig)
25
+ async def register_a2a_front_end(_config: A2AFrontEndConfig, full_config: Config) -> AsyncIterator:
26
+ """Register the A2A front end plugin.
27
+
28
+ Args:
29
+ _config: The A2A front end configuration (unused, provided for registration)
30
+ full_config: The complete NAT configuration
31
+
32
+ Yields:
33
+ A2AFrontEndPlugin instance
34
+ """
35
+ from nat.plugins.a2a.server.front_end_plugin import A2AFrontEndPlugin
36
+
37
+ yield A2AFrontEndPlugin(full_config=full_config)
@@ -0,0 +1,57 @@
1
+ Metadata-Version: 2.4
2
+ Name: nvidia-nat-a2a
3
+ Version: 1.5.0a20251229
4
+ Summary: Subpackage for A2A Protocol integration in NeMo Agent Toolkit
5
+ Author: NVIDIA Corporation
6
+ Maintainer: NVIDIA Corporation
7
+ License: Apache-2.0
8
+ Project-URL: documentation, https://docs.nvidia.com/nemo/agent-toolkit/latest/
9
+ Project-URL: source, https://github.com/NVIDIA/NeMo-Agent-Toolkit
10
+ Keywords: ai,rag,agents,a2a
11
+ Classifier: Programming Language :: Python
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: <3.14,>=3.11
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE.md
18
+ Requires-Dist: nvidia-nat==v1.5.0a20251229
19
+ Requires-Dist: a2a-sdk~=0.3.20
20
+ Dynamic: license-file
21
+
22
+ <!--
23
+ SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
24
+ SPDX-License-Identifier: Apache-2.0
25
+
26
+ Licensed under the Apache License, Version 2.0 (the "License");
27
+ you may not use this file except in compliance with the License.
28
+ You may obtain a copy of the License at
29
+
30
+ http://www.apache.org/licenses/LICENSE-2.0
31
+
32
+ Unless required by applicable law or agreed to in writing, software
33
+ distributed under the License is distributed on an "AS IS" BASIS,
34
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35
+ See the License for the specific language governing permissions and
36
+ limitations under the License.
37
+ -->
38
+
39
+ ![NVIDIA NeMo Agent Toolkit](https://media.githubusercontent.com/media/NVIDIA/NeMo-Agent-Toolkit/refs/heads/main/docs/source/_static/banner.png "NeMo Agent toolkit banner image")
40
+
41
+
42
+ # NVIDIA NeMo Agent Toolkit A2A Subpackage
43
+ Subpackage for A2A Protocol integration in NeMo Agent toolkit.
44
+
45
+ This package provides A2A (Agent-to-Agent) Protocol functionality, allowing NeMo Agent toolkit workflows to connect to remote A2A agents and invoke their skills as functions. This package includes both the client and server components of the A2A protocol.
46
+
47
+ ## Features
48
+ ### Client
49
+ - Connect to remote A2A agents via HTTP with JSON-RPC transport
50
+ - Discover agent capabilities through Agent Cards
51
+ - Submit tasks to remote agents with async execution
52
+
53
+ ### Server
54
+ - Serve A2A agents via HTTP with JSON-RPC transport
55
+ - Support for A2A agent executor pattern
56
+
57
+ For more information about the NVIDIA NeMo Agent Toolkit, please visit the [NeMo Agent Toolkit GitHub Repo](https://github.com/NVIDIA/NeMo-Agent-Toolkit).
@@ -0,0 +1,22 @@
1
+ nat/meta/pypi.md,sha256=YkfjzZntzheoaBie5ZovnAwB78xxVqk9sblkZRZcdLU,1661
2
+ nat/plugins/a2a/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
3
+ nat/plugins/a2a/register.py,sha256=pUN1hbJ38M8GbdNcA0qQzJ1S-ZC91GnRGk_8SO_kTVg,853
4
+ nat/plugins/a2a/auth/__init__.py,sha256=iQFx1YrjFcepS7k8jp93A0IVOkFeNx_I35M6dIngoJA,726
5
+ nat/plugins/a2a/auth/credential_service.py,sha256=-_VdDF4YESaAtY1ONUiOL5z4aGDJZYVuhyhI9BZhuyI,15967
6
+ nat/plugins/a2a/client/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
7
+ nat/plugins/a2a/client/client_base.py,sha256=xShDZDFKa4R2XsY3yBMvM-eDaf_0cdE48XJzQ4WcEOw,13366
8
+ nat/plugins/a2a/client/client_config.py,sha256=KwWjymDg9GUfSYcIaBhcxph4Hu6IeTe414hrNUUo-6g,2875
9
+ nat/plugins/a2a/client/client_impl.py,sha256=CGAjiHr6EyWcnlSipmT8ixgjD4s8VbPRBPOZy2q_Sm0,12958
10
+ nat/plugins/a2a/server/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
11
+ nat/plugins/a2a/server/agent_executor_adapter.py,sha256=wvGXOb3FcV0_pYRv-yr-QzozjzXM909D49Dxm9199xI,7015
12
+ nat/plugins/a2a/server/front_end_config.py,sha256=Lg-qjDmC4fwrwnHNtSRl54pMpdwVnO06xhgbLt-aEZY,4902
13
+ nat/plugins/a2a/server/front_end_plugin.py,sha256=fX3Lagkd48snSiNo2IMTRpR-40WHUWQidpjKu8uQChY,4896
14
+ nat/plugins/a2a/server/front_end_plugin_worker.py,sha256=Ehdv6lyUcrWkfMq7YomD4NYFAusrtQ2JYj2HnkIqGhY,11696
15
+ nat/plugins/a2a/server/oauth_middleware.py,sha256=NvvIJSPB8wRui2eQlxr6AaNhN0JxdUQ1Ajr8Dnk0rnY,4751
16
+ nat/plugins/a2a/server/register_frontend.py,sha256=4TmpBcZF4x71c2xnWuketsygqHmU7D2hKA2bzO34TpU,1480
17
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
18
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/METADATA,sha256=uHbXjyUGN9TADzyHyZgf4PZpkBHpV8uIX6euqTNhPFM,2438
19
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
20
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/entry_points.txt,sha256=Lacvy6nXpDTv8dh8vKJ_QE8TobliVdhgABuw25t8fBg,145
21
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
22
+ nvidia_nat_a2a-1.5.0a20251229.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,5 @@
1
+ [nat.components]
2
+ nat_a2a_client = nat.plugins.a2a.client.client_impl
3
+
4
+ [nat.front_ends]
5
+ nat_a2a_server = nat.plugins.a2a.server.register_frontend