nvidia-nat 1.3.0a20250827__py3-none-any.whl → 1.3.0a20250829__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.
Files changed (87) hide show
  1. nat/agent/base.py +12 -7
  2. nat/agent/dual_node.py +7 -2
  3. nat/agent/react_agent/agent.py +15 -14
  4. nat/agent/react_agent/register.py +5 -1
  5. nat/agent/rewoo_agent/agent.py +23 -32
  6. nat/agent/rewoo_agent/register.py +8 -4
  7. nat/agent/tool_calling_agent/agent.py +15 -20
  8. nat/agent/tool_calling_agent/register.py +6 -2
  9. nat/builder/context.py +7 -2
  10. nat/builder/eval_builder.py +2 -2
  11. nat/builder/function.py +8 -8
  12. nat/builder/workflow_builder.py +21 -24
  13. nat/cli/cli_utils/config_override.py +1 -1
  14. nat/cli/commands/info/list_channels.py +1 -1
  15. nat/cli/commands/object_store/__init__.py +14 -0
  16. nat/cli/commands/object_store/object_store.py +227 -0
  17. nat/cli/commands/registry/publish.py +2 -2
  18. nat/cli/commands/registry/pull.py +2 -2
  19. nat/cli/commands/registry/remove.py +2 -2
  20. nat/cli/commands/registry/search.py +1 -1
  21. nat/cli/commands/start.py +1 -1
  22. nat/cli/commands/uninstall.py +1 -1
  23. nat/cli/commands/workflow/workflow_commands.py +4 -4
  24. nat/cli/entrypoint.py +3 -1
  25. nat/data_models/discovery_metadata.py +4 -4
  26. nat/data_models/gated_field_mixin.py +12 -14
  27. nat/data_models/temperature_mixin.py +1 -1
  28. nat/data_models/thinking_mixin.py +68 -0
  29. nat/data_models/top_p_mixin.py +1 -1
  30. nat/eval/evaluate.py +6 -6
  31. nat/eval/intermediate_step_adapter.py +1 -1
  32. nat/eval/rag_evaluator/evaluate.py +2 -2
  33. nat/eval/rag_evaluator/register.py +1 -1
  34. nat/eval/remote_workflow.py +3 -3
  35. nat/eval/swe_bench_evaluator/evaluate.py +5 -5
  36. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  37. nat/eval/tunable_rag_evaluator/evaluate.py +3 -3
  38. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +2 -2
  39. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  40. nat/front_ends/fastapi/fastapi_front_end_plugin.py +1 -1
  41. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +3 -3
  42. nat/front_ends/fastapi/message_handler.py +2 -2
  43. nat/front_ends/fastapi/message_validator.py +8 -10
  44. nat/front_ends/fastapi/response_helpers.py +4 -4
  45. nat/front_ends/fastapi/step_adaptor.py +1 -1
  46. nat/llm/aws_bedrock_llm.py +10 -9
  47. nat/llm/azure_openai_llm.py +9 -1
  48. nat/llm/nim_llm.py +2 -1
  49. nat/llm/openai_llm.py +2 -1
  50. nat/llm/utils/thinking.py +215 -0
  51. nat/observability/exporter/base_exporter.py +1 -1
  52. nat/observability/exporter/processing_exporter.py +8 -9
  53. nat/observability/exporter_manager.py +5 -5
  54. nat/observability/mixin/file_mixin.py +7 -7
  55. nat/observability/processor/batching_processor.py +4 -6
  56. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  57. nat/observability/processor/processor_factory.py +70 -0
  58. nat/profiler/calc/calc_runner.py +3 -4
  59. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  60. nat/profiler/callbacks/langchain_callback_handler.py +5 -5
  61. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  62. nat/profiler/callbacks/semantic_kernel_callback_handler.py +2 -2
  63. nat/profiler/decorators/function_tracking.py +125 -0
  64. nat/profiler/profile_runner.py +1 -1
  65. nat/profiler/utils.py +1 -1
  66. nat/registry_handlers/local/local_handler.py +2 -2
  67. nat/registry_handlers/package_utils.py +1 -1
  68. nat/registry_handlers/pypi/pypi_handler.py +3 -3
  69. nat/registry_handlers/rest/rest_handler.py +4 -4
  70. nat/retriever/milvus/retriever.py +1 -1
  71. nat/retriever/nemo_retriever/retriever.py +1 -1
  72. nat/runtime/loader.py +1 -1
  73. nat/runtime/runner.py +2 -2
  74. nat/settings/global_settings.py +1 -1
  75. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
  76. nat/tool/nvidia_rag.py +1 -1
  77. nat/tool/retriever.py +3 -2
  78. nat/utils/io/yaml_tools.py +1 -1
  79. nat/utils/reactive/observer.py +2 -2
  80. nat/utils/settings/global_settings.py +2 -2
  81. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/METADATA +3 -1
  82. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/RECORD +87 -81
  83. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/WHEEL +0 -0
  84. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/entry_points.txt +0 -0
  85. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  86. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/licenses/LICENSE.md +0 -0
  87. {nvidia_nat-1.3.0a20250827.dist-info → nvidia_nat-1.3.0a20250829.dist-info}/top_level.txt +0 -0
