nvidia-nat 1.3.0.dev2__py3-none-any.whl → 1.3.0rc1__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 (242) hide show
  1. aiq/__init__.py +2 -2
  2. nat/agent/base.py +24 -15
  3. nat/agent/dual_node.py +9 -4
  4. nat/agent/prompt_optimizer/prompt.py +68 -0
  5. nat/agent/prompt_optimizer/register.py +149 -0
  6. nat/agent/react_agent/agent.py +79 -47
  7. nat/agent/react_agent/register.py +41 -21
  8. nat/agent/reasoning_agent/reasoning_agent.py +11 -9
  9. nat/agent/register.py +1 -1
  10. nat/agent/rewoo_agent/agent.py +326 -148
  11. nat/agent/rewoo_agent/prompt.py +19 -22
  12. nat/agent/rewoo_agent/register.py +46 -26
  13. nat/agent/tool_calling_agent/agent.py +84 -28
  14. nat/agent/tool_calling_agent/register.py +51 -28
  15. nat/authentication/api_key/api_key_auth_provider.py +2 -2
  16. nat/authentication/credential_validator/bearer_token_validator.py +557 -0
  17. nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
  18. nat/authentication/interfaces.py +5 -2
  19. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +40 -20
  20. nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
  21. nat/authentication/register.py +0 -1
  22. nat/builder/builder.py +56 -24
  23. nat/builder/component_utils.py +9 -5
  24. nat/builder/context.py +46 -11
  25. nat/builder/eval_builder.py +16 -11
  26. nat/builder/framework_enum.py +1 -0
  27. nat/builder/front_end.py +1 -1
  28. nat/builder/function.py +378 -8
  29. nat/builder/function_base.py +3 -3
  30. nat/builder/function_info.py +6 -8
  31. nat/builder/user_interaction_manager.py +2 -2
  32. nat/builder/workflow.py +13 -1
  33. nat/builder/workflow_builder.py +281 -76
  34. nat/cli/cli_utils/config_override.py +2 -2
  35. nat/cli/commands/evaluate.py +1 -1
  36. nat/cli/commands/info/info.py +16 -6
  37. nat/cli/commands/info/list_channels.py +1 -1
  38. nat/cli/commands/info/list_components.py +7 -8
  39. nat/cli/commands/mcp/__init__.py +14 -0
  40. nat/cli/commands/mcp/mcp.py +986 -0
  41. nat/cli/commands/object_store/__init__.py +14 -0
  42. nat/cli/commands/object_store/object_store.py +227 -0
  43. nat/cli/commands/optimize.py +90 -0
  44. nat/cli/commands/registry/publish.py +2 -2
  45. nat/cli/commands/registry/pull.py +2 -2
  46. nat/cli/commands/registry/remove.py +2 -2
  47. nat/cli/commands/registry/search.py +15 -17
  48. nat/cli/commands/start.py +16 -5
  49. nat/cli/commands/uninstall.py +1 -1
  50. nat/cli/commands/workflow/templates/config.yml.j2 +0 -1
  51. nat/cli/commands/workflow/templates/pyproject.toml.j2 +4 -1
  52. nat/cli/commands/workflow/templates/register.py.j2 +0 -1
  53. nat/cli/commands/workflow/workflow_commands.py +9 -13
  54. nat/cli/entrypoint.py +8 -10
  55. nat/cli/register_workflow.py +38 -4
  56. nat/cli/type_registry.py +75 -6
  57. nat/control_flow/__init__.py +0 -0
  58. nat/control_flow/register.py +20 -0
  59. nat/control_flow/router_agent/__init__.py +0 -0
  60. nat/control_flow/router_agent/agent.py +329 -0
  61. nat/control_flow/router_agent/prompt.py +48 -0
  62. nat/control_flow/router_agent/register.py +91 -0
  63. nat/control_flow/sequential_executor.py +166 -0
  64. nat/data_models/agent.py +34 -0
  65. nat/data_models/api_server.py +10 -10
  66. nat/data_models/authentication.py +23 -9
  67. nat/data_models/common.py +1 -1
  68. nat/data_models/component.py +2 -0
  69. nat/data_models/component_ref.py +11 -0
  70. nat/data_models/config.py +41 -17
  71. nat/data_models/dataset_handler.py +1 -1
  72. nat/data_models/discovery_metadata.py +4 -4
  73. nat/data_models/evaluate.py +4 -1
  74. nat/data_models/function.py +34 -0
  75. nat/data_models/function_dependencies.py +14 -6
  76. nat/data_models/gated_field_mixin.py +242 -0
  77. nat/data_models/intermediate_step.py +3 -3
  78. nat/data_models/optimizable.py +119 -0
  79. nat/data_models/optimizer.py +149 -0
  80. nat/data_models/swe_bench_model.py +1 -1
  81. nat/data_models/temperature_mixin.py +44 -0
  82. nat/data_models/thinking_mixin.py +86 -0
  83. nat/data_models/top_p_mixin.py +44 -0
  84. nat/embedder/nim_embedder.py +1 -1
  85. nat/embedder/openai_embedder.py +1 -1
  86. nat/embedder/register.py +0 -1
  87. nat/eval/config.py +3 -1
  88. nat/eval/dataset_handler/dataset_handler.py +71 -7
  89. nat/eval/evaluate.py +86 -31
  90. nat/eval/evaluator/base_evaluator.py +1 -1
  91. nat/eval/evaluator/evaluator_model.py +13 -0
  92. nat/eval/intermediate_step_adapter.py +1 -1
  93. nat/eval/rag_evaluator/evaluate.py +2 -2
  94. nat/eval/rag_evaluator/register.py +3 -3
  95. nat/eval/register.py +4 -1
  96. nat/eval/remote_workflow.py +3 -3
  97. nat/eval/runtime_evaluator/__init__.py +14 -0
  98. nat/eval/runtime_evaluator/evaluate.py +123 -0
  99. nat/eval/runtime_evaluator/register.py +100 -0
  100. nat/eval/swe_bench_evaluator/evaluate.py +6 -6
  101. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  102. nat/eval/trajectory_evaluator/register.py +1 -1
  103. nat/eval/tunable_rag_evaluator/evaluate.py +4 -7
  104. nat/eval/utils/eval_trace_ctx.py +89 -0
  105. nat/eval/utils/weave_eval.py +18 -9
  106. nat/experimental/decorators/experimental_warning_decorator.py +27 -7
  107. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +7 -3
  108. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +3 -3
  109. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
  110. nat/experimental/test_time_compute/models/strategy_base.py +5 -4
  111. nat/experimental/test_time_compute/register.py +0 -1
  112. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -3
  113. nat/front_ends/console/authentication_flow_handler.py +82 -30
  114. nat/front_ends/console/console_front_end_plugin.py +8 -5
  115. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +52 -17
  116. nat/front_ends/fastapi/dask_client_mixin.py +65 -0
  117. nat/front_ends/fastapi/fastapi_front_end_config.py +36 -5
  118. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  119. nat/front_ends/fastapi/fastapi_front_end_plugin.py +135 -4
  120. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +481 -281
  121. nat/front_ends/fastapi/job_store.py +518 -99
  122. nat/front_ends/fastapi/main.py +11 -19
  123. nat/front_ends/fastapi/message_handler.py +13 -14
  124. nat/front_ends/fastapi/message_validator.py +17 -19
  125. nat/front_ends/fastapi/response_helpers.py +4 -4
  126. nat/front_ends/fastapi/step_adaptor.py +2 -2
  127. nat/front_ends/fastapi/utils.py +57 -0
  128. nat/front_ends/mcp/introspection_token_verifier.py +73 -0
  129. nat/front_ends/mcp/mcp_front_end_config.py +10 -1
  130. nat/front_ends/mcp/mcp_front_end_plugin.py +45 -13
  131. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +116 -8
  132. nat/front_ends/mcp/tool_converter.py +44 -14
  133. nat/front_ends/register.py +0 -1
  134. nat/front_ends/simple_base/simple_front_end_plugin_base.py +3 -1
  135. nat/llm/aws_bedrock_llm.py +24 -12
  136. nat/llm/azure_openai_llm.py +13 -6
  137. nat/llm/litellm_llm.py +69 -0
  138. nat/llm/nim_llm.py +20 -8
  139. nat/llm/openai_llm.py +14 -6
  140. nat/llm/register.py +4 -1
  141. nat/llm/utils/env_config_value.py +2 -3
  142. nat/llm/utils/thinking.py +215 -0
  143. nat/meta/pypi.md +9 -9
  144. nat/object_store/register.py +0 -1
  145. nat/observability/exporter/base_exporter.py +3 -3
  146. nat/observability/exporter/file_exporter.py +1 -1
  147. nat/observability/exporter/processing_exporter.py +309 -81
  148. nat/observability/exporter/span_exporter.py +1 -1
  149. nat/observability/exporter_manager.py +7 -7
  150. nat/observability/mixin/file_mixin.py +7 -7
  151. nat/observability/mixin/redaction_config_mixin.py +42 -0
  152. nat/observability/mixin/tagging_config_mixin.py +62 -0
  153. nat/observability/mixin/type_introspection_mixin.py +420 -107
  154. nat/observability/processor/batching_processor.py +5 -7
  155. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  156. nat/observability/processor/processor.py +3 -0
  157. nat/observability/processor/processor_factory.py +70 -0
  158. nat/observability/processor/redaction/__init__.py +24 -0
  159. nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
  160. nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
  161. nat/observability/processor/redaction/redaction_processor.py +177 -0
  162. nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
  163. nat/observability/processor/span_tagging_processor.py +68 -0
  164. nat/observability/register.py +6 -4
  165. nat/profiler/calc/calc_runner.py +3 -4
  166. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  167. nat/profiler/callbacks/langchain_callback_handler.py +6 -6
  168. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  169. nat/profiler/callbacks/semantic_kernel_callback_handler.py +3 -3
  170. nat/profiler/data_frame_row.py +1 -1
  171. nat/profiler/decorators/framework_wrapper.py +62 -13
  172. nat/profiler/decorators/function_tracking.py +160 -3
  173. nat/profiler/forecasting/models/forecasting_base_model.py +3 -1
  174. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
  175. nat/profiler/inference_optimization/data_models.py +3 -3
  176. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +7 -8
  177. nat/profiler/inference_optimization/token_uniqueness.py +1 -1
  178. nat/profiler/parameter_optimization/__init__.py +0 -0
  179. nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
  180. nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
  181. nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
  182. nat/profiler/parameter_optimization/parameter_selection.py +107 -0
  183. nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
  184. nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
  185. nat/profiler/parameter_optimization/update_helpers.py +66 -0
  186. nat/profiler/profile_runner.py +14 -9
  187. nat/profiler/utils.py +4 -2
  188. nat/registry_handlers/local/local_handler.py +2 -2
  189. nat/registry_handlers/package_utils.py +1 -2
  190. nat/registry_handlers/pypi/pypi_handler.py +23 -26
  191. nat/registry_handlers/register.py +3 -4
  192. nat/registry_handlers/rest/rest_handler.py +12 -13
  193. nat/retriever/milvus/retriever.py +2 -2
  194. nat/retriever/nemo_retriever/retriever.py +1 -1
  195. nat/retriever/register.py +0 -1
  196. nat/runtime/loader.py +2 -2
  197. nat/runtime/runner.py +3 -2
  198. nat/runtime/session.py +43 -8
  199. nat/settings/global_settings.py +16 -5
  200. nat/tool/chat_completion.py +5 -2
  201. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +3 -3
  202. nat/tool/datetime_tools.py +49 -9
  203. nat/tool/document_search.py +2 -2
  204. nat/tool/github_tools.py +450 -0
  205. nat/tool/nvidia_rag.py +1 -1
  206. nat/tool/register.py +2 -9
  207. nat/tool/retriever.py +3 -2
  208. nat/utils/callable_utils.py +70 -0
  209. nat/utils/data_models/schema_validator.py +3 -3
  210. nat/utils/exception_handlers/automatic_retries.py +104 -51
  211. nat/utils/exception_handlers/schemas.py +1 -1
  212. nat/utils/io/yaml_tools.py +2 -2
  213. nat/utils/log_levels.py +25 -0
  214. nat/utils/reactive/base/observable_base.py +2 -2
  215. nat/utils/reactive/base/observer_base.py +1 -1
  216. nat/utils/reactive/observable.py +2 -2
  217. nat/utils/reactive/observer.py +4 -4
  218. nat/utils/reactive/subscription.py +1 -1
  219. nat/utils/settings/global_settings.py +6 -8
  220. nat/utils/type_converter.py +4 -3
  221. nat/utils/type_utils.py +9 -5
  222. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/METADATA +42 -16
  223. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/RECORD +230 -189
  224. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/entry_points.txt +1 -0
  225. nat/cli/commands/info/list_mcp.py +0 -304
  226. nat/tool/github_tools/create_github_commit.py +0 -133
  227. nat/tool/github_tools/create_github_issue.py +0 -87
  228. nat/tool/github_tools/create_github_pr.py +0 -106
  229. nat/tool/github_tools/get_github_file.py +0 -106
  230. nat/tool/github_tools/get_github_issue.py +0 -166
  231. nat/tool/github_tools/get_github_pr.py +0 -256
  232. nat/tool/github_tools/update_github_issue.py +0 -100
  233. nat/tool/mcp/exceptions.py +0 -142
  234. nat/tool/mcp/mcp_client.py +0 -255
  235. nat/tool/mcp/mcp_tool.py +0 -96
  236. nat/utils/exception_handlers/mcp.py +0 -211
  237. /nat/{tool/github_tools → agent/prompt_optimizer}/__init__.py +0 -0
  238. /nat/{tool/mcp → authentication/credential_validator}/__init__.py +0 -0
  239. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/WHEEL +0 -0
  240. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  241. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE.md +0 -0
  242. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/top_level.txt +0 -0
