nvidia-nat 1.3a20250819__py3-none-any.whl → 1.3.0a20250822__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 (36) hide show
  1. aiq/__init__.py +66 -0
  2. nat/agent/base.py +17 -0
  3. nat/agent/react_agent/agent.py +17 -10
  4. nat/agent/react_agent/prompt.py +4 -1
  5. nat/agent/rewoo_agent/agent.py +6 -2
  6. nat/agent/rewoo_agent/prompt.py +3 -0
  7. nat/agent/rewoo_agent/register.py +3 -2
  8. nat/agent/tool_calling_agent/agent.py +92 -21
  9. nat/agent/tool_calling_agent/register.py +8 -12
  10. nat/builder/user_interaction_manager.py +2 -2
  11. nat/builder/workflow.py +3 -0
  12. nat/cli/commands/workflow/templates/pyproject.toml.j2 +3 -0
  13. nat/cli/type_registry.py +4 -4
  14. nat/embedder/azure_openai_embedder.py +46 -0
  15. nat/embedder/openai_embedder.py +1 -2
  16. nat/embedder/register.py +1 -0
  17. nat/eval/config.py +2 -0
  18. nat/eval/evaluate.py +68 -23
  19. nat/eval/utils/eval_trace_ctx.py +89 -0
  20. nat/eval/utils/weave_eval.py +13 -6
  21. nat/llm/azure_openai_llm.py +50 -0
  22. nat/llm/register.py +1 -0
  23. nat/meta/pypi.md +9 -9
  24. nat/object_store/models.py +2 -0
  25. nat/observability/exporter/base_exporter.py +1 -1
  26. nat/profiler/callbacks/langchain_callback_handler.py +8 -1
  27. nat/settings/global_settings.py +13 -1
  28. nat/tool/datetime_tools.py +49 -9
  29. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/METADATA +17 -15
  30. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/RECORD +35 -31
  31. nvidia_nat-1.3.0a20250822.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
  32. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/top_level.txt +1 -0
  33. nvidia_nat-1.3a20250819.dist-info/licenses/LICENSE-3rd-party.txt +0 -3686
  34. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/WHEEL +0 -0
  35. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/entry_points.txt +0 -0
  36. {nvidia_nat-1.3a20250819.dist-info → nvidia_nat-1.3.0a20250822.dist-info}/licenses/LICENSE.md +0 -0
nat/eval/evaluate.py CHANGED
@@ -63,7 +63,16 @@ class EvaluationRun: # pylint: disable=too-many-public-methods
63
63
 
64
64
  # Helpers
65
65
  self.intermediate_step_adapter: IntermediateStepAdapter = IntermediateStepAdapter()
66
- self.weave_eval: WeaveEvaluationIntegration = WeaveEvaluationIntegration()
66
+
67
+ # Create evaluation trace context
68
+ try:
69
+ from nat.eval.utils.eval_trace_ctx import WeaveEvalTraceContext
70
+ self.eval_trace_context = WeaveEvalTraceContext()
71
+ except Exception:
72
+ from nat.eval.utils.eval_trace_ctx import EvalTraceContext
73
+ self.eval_trace_context = EvalTraceContext()
74
+
75
+ self.weave_eval: WeaveEvaluationIntegration = WeaveEvaluationIntegration(self.eval_trace_context)
67
76
  # Metadata
68
77
  self.eval_input: EvalInput | None = None
69
78
  self.workflow_interrupted: bool = False
@@ -401,6 +410,33 @@ class EvaluationRun: # pylint: disable=too-many-public-methods
401
410
 
402
411
  return workflow_type
403
412
 
413
+ async def wait_for_all_export_tasks_local(self, session_manager: SessionManager, timeout: float) -> None:
414
+ """Wait for all trace export tasks to complete for local workflows.
415
+
416
+ This only works for local workflows where we have direct access to the
417
+ SessionManager and its underlying workflow with exporter manager.
418
+ """
419
+ try:
420
+ workflow = session_manager.workflow
421
+ all_exporters = await workflow.get_all_exporters()
422
+ if not all_exporters:
423
+ logger.debug("No exporters to wait for")
424
+ return
425
+
426
+ logger.info("Waiting for export tasks from %d local exporters (timeout: %ds)", len(all_exporters), timeout)
427
+
428
+ for name, exporter in all_exporters.items():
429
+ try:
430
+ await exporter.wait_for_tasks(timeout=timeout)
431
+ logger.info("Export tasks completed for exporter: %s", name)
432
+ except Exception as e:
433
+ logger.warning("Error waiting for export tasks from %s: %s", name, e)
434
+
435
+ logger.info("All local export task waiting completed")
436
+
437
+ except Exception as e:
438
+ logger.warning("Failed to wait for local export tasks: %s", e)
439
+
404
440
  async def run_and_evaluate(self,
405
441
  session_manager: SessionManager | None = None,
406
442
  job_id: str | None = None) -> EvaluationRunOutput:
