nvidia-nat 1.2.1__py3-none-any.whl → 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. aiq/__init__.py +2 -2
  2. nat/agent/base.py +27 -18
  3. nat/agent/dual_node.py +9 -4
  4. nat/agent/prompt_optimizer/prompt.py +68 -0
  5. nat/agent/prompt_optimizer/register.py +149 -0
  6. nat/agent/react_agent/agent.py +81 -50
  7. nat/agent/react_agent/register.py +59 -40
  8. nat/agent/reasoning_agent/reasoning_agent.py +17 -15
  9. nat/agent/register.py +1 -1
  10. nat/agent/rewoo_agent/agent.py +327 -149
  11. nat/agent/rewoo_agent/prompt.py +19 -22
  12. nat/agent/rewoo_agent/register.py +64 -46
  13. nat/agent/tool_calling_agent/agent.py +152 -29
  14. nat/agent/tool_calling_agent/register.py +61 -38
  15. nat/authentication/api_key/api_key_auth_provider.py +2 -2
  16. nat/authentication/credential_validator/bearer_token_validator.py +557 -0
  17. nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
  18. nat/authentication/interfaces.py +5 -2
  19. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +69 -36
  20. nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
  21. nat/authentication/register.py +0 -1
  22. nat/builder/builder.py +56 -24
  23. nat/builder/component_utils.py +10 -6
  24. nat/builder/context.py +70 -18
  25. nat/builder/eval_builder.py +16 -11
  26. nat/builder/framework_enum.py +1 -0
  27. nat/builder/front_end.py +1 -1
  28. nat/builder/function.py +378 -8
  29. nat/builder/function_base.py +3 -3
  30. nat/builder/function_info.py +6 -8
  31. nat/builder/intermediate_step_manager.py +6 -2
  32. nat/builder/user_interaction_manager.py +2 -2
  33. nat/builder/workflow.py +13 -1
  34. nat/builder/workflow_builder.py +327 -79
  35. nat/cli/cli_utils/config_override.py +2 -2
  36. nat/cli/commands/evaluate.py +1 -1
  37. nat/cli/commands/info/info.py +16 -6
  38. nat/cli/commands/info/list_channels.py +1 -1
  39. nat/cli/commands/info/list_components.py +7 -8
  40. nat/cli/commands/mcp/__init__.py +14 -0
  41. nat/cli/commands/mcp/mcp.py +986 -0
  42. nat/cli/commands/object_store/__init__.py +14 -0
  43. nat/cli/commands/object_store/object_store.py +227 -0
  44. nat/cli/commands/optimize.py +90 -0
  45. nat/cli/commands/registry/publish.py +2 -2
  46. nat/cli/commands/registry/pull.py +2 -2
  47. nat/cli/commands/registry/remove.py +2 -2
  48. nat/cli/commands/registry/search.py +15 -17
  49. nat/cli/commands/start.py +16 -5
  50. nat/cli/commands/uninstall.py +1 -1
  51. nat/cli/commands/workflow/templates/config.yml.j2 +14 -13
  52. nat/cli/commands/workflow/templates/pyproject.toml.j2 +5 -2
  53. nat/cli/commands/workflow/templates/register.py.j2 +2 -3
  54. nat/cli/commands/workflow/templates/workflow.py.j2 +35 -21
  55. nat/cli/commands/workflow/workflow_commands.py +105 -19
  56. nat/cli/entrypoint.py +17 -11
  57. nat/cli/main.py +3 -0
  58. nat/cli/register_workflow.py +38 -4
  59. nat/cli/type_registry.py +79 -10
  60. nat/control_flow/__init__.py +0 -0
  61. nat/control_flow/register.py +20 -0
  62. nat/control_flow/router_agent/__init__.py +0 -0
  63. nat/control_flow/router_agent/agent.py +329 -0
  64. nat/control_flow/router_agent/prompt.py +48 -0
  65. nat/control_flow/router_agent/register.py +91 -0
  66. nat/control_flow/sequential_executor.py +166 -0
  67. nat/data_models/agent.py +34 -0
  68. nat/data_models/api_server.py +196 -67
  69. nat/data_models/authentication.py +23 -9
  70. nat/data_models/common.py +1 -1
  71. nat/data_models/component.py +2 -0
  72. nat/data_models/component_ref.py +11 -0
  73. nat/data_models/config.py +42 -18
  74. nat/data_models/dataset_handler.py +1 -1
  75. nat/data_models/discovery_metadata.py +4 -4
  76. nat/data_models/evaluate.py +4 -1
  77. nat/data_models/function.py +34 -0
  78. nat/data_models/function_dependencies.py +14 -6
  79. nat/data_models/gated_field_mixin.py +242 -0
  80. nat/data_models/intermediate_step.py +3 -3
  81. nat/data_models/optimizable.py +119 -0
  82. nat/data_models/optimizer.py +149 -0
  83. nat/data_models/span.py +41 -3
  84. nat/data_models/swe_bench_model.py +1 -1
  85. nat/data_models/temperature_mixin.py +44 -0
  86. nat/data_models/thinking_mixin.py +86 -0
  87. nat/data_models/top_p_mixin.py +44 -0
  88. nat/embedder/azure_openai_embedder.py +46 -0
  89. nat/embedder/nim_embedder.py +1 -1
  90. nat/embedder/openai_embedder.py +2 -3
  91. nat/embedder/register.py +1 -1
  92. nat/eval/config.py +3 -1
  93. nat/eval/dataset_handler/dataset_handler.py +71 -7
  94. nat/eval/evaluate.py +86 -31
  95. nat/eval/evaluator/base_evaluator.py +1 -1
  96. nat/eval/evaluator/evaluator_model.py +13 -0
  97. nat/eval/intermediate_step_adapter.py +1 -1
  98. nat/eval/rag_evaluator/evaluate.py +9 -6
  99. nat/eval/rag_evaluator/register.py +3 -3
  100. nat/eval/register.py +4 -1
  101. nat/eval/remote_workflow.py +3 -3
  102. nat/eval/runtime_evaluator/__init__.py +14 -0
  103. nat/eval/runtime_evaluator/evaluate.py +123 -0
  104. nat/eval/runtime_evaluator/register.py +100 -0
  105. nat/eval/swe_bench_evaluator/evaluate.py +6 -6
  106. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  107. nat/eval/trajectory_evaluator/register.py +1 -1
  108. nat/eval/tunable_rag_evaluator/evaluate.py +4 -7
  109. nat/eval/utils/eval_trace_ctx.py +89 -0
  110. nat/eval/utils/weave_eval.py +18 -9
  111. nat/experimental/decorators/experimental_warning_decorator.py +27 -7
  112. nat/experimental/test_time_compute/functions/execute_score_select_function.py +1 -1
  113. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +7 -3
  114. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +3 -3
  115. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +3 -3
  116. nat/experimental/test_time_compute/models/strategy_base.py +5 -4
  117. nat/experimental/test_time_compute/register.py +0 -1
  118. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -3
  119. nat/front_ends/console/authentication_flow_handler.py +82 -30
  120. nat/front_ends/console/console_front_end_plugin.py +19 -7
  121. nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +1 -1
  122. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +52 -17
  123. nat/front_ends/fastapi/dask_client_mixin.py +65 -0
  124. nat/front_ends/fastapi/fastapi_front_end_config.py +36 -5
  125. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  126. nat/front_ends/fastapi/fastapi_front_end_plugin.py +135 -4
  127. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +455 -282
  128. nat/front_ends/fastapi/job_store.py +518 -99
  129. nat/front_ends/fastapi/main.py +11 -19
  130. nat/front_ends/fastapi/message_handler.py +74 -50
  131. nat/front_ends/fastapi/message_validator.py +20 -21
  132. nat/front_ends/fastapi/response_helpers.py +4 -4
  133. nat/front_ends/fastapi/step_adaptor.py +2 -2
  134. nat/front_ends/fastapi/utils.py +57 -0
  135. nat/front_ends/mcp/introspection_token_verifier.py +73 -0
  136. nat/front_ends/mcp/mcp_front_end_config.py +47 -3
  137. nat/front_ends/mcp/mcp_front_end_plugin.py +48 -13
  138. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +120 -8
  139. nat/front_ends/mcp/tool_converter.py +44 -14
  140. nat/front_ends/register.py +0 -1
  141. nat/front_ends/simple_base/simple_front_end_plugin_base.py +3 -1
  142. nat/llm/aws_bedrock_llm.py +24 -12
  143. nat/llm/azure_openai_llm.py +57 -0
  144. nat/llm/litellm_llm.py +69 -0
  145. nat/llm/nim_llm.py +20 -8
  146. nat/llm/openai_llm.py +14 -6
  147. nat/llm/register.py +5 -1
  148. nat/llm/utils/env_config_value.py +2 -3
  149. nat/llm/utils/thinking.py +215 -0
  150. nat/meta/pypi.md +9 -9
  151. nat/object_store/register.py +0 -1
  152. nat/observability/exporter/base_exporter.py +3 -3
  153. nat/observability/exporter/file_exporter.py +1 -1
  154. nat/observability/exporter/processing_exporter.py +309 -81
  155. nat/observability/exporter/span_exporter.py +35 -15
  156. nat/observability/exporter_manager.py +7 -7
  157. nat/observability/mixin/file_mixin.py +7 -7
  158. nat/observability/mixin/redaction_config_mixin.py +42 -0
  159. nat/observability/mixin/tagging_config_mixin.py +62 -0
  160. nat/observability/mixin/type_introspection_mixin.py +420 -107
  161. nat/observability/processor/batching_processor.py +5 -7
  162. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  163. nat/observability/processor/processor.py +3 -0
  164. nat/observability/processor/processor_factory.py +70 -0
  165. nat/observability/processor/redaction/__init__.py +24 -0
  166. nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
  167. nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
  168. nat/observability/processor/redaction/redaction_processor.py +177 -0
  169. nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
  170. nat/observability/processor/span_tagging_processor.py +68 -0
  171. nat/observability/register.py +22 -4
  172. nat/profiler/calc/calc_runner.py +3 -4
  173. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  174. nat/profiler/callbacks/langchain_callback_handler.py +14 -7
  175. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  176. nat/profiler/callbacks/semantic_kernel_callback_handler.py +3 -3
  177. nat/profiler/data_frame_row.py +1 -1
  178. nat/profiler/decorators/framework_wrapper.py +62 -13
  179. nat/profiler/decorators/function_tracking.py +160 -3
  180. nat/profiler/forecasting/models/forecasting_base_model.py +3 -1
  181. nat/profiler/forecasting/models/linear_model.py +1 -1
  182. nat/profiler/forecasting/models/random_forest_regressor.py +1 -1
  183. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +1 -1
  184. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
  185. nat/profiler/inference_optimization/data_models.py +3 -3
  186. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +8 -9
  187. nat/profiler/inference_optimization/token_uniqueness.py +1 -1
  188. nat/profiler/parameter_optimization/__init__.py +0 -0
  189. nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
  190. nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
  191. nat/profiler/parameter_optimization/parameter_optimizer.py +164 -0
  192. nat/profiler/parameter_optimization/parameter_selection.py +107 -0
  193. nat/profiler/parameter_optimization/pareto_visualizer.py +395 -0
  194. nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
  195. nat/profiler/parameter_optimization/update_helpers.py +66 -0
  196. nat/profiler/profile_runner.py +14 -9
  197. nat/profiler/utils.py +4 -2
  198. nat/registry_handlers/local/local_handler.py +2 -2
  199. nat/registry_handlers/package_utils.py +1 -2
  200. nat/registry_handlers/pypi/pypi_handler.py +23 -26
  201. nat/registry_handlers/register.py +3 -4
  202. nat/registry_handlers/rest/rest_handler.py +12 -13
  203. nat/retriever/milvus/retriever.py +2 -2
  204. nat/retriever/nemo_retriever/retriever.py +1 -1
  205. nat/retriever/register.py +0 -1
  206. nat/runtime/loader.py +2 -2
  207. nat/runtime/runner.py +105 -8
  208. nat/runtime/session.py +69 -8
  209. nat/settings/global_settings.py +16 -5
  210. nat/tool/chat_completion.py +5 -2
  211. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +3 -3
  212. nat/tool/datetime_tools.py +49 -9
  213. nat/tool/document_search.py +2 -2
  214. nat/tool/github_tools.py +450 -0
  215. nat/tool/memory_tools/add_memory_tool.py +3 -3
  216. nat/tool/memory_tools/delete_memory_tool.py +3 -4
  217. nat/tool/memory_tools/get_memory_tool.py +4 -4
  218. nat/tool/nvidia_rag.py +1 -1
  219. nat/tool/register.py +2 -9
  220. nat/tool/retriever.py +3 -2
  221. nat/utils/callable_utils.py +70 -0
  222. nat/utils/data_models/schema_validator.py +3 -3
  223. nat/utils/decorators.py +210 -0
  224. nat/utils/exception_handlers/automatic_retries.py +104 -51
  225. nat/utils/exception_handlers/schemas.py +1 -1
  226. nat/utils/io/yaml_tools.py +2 -2
  227. nat/utils/log_levels.py +25 -0
  228. nat/utils/reactive/base/observable_base.py +2 -2
  229. nat/utils/reactive/base/observer_base.py +1 -1
  230. nat/utils/reactive/observable.py +2 -2
  231. nat/utils/reactive/observer.py +4 -4
  232. nat/utils/reactive/subscription.py +1 -1
  233. nat/utils/settings/global_settings.py +6 -8
  234. nat/utils/type_converter.py +12 -3
  235. nat/utils/type_utils.py +9 -5
  236. nvidia_nat-1.3.0.dist-info/METADATA +195 -0
  237. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/RECORD +244 -200
  238. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/entry_points.txt +1 -0
  239. nat/cli/commands/info/list_mcp.py +0 -304
  240. nat/tool/github_tools/create_github_commit.py +0 -133
  241. nat/tool/github_tools/create_github_issue.py +0 -87
  242. nat/tool/github_tools/create_github_pr.py +0 -106
  243. nat/tool/github_tools/get_github_file.py +0 -106
  244. nat/tool/github_tools/get_github_issue.py +0 -166
  245. nat/tool/github_tools/get_github_pr.py +0 -256
  246. nat/tool/github_tools/update_github_issue.py +0 -100
  247. nat/tool/mcp/exceptions.py +0 -142
  248. nat/tool/mcp/mcp_client.py +0 -255
  249. nat/tool/mcp/mcp_tool.py +0 -96
  250. nat/utils/exception_handlers/mcp.py +0 -211
  251. nvidia_nat-1.2.1.dist-info/METADATA +0 -365
  252. /nat/{tool/github_tools → agent/prompt_optimizer}/__init__.py +0 -0
  253. /nat/{tool/mcp → authentication/credential_validator}/__init__.py +0 -0
  254. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/WHEEL +0 -0
  255. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  256. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/licenses/LICENSE.md +0 -0
  257. {nvidia_nat-1.2.1.dist-info → nvidia_nat-1.3.0.dist-info}/top_level.txt +0 -0