@@ -22,3 +22,4 @@ class LLMFrameworkEnum(str, Enum):
22
22
  CREWAI = "crewai"
23
23
  SEMANTIC_KERNEL = "semantic_kernel"
24
24
  AGNO = "agno"
25
+ ADK = "adk"
nat/builder/front_end.py CHANGED
@@ -37,7 +37,7 @@ class FrontEndBase(typing.Generic[FrontEndConfigT], ABC):
37
37
 
38
38
  super().__init__()
39
39
 
40
- self._full_config: "Config" = full_config
40
+ self._full_config: Config = full_config
41
41
  self._front_end_config: FrontEndConfigT = typing.cast(FrontEndConfigT, full_config.general.front_end)
42
42
 
43
43
  @property
nat/builder/function.py CHANGED
@@ -14,12 +14,14 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import logging
17
+ import re
17
18
  import typing
18
19
  from abc import ABC
19
20
  from abc import abstractmethod
20
21
  from collections.abc import AsyncGenerator
21
22
  from collections.abc import Awaitable
22
23
  from collections.abc import Callable
24
+ from collections.abc import Sequence
23
25
 
24
26
  from pydantic import BaseModel
25
27
 
@@ -29,7 +31,9 @@ from nat.builder.function_base import InputT
29
31
  from nat.builder.function_base import SingleOutputT
