nvidia-nat 1.3.0rc5__py3-none-any.whl → 1.3.1__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.
@@ -217,7 +217,7 @@ class ReWOOAgentGraph(BaseAgent):
217
217
  if value is not None:
218
218
  if value == placeholder:
219
219
  tool_input[key] = tool_output
220
- elif placeholder in value:
220
+ elif isinstance(value, str) and placeholder in value:
221
221
  # If the placeholder is part of the value, replace it with the stringified output
222
222
  tool_input[key] = value.replace(placeholder, str(tool_output))
223
223
 
nat/builder/context.py CHANGED
@@ -19,6 +19,7 @@ from collections.abc import Awaitable
19
19
  from collections.abc import Callable
20
20
  from contextlib import contextmanager
21
21
  from contextvars import ContextVar
22
+ from functools import cached_property
22
23
 
23
24
  from nat.builder.intermediate_step_manager import IntermediateStepManager
24
25
  from nat.builder.user_interaction_manager import UserInteractionManager
@@ -167,7 +168,7 @@ class Context:
167
168
  """
168
169
  return UserInteractionManager(self._context_state)
169
170
 
170
- @property
171
+ @cached_property
171
172
  def intermediate_step_manager(self) -> IntermediateStepManager:
172
173
  """
173
174
  Retrieves the intermediate step manager instance from the current context state.
@@ -91,7 +91,10 @@ class IntermediateStepManager:
91
91
  open_step = self._outstanding_start_steps.pop(payload.UUID, None)
92
92
 
93
93
  if (open_step is None):
94
- logger.warning("Step id %s not found in outstanding start steps", payload.UUID)
94
+ logger.warning(
95
+ "Step id %s not found in outstanding start steps. "
96
+ "This may occur if the step was started in a different context or already completed.",
97
+ payload.UUID)
95
98
  return
96
99
 
97
100
  parent_step_id = open_step.step_parent_id
@@ -147,7 +150,8 @@ class IntermediateStepManager:
147
150
  if (open_step is None):
148
151
  logger.warning(
149
152
  "Created a chunk for step %s, but no matching start step was found. "
150
- "Chunks must be created with the same ID as the start step.",
153
+ "Chunks must be created with the same ID as the start step. "
154
+ "This may occur if the step was started in a different context.",
151
155
  payload.UUID)
152
156
  return
153
157
 
@@ -608,6 +608,8 @@ class WebSocketUserInteractionResponseMessage(BaseModel):
608
608
  type: typing.Literal[WebSocketMessageType.USER_INTERACTION_MESSAGE]
609
609
  id: str = "default"
610
610
  thread_id: str = "default"
611
+ parent_id: str = "default"
612
+ conversation_id: str | None = None
611
613
  content: UserMessageContent
612
614
  user: User = User()
613
615
  security: Security = Security()
@@ -28,10 +28,13 @@ class TopPMixin(
28
28
  field_name="top_p",
29
29
  default_if_supported=1.0,
30
30
  keys=("model_name", "model", "azure_deployment"),
31
- unsupported=(re.compile(r"gpt-?5", re.IGNORECASE), ),
31
+ unsupported=(
32
+ re.compile(r"gpt-?5", re.IGNORECASE),
33
+ re.compile(r"claude.?sonnet.?4.5", re.IGNORECASE),
34
+ ),
32
35
  ):