@@ -16,7 +16,9 @@
16
16
  import functools
17
17
  import inspect
18
18
  import uuid
19
+ from collections.abc import Callable
19
20
  from typing import Any
21
+ from typing import cast
20
22
 
21
23
  from pydantic import BaseModel
22
24
 
@@ -252,3 +254,126 @@ def track_function(func: Any = None, *, metadata: dict[str, Any] | None = None):
252
254
  return result
253
255
 
254
256
  return sync_wrapper
257
+
258
+
259
+ def track_unregistered_function(func: Callable[..., Any] | None = None,
260
+ *,
261
+ name: str | None = None,
262
+ metadata: dict[str, Any] | None = None) -> Callable[..., Any]:
263
+ """
264
+ Decorator that wraps any function with scope management and automatic tracking.
265
+
266
+ - Sets active function context using the function name
267
+ - Leverages Context.push_active_function for built-in tracking
268
+ - Avoids duplicate tracking entries by relying on the library's built-in systems
269
+ - Supports sync/async functions and generators
270
+
271
+ Args:
272
+ func: The function to wrap (auto-detected when used without parentheses)
273
+ name: Custom name to use for tracking instead of func.__name__
274
+ metadata: Additional metadata to include in tracking
275
+ """
276
+
277
+ # If called with parameters: @track_unregistered_function(name="...", metadata={...})
278
+ if func is None:
279
+
280
+ def decorator_wrapper(actual_func: Callable[..., Any]) -> Callable[..., Any]:
281
+ # Cast to ensure type checker understands this returns a callable
282
+ return cast(Callable[..., Any], track_unregistered_function(actual_func, name=name, metadata=metadata))
283
+
284
+ return decorator_wrapper
285
+
286
+ # Direct decoration: @track_unregistered_function or recursive call with actual function
287
+ function_name: str = name if name else func.__name__
288
+
289
+ # --- Validate metadata ---
290
+ if metadata is not None:
291
+ if not isinstance(metadata, dict):
292
+ raise TypeError("metadata must be a dict[str, Any].")
293
+ if any(not isinstance(k, str) for k in metadata.keys()):
294
+ raise TypeError("All metadata keys must be strings.")
295
+
296
+ trace_metadata = TraceMetadata(provided_metadata=metadata)
297
+
298
+ # --- Now detect the function type and wrap accordingly ---
299
+ if inspect.isasyncgenfunction(func):
300
+ # ---------------------
301
+ # ASYNC GENERATOR
302
+ # ---------------------
303
+
304
+ @functools.wraps(func)
305
+ async def async_gen_wrapper(*args, **kwargs):
306
+ context = Context.get()
307
+ input_data = (
308
+ *args,
309
+ kwargs,
310
+ )
311
+ # Only do context management - let push_active_function handle tracking
312
+ with context.push_active_function(function_name, input_data=input_data, metadata=trace_metadata) as manager:
313
+ final_outputs = []
314
+ async for item in func(*args, **kwargs):
315
+ final_outputs.append(item)
316
+ yield item
317
+
318
+ manager.set_output(final_outputs)
319
+
320
+ return async_gen_wrapper
321
+
322
+ if inspect.iscoroutinefunction(func):
323
+ # ---------------------
324
+ # ASYNC FUNCTION
325
+ # ---------------------
326
+ @functools.wraps(func)
327
+ async def async_wrapper(*args, **kwargs):
328
+ context = Context.get()
329
+ input_data = (
330
+ *args,
331
+ kwargs,
332
+ )
333
+
334
+ # Only do context management - let push_active_function handle tracking
335
+ with context.push_active_function(function_name, input_data=input_data, metadata=trace_metadata) as manager:
336
+ result = await func(*args, **kwargs)
337
+ manager.set_output(result)
338
+ return result
339
+
340
+ return async_wrapper
341
+
342
+ if inspect.isgeneratorfunction(func):
343
+ # ---------------------
344
+ # SYNC GENERATOR
345
+ # ---------------------
346
+ @functools.wraps(func)
347
+ def sync_gen_wrapper(*args, **kwargs):
348
+ context = Context.get()
349
+ input_data = (
350
+ *args,
351
+ kwargs,
352
+ )
353
+
354
+ # Only do context management - let push_active_function handle tracking
355
+ with context.push_active_function(function_name, input_data=input_data, metadata=trace_metadata) as manager:
356
+ final_outputs = []
357
+ for item in func(*args, **kwargs):
358
+ final_outputs.append(item)
359
+ yield item
360
+
361
+ manager.set_output(final_outputs)
362
+
363
+ return sync_gen_wrapper
364
+
365
+ @functools.wraps(func)
366
+ def sync_wrapper(*args, **kwargs):
367
+ context = Context.get()
368
+ input_data = (
369
+ *args,
370
+ kwargs,
371
+ )
372
+
373
+ # Only do context management - let push_active_function handle tracking
374
+ with context.push_active_function(function_name, input_data=input_data, metadata=trace_metadata) as manager:
375
+ result = func(*args, **kwargs)
376
+ manager.set_output(result)
377
+ return result
378
+
379
+ return sync_wrapper
@@ -282,7 +282,7 @@ class ProfilerRunner:
282
282
  fitted_model = model_trainer.train(all_steps)