30
32
  from nat.builder.function_base import StreamingOutputT
31
33
  from nat.builder.function_info import FunctionInfo
34
+ from nat.data_models.function import EmptyFunctionConfig
32
35
  from nat.data_models.function import FunctionBaseConfig
36
+ from nat.data_models.function import FunctionGroupBaseConfig
33
37
 
34
38
  _InvokeFnT = Callable[[InputT], Awaitable[SingleOutputT]]
35
39
  _StreamFnT = Callable[[InputT], AsyncGenerator[StreamingOutputT]]
@@ -155,8 +159,9 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
155
159
 
156
160
  return result
157
161
  except Exception as e:
158
- logger.error("Error with ainvoke in function with input: %s.", value, exc_info=True)
159
- raise e
162
+ err_msg = f"Error: {e}" if str(e).strip() else ""
163
+ logger.error("Error with ainvoke in function with input: %s. %s", value, err_msg)
164
+ raise
160
165
 
161
166
  @typing.final
162
167
  async def acall_invoke(self, *args, **kwargs):
@@ -186,14 +191,14 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
186
191
  input_obj = self.input_schema(*args, **kwargs)
187
192
 
188
193
  return await self.ainvoke(value=input_obj)
189
- except Exception as e:
194
+ except Exception:
190
195
  logger.error(
191
196
  "Error in acall_invoke() converting input to function schema. Both args and kwargs were "
192
197
  "supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
193
198
  args,
194
199
  kwargs,
195
200
  self.input_schema)
