nvidia-nat 1.4.0a20251120__py3-none-any.whl → 1.4.0a20260113__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 (492) hide show
  1. aiq/__init__.py +1 -1
  2. nat/{front_ends/mcp → agent/auto_memory_wrapper}/__init__.py +1 -1
  3. nat/agent/auto_memory_wrapper/agent.py +278 -0
  4. nat/agent/auto_memory_wrapper/register.py +227 -0
  5. nat/agent/auto_memory_wrapper/state.py +30 -0
  6. nat/agent/base.py +1 -1
  7. nat/agent/dual_node.py +1 -1
  8. nat/agent/prompt_optimizer/prompt.py +1 -1
  9. nat/agent/prompt_optimizer/register.py +1 -1
  10. nat/agent/react_agent/agent.py +16 -9
  11. nat/agent/react_agent/output_parser.py +2 -2
  12. nat/agent/react_agent/prompt.py +3 -2
  13. nat/agent/react_agent/register.py +2 -2
  14. nat/agent/react_agent/register_per_user_agent.py +104 -0
  15. nat/agent/reasoning_agent/reasoning_agent.py +1 -1
  16. nat/agent/register.py +3 -1
  17. nat/agent/responses_api_agent/__init__.py +1 -1
  18. nat/agent/responses_api_agent/register.py +1 -1
  19. nat/agent/rewoo_agent/agent.py +9 -4
  20. nat/agent/rewoo_agent/prompt.py +1 -1
  21. nat/agent/rewoo_agent/register.py +1 -1
  22. nat/agent/tool_calling_agent/agent.py +5 -4
  23. nat/agent/tool_calling_agent/register.py +1 -1
  24. nat/authentication/__init__.py +1 -1
  25. nat/authentication/api_key/__init__.py +1 -1
  26. nat/authentication/api_key/api_key_auth_provider.py +1 -1
  27. nat/authentication/api_key/api_key_auth_provider_config.py +22 -7
  28. nat/authentication/api_key/register.py +1 -1
  29. nat/authentication/credential_validator/__init__.py +1 -1
  30. nat/authentication/credential_validator/bearer_token_validator.py +1 -1
  31. nat/authentication/exceptions/__init__.py +1 -1
  32. nat/authentication/exceptions/api_key_exceptions.py +1 -1
  33. nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
  34. nat/authentication/http_basic_auth/register.py +1 -1
  35. nat/authentication/interfaces.py +1 -1
  36. nat/authentication/oauth2/__init__.py +1 -1
  37. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +1 -1
  38. nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +1 -1
  39. nat/authentication/oauth2/oauth2_resource_server_config.py +1 -1
  40. nat/authentication/oauth2/register.py +1 -1
  41. nat/authentication/register.py +1 -1
  42. nat/builder/builder.py +511 -1
  43. nat/builder/child_builder.py +385 -0
  44. nat/builder/component_utils.py +28 -4
  45. nat/builder/context.py +17 -1
  46. nat/builder/embedder.py +1 -1
  47. nat/builder/eval_builder.py +19 -7
  48. nat/builder/evaluator.py +1 -1
  49. nat/builder/framework_enum.py +2 -1
  50. nat/builder/front_end.py +1 -1
  51. nat/builder/function.py +40 -3
  52. nat/builder/function_base.py +1 -1
  53. nat/builder/function_info.py +1 -1
  54. nat/builder/intermediate_step_manager.py +1 -1
  55. nat/builder/llm.py +1 -1
  56. nat/builder/per_user_workflow_builder.py +843 -0
  57. nat/builder/retriever.py +1 -1
  58. nat/builder/sync_builder.py +571 -0
  59. nat/builder/user_interaction_manager.py +1 -1
  60. nat/builder/workflow.py +1 -1
  61. nat/builder/workflow_builder.py +536 -424
  62. nat/cli/__init__.py +1 -1
  63. nat/cli/cli_utils/config_override.py +1 -1
  64. nat/cli/cli_utils/validation.py +32 -1
  65. nat/cli/commands/configure/channel/add.py +1 -1
  66. nat/cli/commands/configure/channel/channel.py +1 -1
  67. nat/cli/commands/configure/channel/remove.py +1 -1
  68. nat/cli/commands/configure/channel/update.py +1 -1
  69. nat/cli/commands/configure/configure.py +1 -1
  70. nat/cli/commands/evaluate.py +87 -13
  71. nat/cli/commands/finetune.py +132 -0
  72. nat/cli/commands/info/__init__.py +1 -1
  73. nat/cli/commands/info/info.py +1 -1
  74. nat/cli/commands/info/list_channels.py +1 -1
  75. nat/cli/commands/info/list_components.py +1 -1
  76. nat/cli/commands/object_store/__init__.py +1 -1
  77. nat/cli/commands/object_store/object_store.py +1 -1
  78. nat/cli/commands/optimize.py +1 -1
  79. nat/cli/commands/{mcp → red_teaming}/__init__.py +1 -1
  80. nat/cli/commands/red_teaming/red_teaming.py +138 -0
  81. nat/cli/commands/red_teaming/red_teaming_utils.py +73 -0
  82. nat/cli/commands/registry/__init__.py +1 -1
  83. nat/cli/commands/registry/publish.py +1 -1
  84. nat/cli/commands/registry/pull.py +1 -1
  85. nat/cli/commands/registry/registry.py +1 -1
  86. nat/cli/commands/registry/remove.py +1 -1
  87. nat/cli/commands/registry/search.py +1 -1
  88. nat/cli/commands/sizing/__init__.py +1 -1
  89. nat/cli/commands/sizing/calc.py +1 -1
  90. nat/cli/commands/sizing/sizing.py +1 -1
  91. nat/cli/commands/start.py +1 -1
  92. nat/cli/commands/uninstall.py +1 -1
  93. nat/cli/commands/validate.py +1 -1
  94. nat/cli/commands/workflow/__init__.py +1 -1
  95. nat/cli/commands/workflow/workflow.py +1 -1
  96. nat/cli/commands/workflow/workflow_commands.py +3 -2
  97. nat/cli/entrypoint.py +15 -37
  98. nat/cli/main.py +2 -2
  99. nat/cli/plugin_loader.py +69 -0
  100. nat/cli/register_workflow.py +183 -5
  101. nat/cli/type_registry.py +169 -3
  102. nat/control_flow/register.py +1 -1
  103. nat/control_flow/router_agent/agent.py +1 -1
  104. nat/control_flow/router_agent/prompt.py +1 -1
  105. nat/control_flow/router_agent/register.py +1 -1
  106. nat/control_flow/sequential_executor.py +28 -7
  107. nat/data_models/__init__.py +1 -1
  108. nat/data_models/agent.py +1 -1
  109. nat/data_models/api_server.py +38 -3
  110. nat/data_models/authentication.py +1 -1
  111. nat/data_models/common.py +1 -1
  112. nat/data_models/component.py +7 -1
  113. nat/data_models/component_ref.py +34 -1
  114. nat/data_models/config.py +62 -1
  115. nat/data_models/dataset_handler.py +15 -2
  116. nat/data_models/discovery_metadata.py +1 -1
  117. nat/data_models/embedder.py +1 -1
  118. nat/data_models/evaluate.py +6 -1
  119. nat/data_models/evaluator.py +1 -1
  120. nat/data_models/finetuning.py +260 -0
  121. nat/data_models/front_end.py +1 -1
  122. nat/data_models/function.py +1 -1
  123. nat/data_models/function_dependencies.py +1 -1
  124. nat/data_models/gated_field_mixin.py +1 -1
  125. nat/data_models/interactive.py +1 -1
  126. nat/data_models/intermediate_step.py +29 -2
  127. nat/data_models/invocation_node.py +1 -1
  128. nat/data_models/llm.py +1 -1
  129. nat/data_models/logging.py +1 -1
  130. nat/data_models/memory.py +1 -1
  131. nat/data_models/middleware.py +3 -1
  132. nat/data_models/object_store.py +1 -1
  133. nat/data_models/openai_mcp.py +1 -1
  134. nat/data_models/optimizable.py +1 -1
  135. nat/data_models/optimizer.py +1 -1
  136. nat/data_models/profiler.py +1 -1
  137. nat/data_models/registry_handler.py +1 -1
  138. nat/data_models/retriever.py +1 -1
  139. nat/data_models/retry_mixin.py +1 -1
  140. nat/data_models/runtime_enum.py +1 -1
  141. nat/data_models/span.py +1 -1
  142. nat/data_models/step_adaptor.py +1 -1
  143. nat/data_models/streaming.py +1 -1
  144. nat/data_models/swe_bench_model.py +1 -1
  145. nat/data_models/telemetry_exporter.py +1 -1
  146. nat/data_models/thinking_mixin.py +1 -1
  147. nat/data_models/ttc_strategy.py +1 -1
  148. nat/embedder/azure_openai_embedder.py +1 -1
  149. nat/embedder/nim_embedder.py +1 -1
  150. nat/embedder/openai_embedder.py +1 -1
  151. nat/embedder/register.py +1 -1
  152. nat/eval/__init__.py +1 -1
  153. nat/eval/config.py +8 -1
  154. nat/eval/dataset_handler/dataset_downloader.py +1 -1
  155. nat/eval/dataset_handler/dataset_filter.py +1 -1
  156. nat/eval/dataset_handler/dataset_handler.py +4 -2
  157. nat/eval/evaluate.py +217 -80
  158. nat/eval/evaluator/__init__.py +1 -1
  159. nat/eval/evaluator/base_evaluator.py +2 -2
  160. nat/eval/evaluator/evaluator_model.py +3 -2
  161. nat/eval/intermediate_step_adapter.py +1 -1
  162. nat/eval/llm_validator.py +336 -0
  163. nat/eval/rag_evaluator/evaluate.py +17 -10
  164. nat/eval/rag_evaluator/register.py +1 -1
  165. nat/eval/red_teaming_evaluator/__init__.py +14 -0
  166. nat/eval/red_teaming_evaluator/data_models.py +66 -0
  167. nat/eval/red_teaming_evaluator/evaluate.py +327 -0
  168. nat/eval/red_teaming_evaluator/filter_conditions.py +75 -0
  169. nat/eval/red_teaming_evaluator/register.py +55 -0
  170. nat/eval/register.py +2 -1
  171. nat/eval/remote_workflow.py +1 -1
  172. nat/eval/runners/__init__.py +1 -1
  173. nat/eval/runners/config.py +1 -1
  174. nat/eval/runners/multi_eval_runner.py +1 -1
  175. nat/eval/runners/red_teaming_runner/__init__.py +24 -0
  176. nat/eval/runners/red_teaming_runner/config.py +282 -0
  177. nat/eval/runners/red_teaming_runner/report_utils.py +707 -0
  178. nat/eval/runners/red_teaming_runner/runner.py +867 -0
  179. nat/eval/runtime_evaluator/__init__.py +1 -1
  180. nat/eval/runtime_evaluator/evaluate.py +1 -1
  181. nat/eval/runtime_evaluator/register.py +1 -1
  182. nat/eval/runtime_event_subscriber.py +1 -1
  183. nat/eval/swe_bench_evaluator/evaluate.py +1 -1
  184. nat/eval/swe_bench_evaluator/register.py +1 -1
  185. nat/eval/trajectory_evaluator/evaluate.py +2 -2
  186. nat/eval/trajectory_evaluator/register.py +1 -1
  187. nat/eval/tunable_rag_evaluator/evaluate.py +5 -5
  188. nat/eval/tunable_rag_evaluator/register.py +1 -1
  189. nat/eval/usage_stats.py +1 -1
  190. nat/eval/utils/eval_trace_ctx.py +1 -1
  191. nat/eval/utils/output_uploader.py +1 -1
  192. nat/eval/utils/tqdm_position_registry.py +1 -1
  193. nat/eval/utils/weave_eval.py +1 -1
  194. nat/experimental/decorators/experimental_warning_decorator.py +1 -1
  195. nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +1 -1
  196. nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +1 -1
  197. nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +1 -1
  198. nat/experimental/test_time_compute/functions/execute_score_select_function.py +1 -1
  199. nat/experimental/test_time_compute/functions/multi_llm_judge_function.py +88 -0
  200. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +1 -1
  201. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +1 -1
  202. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
  203. nat/experimental/test_time_compute/models/editor_config.py +1 -1
  204. nat/experimental/test_time_compute/models/scoring_config.py +1 -1
  205. nat/experimental/test_time_compute/models/search_config.py +20 -2
  206. nat/experimental/test_time_compute/models/selection_config.py +33 -2
  207. nat/experimental/test_time_compute/models/stage_enums.py +1 -1
  208. nat/experimental/test_time_compute/models/strategy_base.py +1 -1
  209. nat/experimental/test_time_compute/models/tool_use_config.py +1 -1
  210. nat/experimental/test_time_compute/models/ttc_item.py +1 -1
  211. nat/experimental/test_time_compute/register.py +4 -1
  212. nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +1 -1
  213. nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +1 -1
  214. nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +1 -1
  215. nat/experimental/test_time_compute/search/multi_llm_generation.py +115 -0
  216. nat/experimental/test_time_compute/search/multi_llm_planner.py +1 -1
  217. nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +1 -1
  218. nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +1 -1
  219. nat/experimental/test_time_compute/selection/best_of_n_selector.py +1 -1
  220. nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +1 -1
  221. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -1
  222. nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +1 -1
  223. nat/experimental/test_time_compute/selection/llm_judge_selection.py +127 -0
  224. nat/experimental/test_time_compute/selection/threshold_selector.py +1 -1
  225. nat/finetuning/__init__.py +24 -0
  226. nat/finetuning/finetuning_runtime.py +143 -0
  227. nat/finetuning/interfaces/__init__.py +24 -0
  228. nat/finetuning/interfaces/finetuning_runner.py +261 -0
  229. nat/finetuning/interfaces/trainer_adapter.py +103 -0
  230. nat/finetuning/interfaces/trajectory_builder.py +115 -0
  231. nat/finetuning/utils/__init__.py +15 -0
  232. nat/finetuning/utils/parsers/__init__.py +15 -0
  233. nat/finetuning/utils/parsers/adk_parser.py +141 -0
  234. nat/finetuning/utils/parsers/base_parser.py +238 -0
  235. nat/finetuning/utils/parsers/common.py +91 -0
  236. nat/finetuning/utils/parsers/langchain_parser.py +267 -0
  237. nat/finetuning/utils/parsers/llama_index_parser.py +218 -0
  238. nat/front_ends/__init__.py +1 -1
  239. nat/front_ends/console/__init__.py +1 -1
  240. nat/front_ends/console/authentication_flow_handler.py +1 -1
  241. nat/front_ends/console/console_front_end_config.py +4 -1
  242. nat/front_ends/console/console_front_end_plugin.py +5 -4
  243. nat/front_ends/console/register.py +1 -1
  244. nat/front_ends/cron/__init__.py +1 -1
  245. nat/front_ends/fastapi/__init__.py +1 -1
  246. nat/front_ends/fastapi/async_job.py +128 -0
  247. nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +1 -1
  248. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +13 -9
  249. nat/front_ends/fastapi/dask_client_mixin.py +1 -1
  250. nat/front_ends/fastapi/fastapi_front_end_config.py +1 -1
  251. nat/front_ends/fastapi/fastapi_front_end_controller.py +1 -1
  252. nat/front_ends/fastapi/fastapi_front_end_plugin.py +25 -30
  253. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +195 -60
  254. nat/front_ends/fastapi/html_snippets/__init__.py +1 -1
  255. nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +1 -1
  256. nat/front_ends/fastapi/intermediate_steps_subscriber.py +12 -1
  257. nat/front_ends/fastapi/job_store.py +23 -11
  258. nat/front_ends/fastapi/main.py +1 -1
  259. nat/front_ends/fastapi/message_handler.py +27 -4
  260. nat/front_ends/fastapi/message_validator.py +54 -2
  261. nat/front_ends/fastapi/register.py +1 -1
  262. nat/front_ends/fastapi/response_helpers.py +16 -15
  263. nat/front_ends/fastapi/step_adaptor.py +1 -1
  264. nat/front_ends/fastapi/utils.py +1 -1
  265. nat/front_ends/register.py +1 -2
  266. nat/front_ends/simple_base/__init__.py +1 -1
  267. nat/front_ends/simple_base/simple_front_end_plugin_base.py +6 -4
  268. nat/llm/aws_bedrock_llm.py +1 -1
  269. nat/llm/azure_openai_llm.py +10 -1
  270. nat/llm/dynamo_llm.py +363 -0
  271. nat/llm/huggingface_llm.py +177 -0
  272. nat/llm/litellm_llm.py +1 -1
  273. nat/llm/nim_llm.py +1 -1
  274. nat/llm/openai_llm.py +1 -1
  275. nat/llm/register.py +3 -1
  276. nat/llm/utils/__init__.py +1 -1
  277. nat/llm/utils/env_config_value.py +1 -1
  278. nat/llm/utils/error.py +1 -1
  279. nat/llm/utils/thinking.py +1 -1
  280. nat/memory/__init__.py +1 -1
  281. nat/memory/interfaces.py +1 -1
  282. nat/memory/models.py +1 -1
  283. nat/meta/pypi.md +1 -1
  284. nat/middleware/__init__.py +5 -5
  285. nat/middleware/cache/__init__.py +14 -0
  286. nat/middleware/{cache_middleware.py → cache/cache_middleware.py} +39 -42
  287. nat/middleware/cache/cache_middleware_config.py +44 -0
  288. nat/middleware/cache/register.py +33 -0
  289. nat/middleware/defense/__init__.py +14 -0
  290. nat/middleware/defense/defense_middleware.py +362 -0
  291. nat/middleware/defense/defense_middleware_content_guard.py +455 -0
  292. nat/middleware/defense/defense_middleware_data_models.py +91 -0
  293. nat/middleware/defense/defense_middleware_output_verifier.py +440 -0
  294. nat/middleware/defense/defense_middleware_pii.py +356 -0
  295. nat/middleware/defense/register.py +82 -0
  296. nat/middleware/dynamic/__init__.py +14 -0
  297. nat/middleware/dynamic/dynamic_function_middleware.py +962 -0
  298. nat/middleware/dynamic/dynamic_middleware_config.py +132 -0
  299. nat/middleware/dynamic/register.py +34 -0
  300. nat/middleware/function_middleware.py +236 -52
  301. nat/middleware/logging/__init__.py +14 -0
  302. nat/middleware/logging/logging_middleware.py +67 -0
  303. nat/middleware/logging/logging_middleware_config.py +28 -0
  304. nat/middleware/logging/register.py +33 -0
  305. nat/middleware/middleware.py +142 -28
  306. nat/middleware/red_teaming/__init__.py +14 -0
  307. nat/middleware/red_teaming/red_teaming_middleware.py +344 -0
  308. nat/middleware/red_teaming/red_teaming_middleware_config.py +112 -0
  309. nat/middleware/red_teaming/register.py +47 -0
  310. nat/middleware/register.py +7 -20
  311. nat/middleware/utils/__init__.py +14 -0
  312. nat/middleware/utils/workflow_inventory.py +155 -0
  313. nat/object_store/__init__.py +1 -1
  314. nat/object_store/in_memory_object_store.py +1 -1
  315. nat/object_store/interfaces.py +1 -1
  316. nat/object_store/models.py +1 -1
  317. nat/object_store/register.py +1 -1
  318. nat/observability/__init__.py +1 -1
  319. nat/observability/exporter/__init__.py +1 -1
  320. nat/observability/exporter/base_exporter.py +1 -1
  321. nat/observability/exporter/exporter.py +1 -1
  322. nat/observability/exporter/file_exporter.py +1 -1
  323. nat/observability/exporter/processing_exporter.py +1 -1
  324. nat/observability/exporter/raw_exporter.py +1 -1
  325. nat/observability/exporter/span_exporter.py +7 -1
  326. nat/observability/exporter_manager.py +1 -1
  327. nat/observability/mixin/__init__.py +1 -1
  328. nat/observability/mixin/batch_config_mixin.py +1 -1
  329. nat/observability/mixin/collector_config_mixin.py +1 -1
  330. nat/observability/mixin/file_mixin.py +1 -1
  331. nat/observability/mixin/file_mode.py +1 -1
  332. nat/observability/mixin/redaction_config_mixin.py +1 -1
  333. nat/observability/mixin/resource_conflict_mixin.py +1 -1
  334. nat/observability/mixin/serialize_mixin.py +1 -1
  335. nat/observability/mixin/tagging_config_mixin.py +1 -1
  336. nat/observability/mixin/type_introspection_mixin.py +1 -1
  337. nat/observability/processor/__init__.py +1 -1
  338. nat/observability/processor/batching_processor.py +1 -1
  339. nat/observability/processor/callback_processor.py +1 -1
  340. nat/observability/processor/falsy_batch_filter_processor.py +1 -1
  341. nat/observability/processor/intermediate_step_serializer.py +1 -1
  342. nat/observability/processor/processor.py +1 -1
  343. nat/observability/processor/processor_factory.py +1 -1
  344. nat/observability/processor/redaction/__init__.py +1 -1
  345. nat/observability/processor/redaction/contextual_redaction_processor.py +1 -1
  346. nat/observability/processor/redaction/contextual_span_redaction_processor.py +1 -1
  347. nat/observability/processor/redaction/redaction_processor.py +1 -1
  348. nat/observability/processor/redaction/span_header_redaction_processor.py +1 -1
  349. nat/observability/processor/span_tagging_processor.py +1 -1
  350. nat/observability/register.py +1 -1
  351. nat/observability/utils/__init__.py +1 -1
  352. nat/observability/utils/dict_utils.py +1 -1
  353. nat/observability/utils/time_utils.py +1 -1
  354. nat/profiler/calc/__init__.py +1 -1
  355. nat/profiler/calc/calc_runner.py +3 -3
  356. nat/profiler/calc/calculations.py +1 -1
  357. nat/profiler/calc/data_models.py +1 -1
  358. nat/profiler/calc/plot.py +30 -3
  359. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  360. nat/profiler/callbacks/base_callback_class.py +1 -1
  361. nat/profiler/callbacks/langchain_callback_handler.py +33 -3
  362. nat/profiler/callbacks/llama_index_callback_handler.py +13 -10
  363. nat/profiler/callbacks/semantic_kernel_callback_handler.py +1 -1
  364. nat/profiler/callbacks/token_usage_base_model.py +1 -1
  365. nat/profiler/data_frame_row.py +1 -1
  366. nat/profiler/data_models.py +1 -1
  367. nat/profiler/decorators/framework_wrapper.py +16 -1
  368. nat/profiler/decorators/function_tracking.py +1 -1
  369. nat/profiler/forecasting/config.py +1 -1
  370. nat/profiler/forecasting/model_trainer.py +1 -1
  371. nat/profiler/forecasting/models/__init__.py +1 -1
  372. nat/profiler/forecasting/models/forecasting_base_model.py +1 -1
  373. nat/profiler/forecasting/models/linear_model.py +1 -1
  374. nat/profiler/forecasting/models/random_forest_regressor.py +1 -1
  375. nat/profiler/inference_metrics_model.py +1 -1
  376. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +1 -1
  377. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
  378. nat/profiler/inference_optimization/data_models.py +1 -1
  379. nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +1 -1
  380. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +1 -1
  381. nat/profiler/inference_optimization/llm_metrics.py +1 -1
  382. nat/profiler/inference_optimization/prompt_caching.py +1 -1
  383. nat/profiler/inference_optimization/token_uniqueness.py +1 -1
  384. nat/profiler/inference_optimization/workflow_runtimes.py +1 -1
  385. nat/profiler/intermediate_property_adapter.py +1 -1
  386. nat/profiler/parameter_optimization/optimizable_utils.py +1 -1
  387. nat/profiler/parameter_optimization/optimizer_runtime.py +1 -1
  388. nat/profiler/parameter_optimization/parameter_optimizer.py +1 -1
  389. nat/profiler/parameter_optimization/parameter_selection.py +1 -1
  390. nat/profiler/parameter_optimization/pareto_visualizer.py +1 -1
  391. nat/profiler/parameter_optimization/prompt_optimizer.py +1 -1
  392. nat/profiler/parameter_optimization/update_helpers.py +1 -1
  393. nat/profiler/profile_runner.py +1 -1
  394. nat/profiler/utils.py +1 -1
  395. nat/registry_handlers/local/local_handler.py +1 -1
  396. nat/registry_handlers/local/register_local.py +1 -1
  397. nat/registry_handlers/metadata_factory.py +1 -1
  398. nat/registry_handlers/package_utils.py +1 -1
  399. nat/registry_handlers/pypi/pypi_handler.py +1 -1
  400. nat/registry_handlers/pypi/register_pypi.py +1 -1
  401. nat/registry_handlers/register.py +1 -1
  402. nat/registry_handlers/registry_handler_base.py +1 -1
  403. nat/registry_handlers/rest/register_rest.py +1 -1
  404. nat/registry_handlers/rest/rest_handler.py +1 -1
  405. nat/registry_handlers/schemas/headers.py +1 -1
  406. nat/registry_handlers/schemas/package.py +1 -1
  407. nat/registry_handlers/schemas/publish.py +1 -1
  408. nat/registry_handlers/schemas/pull.py +1 -1
  409. nat/registry_handlers/schemas/remove.py +1 -1
  410. nat/registry_handlers/schemas/search.py +1 -1
  411. nat/registry_handlers/schemas/status.py +1 -1
  412. nat/retriever/interface.py +1 -1
  413. nat/retriever/milvus/__init__.py +1 -1
  414. nat/retriever/milvus/register.py +1 -1
  415. nat/retriever/milvus/retriever.py +1 -1
  416. nat/retriever/models.py +1 -1
  417. nat/retriever/nemo_retriever/__init__.py +1 -1
  418. nat/retriever/nemo_retriever/register.py +1 -1
  419. nat/retriever/nemo_retriever/retriever.py +5 -5
  420. nat/retriever/register.py +1 -1
  421. nat/runtime/__init__.py +1 -1
  422. nat/runtime/loader.py +10 -3
  423. nat/runtime/metrics.py +180 -0
  424. nat/runtime/runner.py +1 -5
  425. nat/runtime/session.py +451 -32
  426. nat/runtime/user_metadata.py +1 -1
  427. nat/settings/global_settings.py +1 -1
  428. nat/tool/chat_completion.py +1 -1
  429. nat/tool/code_execution/README.md +1 -1
  430. nat/tool/code_execution/code_sandbox.py +1 -1
  431. nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +1 -1
  432. nat/tool/code_execution/local_sandbox/__init__.py +1 -1
  433. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
  434. nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +1 -1
  435. nat/tool/code_execution/register.py +1 -1
  436. nat/tool/code_execution/utils.py +1 -1
  437. nat/tool/datetime_tools.py +1 -1
  438. nat/tool/document_search.py +1 -1
  439. nat/tool/github_tools.py +1 -1
  440. nat/tool/memory_tools/add_memory_tool.py +1 -1
  441. nat/tool/memory_tools/delete_memory_tool.py +1 -1
  442. nat/tool/memory_tools/get_memory_tool.py +1 -1
  443. nat/tool/nvidia_rag.py +2 -2
  444. nat/tool/register.py +1 -1
  445. nat/tool/retriever.py +1 -1
  446. nat/tool/server_tools.py +1 -1
  447. nat/utils/__init__.py +8 -5
  448. nat/utils/callable_utils.py +1 -1
  449. nat/utils/data_models/schema_validator.py +1 -1
  450. nat/utils/debugging_utils.py +1 -1
  451. nat/utils/decorators.py +1 -1
  452. nat/utils/dump_distro_mapping.py +1 -1
  453. nat/utils/exception_handlers/automatic_retries.py +3 -3
  454. nat/utils/exception_handlers/schemas.py +1 -1
  455. nat/utils/io/model_processing.py +1 -1
  456. nat/utils/io/supress_logs.py +33 -0
  457. nat/utils/io/yaml_tools.py +1 -1
  458. nat/utils/log_levels.py +1 -1
  459. nat/utils/log_utils.py +13 -1
  460. nat/utils/metadata_utils.py +1 -1
  461. nat/utils/optional_imports.py +1 -1
  462. nat/utils/producer_consumer_queue.py +1 -1
  463. nat/utils/reactive/base/observable_base.py +1 -1
  464. nat/utils/reactive/base/observer_base.py +1 -1
  465. nat/utils/reactive/base/subject_base.py +1 -1
  466. nat/utils/reactive/observable.py +1 -1
  467. nat/utils/reactive/observer.py +1 -1
  468. nat/utils/reactive/subject.py +1 -1
  469. nat/utils/reactive/subscription.py +1 -1
  470. nat/utils/responses_api.py +1 -1
  471. nat/utils/settings/global_settings.py +1 -1
  472. nat/utils/string_utils.py +1 -1
  473. nat/utils/type_converter.py +18 -5
  474. nat/utils/type_utils.py +1 -1
  475. nat/utils/url_utils.py +1 -1
  476. {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/METADATA +39 -14
  477. nvidia_nat-1.4.0a20260113.dist-info/RECORD +547 -0
  478. nvidia_nat-1.4.0a20260113.dist-info/entry_points.txt +38 -0
  479. nat/cli/commands/mcp/mcp.py +0 -986
  480. nat/front_ends/mcp/introspection_token_verifier.py +0 -73
  481. nat/front_ends/mcp/mcp_front_end_config.py +0 -109
  482. nat/front_ends/mcp/mcp_front_end_plugin.py +0 -155
  483. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +0 -388
  484. nat/front_ends/mcp/memory_profiler.py +0 -320
  485. nat/front_ends/mcp/register.py +0 -27
  486. nat/front_ends/mcp/tool_converter.py +0 -321
  487. nvidia_nat-1.4.0a20251120.dist-info/RECORD +0 -488
  488. nvidia_nat-1.4.0a20251120.dist-info/entry_points.txt +0 -23
  489. {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/WHEEL +0 -0
  490. {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  491. {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE.md +0 -0
  492. {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,455 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """
16
+ Content Safety Guard Middleware.
17
+
18
+ This middleware uses guard models to classify content as safe or harmful
19
+ with simple Yes/No answers.
20
+ """
21
+
22
+ import json
23
+ import logging
24
+ import re
25
+ from collections.abc import AsyncIterator
26
+ from typing import Any
27
+
28
+ from pydantic import Field
29
+
30
+ from nat.middleware.defense.defense_middleware import DefenseMiddleware
31
+ from nat.middleware.defense.defense_middleware import DefenseMiddlewareConfig
32
+ from nat.middleware.defense.defense_middleware_data_models import ContentAnalysisResult
33
+ from nat.middleware.defense.defense_middleware_data_models import GuardResponseResult
34
+ from nat.middleware.function_middleware import CallNext
35
+ from nat.middleware.function_middleware import CallNextStream
36
+ from nat.middleware.middleware import FunctionMiddlewareContext
37
+
38
+ logger = logging.getLogger(__name__)
39
+
40
+
41
+ class ContentSafetyGuardMiddlewareConfig(DefenseMiddlewareConfig, name="content_safety_guard"):
42
+ """Configuration for Content Safety Guard middleware.
43
+
44
+ This middleware uses guard models to classify content as safe or harmful.
45
+
46
+ Actions: partial_compliance (log warning but allow), refusal (block content),
47
+ or redirection (replace with polite refusal message).
48
+
49
+ Note: Only output analysis is currently supported (target_location='output').
50
+ """
51
+
52
+ llm_name: str = Field(description="Name of the guard model LLM (must be defined in llms section)")
53
+
54
+
55
+ class ContentSafetyGuardMiddleware(DefenseMiddleware):
56
+ """Safety guard middleware using guard models to classify content as safe or unsafe.
57
+
58
+ This middleware analyzes content using guard models (e.g., NVIDIA Nemoguard, Qwen Guard)
59
+ that return "Safe" or "Unsafe" classifications. The middleware extracts safety categories
60
+ when unsafe content is detected.
61
+
62
+ Only output analysis is currently supported (``target_location='output'``).
63
+
64
+ Streaming Behavior:
65
+ For 'refusal' and 'redirection' actions, chunks are buffered and checked
66
+ before yielding to prevent unsafe content from being streamed to clients.
67
+ For 'partial_compliance' action, chunks are yielded immediately; violations
68
+ are logged but content passes through.
69
+ """
70
+
71
+ def __init__(self, config: ContentSafetyGuardMiddlewareConfig, builder):
72
+ """Initialize content safety guard middleware.
73
+
74
+ Args:
75
+ config: Configuration for content safety guard middleware
76
+ builder: Builder instance for loading LLMs
77
+ """
78
+ super().__init__(config, builder)
79
+ # Store config with correct type for linter
80
+ self.config: ContentSafetyGuardMiddlewareConfig = config
81
+ self._llm = None # Lazy loaded LLM
82
+
83
+ # Content Safety Guard only supports output analysis
84
+ if config.target_location == "input":
85
+ raise ValueError("ContentSafetyGuardMiddleware only supports target_location='output'. "
86
+ "Input analysis is not yet supported.")
87
+
88
+ async def _get_llm(self):
89
+ """Lazy load the guard model LLM when first needed."""
90
+ if self._llm is None:
91
+ self._llm = await self._get_llm_for_defense(self.config.llm_name)
92
+ return self._llm
93
+
94
+ def _extract_unsafe_categories(self, response_text: str, is_safe: bool) -> list[str]:
95
+ """Extract safety categories only if content is unsafe.
96
+
97
+ Supports both JSON formats (Safety Categories field) and text formats
98
+ (Categories: line).
99
+
100
+ Args:
101
+ response_text: Raw response from guard model.
102
+ is_safe: Whether the content was detected as safe.
103
+
104
+ Returns:
105
+ List of category strings if unsafe, empty list otherwise or on parsing error.
106
+ """
107
+ if is_safe:
108
+ return []
109
+
110
+ try:
111
+ categories = []
112
+
113
+ # Try parsing as JSON first (for Nemoguard)
114
+ try:
115
+ json_data = json.loads(response_text)
116
+ # Look for common category field names
117
+ category_field = None
118
+ for field in ["Safety Categories", "Categories", "Category", "safety_categories", "categories"]:
119
+ if field in json_data:
120
+ category_field = json_data[field]
121
+ break
122
+
123
+ if category_field:
124
+ if isinstance(category_field, str):
125
+ # Split by comma if it's a comma-separated string
126
+ categories = [cat.strip() for cat in category_field.split(",")]
127
+ elif isinstance(category_field, list):
128
+ categories = [str(cat).strip() for cat in category_field]
129
+ except (json.JSONDecodeError, ValueError, AttributeError):
130
+ # Not JSON, try text parsing (for Qwen Guard)
131
+ # Look for "Categories:" or "Category:" followed by text
132
+ category_patterns = [
133
+ r'Categories?:\s*([^\n]+)', # Categories: Violent
134
+ r'Categories?\s*=\s*([^\n]+)', # Categories = Violent
135
+ r'"Safety Categories":\s*"([^"]+)"', # JSON-like in text
136
+ ]
137
+
138
+ for pattern in category_patterns:
139
+ match = re.search(pattern, response_text, re.IGNORECASE)
140
+ if match:
141
+ category_text = match.group(1).strip()
142
+ # Split by comma if comma-separated
143
+ categories = [cat.strip() for cat in category_text.split(",")]
144
+ break
145
+
146
+ return categories
147
+ except Exception:
148
+ # If any error occurs during category extraction, return empty list
149
+ logger.debug("Failed to extract categories from guard response, returning empty list")
150
+ return []
151
+
152
+ def _parse_guard_response(self, response_text: str) -> GuardResponseResult:
153
+ """Parse guard model response.
154
+
155
+ Searches for Safe or Unsafe keywords anywhere in the response (case-insensitive).
156
+ Works with any guard model format (JSON, structured text, or plain text).
157
+ Also extracts safety categories from both JSON and text formats.
158
+ If neither keyword is found, falls back to implicit refusal detection.
159
+ Prioritizes Unsafe if both keywords are present.
160
+
161
+ Args:
162
+ response_text: Raw response from guard model.
163
+
164
+ Returns:
165
+ GuardResponseResult with is_safe boolean, categories list, and raw response.
166
+ """
167
+ cleaned_text = re.sub(r'[*_]+', '', response_text).strip()
168
+ response_lower = cleaned_text.lower()
169
+
170
+ # Search for "Unsafe" or "Safe" anywhere in the response (case-insensitive)
171
+ # Prioritize "Unsafe" if both are present
172
+ if re.search(r'\bunsafe\b', response_lower):
173
+ is_safe = False
174
+ elif re.search(r'\bsafe\b', response_lower):
175
+ is_safe = True
176
+ else:
177
+ # Detect implicit refusals (model refuses = harmful content detected)
178
+ refusal_indicators = [
179
+ "i cannot",
180
+ "i can't",
181
+ "i won't",
182
+ "i will not",
183
+ "i'm unable",
184
+ "i am unable",
185
+ "i refuse",
186
+ "cannot provide",
187
+ "can't provide",
188
+ "won't provide",
189
+ "illegal",
190
+ "harmful",
191
+ "dangerous",
192
+ "inappropriate"
193
+ ]
194
+
195
+ if any(indicator in response_lower for indicator in refusal_indicators):
196
+ is_safe = False # Model refused = harmful detected
197
+ else:
198
+ is_safe = True # Default to safe if unclear
199
+
200
+ # Extract categories only if unsafe
201
+ categories = self._extract_unsafe_categories(response_text, is_safe)
202
+
203
+ return GuardResponseResult(is_safe=is_safe, categories=categories, raw_response=response_text)
204
+
205
+ def _should_refuse(self, parsed_result: GuardResponseResult) -> bool:
206
+ """Determine if content should be refused.
207
+
208
+ Args:
209
+ parsed_result: Result from _parse_guard_response.
210
+
211
+ Returns:
212
+ True if content should be refused.
213
+ """
214
+ return not parsed_result.is_safe
215
+
216
+ async def _analyze_content(self,
217
+ content: Any,
218
+ original_input: Any = None,
219
+ context: FunctionMiddlewareContext | None = None) -> ContentAnalysisResult:
220
+ """Check content safety using guard model.
221
+
222
+ Args:
223
+ content: The content to analyze
224
+ original_input: The original input to the function (for context)
225
+ context: Function metadata
226
+
227
+ Returns:
228
+ Safety classification result with should_refuse flag
229
+ """
230
+ try:
231
+ # Get the guard model LLM
232
+ llm = await self._get_llm()
233
+
234
+ content_str = str(content)
235
+
236
+ # Call the guard model using messages format to ensure chat template is applied
237
+ # Format matches: messages = [{"role": "user", "content": prompt}]
238
+ messages = [{"role": "user", "content": content_str}]
239
+ response = await llm.ainvoke(messages)
240
+
241
+ # Extract text from response
242
+ if hasattr(response, 'content'):
243
+ response_text = response.content.strip()
244
+ elif isinstance(response, str):
245
+ response_text = response.strip()
246
+ else:
247
+ response_text = str(response).strip()
248
+ # Parse the guard model response
249
+
250
+ parsed = self._parse_guard_response(response_text)
251
+ should_refuse = self._should_refuse(parsed)
252
+
253
+ return ContentAnalysisResult(is_safe=parsed.is_safe,
254
+ categories=parsed.categories,
255
+ raw_response=parsed.raw_response,
256
+ should_refuse=should_refuse,
257
+ error=False,
258
+ error_message=None)
259
+
260
+ except Exception as e:
261
+ logger.exception("Content Safety Guard analysis failed: %s", e)
262
+ return ContentAnalysisResult(is_safe=True,
263
+ categories=[],
264
+ raw_response="",
265
+ should_refuse=False,
266
+ error=True,
267
+ error_message=str(e))
268
+
269
+ async def _handle_threat(self,
270
+ content: Any,
271
+ analysis_result: ContentAnalysisResult,
272
+ context: FunctionMiddlewareContext) -> Any:
273
+ """Handle unsafe content based on configured action.
274
+
275
+ Args:
276
+ content: The unsafe content
277
+ analysis_result: Safety classification result.
278
+ context: Function context
279
+
280
+ Returns:
281
+ Handled content (blocked, sanitized, or original)
282
+ """
283
+ action = self.config.action
284
+
285
+ categories = analysis_result.categories
286
+ logger.warning("Content Safety Guard detected unsafe content in %s (categories: %s)",
287
+ context.name,
288
+ ", ".join(categories) if categories else "none")
289
+
290
+ if action == "refusal":
291
+ logger.error("Content Safety Guard refusing output of %s", context.name)
292
+ raise ValueError("Content blocked by safety policy")
293
+
294
+ elif action == "redirection":
295
+ return "I'm sorry, I cannot help you with that request."
296
+
297
+ else: # action == "partial_compliance"
298
+ logger.warning("Safety violation logged but content passed through")
299
+ return content
300
+
301
+ async def _process_content_safety_detection(
302
+ self,
303
+ value: Any,
304
+ location: str,
305
+ context: FunctionMiddlewareContext,
306
+ original_input: Any = None,
307
+ ) -> Any:
308
+ """Process content safety detection and handling for a given value.
309
+
310
+ Handles field extraction, content safety analysis, threat handling,
311
+ and applying sanitized value back to original structure.
312
+
313
+ Args:
314
+ value: The value to analyze (input or output).
315
+ location: Either input or output (for logging).
316
+ context: Function context metadata.
317
+ original_input: Original function input (for output analysis context).
318
+
319
+ Returns:
320
+ The value after content safety handling (may be unchanged, sanitized, or raise).
321
+ """
322
+ # Extract field from value if target_field is specified
323
+ content_to_analyze, field_info = self._extract_field_from_value(value)
324
+
325
+ logger.info("ContentSafetyGuardMiddleware: Checking %s %s for %s",
326
+ f"field '{self.config.target_field}'" if field_info else "entire",
327
+ location,
328
+ context.name)
329
+ analysis_result = await self._analyze_content(content_to_analyze,
330
+ original_input=original_input,
331
+ context=context)
332
+
333
+ if not analysis_result.should_refuse:
334
+ # Content is safe, return original value
335
+ logger.info("ContentSafetyGuardMiddleware: Verified %s of %s as safe", location, context.name)
336
+ return value
337
+
338
+ # Unsafe content detected - handle based on action
339
+ logger.warning("ContentSafetyGuardMiddleware: Blocking %s for %s (unsafe content detected)",
340
+ location,
341
+ context.name)
342
+ sanitized_content = await self._handle_threat(content_to_analyze, analysis_result, context)
343
+
344
+ # If field was extracted, apply sanitized value back to original structure
345
+ if field_info is not None:
346
+ return self._apply_field_result_to_value(value, field_info, sanitized_content)
347
+ else:
348
+ # No field extraction - return sanitized content directly
349
+ return sanitized_content
350
+
351
+ async def function_middleware_invoke(self,
352
+ *args: Any,
353
+ call_next: CallNext,
354
+ context: FunctionMiddlewareContext,
355
+ **kwargs: Any) -> Any:
356
+ """Apply content safety guard check to function invocation.
357
+
358
+ This is the core logic for content safety guard defense - each defense implements
359
+ its own invoke/stream based on its specific strategy.
360
+
361
+ Args:
362
+ args: Positional arguments passed to the function (first arg is typically the input value).
363
+ call_next: Next middleware/function to call.
364
+ context: Function metadata (provides context state).
365
+ kwargs: Keyword arguments passed to the function.
366
+
367
+ Returns:
368
+ Function output (potentially blocked or sanitized).
369
+ """
370
+ value = args[0] if args else None
371
+
372
+ # Check if defense should apply to this function
373
+ if not self._should_apply_defense(context.name):
374
+ logger.debug("ContentSafetyGuardMiddleware: Skipping %s (not targeted)", context.name)
375
+ return await call_next(value, *args[1:], **kwargs)
376
+
377
+ try:
378
+ # Call the function
379
+ output = await call_next(value, *args[1:], **kwargs)
380
+
381
+ # Handle output analysis (only output is supported)
382
+ output = await self._process_content_safety_detection(output, "output", context, original_input=value)
383
+
384
+ return output
385
+
386
+ except Exception as e:
387
+ logger.error("Failed to apply content safety guard to function %s: %s", context.name, e, exc_info=True)
388
+ raise
389
+
390
+ async def function_middleware_stream(self,
391
+ *args: Any,
392
+ call_next: CallNextStream,
393
+ context: FunctionMiddlewareContext,
394
+ **kwargs: Any) -> AsyncIterator[Any]:
395
+ """Apply content safety guard check to streaming function.
396
+
397
+ For 'refusal' and 'redirection' actions: Chunks are buffered and checked before yielding.
398
+ For 'partial_compliance' action: Chunks are yielded immediately; violations are logged.
399
+
400
+ Args:
401
+ args: Positional arguments passed to the function (first arg is typically the input value).
402
+ call_next: Next middleware/function to call.
403
+ context: Function metadata.
404
+ kwargs: Keyword arguments passed to the function.
405
+
406
+ Yields:
407
+ Function output chunks (potentially blocked or sanitized).
408
+ """
409
+ value = args[0] if args else None
410
+
411
+ # Check if defense should apply to this function
412
+ if not self._should_apply_defense(context.name):
413
+ logger.debug("ContentSafetyGuardMiddleware: Skipping %s (not targeted)", context.name)
414
+ async for chunk in call_next(value, *args[1:], **kwargs):
415
+ yield chunk
416
+ return
417
+
418
+ try:
419
+ buffer_chunks = self.config.action in ("refusal", "redirection")
420
+ accumulated_chunks: list[Any] = []
421
+
422
+ async for chunk in call_next(value, *args[1:], **kwargs):
423
+ if buffer_chunks:
424
+ accumulated_chunks.append(chunk)
425
+ else:
426
+ # partial_compliance: stream through, but still accumulate for analysis/logging
427
+ yield chunk
428
+ accumulated_chunks.append(chunk)
429
+
430
+ # Join chunks efficiently (only convert to string if needed)
431
+ full_output = "".join(chunk if isinstance(chunk, str) else str(chunk) for chunk in accumulated_chunks)
432
+
433
+ processed_output = await self._process_content_safety_detection(full_output,
434
+ "output",
435
+ context,
436
+ original_input=value)
437
+
438
+ processed_str = str(processed_output)
439
+ if self.config.action == "redirection" and processed_str != full_output:
440
+ # Redirected: yield replacement once (and stop).
441
+ yield processed_output
442
+ return
443
+
444
+ if buffer_chunks:
445
+ # refusal: would have raised; safe content: preserve chunking
446
+ for chunk in accumulated_chunks:
447
+ yield chunk
448
+
449
+ except Exception:
450
+ logger.error(
451
+ "Failed to apply content safety guard to streaming function %s",
452
+ context.name,
453
+ exc_info=True,
454
+ )
455
+ raise
@@ -0,0 +1,91 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Data models for defense middleware output."""
16
+
17
+ from typing import Any
18
+
19
+ from pydantic import BaseModel
20
+
21
+
22
+ class PIIAnalysisResult(BaseModel):
23
+ """Result of PII analysis using Presidio.
24
+
25
+ Attributes:
26
+ pii_detected: Whether PII was detected in the analyzed text.
27
+ entities: Dictionary mapping entity types to lists of detection metadata (score, start, end).
28
+ anonymized_text: Text with PII replaced by entity type placeholders (e.g., <EMAIL_ADDRESS>).
29
+ original_text: The unmodified original text that was analyzed.
30
+ """
31
+
32
+ pii_detected: bool
33
+ entities: dict[str, list[dict[str, Any]]]
34
+ anonymized_text: str
35
+ original_text: str
36
+
37
+
38
+ class GuardResponseResult(BaseModel):
39
+ """Result of parsing guard model response.
40
+
41
+ Attributes:
42
+ is_safe: Whether the content is classified as safe by the guard model.
43
+ categories: List of unsafe content categories detected (empty if safe).
44
+ raw_response: The unprocessed response text from the guard model.
45
+ """
46
+
47
+ is_safe: bool
48
+ categories: list[str]
49
+ raw_response: str
50
+
51
+
52
+ class ContentAnalysisResult(BaseModel):
53
+ """Result of content safety analysis with guard models.
54
+
55
+ Attributes:
56
+ is_safe: Whether the content is classified as safe by the guard model.
57
+ categories: List of unsafe content categories detected (empty if safe).
58
+ raw_response: The unprocessed response text from the guard model.
59
+ should_refuse: Whether the content should be refused based on the analysis.
60
+ error: Whether an error occurred during analysis.
61
+ error_message: Error message if error occurred, otherwise None.
62
+ """
63
+
64
+ is_safe: bool
65
+ categories: list[str]
66
+ raw_response: str
67
+ should_refuse: bool
68
+ error: bool = False
69
+ error_message: str | None = None
70
+
71
+
72
+ class OutputVerificationResult(BaseModel):
73
+ """Result of output verification using LLM.
74
+
75
+ Attributes:
76
+ threat_detected: Whether a threat (incorrect or manipulated output) was detected.
77
+ confidence: Confidence score (0.0-1.0) in the threat detection.
78
+ reason: Explanation for the detection result.
79
+ correct_answer: The correct output value if threat detected, otherwise None.
80
+ content_type: Type of content analyzed ('input' or 'output').
81
+ should_refuse: Whether the content should be refused based on threshold.
82
+ error: Whether an error occurred during verification.
83
+ """
84
+
85
+ threat_detected: bool
86
+ confidence: float
87
+ reason: str
88
+ correct_answer: Any | None
89
+ content_type: str
90
+ should_refuse: bool
91
+ error: bool = False