@@ -28,6 +28,7 @@ from pydantic import HttpUrl
28
28
  from pydantic import conlist
29
29
  from pydantic import field_serializer
30
30
  from pydantic import field_validator
31
+ from pydantic import model_validator
31
32
  from pydantic_core.core_schema import ValidationInfo
32
33
 
33
34
  from nat.data_models.interactive import HumanPrompt
@@ -36,6 +37,15 @@ from nat.utils.type_converter import GlobalTypeConverter
36
37
  FINISH_REASONS = frozenset({'stop', 'length', 'tool_calls', 'content_filter', 'function_call'})
37
38
 
38
39
 
40
+ class UserMessageContentRoleType(str, Enum):
41
+ """
42
+ Enum representing chat message roles in API requests and responses.
43
+ """
44
+ USER = "user"
45
+ ASSISTANT = "assistant"
46
+ SYSTEM = "system"
47
+
48
+
39
49
  class Request(BaseModel):
40
50
  """
41
51
  Request is a data model that represents HTTP request attributes.
@@ -108,7 +118,7 @@ UserContent = typing.Annotated[TextContent | ImageContent | AudioContent, Discri
108
118
 
109
119
  class Message(BaseModel):
110
120
  content: str | list[UserContent]
111
- role: str
121
+ role: UserMessageContentRoleType
112
122
 
113
123
 
114
124
  class ChatRequest(BaseModel):
@@ -143,7 +153,6 @@ class ChatRequest(BaseModel):
143
153
  tool_choice: str | dict[str, typing.Any] | None = Field(default=None, description="Controls which tool is called")
144
154
  parallel_tool_calls: bool | None = Field(default=True, description="Whether to enable parallel function calling")
145
155
  user: str | None = Field(default=None, description="Unique identifier representing end-user")
146
-
147
156
  model_config = ConfigDict(extra="allow",
148
157
  json_schema_extra={
149
158
  "example": {
@@ -164,7 +173,7 @@ class ChatRequest(BaseModel):
164
173
  max_tokens: int | None = None,
165
174
  top_p: float | None = None) -> "ChatRequest":
166
175
 
167
- return ChatRequest(messages=[Message(content=data, role="user")],
176
+ return ChatRequest(messages=[Message(content=data, role=UserMessageContentRoleType.USER)],
168
177
  model=model,
169
178
  temperature=temperature,
170
179
  max_tokens=max_tokens,
@@ -178,38 +187,128 @@ class ChatRequest(BaseModel):
178
187
  max_tokens: int | None = None,
179
188
  top_p: float | None = None) -> "ChatRequest":
180
189
 
181
- return ChatRequest(messages=[Message(content=content, role="user")],
190
+ return ChatRequest(messages=[Message(content=content, role=UserMessageContentRoleType.USER)],
182
191
  model=model,
183
192
  temperature=temperature,
184
193
  max_tokens=max_tokens,
185
194
  top_p=top_p)
186
195
 
187
196
 
197
+ class ChatRequestOrMessage(BaseModel):
198
+ """
199
+ `ChatRequestOrMessage` is a data model that represents either a conversation or a string input.
200
+ This is useful for functions that can handle either type of input.
201
+
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
+ )
247
+
248
+ messages: typing.Annotated[list[Message] | None, conlist(Message, min_length=1)] = Field(
249
+ default=None, description="A non-empty conversation of messages to process.")
250
+
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")
254
+
255
+ @property
256
+ def is_string(self) -> bool:
257
+ return self.input_message is not None
258
+
259
+ @property
260
+ def is_conversation(self) -> bool:
261
+ return self.messages is not None
262
+
263
+ @model_validator(mode="after")
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)
271
+ if len(extra_fields) > 0:
272
+ raise ValueError("no extra fields are permitted when input_message is provided")
273
+ return self
274
+
275
+
188
276
  class ChoiceMessage(BaseModel):