33
36
  """
34
- Mixin class for top-p configuration. Unsupported on models like gpt-5.
37
+ Mixin class for top-p configuration. Unsupported on models like gpt-5 and claude-sonnet-4.5.
35
38
 
36
39
  Attributes:
37
40
  top_p: Top-p for distribution sampling. Defaults to 1.0 when supported on the model.
@@ -116,11 +116,14 @@ class RAGEvaluator:
116
116
  """Convert NaN or None to 0.0 for safe arithmetic/serialization."""
117
117
  return 0.0 if v is None or (isinstance(v, float) and math.isnan(v)) else v
118
118
 
119
- # Convert from list of dicts to dict of lists, coercing NaN/None to 0.0
119
+ # Keep original scores (preserving NaN/None) for output
120
+ original_scores_dict = {metric: [score.get(metric) for score in scores] for metric in scores[0]}
121
+
122
+ # Convert from list of dicts to dict of lists, coercing NaN/None to 0.0 for average calculation
120
123
  scores_dict = {metric: [_nan_to_zero(score.get(metric)) for score in scores] for metric in scores[0]}
121
124
  first_metric_name = list(scores_dict.keys())[0] if scores_dict else None
122
125
 
123
- # Compute the average of each metric, guarding against empty lists
126
+ # Compute the average of each metric using cleaned scores (NaN/None -> 0.0)
124
127
  average_scores = {
125
128
  metric: (sum(values) / len(values) if values else 0.0)
126
129
  for metric, values in scores_dict.items()
@@ -137,11 +140,11 @@ class RAGEvaluator:
137
140
  else:
138
141
  ids = df["user_input"].tolist() # Use "user_input" as ID fallback
139
142
 
140
- # Construct EvalOutputItem list
143
+ # Construct EvalOutputItem list using original scores (preserving NaN/None)
141
144
  eval_output_items = [
142
145
  EvalOutputItem(
143
146
  id=ids[i],
144
- score=_nan_to_zero(getattr(row, first_metric_name, 0.0) if first_metric_name else 0.0),
147
+ score=original_scores_dict[first_metric_name][i] if first_metric_name else None,
145
148
  reasoning={
146
149
  key:
147
150
  getattr(row, key, None) # Use getattr to safely access attributes
@@ -211,6 +211,13 @@ class FastApiFrontEndConfig(FrontEndBaseConfig, name="fastapi"):
211
211
  "Maximum number of async jobs to run concurrently, this controls the number of dask workers created. "
212
212
  "This parameter is only used when scheduler_address is `None` and a Dask local cluster is created."),
213
213
  ge=1)
214
+ dask_workers: typing.Literal["threads", "processes"] = Field(
215
+ default="processes",
216
+ description=(
217
+ "Type of Dask workers to use. Options are 'threads' for Threaded Dask workers or 'processes' for "
218
+ "Process based Dask workers. This parameter is only used when scheduler_address is `None` and a local Dask "
219
+ "cluster is created."),
220
+ )
214
221
  dask_log_level: str = Field(
215
222
  default="WARNING",
216
223
  description="Logging level for Dask.",
@@ -120,18 +120,24 @@ class FastApiFrontEndPlugin(DaskClientMixin, FrontEndBase[FastApiFrontEndConfig]
120
120
 
121
121
  from dask.distributed import LocalCluster
122
122
 
123
- self._cluster = LocalCluster(processes=True,
123
+ use_threads = self.front_end_config.dask_workers == 'threads'
124
+
125
+ # set n_workers to max_running_async_jobs + 1 to allow for one worker to handle the cleanup task
126
+ self._cluster = LocalCluster(processes=not use_threads,
124
127
  silence_logs=dask_log_level,
125
- n_workers=self.front_end_config.max_running_async_jobs,
126
- threads_per_worker=1)
128
+ protocol="tcp",
129
+ n_workers=self.front_end_config.max_running_async_jobs + 1)
127
130
 
128
131
  self._scheduler_address = self._cluster.scheduler.address
129
132
 
130
- with self.blocking_client(self._scheduler_address) as client:
131
- # Client.run submits a function to be run on each worker
132
- client.run(self._setup_worker)
133
+ if not use_threads and sys.platform != "win32":
134
+ with self.blocking_client(self._scheduler_address) as client:
135
+ # Client.run submits a function to be run on each worker
136
+ client.run(self._setup_worker)
133
137
 
134
- logger.info("Created local Dask cluster with scheduler at %s", self._scheduler_address)
138
+ logger.info("Created local Dask cluster with scheduler at %s using %s workers",
139
+ self._scheduler_address,
140
+ self.front_end_config.dask_workers)
135
141
 
136
142
  except ImportError:
137
143
  logger.warning("Dask is not installed, async execution and evaluation will not be available.")
@@ -544,7 +544,8 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
544
544
  GenerateStreamResponseType = workflow.streaming_output_schema
545
545
  GenerateSingleResponseType = workflow.single_output_schema
546
546
 
547
- if self._dask_available:
547
+ # Skip async generation for custom routes (those with function_name)
548
+ if self._dask_available and not hasattr(endpoint, 'function_name'):
548
549
  # Append job_id and expiry_seconds to the input schema, this effectively makes these reserved keywords
549
550
  # Consider prefixing these with "nat_" to avoid conflicts
550
551
 
@@ -562,6 +563,10 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
562
563
  description="Optional time (in seconds) before the job expires. "
563
564
  "Clamped between 600 (10 min) and 86400 (24h).")
564
565
 
566
+ def validate_model(self):
567
+ # Override to ensure that the parent class validator is not called
568
+ return self
569
+
565
570
  # Ensure that the input is in the body. POD types are treated as query parameters
566
571
  if (not issubclass(GenerateBodyType, BaseModel)):
567
572
  GenerateBodyType = typing.Annotated[GenerateBodyType, Body()]
@@ -760,17 +765,18 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
760
765
  return AsyncGenerateResponse(job_id=job.job_id, status=job.status)
761
766
 
762
767
  job_id = self._job_store.ensure_job_id(request.job_id)
763
- (_, job) = await self._job_store.submit_job(job_id=job_id,
764
- expiry_seconds=request.expiry_seconds,
765
- job_fn=run_generation,
766
- sync_timeout=request.sync_timeout,
767
- job_args=[
768
- self._scheduler_address,
769
- self._db_url,
770
- self._config_file_path,
771
- job_id,
772
- request.model_dump(mode="json")
773
- ])
768
+ (_, job) = await self._job_store.submit_job(
769
+ job_id=job_id,
770
+ expiry_seconds=request.expiry_seconds,
771
+ job_fn=run_generation,
772
+ sync_timeout=request.sync_timeout,
773
+ job_args=[
774
+ self._scheduler_address,
775
+ self._db_url,
776
+ self._config_file_path,
777
+ job_id,
778
+ request.model_dump(mode="json", exclude=["job_id", "sync_timeout", "expiry_seconds"])
779
+ ])
774
780
 
775
781
  if job is not None:
776
782
  response.status_code = 200
@@ -916,7 +922,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
916
922
  responses={500: response_500},
917
923
  )
918
924
 
919
- if self._dask_available:
925
+ if self._dask_available and not hasattr(endpoint, 'function_name'):
920
926
  app.add_api_route(
921
927
  path=f"{endpoint.path}/async",
922
928
  endpoint=post_async_generation(request_type=AsyncGenerateRequest),
@@ -930,7 +936,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
930
936
  else:
931
937
  raise ValueError(f"Unsupported method {endpoint.method}")
932
938
 
933
- if self._dask_available:
939
+ if self._dask_available and not hasattr(endpoint, 'function_name'):
934
940
  app.add_api_route(
935
941
  path=f"{endpoint.path}/async/job/{{job_id}}",
936
942
  endpoint=get_async_job_status,
@@ -37,8 +37,11 @@ class MCPFrontEndConfig(FrontEndBaseConfig, name="mcp"):
37
37
  port: int = Field(default=9901, description="Port to bind the server to (default: 9901)", ge=0, le=65535)
38
38
  debug: bool = Field(default=False, description="Enable debug mode (default: False)")
39
39
  log_level: str = Field(default="INFO", description="Log level for the MCP server (default: INFO)")
40
- tool_names: list[str] = Field(default_factory=list,
41
- description="The list of tools MCP server will expose (default: all tools)")
40
+ tool_names: list[str] = Field(
41
+ default_factory=list,
42
+ description="The list of tools MCP server will expose (default: all tools)."
43
+ "Tool names can be functions or function groups",
44
+ )
42
45
  transport: Literal["sse", "streamable-http"] = Field(
43
46
  default="streamable-http",
44
47
  description="Transport type for the MCP server (default: streamable-http, backwards compatible with sse)")
@@ -234,6 +234,10 @@ class MCPFrontEndPluginWorker(MCPFrontEndPluginWorkerBase):
234
234
  filtered_functions: dict[str, Function] = {}
235
235
  for function_name, function in functions.items():
236
236
  if function_name in self.front_end_config.tool_names:
237
+ # Treat current tool_names as function names, so check if the function name is in the list
238
+ filtered_functions[function_name] = function
239
+ elif any(function_name.startswith(f"{group_name}.") for group_name in self.front_end_config.tool_names):
240
+ # Treat tool_names as function group names, so check if the function name starts with the group name
237
241
  filtered_functions[function_name] = function
238
242
  else:
239
243
  logger.debug("Skipping function %s as it's not in tool_names", function_name)
@@ -18,9 +18,12 @@ import logging
18
18
  from inspect import Parameter
19
19
  from inspect import Signature
20
20
  from typing import TYPE_CHECKING
21
+ from typing import Any
21
22
 
22
23
  from mcp.server.fastmcp import FastMCP
23
24
  from pydantic import BaseModel
25
+ from pydantic.fields import FieldInfo
26
+ from pydantic_core import PydanticUndefined
24
27
 
25
28
  from nat.builder.context import ContextState
26
29
  from nat.builder.function import Function
@@ -31,6 +34,41 @@ if TYPE_CHECKING:
31
34
 
32
35
  logger = logging.getLogger(__name__)
33
36
 
37
+ # Sentinel: marks "optional; let Pydantic supply default/factory"
38
+ _USE_PYDANTIC_DEFAULT = object()
39
+
40
+
41
+ def is_field_optional(field: FieldInfo) -> tuple[bool, Any]:
42
+ """Determine if a Pydantic field is optional and extract its default value for MCP signatures.
43
+
44
+ For MCP tool signatures, we need to distinguish:
45
+ - Required fields: marked with Parameter.empty
46
+ - Optional with concrete default: use that default
47
+ - Optional with factory: use sentinel so Pydantic can apply the factory later
48
+
49
+ Args:
50
+ field: The Pydantic FieldInfo to check
51
+
52
+ Returns:
53
+ A tuple of (is_optional, default_value):
54
+ - (False, Parameter.empty) for required fields
55
+ - (True, actual_default) for optional fields with explicit defaults
56
+ - (True, _USE_PYDANTIC_DEFAULT) for optional fields with default_factory
57
+ """
58
+ if field.is_required():
59
+ return False, Parameter.empty
60
+
61
+ # Field is optional - has either default or factory
62
+ if field.default is not PydanticUndefined:
63
+ return True, field.default
64
+
65
+ # Factory case: mark optional in signature but don't fabricate a value
66
+ if field.default_factory is not None:
67
+ return True, _USE_PYDANTIC_DEFAULT
68
+
69
+ # Rare corner case: non-required yet no default surfaced
70
+ return True, _USE_PYDANTIC_DEFAULT
71
+
34
72
 
35
73
  def create_function_wrapper(
36
74
  function_name: str,
@@ -76,12 +114,15 @@ def create_function_wrapper(
76
114
  # Get the field type and convert to appropriate Python type
77
115
  field_type = field.annotation
78
116
 
117
+ # Check if field is optional and get its default value
118
+ _is_optional, param_default = is_field_optional(field)
119
+
79
120
  # Add the parameter to our list
80
121
  parameters.append(
81
122
  Parameter(
82
123
  name=name,
83
124
  kind=Parameter.KEYWORD_ONLY,
84
- default=Parameter.empty if field.is_required else None,
125
+ default=param_default,
85
126
  annotation=field_type,
86
127
  ))
87
128
 
@@ -140,33 +181,23 @@ def create_function_wrapper(
140
181
  result = await call_with_observability(lambda: function.ainvoke(chat_request, to_type=str))
141
182
  else:
142
183
  # Regular handling
143
- # Handle complex input schema - if we extracted fields from a nested schema,
144
- # we need to reconstruct the input
145
- if len(schema.model_fields) == 1 and len(parameters) > 1:
146
- # Get the field name from the original schema
147
- field_name = next(iter(schema.model_fields.keys()))
148
- field_type = schema.model_fields[field_name].annotation
149
-
150
- # If it's a pydantic model, we need to create an instance
151
- if field_type and hasattr(field_type, "model_validate"):
152
- # Create the nested object
153
- nested_obj = field_type.model_validate(kwargs)
154
- # Call with the nested object
155
- kwargs = {field_name: nested_obj}
184
+ # Strip sentinel values so Pydantic can apply defaults/factories
185
+ cleaned_kwargs = {k: v for k, v in kwargs.items() if v is not _USE_PYDANTIC_DEFAULT}
186
+
187
+ # Always validate with the declared schema
188
+ # This handles defaults, factories, nested models, validators, etc.
189
+ model_input = schema.model_validate(cleaned_kwargs)
156
190
 
157
191
  # Call the NAT function with the parameters - special handling for Workflow
158
192
  if is_workflow:
159
- # For workflow with regular input, we'll assume the first parameter is the input
160
- input_value = list(kwargs.values())[0] if kwargs else ""
161
-
162
- # Workflows have a run method that is an async context manager
163
- # that returns a Runner
164
- async with function.run(input_value) as runner:
193
+ # Workflows expect the model instance directly
194
+ async with function.run(model_input) as runner:
165
195
  # Get the result from the runner
166
196
  result = await runner.result(to_type=str)
167
197
  else:
168
- # Regular function call
169
- result = await call_with_observability(lambda: function.acall_invoke(**kwargs))
198
+ # Regular function call - unpack the validated model
199
+ result = await call_with_observability(lambda: function.acall_invoke(**model_input.model_dump())
200
+ )
170
201
 
171
202
  # Report completion
172
203
  if ctx:
@@ -117,10 +117,21 @@ def optimize_parameters(
117
117
 
118
118
  # Save final results (out_dir already created and defined above)
119
119
  with (out_dir / "optimized_config.yml").open("w") as fh:
120
- yaml.dump(tuned_cfg.model_dump(), fh)
120
+ yaml.dump(tuned_cfg.model_dump(mode='json'), fh)
121
121
  with (out_dir / "trials_dataframe_params.csv").open("w") as fh:
122
122
  # Export full trials DataFrame (values, params, timings, etc.).
123
123
  df = study.trials_dataframe()
124
+
125
+ # Rename values_X columns to actual metric names
126
+ metric_names = list(metric_cfg.keys())
127
+ rename_mapping = {}
128
+ for i, metric_name in enumerate(metric_names):
129
+ old_col = f"values_{i}"
130
+ if old_col in df.columns:
131
+ rename_mapping[old_col] = f"values_{metric_name}"
132
+ if rename_mapping:
133
+ df = df.rename(columns=rename_mapping)
134
+
124
135
  # Normalise rep_scores column naming for convenience.
125
136
  if "user_attrs_rep_scores" in df.columns and "rep_scores" not in df.columns:
126
137
  df = df.rename(columns={"user_attrs_rep_scores": "rep_scores"})
@@ -46,9 +46,13 @@ class ParetoVisualizer:
46
46
 
47
47
  fig, ax = plt.subplots(figsize=figsize)
48
48
 
49
- # Extract metric values
50
- x_vals = trials_df[f"values_{0}"].values
51
- y_vals = trials_df[f"values_{1}"].values
49
+ # Extract metric values - support both old (values_0) and new (values_metricname) formats
50
+ x_col = f"values_{self.metric_names[0]}" \
51
+ if f"values_{self.metric_names[0]}" in trials_df.columns else f"values_{0}"
52
+ y_col = f"values_{self.metric_names[1]}"\
53
+ if f"values_{self.metric_names[1]}" in trials_df.columns else f"values_{1}"
54
+ x_vals = trials_df[x_col].values
55
+ y_vals = trials_df[y_col].values
52
56
 
53
57
  # Plot all trials
54
58
  ax.scatter(x_vals,
@@ -62,8 +66,8 @@ class ParetoVisualizer:
62
66
 
63
67
  # Plot Pareto optimal trials if provided
64
68
  if pareto_trials_df is not None and not pareto_trials_df.empty:
65
- pareto_x = pareto_trials_df[f"values_{0}"].values
66
- pareto_y = pareto_trials_df[f"values_{1}"].values
69
+ pareto_x = pareto_trials_df[x_col].values
70
+ pareto_y = pareto_trials_df[y_col].values
67
71
 
68
72
  ax.scatter(pareto_x,
69
73
  pareto_y,
@@ -98,8 +102,8 @@ class ParetoVisualizer:
98
102
  ax.grid(True, alpha=0.3)
99
103
 
100
104
  # Add direction annotations
101
- x_annotation = (f"Better {self.metric_names[0]} "
102
- if self.directions[0] == "minimize" else f" Better {self.metric_names[0]}")
105
+ x_annotation = (f"Better {self.metric_names[0]} "
106
+ if self.directions[0] == "minimize" else f" Better {self.metric_names[0]}")
103
107
  ax.annotate(x_annotation,
104
108
  xy=(0.02, 0.98),
105
109
  xycoords='axes fraction',
@@ -109,8 +113,8 @@ class ParetoVisualizer:
109
113
  style='italic',
110
114
  bbox=dict(boxstyle="round,pad=0.3", facecolor="wheat", alpha=0.7))
111
115
 
112
- y_annotation = (f"Better {self.metric_names[1]} "
113
- if self.directions[1] == "minimize" else f"Better {self.metric_names[1]} ")
116
+ y_annotation = (f"Better {self.metric_names[1]} "
117
+ if self.directions[1] == "minimize" else f"Better {self.metric_names[1]} ")
114
118
  ax.annotate(y_annotation,
115
119
  xy=(0.02, 0.02),
116
120
  xycoords='axes fraction',
@@ -145,7 +149,10 @@ class ParetoVisualizer:
145
149
  # Normalize values for better visualization
146
150
  all_values = []
147
151
  for i in range(n_metrics):
148
- all_values.append(trials_df[f"values_{i}"].values)
152
+ # Support both old (values_0) and new (values_metricname) formats
153
+ col_name = f"values_{self.metric_names[i]}"\
154
+ if f"values_{self.metric_names[i]}" in trials_df.columns else f"values_{i}"
155
+ all_values.append(trials_df[col_name].values)
149
156
 
150
157
  # Normalize each metric to [0, 1] for parallel coordinates
151
158
  normalized_values = []
@@ -221,23 +228,31 @@ class ParetoVisualizer:
221
228
 
222
229
  if i == j:
223
230
  # Diagonal: histograms
224
- values = trials_df[f"values_{i}"].values
231
+ # Support both old (values_0) and new (values_metricname) formats
232
+ col_name = f"values_{self.metric_names[i]}"\
233
+ if f"values_{self.metric_names[i]}" in trials_df.columns else f"values_{i}"
234
+ values = trials_df[col_name].values
225
235
  ax.hist(values, bins=20, alpha=0.7, color='lightblue', edgecolor='navy')
226
236
  if pareto_trials_df is not None and not pareto_trials_df.empty:
227
- pareto_values = pareto_trials_df[f"values_{i}"].values
237
+ pareto_values = pareto_trials_df[col_name].values
228
238
  ax.hist(pareto_values, bins=20, alpha=0.8, color='red', edgecolor='darkred')
229
239
  ax.set_xlabel(f"{self.metric_names[i]}")
230
240
  ax.set_ylabel("Frequency")
231
241
  else:
232
242
  # Off-diagonal: scatter plots
233
- x_vals = trials_df[f"values_{j}"].values
234
- y_vals = trials_df[f"values_{i}"].values
243
+ # Support both old (values_0) and new (values_metricname) formats
244
+ x_col = f"values_{self.metric_names[j]}"\
245
+ if f"values_{self.metric_names[j]}" in trials_df.columns else f"values_{j}"
246
+ y_col = f"values_{self.metric_names[i]}"\
247
+ if f"values_{self.metric_names[i]}" in trials_df.columns else f"values_{i}"
248
+ x_vals = trials_df[x_col].values
249
+ y_vals = trials_df[y_col].values
235
250
 
236
251
  ax.scatter(x_vals, y_vals, alpha=0.6, s=30, c='lightblue', edgecolors='navy', linewidths=0.5)
237
252
 
238
253
  if pareto_trials_df is not None and not pareto_trials_df.empty:
239
- pareto_x = pareto_trials_df[f"values_{j}"].values
240
- pareto_y = pareto_trials_df[f"values_{i}"].values
254
+ pareto_x = pareto_trials_df[x_col].values
255
+ pareto_y = pareto_trials_df[y_col].values
241
256
  ax.scatter(pareto_x,
242
257
  pareto_y,
243
258
  alpha=0.9,
@@ -92,7 +92,9 @@ class Sandbox(abc.ABC):
92
92
  raise ValueError(f"Language {language} not supported")
93
93
 
94
94
  generated_code = generated_code.strip().strip("`")
95
- code_to_execute = textwrap.dedent("""
95
+ # Use json.dumps to properly escape the generated_code instead of repr()
96
+ escaped_code = json.dumps(generated_code)
97
+ code_to_execute = textwrap.dedent(f"""
96
98
  import traceback
97
99
  import json
98
100
  import os
@@ -101,11 +103,6 @@ class Sandbox(abc.ABC):
101
103
  import io
102
104
  warnings.filterwarnings('ignore')
103
105
  os.environ['OPENBLAS_NUM_THREADS'] = '16'
104
- """).strip()
105
-
106
- # Use json.dumps to properly escape the generated_code instead of repr()
107
- escaped_code = json.dumps(generated_code)
108
- code_to_execute += textwrap.dedent(f"""
109
106
 
110
107
  generated_code = {escaped_code}
111
108
 
nat/tool/server_tools.py CHANGED
@@ -32,14 +32,23 @@ class RequestAttributesTool(FunctionBaseConfig, name="current_request_attributes
32
32
  @register_function(config_type=RequestAttributesTool)
33
33
  async def current_request_attributes(config: RequestAttributesTool, builder: Builder):
34
34
 
35
+ from pydantic import RootModel
36
+ from pydantic.types import JsonValue
35
37
  from starlette.datastructures import Headers
36
38
  from starlette.datastructures import QueryParams
37
39
 
38
- async def _get_request_attributes(unused: str) -> str:
40
+ class RequestBody(RootModel[JsonValue]):
41
+ """
42
+ Data model that accepts a request body of any valid JSON type.
43
+ """
44
+ root: JsonValue
45
+
46
+ async def _get_request_attributes(request_body: RequestBody) -> str:
39
47
 
40
48
  from nat.builder.context import Context
41
49
  nat_context = Context.get()
42
50
 
51
+ # Access request attributes from context
43
52
  method: str | None = nat_context.metadata.method
44
53
  url_path: str | None = nat_context.metadata.url_path
45
54
  url_scheme: str | None = nat_context.metadata.url_scheme
@@ -51,6 +60,9 @@ async def current_request_attributes(config: RequestAttributesTool, builder: Bui
51
60
  cookies: dict[str, str] | None = nat_context.metadata.cookies
52
61
  conversation_id: str | None = nat_context.conversation_id
53
62
 
63
+ # Access the request body data - can be any valid JSON type
64
+ request_body_data: JsonValue = request_body.root
65
+
54
66
  return (f"Method: {method}, "
55
67
  f"URL Path: {url_path}, "
56
68
  f"URL Scheme: {url_scheme}, "
@@ -60,7 +72,8 @@ async def current_request_attributes(config: RequestAttributesTool, builder: Bui
60
72
  f"Client Host: {client_host}, "
61
73
  f"Client Port: {client_port}, "
62
74
  f"Cookies: {cookies}, "
63
- f"Conversation Id: {conversation_id}")
75
+ f"Conversation Id: {conversation_id}, "
76
+ f"Request Body: {request_body_data}")
64
77
 
65
78
  yield FunctionInfo.from_fn(_get_request_attributes,
66
79
  description="Returns the acquired user defined request attributes.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat
3
- Version: 1.3.0rc5
3
+ Version: 1.3.1
4
4
  Summary: NVIDIA NeMo Agent toolkit
5
5
  Author: NVIDIA Corporation
6
6
  Maintainer: NVIDIA Corporation
@@ -84,10 +84,13 @@ Requires-Dist: nvidia-nat-s3; extra == "s3"
84
84
  Provides-Extra: semantic-kernel
85
85
  Requires-Dist: nvidia-nat-semantic-kernel; extra == "semantic-kernel"
86
86
  Provides-Extra: telemetry
87
+ Requires-Dist: nvidia-nat-data-flywheel; extra == "telemetry"
87
88
  Requires-Dist: nvidia-nat-opentelemetry; extra == "telemetry"
88
89
  Requires-Dist: nvidia-nat-phoenix; extra == "telemetry"
89
- Requires-Dist: nvidia-nat-weave; extra == "telemetry"
90
90
  Requires-Dist: nvidia-nat-ragaai; extra == "telemetry"
91
+ Requires-Dist: nvidia-nat-weave; extra == "telemetry"
92
+ Provides-Extra: test
93
+ Requires-Dist: nvidia-nat-test; extra == "test"
91
94
  Provides-Extra: weave
92
95
  Requires-Dist: nvidia-nat-weave; extra == "weave"
93
96
  Provides-Extra: zep-cloud
@@ -14,7 +14,7 @@ nat/agent/react_agent/register.py,sha256=qkPaK6AvXjolL-q_Z3waVobXDz24GMfuqGqCn-2
14
14
  nat/agent/reasoning_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  nat/agent/reasoning_agent/reasoning_agent.py,sha256=fFQtzvaBWtmr_B6S9KSkqAfyl1BdcOc9xkhnbO4O8Pk,9603
16
16
  nat/agent/rewoo_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- nat/agent/rewoo_agent/agent.py,sha256=XXgVXY9xwkyxnr093KXUtfgyNxAQbyGAecoGqN5mMLY,26199
17
+ nat/agent/rewoo_agent/agent.py,sha256=ZcTHOX2cj5zJBgy6suLf_DMFEyBLhjQklPrJuMCjAtU,26226
18
18
  nat/agent/rewoo_agent/prompt.py,sha256=B0JeL1xDX4VKcShlkkviEcAsOKAwzSlX8NcAQdmUUPw,3645
19
19
  nat/agent/rewoo_agent/register.py,sha256=XArlOR37QOBtAvsdKJUjRok5qTmx39S2mJHSteOwU58,9283
20
20
  nat/agent/tool_calling_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -42,7 +42,7 @@ nat/authentication/oauth2/register.py,sha256=7rXhf-ilgSS_bUJsd9pOOCotL1FM8dKUt3k
42
42
  nat/builder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
43
43
  nat/builder/builder.py,sha256=okI3Y101hwF63AwazzxiahQx-W9eFZ_SNdFXzDuoftU,11608
44
44
  nat/builder/component_utils.py,sha256=gxDhm4NCLI1GU0XL9gFe_gife0oJLwgk_YuABJneFfs,13838
45
- nat/builder/context.py,sha256=6NQmHfJS0gY4eLU7Xg84olmrgUdtVJcS3gmxc-OADiw,13093
45
+ nat/builder/context.py,sha256=YmgYHzXggPQCOIBj1Mirr7xKM7NvtVr3XrSfn979fDM,13138
46
46
  nat/builder/embedder.py,sha256=NPkOEcxt_-wc53QRijCQQDLretRUYHRYaKoYmarmrBk,965
47
47
  nat/builder/eval_builder.py,sha256=I-ScvupmorClYoVBIs_PhSsB7Xf9e2nGWe0rCZp3txo,6857
48
48
  nat/builder/evaluator.py,sha256=xWHMND2vcAUkdFP7FU3jnVki1rUHeTa0-9saFh2hWKs,1162
@@ -51,7 +51,7 @@ nat/builder/front_end.py,sha256=FCJ87NSshVVuTg8zZrq3YAr_u0RaYVZVcibnqlRFy-M,2173
51
51
  nat/builder/function.py,sha256=eZZWLwhphgQTnPvbga8sGleX7HCP46usZPIegE7zFzs,27725
52
52
  nat/builder/function_base.py,sha256=0Eg8RtjWhEU3Yme0CVxcRutobA0Qo8-YHZLI6L2qAgM,13116
53
53
  nat/builder/function_info.py,sha256=7Rmrn-gOFrT2TIJklJwA_O-ycx_oimwZ0-qMYpbuZrU,25161
54
- nat/builder/intermediate_step_manager.py,sha256=iOuMLWTaES0J0XzaLxhTUqFvuoCAChJu3V69T43K0k0,7599
54
+ nat/builder/intermediate_step_manager.py,sha256=wTfCV47IKpYO7NvoHSD4BGwOiuZ8Db9ktzs14T38gBE,7834
55
55
  nat/builder/llm.py,sha256=DW-2q64A06VChsXNEL5PfBjH3DcsnTKVoCEWDuP7MF4,951
56
56
  nat/builder/retriever.py,sha256=ZyEqc7pFK31t_yr6Jaxa34c-tRas2edKqJZCNiVh9-0,970
57
57
  nat/builder/user_interaction_manager.py,sha256=-Z2qbQes7a2cuXgT7KEbWeuok0HcCnRdw9WB8Ghyl9k,3081
@@ -112,7 +112,7 @@ nat/control_flow/router_agent/prompt.py,sha256=fIAiNsAs1zXRAatButR76zSpHJNxSkXXK
112
112
  nat/control_flow/router_agent/register.py,sha256=4RGmS9sy-QtIMmvh8mfMcR1VqxFPLpG4RckWCIExh40,4144
113
113
  nat/data_models/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
114
114
  nat/data_models/agent.py,sha256=IwDyb9Zc3R4Zd5rFeqt7q0EQswczAl5focxV9KozIzs,1625
115
- nat/data_models/api_server.py,sha256=IowzLwxJqnSkUehCbK0WJp98hBZFXUQDd1cq8lr9PVs,30582
115
+ nat/data_models/api_server.py,sha256=oQtSiP7jpkHIZ75g21A_lTiidNsQo54pq3qy2StIJcs,30652
116
116
  nat/data_models/authentication.py,sha256=XPu9W8nh4XRSuxPv3HxO-FMQ_JtTEoK6Y02JwnzDwTg,8457
117
117
  nat/data_models/common.py,sha256=nXXfGrjpxebzBUa55mLdmzePLt7VFHvTAc6Znj3yEv0,5875
118
118
  nat/data_models/component.py,sha256=b_hXOA8Gm5UNvlFkAhsR6kEvf33ST50MKtr5kWf75Ao,1894
@@ -147,7 +147,7 @@ nat/data_models/swe_bench_model.py,sha256=uZs-hLFuT1B5CiPFwFg1PHinDW8PHne8TBzu7t
147
147
  nat/data_models/telemetry_exporter.py,sha256=P7kqxIQnFVuvo_UFpH9QSB8fACy_0U2Uzkw_IfWXagE,998
148
148
  nat/data_models/temperature_mixin.py,sha256=LlpfWrWtDrPJfSKfNx5E0P3p5SNGZli7ACRRpmO0QqA,1628
149
149
  nat/data_models/thinking_mixin.py,sha256=VRDUJZ8XP_Vv0gW2FRZUf8O9-kVgNEdZCEZ8oEmHyMk,3335
150
- nat/data_models/top_p_mixin.py,sha256=mu0DLnCAiwNzpSFR8FOW4kQBUpodSrvUR4MsLrNtbgA,1599
150
+ nat/data_models/top_p_mixin.py,sha256=07wOZ7iK2FuNvoyS1LYuIEP0z-7qKDCMoeeeKIAm28k,1705
151
151
  nat/data_models/ttc_strategy.py,sha256=tAkKWcyEBmBOOYtHMtQTgeCbHxFTk5SEkmFunNVnfyE,1114
152
152
  nat/embedder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
153
153
  nat/embedder/azure_openai_embedder.py,sha256=8OM54DCVCmWuQ8eZ5lXxsKYQktRmHMoIelRHZmKurUk,2381
@@ -170,7 +170,7 @@ nat/eval/evaluator/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQ
170
170
  nat/eval/evaluator/base_evaluator.py,sha256=5WaVGhCGzkynCJyQdxRv7CtqLoUpr6B4O8tilP_gb3g,3232
171
171
  nat/eval/evaluator/evaluator_model.py,sha256=riGCcDW8YwC3Kd1yoVmbMdJE1Yf2kVmO8uhsGsKKJA4,1878
172
172
  nat/eval/rag_evaluator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
- nat/eval/rag_evaluator/evaluate.py,sha256=iQ_fUg1PAuIB2gH6Y7Gz9PeztsdhEswUjyUMa5ZXEx4,8399
173
+ nat/eval/rag_evaluator/evaluate.py,sha256=IfCpfCKBTYhReRkPPbOqyr-9H6gsPGaeFWBIcGDUynw,8639
174
174
  nat/eval/rag_evaluator/register.py,sha256=AzT5uICDU5dEo7scvStmOWC7ac-S0Tx4UY87idGtXIs,5835
175
175
  nat/eval/runners/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv-tZzbU9W-izgx9aMEQg,680
176
176
  nat/eval/runners/config.py,sha256=bRPai_th02OJrFepbbY6w-t7A18TBXozQUnnnH9iWIU,1403
@@ -239,10 +239,10 @@ nat/front_ends/console/register.py,sha256=2Kf6Mthx6jzWzU8YdhYIR1iABmZDvs1UXM_20n
239
239
  nat/front_ends/cron/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
240
240
  nat/front_ends/fastapi/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
241
241
  nat/front_ends/fastapi/dask_client_mixin.py,sha256=N_tw4yxA7EKIFTKp5_C2ZksIZucWxRYkFjmZszkAkXc,2072
242
- nat/front_ends/fastapi/fastapi_front_end_config.py,sha256=BcuzrVlA5b7yYyQKNvQgEanDBtKEHdpC8TAd-O7lfF0,11992
242
+ nat/front_ends/fastapi/fastapi_front_end_config.py,sha256=O_iRpGS3Vpht7ZNr1bYpvoldDEb9Z6f7tFPazVP5i9U,12383
243
243
  nat/front_ends/fastapi/fastapi_front_end_controller.py,sha256=ei-34KCMpyaeAgeAN4gVvSGFjewjjRhHZPN0FqAfhDY,2548
244
- nat/front_ends/fastapi/fastapi_front_end_plugin.py,sha256=e33YkMcLzvm4OUG34bhl-WYiBTqkR-_wJYKG4GODkGM,11169
245
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py,sha256=T6uslFdkHl_r0U54_7cRRKLnWYP2tTMcD7snx9Gv1xs,60547
244
+ nat/front_ends/fastapi/fastapi_front_end_plugin.py,sha256=5akdWipe8onOTdSqrbGq9KO71y0_BNQQJ3JAFj6LmFY,11575
245
+ nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py,sha256=QMERZEifVTDMWttSQqPZu3k8EEPJflPrIR6tkHTkZYg,60594
246
246
  nat/front_ends/fastapi/intermediate_steps_subscriber.py,sha256=kbyWlBVpyvyQQjeUnFG9nsR4RaqqNkx567ZSVwwl2RU,3104
247
247
  nat/front_ends/fastapi/job_store.py,sha256=cWIBnIgRdkGL7qbBunEKzTYzdPp3l3QCDHMP-qTZJpc,22743
248
248
  nat/front_ends/fastapi/main.py,sha256=s8gXCy61rJjK1aywMRpgPvzlkMGsCS-kI_0EIy4JjBM,2445
@@ -259,11 +259,11 @@ nat/front_ends/fastapi/html_snippets/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv
259
259
  nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py,sha256=BNpWwzmA58UM0GK4kZXG4PHJy_5K9ihaVHu8SgCs5JA,1131
260
260
  nat/front_ends/mcp/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
261
261
  nat/front_ends/mcp/introspection_token_verifier.py,sha256=s7Q4Q6rWZJ0ZVujSxxpvVI6Bnhkg1LJQ3RLkvhzFIGE,2836
262
- nat/front_ends/mcp/mcp_front_end_config.py,sha256=m6z5qSz8YGnFnfu8hRID69suvO1YT_L6sxy1Ki64Ufw,4042
262
+ nat/front_ends/mcp/mcp_front_end_config.py,sha256=KP13AKCyqOwXqebMmYrPwx1Ogq5iPo7R9uzLNSIHySY,4089
263
263
  nat/front_ends/mcp/mcp_front_end_plugin.py,sha256=4u_kpen_T-_Uh62V5M7dfW9KyzbqXI7tGBG4AxJXWm0,5231
264
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py,sha256=jMclC0qEd910oTGCqd1kQ8WjP3WPdQKTl854-2bU_KI,10200
264
+ nat/front_ends/mcp/mcp_front_end_plugin_worker.py,sha256=rOF5Z4o-4n3ky57ncNm8XCdBsbRazGeOeiw55nKbZF4,10618
265
265
  nat/front_ends/mcp/register.py,sha256=3aJtgG5VaiqujoeU1-Eq7Hl5pWslIlIwGFU2ASLTXgM,1173
266
- nat/front_ends/mcp/tool_converter.py,sha256=jyH6tFKUDXSfRBKkv8WjvJsQt05zk3FJBTCwnIuUh5M,11547
266
+ nat/front_ends/mcp/tool_converter.py,sha256=q3GgouOAQ8CGeX8nYtpwHUDEm3BWJgstgZHfKSg2m80,12470
267
267
  nat/front_ends/simple_base/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
268
268
  nat/front_ends/simple_base/simple_front_end_plugin_base.py,sha256=py_yA9XAw-yHfK5cQJLM8ElnubEEM2ac8M0bvz-ScWs,1801
269
269
  nat/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -367,9 +367,9 @@ nat/profiler/inference_optimization/experimental/prefix_span_analysis.py,sha256=
367
367
  nat/profiler/parameter_optimization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
368
368
  nat/profiler/parameter_optimization/optimizable_utils.py,sha256=93Pl8A14Zq_f3XsxSH-yFnEJ6B7W5hp7doPnPoLlRB4,3714
369
369
  nat/profiler/parameter_optimization/optimizer_runtime.py,sha256=rXmCOq81o7ZorQOUYociVjuO3NO9CIjFBbwql2u_4H4,2715
370
- nat/profiler/parameter_optimization/parameter_optimizer.py,sha256=CxRFSnl0w0JQvijsY-gszUNxvSrihQ0Jl9ySYClsNFI,6544
370
+ nat/profiler/parameter_optimization/parameter_optimizer.py,sha256=61U2jW0md2oqaN8cy7cmF8oMTbHlgyzdlAXhG5DqfI8,6962
371
371
  nat/profiler/parameter_optimization/parameter_selection.py,sha256=pfnNQIx1evNICgChsOJXIFQHoL1R_kmh_vNDsVMC9kg,3982
372
- nat/profiler/parameter_optimization/pareto_visualizer.py,sha256=IU-4Kw3cVKfDBmXyxtzDf5B325hizMmEYjB9_QJGwD0,15903
372
+ nat/profiler/parameter_optimization/pareto_visualizer.py,sha256=QclLZmmsWINIAh4n0XAKmnIZOqGHTMr-iggZS0kxj-Y,17055
373
373
  nat/profiler/parameter_optimization/prompt_optimizer.py,sha256=_AmdeB1jRamd93qR5UqRy5LweYR3bjnD7zoLxzXYE0k,17658
374
374
  nat/profiler/parameter_optimization/update_helpers.py,sha256=NxWhrGVchbjws85pPd-jS-C14_l70QvVSvEfENndVcY,2339
375
375
  nat/registry_handlers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -420,10 +420,10 @@ nat/tool/github_tools.py,sha256=Pn6p6ebLClNUy6MJIWf-Pl5NguMUT-IJ-bhmpJmvBrg,2190
420
420
  nat/tool/nvidia_rag.py,sha256=cEHSc3CZwpd71YcOQngya-Ca_B6ZOb87Dmsoza0VhFY,4163
421
421
  nat/tool/register.py,sha256=F1mQs9gI3Ee0EESFyBiTFZeqscY173ENQlfKcWZup0c,1208
422
422
  nat/tool/retriever.py,sha256=FP5JL1vCQNrqaKz4F1up-osjxEPhxPFOyaScrgByc34,3877
423
- nat/tool/server_tools.py,sha256=rQLipwRv8lAyU-gohky2JoVDxWQTUTSttNWjhu7lcHU,3194
423
+ nat/tool/server_tools.py,sha256=sxsgaF5ZjKIc3cSLldt1MDhY3kptrDnkP3kVYvVexfY,3679
424
424
  nat/tool/code_execution/README.md,sha256=sl3YX4As95HX61XqTXOGnUcHBV1lla-OeuTnLI4qgng,4019
425
425
  nat/tool/code_execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
426
- nat/tool/code_execution/code_sandbox.py,sha256=AynQfmvT4mtrV5DHV-20HF8mNTDo1NX4Wj2mlpaEIfs,10159
426
+ nat/tool/code_execution/code_sandbox.py,sha256=evu0n9oTc_MrrQuYbo1TP0sjnjLniRgCsOubO5G5WT4,10090
427
427
  nat/tool/code_execution/register.py,sha256=zPFzYqaQhH2B3K8iildVYY_7RKgpoRNKdAo00KmBLQI,3322
428
428
  nat/tool/code_execution/test_code_execution_sandbox.py,sha256=iyK9awJs6ST8fc_S3lNPye6It0DxSNp1UIrAUKPJ1h4,14776
429
429
  nat/tool/code_execution/utils.py,sha256=__W-T1kaphFKYSc2AydQW8lCdvD7zAccarvs7XVFTtI,4194
@@ -470,10 +470,10 @@ nat/utils/reactive/base/observer_base.py,sha256=6BiQfx26EMumotJ3KoVcdmFBYR_fnAss
470
470
  nat/utils/reactive/base/subject_base.py,sha256=UQOxlkZTIeeyYmG5qLtDpNf_63Y7p-doEeUA08_R8ME,2521
471
471
  nat/utils/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
472
472
  nat/utils/settings/global_settings.py,sha256=9JaO6pxKT_Pjw6rxJRsRlFCXdVKCl_xUKU2QHZQWWNM,7294
473
- nvidia_nat-1.3.0rc5.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
474
- nvidia_nat-1.3.0rc5.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
475
- nvidia_nat-1.3.0rc5.dist-info/METADATA,sha256=nm0UvCzWa259-7OnT21duAI9yl9EqwKs3lXaxup59OA,10180
476
- nvidia_nat-1.3.0rc5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
477
- nvidia_nat-1.3.0rc5.dist-info/entry_points.txt,sha256=4jCqjyETMpyoWbCBf4GalZU8I_wbstpzwQNezdAVbbo,698
478
- nvidia_nat-1.3.0rc5.dist-info/top_level.txt,sha256=lgJWLkigiVZuZ_O1nxVnD_ziYBwgpE2OStdaCduMEGc,8
479
- nvidia_nat-1.3.0rc5.dist-info/RECORD,,
473
+ nvidia_nat-1.3.1.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
474
+ nvidia_nat-1.3.1.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
475
+ nvidia_nat-1.3.1.dist-info/METADATA,sha256=ruhwwSGFasQJ8s7Mai4F3ziiq5VQzCf_d9H7evi4wVA,10308
476
+ nvidia_nat-1.3.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
477
+ nvidia_nat-1.3.1.dist-info/entry_points.txt,sha256=4jCqjyETMpyoWbCBf4GalZU8I_wbstpzwQNezdAVbbo,698
478
+ nvidia_nat-1.3.1.dist-info/top_level.txt,sha256=lgJWLkigiVZuZ_O1nxVnD_ziYBwgpE2OStdaCduMEGc,8
479
+ nvidia_nat-1.3.1.dist-info/RECORD,,