283
283
  logger.info("Fitted model for forecasting.")
284
284
  except Exception as e:
285
- logger.exception("Fitting model failed. %s", e, exc_info=True)
285
+ logger.exception("Fitting model failed. %s", e)
286
286
  return ProfilerResults()
287
287
 
288
288
  if self.write_output:
nat/profiler/utils.py CHANGED
@@ -175,7 +175,7 @@ def create_standardized_dataframe(requests_data: list[list[IntermediateStep]]) -
175
175
  event_type=step.event_type).model_dump(), )
176
176
 
177
177
  except Exception as e:
178
- logger.exception("Error creating standardized DataFrame: %s", e, exc_info=True)
178
+ logger.exception("Error creating standardized DataFrame: %s", e)
179
179
  return pd.DataFrame()
180
180
 
181
181
  if not all_rows:
@@ -133,7 +133,7 @@ class LocalRegistryHandler(AbstractRegistryHandler):
133
133
  "message": msg,
134
134
  "action": ActionEnum.SEARCH
135
135
  })
136
- logger.exception(validated_search_response.status.message, exc_info=True)
136
+ logger.exception(validated_search_response.status.message)
137
137
 
138
138
  yield validated_search_response
139
139
 
@@ -168,7 +168,7 @@ class LocalRegistryHandler(AbstractRegistryHandler):
168
168
  validated_remove_response = RemoveResponse(status={
169
169
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.REMOVE
170
170
  }) # type: ignore