189
277
  content: str | None = None
190
- role: str | None = None
278
+ role: UserMessageContentRoleType | None = None
191
279
 
192
280
 
193
281
  class ChoiceDelta(BaseModel):
194
282
  """Delta object for streaming responses (OpenAI-compatible)"""
195
283
  content: str | None = None
196
- role: str | None = None
284
+ role: UserMessageContentRoleType | None = None
197
285
 
198
286
 
199
- class Choice(BaseModel):
287
+ class ChoiceBase(BaseModel):
288
+ """Base choice model with common fields for both streaming and non-streaming responses"""
200
289
  model_config = ConfigDict(extra="allow")
201
-
202
- message: ChoiceMessage | None = None
203
- delta: ChoiceDelta | None = None
204
290
  finish_reason: typing.Literal['stop', 'length', 'tool_calls', 'content_filter', 'function_call'] | None = None
205
291
  index: int
206
- # logprobs: ChoiceLogprobs | None = None
292
+
293
+
294
+ class ChatResponseChoice(ChoiceBase):
295
+ """Choice model for non-streaming responses - contains message field"""
296
+ message: ChoiceMessage
297
+
298
+
299
+ class ChatResponseChunkChoice(ChoiceBase):
300
+ """Choice model for streaming responses - contains delta field"""
301
+ delta: ChoiceDelta
302
+
303
+
304
+ # Backward compatibility alias
305
+ Choice = ChatResponseChoice
207
306
 
