nvidia-nat 1.4.0a20251112__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 +563 -1
  43. nat/builder/child_builder.py +385 -0
  44. nat/builder/component_utils.py +34 -4
  45. nat/builder/context.py +34 -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 +3 -1
  50. nat/builder/front_end.py +1 -1
  51. nat/builder/function.py +113 -5
  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 +5 -3
  61. nat/builder/workflow_builder.py +619 -378
  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 +233 -5
  101. nat/cli/type_registry.py +237 -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 +9 -1
  113. nat/data_models/component_ref.py +45 -1
  114. nat/data_models/config.py +78 -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 +15 -2
  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 +37 -0
  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 +26 -0
  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 +226 -81
  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 +23 -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 +318 -59
  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 +35 -0
  285. nat/middleware/cache/__init__.py +14 -0
  286. nat/middleware/cache/cache_middleware.py +253 -0
  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 +370 -0
  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 +298 -0
  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 +22 -0
  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 +32 -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 +12 -4
  415. nat/retriever/milvus/retriever.py +103 -41
  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 +13 -6
  425. nat/runtime/session.py +458 -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 +2 -2
  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.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/METADATA +46 -15
  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 -151
  483. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +0 -362
  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.0a20251112.dist-info/RECORD +0 -481
  488. nvidia_nat-1.4.0a20251112.dist-info/entry_points.txt +0 -22
  489. {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/WHEEL +0 -0
  490. {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  491. {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE.md +0 -0
  492. {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,962 @@
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
+ from __future__ import annotations
17
+
18
+ import inspect
19
+ import logging
20
+ from collections.abc import Callable
21
+ from typing import Any
22
+
23
+ from nat.builder.builder import Builder
24
+ from nat.builder.function import Function
25
+ from nat.data_models.component import ComponentGroup
26
+ from nat.data_models.component_ref import FunctionRef
27
+ from nat.middleware.dynamic.dynamic_middleware_config import DynamicMiddlewareConfig
28
+ from nat.middleware.function_middleware import FunctionMiddleware
29
+ from nat.middleware.function_middleware import FunctionMiddlewareChain
30
+ from nat.middleware.middleware import FunctionMiddlewareContext
31
+ from nat.middleware.middleware import InvocationContext
32
+ from nat.middleware.utils.workflow_inventory import COMPONENT_FUNCTION_ALLOWLISTS
33
+ from nat.middleware.utils.workflow_inventory import DiscoveredComponent
34
+ from nat.middleware.utils.workflow_inventory import DiscoveredFunction
35
+ from nat.middleware.utils.workflow_inventory import RegisteredComponentMethod
36
+ from nat.middleware.utils.workflow_inventory import RegisteredFunction
37
+ from nat.middleware.utils.workflow_inventory import WorkflowInventory
38
+
39
+ logger = logging.getLogger(__name__)
40
+
41
+
42
+ class DynamicFunctionMiddleware(FunctionMiddleware):
43
+ """Middleware extends FunctionMiddleware to provide dynamic discovery and
44
+ interception of all workflow components, including functions and components, without requiring explicit
45
+ per-component configuration.
46
+ """
47
+
48
+ def __init__(self, config: DynamicMiddlewareConfig, builder: Builder):
49
+ """Initialize middleware and discover workflow functions.
50
+
51
+ Args:
52
+ config: Middleware configuration
53
+ builder: Workflow builder
54
+ """
55
+ super().__init__()
56
+ self._config = config
57
+ self._builder = builder
58
+
59
+ self._registered_callables: dict[str, RegisteredFunction | RegisteredComponentMethod] = {}
60
+
61
+ self._builder_get_llm: Callable | None = None
62
+ self._builder_get_embedder: Callable | None = None
63
+ self._builder_get_retriever: Callable | None = None
64
+ self._builder_get_memory: Callable | None = None
65
+ self._builder_get_object_store: Callable | None = None
66
+ self._builder_get_auth_provider: Callable | None = None
67
+ self._builder_get_function: Callable | None = None
68
+
69
+ self._workflow_inventory: WorkflowInventory = WorkflowInventory()
70
+
71
+ self._component_allowlists: dict[ComponentGroup, set[str]] = self._build_component_allowlists()
72
+
73
+ self._discover_workflow()
74
+
75
+ # ==================== FunctionMiddleware Interface Implementation ====================
76
+
77
+ @property
78
+ def enabled(self) -> bool:
79
+ """Whether this middleware should execute.
80
+
81
+ Returns config.enabled value. Framework checks this before invoking
82
+ any middleware methods.
83
+ """
84
+ return self._config.enabled
85
+
86
+ async def pre_invoke(self, context: InvocationContext) -> InvocationContext | None: # noqa: ARG002
87
+ """Transform inputs before function execution.
88
+
89
+ Default implementation passes through unchanged. Override in subclass
90
+ to add input transformation logic.
91
+
92
+ Args:
93
+ context: Invocation context (Pydantic model) containing:
94
+ - function_context: Static function metadata (frozen)
95
+ - original_args: What entered the middleware chain (frozen)
96
+ - original_kwargs: What entered the middleware chain (frozen)
97
+ - modified_args: Current args (mutable)
98
+ - modified_kwargs: Current kwargs (mutable)
99
+ - output: None (function not yet called)
100
+
101
+ Returns:
102
+ InvocationContext: Return the (modified) context to signal changes
103
+ None: Pass through unchanged (framework uses current context state)
104
+ """
105
+ return None
106
+
107
+ async def post_invoke(self, context: InvocationContext) -> InvocationContext | None: # noqa: ARG002
108
+ """Transform output after function execution.
109
+
110
+ Default implementation passes through unchanged. Override in subclass
111
+ to add output transformation logic.
112
+
113
+ Args:
114
+ context: Invocation context (Pydantic model) containing:
115
+ - function_context: Static function metadata (frozen)
116
+ - original_args: What entered the middleware chain (frozen)
117
+ - original_kwargs: What entered the middleware chain (frozen)
118
+ - modified_args: What the function received (mutable)
119
+ - modified_kwargs: What the function received (mutable)
120
+ - output: Current output value (mutable)
121
+
122
+ Returns:
123
+ InvocationContext: Return the (modified) context to signal changes
124
+ None: Pass through unchanged (framework uses current context.output)
125
+ """
126
+ return None
127
+
128
+ # ==================== Component Discovery and Registration ====================
129
+
130
+ async def _discover_and_register_llm(self, llm_name: str, wrapper_type: Any) -> Any:
131
+ """Intercept LLM creation and register allowlisted component functions with middleware.
132
+
133
+ Args:
134
+ llm_name: LLM component name
135
+ wrapper_type: LLM framework wrapper type
136
+
137
+ Returns:
138
+ The LLM client instance
139
+ """
140
+ # Call the original get_llm to get the actual LLM client
141
+ llm_client = await self._get_builder_get_llm()(llm_name, wrapper_type)
142
+
143
+ if not self._should_intercept_llm(llm_name):
144
+ return llm_client
145
+
146
+ if any(client.name == llm_name for client in self._workflow_inventory.llms):
147
+ return llm_client
148
+
149
+ all_functions = self._get_callable_functions(llm_client, component_type='llm')
150
+
151
+ discovered_component = DiscoveredComponent(name=llm_name,
152
+ component_type=ComponentGroup.LLMS,
153
+ instance=llm_client,
154
+ config=None,
155
+ callable_functions=all_functions)
156
+ self._workflow_inventory.llms.append(discovered_component)
157
+
158
+ for function_name in all_functions:
159
+ try:
160
+ self._register_component_function(discovered_component, function_name)
161
+ except Exception:
162
+ logger.debug("Failed to register component function '%s' on LLM '%s'",
163
+ function_name,
164
+ llm_name,
165
+ exc_info=True)
166
+
167
+ return llm_client
168
+
169
+ async def _discover_and_register_embedder(self, embedder_name: str, wrapper_type: Any) -> Any:
170
+ """Intercept embedder creation and register allowlisted component functions with middleware.
171
+
172
+ Args:
173
+ embedder_name: Embedder component name
174
+ wrapper_type: Embedder framework wrapper type
175
+
176
+ Returns:
177
+ The Embedder client instance
178
+ """
179
+ # Call the original get_embedder to get the actual embedder client
180
+ embedder_client = await self._get_builder_get_embedder()(embedder_name, wrapper_type)
181
+
182
+ if not self._should_intercept_embedder(embedder_name):
183
+ return embedder_client
184
+
185
+ if any(client.name == embedder_name for client in self._workflow_inventory.embedders):
186
+ return embedder_client
187
+
188
+ all_functions = self._get_callable_functions(embedder_client, component_type='embedder')
189
+
190
+ embedder_config = getattr(embedder_client, 'config', None)
191
+ discovered_component = DiscoveredComponent(name=embedder_name,
192
+ component_type=ComponentGroup.EMBEDDERS,
193
+ instance=embedder_client,
194
+ config=embedder_config,
195
+ callable_functions=all_functions)
196
+ self._workflow_inventory.embedders.append(discovered_component)
197
+
198
+ for function_name in all_functions:
199
+ try:
200
+ self._register_component_function(discovered_component, function_name)
201
+ except Exception:
202
+ logger.debug("Failed to register component function '%s' on embedder '%s'",
203
+ function_name,
204
+ embedder_name,
205
+ exc_info=True)
206
+
207
+ return embedder_client
208
+
209
+ async def _discover_and_register_retriever(self, retriever_name: str, wrapper_type: Any = None):
210
+ """Intercept retriever creation and register allowlisted component functions with middleware.
211
+
212
+ Args:
213
+ retriever_name: Retriever component name
214
+ wrapper_type: Retriever framework wrapper type
215
+
216
+ Returns:
217
+ The retriever client instance
218
+ """
219
+ retriever_client = await self._get_builder_get_retriever()(retriever_name, wrapper_type)
220
+
221
+ if not self._should_intercept_retriever(retriever_name):
222
+ return retriever_client
223
+
224
+ if any(client.name == retriever_name for client in self._workflow_inventory.retrievers):
225
+ return retriever_client
226
+
227
+ all_functions = self._get_callable_functions(retriever_client, component_type='retriever')
228
+
229
+ retriever_config = getattr(retriever_client, 'config', None)
230
+ discovered_component = DiscoveredComponent(name=retriever_name,
231
+ component_type=ComponentGroup.RETRIEVERS,
232
+ instance=retriever_client,
233
+ config=retriever_config,
234
+ callable_functions=all_functions)
235
+ self._workflow_inventory.retrievers.append(discovered_component)
236
+
237
+ for function_name in all_functions:
238
+ try:
239
+ self._register_component_function(discovered_component, function_name)
240
+ except Exception:
241
+ logger.debug("Failed to register component function '%s' on retriever '%s'",
242
+ function_name,
243
+ retriever_name,
244
+ exc_info=True)
245
+
246
+ return retriever_client
247
+
248
+ async def _discover_and_register_memory(self, memory_name: str):
249
+ """Intercept memory creation and register allowlisted component functions with middleware.
250
+
251
+ Args:
252
+ memory_name: Memory component name
253
+
254
+ Returns:
255
+ The memory client instance
256
+ """
257
+ memory_client = await self._get_builder_get_memory_client()(memory_name)
258
+
259
+ if not self._should_intercept_memory(memory_name):
260
+ return memory_client
261
+
262
+ if any(client.name == memory_name for client in self._workflow_inventory.memory):
263
+ return memory_client
264
+
265
+ all_functions = self._get_callable_functions(memory_client, component_type='memory')
266
+
267
+ memory_config = getattr(memory_client, 'config', None)
268
+ discovered_component = DiscoveredComponent(name=memory_name,
269
+ component_type=ComponentGroup.MEMORY,
270
+ instance=memory_client,
271
+ config=memory_config,
272
+ callable_functions=all_functions)
273
+ self._workflow_inventory.memory.append(discovered_component)
274
+
275
+ for function_name in all_functions:
276
+ try:
277
+ self._register_component_function(discovered_component, function_name)
278
+ except Exception:
279
+ logger.debug("Failed to register component function '%s' on memory '%s'",
280
+ function_name,
281
+ memory_name,
282
+ exc_info=True)
283
+
284
+ return memory_client
285
+
286
+ async def _discover_and_register_object_store(self, object_store_name: str) -> Any:
287
+ """Intercept object store creation and register allowlisted component functions with middleware.
288
+
289
+
290
+ Args:
291
+ object_store_name: Object store component name
292
+
293
+ Returns:
294
+ The object store client instance
295
+ """
296
+ store_client = await self._get_builder_get_object_store()(object_store_name)
297
+
298
+ if not self._should_intercept_object_store(object_store_name):
299
+ return store_client
300
+
301
+ if any(client.name == object_store_name for client in self._workflow_inventory.object_stores):
302
+ return store_client
303
+
304
+ all_functions = self._get_callable_functions(store_client, component_type='object_store')
305
+
306
+ store_config = getattr(store_client, 'config', None)
307
+ discovered_component = DiscoveredComponent(name=object_store_name,
308
+ component_type=ComponentGroup.OBJECT_STORES,
309
+ instance=store_client,
310
+ config=store_config,
311
+ callable_functions=all_functions)
312
+ self._workflow_inventory.object_stores.append(discovered_component)
313
+
314
+ # Register all functions - filtering happens in _register_component_function
315
+ for function_name in all_functions:
316
+ try:
317
+ self._register_component_function(discovered_component, function_name)
318
+ except Exception:
319
+ logger.debug("Failed to register component function '%s' on object store '%s'",
320
+ function_name,
321
+ object_store_name,
322
+ exc_info=True)
323
+
324
+ return store_client
325
+
326
+ async def _discover_and_register_auth_provider(self, auth_provider_name: str) -> Any:
327
+ """Intercept auth provider creation and register allowlisted component functions with middleware.
328
+
329
+
330
+ Args:
331
+ auth_provider_name: Auth provider component name
332
+
333
+ Returns:
334
+ The auth provider client instance
335
+ """
336
+ auth_client = await self._get_builder_get_auth_provider()(auth_provider_name)
337
+
338
+ if not self._should_intercept_auth_provider(auth_provider_name):
339
+ return auth_client
340
+
341
+ if any(client.name == auth_provider_name for client in self._workflow_inventory.auth_providers):
342
+ return auth_client
343
+
344
+ all_functions = self._get_callable_functions(auth_client, component_type='auth')
345
+
346
+ auth_config = getattr(auth_client, 'config', None)
347
+ discovered_component = DiscoveredComponent(name=auth_provider_name,
348
+ component_type=ComponentGroup.AUTHENTICATION,
349
+ instance=auth_client,
350
+ config=auth_config,
351
+ callable_functions=all_functions)
352
+ self._workflow_inventory.auth_providers.append(discovered_component)
353
+
354
+ # Register all functions - filtering happens in _register_component_function
355
+ for function_name in all_functions:
356
+ try:
357
+ self._register_component_function(discovered_component, function_name)
358
+ except Exception:
359
+ logger.debug("Failed to register component function '%s' on auth provider '%s'",
360
+ function_name,
361
+ auth_provider_name,
362
+ exc_info=True)
363
+
364
+ return auth_client
365
+
366
+ async def _discover_and_register_function(self, name: str | FunctionRef) -> Function:
367
+ """Intercept workflow function and register with middleware.
368
+
369
+ Args:
370
+ name: Function name or reference
371
+
372
+ Returns:
373
+ The function instance
374
+ """
375
+ function = await self._get_builder_get_function()(name)
376
+
377
+ if not self._config.register_workflow_functions:
378
+ return function
379
+
380
+ func_name = str(name)
381
+
382
+ if any(f.name == func_name for f in self._workflow_inventory.workflow_functions):
383
+ return function
384
+
385
+ func_config = self._builder.get_function_config(name)
386
+ discovered_function = DiscoveredFunction(name=func_name, config=func_config, instance=function)
387
+ self._workflow_inventory.workflow_functions.append(discovered_function)
388
+
389
+ # Register with middleware
390
+ self._register_function(discovered_function)
391
+
392
+ return function
393
+
394
+ # ==================== Internal Discovery and Registration ====================
395
+
396
+ def _discover_workflow(self) -> None:
397
+ """Discover workflow functions and patch builder methods for runtime interception."""
398
+ # Patch all builder for runtime discovery and registration
399
+ self._patch_components()
400
+
401
+ # Discover registered functions not listed in the config
402
+ self._discover_functions()
403
+
404
+ def _discover_functions(self) -> None:
405
+ """Discover and register workflow functions already in the builder."""
406
+ if not self._config.register_workflow_functions:
407
+ return
408
+
409
+ if not hasattr(self._builder, '_functions'):
410
+ return
411
+
412
+ # Discover functions already registered
413
+ for func_name, configured_func in self._builder._functions.items(): # type: ignore
414
+ # Skip if already in inventory
415
+ if any(func.name == func_name for func in self._workflow_inventory.workflow_functions):
416
+ continue
417
+
418
+ # Add to inventory
419
+ discovered_function = DiscoveredFunction(name=func_name,
420
+ config=configured_func.config,
421
+ instance=configured_func.instance)
422
+ self._workflow_inventory.workflow_functions.append(discovered_function)
423
+
424
+ # Register with middleware
425
+ self._register_function(discovered_function)
426
+
427
+ # ==================== Helper Methods for Interception ====================
428
+
429
+ def _should_intercept_llm(self, llm_name: str) -> bool:
430
+ """Check if LLM should be intercepted based on config.
431
+
432
+ Args:
433
+ llm_name: Name of the LLM to check
434
+
435
+ Returns:
436
+ True if should intercept, False otherwise
437
+ """
438
+ # Check if already registered
439
+ if any(client.name == llm_name for client in self._workflow_inventory.llms):
440
+ return False
441
+
442
+ # If register_llms is True, intercept all LLMs
443
+ if self._config.register_llms:
444
+ return True
445
+
446
+ # Otherwise, only intercept if explicitly configured
447
+ return self._config.llms is not None and llm_name in self._config.llms
448
+
449
+ def _should_intercept_embedder(self, embedder_name: str) -> bool:
450
+ """Check if embedder should be intercepted based on config.
451
+
452
+ Args:
453
+ embedder_name: Name of the embedder to check
454
+
455
+ Returns:
456
+ True if should intercept, False otherwise
457
+ """
458
+ # Check if already registered
459
+ if any(client.name == embedder_name for client in self._workflow_inventory.embedders):
460
+ return False
461
+
462
+ # If register_embedders is True, intercept all embedders
463
+ if self._config.register_embedders:
464
+ return True
465
+
466
+ # Otherwise, only intercept if explicitly configured
467
+ return self._config.embedders is not None and embedder_name in self._config.embedders
468
+
469
+ def _should_intercept_retriever(self, retriever_name: str) -> bool:
470
+ """Check if retriever should be intercepted based on config.
471
+
472
+ Args:
473
+ retriever_name: Name of the retriever to check
474
+
475
+ Returns:
476
+ True if should intercept, False otherwise
477
+ """
478
+ # Check if already registered
479
+ if any(client.name == retriever_name for client in self._workflow_inventory.retrievers):
480
+ return False
481
+
482
+ # If register_retrievers is True, intercept all retrievers
483
+ if self._config.register_retrievers:
484
+ return True
485
+
486
+ # Otherwise, only intercept if explicitly configured
487
+ return self._config.retrievers is not None and retriever_name in self._config.retrievers
488
+
489
+ def _should_intercept_memory(self, memory_name: str) -> bool:
490
+ """Check if memory provider should be intercepted based on config.
491
+
492
+ Args:
493
+ memory_name: Name of the memory provider to check
494
+
495
+ Returns:
496
+ True if should intercept, False otherwise
497
+ """
498
+ # Check if already registered
499
+ if any(client.name == memory_name for client in self._workflow_inventory.memory):
500
+ return False
501
+
502
+ # If register_memory is True, intercept all memory providers
503
+ if self._config.register_memory:
504
+ return True
505
+
506
+ # Otherwise, only intercept if explicitly configured
507
+ return self._config.memory is not None and memory_name in self._config.memory
508
+
509
+ def _should_intercept_object_store(self, store_name: str) -> bool:
510
+ """Check if object store should be intercepted based on config.
511
+
512
+ Args:
513
+ store_name: Name of the object store to check
514
+
515
+ Returns:
516
+ True if should intercept, False otherwise
517
+ """
518
+ # Check if already registered
519
+ if any(client.name == store_name for client in self._workflow_inventory.object_stores):
520
+ return False
521
+
522
+ # If register_object_stores is True, intercept all object stores
523
+ if self._config.register_object_stores:
524
+ return True
525
+
526
+ # Otherwise, only intercept if explicitly configured
527
+ return self._config.object_stores is not None and store_name in self._config.object_stores
528
+
529
+ def _should_intercept_auth_provider(self, auth_name: str) -> bool:
530
+ """Check if auth provider should be intercepted based on config.
531
+
532
+ Args:
533
+ auth_name: Name of the auth provider to check
534
+
535
+ Returns:
536
+ True if should intercept, False otherwise
537
+ """
538
+ # Check if already registered
539
+ if any(client.name == auth_name for client in self._workflow_inventory.auth_providers):
540
+ return False
541
+
542
+ # If register_auth_providers is True, intercept all auth providers
543
+ if self._config.register_auth_providers:
544
+ return True
545
+
546
+ # Otherwise, only intercept if explicitly configured
547
+ return self._config.auth_providers is not None and auth_name in self._config.auth_providers
548
+
549
+ def _register_function(self, discovered: DiscoveredFunction) -> None:
550
+ """Register a discovered workflow function with this middleware.
551
+
552
+ Args:
553
+ discovered: A DiscoveredFunction from the workflow inventory
554
+ """
555
+ registration_key = discovered.name
556
+
557
+ if registration_key in self._registered_callables:
558
+ logger.debug("Function '%s' already registered, skipping", registration_key)
559
+ return
560
+
561
+ # Add this middleware to the function's existing middleware chain
562
+ existing_middleware = list(discovered.instance.middleware)
563
+ existing_middleware.append(self)
564
+ discovered.instance.configure_middleware(existing_middleware)
565
+
566
+ self._registered_callables[registration_key] = RegisteredFunction(key=registration_key,
567
+ function_instance=discovered.instance)
568
+
569
+ def _register_component_function(self, discovered: DiscoveredComponent, function_name: str) -> None:
570
+ """Register a specific component function from a discovered component.
571
+
572
+ Args:
573
+ discovered: A DiscoveredComponent from the workflow inventory
574
+ function_name: Name of the component function to register
575
+ """
576
+ component = discovered.instance
577
+ component_name = discovered.name
578
+
579
+ # Validate function exists
580
+ if not hasattr(component, function_name):
581
+ raise ValueError(f"Component function '{function_name}' does not exist on component '{component_name}'")
582
+
583
+ # Validate function is in discovered callable_functions
584
+ if function_name not in discovered.callable_functions:
585
+ raise ValueError(
586
+ f"Component function '{function_name}' was not discovered as callable on '{component_name}'. "
587
+ f"Available functions: {sorted(discovered.callable_functions)}")
588
+
589
+ # Check allowlist - only auto-register functions in the allowlist (includes user customizations)
590
+ allowlist = self._component_allowlists.get(discovered.component_type, set())
591
+ if function_name not in allowlist:
592
+ logger.debug("Component function '%s.%s' not in allowlist for %s, skipping auto-registration",
593
+ component_name,
594
+ function_name,
595
+ discovered.component_type.value)
596
+ return
597
+
598
+ # Check if already registered
599
+ registration_key = f"{component_name}.{function_name}"
600
+ if registration_key in self._registered_callables:
601
+ logger.debug("Component function '%s' already registered, skipping", registration_key)
602
+ return
603
+
604
+ # Store original callable before wrapping
605
+ original_callable = getattr(component, function_name)
606
+
607
+ # Wrap it with middleware
608
+ wrapped_function = self._configure_component_function_middleware(discovered, function_name)
609
+
610
+ # Replace the function on the component instance
611
+ object.__setattr__(component, function_name, wrapped_function)
612
+
613
+ self._registered_callables[registration_key] = RegisteredComponentMethod(key=registration_key,
614
+ component_instance=component,
615
+ function_name=function_name,
616
+ original_callable=original_callable)
617
+ logger.debug("Registered component function '%s'", registration_key)
618
+
619
+ def get_registered(self, key: str) -> RegisteredFunction | RegisteredComponentMethod | None:
620
+ """Get a registered callable by its key.
621
+
622
+ Args:
623
+ key: The registration key (for example, "my_llm.invoke" or "calculator__add")
624
+
625
+ Returns:
626
+ The RegisteredFunction or RegisteredComponentMethod if found, None otherwise
627
+ """
628
+ return self._registered_callables.get(key)
629
+
630
+ def get_registered_keys(self) -> list[str]:
631
+ """Get all registered callable keys.
632
+
633
+ Returns:
634
+ List of all registration keys currently tracked by this middleware
635
+ """
636
+ return list(self._registered_callables.keys())
637
+
638
+ def unregister(self, registered: RegisteredFunction | RegisteredComponentMethod) -> None:
639
+ """Unregister a callable from middleware interception.
640
+
641
+ Args:
642
+ registered: The registered function or component method to unregister
643
+
644
+ Raises:
645
+ ValueError: If not currently registered
646
+ """
647
+ if registered.key not in self._registered_callables:
648
+ raise ValueError(f"'{registered.key}' is not registered")
649
+
650
+ if isinstance(registered, RegisteredFunction):
651
+ # Remove this middleware from the function's middleware chain
652
+ chain = [m for m in registered.function_instance.middleware if m is not self]
653
+ registered.function_instance.configure_middleware(chain)
654
+ logger.debug("Unregistered workflow function '%s' from middleware interception", registered.key)
655
+
656
+ elif isinstance(registered, RegisteredComponentMethod):
657
+ # Restore original callable on the component instance
658
+ object.__setattr__(registered.component_instance, registered.function_name, registered.original_callable)
659
+ logger.debug("Unregistered component method '%s.%s' from middleware interception",
660
+ type(registered.component_instance).__name__,
661
+ registered.function_name)
662
+
663
+ del self._registered_callables[registered.key]
664
+
665
+ def _configure_component_function_middleware(self, discovered: DiscoveredComponent, function_name: str) -> Any:
666
+ """Wrap a component function with middleware interception.
667
+
668
+ Args:
669
+ discovered: The DiscoveredComponent from the workflow inventory
670
+ function_name: Name of the component function to wrap
671
+
672
+ Returns:
673
+ Wrapped component function
674
+ """
675
+ component_instance = discovered.instance
676
+ component_name = discovered.name
677
+ original_function = getattr(component_instance, function_name)
678
+
679
+ # Verify function has __name__
680
+ if not hasattr(original_function, '__name__'):
681
+ raise RuntimeError(
682
+ f"Component function '{function_name}' on component '{component_name}' has no __name__ attribute")
683
+
684
+ registration_key = f"{component_name}.{function_name}"
685
+
686
+ # Check if already registered - return original function to prevent nested wrapping
687
+ if registration_key in self._registered_callables:
688
+ return original_function
689
+
690
+ # Extract metadata safely - defaults to None for missing/inaccessible attributes
691
+ component_config = self._extract_component_attributes(discovered, 'config')
692
+ description = self._extract_component_attributes(discovered, 'description')
693
+ input_schema = self._extract_component_attributes(discovered, 'input_schema')
694
+ single_output_schema = self._extract_component_attributes(discovered, 'single_output_schema')
695
+ stream_output_schema = self._extract_component_attributes(discovered, 'stream_output_schema')
696
+
697
+ # Create static metadata context (original args/kwargs captured by orchestration)
698
+ context = FunctionMiddlewareContext(name=function_name,
699
+ config=component_config,
700
+ description=description,
701
+ input_schema=input_schema,
702
+ single_output_schema=single_output_schema,
703
+ stream_output_schema=stream_output_schema)
704
+
705
+ chain = FunctionMiddlewareChain(middleware=[self], context=context)
706
+
707
+ if inspect.isasyncgenfunction(original_function):
708
+ wrapped_function = chain.build_stream(original_function)
709
+ else:
710
+ wrapped_function = chain.build_single(original_function)
711
+
712
+ return wrapped_function
713
+
714
+ # ==================== Helper Methods ====================
715
+
716
+ def _build_component_allowlists(self) -> dict[ComponentGroup, set[str]]:
717
+ """Build component allowlists from config (merged with defaults).
718
+
719
+ Returns:
720
+ Dict mapping ComponentGroup enums to sets of allowed function names
721
+ """
722
+ if self._config.allowed_component_functions is None:
723
+ # No custom config, use defaults
724
+ return {k: v.copy() for k, v in COMPONENT_FUNCTION_ALLOWLISTS.items()}
725
+
726
+ allowed = self._config.allowed_component_functions
727
+ return {
728
+ ComponentGroup.LLMS: allowed.llms, # type: ignore[dict-item]
729
+ ComponentGroup.EMBEDDERS: allowed.embedders, # type: ignore[dict-item]
730
+ ComponentGroup.RETRIEVERS: allowed.retrievers, # type: ignore[dict-item]
731
+ ComponentGroup.MEMORY: allowed.memory, # type: ignore[dict-item]
732
+ ComponentGroup.OBJECT_STORES: allowed.object_stores, # type: ignore[dict-item]
733
+ ComponentGroup.AUTHENTICATION: allowed.authentication, # type: ignore[dict-item]
734
+ }
735
+
736
+ def _extract_component_attributes(self, discovered: DiscoveredComponent, attr_name: str) -> Any:
737
+ """Safely extract an attribute from a discovered component's instance.
738
+
739
+ Args:
740
+ discovered: DiscoveredComponent containing the component instance
741
+ attr_name: Name of the attribute to extract from the component instance
742
+
743
+ Returns:
744
+ Attribute value or None if it cannot be safely extracted
745
+ """
746
+ try:
747
+ obj = discovered.instance
748
+ # Check class-level attribute to avoid triggering async properties
749
+ class_attr = getattr(type(obj), attr_name, None)
750
+ if isinstance(class_attr, property):
751
+ return None
752
+
753
+ value = getattr(obj, attr_name, None)
754
+ if callable(value) or inspect.iscoroutine(value):
755
+ return None
756
+ return value
757
+ except Exception:
758
+ return None
759
+
760
+ # ==================== Helper Methods ====================
761
+
762
+ def _get_callable_functions(self, instance: Any, component_type: str | None = None) -> set[str]:
763
+ """Get all callable functions from component instance that can be safely wrapped.
764
+
765
+ This discovers ALL potentially wrappable component functions without allowlist filtering.
766
+ Safety checks ensure only valid, callable, bound functions are included.
767
+
768
+ Args:
769
+ instance: The component instance to introspect
770
+ component_type: Type of component (for logging/metadata, not filtering)
771
+
772
+ Returns:
773
+ Set of all valid component function names that could be wrapped
774
+ """
775
+ functions = set()
776
+
777
+ for function_name in dir(instance):
778
+ # Skip private/dunder functions
779
+ if function_name.startswith('_'):
780
+ continue
781
+
782
+ try:
783
+ # Must pass basic validity checks (no errors)
784
+ if not self._is_valid_wrappable_function(instance, function_name):
785
+ continue
786
+
787
+ # Passed all safety checks - this component function CAN be wrapped
788
+ functions.add(function_name)
789
+
790
+ except Exception:
791
+ logger.debug("Skipping function '%s' due to introspection error", function_name, exc_info=True)
792
+ continue
793
+
794
+ return functions
795
+
796
+ def _is_valid_wrappable_function(self, instance: Any, function_name: str) -> bool:
797
+ """Check if a component function passes all safety checks for wrapping.
798
+
799
+ This is the gatekeeper for what CAN be wrapped (not what SHOULD be).
800
+
801
+ Args:
802
+ instance: The component instance
803
+ function_name: Name of the component function to check
804
+
805
+ Returns:
806
+ True if component function is safe to wrap, False otherwise
807
+ """
808
+ try:
809
+ instance_class = type(instance)
810
+
811
+ # Check if function exists
812
+ if not hasattr(instance, function_name):
813
+ return False
814
+
815
+ # Get class-level attribute to check type
816
+ class_attr = getattr(instance_class, function_name, None)
817
+
818
+ # Skip properties
819
+ if isinstance(class_attr, property):
820
+ return False
821
+
822
+ # Skip static/class methods
823
+ if isinstance(class_attr, (staticmethod, classmethod)): # noqa: UP038
824
+ return False
825
+
826
+ # Get instance attribute
827
+ attr = getattr(instance, function_name, None)
828
+ if attr is None or not callable(attr):
829
+ return False
830
+
831
+ # Must be a bound method (component function)
832
+ if not inspect.ismethod(attr):
833
+ return False
834
+
835
+ # Must be bound to our instance
836
+ if not hasattr(attr, '__self__') or attr.__self__ is not instance:
837
+ return False
838
+
839
+ # Must have a valid signature
840
+ try:
841
+ inspect.signature(attr)
842
+ except (ValueError, TypeError):
843
+ return False
844
+
845
+ return True
846
+
847
+ except Exception:
848
+ return False
849
+
850
+ def _patch_components(self):
851
+ """Patch builder getter methods to enable runtime discovery and registration."""
852
+ self._patch_get_llm()
853
+ self._patch_get_embedder()
854
+ self._patch_get_retriever()
855
+ self._patch_get_memory()
856
+ self._patch_get_object_store()
857
+ self._patch_get_auth_provider()
858
+ self._patch_get_function()
859
+
860
+ def _patch_get_llm(self):
861
+ """Patch builder.get_llm() for runtime LLM interception."""
862
+ if not hasattr(self._builder, 'get_llm'):
863
+ raise RuntimeError("Builder does not have 'get_llm' method. Cannot patch LLM creation.")
864
+
865
+ self._builder_get_llm = self._builder.get_llm
866
+ self._builder.get_llm = self._discover_and_register_llm
867
+
868
+ def _patch_get_embedder(self):
869
+ """Patch builder.get_embedder() for runtime embedder interception."""
870
+ if not hasattr(self._builder, 'get_embedder'):
871
+ raise RuntimeError("Builder does not have 'get_embedder' method. Cannot patch embedder creation.")
872
+
873
+ self._builder_get_embedder = self._builder.get_embedder
874
+ self._builder.get_embedder = self._discover_and_register_embedder
875
+
876
+ def _patch_get_retriever(self):
877
+ """Patch builder.get_retriever() for runtime retriever interception."""
878
+ if not hasattr(self._builder, 'get_retriever'):
879
+ raise RuntimeError("Builder does not have 'get_retriever' method. Cannot patch retriever creation.")
880
+
881
+ self._builder_get_retriever = self._builder.get_retriever
882
+ self._builder.get_retriever = self._discover_and_register_retriever
883
+
884
+ def _patch_get_memory(self):
885
+ """Patch builder.get_memory_client() for runtime memory provider interception."""
886
+ if not hasattr(self._builder, 'get_memory_client'):
887
+ raise RuntimeError("Builder does not have 'get_memory_client' method. Cannot patch memory creation.")
888
+
889
+ self._builder_get_memory = self._builder.get_memory_client
890
+ self._builder.get_memory_client = self._discover_and_register_memory
891
+
892
+ def _patch_get_object_store(self):
893
+ """Patch builder.get_object_store_client() for runtime object store interception."""
894
+ if not hasattr(self._builder, 'get_object_store_client'):
895
+ raise RuntimeError("Builder does not have 'get_object_store_client' method. "
896
+ "Cannot patch object store creation.")
897
+
898
+ self._builder_get_object_store = self._builder.get_object_store_client
899
+ self._builder.get_object_store_client = self._discover_and_register_object_store
900
+
901
+ def _patch_get_auth_provider(self):
902
+ """Patch builder.get_auth_provider() for runtime auth provider interception."""
903
+ if not hasattr(self._builder, 'get_auth_provider'):
904
+ raise RuntimeError("Builder does not have 'get_auth_provider' method. Cannot patch auth provider creation.")
905
+
906
+ self._builder_get_auth_provider = self._builder.get_auth_provider
907
+ self._builder.get_auth_provider = self._discover_and_register_auth_provider
908
+
909
+ def _patch_get_function(self):
910
+ """Patch builder.get_function() for runtime function interception."""
911
+ if not hasattr(self._builder, 'get_function'):
912
+ raise RuntimeError("Builder does not have 'get_function' method. Cannot patch function retrieval.")
913
+
914
+ self._builder_get_function = self._builder.get_function
915
+ self._builder.get_function = self._discover_and_register_function
916
+
917
+ # ==================== Original Method Getters ====================
918
+
919
+ def _get_builder_get_llm(self):
920
+ """Return original builder.get_llm method."""
921
+ if self._builder_get_llm is None:
922
+ raise RuntimeError("get_llm has not been patched yet")
923
+ return self._builder_get_llm
924
+
925
+ def _get_builder_get_embedder(self):
926
+ """Return original builder.get_embedder method."""
927
+ if self._builder_get_embedder is None:
928
+ raise RuntimeError("get_embedder has not been patched yet")
929
+ return self._builder_get_embedder
930
+
931
+ def _get_builder_get_retriever(self):
932
+ """Return original builder.get_retriever method."""
933
+ if self._builder_get_retriever is None:
934
+ raise RuntimeError("get_retriever has not been patched yet")
935
+ return self._builder_get_retriever
936
+
937
+ def _get_builder_get_memory_client(self):
938
+ """Return original builder.get_memory_client method."""
939
+ if self._builder_get_memory is None:
940
+ raise RuntimeError("get_memory_client has not been patched yet")
941
+ return self._builder_get_memory
942
+
943
+ def _get_builder_get_object_store(self):
944
+ """Return original builder.get_object_store_client method."""
945
+ if self._builder_get_object_store is None:
946
+ raise RuntimeError("get_object_store_client has not been patched yet")
947
+ return self._builder_get_object_store
948
+
949
+ def _get_builder_get_auth_provider(self):
950
+ """Return original builder.get_auth_provider method."""
951
+ if self._builder_get_auth_provider is None:
952
+ raise RuntimeError("get_auth_provider has not been patched yet")
953
+ return self._builder_get_auth_provider
954
+
955
+ def _get_builder_get_function(self):
956
+ """Return original builder.get_function method."""
957
+ if self._builder_get_function is None:
958
+ raise RuntimeError("get_function has not been patched yet")
959
+ return self._builder_get_function
960
+
961
+
962
+ __all__ = ["DynamicFunctionMiddleware"]