nvidia-nat 1.2.0a20250813__py3-none-any.whl → 1.2.0rc6__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.
- nat/agent/react_agent/register.py +1 -1
- nat/agent/reasoning_agent/reasoning_agent.py +3 -3
- nat/agent/rewoo_agent/prompt.py +11 -12
- nat/agent/rewoo_agent/register.py +30 -32
- nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
- nat/authentication/interfaces.py +1 -1
- nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +2 -2
- nat/builder/component_utils.py +5 -5
- nat/builder/context.py +5 -5
- nat/builder/front_end.py +6 -6
- nat/builder/function_base.py +4 -4
- nat/builder/function_info.py +1 -1
- nat/builder/intermediate_step_manager.py +4 -4
- nat/builder/user_interaction_manager.py +3 -3
- nat/builder/workflow_builder.py +2 -2
- nat/cli/cli_utils/validation.py +1 -1
- nat/cli/commands/configure/channel/add.py +1 -1
- nat/cli/commands/configure/channel/channel.py +1 -3
- nat/cli/commands/configure/channel/remove.py +1 -1
- nat/cli/commands/configure/channel/update.py +1 -1
- nat/cli/commands/configure/configure.py +2 -2
- nat/cli/commands/evaluate.py +1 -1
- nat/cli/commands/info/info.py +2 -4
- nat/cli/commands/info/list_components.py +2 -2
- nat/cli/commands/info/list_mcp.py +9 -9
- nat/cli/commands/registry/publish.py +3 -3
- nat/cli/commands/registry/pull.py +3 -3
- nat/cli/commands/registry/registry.py +1 -3
- nat/cli/commands/registry/remove.py +3 -3
- nat/cli/commands/registry/search.py +3 -3
- nat/cli/commands/start.py +4 -4
- nat/cli/commands/workflow/templates/pyproject.toml.j2 +4 -4
- nat/cli/commands/workflow/workflow_commands.py +6 -6
- nat/data_models/api_server.py +38 -31
- nat/data_models/component_ref.py +9 -9
- nat/data_models/dataset_handler.py +56 -10
- nat/data_models/discovery_metadata.py +21 -50
- nat/data_models/evaluate.py +2 -2
- nat/data_models/intermediate_step.py +4 -4
- nat/embedder/register.py +0 -2
- nat/eval/dataset_handler/dataset_handler.py +118 -5
- nat/eval/evaluate.py +1 -1
- nat/eval/evaluator/evaluator_model.py +3 -3
- nat/eval/rag_evaluator/evaluate.py +1 -1
- nat/eval/swe_bench_evaluator/evaluate.py +2 -2
- nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +1 -1
- nat/experimental/test_time_compute/functions/plan_select_execute_function.py +4 -4
- nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
- nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +1 -1
- nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +1 -1
- nat/experimental/test_time_compute/search/multi_llm_planner.py +1 -1
- nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +1 -1
- nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +1 -1
- nat/front_ends/console/console_front_end_config.py +1 -1
- nat/front_ends/fastapi/fastapi_front_end_config.py +6 -7
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +4 -4
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +13 -13
- nat/front_ends/fastapi/intermediate_steps_subscriber.py +1 -1
- nat/front_ends/fastapi/main.py +6 -6
- nat/front_ends/fastapi/message_handler.py +14 -3
- nat/front_ends/fastapi/message_validator.py +6 -8
- nat/front_ends/fastapi/response_helpers.py +3 -3
- nat/front_ends/mcp/mcp_front_end_config.py +3 -2
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +4 -4
- nat/front_ends/mcp/tool_converter.py +15 -16
- nat/memory/__init__.py +2 -2
- nat/meta/pypi.md +8 -8
- nat/object_store/__init__.py +2 -2
- nat/observability/exporter/base_exporter.py +1 -1
- nat/observability/exporter/raw_exporter.py +1 -1
- nat/observability/exporter_manager.py +1 -1
- nat/profiler/callbacks/agno_callback_handler.py +4 -4
- nat/profiler/callbacks/llama_index_callback_handler.py +2 -2
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +1 -1
- nat/profiler/decorators/function_tracking.py +1 -1
- nat/profiler/forecasting/models/linear_model.py +3 -2
- nat/profiler/forecasting/models/random_forest_regressor.py +3 -2
- nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +1 -1
- nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +1 -1
- nat/profiler/profile_runner.py +1 -1
- nat/registry_handlers/local/local_handler.py +5 -5
- nat/registry_handlers/local/register_local.py +1 -1
- nat/registry_handlers/package_utils.py +17 -17
- nat/registry_handlers/pypi/pypi_handler.py +5 -5
- nat/registry_handlers/pypi/register_pypi.py +3 -3
- nat/registry_handlers/registry_handler_base.py +8 -8
- nat/registry_handlers/rest/register_rest.py +4 -4
- nat/registry_handlers/rest/rest_handler.py +6 -6
- nat/registry_handlers/schemas/package.py +3 -3
- nat/registry_handlers/schemas/publish.py +4 -4
- nat/registry_handlers/schemas/pull.py +4 -4
- nat/registry_handlers/schemas/search.py +7 -7
- nat/runtime/loader.py +21 -20
- nat/runtime/runner.py +2 -2
- nat/runtime/session.py +1 -1
- nat/settings/global_settings.py +2 -2
- nat/tool/chat_completion.py +1 -1
- nat/tool/code_execution/README.md +2 -2
- nat/tool/code_execution/test_code_execution_sandbox.py +1 -1
- nat/tool/mcp/exceptions.py +1 -1
- nat/tool/mcp/mcp_tool.py +1 -1
- nat/tool/retriever.py +3 -3
- nat/tool/server_tools.py +11 -11
- nat/utils/settings/global_settings.py +1 -1
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/METADATA +9 -9
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/RECORD +114 -116
- nat/embedder/langchain_client.py +0 -41
- nat/meta/module_to_distro.json +0 -4
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/entry_points.txt +0 -0
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.2.0a20250813.dist-info → nvidia_nat-1.2.0rc6.dist-info}/top_level.txt +0 -0
|
@@ -47,7 +47,7 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
|
|
|
47
47
|
async def run(self):
|
|
48
48
|
|
|
49
49
|
# Write the entire config to a temporary file
|
|
50
|
-
with tempfile.NamedTemporaryFile(mode="w", prefix="
|
|
50
|
+
with tempfile.NamedTemporaryFile(mode="w", prefix="nat_config", suffix=".yml", delete=False) as config_file:
|
|
51
51
|
|
|
52
52
|
# Get as dict
|
|
53
53
|
config_dict = self.full_config.model_dump(mode="json", by_alias=True, round_trip=True)
|
|
@@ -59,10 +59,10 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
|
|
|
59
59
|
config_file_name = config_file.name
|
|
60
60
|
|
|
61
61
|
# Set the config file in the environment
|
|
62
|
-
os.environ["
|
|
62
|
+
os.environ["NAT_CONFIG_FILE"] = str(config_file.name)
|
|
63
63
|
|
|
64
64
|
# Set the worker class in the environment
|
|
65
|
-
os.environ["
|
|
65
|
+
os.environ["NAT_FRONT_END_WORKER"] = self.get_worker_class_name()
|
|
66
66
|
|
|
67
67
|
try:
|
|
68
68
|
if not self.front_end_config.use_gunicorn:
|
|
@@ -70,7 +70,7 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
|
|
|
70
70
|
|
|
71
71
|
reload_excludes = ["./.*"]
|
|
72
72
|
|
|
73
|
-
uvicorn.run("
|
|
73
|
+
uvicorn.run("nat.front_ends.fastapi.main:get_app",
|
|
74
74
|
host=self.front_end_config.host,
|
|
75
75
|
port=self.front_end_config.port,
|
|
76
76
|
workers=self.front_end_config.workers,
|
|
@@ -99,7 +99,7 @@ class FastApiFrontEndPluginWorkerBase(ABC):
|
|
|
99
99
|
@asynccontextmanager
|
|
100
100
|
async def lifespan(starting_app: FastAPI):
|
|
101
101
|
|
|
102
|
-
logger.debug("Starting
|
|
102
|
+
logger.debug("Starting NAT server from process %s", os.getpid())
|
|
103
103
|
|
|
104
104
|
async with WorkflowBuilder.from_config(self.config) as builder:
|
|
105
105
|
|
|
@@ -121,20 +121,20 @@ class FastApiFrontEndPluginWorkerBase(ABC):
|
|
|
121
121
|
|
|
122
122
|
self._cleanup_tasks.clear()
|
|
123
123
|
|
|
124
|
-
logger.debug("Closing
|
|
124
|
+
logger.debug("Closing NAT server from process %s", os.getpid())
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
nat_app = FastAPI(lifespan=lifespan)
|
|
127
127
|
|
|
128
128
|
# Configure app CORS.
|
|
129
|
-
self.set_cors_config(
|
|
129
|
+
self.set_cors_config(nat_app)
|
|
130
130
|
|
|
131
|
-
@
|
|
131
|
+
@nat_app.middleware("http")
|
|
132
132
|
async def authentication_log_filter(request: Request, call_next: Callable[[Request], Awaitable[Response]]):
|
|
133
133
|
return await self._suppress_authentication_logs(request, call_next)
|
|
134
134
|
|
|
135
|
-
return
|
|
135
|
+
return nat_app
|
|
136
136
|
|
|
137
|
-
def set_cors_config(self,
|
|
137
|
+
def set_cors_config(self, nat_app: FastAPI) -> None:
|
|
138
138
|
"""
|
|
139
139
|
Set the cross origin resource sharing configuration.
|
|
140
140
|
"""
|
|
@@ -161,7 +161,7 @@ class FastApiFrontEndPluginWorkerBase(ABC):
|
|
|
161
161
|
if self.front_end_config.cors.max_age is not None:
|
|
162
162
|
cors_kwargs["max_age"] = self.front_end_config.cors.max_age
|
|
163
163
|
|
|
164
|
-
|
|
164
|
+
nat_app.add_middleware(
|
|
165
165
|
CORSMiddleware,
|
|
166
166
|
**cors_kwargs,
|
|
167
167
|
)
|
|
@@ -322,7 +322,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
322
322
|
return EvaluateResponse(job_id=job_id, status="submitted")
|
|
323
323
|
|
|
324
324
|
def translate_job_to_response(job: JobInfo) -> EvaluateStatusResponse:
|
|
325
|
-
"""Translate a JobInfo object to an
|
|
325
|
+
"""Translate a JobInfo object to an EvaluateStatusResponse."""
|
|
326
326
|
return EvaluateStatusResponse(job_id=job.job_id,
|
|
327
327
|
status=job.status,
|
|
328
328
|
config_file=str(job.config_file),
|
|
@@ -527,7 +527,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
527
527
|
GenerateSingleResponseType = workflow.single_output_schema # pylint: disable=invalid-name
|
|
528
528
|
|
|
529
529
|
# Append job_id and expiry_seconds to the input schema, this effectively makes these reserved keywords
|
|
530
|
-
# Consider prefixing these with "
|
|
530
|
+
# Consider prefixing these with "nat_" to avoid conflicts
|
|
531
531
|
class AsyncGenerateRequest(GenerateBodyType):
|
|
532
532
|
job_id: str | None = Field(default=None, description="Unique identifier for the evaluation job")
|
|
533
533
|
sync_timeout: int = Field(
|
|
@@ -820,7 +820,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
820
820
|
session_id = websocket.query_params.get("session")
|
|
821
821
|
if session_id:
|
|
822
822
|
headers = list(websocket.scope.get("headers", []))
|
|
823
|
-
cookie_header = f"
|
|
823
|
+
cookie_header = f"nat-session={session_id}"
|
|
824
824
|
|
|
825
825
|
# Check if the session cookie already exists to avoid duplicates
|
|
826
826
|
cookie_exists = False
|
|
@@ -831,8 +831,8 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
831
831
|
cookie_exists = True
|
|
832
832
|
cookie_str = value.decode()
|
|
833
833
|
|
|
834
|
-
# Check if
|
|
835
|
-
if "
|
|
834
|
+
# Check if nat-session already exists in cookies
|
|
835
|
+
if "nat-session=" in cookie_str:
|
|
836
836
|
existing_session_cookie = True
|
|
837
837
|
logger.info("WebSocket: Session cookie already present in headers (same-origin)")
|
|
838
838
|
else:
|
|
@@ -41,7 +41,7 @@ async def pull_intermediate(_q, adapter):
|
|
|
41
41
|
Synchronously called whenever the runner publishes an event.
|
|
42
42
|
We process it, then place it into the async queue (via a small async task).
|
|
43
43
|
If adapter is None, convert the raw IntermediateStep into the complete
|
|
44
|
-
|
|
44
|
+
ResponseIntermediateStep and place it into the queue.
|
|
45
45
|
"""
|
|
46
46
|
if adapter is None:
|
|
47
47
|
adapted = ResponseIntermediateStep(id=item.UUID,
|
nat/front_ends/fastapi/main.py
CHANGED
|
@@ -25,14 +25,14 @@ logger = logging.getLogger(__name__)
|
|
|
25
25
|
|
|
26
26
|
def get_app():
|
|
27
27
|
|
|
28
|
-
config_file_path = os.getenv("
|
|
29
|
-
front_end_worker_full_name = os.getenv("
|
|
28
|
+
config_file_path = os.getenv("NAT_CONFIG_FILE")
|
|
29
|
+
front_end_worker_full_name = os.getenv("NAT_FRONT_END_WORKER")
|
|
30
30
|
|
|
31
31
|
if (not config_file_path):
|
|
32
|
-
raise ValueError("Config file not found in environment variable
|
|
32
|
+
raise ValueError("Config file not found in environment variable NAT_CONFIG_FILE.")
|
|
33
33
|
|
|
34
34
|
if (not front_end_worker_full_name):
|
|
35
|
-
raise ValueError("Front end worker not found in environment variable
|
|
35
|
+
raise ValueError("Front end worker not found in environment variable NAT_FRONT_END_WORKER.")
|
|
36
36
|
|
|
37
37
|
# Try to import the front end worker class
|
|
38
38
|
try:
|
|
@@ -62,9 +62,9 @@ def get_app():
|
|
|
62
62
|
# Create an instance of the front end worker class
|
|
63
63
|
front_end_worker = front_end_worker_class(config)
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
nat_app = front_end_worker.build_app()
|
|
66
66
|
|
|
67
|
-
return
|
|
67
|
+
return nat_app
|
|
68
68
|
|
|
69
69
|
except ImportError as e:
|
|
70
70
|
raise ValueError(f"Front end worker {front_end_worker_full_name} not found.") from e
|
|
@@ -26,6 +26,7 @@ from starlette.websockets import WebSocketDisconnect
|
|
|
26
26
|
|
|
27
27
|
from nat.authentication.interfaces import FlowHandlerBase
|
|
28
28
|
from nat.data_models.api_server import ChatResponse
|
|
29
|
+
from nat.data_models.api_server import ChatResponseChunk
|
|
29
30
|
from nat.data_models.api_server import Error
|
|
30
31
|
from nat.data_models.api_server import ErrorTypes
|
|
31
32
|
from nat.data_models.api_server import ResponsePayloadOutput
|
|
@@ -39,6 +40,7 @@ from nat.data_models.api_server import WebSocketSystemIntermediateStepMessage
|
|
|
39
40
|
from nat.data_models.api_server import WebSocketSystemResponseTokenMessage
|
|
40
41
|
from nat.data_models.api_server import WebSocketUserInteractionResponseMessage
|
|
41
42
|
from nat.data_models.api_server import WebSocketUserMessage
|
|
43
|
+
from nat.data_models.api_server import WorkflowSchemaType
|
|
42
44
|
from nat.data_models.interactive import HumanPromptNotification
|
|
43
45
|
from nat.data_models.interactive import HumanResponse
|
|
44
46
|
from nat.data_models.interactive import HumanResponseNotification
|
|
@@ -67,6 +69,13 @@ class WebSocketMessageHandler:
|
|
|
67
69
|
|
|
68
70
|
self._flow_handler: FlowHandlerBase | None = None
|
|
69
71
|
|
|
72
|
+
self._schema_output_mapping: dict[str, type[BaseModel] | None] = {
|
|
73
|
+
WorkflowSchemaType.GENERATE: self._session_manager.workflow.single_output_schema,
|
|
74
|
+
WorkflowSchemaType.CHAT: ChatResponse,
|
|
75
|
+
WorkflowSchemaType.CHAT_STREAM: ChatResponseChunk,
|
|
76
|
+
WorkflowSchemaType.GENERATE_STREAM: self._session_manager.workflow.streaming_output_schema,
|
|
77
|
+
}
|
|
78
|
+
|
|
70
79
|
def set_flow_handler(self, flow_handler: FlowHandlerBase) -> None:
|
|
71
80
|
self._flow_handler = flow_handler
|
|
72
81
|
|
|
@@ -156,10 +165,12 @@ class WebSocketMessageHandler:
|
|
|
156
165
|
def _done_callback(task: asyncio.Task): # pylint: disable=unused-argument
|
|
157
166
|
self._running_workflow_task = None
|
|
158
167
|
|
|
159
|
-
# await self._process_response()
|
|
160
168
|
self._running_workflow_task = asyncio.create_task(
|
|
161
|
-
self._run_workflow(content.text,
|
|
162
|
-
|
|
169
|
+
self._run_workflow(content.text,
|
|
170
|
+
self._conversation_id,
|
|
171
|
+
result_type=self._schema_output_mapping[self._workflow_schema_type],
|
|
172
|
+
output_type=self._schema_output_mapping[
|
|
173
|
+
self._workflow_schema_type])).add_done_callback(_done_callback)
|
|
163
174
|
|
|
164
175
|
except ValueError as e:
|
|
165
176
|
logger.error("User message content not found: %s", str(e), exc_info=True)
|
|
@@ -39,7 +39,6 @@ from nat.data_models.api_server import WebSocketSystemIntermediateStepMessage
|
|
|
39
39
|
from nat.data_models.api_server import WebSocketSystemResponseTokenMessage
|
|
40
40
|
from nat.data_models.api_server import WebSocketUserInteractionResponseMessage
|
|
41
41
|
from nat.data_models.api_server import WebSocketUserMessage
|
|
42
|
-
from nat.data_models.api_server import WorkflowSchemaType
|
|
43
42
|
from nat.data_models.interactive import BinaryHumanPromptOption
|
|
44
43
|
from nat.data_models.interactive import HumanPrompt
|
|
45
44
|
from nat.data_models.interactive import HumanPromptBase
|
|
@@ -70,12 +69,7 @@ class MessageValidator:
|
|
|
70
69
|
WebSocketMessageType.USER_INTERACTION_MESSAGE: WebSocketUserInteractionResponseMessage,
|
|
71
70
|
WebSocketMessageType.ERROR_MESSAGE: Error
|
|
72
71
|
}
|
|
73
|
-
|
|
74
|
-
WorkflowSchemaType.GENERATE: ResponsePayloadOutput,
|
|
75
|
-
WorkflowSchemaType.CHAT: ChatResponse,
|
|
76
|
-
WorkflowSchemaType.CHAT_STREAM: ChatResponseChunk,
|
|
77
|
-
WorkflowSchemaType.GENERATE_STREAM: ResponseIntermediateStep,
|
|
78
|
-
}
|
|
72
|
+
|
|
79
73
|
self._message_parent_id: str = "default_id"
|
|
80
74
|
|
|
81
75
|
async def validate_message(self, message: dict[str, Any]) -> BaseModel:
|
|
@@ -139,7 +133,11 @@ class MessageValidator:
|
|
|
139
133
|
validated_message_content: BaseModel = None
|
|
140
134
|
try:
|
|
141
135
|
if (isinstance(data_model, ResponsePayloadOutput)):
|
|
142
|
-
|
|
136
|
+
if hasattr(data_model.payload, 'model_dump_json'):
|
|
137
|
+
text_content: str = data_model.payload.model_dump_json()
|
|
138
|
+
else:
|
|
139
|
+
text_content: str = str(data_model.payload)
|
|
140
|
+
validated_message_content = SystemResponseContent(text=text_content)
|
|
143
141
|
|
|
144
142
|
elif (isinstance(data_model, (ChatResponse, ChatResponseChunk))):
|
|
145
143
|
validated_message_content = SystemResponseContent(text=data_model.choices[0].message.content)
|
|
@@ -45,7 +45,7 @@ async def generate_streaming_response_as_str(payload: typing.Any,
|
|
|
45
45
|
if (isinstance(item, ResponseSerializable)):
|
|
46
46
|
yield item.get_stream_data()
|
|
47
47
|
else:
|
|
48
|
-
raise ValueError("Unexpected item type in stream. Expected
|
|
48
|
+
raise ValueError("Unexpected item type in stream. Expected ChatResponseSerializable, got: " +
|
|
49
49
|
str(type(item)))
|
|
50
50
|
|
|
51
51
|
|
|
@@ -125,7 +125,7 @@ async def generate_streaming_response_full(payload: typing.Any,
|
|
|
125
125
|
output_type: type | None = None,
|
|
126
126
|
filter_steps: str | None = None) -> AsyncGenerator[ResponseSerializable]:
|
|
127
127
|
"""
|
|
128
|
-
Similar to generate_streaming_response but provides raw
|
|
128
|
+
Similar to generate_streaming_response but provides raw ResponseIntermediateStep objects
|
|
129
129
|
without any step adaptor translations.
|
|
130
130
|
"""
|
|
131
131
|
# Parse filter_steps into a set of allowed types if provided
|
|
@@ -191,5 +191,5 @@ async def generate_streaming_response_full_as_str(payload: typing.Any,
|
|
|
191
191
|
if (isinstance(item, ResponseIntermediateStep) or isinstance(item, ResponsePayloadOutput)):
|
|
192
192
|
yield item.get_stream_data()
|
|
193
193
|
else:
|
|
194
|
-
raise ValueError("Unexpected item type in stream. Expected
|
|
194
|
+
raise ValueError("Unexpected item type in stream. Expected ChatResponseSerializable, got: " +
|
|
195
195
|
str(type(item)))
|
|
@@ -21,10 +21,11 @@ from nat.data_models.front_end import FrontEndBaseConfig
|
|
|
21
21
|
class MCPFrontEndConfig(FrontEndBaseConfig, name="mcp"):
|
|
22
22
|
"""MCP front end configuration.
|
|
23
23
|
|
|
24
|
-
A simple MCP (Modular Communication Protocol) front end for
|
|
24
|
+
A simple MCP (Modular Communication Protocol) front end for NeMo Agent toolkit.
|
|
25
25
|
"""
|
|
26
26
|
|
|
27
|
-
name: str = Field(default="
|
|
27
|
+
name: str = Field(default="NeMo Agent Toolkit MCP",
|
|
28
|
+
description="Name of the MCP server (default: NeMo Agent Toolkit MCP)")
|
|
28
29
|
host: str = Field(default="localhost", description="Host to bind the server to (default: localhost)")
|
|
29
30
|
port: int = Field(default=9901, description="Port to bind the server to (default: 9901)", ge=0, le=65535)
|
|
30
31
|
debug: bool = Field(default=False, description="Enable debug mode (default: False)")
|
|
@@ -23,7 +23,7 @@ from starlette.requests import Request
|
|
|
23
23
|
from nat.builder.function import Function
|
|
24
24
|
from nat.builder.workflow import Workflow
|
|
25
25
|
from nat.builder.workflow_builder import WorkflowBuilder
|
|
26
|
-
from nat.data_models.config import
|
|
26
|
+
from nat.data_models.config import Config
|
|
27
27
|
from nat.front_ends.mcp.mcp_front_end_config import MCPFrontEndConfig
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
@@ -32,11 +32,11 @@ logger = logging.getLogger(__name__)
|
|
|
32
32
|
class MCPFrontEndPluginWorkerBase(ABC):
|
|
33
33
|
"""Base class for MCP front end plugin workers."""
|
|
34
34
|
|
|
35
|
-
def __init__(self, config:
|
|
35
|
+
def __init__(self, config: Config):
|
|
36
36
|
"""Initialize the MCP worker with configuration.
|
|
37
37
|
|
|
38
38
|
Args:
|
|
39
|
-
config: The full
|
|
39
|
+
config: The full NAT configuration
|
|
40
40
|
"""
|
|
41
41
|
self.full_config = config
|
|
42
42
|
self.front_end_config: MCPFrontEndConfig = config.general.front_end
|
|
@@ -86,7 +86,7 @@ class MCPFrontEndPluginWorkerBase(ABC):
|
|
|
86
86
|
"""Get all functions from the workflow.
|
|
87
87
|
|
|
88
88
|
Args:
|
|
89
|
-
workflow: The
|
|
89
|
+
workflow: The NAT workflow.
|
|
90
90
|
|
|
91
91
|
Returns:
|
|
92
92
|
Dict mapping function names to Function objects.
|
|
@@ -34,27 +34,26 @@ def create_function_wrapper(
|
|
|
34
34
|
schema: type[BaseModel],
|
|
35
35
|
is_workflow: bool = False,
|
|
36
36
|
):
|
|
37
|
-
"""Create a wrapper function that exposes the actual parameters of
|
|
37
|
+
"""Create a wrapper function that exposes the actual parameters of a NAT Function as an MCP tool.
|
|
38
38
|
|
|
39
39
|
Args:
|
|
40
40
|
function_name: The name of the function/tool
|
|
41
|
-
function: The
|
|
41
|
+
function: The NAT Function object
|
|
42
42
|
schema: The input schema of the function
|
|
43
43
|
is_workflow: Whether the function is a Workflow
|
|
44
44
|
|
|
45
45
|
Returns:
|
|
46
46
|
A wrapper function suitable for registration with MCP
|
|
47
47
|
"""
|
|
48
|
-
# Check if we're dealing with
|
|
48
|
+
# Check if we're dealing with ChatRequest - special case
|
|
49
49
|
is_chat_request = False
|
|
50
50
|
|
|
51
|
-
# Check if the schema name is
|
|
52
|
-
if schema.__name__ == "
|
|
53
|
-
and "AIQChatRequest" in schema.__qualname__):
|
|
51
|
+
# Check if the schema name is ChatRequest
|
|
52
|
+
if schema.__name__ == "ChatRequest" or (hasattr(schema, "__qualname__") and "ChatRequest" in schema.__qualname__):
|
|
54
53
|
is_chat_request = True
|
|
55
|
-
logger.info("Function %s uses
|
|
54
|
+
logger.info("Function %s uses ChatRequest - creating simplified interface", function_name)
|
|
56
55
|
|
|
57
|
-
# For
|
|
56
|
+
# For ChatRequest, we'll create a simple wrapper with just a query parameter
|
|
58
57
|
parameters = [Parameter(
|
|
59
58
|
name="query",
|
|
60
59
|
kind=Parameter.KEYWORD_ONLY,
|
|
@@ -102,7 +101,7 @@ def create_function_wrapper(
|
|
|
102
101
|
await ctx.report_progress(0, 100)
|
|
103
102
|
|
|
104
103
|
try:
|
|
105
|
-
# Special handling for
|
|
104
|
+
# Special handling for ChatRequest
|
|
106
105
|
if is_chat_request:
|
|
107
106
|
from nat.data_models.api_server import ChatRequest
|
|
108
107
|
|
|
@@ -113,7 +112,7 @@ def create_function_wrapper(
|
|
|
113
112
|
# Special handling for Workflow objects
|
|
114
113
|
if is_workflow:
|
|
115
114
|
# Workflows have a run method that is an async context manager
|
|
116
|
-
# that returns
|
|
115
|
+
# that returns a Runner
|
|
117
116
|
async with function.run(chat_request) as runner:
|
|
118
117
|
# Get the result from the runner
|
|
119
118
|
result = await runner.result(to_type=str)
|
|
@@ -136,13 +135,13 @@ def create_function_wrapper(
|
|
|
136
135
|
# Call with the nested object
|
|
137
136
|
kwargs = {field_name: nested_obj}
|
|
138
137
|
|
|
139
|
-
# Call the
|
|
138
|
+
# Call the NAT function with the parameters - special handling for Workflow
|
|
140
139
|
if is_workflow:
|
|
141
140
|
# For workflow with regular input, we'll assume the first parameter is the input
|
|
142
141
|
input_value = list(kwargs.values())[0] if kwargs else ""
|
|
143
142
|
|
|
144
143
|
# Workflows have a run method that is an async context manager
|
|
145
|
-
# that returns
|
|
144
|
+
# that returns a Runner
|
|
146
145
|
async with function.run(input_value) as runner:
|
|
147
146
|
# Get the result from the runner
|
|
148
147
|
result = await runner.result(to_type=str)
|
|
@@ -180,7 +179,7 @@ def create_function_wrapper(
|
|
|
180
179
|
|
|
181
180
|
def get_function_description(function: FunctionBase) -> str:
|
|
182
181
|
"""
|
|
183
|
-
Retrieve a human-readable description for
|
|
182
|
+
Retrieve a human-readable description for a NAT function or workflow.
|
|
184
183
|
|
|
185
184
|
The description is determined using the following precedence:
|
|
186
185
|
1. If the function is a Workflow and has a 'description' attribute, use it.
|
|
@@ -189,7 +188,7 @@ def get_function_description(function: FunctionBase) -> str:
|
|
|
189
188
|
4. If the function is a regular Function, use its 'description' attribute.
|
|
190
189
|
|
|
191
190
|
Args:
|
|
192
|
-
function: The
|
|
191
|
+
function: The NAT FunctionBase instance (Function or Workflow).
|
|
193
192
|
|
|
194
193
|
Returns:
|
|
195
194
|
The best available description string for the function.
|
|
@@ -216,12 +215,12 @@ def get_function_description(function: FunctionBase) -> str:
|
|
|
216
215
|
|
|
217
216
|
|
|
218
217
|
def register_function_with_mcp(mcp: FastMCP, function_name: str, function: FunctionBase) -> None:
|
|
219
|
-
"""Register
|
|
218
|
+
"""Register a NAT Function as an MCP tool.
|
|
220
219
|
|
|
221
220
|
Args:
|
|
222
221
|
mcp: The FastMCP instance
|
|
223
222
|
function_name: The name to register the function under
|
|
224
|
-
function: The
|
|
223
|
+
function: The NAT Function to register
|
|
225
224
|
"""
|
|
226
225
|
logger.info("Registering function %s with MCP", function_name)
|
|
227
226
|
|
nat/memory/__init__.py
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
"""
|
|
16
|
-
|
|
16
|
+
NAT Memory Module
|
|
17
17
|
|
|
18
18
|
This package provides foundational classes and interfaces
|
|
19
|
-
for managing text-based memory in
|
|
19
|
+
for managing text-based memory in NAT's LLM-based agents.
|
|
20
20
|
"""
|
nat/meta/pypi.md
CHANGED
|
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
-->
|
|
17
17
|
|
|
18
|
-

|
|
19
19
|
|
|
20
20
|
# NVIDIA NeMo Agent Toolkit
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
NeMo Agent toolkit is a flexible library designed to seamlessly integrate your enterprise agents—regardless of framework—with various data sources and tools. By treating agents, tools, and agentic workflows as simple function calls, NeMo Agent toolkit enables true composability: build once and reuse anywhere.
|
|
23
23
|
|
|
24
24
|
## Key Features
|
|
25
25
|
|
|
@@ -29,16 +29,16 @@ AIQ toolkit is a flexible library designed to seamlessly integrate your enterpri
|
|
|
29
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
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
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
|
|
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
|
|
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.
|
|
34
34
|
|
|
35
|
-
With
|
|
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
|
|
38
|
+
* [Documentation](https://docs.nvidia.com/nemo/agent-toolkit/1.2.0/index.html): Explore the full documentation for NeMo Agent toolkit.
|
|
39
39
|
|
|
40
40
|
## First time user?
|
|
41
|
-
If this is your first time using
|
|
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.
|
|
42
42
|
|
|
43
43
|
## Feedback
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ We would love to hear from you! Please file an issue on [GitHub](https://github.
|
|
|
46
46
|
|
|
47
47
|
## Acknowledgements
|
|
48
48
|
|
|
49
|
-
We would like to thank the following open source projects that made
|
|
49
|
+
We would like to thank the following open source projects that made NeMo Agent toolkit possible:
|
|
50
50
|
|
|
51
51
|
- [CrewAI](https://github.com/crewAIInc/crewAI)
|
|
52
52
|
- [FastAPI](https://github.com/tiangolo/fastapi)
|
nat/object_store/__init__.py
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
"""
|
|
16
|
-
|
|
16
|
+
NAT Object Store Module
|
|
17
17
|
|
|
18
18
|
This package provides foundational classes and interfaces
|
|
19
|
-
for managing object storage in
|
|
19
|
+
for managing object storage in NAT's LLM-based agents.
|
|
20
20
|
"""
|
|
@@ -117,7 +117,7 @@ class BaseExporter(Exporter):
|
|
|
117
117
|
- This keeps observability "off the hot path" for optimal performance
|
|
118
118
|
|
|
119
119
|
Args:
|
|
120
|
-
context_state (
|
|
120
|
+
context_state (ContextState, optional): The context state to use for the exporter. Defaults to None.
|
|
121
121
|
"""
|
|
122
122
|
|
|
123
123
|
# Class-level tracking for debugging and monitoring
|
|
@@ -37,7 +37,7 @@ class RawExporter(ProcessingExporter[InputT, OutputT]):
|
|
|
37
37
|
The flow is: IntermediateStep -> [Processing Pipeline] -> OutputT -> Export
|
|
38
38
|
|
|
39
39
|
Args:
|
|
40
|
-
context_state (
|
|
40
|
+
context_state (ContextState, optional): The context state to use for the exporter. Defaults to None.
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
43
|
@abstractmethod
|
|
@@ -141,7 +141,7 @@ class ExporterManager:
|
|
|
141
141
|
expensive resources but have separate mutable state.
|
|
142
142
|
|
|
143
143
|
Args:
|
|
144
|
-
context_state (
|
|
144
|
+
context_state (ContextState | None, optional): The isolated context state for the new exporter instances.
|
|
145
145
|
If not provided, a new context state will be created.
|
|
146
146
|
|
|
147
147
|
Returns:
|
|
@@ -44,7 +44,7 @@ class AgnoProfilerHandler(BaseProfilerCallback):
|
|
|
44
44
|
- LLM Calls
|
|
45
45
|
|
|
46
46
|
to collect usage statistics (tokens, inputs, outputs, time intervals, etc.)
|
|
47
|
-
and store them in
|
|
47
|
+
and store them in NAT's usage_stats queue for subsequent analysis.
|
|
48
48
|
"""
|
|
49
49
|
|
|
50
50
|
def __init__(self) -> None:
|
|
@@ -73,8 +73,8 @@ class AgnoProfilerHandler(BaseProfilerCallback):
|
|
|
73
73
|
|
|
74
74
|
# Note: Agno doesn't have a class-based tool structure to patch directly.
|
|
75
75
|
# Instead, it uses decorators to convert functions to tools.
|
|
76
|
-
# In
|
|
77
|
-
# in packages/
|
|
76
|
+
# In NAT, tool executions are captured at the execute_agno_tool level
|
|
77
|
+
# in packages/nvidia_nat_agno/src/nat/plugins/agno/tool_wrapper.py
|
|
78
78
|
|
|
79
79
|
# To properly monitor Agno tool executions, we would need to either:
|
|
80
80
|
# 1. Patch the execute_agno_tool function in tool_wrapper.py
|
|
@@ -83,7 +83,7 @@ class AgnoProfilerHandler(BaseProfilerCallback):
|
|
|
83
83
|
# to patch those classes
|
|
84
84
|
|
|
85
85
|
# Recommended future enhancement:
|
|
86
|
-
# The execute_agno_tool function in packages/
|
|
86
|
+
# The execute_agno_tool function in packages/nvidia_nat_agno/src/nat/plugins/agno/tool_wrapper.py
|
|
87
87
|
# should be updated to directly push IntermediateStepPayload events to the step manager
|
|
88
88
|
# at the beginning and end of tool execution, similar to what this handler does for LLM calls.
|
|
89
89
|
|
|
@@ -49,7 +49,7 @@ class LlamaIndexProfilerHandler(BaseCallbackHandler, BaseProfilerCallback):
|
|
|
49
49
|
- Response data
|
|
50
50
|
- Time intervals between calls
|
|
51
51
|
|
|
52
|
-
and appends them to
|
|
52
|
+
and appends them to ContextState.usage_stats.
|
|
53
53
|
"""
|
|
54
54
|
|
|
55
55
|
def __init__(self) -> None:
|
|
@@ -167,7 +167,7 @@ class LlamaIndexProfilerHandler(BaseCallbackHandler, BaseProfilerCallback):
|
|
|
167
167
|
except Exception as e:
|
|
168
168
|
logger.exception("Error getting model name: %s", e, exc_info=True)
|
|
169
169
|
|
|
170
|
-
# Append usage data to
|
|
170
|
+
# Append usage data to NAT usage stats
|
|
171
171
|
with self._lock:
|
|
172
172
|
stats = IntermediateStepPayload(
|
|
173
173
|
event_type=IntermediateStepType.LLM_END,
|
|
@@ -55,7 +55,7 @@ class SemanticKernelProfilerHandler(BaseProfilerCallback):
|
|
|
55
55
|
- Tool calls
|
|
56
56
|
|
|
57
57
|
to collect usage statistics (tokens, inputs, outputs, time intervals, etc.)
|
|
58
|
-
and store them in
|
|
58
|
+
and store them in NAT's usage_stats queue for subsequent analysis.
|
|
59
59
|
"""
|
|
60
60
|
|
|
61
61
|
def __init__(self, workflow_llms: dict) -> None:
|
|
@@ -61,7 +61,7 @@ def push_intermediate_step(step_manager: IntermediateStepManager,
|
|
|
61
61
|
kwargs: Any = None,
|
|
62
62
|
output: Any = None,
|
|
63
63
|
metadata: dict[str, Any] | None = None) -> None:
|
|
64
|
-
"""Push an intermediate step to the
|
|
64
|
+
"""Push an intermediate step to the NAT Event Stream."""
|
|
65
65
|
|
|
66
66
|
payload = IntermediateStepPayload(UUID=identifier,
|
|
67
67
|
event_type=event_type,
|
|
@@ -34,8 +34,9 @@ class LinearModel(ForecastingBaseModel):
|
|
|
34
34
|
try:
|
|
35
35
|
from sklearn.linear_model import LinearRegression
|
|
36
36
|
except ImportError:
|
|
37
|
-
logger.error(
|
|
38
|
-
|
|
37
|
+
logger.error(
|
|
38
|
+
"scikit-learn is not installed. Please install scikit-learn to use the LinearModel "
|
|
39
|
+
"profiling model or install `nvidia-nat[profiler]` to install all necessary profiling packages.")
|
|
39
40
|
|
|
40
41
|
raise
|
|
41
42
|
|
|
@@ -34,8 +34,9 @@ class RandomForestModel(ForecastingBaseModel):
|
|
|
34
34
|
try:
|
|
35
35
|
from sklearn.ensemble import RandomForestRegressor
|
|
36
36
|
except ImportError:
|
|
37
|
-
logger.error(
|
|
38
|
-
|
|
37
|
+
logger.error(
|
|
38
|
+
"scikit-learn is not installed. Please install scikit-learn to use the RandomForest "
|
|
39
|
+
"profiling model or install `nvidia-nat[profiler]` to install all necessary profiling packages.")
|
|
39
40
|
|
|
40
41
|
raise
|
|
41
42
|
|
|
@@ -304,7 +304,7 @@ def save_gantt_chart(all_nodes: list[CallNode], output_path: str) -> None:
|
|
|
304
304
|
import matplotlib.pyplot as plt
|
|
305
305
|
except ImportError:
|
|
306
306
|
logger.error("matplotlib is not installed. Please install matplotlib to use generate plots for the profiler "
|
|
307
|
-
"or install `
|
|
307
|
+
"or install `nvidia-nat[profiler]` to install all necessary profiling packages.")
|
|
308
308
|
|
|
309
309
|
raise
|
|
310
310
|
|
|
@@ -212,7 +212,7 @@ def run_prefixspan(sequences_map: dict[int, list[PrefixCallNode]],
|
|
|
212
212
|
from prefixspan import PrefixSpan
|
|
213
213
|
except ImportError:
|
|
214
214
|
logger.error("prefixspan is not installed. Please install prefixspan to run the prefix analysis in the "
|
|
215
|
-
"profiler or install `
|
|
215
|
+
"profiler or install `nvidia-nat[profiler]` to install all necessary profiling packages.")
|
|
216
216
|
|
|
217
217
|
raise
|
|
218
218
|
|