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.
- aiq/__init__.py +2 -2
- nat/agent/base.py +24 -15
- nat/agent/dual_node.py +9 -4
- nat/agent/prompt_optimizer/prompt.py +68 -0
- nat/agent/prompt_optimizer/register.py +149 -0
- nat/agent/react_agent/agent.py +79 -47
- nat/agent/react_agent/register.py +41 -21
- nat/agent/reasoning_agent/reasoning_agent.py +11 -9
- nat/agent/register.py +1 -1
- nat/agent/rewoo_agent/agent.py +326 -148
- nat/agent/rewoo_agent/prompt.py +19 -22
- nat/agent/rewoo_agent/register.py +46 -26
- nat/agent/tool_calling_agent/agent.py +84 -28
- nat/agent/tool_calling_agent/register.py +51 -28
- nat/authentication/api_key/api_key_auth_provider.py +2 -2
- nat/authentication/credential_validator/bearer_token_validator.py +557 -0
- nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
- nat/authentication/interfaces.py +5 -2
- nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +40 -20
- nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
- nat/authentication/register.py +0 -1
- nat/builder/builder.py +56 -24
- nat/builder/component_utils.py +9 -5
- nat/builder/context.py +46 -11
- nat/builder/eval_builder.py +16 -11
- nat/builder/framework_enum.py +1 -0
- nat/builder/front_end.py +1 -1
- nat/builder/function.py +378 -8
- nat/builder/function_base.py +3 -3
- nat/builder/function_info.py +6 -8
- nat/builder/user_interaction_manager.py +2 -2
- nat/builder/workflow.py +13 -1
- nat/builder/workflow_builder.py +281 -76
- nat/cli/cli_utils/config_override.py +2 -2
- nat/cli/commands/evaluate.py +1 -1
- nat/cli/commands/info/info.py +16 -6
- nat/cli/commands/info/list_channels.py +1 -1
- nat/cli/commands/info/list_components.py +7 -8
- nat/cli/commands/mcp/__init__.py +14 -0
- nat/cli/commands/mcp/mcp.py +986 -0
- nat/cli/commands/object_store/__init__.py +14 -0
- nat/cli/commands/object_store/object_store.py +227 -0
- nat/cli/commands/optimize.py +90 -0
- nat/cli/commands/registry/publish.py +2 -2
- nat/cli/commands/registry/pull.py +2 -2
- nat/cli/commands/registry/remove.py +2 -2
- nat/cli/commands/registry/search.py +15 -17
- nat/cli/commands/start.py +16 -5
- nat/cli/commands/uninstall.py +1 -1
- nat/cli/commands/workflow/templates/config.yml.j2 +0 -1
- nat/cli/commands/workflow/templates/pyproject.toml.j2 +4 -1
- nat/cli/commands/workflow/templates/register.py.j2 +0 -1
- nat/cli/commands/workflow/workflow_commands.py +9 -13
- nat/cli/entrypoint.py +8 -10
- nat/cli/register_workflow.py +38 -4
- nat/cli/type_registry.py +75 -6
- nat/control_flow/__init__.py +0 -0
- nat/control_flow/register.py +20 -0
- nat/control_flow/router_agent/__init__.py +0 -0
- nat/control_flow/router_agent/agent.py +329 -0
- nat/control_flow/router_agent/prompt.py +48 -0
- nat/control_flow/router_agent/register.py +91 -0
- nat/control_flow/sequential_executor.py +166 -0
- nat/data_models/agent.py +34 -0
- nat/data_models/api_server.py +10 -10
- nat/data_models/authentication.py +23 -9
- nat/data_models/common.py +1 -1
- nat/data_models/component.py +2 -0
- nat/data_models/component_ref.py +11 -0
- nat/data_models/config.py +41 -17
- nat/data_models/dataset_handler.py +1 -1
- nat/data_models/discovery_metadata.py +4 -4
- nat/data_models/evaluate.py +4 -1
- nat/data_models/function.py +34 -0
- nat/data_models/function_dependencies.py +14 -6
- nat/data_models/gated_field_mixin.py +242 -0
- nat/data_models/intermediate_step.py +3 -3
- nat/data_models/optimizable.py +119 -0
- nat/data_models/optimizer.py +149 -0
- nat/data_models/swe_bench_model.py +1 -1
- nat/data_models/temperature_mixin.py +44 -0
- nat/data_models/thinking_mixin.py +86 -0
- nat/data_models/top_p_mixin.py +44 -0
- nat/embedder/nim_embedder.py +1 -1
- nat/embedder/openai_embedder.py +1 -1
- nat/embedder/register.py +0 -1
- nat/eval/config.py +3 -1
- nat/eval/dataset_handler/dataset_handler.py +71 -7
- nat/eval/evaluate.py +86 -31
- nat/eval/evaluator/base_evaluator.py +1 -1
- nat/eval/evaluator/evaluator_model.py +13 -0
- nat/eval/intermediate_step_adapter.py +1 -1
- nat/eval/rag_evaluator/evaluate.py +2 -2
- nat/eval/rag_evaluator/register.py +3 -3
- nat/eval/register.py +4 -1
- nat/eval/remote_workflow.py +3 -3
- nat/eval/runtime_evaluator/__init__.py +14 -0
- nat/eval/runtime_evaluator/evaluate.py +123 -0
- nat/eval/runtime_evaluator/register.py +100 -0
- nat/eval/swe_bench_evaluator/evaluate.py +6 -6
- nat/eval/trajectory_evaluator/evaluate.py +1 -1
- nat/eval/trajectory_evaluator/register.py +1 -1
- nat/eval/tunable_rag_evaluator/evaluate.py +4 -7
- nat/eval/utils/eval_trace_ctx.py +89 -0
- nat/eval/utils/weave_eval.py +18 -9
- nat/experimental/decorators/experimental_warning_decorator.py +27 -7
- nat/experimental/test_time_compute/functions/plan_select_execute_function.py +7 -3
- nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +3 -3
- nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
- nat/experimental/test_time_compute/models/strategy_base.py +5 -4
- nat/experimental/test_time_compute/register.py +0 -1
- nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -3
- nat/front_ends/console/authentication_flow_handler.py +82 -30
- nat/front_ends/console/console_front_end_plugin.py +8 -5
- nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +52 -17
- nat/front_ends/fastapi/dask_client_mixin.py +65 -0
- nat/front_ends/fastapi/fastapi_front_end_config.py +36 -5
- nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +135 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +481 -281
- nat/front_ends/fastapi/job_store.py +518 -99
- nat/front_ends/fastapi/main.py +11 -19
- nat/front_ends/fastapi/message_handler.py +13 -14
- nat/front_ends/fastapi/message_validator.py +17 -19
- nat/front_ends/fastapi/response_helpers.py +4 -4
- nat/front_ends/fastapi/step_adaptor.py +2 -2
- nat/front_ends/fastapi/utils.py +57 -0
- nat/front_ends/mcp/introspection_token_verifier.py +73 -0
- nat/front_ends/mcp/mcp_front_end_config.py +10 -1
- nat/front_ends/mcp/mcp_front_end_plugin.py +45 -13
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +116 -8
- nat/front_ends/mcp/tool_converter.py +44 -14
- nat/front_ends/register.py +0 -1
- nat/front_ends/simple_base/simple_front_end_plugin_base.py +3 -1
- nat/llm/aws_bedrock_llm.py +24 -12
- nat/llm/azure_openai_llm.py +13 -6
- nat/llm/litellm_llm.py +69 -0
- nat/llm/nim_llm.py +20 -8
- nat/llm/openai_llm.py +14 -6
- nat/llm/register.py +4 -1
- nat/llm/utils/env_config_value.py +2 -3
- nat/llm/utils/thinking.py +215 -0
- nat/meta/pypi.md +9 -9
- nat/object_store/register.py +0 -1
- nat/observability/exporter/base_exporter.py +3 -3
- nat/observability/exporter/file_exporter.py +1 -1
- nat/observability/exporter/processing_exporter.py +309 -81
- nat/observability/exporter/span_exporter.py +1 -1
- nat/observability/exporter_manager.py +7 -7
- nat/observability/mixin/file_mixin.py +7 -7
- nat/observability/mixin/redaction_config_mixin.py +42 -0
- nat/observability/mixin/tagging_config_mixin.py +62 -0
- nat/observability/mixin/type_introspection_mixin.py +420 -107
- nat/observability/processor/batching_processor.py +5 -7
- nat/observability/processor/falsy_batch_filter_processor.py +55 -0
- nat/observability/processor/processor.py +3 -0
- nat/observability/processor/processor_factory.py +70 -0
- nat/observability/processor/redaction/__init__.py +24 -0
- nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
- nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
- nat/observability/processor/redaction/redaction_processor.py +177 -0
- nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
- nat/observability/processor/span_tagging_processor.py +68 -0
- nat/observability/register.py +6 -4
- nat/profiler/calc/calc_runner.py +3 -4
- nat/profiler/callbacks/agno_callback_handler.py +1 -1
- nat/profiler/callbacks/langchain_callback_handler.py +6 -6
- nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +3 -3
- nat/profiler/data_frame_row.py +1 -1
- nat/profiler/decorators/framework_wrapper.py +62 -13
- nat/profiler/decorators/function_tracking.py +160 -3
- nat/profiler/forecasting/models/forecasting_base_model.py +3 -1
- nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
- nat/profiler/inference_optimization/data_models.py +3 -3
- nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +7 -8
- nat/profiler/inference_optimization/token_uniqueness.py +1 -1
- nat/profiler/parameter_optimization/__init__.py +0 -0
- nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
- nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
- nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
- nat/profiler/parameter_optimization/parameter_selection.py +107 -0
- nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
- nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
- nat/profiler/parameter_optimization/update_helpers.py +66 -0
- nat/profiler/profile_runner.py +14 -9
- nat/profiler/utils.py +4 -2
- nat/registry_handlers/local/local_handler.py +2 -2
- nat/registry_handlers/package_utils.py +1 -2
- nat/registry_handlers/pypi/pypi_handler.py +23 -26
- nat/registry_handlers/register.py +3 -4
- nat/registry_handlers/rest/rest_handler.py +12 -13
- nat/retriever/milvus/retriever.py +2 -2
- nat/retriever/nemo_retriever/retriever.py +1 -1
- nat/retriever/register.py +0 -1
- nat/runtime/loader.py +2 -2
- nat/runtime/runner.py +3 -2
- nat/runtime/session.py +43 -8
- nat/settings/global_settings.py +16 -5
- nat/tool/chat_completion.py +5 -2
- nat/tool/code_execution/local_sandbox/local_sandbox_server.py +3 -3
- nat/tool/datetime_tools.py +49 -9
- nat/tool/document_search.py +2 -2
- nat/tool/github_tools.py +450 -0
- nat/tool/nvidia_rag.py +1 -1
- nat/tool/register.py +2 -9
- nat/tool/retriever.py +3 -2
- nat/utils/callable_utils.py +70 -0
- nat/utils/data_models/schema_validator.py +3 -3
- nat/utils/exception_handlers/automatic_retries.py +104 -51
- nat/utils/exception_handlers/schemas.py +1 -1
- nat/utils/io/yaml_tools.py +2 -2
- nat/utils/log_levels.py +25 -0
- nat/utils/reactive/base/observable_base.py +2 -2
- nat/utils/reactive/base/observer_base.py +1 -1
- nat/utils/reactive/observable.py +2 -2
- nat/utils/reactive/observer.py +4 -4
- nat/utils/reactive/subscription.py +1 -1
- nat/utils/settings/global_settings.py +6 -8
- nat/utils/type_converter.py +4 -3
- nat/utils/type_utils.py +9 -5
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/METADATA +42 -16
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/RECORD +230 -189
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/entry_points.txt +1 -0
- nat/cli/commands/info/list_mcp.py +0 -304
- nat/tool/github_tools/create_github_commit.py +0 -133
- nat/tool/github_tools/create_github_issue.py +0 -87
- nat/tool/github_tools/create_github_pr.py +0 -106
- nat/tool/github_tools/get_github_file.py +0 -106
- nat/tool/github_tools/get_github_issue.py +0 -166
- nat/tool/github_tools/get_github_pr.py +0 -256
- nat/tool/github_tools/update_github_issue.py +0 -100
- nat/tool/mcp/exceptions.py +0 -142
- nat/tool/mcp/mcp_client.py +0 -255
- nat/tool/mcp/mcp_tool.py +0 -96
- nat/utils/exception_handlers/mcp.py +0 -211
- /nat/{tool/github_tools → agent/prompt_optimizer}/__init__.py +0 -0
- /nat/{tool/mcp → authentication/credential_validator}/__init__.py +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc1.dist-info}/top_level.txt +0 -0
nat/builder/framework_enum.py
CHANGED
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:
|
|
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
|
-
|
|
159
|
-
|
|
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
|
|
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
|
|
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,
|
|
256
|
-
raise
|
|
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
|
|
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
|
|
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
|
nat/builder/function_base.py
CHANGED
|
@@ -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__:
|
|
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__:
|
|
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__:
|
|
272
|
+
for base_cls in self.__class__.__orig_bases__:
|
|
273
273
|
|
|
274
274
|
base_cls_args = typing.get_args(base_cls)
|
|
275
275
|
|
nat/builder/function_info.py
CHANGED
|
@@ -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])
|
|
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
|
|
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
|
-
|
|
376
|
-
|
|
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
|
-
|
|
385
|
-
|
|
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.
|
|
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,
|