@@ -442,11 +478,13 @@ class EvaluationRun: # pylint: disable=too-many-public-methods
442
478
  dataset_config = self.eval_config.general.dataset # Currently only one dataset is supported
443
479
  if not dataset_config:
444
480
  logger.info("No dataset found, nothing to evaluate")
445
- return EvaluationRunOutput(
446
- workflow_output_file=self.workflow_output_file,
447
- evaluator_output_files=self.evaluator_output_files,
448
- workflow_interrupted=self.workflow_interrupted,
449
- )
481
+ return EvaluationRunOutput(workflow_output_file=self.workflow_output_file,
482
+ evaluator_output_files=self.evaluator_output_files,
483
+ workflow_interrupted=self.workflow_interrupted,
484
+ eval_input=EvalInput(eval_input_items=[]),
485
+ evaluation_results=[],
486
+ usage_stats=UsageStats(),
487
+ profiler_results=ProfilerResults())
450
488
 
451
489
  dataset_handler = DatasetHandler(dataset_config=dataset_config,
452
490
  reps=self.config.reps,
@@ -456,11 +494,13 @@ class EvaluationRun: # pylint: disable=too-many-public-methods
456
494
  self.eval_input = dataset_handler.get_eval_input_from_dataset(self.config.dataset)
457
495
  if not self.eval_input.eval_input_items:
458
496
  logger.info("Dataset is empty. Nothing to evaluate.")
459
- return EvaluationRunOutput(
460
- workflow_output_file=self.workflow_output_file,
461
- evaluator_output_files=self.evaluator_output_files,
462
- workflow_interrupted=self.workflow_interrupted,
463
- )
497
+ return EvaluationRunOutput(workflow_output_file=self.workflow_output_file,
498
+ evaluator_output_files=self.evaluator_output_files,
499
+ workflow_interrupted=self.workflow_interrupted,
500
+ eval_input=self.eval_input,
501
+ evaluation_results=self.evaluation_results,
502
+ usage_stats=self.usage_stats,
503
+ profiler_results=ProfilerResults())
464
504
 
465
505
  # Run workflow and evaluate
466
506
  async with WorkflowEvalBuilder.from_config(config=config) as eval_workflow:
@@ -468,18 +508,23 @@ class EvaluationRun: # pylint: disable=too-many-public-methods
468
508
  self.weave_eval.initialize_logger(workflow_alias, self.eval_input, config)
469
509
 
470
510
  # Run workflow
471
- if self.config.endpoint:
472
- await self.run_workflow_remote()
473
- else:
474
- if not self.config.skip_workflow:
475
- if session_manager is None:
476
- session_manager = SessionManager(eval_workflow.build(),
477
- max_concurrency=self.eval_config.general.max_concurrency)
478
- await self.run_workflow_local(session_manager)
479
-
480
- # Evaluate
481
- evaluators = {name: eval_workflow.get_evaluator(name) for name in self.eval_config.evaluators}
482
- await self.run_evaluators(evaluators)
511
+ with self.eval_trace_context.evaluation_context():
512
+ if self.config.endpoint:
513
+ await self.run_workflow_remote()
514
+ else:
515
+ if not self.config.skip_workflow:
516
+ if session_manager is None:
517
+ session_manager = SessionManager(eval_workflow.build(),
518
+ max_concurrency=self.eval_config.general.max_concurrency)
519
+ await self.run_workflow_local(session_manager)
520
+
521
+ # Evaluate
522
+ evaluators = {name: eval_workflow.get_evaluator(name) for name in self.eval_config.evaluators}
523
+ await self.run_evaluators(evaluators)
524
+
525
+ # Wait for all trace export tasks to complete (local workflows only)
526
+ if session_manager and not self.config.endpoint:
527
+ await self.wait_for_all_export_tasks_local(session_manager, timeout=self.config.export_timeout)
483
528
 
484
529
  # Profile the workflow
485
530
  profiler_results = await self.profile_workflow()
@@ -0,0 +1,89 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+ from collections.abc import Callable
18
+ from contextlib import contextmanager
19
+ from typing import Any
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Type alias for evaluation call objects that have an optional 'id' attribute
24
+ EvalCallType = Any # Could be Weave Call object or other tracing framework objects
25
+
26
+
27
+ class EvalTraceContext:
28
+ """
29
+ Evaluation trace context manager for coordinating traces.
30
+
31
+ This class provides a framework-agnostic way to:
32
+ 1. Track evaluation calls/contexts
33
+ 2. Ensure proper parent-child relationships in traces
34
+ """
35
+
36
+ def __init__(self):
37
+ self.eval_call: EvalCallType | None = None # Store the evaluation call/context for propagation
38
+
39
+ def set_eval_call(self, eval_call: EvalCallType | None) -> None:
40
+ """Set the evaluation call/context for propagation to traces."""
41
+ self.eval_call = eval_call
42
+ if eval_call:
43
+ logger.debug("Set evaluation call context: %s", getattr(eval_call, 'id', str(eval_call)))
44
+
45
+ def get_eval_call(self) -> EvalCallType | None:
46
+ """Get the current evaluation call/context."""
47
+ return self.eval_call
48
+
49
+ @contextmanager
50
+ def evaluation_context(self):
51
+ """
52
+ Context manager that can be overridden by framework-specific implementations.
53
+ Default implementation is a no-op.
54
+ """
55
+ yield
56
+
57
+
58
+ class WeaveEvalTraceContext(EvalTraceContext):
59
+ """
60
+ Weave-specific implementation of evaluation trace context.
61
+ """
62
+
63
+ def __init__(self):
64
+ super().__init__()
65
+ self.available = False
66
+ self.set_call_stack: Callable[[list[EvalCallType]], Any] | None = None
67
+
68
+ try:
69
+ from weave.trace.context.call_context import set_call_stack
70
+ self.set_call_stack = set_call_stack
71
+ self.available = True
72
+ except ImportError:
73
+ self.available = False
74
+ logger.debug("Weave not available for trace context")
75
+
76
+ @contextmanager
77
+ def evaluation_context(self):
78
+ """Set the evaluation call as active context for Weave traces."""
79
+ if self.available and self.eval_call and self.set_call_stack:
80
+ try:
81
+ with self.set_call_stack([self.eval_call]):
82
+ logger.debug("Set Weave evaluation call context: %s",
83
+ getattr(self.eval_call, 'id', str(self.eval_call)))
84
+ yield
85
+ except Exception as e:
86
+ logger.warning("Failed to set Weave evaluation call context: %s", e)
87
+ yield
88
+ else:
89
+ yield
@@ -15,6 +15,7 @@
15
15
 
16
16
  import asyncio
17
17
  import logging
18
+ from typing import TYPE_CHECKING
18
19
  from typing import Any
19
20
 
20
21
  from nat.eval.evaluator.evaluator_model import EvalInput
@@ -24,6 +25,9 @@ from nat.eval.usage_stats import UsageStats
24
25
  from nat.eval.usage_stats import UsageStatsItem
25
26
  from nat.profiler.data_models import ProfilerResults
26
27
 
28
+ if TYPE_CHECKING:
29
+ from nat.eval.utils.eval_trace_ctx import EvalTraceContext
30
+
27
31
  logger = logging.getLogger(__name__)
28
32
 
29
33
 
@@ -32,18 +36,19 @@ class WeaveEvaluationIntegration: # pylint: disable=too-many-public-methods
32
36
  Class to handle all Weave integration functionality.
33
37
  """
34
38
 
35
- def __init__(self):
39
+ def __init__(self, eval_trace_context: "EvalTraceContext"):
36
40
  self.available = False
37
41
  self.client = None
38
42
  self.eval_logger = None
39
43
  self.pred_loggers = {}
44
+ self.eval_trace_context = eval_trace_context
40
45
 
41
46
  try:
42
47
  from weave.flow.eval_imperative import EvaluationLogger
43
48
  from weave.flow.eval_imperative import ScoreLogger
44
49
  from weave.trace.context import weave_client_context
45
- self.EvaluationLogger = EvaluationLogger
46
- self.ScoreLogger = ScoreLogger
50
+ self.evaluation_logger_cls = EvaluationLogger # pylint: disable=invalid-name
51
+ self.score_logger_cls = ScoreLogger # pylint: disable=invalid-name
47
52
  self.weave_client_context = weave_client_context
48
53
  self.available = True
49
54
  except ImportError:
@@ -89,9 +94,12 @@ class WeaveEvaluationIntegration: # pylint: disable=too-many-public-methods
89
94
  weave_dataset = self._get_weave_dataset(eval_input)
90
95
  config_dict = config.model_dump(mode="json")
91
96
  config_dict["name"] = workflow_alias
92
- self.eval_logger = self.EvaluationLogger(model=config_dict, dataset=weave_dataset)
97
+ self.eval_logger = self.evaluation_logger_cls(model=config_dict, dataset=weave_dataset)
93
98
  self.pred_loggers = {}
94
99
 
100
+ # Capture the current evaluation call for context propagation
101
+ self.eval_trace_context.set_eval_call(self.eval_logger._evaluate_call)
102
+
95
103
  return True
96
104
  except Exception as e:
97
105
  self.eval_logger = None
@@ -137,7 +145,7 @@ class WeaveEvaluationIntegration: # pylint: disable=too-many-public-methods
137
145
  await asyncio.gather(*coros)
138
146
 
139
147
  async def afinish_loggers(self):
140
- """Finish all prediction loggers."""
148
+ """Finish all prediction loggers and wait for exports."""
141
149
  if not self.eval_logger:
142
150
  return
143
151
 
@@ -157,7 +165,6 @@ class WeaveEvaluationIntegration: # pylint: disable=too-many-public-methods
157
165
  if profiler_results.workflow_runtime_metrics:
158
166
  profile_metrics["wf_runtime_p95"] = profiler_results.workflow_runtime_metrics.p95
159
167
 
160
- # TODO:get the LLM tokens from the usage stats and log them
161
168
  profile_metrics["total_runtime"] = usage_stats.total_runtime
162
169
 
163
170
  return profile_metrics
@@ -0,0 +1,50 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from pydantic import AliasChoices
17
+ from pydantic import ConfigDict
18
+ from pydantic import Field
19
+
20
+ from nat.builder.builder import Builder
21
+ from nat.builder.llm import LLMProviderInfo
22
+ from nat.cli.register_workflow import register_llm_provider
23
+ from nat.data_models.llm import LLMBaseConfig
24
+ from nat.data_models.retry_mixin import RetryMixin
25
+
26
+
27
+ class AzureOpenAIModelConfig(LLMBaseConfig, RetryMixin, name="azure_openai"):
28
+ """An Azure OpenAI LLM provider to be used with an LLM client."""
29
+
30
+ model_config = ConfigDict(protected_namespaces=(), extra="allow")
31
+
32
+ api_key: str | None = Field(default=None, description="Azure OpenAI API key to interact with hosted model.")
33
+ api_version: str = Field(default="2025-04-01-preview", description="Azure OpenAI API version.")
34
+ azure_endpoint: str | None = Field(validation_alias=AliasChoices("azure_endpoint", "base_url"),
35
+ serialization_alias="azure_endpoint",
36
+ default=None,
37
+ description="Base URL for the hosted model.")
38
+ azure_deployment: str = Field(validation_alias=AliasChoices("azure_deployment", "model_name", "model"),
39
+ serialization_alias="azure_deployment",
40
+ description="The Azure OpenAI hosted model/deployment name.")
41
+ temperature: float = Field(default=0.0, description="Sampling temperature in [0, 1].")
42
+ top_p: float = Field(default=1.0, description="Top-p for distribution sampling.")
43
+ seed: int | None = Field(default=None, description="Random seed to set for generation.")
44
+ max_retries: int = Field(default=10, description="The max number of retries for the request.")
45
+
46
+
47
+ @register_llm_provider(config_type=AzureOpenAIModelConfig)
48
+ async def azure_openai_llm(config: AzureOpenAIModelConfig, _builder: Builder):
49
+
50
+ yield LLMProviderInfo(config=config, description="An Azure OpenAI model for use with an LLM client.")
nat/llm/register.py CHANGED
@@ -19,5 +19,6 @@
19
19
 
20
20
  # Import any providers which need to be automatically registered here
21
21
  from . import aws_bedrock_llm
22
+ from . import azure_openai_llm
22
23
  from . import nim_llm
23
24
  from . import openai_llm
nat/meta/pypi.md CHANGED
@@ -23,19 +23,19 @@ NeMo Agent toolkit is a flexible library designed to seamlessly integrate your e
23
23
 
24
24
  ## Key Features
25
25
 
26
- - [**Framework Agnostic:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/extend/plugins.html) Works with any agentic framework, so you can use your current technology stack without replatforming.
27
- - [**Reusability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/extend/sharing-components.html) Every agent, tool, or workflow can be combined and repurposed, allowing developers to leverage existing work in new scenarios.
28
- - [**Rapid Development:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/tutorials/index.html) Start with a pre-built agent, tool, or workflow, and customize it to your needs.
29
- - [**Profiling:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/profiler.html) Profile entire workflows down to the tool and agent level, track input/output tokens and timings, and identify bottlenecks.
30
- - [**Observability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-phoenix.html) Monitor and debug your workflows with any OpenTelemetry-compatible observability tool, with examples using [Phoenix](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-phoenix.html) and [W&B Weave](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-weave.html).
31
- - [**Evaluation System:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/evaluate.html) Validate and maintain accuracy of agentic workflows with built-in evaluation tools.
32
- - [**User Interface:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/quick-start/launching-ui.html) Use the NeMo Agent toolkit UI chat interface to interact with your agents, visualize output, and debug workflows.
33
- - [**MCP Compatibility**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/mcp/mcp-client.html) Compatible with Model Context Protocol (MCP), allowing tools served by MCP Servers to be used as NeMo Agent toolkit functions.
26
+ - [**Framework Agnostic:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/extend/plugins.html) Works with any agentic framework, so you can use your current technology stack without replatforming.
27
+ - [**Reusability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/extend/sharing-components.html) Every agent, tool, or workflow can be combined and repurposed, allowing developers to leverage existing work in new scenarios.
28
+ - [**Rapid Development:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/tutorials/index.html) Start with a pre-built agent, tool, or workflow, and customize it to your needs.
29
+ - [**Profiling:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/profiler.html) Profile entire workflows down to the tool and agent level, track input/output tokens and timings, and identify bottlenecks.
30
+ - [**Observability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-phoenix.html) Monitor and debug your workflows with any OpenTelemetry-compatible observability tool, with examples using [Phoenix](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-phoenix.html) and [W&B Weave](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-weave.html).
31
+ - [**Evaluation System:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/evaluate.html) Validate and maintain accuracy of agentic workflows with built-in evaluation tools.
32
+ - [**User Interface:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/quick-start/launching-ui.html) Use the NeMo Agent toolkit UI chat interface to interact with your agents, visualize output, and debug workflows.
33
+ - [**MCP Compatibility**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/mcp/mcp-client.html) Compatible with Model Context Protocol (MCP), allowing tools served by MCP Servers to be used as NeMo Agent toolkit functions.
34
34
 
35
35
  With NeMo Agent toolkit, you can move quickly, experiment freely, and ensure reliability across all your agent-driven projects.
36
36
 
37
37
  ## Links
38
- * [Documentation](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/index.html): Explore the full documentation for NeMo Agent toolkit.
38
+ * [Documentation](https://docs.nvidia.com/nemo/agent-toolkit/1.3/index.html): Explore the full documentation for NeMo Agent toolkit.
39
39
 
40
40
  ## First time user?
41
41
  If this is your first time using NeMo Agent toolkit, it is recommended to install the latest version from the [source repository](https://github.com/NVIDIA/NeMo-Agent-Toolkit?tab=readme-ov-file#quick-start) on GitHub. This package is intended for users who are familiar with NeMo Agent toolkit applications and need to add NeMo Agent toolkit as a dependency to their project.
@@ -14,6 +14,7 @@
14
14
  # limitations under the License.
15
15
 
16
16
  from pydantic import BaseModel
17
+ from pydantic import ConfigDict
17
18
  from pydantic import Field
18
19
 
19
20
 
@@ -30,6 +31,7 @@ class ObjectStoreItem(BaseModel):
30
31
  metadata : dict[str, str] | None
31
32
  Metadata providing context and utility for management operations.
32
33
  """
34
+ model_config = ConfigDict(ser_json_bytes="base64", val_json_bytes="base64")
33
35
 
34
36
  data: bytes = Field(description="The data to store in the object store.")
35
37
  content_type: str | None = Field(description="The content type of the data.", default=None)
@@ -357,7 +357,7 @@ class BaseExporter(Exporter):
357
357
  except Exception as e:
358
358
  logger.warning("Error while canceling task %s: %s", task.get_name(), e)
359
359
 
360
- async def _wait_for_tasks(self, timeout: float = 5.0):
360
+ async def wait_for_tasks(self, timeout: float = 5.0):
361
361
  """Wait for all tracked tasks to complete with a timeout.
362
362
 
363
363
  Note: This method is NOT called during normal stop() operation for performance.
@@ -223,7 +223,14 @@ class LangchainProfilerHandler(AsyncCallbackHandler, BaseProfilerCallback): # p
223
223
  except AttributeError:
224
224
  usage_metadata = {}
225
225
 
226
- llm_text_output = generation.message.content if generation else ""
226
+ if generation:
227
+ llm_text_output = generation.message.content
228
+ if "tool_calls" in generation.message.additional_kwargs:
229
+ # add tool calls if included in the output
230
+ tool_calls = generation.message.additional_kwargs['tool_calls']
231
+ llm_text_output = f"{llm_text_output}\n\nTool calls: {tool_calls}"
232
+ else:
233
+ llm_text_output = ""
227
234
 
228
235
  # update shared state behind lock
229
236
  with self._lock:
@@ -47,6 +47,12 @@ class Settings(HashableBaseModel):
47
47
  # Registry Handeler Configuration
48
48
  channels: dict[str, RegistryHandlerBaseConfig] = {}
49
49
 
50
+ # Timezone fallback behavior
51
+ # Options:
52
+ # - "utc": default to UTC
53
+ # - "system": use the system's local timezone
54
+ fallback_timezone: typing.Literal["system", "utc"] = "utc"
55
+
50
56
  _configuration_directory: typing.ClassVar[str]
51
57
  _settings_changed_hooks: typing.ClassVar[list[Callable[[], None]]] = []
52
58
  _settings_changed_hooks_active: bool = True
@@ -165,7 +171,11 @@ class Settings(HashableBaseModel):
165
171
  loaded_config = {}
166
172
  else:
167
173
  with open(file_path, mode="r", encoding="utf-8") as f:
168
- loaded_config = json.load(f)
174
+ try:
175
+ loaded_config = json.load(f)
176
+ except Exception as e:
177
+ logger.exception("Error loading configuration file %s: %s", file_path, e)
178
+ loaded_config = {}
169
179
 
170
180
  settings = Settings(**loaded_config)
171
181
  settings.set_configuration_directory(configuration_directory)
@@ -214,6 +224,8 @@ class Settings(HashableBaseModel):
214
224
  match field:
215
225
  case "channels":
216
226
  self.channels = validated_data.channels
227
+ case "fallback_timezone":
228
+ self.fallback_timezone = validated_data.fallback_timezone
217
229
  case _:
218
230
  raise ValueError(f"Encountered invalid model field: {field}")
219
231
 
@@ -13,30 +13,70 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ import datetime
17
+ import zoneinfo
18
+
19
+ from starlette.datastructures import Headers
20
+
16
21
  from nat.builder.builder import Builder
17
22
  from nat.builder.function_info import FunctionInfo
18
23
  from nat.cli.register_workflow import register_function
19
24
  from nat.data_models.function import FunctionBaseConfig
25
+ from nat.settings.global_settings import GlobalSettings
20
26
 
21
27
 
22
28
  class CurrentTimeToolConfig(FunctionBaseConfig, name="current_datetime"):
23
29
  """
24
- Simple tool which returns the current date and time in human readable format.
30
+ Simple tool which returns the current date and time in human readable format with timezone information. By default,
31
+ the timezone is in Etc/UTC. If the user provides a timezone in the header, we will use it. Timezone will be
32
+ provided in IANA zone name format. For example, "America/New_York" or "Etc/UTC".
25
33
  """
26
34
  pass
27
35
 
28
36
 
29
- @register_function(config_type=CurrentTimeToolConfig)
30
- async def current_datetime(config: CurrentTimeToolConfig, builder: Builder):
37
+ def _get_timezone_obj(headers: Headers | None) -> zoneinfo.ZoneInfo | datetime.tzinfo:
38
+ # Default to UTC
39
+ timezone_obj = zoneinfo.ZoneInfo("Etc/UTC")
40
+
41
+ if headers:
42
+ # If user has provided a timezone in the header, we will prioritize on using it
43
+ timezone_header = headers.get("x-timezone")
44
+ if timezone_header:
45
+ try:
46
+ timezone_obj = zoneinfo.ZoneInfo(timezone_header)
47
+ except Exception:
48
+ pass
49
+ else:
50
+ # Only if a timezone is not in the header, we will determine default timezone based on global settings
51
+ fallback_tz = GlobalSettings.get().fallback_timezone
52
+
53
+ if fallback_tz == "system":
54
+ # Use the system's local timezone. Avoid requiring external deps.
55
+ timezone_obj = datetime.datetime.now().astimezone().tzinfo or zoneinfo.ZoneInfo("Etc/UTC")
56
+
57
+ return timezone_obj
31
58
 
32
- import datetime
59
+
60
+ @register_function(config_type=CurrentTimeToolConfig)
61
+ async def current_datetime(_config: CurrentTimeToolConfig, _builder: Builder):
33
62
 
34
63
  async def _get_current_time(unused: str) -> str:
35
64
 
36
- now = datetime.datetime.now() # Get current time
37
- now_human_readable = now.strftime(("%Y-%m-%d %H:%M:%S"))
65
+ del unused # Unused parameter to avoid linting error
66
+
67
+ from nat.builder.context import Context
68
+ nat_context = Context.get()
69
+
70
+ headers: Headers | None = nat_context.metadata.headers
71
+
72
+ timezone_obj = _get_timezone_obj(headers)
73
+
74
+ now = datetime.datetime.now(timezone_obj)
75
+ now_machine_readable = now.strftime(("%Y-%m-%d %H:%M:%S %z"))
38
76
 
39
- return f"The current time of day is {now_human_readable}" # Format time in H:MM AM/PM format
77
+ # Returns the current time in machine readable format with timezone offset.
78
+ return f"The current time of day is {now_machine_readable}"
40
79
 
41
- yield FunctionInfo.from_fn(_get_current_time,
42
- description="Returns the current date and time in human readable format.")
80
+ yield FunctionInfo.from_fn(
81
+ _get_current_time,
82
+ description="Returns the current date and time in human readable format with timezone information.")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nvidia-nat
3
- Version: 1.3a20250819
3
+ Version: 1.3.0a20250822
4
4
  Summary: NVIDIA NeMo Agent toolkit
5
5
  Author: NVIDIA Corporation
6
6
  Maintainer: NVIDIA Corporation
@@ -237,12 +237,14 @@ Requires-Dist: rich~=13.9
237
237
  Requires-Dist: tabulate~=0.9
238
238
  Requires-Dist: uvicorn[standard]~=0.32.0
239
239
  Requires-Dist: wikipedia~=1.4
240
+ Provides-Extra: all
241
+ Requires-Dist: nvidia-nat-all; extra == "all"
240
242
  Provides-Extra: agno
241
243
  Requires-Dist: nvidia-nat-agno; extra == "agno"
242
244
  Provides-Extra: crewai
243
245
  Requires-Dist: nvidia-nat-crewai; extra == "crewai"
244
246
  Provides-Extra: ingestion
245
- Requires-Dist: lxml~=5.4; extra == "ingestion"
247
+ Requires-Dist: nvidia-nat-ingestion; extra == "ingestion"
246
248
  Provides-Extra: langchain
247
249
  Requires-Dist: nvidia-nat-langchain; extra == "langchain"
248
250
  Provides-Extra: llama-index
@@ -253,6 +255,8 @@ Provides-Extra: opentelemetry
253
255
  Requires-Dist: nvidia-nat-opentelemetry; extra == "opentelemetry"
254
256
  Provides-Extra: phoenix
255
257
  Requires-Dist: nvidia-nat-phoenix; extra == "phoenix"
258
+ Provides-Extra: profiling
259
+ Requires-Dist: nvidia-nat-profiling; extra == "profiling"
256
260
  Provides-Extra: ragaai
257
261
  Requires-Dist: nvidia-nat-ragaai; extra == "ragaai"
258
262
  Provides-Extra: mysql
@@ -278,10 +282,12 @@ Requires-Dist: nat_alert_triage_agent; extra == "examples"
278
282
  Requires-Dist: nat_automated_description_generation; extra == "examples"
279
283
  Requires-Dist: nat_email_phishing_analyzer; extra == "examples"
280
284
  Requires-Dist: nat_multi_frameworks; extra == "examples"
285
+ Requires-Dist: nat_first_search_agent; extra == "examples"
281
286
  Requires-Dist: nat_plot_charts; extra == "examples"
282
287
  Requires-Dist: nat_por_to_jiratickets; extra == "examples"
283
288
  Requires-Dist: nat_profiler_agent; extra == "examples"
284
289
  Requires-Dist: nat_redact_pii; extra == "examples"
290
+ Requires-Dist: nat_retail_sales_agent; extra == "examples"
285
291
  Requires-Dist: nat_semantic_kernel_demo; extra == "examples"
286
292
  Requires-Dist: nat_simple_auth; extra == "examples"
287
293
  Requires-Dist: nat_simple_web_query; extra == "examples"
@@ -295,10 +301,6 @@ Requires-Dist: nat_simple_calculator_hitl; extra == "examples"
295
301
  Requires-Dist: nat_simple_rag; extra == "examples"
296
302
  Requires-Dist: nat_swe_bench; extra == "examples"
297
303
  Requires-Dist: nat_user_report; extra == "examples"
298
- Provides-Extra: profiling
299
- Requires-Dist: matplotlib~=3.9; extra == "profiling"
300
- Requires-Dist: prefixspan~=0.5.2; extra == "profiling"
301
- Requires-Dist: scikit-learn~=1.6; extra == "profiling"
302
304
  Provides-Extra: gunicorn
303
305
  Requires-Dist: gunicorn~=23.0; extra == "gunicorn"
304
306
  Dynamic: license-file
@@ -328,19 +330,19 @@ NeMo Agent toolkit is a flexible library designed to seamlessly integrate your e
328
330
 
329
331
  ## Key Features
330
332
 
331
- - [**Framework Agnostic:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/extend/plugins.html) Works with any agentic framework, so you can use your current technology stack without replatforming.
332
- - [**Reusability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/extend/sharing-components.html) Every agent, tool, or workflow can be combined and repurposed, allowing developers to leverage existing work in new scenarios.
333
- - [**Rapid Development:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/tutorials/index.html) Start with a pre-built agent, tool, or workflow, and customize it to your needs.
334
- - [**Profiling:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/profiler.html) Profile entire workflows down to the tool and agent level, track input/output tokens and timings, and identify bottlenecks.
335
- - [**Observability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-phoenix.html) Monitor and debug your workflows with any OpenTelemetry-compatible observability tool, with examples using [Phoenix](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-phoenix.html) and [W&B Weave](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/observe/observe-workflow-with-weave.html).
336
- - [**Evaluation System:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/evaluate.html) Validate and maintain accuracy of agentic workflows with built-in evaluation tools.
337
- - [**User Interface:**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/quick-start/launching-ui.html) Use the NeMo Agent toolkit UI chat interface to interact with your agents, visualize output, and debug workflows.
338
- - [**MCP Compatibility**](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/workflows/mcp/mcp-client.html) Compatible with Model Context Protocol (MCP), allowing tools served by MCP Servers to be used as NeMo Agent toolkit functions.
333
+ - [**Framework Agnostic:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/extend/plugins.html) Works with any agentic framework, so you can use your current technology stack without replatforming.
334
+ - [**Reusability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/extend/sharing-components.html) Every agent, tool, or workflow can be combined and repurposed, allowing developers to leverage existing work in new scenarios.
335
+ - [**Rapid Development:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/tutorials/index.html) Start with a pre-built agent, tool, or workflow, and customize it to your needs.
336
+ - [**Profiling:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/profiler.html) Profile entire workflows down to the tool and agent level, track input/output tokens and timings, and identify bottlenecks.
337
+ - [**Observability:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-phoenix.html) Monitor and debug your workflows with any OpenTelemetry-compatible observability tool, with examples using [Phoenix](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-phoenix.html) and [W&B Weave](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/observe/observe-workflow-with-weave.html).
338
+ - [**Evaluation System:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/evaluate.html) Validate and maintain accuracy of agentic workflows with built-in evaluation tools.
339
+ - [**User Interface:**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/quick-start/launching-ui.html) Use the NeMo Agent toolkit UI chat interface to interact with your agents, visualize output, and debug workflows.
340
+ - [**MCP Compatibility**](https://docs.nvidia.com/nemo/agent-toolkit/1.3/workflows/mcp/mcp-client.html) Compatible with Model Context Protocol (MCP), allowing tools served by MCP Servers to be used as NeMo Agent toolkit functions.
339
341
 
340
342
  With NeMo Agent toolkit, you can move quickly, experiment freely, and ensure reliability across all your agent-driven projects.
341
343
 
342
344
  ## Links
343
- * [Documentation](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/index.html): Explore the full documentation for NeMo Agent toolkit.
345
+ * [Documentation](https://docs.nvidia.com/nemo/agent-toolkit/1.3/index.html): Explore the full documentation for NeMo Agent toolkit.
344
346
 
345
347
  ## First time user?
346
348
  If this is your first time using NeMo Agent toolkit, it is recommended to install the latest version from the [source repository](https://github.com/NVIDIA/NeMo-Agent-Toolkit?tab=readme-ov-file#quick-start) on GitHub. This package is intended for users who are familiar with NeMo Agent toolkit applications and need to add NeMo Agent toolkit as a dependency to their project.