196
- raise e
201
+ raise
197
202
 
198
203
  @abstractmethod
199
204
  async def _astream(self, value: InputT) -> AsyncGenerator[StreamingOutputT]:
@@ -252,8 +257,8 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
252
257
  manager.set_output(final_output)
253
258
 
254
259
  except Exception as e:
255
- logger.error("Error with astream in function with input: %s.", value, exc_info=True)
256
- raise e
260
+ logger.error("Error with astream in function with input: %s. Error: %s", value, e)
261
+ raise
257
262
 
258
263
  @typing.final
259
264
  async def acall_stream(self, *args, **kwargs):
@@ -287,14 +292,14 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
287
292
 
288
293
  async for x in self.astream(value=input_obj):
289
294
  yield x
290
- except Exception as e:
295
+ except Exception:
291
296
  logger.error(
292
297
  "Error in acall_stream() converting input to function schema. Both args and kwargs were "
293
298
  "supplied which could not be converted to the input schema. args: %s\nkwargs: %s\nschema: %s",
294
299
  args,
295
300
  kwargs,
296
301
  self.input_schema)
297
- raise e
302
+ raise
298
303
 
299
304
 
300
305
  class LambdaFunction(Function[InputT, StreamingOutputT, SingleOutputT]):
@@ -342,3 +347,368 @@ class LambdaFunction(Function[InputT, StreamingOutputT, SingleOutputT]):
342
347
  pass
343
348
 
344
349
  return FunctionImpl(config=config, info=info, instance_name=instance_name)
