nvidia-nat-a2a 1.4.0a20251231__py3-none-any.whl → 1.4.0rc2__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.
- nat/meta/pypi.md +1 -1
- nat/plugins/a2a/__init__.py +1 -1
- nat/plugins/a2a/auth/__init__.py +1 -1
- nat/plugins/a2a/auth/credential_service.py +1 -1
- nat/plugins/a2a/cli/__init__.py +15 -0
- nat/plugins/a2a/cli/commands.py +766 -0
- nat/plugins/a2a/client/__init__.py +1 -1
- nat/plugins/a2a/client/client_base.py +1 -1
- nat/plugins/a2a/client/client_config.py +1 -1
- nat/plugins/a2a/client/client_impl.py +1 -1
- nat/plugins/a2a/register.py +1 -1
- nat/plugins/a2a/server/__init__.py +1 -1
- nat/plugins/a2a/server/agent_executor_adapter.py +1 -1
- nat/plugins/a2a/server/front_end_config.py +1 -1
- nat/plugins/a2a/server/front_end_plugin.py +1 -1
- nat/plugins/a2a/server/front_end_plugin_worker.py +2 -2
- nat/plugins/a2a/server/oauth_middleware.py +1 -1
- nat/plugins/a2a/server/register_frontend.py +1 -1
- {nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/METADATA +3 -3
- nvidia_nat_a2a-1.4.0rc2.dist-info/RECORD +24 -0
- {nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/entry_points.txt +3 -0
- nvidia_nat_a2a-1.4.0a20251231.dist-info/RECORD +0 -22
- {nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/WHEEL +0 -0
- {nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/top_level.txt +0 -0
nat/meta/pypi.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!--
|
|
2
|
-
SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
3
3
|
SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
Licensed under the Apache License, Version 2.0 (the "License");
|
nat/plugins/a2a/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
nat/plugins/a2a/auth/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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
|
+
"""A2A CLI commands."""
|
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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 asyncio
|
|
17
|
+
import json
|
|
18
|
+
import logging
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
import click
|
|
22
|
+
|
|
23
|
+
from nat.cli.cli_utils.validation import validate_url
|
|
24
|
+
from nat.cli.commands.start import start_command
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@click.group(name=__name__, invoke_without_command=False, help="A2A-related commands.")
|
|
30
|
+
def a2a_command():
|
|
31
|
+
"""
|
|
32
|
+
A2A-related commands.
|
|
33
|
+
"""
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# nat a2a serve: reuses the start/a2a frontend command
|
|
38
|
+
a2a_command.add_command(start_command.get_command(None, "a2a"), name="serve") # type: ignore
|
|
39
|
+
|
|
40
|
+
# Suppress verbose logs from httpx
|
|
41
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@a2a_command.group(name="client", invoke_without_command=False, help="A2A client commands.")
|
|
45
|
+
def a2a_client_command():
|
|
46
|
+
"""
|
|
47
|
+
A2A client commands.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
from nat.runtime.loader import PluginTypes
|
|
51
|
+
from nat.runtime.loader import discover_and_register_plugins
|
|
52
|
+
discover_and_register_plugins(PluginTypes.CONFIG_OBJECT)
|
|
53
|
+
except ImportError:
|
|
54
|
+
click.echo("[WARNING] A2A client functionality requires nvidia-nat-a2a package.", err=True)
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
async def discover_agent(url: str, timeout: int = 30):
|
|
59
|
+
"""Discover A2A agent and fetch AgentCard.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
url: A2A agent URL
|
|
63
|
+
timeout: Timeout in seconds
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
AgentCard object or None if failed
|
|
67
|
+
"""
|
|
68
|
+
try:
|
|
69
|
+
from datetime import timedelta
|
|
70
|
+
|
|
71
|
+
from nat.plugins.a2a.client.client_base import A2ABaseClient
|
|
72
|
+
|
|
73
|
+
# Create client
|
|
74
|
+
client = A2ABaseClient(base_url=url, task_timeout=timedelta(seconds=timeout))
|
|
75
|
+
|
|
76
|
+
async with client:
|
|
77
|
+
agent_card = client.agent_card
|
|
78
|
+
|
|
79
|
+
if not agent_card:
|
|
80
|
+
raise RuntimeError(f"Failed to fetch agent card from {url}")
|
|
81
|
+
|
|
82
|
+
return agent_card
|
|
83
|
+
|
|
84
|
+
except ImportError:
|
|
85
|
+
click.echo(
|
|
86
|
+
"A2A client functionality requires nvidia-nat-a2a package. Install with: uv pip install nvidia-nat-a2a",
|
|
87
|
+
err=True)
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def format_agent_card_display(agent_card, verbose: bool = False):
|
|
92
|
+
"""Format AgentCard for display.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
agent_card: AgentCard object
|
|
96
|
+
verbose: Show full details
|
|
97
|
+
"""
|
|
98
|
+
from rich.console import Console
|
|
99
|
+
from rich.panel import Panel
|
|
100
|
+
|
|
101
|
+
console = Console()
|
|
102
|
+
|
|
103
|
+
# Build content
|
|
104
|
+
content = []
|
|
105
|
+
|
|
106
|
+
# Basic info
|
|
107
|
+
content.append(f"[bold]Name:[/bold] {agent_card.name}")
|
|
108
|
+
content.append(f"[bold]Version:[/bold] {agent_card.version}")
|
|
109
|
+
content.append(f"[bold]Protocol Version:[/bold] {agent_card.protocol_version}")
|
|
110
|
+
content.append(f"[bold]URL:[/bold] {agent_card.url}")
|
|
111
|
+
|
|
112
|
+
# Transport
|
|
113
|
+
transport = agent_card.preferred_transport or "JSONRPC"
|
|
114
|
+
content.append(f"[bold]Transport:[/bold] {transport} (preferred)")
|
|
115
|
+
|
|
116
|
+
# Description
|
|
117
|
+
if agent_card.description:
|
|
118
|
+
content.append(f"[bold]Description:[/bold] {agent_card.description}")
|
|
119
|
+
|
|
120
|
+
content.append("") # Blank line
|
|
121
|
+
|
|
122
|
+
# Capabilities
|
|
123
|
+
content.append("[bold]Capabilities:[/bold]")
|
|
124
|
+
caps = agent_card.capabilities
|
|
125
|
+
if caps:
|
|
126
|
+
streaming = "✓" if caps.streaming else "✗"
|
|
127
|
+
content.append(f" {streaming} Streaming")
|
|
128
|
+
push = "✓" if caps.push_notifications else "✗"
|
|
129
|
+
content.append(f" {push} Push Notifications")
|
|
130
|
+
else:
|
|
131
|
+
content.append(" None specified")
|
|
132
|
+
|
|
133
|
+
content.append("") # Blank line
|
|
134
|
+
|
|
135
|
+
# Skills
|
|
136
|
+
skills = agent_card.skills
|
|
137
|
+
content.append(f"[bold]Skills:[/bold] ({len(skills)})")
|
|
138
|
+
|
|
139
|
+
for skill in skills:
|
|
140
|
+
content.append(f" • [cyan]{skill.id}[/cyan]")
|
|
141
|
+
if skill.name:
|
|
142
|
+
content.append(f" Name: {skill.name}")
|
|
143
|
+
content.append(f" Description: {skill.description}")
|
|
144
|
+
if skill.examples:
|
|
145
|
+
if verbose:
|
|
146
|
+
content.append(f" Examples: {', '.join(repr(e) for e in skill.examples)}")
|
|
147
|
+
else:
|
|
148
|
+
# Show first example in normal mode
|
|
149
|
+
content.append(f" Example: {repr(skill.examples[0])}")
|
|
150
|
+
if skill.tags:
|
|
151
|
+
content.append(f" Tags: {', '.join(skill.tags)}")
|
|
152
|
+
|
|
153
|
+
content.append("") # Blank line
|
|
154
|
+
|
|
155
|
+
# Input/Output modes
|
|
156
|
+
content.append(f"[bold]Input Modes:[/bold] {', '.join(agent_card.default_input_modes)}")
|
|
157
|
+
content.append(f"[bold]Output Modes:[/bold] {', '.join(agent_card.default_output_modes)}")
|
|
158
|
+
|
|
159
|
+
content.append("") # Blank line
|
|
160
|
+
|
|
161
|
+
# Auth
|
|
162
|
+
if agent_card.security or agent_card.security_schemes:
|
|
163
|
+
content.append("[bold]Auth Required:[/bold] Yes")
|
|
164
|
+
if verbose and agent_card.security_schemes:
|
|
165
|
+
content.append(f" Schemes: {', '.join(agent_card.security_schemes.keys())}")
|
|
166
|
+
else:
|
|
167
|
+
content.append("[bold]Auth Required:[/bold] None (public agent)")
|
|
168
|
+
|
|
169
|
+
# Create panel
|
|
170
|
+
panel = Panel("\n".join(content), title="[bold]Agent Card Discovery[/bold]", border_style="blue", padding=(1, 2))
|
|
171
|
+
|
|
172
|
+
console.print(panel)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
@a2a_client_command.command(name="discover", help="Discover A2A agent and display AgentCard information.")
|
|
176
|
+
@click.option('--url', required=True, callback=validate_url, help='A2A agent URL (e.g., http://localhost:9999)')
|
|
177
|
+
@click.option('--json-output', is_flag=True, help='Output AgentCard as JSON')
|
|
178
|
+
@click.option('--verbose', is_flag=True, help='Show full AgentCard details')
|
|
179
|
+
@click.option('--save', type=click.Path(), help='Save AgentCard to file')
|
|
180
|
+
@click.option('--timeout', default=30, show_default=True, help='Timeout in seconds')
|
|
181
|
+
def a2a_client_discover(url: str, json_output: bool, verbose: bool, save: str | None, timeout: int):
|
|
182
|
+
"""Discover A2A agent and display AgentCard information.
|
|
183
|
+
|
|
184
|
+
Connects to an A2A agent at the specified URL and fetches its AgentCard,
|
|
185
|
+
which contains information about the agent's capabilities, skills, and
|
|
186
|
+
configuration requirements.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
url: A2A agent URL (e.g., http://localhost:9999)
|
|
190
|
+
json_output: Output as JSON instead of formatted display
|
|
191
|
+
verbose: Show full details including all skill information
|
|
192
|
+
save: Save AgentCard JSON to specified file
|
|
193
|
+
timeout: Timeout in seconds for agent connection
|
|
194
|
+
|
|
195
|
+
Examples:
|
|
196
|
+
nat a2a client discover --url http://localhost:9999
|
|
197
|
+
nat a2a client discover --url http://localhost:9999 --json-output
|
|
198
|
+
nat a2a client discover --url http://localhost:9999 --verbose
|
|
199
|
+
nat a2a client discover --url http://localhost:9999 --save agent-card.json
|
|
200
|
+
"""
|
|
201
|
+
try:
|
|
202
|
+
# Discover agent
|
|
203
|
+
start_time = time.time()
|
|
204
|
+
agent_card = asyncio.run(discover_agent(url, timeout=timeout))
|
|
205
|
+
elapsed = time.time() - start_time
|
|
206
|
+
|
|
207
|
+
if not agent_card:
|
|
208
|
+
click.echo(f"[ERROR] Failed to discover agent at {url}", err=True)
|
|
209
|
+
return
|
|
210
|
+
|
|
211
|
+
# JSON output
|
|
212
|
+
if json_output:
|
|
213
|
+
output = agent_card.model_dump_json(indent=2)
|
|
214
|
+
click.echo(output)
|
|
215
|
+
|
|
216
|
+
# Save if requested
|
|
217
|
+
if save:
|
|
218
|
+
with open(save, 'w') as f:
|
|
219
|
+
f.write(output)
|
|
220
|
+
click.echo(f"\n[INFO] Saved to {save}", err=False)
|
|
221
|
+
|
|
222
|
+
else:
|
|
223
|
+
# Rich formatted output
|
|
224
|
+
format_agent_card_display(agent_card, verbose=verbose)
|
|
225
|
+
|
|
226
|
+
# Save if requested
|
|
227
|
+
if save:
|
|
228
|
+
with open(save, 'w') as f:
|
|
229
|
+
f.write(agent_card.model_dump_json(indent=2))
|
|
230
|
+
click.echo(f"\n✓ Saved AgentCard to {save}")
|
|
231
|
+
|
|
232
|
+
click.echo(f"\n✓ Discovery completed in {elapsed:.2f}s")
|
|
233
|
+
|
|
234
|
+
except Exception as e:
|
|
235
|
+
click.echo(f"[ERROR] {e}", err=True)
|
|
236
|
+
logger.error(f"Error in discover command: {e}", exc_info=True)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
async def _create_bearer_token_auth(
|
|
240
|
+
builder,
|
|
241
|
+
bearer_token: str | None,
|
|
242
|
+
bearer_token_env: str | None,
|
|
243
|
+
):
|
|
244
|
+
"""Create bearer token auth configuration for CLI usage."""
|
|
245
|
+
import os
|
|
246
|
+
|
|
247
|
+
from pydantic import SecretStr
|
|
248
|
+
|
|
249
|
+
from nat.authentication.api_key.api_key_auth_provider_config import APIKeyAuthProviderConfig
|
|
250
|
+
from nat.data_models.authentication import HeaderAuthScheme
|
|
251
|
+
|
|
252
|
+
# Get token from env var or direct input
|
|
253
|
+
if bearer_token_env:
|
|
254
|
+
token_value = os.getenv(bearer_token_env)
|
|
255
|
+
if not token_value:
|
|
256
|
+
raise ValueError(f"Environment variable '{bearer_token_env}' not found or empty")
|
|
257
|
+
elif bearer_token:
|
|
258
|
+
token_value = bearer_token
|
|
259
|
+
else:
|
|
260
|
+
raise ValueError("No bearer token provided")
|
|
261
|
+
|
|
262
|
+
# Create API key auth config with Bearer scheme
|
|
263
|
+
auth_config = APIKeyAuthProviderConfig(
|
|
264
|
+
raw_key=SecretStr(token_value),
|
|
265
|
+
auth_scheme=HeaderAuthScheme.BEARER,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
auth_provider_name = "bearer_token_cli"
|
|
269
|
+
await builder.add_auth_provider(auth_provider_name, auth_config)
|
|
270
|
+
return auth_provider_name
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def _load_auth_from_config(
|
|
274
|
+
builder,
|
|
275
|
+
config_path: str,
|
|
276
|
+
auth_provider_name: str,
|
|
277
|
+
):
|
|
278
|
+
"""Load auth provider from auth-only config file.
|
|
279
|
+
|
|
280
|
+
Parses only the authentication section from YAML file.
|
|
281
|
+
No other workflow sections are required.
|
|
282
|
+
"""
|
|
283
|
+
import yaml
|
|
284
|
+
from pydantic import TypeAdapter
|
|
285
|
+
|
|
286
|
+
from nat.cli.type_registry import GlobalTypeRegistry
|
|
287
|
+
from nat.data_models.authentication import AuthProviderBaseConfig
|
|
288
|
+
|
|
289
|
+
with open(config_path) as f:
|
|
290
|
+
config_data = yaml.safe_load(f)
|
|
291
|
+
|
|
292
|
+
# Extract just the authentication section
|
|
293
|
+
if 'authentication' not in config_data:
|
|
294
|
+
raise ValueError("Config file must contain 'authentication' section")
|
|
295
|
+
|
|
296
|
+
auth_configs = config_data['authentication']
|
|
297
|
+
if auth_provider_name not in auth_configs:
|
|
298
|
+
raise ValueError(f"Auth provider '{auth_provider_name}' not found in config")
|
|
299
|
+
|
|
300
|
+
auth_config_dict = auth_configs[auth_provider_name]
|
|
301
|
+
|
|
302
|
+
# Parse the dictionary into the proper AuthProviderBaseConfig subclass
|
|
303
|
+
auth_union_type = GlobalTypeRegistry.get().compute_annotation(AuthProviderBaseConfig)
|
|
304
|
+
auth_config = TypeAdapter(auth_union_type).validate_python(auth_config_dict)
|
|
305
|
+
|
|
306
|
+
# Add the auth provider to builder
|
|
307
|
+
await builder.add_auth_provider(auth_provider_name, auth_config)
|
|
308
|
+
return auth_provider_name
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
async def _create_auth_from_json(
|
|
312
|
+
builder,
|
|
313
|
+
auth_json: str,
|
|
314
|
+
):
|
|
315
|
+
"""Create auth provider from inline JSON config."""
|
|
316
|
+
from pydantic import TypeAdapter
|
|
317
|
+
|
|
318
|
+
from nat.cli.type_registry import GlobalTypeRegistry
|
|
319
|
+
from nat.data_models.authentication import AuthProviderBaseConfig
|
|
320
|
+
|
|
321
|
+
auth_config_dict = json.loads(auth_json)
|
|
322
|
+
|
|
323
|
+
if '_type' not in auth_config_dict:
|
|
324
|
+
raise ValueError("Auth JSON must contain '_type' field")
|
|
325
|
+
|
|
326
|
+
# Parse the dictionary into the proper AuthProviderBaseConfig subclass
|
|
327
|
+
auth_union_type = GlobalTypeRegistry.get().compute_annotation(AuthProviderBaseConfig)
|
|
328
|
+
auth_config = TypeAdapter(auth_union_type).validate_python(auth_config_dict)
|
|
329
|
+
|
|
330
|
+
# Add the auth provider to builder
|
|
331
|
+
auth_provider_name = "auth_json_cli"
|
|
332
|
+
await builder.add_auth_provider(auth_provider_name, auth_config)
|
|
333
|
+
return auth_provider_name
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
async def get_a2a_function_group(
|
|
337
|
+
url: str,
|
|
338
|
+
timeout: int = 30,
|
|
339
|
+
auth_provider_name: str | None = None,
|
|
340
|
+
user_id: str | None = None,
|
|
341
|
+
):
|
|
342
|
+
"""Load A2A client as a function group with optional authentication.
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
url: A2A agent URL
|
|
346
|
+
timeout: Timeout in seconds
|
|
347
|
+
auth_provider_name: Optional auth provider name (from builder)
|
|
348
|
+
user_id: Optional user ID for authentication
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Tuple of (builder, group, functions dict) or (None, None, None) if failed
|
|
352
|
+
"""
|
|
353
|
+
try:
|
|
354
|
+
from datetime import timedelta
|
|
355
|
+
|
|
356
|
+
from nat.builder.context import ContextState
|
|
357
|
+
from nat.builder.workflow_builder import WorkflowBuilder
|
|
358
|
+
from nat.plugins.a2a.client.client_config import A2AClientConfig
|
|
359
|
+
|
|
360
|
+
builder = WorkflowBuilder()
|
|
361
|
+
await builder.__aenter__()
|
|
362
|
+
|
|
363
|
+
# Set user_id in context before creating function group (similar to nat run)
|
|
364
|
+
# This is required for per-user function groups after multi-user support
|
|
365
|
+
if user_id is None:
|
|
366
|
+
user_id = "nat_a2a_cli_user_id" # Default user_id for CLI operations
|
|
367
|
+
|
|
368
|
+
context_state = ContextState()
|
|
369
|
+
context_state.user_id.set(user_id)
|
|
370
|
+
logger.debug(f"Set user_id in context: {user_id}")
|
|
371
|
+
|
|
372
|
+
# Create A2A config with optional auth
|
|
373
|
+
config = A2AClientConfig(
|
|
374
|
+
url=url,
|
|
375
|
+
task_timeout=timedelta(seconds=timeout),
|
|
376
|
+
auth_provider=auth_provider_name,
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
# Add function group
|
|
380
|
+
group = await builder.add_function_group("a2a_client", config)
|
|
381
|
+
|
|
382
|
+
# Get accessible functions
|
|
383
|
+
fns = await group.get_accessible_functions()
|
|
384
|
+
logger.debug(f"Available functions: {list(fns.keys())}")
|
|
385
|
+
|
|
386
|
+
return builder, group, fns
|
|
387
|
+
|
|
388
|
+
except ImportError:
|
|
389
|
+
click.echo(
|
|
390
|
+
"A2A client functionality requires nvidia-nat-a2a package. Install with: uv pip install nvidia-nat-a2a",
|
|
391
|
+
err=True)
|
|
392
|
+
return None, None, None
|
|
393
|
+
except Exception as e:
|
|
394
|
+
logger.error(f"Error loading A2A function group: {e}", exc_info=True)
|
|
395
|
+
raise
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def format_info_display(info: dict):
|
|
399
|
+
"""Format agent info for simple text display."""
|
|
400
|
+
click.secho("Agent Information", fg='cyan', bold=True)
|
|
401
|
+
click.echo(f" Name: {info.get('name', 'N/A')}")
|
|
402
|
+
click.echo(f" Version: {info.get('version', 'N/A')}")
|
|
403
|
+
click.echo(f" URL: {info.get('url', 'N/A')}")
|
|
404
|
+
|
|
405
|
+
if info.get('description'):
|
|
406
|
+
click.echo(f" Description: {info['description']}")
|
|
407
|
+
|
|
408
|
+
if info.get('provider'):
|
|
409
|
+
provider = info['provider']
|
|
410
|
+
if provider.get('name'):
|
|
411
|
+
click.echo(f" Provider: {provider['name']}")
|
|
412
|
+
|
|
413
|
+
caps = info.get('capabilities', {})
|
|
414
|
+
streaming = "✓" if caps.get('streaming') else "✗"
|
|
415
|
+
click.echo(f" Streaming: {streaming}")
|
|
416
|
+
|
|
417
|
+
click.echo(f" Skills: {info.get('num_skills', 0)}")
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def format_skills_display(skills_data: dict):
|
|
421
|
+
"""Format agent skills for simple text display."""
|
|
422
|
+
agent_name = skills_data.get('agent', 'Unknown')
|
|
423
|
+
skills = skills_data.get('skills', [])
|
|
424
|
+
|
|
425
|
+
click.secho(f"Agent Skills ({len(skills)})", fg='cyan', bold=True)
|
|
426
|
+
click.echo(f" Agent: {agent_name}")
|
|
427
|
+
click.echo()
|
|
428
|
+
|
|
429
|
+
for i, skill in enumerate(skills, 1):
|
|
430
|
+
click.secho(f" [{i}] {skill['id']}", fg='yellow')
|
|
431
|
+
if skill.get('name'):
|
|
432
|
+
click.echo(f" Name: {skill['name']}")
|
|
433
|
+
click.echo(f" Description: {skill['description']}")
|
|
434
|
+
|
|
435
|
+
if skill.get('examples'):
|
|
436
|
+
examples = skill['examples']
|
|
437
|
+
if len(examples) == 1:
|
|
438
|
+
click.echo(f" Example: {examples[0]}")
|
|
439
|
+
else:
|
|
440
|
+
click.echo(f" Examples: {examples[0]}")
|
|
441
|
+
if len(examples) > 1:
|
|
442
|
+
click.secho(f" (+{len(examples)-1} more)", fg='bright_black')
|
|
443
|
+
|
|
444
|
+
if skill.get('tags'):
|
|
445
|
+
click.echo(f" Tags: {', '.join(skill['tags'])}")
|
|
446
|
+
|
|
447
|
+
if i < len(skills):
|
|
448
|
+
click.echo() # Blank line between skills
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def format_call_response_display(message: str, response: str, elapsed: float):
|
|
452
|
+
"""Format agent call response for simple text display."""
|
|
453
|
+
# Show query for context
|
|
454
|
+
click.secho(f"Query: {message}", fg='cyan')
|
|
455
|
+
click.echo()
|
|
456
|
+
|
|
457
|
+
# Show response (main output)
|
|
458
|
+
click.echo(response)
|
|
459
|
+
|
|
460
|
+
# Show timing info in bright green to stderr
|
|
461
|
+
click.echo()
|
|
462
|
+
click.secho(f"({elapsed:.2f}s)", fg='bright_green', err=True)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
@a2a_client_command.command(name="get_info", help="Get agent metadata and information.")
|
|
466
|
+
@click.option('--url', required=True, callback=validate_url, help='A2A agent URL (e.g., http://localhost:9999)')
|
|
467
|
+
@click.option('--json-output', is_flag=True, help='Output as JSON')
|
|
468
|
+
@click.option('--timeout', default=30, show_default=True, help='Timeout in seconds')
|
|
469
|
+
@click.option('--user-id', help='User ID for authentication (optional)')
|
|
470
|
+
def a2a_client_get_info(url: str, json_output: bool, timeout: int, user_id: str | None):
|
|
471
|
+
"""Get agent metadata including name, version, provider, and capabilities.
|
|
472
|
+
|
|
473
|
+
This command connects to an A2A agent and retrieves its metadata.
|
|
474
|
+
|
|
475
|
+
Args:
|
|
476
|
+
url: A2A agent URL (e.g., http://localhost:9999)
|
|
477
|
+
json_output: Output as JSON instead of formatted display
|
|
478
|
+
timeout: Timeout in seconds for agent connection
|
|
479
|
+
user_id: User ID for authentication (optional)
|
|
480
|
+
|
|
481
|
+
Examples:
|
|
482
|
+
nat a2a client get_info --url http://localhost:9999
|
|
483
|
+
nat a2a client get_info --url http://localhost:9999 --json-output
|
|
484
|
+
nat a2a client get_info --url http://localhost:9999 --user-id alice
|
|
485
|
+
"""
|
|
486
|
+
|
|
487
|
+
async def run():
|
|
488
|
+
builder = None
|
|
489
|
+
try:
|
|
490
|
+
# Load A2A function group
|
|
491
|
+
builder, group, fns = await get_a2a_function_group(url, timeout=timeout, user_id=user_id)
|
|
492
|
+
if not builder:
|
|
493
|
+
return
|
|
494
|
+
|
|
495
|
+
# Get the get_info function
|
|
496
|
+
fn = fns.get("a2a_client__get_info")
|
|
497
|
+
if not fn:
|
|
498
|
+
click.echo(f"[ERROR] get_info function not found. Available: {list(fns.keys())}", err=True)
|
|
499
|
+
return
|
|
500
|
+
|
|
501
|
+
# Call the function
|
|
502
|
+
info = await fn.acall_invoke()
|
|
503
|
+
|
|
504
|
+
if json_output:
|
|
505
|
+
click.echo(json.dumps(info, indent=2))
|
|
506
|
+
else:
|
|
507
|
+
format_info_display(info)
|
|
508
|
+
|
|
509
|
+
except Exception as e:
|
|
510
|
+
click.echo(f"[ERROR] {e}", err=True)
|
|
511
|
+
logger.error(f"Error in get_info command: {e}", exc_info=True)
|
|
512
|
+
finally:
|
|
513
|
+
if builder:
|
|
514
|
+
await builder.__aexit__(None, None, None)
|
|
515
|
+
|
|
516
|
+
asyncio.run(run())
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
@a2a_client_command.command(name="get_skills", help="Get agent skills and capabilities.")
|
|
520
|
+
@click.option('--url', required=True, callback=validate_url, help='A2A agent URL (e.g., http://localhost:9999)')
|
|
521
|
+
@click.option('--json-output', is_flag=True, help='Output as JSON')
|
|
522
|
+
@click.option('--timeout', default=30, show_default=True, help='Timeout in seconds')
|
|
523
|
+
@click.option('--user-id', help='User ID for authentication (optional)')
|
|
524
|
+
def a2a_client_get_skills(url: str, json_output: bool, timeout: int, user_id: str | None):
|
|
525
|
+
"""Get detailed list of agent skills and capabilities.
|
|
526
|
+
|
|
527
|
+
This command connects to an A2A agent and retrieves all available skills
|
|
528
|
+
with their descriptions, examples, and tags.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
url: A2A agent URL (e.g., http://localhost:9999)
|
|
532
|
+
json_output: Output as JSON instead of formatted display
|
|
533
|
+
timeout: Timeout in seconds for agent connection
|
|
534
|
+
user_id: User ID for authentication (optional)
|
|
535
|
+
|
|
536
|
+
Examples:
|
|
537
|
+
nat a2a client get_skills --url http://localhost:9999
|
|
538
|
+
nat a2a client get_skills --url http://localhost:9999 --json-output
|
|
539
|
+
nat a2a client get_skills --url http://localhost:9999 --user-id alice
|
|
540
|
+
"""
|
|
541
|
+
|
|
542
|
+
async def run():
|
|
543
|
+
builder = None
|
|
544
|
+
try:
|
|
545
|
+
# Load A2A function group
|
|
546
|
+
builder, group, fns = await get_a2a_function_group(url, timeout=timeout, user_id=user_id)
|
|
547
|
+
if not builder:
|
|
548
|
+
return
|
|
549
|
+
|
|
550
|
+
# Get the get_skills function
|
|
551
|
+
fn = fns.get("a2a_client__get_skills")
|
|
552
|
+
if not fn:
|
|
553
|
+
click.echo(f"[ERROR] get_skills function not found. Available: {list(fns.keys())}", err=True)
|
|
554
|
+
return
|
|
555
|
+
|
|
556
|
+
# Call the function
|
|
557
|
+
skills_data = await fn.acall_invoke()
|
|
558
|
+
|
|
559
|
+
if json_output:
|
|
560
|
+
click.echo(json.dumps(skills_data, indent=2))
|
|
561
|
+
else:
|
|
562
|
+
format_skills_display(skills_data)
|
|
563
|
+
|
|
564
|
+
except Exception as e:
|
|
565
|
+
click.echo(f"[ERROR] {e}", err=True)
|
|
566
|
+
logger.error(f"Error in get_skills command: {e}", exc_info=True)
|
|
567
|
+
finally:
|
|
568
|
+
if builder:
|
|
569
|
+
await builder.__aexit__(None, None, None)
|
|
570
|
+
|
|
571
|
+
asyncio.run(run())
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
@a2a_client_command.command(name="call", help="Call the agent with a message.")
|
|
575
|
+
@click.option('--url', required=True, callback=validate_url, help='A2A agent URL (e.g., http://localhost:9999)')
|
|
576
|
+
@click.option('--message', required=True, help='Message to send to the agent')
|
|
577
|
+
@click.option('--task-id', help='Optional task ID for continuing a conversation')
|
|
578
|
+
@click.option('--context-id', help='Optional context ID for maintaining context')
|
|
579
|
+
@click.option('--json-output', is_flag=True, help='Output as JSON')
|
|
580
|
+
@click.option('--timeout', default=30, show_default=True, help='Timeout in seconds')
|
|
581
|
+
@click.option('--bearer-token', help='Bearer token for authentication')
|
|
582
|
+
@click.option('--bearer-token-env', help='Environment variable containing bearer token')
|
|
583
|
+
@click.option('--auth-config', type=click.Path(exists=True), help='Auth-only config file (YAML)')
|
|
584
|
+
@click.option('--auth-provider', help='Auth provider name from config')
|
|
585
|
+
@click.option('--auth-json', help='Inline auth provider config as JSON')
|
|
586
|
+
@click.option('--user-id', help='User ID for authentication (optional)')
|
|
587
|
+
def a2a_client_call(url: str,
|
|
588
|
+
message: str,
|
|
589
|
+
task_id: str | None,
|
|
590
|
+
context_id: str | None,
|
|
591
|
+
json_output: bool,
|
|
592
|
+
timeout: int,
|
|
593
|
+
bearer_token: str | None,
|
|
594
|
+
bearer_token_env: str | None,
|
|
595
|
+
auth_config: str | None,
|
|
596
|
+
auth_provider: str | None,
|
|
597
|
+
auth_json: str | None,
|
|
598
|
+
user_id: str | None):
|
|
599
|
+
"""Call an A2A agent with a message and get a response.
|
|
600
|
+
|
|
601
|
+
This command connects to an A2A agent, sends a message, and displays the response.
|
|
602
|
+
Use this for one-off queries or testing. For complex workflows with multiple agents
|
|
603
|
+
and tools, create a NAT workflow instead.
|
|
604
|
+
|
|
605
|
+
Authentication is optional. If the agent requires authentication, use one of:
|
|
606
|
+
- --bearer-token or --bearer-token-env for simple token auth
|
|
607
|
+
- --auth-config and --auth-provider for config-based auth
|
|
608
|
+
- --auth-json for inline JSON auth configuration
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
url: A2A agent URL (e.g., http://localhost:9999)
|
|
612
|
+
message: Message to send to the agent
|
|
613
|
+
task_id: Optional task ID for continuing a conversation
|
|
614
|
+
context_id: Optional context ID for maintaining context
|
|
615
|
+
json_output: Output as JSON instead of formatted display
|
|
616
|
+
timeout: Timeout in seconds for agent connection
|
|
617
|
+
bearer_token: Bearer token for authentication
|
|
618
|
+
bearer_token_env: Environment variable containing bearer token
|
|
619
|
+
auth_config: Path to auth-only config file (YAML)
|
|
620
|
+
auth_provider: Auth provider name from config
|
|
621
|
+
auth_json: Inline auth provider config as JSON
|
|
622
|
+
user_id: User ID for authentication
|
|
623
|
+
|
|
624
|
+
Examples:
|
|
625
|
+
# Public agent (no auth)
|
|
626
|
+
nat a2a client call --url http://localhost:9999 --message "Hello"
|
|
627
|
+
|
|
628
|
+
# Bearer token auth
|
|
629
|
+
nat a2a client call --url http://localhost:9999 --message "Hello" --bearer-token "sk-abc123"
|
|
630
|
+
|
|
631
|
+
# Config-based auth
|
|
632
|
+
nat a2a client call --url http://localhost:9999 --message "Hello" \
|
|
633
|
+
--auth-config auth.yml --auth-provider my_oauth --user-id alice
|
|
634
|
+
|
|
635
|
+
# Inline JSON auth
|
|
636
|
+
nat a2a client call --url http://localhost:9999 --message "Hello" \
|
|
637
|
+
--auth-json '{"_type": "api_key", "raw_key": "sk-abc123", "auth_scheme": "Bearer"}'
|
|
638
|
+
"""
|
|
639
|
+
|
|
640
|
+
async def run():
|
|
641
|
+
# Set up authentication callback for CLI workflows
|
|
642
|
+
# This is needed for A2A clients that authenticate during build
|
|
643
|
+
try:
|
|
644
|
+
from nat.builder.context import Context
|
|
645
|
+
from nat.front_ends.console.authentication_flow_handler import ConsoleAuthenticationFlowHandler
|
|
646
|
+
|
|
647
|
+
# Create and set the auth handler early so it's available during workflow building
|
|
648
|
+
auth_handler = ConsoleAuthenticationFlowHandler()
|
|
649
|
+
Context.get()._context_state.user_auth_callback.set(auth_handler.authenticate)
|
|
650
|
+
logger.debug("CLI authentication callback registered for A2A client call")
|
|
651
|
+
except ImportError:
|
|
652
|
+
# Console auth handler not available, skip auth handler setup
|
|
653
|
+
logger.debug("Console authentication handler not available, skipping CLI authentication callback setup")
|
|
654
|
+
|
|
655
|
+
builder = None
|
|
656
|
+
try:
|
|
657
|
+
# Validate auth options
|
|
658
|
+
auth_methods = sum([bool(bearer_token or bearer_token_env), bool(auth_config), bool(auth_json)])
|
|
659
|
+
|
|
660
|
+
if auth_methods > 1:
|
|
661
|
+
click.echo("[ERROR] Use only one authentication method", err=True)
|
|
662
|
+
return
|
|
663
|
+
|
|
664
|
+
if auth_provider and not auth_config:
|
|
665
|
+
click.echo("[ERROR] --auth-provider requires --auth-config", err=True)
|
|
666
|
+
return
|
|
667
|
+
|
|
668
|
+
# Setup authentication if provided
|
|
669
|
+
auth_provider_name = None
|
|
670
|
+
if bearer_token or bearer_token_env:
|
|
671
|
+
# Bearer token auth
|
|
672
|
+
from nat.builder.workflow_builder import WorkflowBuilder
|
|
673
|
+
builder = WorkflowBuilder()
|
|
674
|
+
await builder.__aenter__()
|
|
675
|
+
|
|
676
|
+
try:
|
|
677
|
+
auth_provider_name = await _create_bearer_token_auth(builder, bearer_token, bearer_token_env)
|
|
678
|
+
except Exception as e:
|
|
679
|
+
click.echo(f"[ERROR] Failed to configure bearer token authentication: {e}", err=True)
|
|
680
|
+
return
|
|
681
|
+
|
|
682
|
+
elif auth_config:
|
|
683
|
+
# Config-based auth
|
|
684
|
+
from nat.builder.workflow_builder import WorkflowBuilder
|
|
685
|
+
builder = WorkflowBuilder()
|
|
686
|
+
await builder.__aenter__()
|
|
687
|
+
|
|
688
|
+
try:
|
|
689
|
+
if not auth_provider:
|
|
690
|
+
click.echo("[ERROR] --auth-provider is required with --auth-config", err=True)
|
|
691
|
+
return
|
|
692
|
+
auth_provider_name = await _load_auth_from_config(builder, auth_config, auth_provider)
|
|
693
|
+
except Exception as e:
|
|
694
|
+
click.echo(f"[ERROR] Failed to load auth from config: {e}", err=True)
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
elif auth_json:
|
|
698
|
+
# Inline JSON auth
|
|
699
|
+
from nat.builder.workflow_builder import WorkflowBuilder
|
|
700
|
+
builder = WorkflowBuilder()
|
|
701
|
+
await builder.__aenter__()
|
|
702
|
+
|
|
703
|
+
try:
|
|
704
|
+
auth_provider_name = await _create_auth_from_json(builder, auth_json)
|
|
705
|
+
except Exception as e:
|
|
706
|
+
click.echo(f"[ERROR] Failed to parse auth JSON: {e}", err=True)
|
|
707
|
+
return
|
|
708
|
+
|
|
709
|
+
# Load A2A function group (with or without auth)
|
|
710
|
+
start_time = time.time()
|
|
711
|
+
|
|
712
|
+
if builder:
|
|
713
|
+
# Auth was configured, use existing builder
|
|
714
|
+
from datetime import timedelta
|
|
715
|
+
|
|
716
|
+
from nat.builder.context import ContextState
|
|
717
|
+
from nat.plugins.a2a.client.client_config import A2AClientConfig
|
|
718
|
+
|
|
719
|
+
# Set user_id in context before creating function group (similar to nat run)
|
|
720
|
+
# This is required for per-user function groups after multi-user support
|
|
721
|
+
resolved_user_id = user_id if user_id else "nat_a2a_cli_user_id"
|
|
722
|
+
context_state = ContextState()
|
|
723
|
+
context_state.user_id.set(resolved_user_id)
|
|
724
|
+
logger.debug(f"Set user_id in context: {resolved_user_id}")
|
|
725
|
+
|
|
726
|
+
config = A2AClientConfig(
|
|
727
|
+
url=url,
|
|
728
|
+
task_timeout=timedelta(seconds=timeout),
|
|
729
|
+
auth_provider=auth_provider_name,
|
|
730
|
+
)
|
|
731
|
+
group = await builder.add_function_group("a2a_client", config)
|
|
732
|
+
fns = await group.get_accessible_functions()
|
|
733
|
+
else:
|
|
734
|
+
# No auth, use helper function
|
|
735
|
+
builder, group, fns = await get_a2a_function_group(url, timeout=timeout, user_id=user_id)
|
|
736
|
+
if not builder:
|
|
737
|
+
return
|
|
738
|
+
|
|
739
|
+
# Get the call function
|
|
740
|
+
fn = fns.get("a2a_client__call")
|
|
741
|
+
if not fn:
|
|
742
|
+
click.echo(f"[ERROR] call function not found. Available: {list(fns.keys())}", err=True)
|
|
743
|
+
return
|
|
744
|
+
|
|
745
|
+
# Call the agent with the message
|
|
746
|
+
response = await fn.acall_invoke(query=message, task_id=task_id, context_id=context_id)
|
|
747
|
+
elapsed = time.time() - start_time
|
|
748
|
+
|
|
749
|
+
if json_output:
|
|
750
|
+
result = {"message": message, "response": response, "elapsed": elapsed}
|
|
751
|
+
if task_id:
|
|
752
|
+
result["task_id"] = task_id
|
|
753
|
+
if context_id:
|
|
754
|
+
result["context_id"] = context_id
|
|
755
|
+
click.echo(json.dumps(result, indent=2))
|
|
756
|
+
else:
|
|
757
|
+
format_call_response_display(message, response, elapsed)
|
|
758
|
+
|
|
759
|
+
except Exception as e:
|
|
760
|
+
click.echo(f"[ERROR] {e}", err=True)
|
|
761
|
+
logger.error(f"Error in call command: {e}", exc_info=True)
|
|
762
|
+
finally:
|
|
763
|
+
if builder:
|
|
764
|
+
await builder.__aexit__(None, None, None)
|
|
765
|
+
|
|
766
|
+
asyncio.run(run())
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
nat/plugins/a2a/register.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -175,7 +175,7 @@ class A2AFrontEndPluginWorker:
|
|
|
175
175
|
|
|
176
176
|
for function_name, function in functions.items():
|
|
177
177
|
# Create skill from function metadata
|
|
178
|
-
skill_name = function_name.replace('
|
|
178
|
+
skill_name = function_name.replace('__', ' - ').replace('_', ' ').title()
|
|
179
179
|
skill_description = function.description or f"Execute {function_name}"
|
|
180
180
|
|
|
181
181
|
skill = AgentSkill(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
#
|
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvidia-nat-a2a
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.0rc2
|
|
4
4
|
Summary: Subpackage for A2A Protocol integration in NeMo Agent Toolkit
|
|
5
5
|
Author: NVIDIA Corporation
|
|
6
6
|
Maintainer: NVIDIA Corporation
|
|
@@ -15,12 +15,12 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Requires-Python: <3.14,>=3.11
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE.md
|
|
18
|
-
Requires-Dist: nvidia-nat==v1.4.
|
|
18
|
+
Requires-Dist: nvidia-nat==v1.4.0-rc2
|
|
19
19
|
Requires-Dist: a2a-sdk~=0.3.20
|
|
20
20
|
Dynamic: license-file
|
|
21
21
|
|
|
22
22
|
<!--
|
|
23
|
-
SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
23
|
+
SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
24
24
|
SPDX-License-Identifier: Apache-2.0
|
|
25
25
|
|
|
26
26
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
nat/meta/pypi.md,sha256=dbcW3Z1lelHY5WjLQpB8uNv8dY2of31hQ0EV8KoOiNI,1666
|
|
2
|
+
nat/plugins/a2a/__init__.py,sha256=hiEJ2ajB5zhZuZOUHAyHfTA9w9UNhygij7hp4yfA6QE,685
|
|
3
|
+
nat/plugins/a2a/register.py,sha256=-_SHPsqmVuFNlo6fwnXpZA9whIlMKifk4vnBfNX33mA,858
|
|
4
|
+
nat/plugins/a2a/auth/__init__.py,sha256=9NviPz5TT6MRGgkaqDORYoEFJng0aiTUbJqlsZxNKC0,731
|
|
5
|
+
nat/plugins/a2a/auth/credential_service.py,sha256=xTVX68L-dW31PIseHRaVFgISDxRdJHpyIj6L_tIfoPA,15972
|
|
6
|
+
nat/plugins/a2a/cli/__init__.py,sha256=PGHkqZGf07j-wBfUk-D8-MocQ06oiwAmn3Dy2ttLDaQ,709
|
|
7
|
+
nat/plugins/a2a/cli/commands.py,sha256=3vnx1VWlLuvathrKsMBwDzRjAvvArtmOTnocKNdmttY,29536
|
|
8
|
+
nat/plugins/a2a/client/__init__.py,sha256=hiEJ2ajB5zhZuZOUHAyHfTA9w9UNhygij7hp4yfA6QE,685
|
|
9
|
+
nat/plugins/a2a/client/client_base.py,sha256=80rSNokaC90ImorDOLWMLGvntm8lzSI_W9hCuYbD4l8,13371
|
|
10
|
+
nat/plugins/a2a/client/client_config.py,sha256=dDsdVR9ZLf7ihmHf_g9Osh0oH7fQcbHNiipZsxqyaPo,2880
|
|
11
|
+
nat/plugins/a2a/client/client_impl.py,sha256=J3CghF6rUGtzMSahOOWy1L31V1hrYKdiZbmJnMqXjRU,12963
|
|
12
|
+
nat/plugins/a2a/server/__init__.py,sha256=hiEJ2ajB5zhZuZOUHAyHfTA9w9UNhygij7hp4yfA6QE,685
|
|
13
|
+
nat/plugins/a2a/server/agent_executor_adapter.py,sha256=_fqPseJXhH9PFX6rvYNOOY8jxhs1ykQuHEUyU0PN3JU,7020
|
|
14
|
+
nat/plugins/a2a/server/front_end_config.py,sha256=ny-H_0baW5JhhPU-WAl9fczttwRG_tco_mC3Hzk_Jl0,4907
|
|
15
|
+
nat/plugins/a2a/server/front_end_plugin.py,sha256=G0jBcmLH5u0Kch-7_dQSUakI9RqbMzeO5xFIuXGctSY,4901
|
|
16
|
+
nat/plugins/a2a/server/front_end_plugin_worker.py,sha256=ejGU8osQM2MOzrp32ZOY5XJws4nd8Uu_G79Xs1DUh1g,11702
|
|
17
|
+
nat/plugins/a2a/server/oauth_middleware.py,sha256=K_wvdyrZcKDJXNd_ZxqjSr6i1cZ7fPE28Yfvr3DvdIA,4756
|
|
18
|
+
nat/plugins/a2a/server/register_frontend.py,sha256=2vORvVf6Y0EX4ccNnYpFcZ_VOmH4qfdfoJXgWmSedy0,1485
|
|
19
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
20
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/METADATA,sha256=_Twqb8_SduSCvvMO6jNcO8D30tOJTrhjWW8wLAD4SUA,2432
|
|
21
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/entry_points.txt,sha256=pJzjTpzHY3p6xwedXlEMMm7cOKyze9pFue4PoN4ZB-I,203
|
|
23
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
|
|
24
|
+
nvidia_nat_a2a-1.4.0rc2.dist-info/RECORD,,
|
|
@@ -1,22 +0,0 @@
|
|
|
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.4.0a20251231.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
18
|
-
nvidia_nat_a2a-1.4.0a20251231.dist-info/METADATA,sha256=U2rDLPsY0wUsArIBqHmm3srRc85nlXDjQHRUsKpYHzQ,2438
|
|
19
|
-
nvidia_nat_a2a-1.4.0a20251231.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
-
nvidia_nat_a2a-1.4.0a20251231.dist-info/entry_points.txt,sha256=Lacvy6nXpDTv8dh8vKJ_QE8TobliVdhgABuw25t8fBg,145
|
|
21
|
-
nvidia_nat_a2a-1.4.0a20251231.dist-info/top_level.txt,sha256=8-CJ2cP6-f0ZReXe5Hzqp-5pvzzHz-5Ds5H2bGqh1-U,4
|
|
22
|
-
nvidia_nat_a2a-1.4.0a20251231.dist-info/RECORD,,
|
|
File without changes
|
{nvidia_nat_a2a-1.4.0a20251231.dist-info → nvidia_nat_a2a-1.4.0rc2.dist-info}/licenses/LICENSE.md
RENAMED
|
File without changes
|
|
File without changes
|