208
307
 
209
308
  class Usage(BaseModel):
210
- prompt_tokens: int
211
- completion_tokens: int
212
- total_tokens: int
309
+ prompt_tokens: int | None = None
310
+ completion_tokens: int | None = None
311
+ total_tokens: int | None = None
213
312
 
214
313
 
215
314
  class ResponseSerializable(abc.ABC):
@@ -245,10 +344,10 @@ class ChatResponse(ResponseBaseModelOutput):
245
344
  model_config = ConfigDict(extra="allow")
246
345
  id: str
247
346
  object: str = "chat.completion"
248
- model: str = ""
347
+ model: str = "unknown-model"
249
348
  created: datetime.datetime
250
- choices: list[Choice]
251
- usage: Usage | None = None
349
+ choices: list[ChatResponseChoice]
350
+ usage: Usage
252
351
  system_fingerprint: str | None = None
253
352
  service_tier: typing.Literal["scale", "default"] | None = None
254
353
 
@@ -264,22 +363,27 @@ class ChatResponse(ResponseBaseModelOutput):
264
363
  object_: str | None = None,
265
364
  model: str | None = None,
266
365
  created: datetime.datetime | None = None,
267
- usage: Usage | None = None) -> "ChatResponse":
366
+ usage: Usage) -> "ChatResponse":
268
367
 
