nvidia-nat 1.3.0rc3__py3-none-any.whl → 1.3.0rc5__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/base.py +3 -3
- nat/agent/react_agent/register.py +2 -2
- nat/agent/reasoning_agent/reasoning_agent.py +6 -6
- nat/agent/rewoo_agent/register.py +2 -2
- nat/agent/tool_calling_agent/agent.py +6 -10
- nat/agent/tool_calling_agent/register.py +2 -2
- nat/builder/function.py +1 -2
- nat/data_models/api_server.py +83 -33
- nat/data_models/thinking_mixin.py +2 -2
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +3 -0
- nat/front_ends/fastapi/message_handler.py +65 -40
- nat/front_ends/fastapi/message_validator.py +1 -2
- nat/front_ends/mcp/mcp_front_end_config.py +32 -0
- nat/front_ends/mcp/mcp_front_end_plugin.py +9 -6
- nat/runtime/runner.py +1 -2
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/METADATA +2 -3
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/RECORD +22 -22
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/entry_points.txt +0 -0
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/top_level.txt +0 -0
nat/agent/base.py
CHANGED
|
@@ -102,11 +102,11 @@ class BaseAgent(ABC):
|
|
|
102
102
|
AIMessage
|
|
103
103
|
The LLM response
|
|
104
104
|
"""
|
|
105
|
-
output_message =
|
|
105
|
+
output_message = []
|
|
106
106
|
async for event in runnable.astream(inputs, config=config):
|
|
107
|
-
output_message
|
|
107
|
+
output_message.append(event.content)
|
|
108
108
|
|
|
109
|
-
return AIMessage(content=output_message)
|
|
109
|
+
return AIMessage(content="".join(output_message))
|
|
110
110
|
|
|
111
111
|
async def _call_llm(self, llm: Runnable, inputs: dict[str, Any], config: RunnableConfig | None = None) -> AIMessage:
|
|
112
112
|
"""
|
|
@@ -162,7 +162,7 @@ async def react_agent_workflow(config: ReActAgentWorkflowConfig, builder: Builde
|
|
|
162
162
|
return GlobalTypeConverter.get().convert(response, to_type=str)
|
|
163
163
|
return response
|
|
164
164
|
except Exception as ex:
|
|
165
|
-
logger.
|
|
166
|
-
raise
|
|
165
|
+
logger.error("%s ReAct Agent failed with exception: %s", AGENT_LOG_PREFIX, str(ex))
|
|
166
|
+
raise
|
|
167
167
|
|
|
168
168
|
yield FunctionInfo.from_fn(_response_fn, description=config.description)
|
|
@@ -157,12 +157,12 @@ async def build_reasoning_function(config: ReasoningFunctionConfig, builder: Bui
|
|
|
157
157
|
prompt = prompt.to_string()
|
|
158
158
|
|
|
159
159
|
# Get the reasoning output from the LLM
|
|
160
|
-
reasoning_output =
|
|
160
|
+
reasoning_output = []
|
|
161
161
|
|
|
162
162
|
async for chunk in llm.astream(prompt):
|
|
163
|
-
reasoning_output
|
|
163
|
+
reasoning_output.append(chunk.content)
|
|
164
164
|
|
|
165
|
-
reasoning_output = remove_r1_think_tags(reasoning_output)
|
|
165
|
+
reasoning_output = remove_r1_think_tags("".join(reasoning_output))
|
|
166
166
|
|
|
167
167
|
output = await downstream_template.ainvoke(input={
|
|
168
168
|
"input_text": input_text, "reasoning_output": reasoning_output
|
|
@@ -200,12 +200,12 @@ async def build_reasoning_function(config: ReasoningFunctionConfig, builder: Bui
|
|
|
200
200
|
prompt = prompt.to_string()
|
|
201
201
|
|
|
202
202
|
# Get the reasoning output from the LLM
|
|
203
|
-
reasoning_output =
|
|
203
|
+
reasoning_output = []
|
|
204
204
|
|
|
205
205
|
async for chunk in llm.astream(prompt):
|
|
206
|
-
reasoning_output
|
|
206
|
+
reasoning_output.append(chunk.content)
|
|
207
207
|
|
|
208
|
-
reasoning_output = remove_r1_think_tags(reasoning_output)
|
|
208
|
+
reasoning_output = remove_r1_think_tags("".join(reasoning_output))
|
|
209
209
|
|
|
210
210
|
output = await downstream_template.ainvoke(input={
|
|
211
211
|
"input_text": input_text, "reasoning_output": reasoning_output
|
|
@@ -169,7 +169,7 @@ async def rewoo_agent_workflow(config: ReWOOAgentWorkflowConfig, builder: Builde
|
|
|
169
169
|
return GlobalTypeConverter.get().convert(response, to_type=str)
|
|
170
170
|
return response
|
|
171
171
|
except Exception as ex:
|
|
172
|
-
logger.
|
|
173
|
-
raise
|
|
172
|
+
logger.error("ReWOO Agent failed with exception: %s", ex)
|
|
173
|
+
raise
|
|
174
174
|
|
|
175
175
|
yield FunctionInfo.from_fn(_response_fn, description=config.description)
|
|
@@ -233,14 +233,10 @@ def create_tool_calling_agent_prompt(config: "ToolCallAgentWorkflowConfig") -> s
|
|
|
233
233
|
"""
|
|
234
234
|
# the Tool Calling Agent prompt can be customized via config option system_prompt and additional_instructions.
|
|
235
235
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
prompt_str += f" {config.additional_instructions}"
|
|
243
|
-
|
|
244
|
-
if len(prompt_str) > 0:
|
|
245
|
-
return prompt_str
|
|
236
|
+
prompt_strs = []
|
|
237
|
+
for msg in [config.system_prompt, config.additional_instructions]:
|
|
238
|
+
if msg is not None:
|
|
239
|
+
prompt_strs.append(msg)
|
|
240
|
+
if prompt_strs:
|
|
241
|
+
return " ".join(prompt_strs)
|
|
246
242
|
return None
|
|
@@ -118,8 +118,8 @@ async def tool_calling_agent_workflow(config: ToolCallAgentWorkflowConfig, build
|
|
|
118
118
|
output_message = state.messages[-1]
|
|
119
119
|
return str(output_message.content)
|
|
120
120
|
except Exception as ex:
|
|
121
|
-
logger.
|
|
122
|
-
raise
|
|
121
|
+
logger.error("%s Tool Calling Agent failed with exception: %s", AGENT_LOG_PREFIX, ex)
|
|
122
|
+
raise
|
|
123
123
|
|
|
124
124
|
try:
|
|
125
125
|
yield FunctionInfo.from_fn(_response_fn, description=config.description)
|
nat/builder/function.py
CHANGED
|
@@ -159,8 +159,7 @@ class Function(FunctionBase[InputT, StreamingOutputT, SingleOutputT], ABC):
|
|
|
159
159
|
|
|
160
160
|
return result
|
|
161
161
|
except Exception as e:
|
|
162
|
-
|
|
163
|
-
logger.error("Error with ainvoke in function with input: %s. %s", value, err_msg)
|
|
162
|
+
logger.error("Error with ainvoke in function with input: %s. Error: %s", value, e)
|
|
164
163
|
raise
|
|
165
164
|
|
|
166
165
|
@typing.final
|
nat/data_models/api_server.py
CHANGED
|
@@ -121,7 +121,15 @@ class Message(BaseModel):
|
|
|
121
121
|
role: UserMessageContentRoleType
|
|
122
122
|
|
|
123
123
|
|
|
124
|
-
class
|
|
124
|
+
class ChatRequest(BaseModel):
|
|
125
|
+
"""
|
|
126
|
+
ChatRequest is a data model that represents a request to the NAT chat API.
|
|
127
|
+
Fully compatible with OpenAI Chat Completions API specification.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
# Required fields
|
|
131
|
+
messages: typing.Annotated[list[Message], conlist(Message, min_length=1)]
|
|
132
|
+
|
|
125
133
|
# Optional fields (OpenAI Chat Completions API compatible)
|
|
126
134
|
model: str | None = Field(default=None, description="name of the model to use")
|
|
127
135
|
frequency_penalty: float | None = Field(default=0.0,
|
|
@@ -145,17 +153,6 @@ class ChatRequestOptionals(BaseModel):
|
|
|
145
153
|
tool_choice: str | dict[str, typing.Any] | None = Field(default=None, description="Controls which tool is called")
|
|
146
154
|
parallel_tool_calls: bool | None = Field(default=True, description="Whether to enable parallel function calling")
|
|
147
155
|
user: str | None = Field(default=None, description="Unique identifier representing end-user")
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
class ChatRequest(ChatRequestOptionals):
|
|
151
|
-
"""
|
|
152
|
-
ChatRequest is a data model that represents a request to the NAT chat API.
|
|
153
|
-
Fully compatible with OpenAI Chat Completions API specification.
|
|
154
|
-
"""
|
|
155
|
-
|
|
156
|
-
# Required fields
|
|
157
|
-
messages: typing.Annotated[list[Message], conlist(Message, min_length=1)]
|
|
158
|
-
|
|
159
156
|
model_config = ConfigDict(extra="allow",
|
|
160
157
|
json_schema_extra={
|
|
161
158
|
"example": {
|
|
@@ -197,39 +194,82 @@ class ChatRequest(ChatRequestOptionals):
|
|
|
197
194
|
top_p=top_p)
|
|
198
195
|
|
|
199
196
|
|
|
200
|
-
class ChatRequestOrMessage(
|
|
197
|
+
class ChatRequestOrMessage(BaseModel):
|
|
201
198
|
"""
|
|
202
|
-
ChatRequestOrMessage is a data model that represents either a conversation or a string input.
|
|
199
|
+
`ChatRequestOrMessage` is a data model that represents either a conversation or a string input.
|
|
203
200
|
This is useful for functions that can handle either type of input.
|
|
204
201
|
|
|
205
|
-
`messages` is compatible with the OpenAI Chat Completions API specification.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
202
|
+
- `messages` is compatible with the OpenAI Chat Completions API specification.
|
|
203
|
+
- `input_message` is a string input that can be used for functions that do not require a conversation.
|
|
204
|
+
|
|
205
|
+
Note: When `messages` is provided, extra fields are allowed to enable lossless round-trip
|
|
206
|
+
conversion with ChatRequest. When `input_message` is provided, no extra fields are permitted.
|
|
207
|
+
"""
|
|
208
|
+
model_config = ConfigDict(
|
|
209
|
+
extra="allow",
|
|
210
|
+
json_schema_extra={
|
|
211
|
+
"examples": [
|
|
212
|
+
{
|
|
213
|
+
"input_message": "What can you do?"
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
"messages": [{
|
|
217
|
+
"role": "user", "content": "What can you do?"
|
|
218
|
+
}],
|
|
219
|
+
"model": "nvidia/nemotron",
|
|
220
|
+
"temperature": 0.7
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
"oneOf": [
|
|
224
|
+
{
|
|
225
|
+
"required": ["input_message"],
|
|
226
|
+
"properties": {
|
|
227
|
+
"input_message": {
|
|
228
|
+
"type": "string"
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
"additionalProperties": {
|
|
232
|
+
"not": True, "errorMessage": 'remove additional property ${0#}'
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"required": ["messages"],
|
|
237
|
+
"properties": {
|
|
238
|
+
"messages": {
|
|
239
|
+
"type": "array"
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
"additionalProperties": True
|
|
243
|
+
},
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
)
|
|
209
247
|
|
|
210
248
|
messages: typing.Annotated[list[Message] | None, conlist(Message, min_length=1)] = Field(
|
|
211
|
-
default=None, description="
|
|
249
|
+
default=None, description="A non-empty conversation of messages to process.")
|
|
212
250
|
|
|
213
|
-
|
|
251
|
+
input_message: str | None = Field(
|
|
252
|
+
default=None,
|
|
253
|
+
description="A single input message to process. Useful for functions that do not require a conversation")
|
|
214
254
|
|
|
215
255
|
@property
|
|
216
256
|
def is_string(self) -> bool:
|
|
217
|
-
return self.
|
|
257
|
+
return self.input_message is not None
|
|
218
258
|
|
|
219
259
|
@property
|
|
220
260
|
def is_conversation(self) -> bool:
|
|
221
261
|
return self.messages is not None
|
|
222
262
|
|
|
223
263
|
@model_validator(mode="after")
|
|
224
|
-
def
|
|
225
|
-
if self.messages is not None and self.
|
|
226
|
-
raise ValueError("Either messages or input_message
|
|
227
|
-
if self.messages is None and self.
|
|
228
|
-
raise ValueError("Either messages or input_message
|
|
229
|
-
if self.
|
|
230
|
-
extra_fields = self.model_dump(exclude={"
|
|
264
|
+
def validate_model(self):
|
|
265
|
+
if self.messages is not None and self.input_message is not None:
|
|
266
|
+
raise ValueError("Either messages or input_message must be provided, not both")
|
|
267
|
+
if self.messages is None and self.input_message is None:
|
|
268
|
+
raise ValueError("Either messages or input_message must be provided")
|
|
269
|
+
if self.input_message is not None:
|
|
270
|
+
extra_fields = self.model_dump(exclude={"input_message"}, exclude_none=True, exclude_unset=True)
|
|
231
271
|
if len(extra_fields) > 0:
|
|
232
|
-
raise ValueError("no extra fields are permitted when input_message
|
|
272
|
+
raise ValueError("no extra fields are permitted when input_message is provided")
|
|
233
273
|
return self
|
|
234
274
|
|
|
235
275
|
|
|
@@ -701,9 +741,9 @@ GlobalTypeConverter.register_converter(_string_to_nat_chat_request)
|
|
|
701
741
|
|
|
702
742
|
|
|
703
743
|
def _chat_request_or_message_to_chat_request(data: ChatRequestOrMessage) -> ChatRequest:
|
|
704
|
-
if data.
|
|
705
|
-
return _string_to_nat_chat_request(data.
|
|
706
|
-
return ChatRequest(**data.model_dump(exclude={"
|
|
744
|
+
if data.input_message is not None:
|
|
745
|
+
return _string_to_nat_chat_request(data.input_message)
|
|
746
|
+
return ChatRequest(**data.model_dump(exclude={"input_message"}))
|
|
707
747
|
|
|
708
748
|
|
|
709
749
|
GlobalTypeConverter.register_converter(_chat_request_or_message_to_chat_request)
|
|
@@ -717,7 +757,17 @@ GlobalTypeConverter.register_converter(_chat_request_to_chat_request_or_message)
|
|
|
717
757
|
|
|
718
758
|
|
|
719
759
|
def _chat_request_or_message_to_string(data: ChatRequestOrMessage) -> str:
|
|
720
|
-
|
|
760
|
+
if data.input_message is not None:
|
|
761
|
+
return data.input_message
|
|
762
|
+
# Extract content from last message in conversation
|
|
763
|
+
if data.messages is None:
|
|
764
|
+
return ""
|
|
765
|
+
content = data.messages[-1].content
|
|
766
|
+
if content is None:
|
|
767
|
+
return ""
|
|
768
|
+
if isinstance(content, str):
|
|
769
|
+
return content
|
|
770
|
+
return str(content)
|
|
721
771
|
|
|
722
772
|
|
|
723
773
|
GlobalTypeConverter.register_converter(_chat_request_or_message_to_string)
|
|
@@ -51,7 +51,7 @@ class ThinkingMixin(
|
|
|
51
51
|
Returns the system prompt to use for thinking.
|
|
52
52
|
For NVIDIA Nemotron, returns "/think" if enabled, else "/no_think".
|
|
53
53
|
For Llama Nemotron v1.5, returns "/think" if enabled, else "/no_think".
|
|
54
|
-
For Llama Nemotron v1.0, returns "detailed thinking on" if enabled, else "detailed thinking off".
|
|
54
|
+
For Llama Nemotron v1.0 or v1.1, returns "detailed thinking on" if enabled, else "detailed thinking off".
|
|
55
55
|
If thinking is not supported on the model, returns None.
|
|
56
56
|
|
|
57
57
|
Returns:
|
|
@@ -72,7 +72,7 @@ class ThinkingMixin(
|
|
|
72
72
|
return "/think" if self.thinking else "/no_think"
|
|
73
73
|
|
|
74
74
|
if model.startswith("nvidia/llama"):
|
|
75
|
-
if "v1-0" in model or "v1-1" in model:
|
|
75
|
+
if "v1-0" in model or "v1-1" in model or model.endswith("v1"):
|
|
76
76
|
return f"detailed thinking {'on' if self.thinking else 'off'}"
|
|
77
77
|
|
|
78
78
|
if "v1-5" in model:
|
|
@@ -1184,6 +1184,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
1184
1184
|
"server": client.server_name,
|
|
1185
1185
|
"transport": config.server.transport,
|
|
1186
1186
|
"session_healthy": session_healthy,
|
|
1187
|
+
"protected": True if config.server.auth_provider is not None else False,
|
|
1187
1188
|
"tools": tools_info,
|
|
1188
1189
|
"total_tools": len(configured_short_names),
|
|
1189
1190
|
"available_tools": available_count
|
|
@@ -1196,6 +1197,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
1196
1197
|
"server": "unknown",
|
|
1197
1198
|
"transport": config.server.transport if config.server else "unknown",
|
|
1198
1199
|
"session_healthy": False,
|
|
1200
|
+
"protected": False,
|
|
1199
1201
|
"error": str(e),
|
|
1200
1202
|
"tools": [],
|
|
1201
1203
|
"total_tools": 0,
|
|
@@ -1226,6 +1228,7 @@ class FastApiFrontEndPluginWorker(FastApiFrontEndPluginWorkerBase):
|
|
|
1226
1228
|
"server": "streamable-http:http://localhost:9901/mcp",
|
|
1227
1229
|
"transport": "streamable-http",
|
|
1228
1230
|
"session_healthy": True,
|
|
1231
|
+
"protected": False,
|
|
1229
1232
|
"tools": [{
|
|
1230
1233
|
"name": "tool_a",
|
|
1231
1234
|
"description": "Tool A description",
|
|
@@ -25,6 +25,7 @@ from pydantic import ValidationError
|
|
|
25
25
|
from starlette.websockets import WebSocketDisconnect
|
|
26
26
|
|
|
27
27
|
from nat.authentication.interfaces import FlowHandlerBase
|
|
28
|
+
from nat.data_models.api_server import ChatRequest
|
|
28
29
|
from nat.data_models.api_server import ChatResponse
|
|
29
30
|
from nat.data_models.api_server import ChatResponseChunk
|
|
30
31
|
from nat.data_models.api_server import Error
|
|
@@ -33,6 +34,8 @@ from nat.data_models.api_server import ResponsePayloadOutput
|
|
|
33
34
|
from nat.data_models.api_server import ResponseSerializable
|
|
34
35
|
from nat.data_models.api_server import SystemResponseContent
|
|
35
36
|
from nat.data_models.api_server import TextContent
|
|
37
|
+
from nat.data_models.api_server import UserMessageContentRoleType
|
|
38
|
+
from nat.data_models.api_server import UserMessages
|
|
36
39
|
from nat.data_models.api_server import WebSocketMessageStatus
|
|
37
40
|
from nat.data_models.api_server import WebSocketMessageType
|
|
38
41
|
from nat.data_models.api_server import WebSocketSystemInteractionMessage
|
|
@@ -64,12 +67,12 @@ class WebSocketMessageHandler:
|
|
|
64
67
|
self._running_workflow_task: asyncio.Task | None = None
|
|
65
68
|
self._message_parent_id: str = "default_id"
|
|
66
69
|
self._conversation_id: str | None = None
|
|
67
|
-
self._workflow_schema_type: str = None
|
|
68
|
-
self._user_interaction_response: asyncio.Future[
|
|
70
|
+
self._workflow_schema_type: str | None = None
|
|
71
|
+
self._user_interaction_response: asyncio.Future[TextContent] | None = None
|
|
69
72
|
|
|
70
73
|
self._flow_handler: FlowHandlerBase | None = None
|
|
71
74
|
|
|
72
|
-
self._schema_output_mapping: dict[str, type[BaseModel] | None] = {
|
|
75
|
+
self._schema_output_mapping: dict[str, type[BaseModel] | type[None]] = {
|
|
73
76
|
WorkflowSchemaType.GENERATE: self._session_manager.workflow.single_output_schema,
|
|
74
77
|
WorkflowSchemaType.CHAT: ChatResponse,
|
|
75
78
|
WorkflowSchemaType.CHAT_STREAM: ChatResponseChunk,
|
|
@@ -114,36 +117,58 @@ class WebSocketMessageHandler:
|
|
|
114
117
|
pass
|
|
115
118
|
|
|
116
119
|
elif (isinstance(validated_message, WebSocketUserInteractionResponseMessage)):
|
|
117
|
-
user_content = await self.
|
|
120
|
+
user_content = await self._process_websocket_user_interaction_response_message(validated_message)
|
|
121
|
+
assert self._user_interaction_response is not None
|
|
118
122
|
self._user_interaction_response.set_result(user_content)
|
|
119
123
|
except (asyncio.CancelledError, WebSocketDisconnect):
|
|
120
124
|
# TODO: Handle the disconnect
|
|
121
125
|
break
|
|
122
126
|
|
|
123
|
-
|
|
124
|
-
self, user_content: WebSocketUserMessage | WebSocketUserInteractionResponseMessage) -> BaseModel | None:
|
|
127
|
+
def _extract_last_user_message_content(self, messages: list[UserMessages]) -> TextContent:
|
|
125
128
|
"""
|
|
126
|
-
|
|
129
|
+
Extracts the last user's TextContent from a list of messages.
|
|
127
130
|
|
|
128
|
-
:
|
|
129
|
-
|
|
130
|
-
"""
|
|
131
|
+
Args:
|
|
132
|
+
messages: List of UserMessages.
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
+
Returns:
|
|
135
|
+
TextContent object from the last user message.
|
|
134
136
|
|
|
137
|
+
Raises:
|
|
138
|
+
ValueError: If no user text content is found.
|
|
139
|
+
"""
|
|
140
|
+
for user_message in messages[::-1]:
|
|
141
|
+
if user_message.role == UserMessageContentRoleType.USER:
|
|
135
142
|
for attachment in user_message.content:
|
|
136
|
-
|
|
137
143
|
if isinstance(attachment, TextContent):
|
|
138
144
|
return attachment
|
|
145
|
+
raise ValueError("No user text content found in messages.")
|
|
146
|
+
|
|
147
|
+
async def _process_websocket_user_interaction_response_message(
|
|
148
|
+
self, user_content: WebSocketUserInteractionResponseMessage) -> TextContent:
|
|
149
|
+
"""
|
|
150
|
+
Processes a WebSocketUserInteractionResponseMessage.
|
|
151
|
+
"""
|
|
152
|
+
return self._extract_last_user_message_content(user_content.content.messages)
|
|
139
153
|
|
|
140
|
-
|
|
154
|
+
async def _process_websocket_user_message(self, user_content: WebSocketUserMessage) -> ChatRequest | str:
|
|
155
|
+
"""
|
|
156
|
+
Processes a WebSocketUserMessage based on schema type.
|
|
157
|
+
"""
|
|
158
|
+
if self._workflow_schema_type in [WorkflowSchemaType.CHAT, WorkflowSchemaType.CHAT_STREAM]:
|
|
159
|
+
return ChatRequest(**user_content.content.model_dump(include={"messages"}))
|
|
160
|
+
|
|
161
|
+
elif self._workflow_schema_type in [WorkflowSchemaType.GENERATE, WorkflowSchemaType.GENERATE_STREAM]:
|
|
162
|
+
return self._extract_last_user_message_content(user_content.content.messages).text
|
|
163
|
+
|
|
164
|
+
raise ValueError("Unsupported workflow schema type for WebSocketUserMessage")
|
|
141
165
|
|
|
142
166
|
async def process_workflow_request(self, user_message_as_validated_type: WebSocketUserMessage) -> None:
|
|
143
167
|
"""
|
|
144
168
|
Process user messages and routes them appropriately.
|
|
145
169
|
|
|
146
|
-
:
|
|
170
|
+
Args:
|
|
171
|
+
user_message_as_validated_type (WebSocketUserMessage): The validated user message to process.
|
|
147
172
|
"""
|
|
148
173
|
|
|
149
174
|
try:
|
|
@@ -151,18 +176,15 @@ class WebSocketMessageHandler:
|
|
|
151
176
|
self._workflow_schema_type = user_message_as_validated_type.schema_type
|
|
152
177
|
self._conversation_id = user_message_as_validated_type.conversation_id
|
|
153
178
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if content is None:
|
|
157
|
-
raise ValueError(f"User message content could not be found: {user_message_as_validated_type}")
|
|
179
|
+
message_content: typing.Any = await self._process_websocket_user_message(user_message_as_validated_type)
|
|
158
180
|
|
|
159
|
-
if
|
|
181
|
+
if (self._running_workflow_task is None):
|
|
160
182
|
|
|
161
|
-
def _done_callback(
|
|
183
|
+
def _done_callback(_task: asyncio.Task):
|
|
162
184
|
self._running_workflow_task = None
|
|
163
185
|
|
|
164
186
|
self._running_workflow_task = asyncio.create_task(
|
|
165
|
-
self._run_workflow(payload=
|
|
187
|
+
self._run_workflow(payload=message_content,
|
|
166
188
|
user_message_id=self._message_parent_id,
|
|
167
189
|
conversation_id=self._conversation_id,
|
|
168
190
|
result_type=self._schema_output_mapping[self._workflow_schema_type],
|
|
@@ -180,13 +202,14 @@ class WebSocketMessageHandler:
|
|
|
180
202
|
async def create_websocket_message(self,
|
|
181
203
|
data_model: BaseModel,
|
|
182
204
|
message_type: str | None = None,
|
|
183
|
-
status:
|
|
205
|
+
status: WebSocketMessageStatus = WebSocketMessageStatus.IN_PROGRESS) -> None:
|
|
184
206
|
"""
|
|
185
207
|
Creates a websocket message that will be ready for routing based on message type or data model.
|
|
186
208
|
|
|
187
|
-
:
|
|
188
|
-
|
|
189
|
-
|
|
209
|
+
Args:
|
|
210
|
+
data_model (BaseModel): Message content model.
|
|
211
|
+
message_type (str | None): Message content model.
|
|
212
|
+
status (WebSocketMessageStatus): Message content model.
|
|
190
213
|
"""
|
|
191
214
|
try:
|
|
192
215
|
message: BaseModel | None = None
|
|
@@ -196,8 +219,8 @@ class WebSocketMessageHandler:
|
|
|
196
219
|
|
|
197
220
|
message_schema: type[BaseModel] = await self._message_validator.get_message_schema_by_type(message_type)
|
|
198
221
|
|
|
199
|
-
if 'id'
|
|
200
|
-
message_id: str = data_model
|
|
222
|
+
if hasattr(data_model, 'id'):
|
|
223
|
+
message_id: str = str(getattr(data_model, 'id'))
|
|
201
224
|
else:
|
|
202
225
|
message_id = str(uuid.uuid4())
|
|
203
226
|
|
|
@@ -253,12 +276,15 @@ class WebSocketMessageHandler:
|
|
|
253
276
|
Registered human interaction callback that processes human interactions and returns
|
|
254
277
|
responses from websocket connection.
|
|
255
278
|
|
|
256
|
-
:
|
|
257
|
-
|
|
279
|
+
Args:
|
|
280
|
+
prompt: Incoming interaction content data model.
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
A Text Content Base Pydantic model.
|
|
258
284
|
"""
|
|
259
285
|
|
|
260
286
|
# First create a future from the loop for the human response
|
|
261
|
-
human_response_future: asyncio.Future[
|
|
287
|
+
human_response_future: asyncio.Future[TextContent] = asyncio.get_running_loop().create_future()
|
|
262
288
|
|
|
263
289
|
# Then add the future to the outstanding human prompts dictionary
|
|
264
290
|
self._user_interaction_response = human_response_future
|
|
@@ -274,10 +300,10 @@ class WebSocketMessageHandler:
|
|
|
274
300
|
return HumanResponseNotification()
|
|
275
301
|
|
|
276
302
|
# Wait for the human response future to complete
|
|
277
|
-
|
|
303
|
+
text_content: TextContent = await human_response_future
|
|
278
304
|
|
|
279
305
|
interaction_response: HumanResponse = await self._message_validator.convert_text_content_to_human_response(
|
|
280
|
-
|
|
306
|
+
text_content, prompt.content)
|
|
281
307
|
|
|
282
308
|
return interaction_response
|
|
283
309
|
|
|
@@ -293,13 +319,12 @@ class WebSocketMessageHandler:
|
|
|
293
319
|
output_type: type | None = None) -> None:
|
|
294
320
|
|
|
295
321
|
try:
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if self._flow_handler else None)) as session:
|
|
322
|
+
auth_callback = self._flow_handler.authenticate if self._flow_handler else None
|
|
323
|
+
async with self._session_manager.session(user_message_id=user_message_id,
|
|
324
|
+
conversation_id=conversation_id,
|
|
325
|
+
http_connection=self._socket,
|
|
326
|
+
user_input_callback=self.human_interaction_callback,
|
|
327
|
+
user_authentication_callback=auth_callback) as session:
|
|
303
328
|
|
|
304
329
|
async for value in generate_streaming_response(payload,
|
|
305
330
|
session_manager=session,
|
|
@@ -240,8 +240,7 @@ class MessageValidator:
|
|
|
240
240
|
thread_id: str = "default",
|
|
241
241
|
parent_id: str = "default",
|
|
242
242
|
conversation_id: str | None = None,
|
|
243
|
-
content: SystemResponseContent
|
|
244
|
-
| Error = SystemResponseContent(),
|
|
243
|
+
content: SystemResponseContent | Error = SystemResponseContent(),
|
|
245
244
|
status: WebSocketMessageStatus = WebSocketMessageStatus.IN_PROGRESS,
|
|
246
245
|
timestamp: str = str(datetime.datetime.now(datetime.UTC))
|
|
247
246
|
) -> WebSocketSystemResponseTokenMessage | None:
|
|
@@ -13,13 +13,17 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
+
import logging
|
|
16
17
|
from typing import Literal
|
|
17
18
|
|
|
18
19
|
from pydantic import Field
|
|
20
|
+
from pydantic import model_validator
|
|
19
21
|
|
|
20
22
|
from nat.authentication.oauth2.oauth2_resource_server_config import OAuth2ResourceServerConfig
|
|
21
23
|
from nat.data_models.front_end import FrontEndBaseConfig
|
|
22
24
|
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
class MCPFrontEndConfig(FrontEndBaseConfig, name="mcp"):
|
|
25
29
|
"""MCP front end configuration.
|
|
@@ -43,3 +47,31 @@ class MCPFrontEndConfig(FrontEndBaseConfig, name="mcp"):
|
|
|
43
47
|
|
|
44
48
|
server_auth: OAuth2ResourceServerConfig | None = Field(
|
|
45
49
|
default=None, description=("OAuth 2.0 Resource Server configuration for token verification."))
|
|
50
|
+
|
|
51
|
+
@model_validator(mode="after")
|
|
52
|
+
def validate_security_configuration(self):
|
|
53
|
+
"""Validate security configuration to prevent accidental misconfigurations."""
|
|
54
|
+
# Check if server is bound to a non-localhost interface without authentication
|
|
55
|
+
localhost_hosts = {"localhost", "127.0.0.1", "::1"}
|
|
56
|
+
if self.host not in localhost_hosts and self.server_auth is None:
|
|
57
|
+
logger.warning(
|
|
58
|
+
"MCP server is configured to bind to '%s' without authentication. "
|
|
59
|
+
"This may expose your server to unauthorized access. "
|
|
60
|
+
"Consider either: (1) binding to localhost for local-only access, "
|
|
61
|
+
"or (2) configuring server_auth for production deployments on public interfaces.",
|
|
62
|
+
self.host)
|
|
63
|
+
|
|
64
|
+
# Check if SSE transport is used (which doesn't support authentication)
|
|
65
|
+
if self.transport == "sse":
|
|
66
|
+
if self.server_auth is not None:
|
|
67
|
+
logger.warning("SSE transport does not support authentication. "
|
|
68
|
+
"The configured server_auth will be ignored. "
|
|
69
|
+
"For production use with authentication, use 'streamable-http' transport instead.")
|
|
70
|
+
elif self.host not in localhost_hosts:
|
|
71
|
+
logger.warning(
|
|
72
|
+
"SSE transport does not support authentication and is bound to '%s'. "
|
|
73
|
+
"This configuration is not recommended for production use. "
|
|
74
|
+
"For production deployments, use 'streamable-http' transport with server_auth configured.",
|
|
75
|
+
self.host)
|
|
76
|
+
|
|
77
|
+
return self
|
|
@@ -105,9 +105,12 @@ class MCPFrontEndPlugin(FrontEndBase[MCPFrontEndConfig]):
|
|
|
105
105
|
|
|
106
106
|
# Start the MCP server with configurable transport
|
|
107
107
|
# streamable-http is the default, but users can choose sse if preferred
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
try:
|
|
109
|
+
if self.front_end_config.transport == "sse":
|
|
110
|
+
logger.info("Starting MCP server with SSE endpoint at /sse")
|
|
111
|
+
await mcp.run_sse_async()
|
|
112
|
+
else: # streamable-http
|
|
113
|
+
logger.info("Starting MCP server with streamable-http endpoint at /mcp/")
|
|
114
|
+
await mcp.run_streamable_http_async()
|
|
115
|
+
except KeyboardInterrupt:
|
|
116
|
+
logger.info("MCP server shutdown requested (Ctrl+C). Shutting down gracefully.")
|
nat/runtime/runner.py
CHANGED
|
@@ -196,8 +196,7 @@ class Runner:
|
|
|
196
196
|
|
|
197
197
|
return result
|
|
198
198
|
except Exception as e:
|
|
199
|
-
|
|
200
|
-
logger.error("Error running workflow%s", err_msg)
|
|
199
|
+
logger.error("Error running workflow: %s", e)
|
|
201
200
|
event_stream = self._context_state.event_stream.get()
|
|
202
201
|
if event_stream:
|
|
203
202
|
event_stream.on_complete()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nvidia-nat
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.0rc5
|
|
4
4
|
Summary: NVIDIA NeMo Agent toolkit
|
|
5
5
|
Author: NVIDIA Corporation
|
|
6
6
|
Maintainer: NVIDIA Corporation
|
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
License-File: LICENSE-3rd-party.txt
|
|
18
18
|
License-File: LICENSE.md
|
|
19
19
|
Requires-Dist: aioboto3>=11.0.0
|
|
20
|
-
Requires-Dist: authlib
|
|
20
|
+
Requires-Dist: authlib<2.0.0,>=1.6.5
|
|
21
21
|
Requires-Dist: click~=8.1
|
|
22
22
|
Requires-Dist: colorama~=0.4.6
|
|
23
23
|
Requires-Dist: datasets~=4.0
|
|
@@ -103,7 +103,6 @@ Requires-Dist: nat_multi_frameworks; extra == "examples"
|
|
|
103
103
|
Requires-Dist: nat_plot_charts; extra == "examples"
|
|
104
104
|
Requires-Dist: nat_por_to_jiratickets; extra == "examples"
|
|
105
105
|
Requires-Dist: nat_profiler_agent; extra == "examples"
|
|
106
|
-
Requires-Dist: nat_redact_pii; extra == "examples"
|
|
107
106
|
Requires-Dist: nat_router_agent; extra == "examples"
|
|
108
107
|
Requires-Dist: nat_semantic_kernel_demo; extra == "examples"
|
|
109
108
|
Requires-Dist: nat_sequential_executor; extra == "examples"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
aiq/__init__.py,sha256=qte-NlwgM990yEeyaRUxA4IBQq3PaEkQUCK3i95iwPw,2341
|
|
2
2
|
nat/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
nat/agent/base.py,sha256=
|
|
3
|
+
nat/agent/base.py,sha256=Q6byRPl4mrfVFD3boKt5yftnnJ3ucH9l39FTt3QOQbg,10169
|
|
4
4
|
nat/agent/dual_node.py,sha256=pfvXa1iLKtrNBHsh-tM5RWRmVe7QkyYhQNanOfWdJJs,2569
|
|
5
5
|
nat/agent/register.py,sha256=rPhHDyqRBIKyon3HqhOmpAjqPP18Or6wDQb0wRUBIjQ,1032
|
|
6
6
|
nat/agent/prompt_optimizer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -10,16 +10,16 @@ nat/agent/react_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
10
10
|
nat/agent/react_agent/agent.py,sha256=sWrg9WrglTKQQyG3EcjNm2JTEchCPEo9li-Po7TJKss,21294
|
|
11
11
|
nat/agent/react_agent/output_parser.py,sha256=m7K6wRwtckBBpAHqOf3BZ9mqZLwrP13Kxz5fvNxbyZE,4219
|
|
12
12
|
nat/agent/react_agent/prompt.py,sha256=N47JJrT6xwYQCv1jedHhlul2AE7EfKsSYfAbgJwWRew,1758
|
|
13
|
-
nat/agent/react_agent/register.py,sha256=
|
|
13
|
+
nat/agent/react_agent/register.py,sha256=qkPaK6AvXjolL-q_Z3waVobXDz24GMfuqGqCn-2un2Q,8991
|
|
14
14
|
nat/agent/reasoning_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
nat/agent/reasoning_agent/reasoning_agent.py,sha256=
|
|
15
|
+
nat/agent/reasoning_agent/reasoning_agent.py,sha256=fFQtzvaBWtmr_B6S9KSkqAfyl1BdcOc9xkhnbO4O8Pk,9603
|
|
16
16
|
nat/agent/rewoo_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
nat/agent/rewoo_agent/agent.py,sha256=XXgVXY9xwkyxnr093KXUtfgyNxAQbyGAecoGqN5mMLY,26199
|
|
18
18
|
nat/agent/rewoo_agent/prompt.py,sha256=B0JeL1xDX4VKcShlkkviEcAsOKAwzSlX8NcAQdmUUPw,3645
|
|
19
|
-
nat/agent/rewoo_agent/register.py,sha256=
|
|
19
|
+
nat/agent/rewoo_agent/register.py,sha256=XArlOR37QOBtAvsdKJUjRok5qTmx39S2mJHSteOwU58,9283
|
|
20
20
|
nat/agent/tool_calling_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
nat/agent/tool_calling_agent/agent.py,sha256=
|
|
22
|
-
nat/agent/tool_calling_agent/register.py,sha256=
|
|
21
|
+
nat/agent/tool_calling_agent/agent.py,sha256=9CRQbFlcJ02WvuRojaWcRS8ISl38JlS18BUflApuoAw,10960
|
|
22
|
+
nat/agent/tool_calling_agent/register.py,sha256=OucceyELA2xZL3KdANWK9w12fnVP75eVbZgzOnmXHys,7057
|
|
23
23
|
nat/authentication/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
|
|
24
24
|
nat/authentication/interfaces.py,sha256=1J2CWEJ_n6CLA3_HD3XV28CSbyfxrPAHzr7Q4kKDFdc,3511
|
|
25
25
|
nat/authentication/register.py,sha256=lFhswYUk9iZ53mq33fClR9UfjJPdjGIivGGNHQeWiYo,915
|
|
@@ -48,7 +48,7 @@ nat/builder/eval_builder.py,sha256=I-ScvupmorClYoVBIs_PhSsB7Xf9e2nGWe0rCZp3txo,6
|
|
|
48
48
|
nat/builder/evaluator.py,sha256=xWHMND2vcAUkdFP7FU3jnVki1rUHeTa0-9saFh2hWKs,1162
|
|
49
49
|
nat/builder/framework_enum.py,sha256=n7IaTQBxhFozIQqRMcX9kXntw28JhFzCj82jJ0C5tNU,901
|
|
50
50
|
nat/builder/front_end.py,sha256=FCJ87NSshVVuTg8zZrq3YAr_u0RaYVZVcibnqlRFy-M,2173
|
|
51
|
-
nat/builder/function.py,sha256=
|
|
51
|
+
nat/builder/function.py,sha256=eZZWLwhphgQTnPvbga8sGleX7HCP46usZPIegE7zFzs,27725
|
|
52
52
|
nat/builder/function_base.py,sha256=0Eg8RtjWhEU3Yme0CVxcRutobA0Qo8-YHZLI6L2qAgM,13116
|
|
53
53
|
nat/builder/function_info.py,sha256=7Rmrn-gOFrT2TIJklJwA_O-ycx_oimwZ0-qMYpbuZrU,25161
|
|
54
54
|
nat/builder/intermediate_step_manager.py,sha256=iOuMLWTaES0J0XzaLxhTUqFvuoCAChJu3V69T43K0k0,7599
|
|
@@ -112,7 +112,7 @@ nat/control_flow/router_agent/prompt.py,sha256=fIAiNsAs1zXRAatButR76zSpHJNxSkXXK
|
|
|
112
112
|
nat/control_flow/router_agent/register.py,sha256=4RGmS9sy-QtIMmvh8mfMcR1VqxFPLpG4RckWCIExh40,4144
|
|
113
113
|
nat/data_models/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
|
|
114
114
|
nat/data_models/agent.py,sha256=IwDyb9Zc3R4Zd5rFeqt7q0EQswczAl5focxV9KozIzs,1625
|
|
115
|
-
nat/data_models/api_server.py,sha256=
|
|
115
|
+
nat/data_models/api_server.py,sha256=IowzLwxJqnSkUehCbK0WJp98hBZFXUQDd1cq8lr9PVs,30582
|
|
116
116
|
nat/data_models/authentication.py,sha256=XPu9W8nh4XRSuxPv3HxO-FMQ_JtTEoK6Y02JwnzDwTg,8457
|
|
117
117
|
nat/data_models/common.py,sha256=nXXfGrjpxebzBUa55mLdmzePLt7VFHvTAc6Znj3yEv0,5875
|
|
118
118
|
nat/data_models/component.py,sha256=b_hXOA8Gm5UNvlFkAhsR6kEvf33ST50MKtr5kWf75Ao,1894
|
|
@@ -146,7 +146,7 @@ nat/data_models/streaming.py,sha256=sSqJqLqb70qyw69_4R9QC2RMbRw7UjTLPdo3FYBUGxE,
|
|
|
146
146
|
nat/data_models/swe_bench_model.py,sha256=uZs-hLFuT1B5CiPFwFg1PHinDW8PHne8TBzu7tHFv_k,1718
|
|
147
147
|
nat/data_models/telemetry_exporter.py,sha256=P7kqxIQnFVuvo_UFpH9QSB8fACy_0U2Uzkw_IfWXagE,998
|
|
148
148
|
nat/data_models/temperature_mixin.py,sha256=LlpfWrWtDrPJfSKfNx5E0P3p5SNGZli7ACRRpmO0QqA,1628
|
|
149
|
-
nat/data_models/thinking_mixin.py,sha256=
|
|
149
|
+
nat/data_models/thinking_mixin.py,sha256=VRDUJZ8XP_Vv0gW2FRZUf8O9-kVgNEdZCEZ8oEmHyMk,3335
|
|
150
150
|
nat/data_models/top_p_mixin.py,sha256=mu0DLnCAiwNzpSFR8FOW4kQBUpodSrvUR4MsLrNtbgA,1599
|
|
151
151
|
nat/data_models/ttc_strategy.py,sha256=tAkKWcyEBmBOOYtHMtQTgeCbHxFTk5SEkmFunNVnfyE,1114
|
|
152
152
|
nat/embedder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -242,12 +242,12 @@ nat/front_ends/fastapi/dask_client_mixin.py,sha256=N_tw4yxA7EKIFTKp5_C2ZksIZucWx
|
|
|
242
242
|
nat/front_ends/fastapi/fastapi_front_end_config.py,sha256=BcuzrVlA5b7yYyQKNvQgEanDBtKEHdpC8TAd-O7lfF0,11992
|
|
243
243
|
nat/front_ends/fastapi/fastapi_front_end_controller.py,sha256=ei-34KCMpyaeAgeAN4gVvSGFjewjjRhHZPN0FqAfhDY,2548
|
|
244
244
|
nat/front_ends/fastapi/fastapi_front_end_plugin.py,sha256=e33YkMcLzvm4OUG34bhl-WYiBTqkR-_wJYKG4GODkGM,11169
|
|
245
|
-
nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py,sha256=
|
|
245
|
+
nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py,sha256=T6uslFdkHl_r0U54_7cRRKLnWYP2tTMcD7snx9Gv1xs,60547
|
|
246
246
|
nat/front_ends/fastapi/intermediate_steps_subscriber.py,sha256=kbyWlBVpyvyQQjeUnFG9nsR4RaqqNkx567ZSVwwl2RU,3104
|
|
247
247
|
nat/front_ends/fastapi/job_store.py,sha256=cWIBnIgRdkGL7qbBunEKzTYzdPp3l3QCDHMP-qTZJpc,22743
|
|
248
248
|
nat/front_ends/fastapi/main.py,sha256=s8gXCy61rJjK1aywMRpgPvzlkMGsCS-kI_0EIy4JjBM,2445
|
|
249
|
-
nat/front_ends/fastapi/message_handler.py,sha256=
|
|
250
|
-
nat/front_ends/fastapi/message_validator.py,sha256=
|
|
249
|
+
nat/front_ends/fastapi/message_handler.py,sha256=9CH42D-Ti5cczvRTLVuvlfCV9LaTqFGJCny46zrrEnk,16584
|
|
250
|
+
nat/front_ends/fastapi/message_validator.py,sha256=9n-DvUNfqlTtPgcBMC684quD619XYSzjgPfe6f23LPQ,17606
|
|
251
251
|
nat/front_ends/fastapi/register.py,sha256=rA12NPFgV9ZNHOEIgB7_SB6NytjRxgBTLo7fJ-73_HM,1153
|
|
252
252
|
nat/front_ends/fastapi/response_helpers.py,sha256=MGE9E73sQSCYjsR5YXRga2qbl44hrTAPW2N5Ui3vXX0,9028
|
|
253
253
|
nat/front_ends/fastapi/step_adaptor.py,sha256=J6UtoXL9De8bgAg93nE0ASLUHZbidWOfRiuFo-tyZgY,12412
|
|
@@ -259,8 +259,8 @@ nat/front_ends/fastapi/html_snippets/__init__.py,sha256=GUJrgGtpvyMUCjUBvR3faAdv
|
|
|
259
259
|
nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py,sha256=BNpWwzmA58UM0GK4kZXG4PHJy_5K9ihaVHu8SgCs5JA,1131
|
|
260
260
|
nat/front_ends/mcp/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
|
|
261
261
|
nat/front_ends/mcp/introspection_token_verifier.py,sha256=s7Q4Q6rWZJ0ZVujSxxpvVI6Bnhkg1LJQ3RLkvhzFIGE,2836
|
|
262
|
-
nat/front_ends/mcp/mcp_front_end_config.py,sha256=
|
|
263
|
-
nat/front_ends/mcp/mcp_front_end_plugin.py,sha256=
|
|
262
|
+
nat/front_ends/mcp/mcp_front_end_config.py,sha256=m6z5qSz8YGnFnfu8hRID69suvO1YT_L6sxy1Ki64Ufw,4042
|
|
263
|
+
nat/front_ends/mcp/mcp_front_end_plugin.py,sha256=4u_kpen_T-_Uh62V5M7dfW9KyzbqXI7tGBG4AxJXWm0,5231
|
|
264
264
|
nat/front_ends/mcp/mcp_front_end_plugin_worker.py,sha256=jMclC0qEd910oTGCqd1kQ8WjP3WPdQKTl854-2bU_KI,10200
|
|
265
265
|
nat/front_ends/mcp/register.py,sha256=3aJtgG5VaiqujoeU1-Eq7Hl5pWslIlIwGFU2ASLTXgM,1173
|
|
266
266
|
nat/front_ends/mcp/tool_converter.py,sha256=jyH6tFKUDXSfRBKkv8WjvJsQt05zk3FJBTCwnIuUh5M,11547
|
|
@@ -406,7 +406,7 @@ nat/retriever/nemo_retriever/register.py,sha256=3XdrvEJzX2Zc8wpdm__4YYlEWBW-FK3t
|
|
|
406
406
|
nat/retriever/nemo_retriever/retriever.py,sha256=gi3_qJFqE-iqRh3of_cmJg-SwzaQ3z24zA9LwY_MSLY,6930
|
|
407
407
|
nat/runtime/__init__.py,sha256=Xs1JQ16L9btwreh4pdGKwskffAw1YFO48jKrU4ib_7c,685
|
|
408
408
|
nat/runtime/loader.py,sha256=obUdAgZVYCPGC0R8u3wcoKFJzzSPQgJvrbU4OWygtog,7953
|
|
409
|
-
nat/runtime/runner.py,sha256=
|
|
409
|
+
nat/runtime/runner.py,sha256=qa_AqtmB8TUHX6nVJ0TLEYCKUsm2L99kq5O72AuL3yc,11736
|
|
410
410
|
nat/runtime/session.py,sha256=E8RTbnAhPbY5KCoSfiHzOJksmBh7xWjsoX0BC7Rn1ck,9101
|
|
411
411
|
nat/runtime/user_metadata.py,sha256=ce37NRYJWnMOWk6A7VAQ1GQztjMmkhMOq-uYf2gNCwo,3692
|
|
412
412
|
nat/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -470,10 +470,10 @@ nat/utils/reactive/base/observer_base.py,sha256=6BiQfx26EMumotJ3KoVcdmFBYR_fnAss
|
|
|
470
470
|
nat/utils/reactive/base/subject_base.py,sha256=UQOxlkZTIeeyYmG5qLtDpNf_63Y7p-doEeUA08_R8ME,2521
|
|
471
471
|
nat/utils/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
472
472
|
nat/utils/settings/global_settings.py,sha256=9JaO6pxKT_Pjw6rxJRsRlFCXdVKCl_xUKU2QHZQWWNM,7294
|
|
473
|
-
nvidia_nat-1.3.
|
|
474
|
-
nvidia_nat-1.3.
|
|
475
|
-
nvidia_nat-1.3.
|
|
476
|
-
nvidia_nat-1.3.
|
|
477
|
-
nvidia_nat-1.3.
|
|
478
|
-
nvidia_nat-1.3.
|
|
479
|
-
nvidia_nat-1.3.
|
|
473
|
+
nvidia_nat-1.3.0rc5.dist-info/licenses/LICENSE-3rd-party.txt,sha256=fOk5jMmCX9YoKWyYzTtfgl-SUy477audFC5hNY4oP7Q,284609
|
|
474
|
+
nvidia_nat-1.3.0rc5.dist-info/licenses/LICENSE.md,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
475
|
+
nvidia_nat-1.3.0rc5.dist-info/METADATA,sha256=nm0UvCzWa259-7OnT21duAI9yl9EqwKs3lXaxup59OA,10180
|
|
476
|
+
nvidia_nat-1.3.0rc5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
477
|
+
nvidia_nat-1.3.0rc5.dist-info/entry_points.txt,sha256=4jCqjyETMpyoWbCBf4GalZU8I_wbstpzwQNezdAVbbo,698
|
|
478
|
+
nvidia_nat-1.3.0rc5.dist-info/top_level.txt,sha256=lgJWLkigiVZuZ_O1nxVnD_ziYBwgpE2OStdaCduMEGc,8
|
|
479
|
+
nvidia_nat-1.3.0rc5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
{nvidia_nat-1.3.0rc3.dist-info → nvidia_nat-1.3.0rc5.dist-info}/licenses/LICENSE-3rd-party.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|