171
- logger.exception(validated_remove_response.status.message, exc_info=True)
171
+ logger.exception(validated_remove_response.status.message)
172
172
 
173
173
  yield validated_remove_response
174
174
 
@@ -396,7 +396,7 @@ def get_transitive_dependencies(distribution_names: list[str]) -> dict[str, set[
396
396
  except importlib.metadata.PackageNotFoundError:
397
397
  pass
398
398
 
399
- logger.error("Distribution %s not found (tried common variations)", dist_name)
399
+ logger.error("Distribution %s not found (tried common variations)", dist_name, exc_info=True)
400
400
  result[dist_name] = set()
401
401
 
402
402
  return result
@@ -85,7 +85,7 @@ class PypiRegistryHandler(AbstractRegistryHandler):
85
85
  validated_publish_response = PublishResponse(status={
86
86
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.PUBLISH
87
87
  })
88
- logger.exception(validated_publish_response.status.message, exc_info=True)
88
+ logger.exception(validated_publish_response.status.message)
89
89
 
90
90
  yield validated_publish_response
91
91
 
@@ -149,7 +149,7 @@ class PypiRegistryHandler(AbstractRegistryHandler):
149
149
  validated_pull_response = PullResponse(status={
150
150
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.PULL
151
151
  })
152
- logger.exception(validated_pull_response.status.message, exc_info=True)
152
+ logger.exception(validated_pull_response.status.message)
153
153
 
154
154
  yield validated_pull_response
155
155
 
@@ -212,7 +212,7 @@ class PypiRegistryHandler(AbstractRegistryHandler):
212
212
 
213
213
  except Exception as e:
214
214
  msg = f"Error searching for artifacts: {e}"
215
- logger.exception(msg, exc_info=True)
215
+ logger.exception(msg)
216
216
  validated_search_response = SearchResponse(params=query,
217
217
  status={
218
218
  "status": StatusEnum.ERROR,
@@ -88,7 +88,7 @@ class RestRegistryHandler(AbstractRegistryHandler):
88
88
  validated_publish_response = PublishResponse(status={
89
89
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.PUBLISH
90
90
  })
91
- logger.exception(validated_publish_response.status.message, exc_info=True)
91
+ logger.exception(validated_publish_response.status.message)
92
92
 
93
93
  yield validated_publish_response
94
94
 
@@ -155,7 +155,7 @@ class RestRegistryHandler(AbstractRegistryHandler):
155
155
  validated_pull_response = PullResponse(status={
156
156
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.PULL
157
157
  })
158
- logger.exception(validated_pull_response.status.message, exc_info=True)
158
+ logger.exception(validated_pull_response.status.message)
159
159
 
160
160
  yield validated_pull_response
161
161
 
@@ -193,7 +193,7 @@ class RestRegistryHandler(AbstractRegistryHandler):
193
193
  "message": msg,
194
194
  "action": ActionEnum.SEARCH
195
195
  })
196
- logger.exception(validated_search_response.status.message, exc_info=True)
196
+ logger.exception(validated_search_response.status.message)
197
197
 
198
198
  yield validated_search_response
199
199
 
@@ -228,7 +228,7 @@ class RestRegistryHandler(AbstractRegistryHandler):
228
228
  validated_remove_response = RemoveResponse(status={
229
229
  "status": StatusEnum.ERROR, "message": msg, "action": ActionEnum.REMOVE
230
230
  })
231
- logger.exception(validated_remove_response.status.message, exc_info=True)
231
+ logger.exception(validated_remove_response.status.message)
232
232
 
233
233
  yield validated_remove_response
234
234
 
@@ -154,7 +154,7 @@ class MilvusRetriever(Retriever):
154
154
  return _wrap_milvus_results(results, content_field=self.content_field)
155
155
 
156
156
  except Exception as e:
157
- logger.exception("Exception when retrieving results from milvus for query %s: %s", query, e)
157
+ logger.error("Exception when retrieving results from milvus for query %s: %s", query, e)
158
158
  raise RetrieverError(f"Error when retrieving documents from {collection_name} for query '{query}'") from e
159
159
 
160
160
  async def _search(self,
@@ -143,7 +143,7 @@ class NemoRetriever(Retriever):
143
143
  return _wrap_nemo_results(output=output, content_field="content")
144
144
 
145
145
  except Exception as e:
146
- logger.exception("Encountered an error when retrieving results from Nemo Retriever: %s", e)
146
+ logger.error("Encountered an error when retrieving results from Nemo Retriever: %s", e)
147
147
  raise CollectionUnavailableError(
148
148
  f"Error when retrieving documents from {collection_name} for query '{query}'") from e
149
149
 
nat/runtime/loader.py CHANGED
@@ -210,7 +210,7 @@ def discover_and_register_plugins(plugin_type: PluginTypes):
210
210
  # Optionally, you can mark the plugin as unavailable or take other actions
211
211
 
212
212
  except Exception:
213
- logger.exception("An error occurred while loading plugin '%s': {e}", entry_point.name, exc_info=True)
213
+ logger.exception("An error occurred while loading plugin '%s'", entry_point.name)
214
214
 
215
215
  finally:
216
216
  count += 1
nat/runtime/runner.py CHANGED
@@ -149,7 +149,7 @@ class Runner:
149
149
 
150
150
  return result
151
151
  except Exception as e:
152
- logger.exception("Error running workflow: %s", e)
152
+ logger.error("Error running workflow: %s", e)
153
153
  event_stream = self._context_state.event_stream.get()
154
154
  if event_stream:
155
155
  event_stream.on_complete()
@@ -181,7 +181,7 @@ class Runner:
181
181
  event_stream.on_complete()
182
182
 
183
183
  except Exception as e:
184
- logger.exception("Error running workflow: %s", e)
184
+ logger.error("Error running workflow: %s", e)
185
185
  event_stream = self._context_state.event_stream.get()
186
186
  if event_stream:
187
187
  event_stream.on_complete()
@@ -231,7 +231,7 @@ class Settings(HashableBaseModel):
231
231
  return True
232
232
 
233
233
  except Exception as e:
234
- logger.exception("Unable to validate user settings configuration: %s", e, exc_info=True)
234
+ logger.exception("Unable to validate user settings configuration: %s", e)
235
235
  return False
236
236
 
237
237
  def print_channel_settings(self, channel_type: str | None = None) -> None:
@@ -121,7 +121,7 @@ def execute_code_subprocess(generated_code: str, queue):
121
121
  resource.setrlimit(resource.RLIMIT_AS, (limit, limit))
122
122
  resource.setrlimit(resource.RLIMIT_DATA, (limit, limit))
123
123
  except Exception as e:
124
- logger.error("Failed to set resource limits, PID: %s, error: %s", os.getpid(), e)
124
+ logger.exception("Failed to set resource limits, PID: %s, error: %s", os.getpid(), e)
125
125
 
126
126
  stdout_capture = StringIO()
127
127
  stderr_capture = StringIO()
nat/tool/nvidia_rag.py CHANGED
@@ -86,7 +86,7 @@ async def nvidia_rag_tool(config: NVIDIARAGToolConfig, builder: Builder):
86
86
  [await aformat_document(doc, document_prompt) for doc in docs])
87
87
  return parsed_output
88
88
  except Exception as e:
89
- logger.exception("Error while running the tool", exc_info=True)
89
+ logger.exception("Error while running the tool")
90
90
  return f"Error while running the tool: {e}"
91
91
 
92
92
  yield FunctionInfo.from_fn(
nat/tool/retriever.py CHANGED
@@ -78,8 +78,9 @@ async def retriever_tool(config: RetrieverConfig, builder: Builder):
78
78
 
79
79
  except RetrieverError as e:
80
80
  if config.raise_errors:
81
- raise e
82
- logger.warning("Retriever threw an error: %s. Returning an empty response.", e)
81
+ logger.error("Retriever threw an error: %s.", e)
82
+ raise
83
+ logger.exception("Retriever threw an error: %s. Returning an empty response.", e)
83
84
  return RetrieverOutput(results=[])
84
85
 
85
86
  yield FunctionInfo.from_fn(
@@ -85,7 +85,7 @@ def yaml_loads(config: str) -> dict:
85
85
  try:
86
86
  config_data = yaml.safe_load(stream)
87
87
  except yaml.YAMLError as e:
88
- logger.error("Error loading YAML: %s", interpolated_config_str, exc_info=True)
88
+ logger.error("Error loading YAML: %s", interpolated_config_str)
89
89
  raise ValueError(f"Error loading YAML: {e}") from e
90
90
 
91
91
  assert isinstance(config_data, dict)
@@ -64,7 +64,7 @@ class Observer(ObserverBase[_T_in_contra]):
64
64
  try:
65
65
  self._on_error(exc)
66
66
  except Exception as e:
67
- logger.exception("Error in on_error callback: %s", e, exc_info=True)
67
+ logger.exception("Error in on_error callback: %s", e)
68
68
 
69
69
  def on_complete(self) -> None:
70
70
  if not self._stopped:
@@ -73,4 +73,4 @@ class Observer(ObserverBase[_T_in_contra]):
73
73
  try:
74
74
  self._on_complete()
75
75
  except Exception as e:
76
- logger.exception("Error in on_complete callback: %s", e, exc_info=True)
76
+ logger.exception("Error in on_complete callback: %s", e)
@@ -55,7 +55,7 @@ def configure_registry_channel(config_type: RegistryHandlerBaseConfig, channel_n
55
55
  channel_registry_pre[field] = getattr(validated_field_model, field)
56
56
  break
57
57
  except Exception as e:
58
- logger.exception(e, exc_info=True)
58
+ logger.exception(e)
59
59
  logger.warning("Invalid '%s' input, input must be of type %s.", field, info.annotation)
60
60
 
61
61
  validated_model = config_type(**channel_registry_pre)
@@ -78,7 +78,7 @@ def add_channel_interative(channel_type: str) -> None:
78
78
  try:
79
79
  ChannelConfigType = registry.get_registered_channel_info_by_channel_type(channel_type=channel_type).config_type
80
80
  except Exception as e:
81
- logger.exception("Invalid channel type: %s", e, exc_info=True)
81
+ logger.exception("Invalid channel type: %s", e)
82
82
  return
83
83
 
84
84
  while (True):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat
3
- Version: 1.3.0a20250827
3
+ Version: 1.3.0a20250829
4
4
  Summary: NVIDIA NeMo Agent toolkit
5
5
  Author: NVIDIA Corporation
6
6
  Maintainer: NVIDIA Corporation
@@ -243,6 +243,8 @@ Provides-Extra: agno
243
243
  Requires-Dist: nvidia-nat-agno; extra == "agno"
244
244
  Provides-Extra: crewai
245
245
  Requires-Dist: nvidia-nat-crewai; extra == "crewai"
246
+ Provides-Extra: data-flywheel
247
+ Requires-Dist: nvidia-nat-data-flywheel; extra == "data-flywheel"
246
248
  Provides-Extra: ingestion
247
249
  Requires-Dist: nvidia-nat-ingestion; extra == "ingestion"
248
250
  Provides-Extra: langchain