269
368
  if id_ is None:
270
369
  id_ = str(uuid.uuid4())
271
370
  if object_ is None:
272
371
  object_ = "chat.completion"
273
372
  if model is None:
274
- model = ""
373
+ model = "unknown-model"
275
374
  if created is None:
276
- created = datetime.datetime.now(datetime.timezone.utc)
375
+ created = datetime.datetime.now(datetime.UTC)
277
376
 
278
377
  return ChatResponse(id=id_,
279
378
  object=object_,
280
379
  model=model,
281
380
  created=created,
282
- choices=[Choice(index=0, message=ChoiceMessage(content=data), finish_reason="stop")],
381
+ choices=[
382
+ ChatResponseChoice(index=0,
383
+ message=ChoiceMessage(content=data,
384
+ role=UserMessageContentRoleType.ASSISTANT),
385
+ finish_reason="stop")
386
+ ],
283
387
  usage=usage)
284
388
 
285
389
 
@@ -293,9 +397,9 @@ class ChatResponseChunk(ResponseBaseModelOutput):
293
397
  model_config = ConfigDict(extra="allow")
294
398
 
295
399
  id: str
296
- choices: list[Choice]
400
+ choices: list[ChatResponseChunkChoice]
297
401
  created: datetime.datetime
298
- model: str = ""
402
+ model: str = "unknown-model"
299
403
  object: str = "chat.completion.chunk"
300
404
  system_fingerprint: str | None = None
301
405
  service_tier: typing.Literal["scale", "default"] | None = None
@@ -317,14 +421,20 @@ class ChatResponseChunk(ResponseBaseModelOutput):
317
421
  if id_ is None:
318
422
  id_ = str(uuid.uuid4())
319
423
  if created is None:
320
- created = datetime.datetime.now(datetime.timezone.utc)
424
+ created = datetime.datetime.now(datetime.UTC)
321
425
  if model is None:
322
- model = ""
426
+ model = "unknown-model"
323
427
  if object_ is None:
324
428
  object_ = "chat.completion.chunk"
325
429
 
326
430
  return ChatResponseChunk(id=id_,
327
- choices=[Choice(index=0, message=ChoiceMessage(content=data), finish_reason="stop")],
431
+ choices=[
432
+ ChatResponseChunkChoice(index=0,
433
+ delta=ChoiceDelta(
434
+ content=data,
435
+ role=UserMessageContentRoleType.ASSISTANT),
436
+ finish_reason="stop")
437
+ ],
328
438
  created=created,
329
439
  model=model,
330
440
  object=object_)