350
+
351
+
352
+ class FunctionGroup:
353
+ """
354
+ A group of functions that can be used together, sharing the same configuration, context, and resources.
355
+ """
356
+
357
+ def __init__(self,
358
+ *,
359
+ config: FunctionGroupBaseConfig,
360
+ instance_name: str | None = None,
361
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None):
362
+ """
363
+ Creates a new function group.
364
+
365
+ Parameters
366
+ ----------
367
+ config : FunctionGroupBaseConfig
368
+ The configuration for the function group.
369
+ instance_name : str | None, optional
370
+ The name of the function group. If not provided, the type of the function group will be used.
371
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None, optional
372
+ A callback function to additionally filter the functions in the function group dynamically when
373
+ the functions are accessed via any accessor method.
374
+ """
375
+ self._config = config
376
+ self._instance_name = instance_name or config.type
377
+ self._functions: dict[str, Function] = dict()
378
+ self._filter_fn = filter_fn
379
+ self._per_function_filter_fn: dict[str, Callable[[str], Awaitable[bool]]] = dict()
380
+
381
+ def add_function(self,
382
+ name: str,
383
+ fn: Callable,
384
+ *,
385
+ input_schema: type[BaseModel] | None = None,
386
+ description: str | None = None,
387
+ converters: list[Callable] | None = None,
388
+ filter_fn: Callable[[str], Awaitable[bool]] | None = None):
389
+ """
390
+ Adds a function to the function group.
391
+
392
+ Parameters
393
+ ----------
394
+ name : str
395
+ The name of the function.
396
+ fn : Callable
397
+ The function to add to the function group.
398
+ input_schema : type[BaseModel] | None, optional
399
+ The input schema for the function.
400
+ description : str | None, optional
401
+ The description of the function.
402
+ converters : list[Callable] | None, optional
403
+ The converters to use for the function.
404
+ filter_fn : Callable[[str], Awaitable[bool]] | None, optional
405
+ A callback to determine if the function should be included in the function group. The
406
+ callback will be called with the function name. The callback is invoked dynamically when
407
+ the functions are accessed via any accessor method such as `get_accessible_functions`,
408
+ `get_included_functions`, `get_excluded_functions`, `get_all_functions`.
409
+
410
+ Raises
411
+ ------
412
+ ValueError
413
+ When the function name is empty or blank.
414
+ When the function name contains invalid characters.
415
+ When the function already exists in the function group.
416
+ """
417
+ if not name.strip():
418
+ raise ValueError("Function name cannot be empty or blank")
419
+ if not re.match(r"^[a-zA-Z0-9_-]+$", name):
420
+ raise ValueError(f"Function name can only contain letters, numbers, underscores, and hyphens: {name}")
421
+ if name in self._functions:
422
+ raise ValueError(f"Function {name} already exists in function group {self._instance_name}")
423
+
424
+ info = FunctionInfo.from_fn(fn, input_schema=input_schema, description=description, converters=converters)
425
+ full_name = self._get_fn_name(name)
426
+ lambda_fn = LambdaFunction.from_info(config=EmptyFunctionConfig(), info=info, instance_name=full_name)
427
+ self._functions[name] = lambda_fn
428
+ if filter_fn:
429
+ self._per_function_filter_fn[name] = filter_fn
430
+
431
+ def get_config(self) -> FunctionGroupBaseConfig:
432
+ """
433
+ Returns the configuration for the function group.
434
+
435
+ Returns
436
+ -------
437
+ FunctionGroupBaseConfig
438
+ The configuration for the function group.
439
+ """
440
+ return self._config
441
+
442
+ def _get_fn_name(self, name: str) -> str:
443
+ return f"{self._instance_name}.{name}"
444
+
445
+ async def _fn_should_be_included(self, name: str) -> bool:
446
+ if name not in self._per_function_filter_fn:
447
+ return True
448
+ return await self._per_function_filter_fn[name](name)
449
+
450
+ async def _get_all_but_excluded_functions(
451
+ self,
452
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None,
453
+ ) -> dict[str, Function]:
454
+ """
455
+ Returns a dictionary of all functions in the function group except the excluded functions.
456
+ """
457
+ missing = set(self._config.exclude) - set(self._functions.keys())
458
+ if missing:
459
+ raise ValueError(f"Unknown excluded functions: {sorted(missing)}")
460
+
461
+ if filter_fn is None:
462
+ if self._filter_fn is None:
463
+
464
+ async def identity_filter(x: Sequence[str]) -> Sequence[str]:
465
+ return x
466
+
467
+ filter_fn = identity_filter
468
+ else:
469
+ filter_fn = self._filter_fn
470
+
471
+ excluded = set(self._config.exclude)
472
+ included = set(await filter_fn(list(self._functions.keys())))
473
+
474
+ result = {}
475
+ for name in self._functions:
476
+ if name in excluded:
477
+ continue
478
+ if not await self._fn_should_be_included(name):
479
+ continue
480
+ if name not in included:
481
+ continue
482
+ result[self._get_fn_name(name)] = self._functions[name]
483
+
484
+ return result
485
+
486
+ async def get_accessible_functions(
487
+ self,
488
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None,
489
+ ) -> dict[str, Function]:
490
+ """
491
+ Returns a dictionary of all accessible functions in the function group.
492
+
493
+ First, the functions are filtered by the function group's configuration.
494
+ If the function group is configured to:
495
+ - include some functions, this will return only the included functions.
496
+ - not include or exclude any function, this will return all functions in the group.
497
+ - exclude some functions, this will return all functions in the group except the excluded functions.
498
+
499
+ Then, the functions are filtered by filter function and per-function filter functions.
500
+
501
+ Parameters
502
+ ----------
503
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None, optional
504
+ A callback function to additionally filter the functions in the function group dynamically. If not provided
505
+ then fall back to the function group's filter function. If no filter function is set for the function group
506
+ all functions will be returned.
507
+
508
+ Returns
509
+ -------
510
+ dict[str, Function]
511
+ A dictionary of all accessible functions in the function group.
512
+
513
+ Raises
514
+ ------
515
+ ValueError
516
+ When the function group is configured to include functions that are not found in the group.
517
+ """
518
+ if self._config.include:
519
+ return await self.get_included_functions(filter_fn=filter_fn)
520
+ if self._config.exclude:
521
+ return await self._get_all_but_excluded_functions(filter_fn=filter_fn)
522
+ return await self.get_all_functions(filter_fn=filter_fn)
523
+
524
+ async def get_excluded_functions(
525
+ self,
526
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None,
527
+ ) -> dict[str, Function]:
528
+ """
529
+ Returns a dictionary of all functions in the function group which are configured to be excluded or filtered
530
+ out by a filter function or per-function filter function.
531
+
532
+ Parameters
533
+ ----------
534
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None, optional
535
+ A callback function to additionally filter the functions in the function group dynamically. If not provided
536
+ then fall back to the function group's filter function. If no filter function is set for the function group
537
+ then no functions will be added to the returned dictionary.
538
+
539
+ Returns
540
+ -------
541
+ dict[str, Function]
542
+ A dictionary of all excluded functions in the function group.
543
+
544
+ Raises
545
+ ------
546
+ ValueError
547
+ When the function group is configured to exclude functions that are not found in the group.
548
+ """
549
+ missing = set(self._config.exclude) - set(self._functions.keys())
550
+ if missing:
551
+ raise ValueError(f"Unknown excluded functions: {sorted(missing)}")
552
+
553
+ if filter_fn is None:
554
+ if self._filter_fn is None:
555
+
556
+ async def identity_filter(x: Sequence[str]) -> Sequence[str]:
557
+ return x
558
+
559
+ filter_fn = identity_filter
560
+ else:
561
+ filter_fn = self._filter_fn
562
+
563
+ excluded = set(self._config.exclude)
564
+ included = set(await filter_fn(list(self._functions.keys())))
565
+
566
+ result = {}
567
+ for name in self._functions:
568
+ is_excluded = False
569
+ if name in excluded:
570
+ is_excluded = True
571
+ elif not await self._fn_should_be_included(name):
572
+ is_excluded = True
573
+ elif name not in included:
574
+ is_excluded = True
575
+
576
+ if is_excluded:
577
+ result[self._get_fn_name(name)] = self._functions[name]
578
+
579
+ return result
580
+
581
+ async def get_included_functions(
582
+ self,
583
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None,
584
+ ) -> dict[str, Function]:
585
+ """
586
+ Returns a dictionary of all functions in the function group which are:
587
+ - configured to be included and added to the global function registry
588
+ - not configured to be excluded.
589
+ - not filtered out by a filter function.
590
+
591
+ Parameters
592
+ ----------
593
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None, optional
594
+ A callback function to additionally filter the functions in the function group dynamically. If not provided
595
+ then fall back to the function group's filter function. If no filter function is set for the function group
596
+ all functions will be returned.
597
+
598
+ Returns
599
+ -------
600
+ dict[str, Function]
601
+ A dictionary of all included functions in the function group.
602
+
603
+ Raises
604
+ ------
605
+ ValueError
606
+ When the function group is configured to include functions that are not found in the group.
607
+ """
608
+ missing = set(self._config.include) - set(self._functions.keys())
609
+ if missing:
610
+ raise ValueError(f"Unknown included functions: {sorted(missing)}")
611
+
612
+ if filter_fn is None:
613
+ if self._filter_fn is None:
614
+
615
+ async def identity_filter(x: Sequence[str]) -> Sequence[str]:
616
+ return x
617
+
618
+ filter_fn = identity_filter
619
+ else:
620
+ filter_fn = self._filter_fn
621
+
622
+ included = set(await filter_fn(list(self._config.include)))
623
+ result = {}
624
+ for name in included:
625
+ if await self._fn_should_be_included(name):
626
+ result[self._get_fn_name(name)] = self._functions[name]
627
+ return result
628
+
629
+ async def get_all_functions(
630
+ self,
631
+ filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None = None,
632
+ ) -> dict[str, Function]:
633
+ """
634
+ Returns a dictionary of all functions in the function group, regardless if they are included or excluded.
635
+
636
+ If a filter function has been set, the returned functions will additionally be filtered by the callback.
637
+
638
+ Parameters
639
+ ----------
640
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]] | None, optional
641
+ A callback function to additionally filter the functions in the function group dynamically. If not provided
642
+ then fall back to the function group's filter function. If no filter function is set for the function group
643
+ all functions will be returned.
644
+
645
+ Returns
646
+ -------
647
+ dict[str, Function]
648
+ A dictionary of all functions in the function group.
649
+ """
650
+ if filter_fn is None:
651
+ if self._filter_fn is None:
652
+
653
+ async def identity_filter(x: Sequence[str]) -> Sequence[str]:
654
+ return x
655
+
656
+ filter_fn = identity_filter
657
+ else:
658
+ filter_fn = self._filter_fn
659
+
660
+ included = set(await filter_fn(list(self._functions.keys())))
661
+ result = {}
662
+ for name in included:
663
+ if await self._fn_should_be_included(name):
664
+ result[self._get_fn_name(name)] = self._functions[name]
665
+ return result
666
+
667
+ def set_filter_fn(self, filter_fn: Callable[[Sequence[str]], Awaitable[Sequence[str]]]):
668
+ """
669
+ Sets the filter function for the function group.
670
+
671
+ Parameters
672
+ ----------
673
+ filter_fn : Callable[[Sequence[str]], Awaitable[Sequence[str]]]
674
+ The filter function to set for the function group.
675
+ """
676
+ self._filter_fn = filter_fn
677
+
678
+ def set_per_function_filter_fn(self, name: str, filter_fn: Callable[[str], Awaitable[bool]]):
679
+ """
680
+ Sets the a per-function filter function for the a function within the function group.
681
+
682
+ Parameters
683
+ ----------
684
+ name : str
685
+ The name of the function.
686
+ filter_fn : Callable[[str], Awaitable[bool]]
687
+ The per-function filter function to set for the function group.
688
+
689
+ Raises
690
+ ------
691
+ ValueError
692
+ When the function is not found in the function group.
693
+ """
694
+ if name not in self._functions:
695
+ raise ValueError(f"Function {name} not found in function group {self._instance_name}")
696
+ self._per_function_filter_fn[name] = filter_fn
697
+
698
+ def set_instance_name(self, instance_name: str):
699
+ """
700
+ Sets the instance name for the function group.
701
+
702
+ Parameters
703
+ ----------
704
+ instance_name : str
705
+ The instance name to set for the function group.
706
+ """
707
+ self._instance_name = instance_name
708
+
709
+ @property
710
+ def instance_name(self) -> str:
711
+ """
712
+ Returns the instance name for the function group.
713
+ """
714
+ return self._instance_name
@@ -111,7 +111,7 @@ class FunctionBase(typing.Generic[InputT, StreamingOutputT, SingleOutputT], ABC)
111
111
  ValueError