@@ -335,7 +445,7 @@ class ChatResponseChunk(ResponseBaseModelOutput):
335
445
  id_: str | None = None,
336
446
  created: datetime.datetime | None = None,
337
447
  model: str | None = None,
338
- role: str | None = None,
448
+ role: UserMessageContentRoleType | None = None,
339
449
  finish_reason: str | None = None,
340
450
  usage: Usage | None = None,
341
451
  system_fingerprint: str | None = None) -> "ChatResponseChunk":
@@ -343,9 +453,9 @@ class ChatResponseChunk(ResponseBaseModelOutput):
343
453
  if id_ is None:
344
454
  id_ = str(uuid.uuid4())
345
455
  if created is None:
346
- created = datetime.datetime.now(datetime.timezone.utc)
456
+ created = datetime.datetime.now(datetime.UTC)
347
457
  if model is None:
348
- model = ""
458
+ model = "unknown-model"
349
459
 
350
460
  delta = ChoiceDelta(content=content, role=role) if content is not None or role is not None else ChoiceDelta()
351
461
 
@@ -353,7 +463,14 @@ class ChatResponseChunk(ResponseBaseModelOutput):
353
463
 
354
464
  return ChatResponseChunk(
355
465
  id=id_,
356
- choices=[Choice(index=0, message=None, delta=delta, finish_reason=final_finish_reason)],
466
+ choices=[
467
+ ChatResponseChunkChoice(
468
+ index=0,
469
+ delta=delta,
470
+ finish_reason=typing.cast(
471
+ typing.Literal['stop', 'length', 'tool_calls', 'content_filter', 'function_call'] | None,
472
+ final_finish_reason))
473
+ ],
357
474
  created=created,
358
475
  model=model,
359
476
  object="chat.completion.chunk",
@@ -398,11 +515,6 @@ class GenerateResponse(BaseModel):
398
515
  value: str | None = "default"
399
516
 
400
517
 
401
- class UserMessageContentRoleType(str, Enum):
402
- USER = "user"
403
- ASSISTANT = "assistant"
404
-
405
-
406
518
  class WebSocketMessageType(str, Enum):
407
519
  """
408
520
  WebSocketMessageType is an Enum that represents WebSocket Message types.
@@ -485,7 +597,7 @@ class WebSocketUserMessage(BaseModel):
485
597
  security: Security = Security()
486
598
  error: Error = Error()
487
599
  schema_version: str = "1.0.0"
488
- timestamp: str = str(datetime.datetime.now(datetime.timezone.utc))
600
+ timestamp: str = str(datetime.datetime.now(datetime.UTC))
489
601
 
490
602
 
491
603
  class WebSocketUserInteractionResponseMessage(BaseModel):
@@ -496,12 +608,14 @@ class WebSocketUserInteractionResponseMessage(BaseModel):
496
608
  type: typing.Literal[WebSocketMessageType.USER_INTERACTION_MESSAGE]
497
609
  id: str = "default"
498
610
  thread_id: str = "default"
611
+ parent_id: str = "default"
612
+ conversation_id: str | None = None
499
613
  content: UserMessageContent
500
614
  user: User = User()
501
615
  security: Security = Security()
502
616
  error: Error = Error()
503
617
  schema_version: str = "1.0.0"
504
- timestamp: str = str(datetime.datetime.now(datetime.timezone.utc))
618
+ timestamp: str = str(datetime.datetime.now(datetime.UTC))
505
619
 
506
620
 
507
621
  class SystemIntermediateStepContent(BaseModel):
@@ -527,7 +641,7 @@ class WebSocketSystemIntermediateStepMessage(BaseModel):
527
641
  conversation_id: str | None = None
528
642
  content: SystemIntermediateStepContent
529
643
  status: WebSocketMessageStatus
530
- timestamp: str = str(datetime.datetime.now(datetime.timezone.utc))
644
+ timestamp: str = str(datetime.datetime.now(datetime.UTC))
531
645
 
532
646
 
533
647
  class SystemResponseContent(BaseModel):
@@ -551,7 +665,7 @@ class WebSocketSystemResponseTokenMessage(BaseModel):
551
665
  conversation_id: str | None = None
552
666
  content: SystemResponseContent | Error | GenerateResponse
553
667
  status: WebSocketMessageStatus
554
- timestamp: str = str(datetime.datetime.now(datetime.timezone.utc))
668
+ timestamp: str = str(datetime.datetime.now(datetime.UTC))
555
669
 
556
670
  @field_validator("content")
557
671
  @classmethod
@@ -560,7 +674,7 @@ class WebSocketSystemResponseTokenMessage(BaseModel):
560
674
  raise ValueError(f"Field: content must be 'Error' when type is {WebSocketMessageType.ERROR_MESSAGE}")
561
675
 
562
676
  if info.data.get("type") == WebSocketMessageType.RESPONSE_MESSAGE and not isinstance(
563
- value, (SystemResponseContent, GenerateResponse)):
677
+ value, SystemResponseContent | GenerateResponse):
564
678
  raise ValueError(
565
679
  f"Field: content must be 'SystemResponseContent' when type is {WebSocketMessageType.RESPONSE_MESSAGE}")
566
680
  return value
@@ -582,7 +696,7 @@ class WebSocketSystemInteractionMessage(BaseModel):
582
696
  conversation_id: str | None = None
583
697
  content: HumanPrompt
584
698
  status: WebSocketMessageStatus
585
- timestamp: str = str(datetime.datetime.now(datetime.timezone.utc))
699
+ timestamp: str = str(datetime.datetime.now(datetime.UTC))
586
700
 
587
701
 
588
702
  # ======== GenerateResponse Converters ========
@@ -622,12 +736,52 @@ GlobalTypeConverter.register_converter(_nat_chat_request_to_string)
622
736
 
623
737
 
624
738
  def _string_to_nat_chat_request(data: str) -> ChatRequest:
625
- return ChatRequest.from_string(data, model="")
739
+ return ChatRequest.from_string(data, model="unknown-model")
626
740
 
627
741
 
628
742
  GlobalTypeConverter.register_converter(_string_to_nat_chat_request)
629
743
 
630
744
 
745
+ def _chat_request_or_message_to_chat_request(data: ChatRequestOrMessage) -> ChatRequest:
746
+ if data.input_message is not None:
747
+ return _string_to_nat_chat_request(data.input_message)
748
+ return ChatRequest(**data.model_dump(exclude={"input_message"}))
749
+
750
+
751
+ GlobalTypeConverter.register_converter(_chat_request_or_message_to_chat_request)
752
+
753
+
754
+ def _chat_request_to_chat_request_or_message(data: ChatRequest) -> ChatRequestOrMessage:
755
+ return ChatRequestOrMessage(**data.model_dump(by_alias=True))
756
+
757
+
758
+ GlobalTypeConverter.register_converter(_chat_request_to_chat_request_or_message)
759
+
760
+
761
+ def _chat_request_or_message_to_string(data: ChatRequestOrMessage) -> str:
762
+ if data.input_message is not None:
763
+ return data.input_message
764
+ # Extract content from last message in conversation
765
+ if data.messages is None:
766
+ return ""
767
+ content = data.messages[-1].content
768
+ if content is None:
769
+ return ""
770
+ if isinstance(content, str):
771
+ return content
772
+ return str(content)
773
+
774
+
775
+ GlobalTypeConverter.register_converter(_chat_request_or_message_to_string)
776
+
777
+
778
+ def _string_to_chat_request_or_message(data: str) -> ChatRequestOrMessage:
779
+ return ChatRequestOrMessage(input_message=data)
780
+
781
+
782
+ GlobalTypeConverter.register_converter(_string_to_chat_request_or_message)
783
+
784
+
631
785
  # ======== ChatResponse Converters ========
632
786
  def _nat_chat_response_to_string(data: ChatResponse) -> str:
633
787
  if data.choices and data.choices[0].message:
@@ -654,22 +808,12 @@ def _string_to_nat_chat_response(data: str) -> ChatResponse:
654
808
  GlobalTypeConverter.register_converter(_string_to_nat_chat_response)
655
809
 
656
810
 
657
- def _chat_response_to_chat_response_chunk(data: ChatResponse) -> ChatResponseChunk:
658
- # Preserve original message structure for backward compatibility
659
- return ChatResponseChunk(id=data.id, choices=data.choices, created=data.created, model=data.model)
660
-
661
-
662
- GlobalTypeConverter.register_converter(_chat_response_to_chat_response_chunk)
663
-
664
-
665
811
  # ======== ChatResponseChunk Converters ========
666
812
  def _chat_response_chunk_to_string(data: ChatResponseChunk) -> str:
667
813
  if data.choices and len(data.choices) > 0:
668
814
  choice = data.choices[0]
669
815
  if choice.delta and choice.delta.content:
670
816
  return choice.delta.content
671
- if choice.message and choice.message.content:
672
- return choice.message.content
673
817
  return ""
674
818
 
675
819
 
@@ -685,21 +829,6 @@ def _string_to_nat_chat_response_chunk(data: str) -> ChatResponseChunk:
685
829
 
686
830
  GlobalTypeConverter.register_converter(_string_to_nat_chat_response_chunk)
687
831
 
688
-
689
- # ======== AINodeMessageChunk Converters ========
690
- def _ai_message_chunk_to_nat_chat_response_chunk(data) -> ChatResponseChunk:
691
- '''Converts LangChain AINodeMessageChunk to ChatResponseChunk'''
692
- content = ""
693
- if hasattr(data, 'content') and data.content is not None:
694
- content = str(data.content)
695
- elif hasattr(data, 'text') and data.text is not None:
696
- content = str(data.text)
697
- elif hasattr(data, 'message') and data.message is not None:
698
- content = str(data.message)
699
-
700
- return ChatResponseChunk.create_streaming_chunk(content=content, role="assistant", finish_reason=None)
701
-
702
-
703
832
  # Compatibility aliases with previous releases
704
833
  AIQChatRequest = ChatRequest
705
834
  AIQChoiceMessage = ChoiceMessage
@@ -14,8 +14,8 @@
14
14
  # limitations under the License.
15
15
 
16
16
  import typing
17
+ from datetime import UTC
17
18
  from datetime import datetime
18
- from datetime import timezone
19
19
  from enum import Enum
20
20
 
21
21
  import httpx
@@ -166,17 +166,31 @@ class BearerTokenCred(_CredBase):
166
166
 
167
167
 
168
168
  Credential = typing.Annotated[
169
- typing.Union[
170
- HeaderCred,
171
- QueryCred,
172
- CookieCred,
173
- BasicAuthCred,
174
- BearerTokenCred,
175
- ],
169
+ HeaderCred | QueryCred | CookieCred | BasicAuthCred | BearerTokenCred,
176
170
  Field(discriminator="kind"),
177
171
  ]
178
172
 
179
173
 
174
+ class TokenValidationResult(BaseModel):
175
+ """
176
+ Standard result for Bearer Token Validation.
177
+ """
178
+ model_config = ConfigDict(extra="forbid")
179
+
180
+ client_id: str | None = Field(description="OAuth2 client identifier")
181
+ scopes: list[str] | None = Field(default=None, description="List of granted scopes (introspection only)")
182
+ expires_at: int | None = Field(default=None, description="Token expiration time (Unix timestamp)")
183
+ audience: list[str] | None = Field(default=None, description="Token audiences (aud claim)")
184
+ subject: str | None = Field(default=None, description="Token subject (sub claim)")
185
+ issuer: str | None = Field(default=None, description="Token issuer (iss claim)")
186
+ token_type: str = Field(description="Token type")
187
+ active: bool | None = Field(default=True, description="Token active status")
188
+ nbf: int | None = Field(default=None, description="Not before time (Unix timestamp)")
189
+ iat: int | None = Field(default=None, description="Issued at time (Unix timestamp)")
190
+ jti: str | None = Field(default=None, description="JWT ID")
191
+ username: str | None = Field(default=None, description="Username (introspection only)")
192
+
193
+
180
194
  class AuthResult(BaseModel):
181
195
  """
182
196
  Represents the result of an authentication process.
@@ -193,7 +207,7 @@ class AuthResult(BaseModel):
193
207
  """
194
208
  Checks if the authentication token has expired.
195
209
  """
196
- return bool(self.token_expires_at and datetime.now(timezone.utc) >= self.token_expires_at)
210
+ return bool(self.token_expires_at and datetime.now(UTC) >= self.token_expires_at)
197
211
 
198
212
  def as_requests_kwargs(self) -> dict[str, typing.Any]:
199
213
  """
nat/data_models/common.py CHANGED
@@ -160,7 +160,7 @@ class TypedBaseModel(BaseModel):
160
160
 
161
161
  @staticmethod
162
162
  def discriminator(v: typing.Any) -> str | None:
163
- # If its serialized, then we use the alias
163
+ # If it's serialized, then we use the alias
164
164
  if isinstance(v, dict):
165
165
  return v.get("_type", v.get("type"))
166
166
 
@@ -27,6 +27,7 @@ class ComponentEnum(StrEnum):
27
27
  EVALUATOR = "evaluator"
28
28
  FRONT_END = "front_end"
29
29
  FUNCTION = "function"
30
+ FUNCTION_GROUP = "function_group"
30
31
  TTC_STRATEGY = "ttc_strategy"
31
32
  LLM_CLIENT = "llm_client"
32
33
  LLM_PROVIDER = "llm_provider"
@@ -47,6 +48,7 @@ class ComponentGroup(StrEnum):
47
48
  AUTHENTICATION = "authentication"
48
49
  EMBEDDERS = "embedders"
49
50
  FUNCTIONS = "functions"
51
+ FUNCTION_GROUPS = "function_groups"
50
52
  TTC_STRATEGIES = "ttc_strategies"
51
53
  LLMS = "llms"
52
54
  MEMORY = "memory"
@@ -102,6 +102,17 @@ class FunctionRef(ComponentRef):
102
102
  return ComponentGroup.FUNCTIONS
103
103
 
104
104
 
105
+ class FunctionGroupRef(ComponentRef):
106
+ """
107
+ A reference to a function group in a NAT configuration object.
108
+ """
109
+
110
+ @property
111
+ @override
112
+ def component_group(self):
113
+ return ComponentGroup.FUNCTION_GROUPS
114
+
115
+
105
116
  class LLMRef(ComponentRef):
106
117
  """
107
118
  A reference to an LLM in a NAT configuration object.