112
112
  If the input type cannot be determined from the class definition
113
113
  """
114
- for base_cls in self.__class__.__orig_bases__: # pylint: disable=no-member # type: ignore
114
+ for base_cls in self.__class__.__orig_bases__:
115
115
 
116
116
  base_cls_args = typing.get_args(base_cls)
117
117
 
@@ -196,7 +196,7 @@ class FunctionBase(typing.Generic[InputT, StreamingOutputT, SingleOutputT], ABC)
196
196
  ValueError
197
197
  If the streaming output type cannot be determined from the class definition
198
198
  """
199
- for base_cls in self.__class__.__orig_bases__: # pylint: disable=no-member # type: ignore
199
+ for base_cls in self.__class__.__orig_bases__:
200
200
 
201
201
  base_cls_args = typing.get_args(base_cls)
202
202
 
@@ -269,7 +269,7 @@ class FunctionBase(typing.Generic[InputT, StreamingOutputT, SingleOutputT], ABC)
269
269
  ValueError
270
270
  If the single output type cannot be determined from the class definition
271
271
  """
272
- for base_cls in self.__class__.__orig_bases__: # pylint: disable=no-member # type: ignore
272
+ for base_cls in self.__class__.__orig_bases__:
273
273
 
274
274
  base_cls_args = typing.get_args(base_cls)
275
275
 
@@ -231,9 +231,9 @@ class FunctionDescriptor:
231
231
  else:
232
232
  annotations = [param.annotation for param in sig.parameters.values()]
233
233
 
234
- is_input_typed = all([a != sig.empty for a in annotations]) # pylint: disable=use-a-generator
234
+ is_input_typed = all([a != sig.empty for a in annotations])
235
235
 
236
- input_type = tuple[*annotations] if is_input_typed else None # noqa: syntax-error
236
+ input_type = tuple[*annotations] if is_input_typed else None
237
237
 
238
238
  # Get the base type here removing all annotations and async generators
239
239
  output_annotation_decomp = DecomposedType(sig.return_annotation).get_base_type()
@@ -372,18 +372,16 @@ class FunctionInfo:
372
372
 
373
373
  if (stream_to_single_fn is not None):
374
374
  raise ValueError("Cannot provide both single_fn and stream_to_single_fn")
375
- else:
376
- if (stream_to_single_fn is not None and stream_fn is None):
377
- raise ValueError("stream_fn must be provided if stream_to_single_fn is provided")
375
+ elif (stream_to_single_fn is not None and stream_fn is None):
376
+ raise ValueError("stream_fn must be provided if stream_to_single_fn is provided")
378
377
 
379
378
  if (stream_fn is not None):
380
379
  final_stream_fn = stream_fn
381
380
 
382
381
  if (single_to_stream_fn is not None):
383
382
  raise ValueError("Cannot provide both stream_fn and single_to_stream_fn")
384
- else:
385
- if (single_to_stream_fn is not None and single_fn is None):
386
- raise ValueError("single_fn must be provided if single_to_stream_fn is provided")
383
+ elif (single_to_stream_fn is not None and single_fn is None):
384
+ raise ValueError("single_fn must be provided if single_to_stream_fn is provided")
387
385
 
388
386
  if (single_fn is None and stream_fn is None):
389
387
  raise ValueError("At least one of single_fn or stream_fn must be provided")
@@ -61,13 +61,13 @@ class UserInteractionManager:
61
61
 
62
62
  uuid_req = str(uuid.uuid4())
63
63
  status = InteractionStatus.IN_PROGRESS
64
- timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ")
64
+ timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
65
65
  sys_human_interaction = InteractionPrompt(id=uuid_req, status=status, timestamp=timestamp, content=content)
66
66
 
67
67
  resp = await self._context_state.user_input_callback.get()(sys_human_interaction)
68
68
 
69
69
  # Rebuild a InteractionResponse object with the response
70
- timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ")
70
+ timestamp = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
71
71
  status = InteractionStatus.COMPLETED
72
72
  sys_human_interaction = InteractionResponse(id=uuid_req, status=status, timestamp=timestamp, content=resp)
73
73
 
nat/builder/workflow.py CHANGED
@@ -20,6 +20,7 @@ from typing import Any
20
20
  from nat.builder.context import ContextState
21
21
  from nat.builder.embedder import EmbedderProviderInfo
22
22
  from nat.builder.function import Function
23
+ from nat.builder.function import FunctionGroup
23
24
  from nat.builder.function_base import FunctionBase
24
25
  from nat.builder.function_base import InputT
25
26
  from nat.builder.function_base import SingleOutputT
@@ -44,6 +45,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
44
45
  config: Config,
45
46
  entry_fn: Function[InputT, StreamingOutputT, SingleOutputT],
46
47
  functions: dict[str, Function] | None = None,
48
+ function_groups: dict[str, FunctionGroup] | None = None,
47
49
  llms: dict[str, LLMProviderInfo] | None = None,
48
50
  embeddings: dict[str, EmbedderProviderInfo] | None = None,
49
51
  memory: dict[str, MemoryEditor] | None = None,
@@ -59,6 +61,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
59
61
 
60
62
  self.config = config
61
63
  self.functions = functions or {}
64
+ self.function_groups = function_groups or {}
62
65
  self.llms = llms or {}
63
66
  self.embeddings = embeddings or {}
64
67
  self.memory = memory or {}
@@ -83,6 +86,13 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
83
86
 
84
87
  return self._entry_fn.has_single_output
85
88
 
89
+ async def get_all_exporters(self) -> dict[str, BaseExporter]:
90
+ return await self.exporter_manager.get_all_exporters()
91
+
92
+ @property
93
+ def exporter_manager(self) -> ExporterManager:
94
+ return self._exporter_manager.get()
95
+
86
96
  @asynccontextmanager
87
97
  async def run(self, message: InputT):
88
98
  """
@@ -93,7 +103,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
93
103
  async with Runner(input_message=message,
94
104
  entry_fn=self._entry_fn,
95
105
  context_state=self._context_state,
96
- exporter_manager=self._exporter_manager.get()) as runner:
106
+ exporter_manager=self.exporter_manager) as runner:
97
107
 
98
108
  # The caller can `yield runner` so they can do `runner.result()` or `runner.result_stream()`
99
109
  yield runner
@@ -119,6 +129,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
119
129
  config: Config,
120
130
  entry_fn: Function[InputT, StreamingOutputT, SingleOutputT],
121
131
  functions: dict[str, Function] | None = None,
132
+ function_groups: dict[str, FunctionGroup] | None = None,
122
133
  llms: dict[str, LLMProviderInfo] | None = None,
123
134
  embeddings: dict[str, EmbedderProviderInfo] | None = None,
124
135
  memory: dict[str, MemoryEditor] | None = None,
@@ -138,6 +149,7 @@ class Workflow(FunctionBase[InputT, StreamingOutputT, SingleOutputT]):
138
149
  return WorkflowImpl(config=config,
139
150
  entry_fn=entry_fn,
140
151
  functions=functions,
152
+ function_groups=function_groups,
141
153
  llms=llms,
142
154
  embeddings=embeddings,
143
155
  memory=memory,