nvidia-nat 1.1.0a20251020__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 (480) hide show
  1. aiq/__init__.py +66 -0
  2. nat/agent/__init__.py +0 -0
  3. nat/agent/base.py +265 -0
  4. nat/agent/dual_node.py +72 -0
  5. nat/agent/prompt_optimizer/__init__.py +0 -0
  6. nat/agent/prompt_optimizer/prompt.py +68 -0
  7. nat/agent/prompt_optimizer/register.py +149 -0
  8. nat/agent/react_agent/__init__.py +0 -0
  9. nat/agent/react_agent/agent.py +394 -0
  10. nat/agent/react_agent/output_parser.py +104 -0
  11. nat/agent/react_agent/prompt.py +44 -0
  12. nat/agent/react_agent/register.py +168 -0
  13. nat/agent/reasoning_agent/__init__.py +0 -0
  14. nat/agent/reasoning_agent/reasoning_agent.py +227 -0
  15. nat/agent/register.py +23 -0
  16. nat/agent/rewoo_agent/__init__.py +0 -0
  17. nat/agent/rewoo_agent/agent.py +593 -0
  18. nat/agent/rewoo_agent/prompt.py +107 -0
  19. nat/agent/rewoo_agent/register.py +175 -0
  20. nat/agent/tool_calling_agent/__init__.py +0 -0
  21. nat/agent/tool_calling_agent/agent.py +246 -0
  22. nat/agent/tool_calling_agent/register.py +129 -0
  23. nat/authentication/__init__.py +14 -0
  24. nat/authentication/api_key/__init__.py +14 -0
  25. nat/authentication/api_key/api_key_auth_provider.py +96 -0
  26. nat/authentication/api_key/api_key_auth_provider_config.py +124 -0
  27. nat/authentication/api_key/register.py +26 -0
  28. nat/authentication/credential_validator/__init__.py +14 -0
  29. nat/authentication/credential_validator/bearer_token_validator.py +557 -0
  30. nat/authentication/exceptions/__init__.py +14 -0
  31. nat/authentication/exceptions/api_key_exceptions.py +38 -0
  32. nat/authentication/http_basic_auth/__init__.py +0 -0
  33. nat/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
  34. nat/authentication/http_basic_auth/register.py +30 -0
  35. nat/authentication/interfaces.py +96 -0
  36. nat/authentication/oauth2/__init__.py +14 -0
  37. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +140 -0
  38. nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
  39. nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
  40. nat/authentication/oauth2/register.py +25 -0
  41. nat/authentication/register.py +20 -0
  42. nat/builder/__init__.py +0 -0
  43. nat/builder/builder.py +317 -0
  44. nat/builder/component_utils.py +320 -0
  45. nat/builder/context.py +321 -0
  46. nat/builder/embedder.py +24 -0
  47. nat/builder/eval_builder.py +166 -0
  48. nat/builder/evaluator.py +29 -0
  49. nat/builder/framework_enum.py +25 -0
  50. nat/builder/front_end.py +73 -0
  51. nat/builder/function.py +714 -0
  52. nat/builder/function_base.py +380 -0
  53. nat/builder/function_info.py +625 -0
  54. nat/builder/intermediate_step_manager.py +206 -0
  55. nat/builder/llm.py +25 -0
  56. nat/builder/retriever.py +25 -0
  57. nat/builder/user_interaction_manager.py +78 -0
  58. nat/builder/workflow.py +160 -0
  59. nat/builder/workflow_builder.py +1365 -0
  60. nat/cli/__init__.py +14 -0
  61. nat/cli/cli_utils/__init__.py +0 -0
  62. nat/cli/cli_utils/config_override.py +231 -0
  63. nat/cli/cli_utils/validation.py +37 -0
  64. nat/cli/commands/__init__.py +0 -0
  65. nat/cli/commands/configure/__init__.py +0 -0
  66. nat/cli/commands/configure/channel/__init__.py +0 -0
  67. nat/cli/commands/configure/channel/add.py +28 -0
  68. nat/cli/commands/configure/channel/channel.py +34 -0
  69. nat/cli/commands/configure/channel/remove.py +30 -0
  70. nat/cli/commands/configure/channel/update.py +30 -0
  71. nat/cli/commands/configure/configure.py +33 -0
  72. nat/cli/commands/evaluate.py +139 -0
  73. nat/cli/commands/info/__init__.py +14 -0
  74. nat/cli/commands/info/info.py +47 -0
  75. nat/cli/commands/info/list_channels.py +32 -0
  76. nat/cli/commands/info/list_components.py +128 -0
  77. nat/cli/commands/mcp/__init__.py +14 -0
  78. nat/cli/commands/mcp/mcp.py +986 -0
  79. nat/cli/commands/object_store/__init__.py +14 -0
  80. nat/cli/commands/object_store/object_store.py +227 -0
  81. nat/cli/commands/optimize.py +90 -0
  82. nat/cli/commands/registry/__init__.py +14 -0
  83. nat/cli/commands/registry/publish.py +88 -0
  84. nat/cli/commands/registry/pull.py +118 -0
  85. nat/cli/commands/registry/registry.py +36 -0
  86. nat/cli/commands/registry/remove.py +108 -0
  87. nat/cli/commands/registry/search.py +153 -0
  88. nat/cli/commands/sizing/__init__.py +14 -0
  89. nat/cli/commands/sizing/calc.py +297 -0
  90. nat/cli/commands/sizing/sizing.py +27 -0
  91. nat/cli/commands/start.py +257 -0
  92. nat/cli/commands/uninstall.py +81 -0
  93. nat/cli/commands/validate.py +47 -0
  94. nat/cli/commands/workflow/__init__.py +14 -0
  95. nat/cli/commands/workflow/templates/__init__.py.j2 +0 -0
  96. nat/cli/commands/workflow/templates/config.yml.j2 +17 -0
  97. nat/cli/commands/workflow/templates/pyproject.toml.j2 +25 -0
  98. nat/cli/commands/workflow/templates/register.py.j2 +4 -0
  99. nat/cli/commands/workflow/templates/workflow.py.j2 +50 -0
  100. nat/cli/commands/workflow/workflow.py +37 -0
  101. nat/cli/commands/workflow/workflow_commands.py +403 -0
  102. nat/cli/entrypoint.py +141 -0
  103. nat/cli/main.py +60 -0
  104. nat/cli/register_workflow.py +522 -0
  105. nat/cli/type_registry.py +1069 -0
  106. nat/control_flow/__init__.py +0 -0
  107. nat/control_flow/register.py +20 -0
  108. nat/control_flow/router_agent/__init__.py +0 -0
  109. nat/control_flow/router_agent/agent.py +329 -0
  110. nat/control_flow/router_agent/prompt.py +48 -0
  111. nat/control_flow/router_agent/register.py +91 -0
  112. nat/control_flow/sequential_executor.py +166 -0
  113. nat/data_models/__init__.py +14 -0
  114. nat/data_models/agent.py +34 -0
  115. nat/data_models/api_server.py +843 -0
  116. nat/data_models/authentication.py +245 -0
  117. nat/data_models/common.py +171 -0
  118. nat/data_models/component.py +60 -0
  119. nat/data_models/component_ref.py +179 -0
  120. nat/data_models/config.py +434 -0
  121. nat/data_models/dataset_handler.py +169 -0
  122. nat/data_models/discovery_metadata.py +305 -0
  123. nat/data_models/embedder.py +27 -0
  124. nat/data_models/evaluate.py +130 -0
  125. nat/data_models/evaluator.py +26 -0
  126. nat/data_models/front_end.py +26 -0
  127. nat/data_models/function.py +64 -0
  128. nat/data_models/function_dependencies.py +80 -0
  129. nat/data_models/gated_field_mixin.py +242 -0
  130. nat/data_models/interactive.py +246 -0
  131. nat/data_models/intermediate_step.py +302 -0
  132. nat/data_models/invocation_node.py +38 -0
  133. nat/data_models/llm.py +27 -0
  134. nat/data_models/logging.py +26 -0
  135. nat/data_models/memory.py +27 -0
  136. nat/data_models/object_store.py +44 -0
  137. nat/data_models/optimizable.py +119 -0
  138. nat/data_models/optimizer.py +149 -0
  139. nat/data_models/profiler.py +54 -0
  140. nat/data_models/registry_handler.py +26 -0
  141. nat/data_models/retriever.py +30 -0
  142. nat/data_models/retry_mixin.py +35 -0
  143. nat/data_models/span.py +228 -0
  144. nat/data_models/step_adaptor.py +64 -0
  145. nat/data_models/streaming.py +33 -0
  146. nat/data_models/swe_bench_model.py +54 -0
  147. nat/data_models/telemetry_exporter.py +26 -0
  148. nat/data_models/temperature_mixin.py +44 -0
  149. nat/data_models/thinking_mixin.py +86 -0
  150. nat/data_models/top_p_mixin.py +44 -0
  151. nat/data_models/ttc_strategy.py +30 -0
  152. nat/embedder/__init__.py +0 -0
  153. nat/embedder/azure_openai_embedder.py +46 -0
  154. nat/embedder/nim_embedder.py +59 -0
  155. nat/embedder/openai_embedder.py +42 -0
  156. nat/embedder/register.py +22 -0
  157. nat/eval/__init__.py +14 -0
  158. nat/eval/config.py +62 -0
  159. nat/eval/dataset_handler/__init__.py +0 -0
  160. nat/eval/dataset_handler/dataset_downloader.py +106 -0
  161. nat/eval/dataset_handler/dataset_filter.py +52 -0
  162. nat/eval/dataset_handler/dataset_handler.py +431 -0
  163. nat/eval/evaluate.py +565 -0
  164. nat/eval/evaluator/__init__.py +14 -0
  165. nat/eval/evaluator/base_evaluator.py +77 -0
  166. nat/eval/evaluator/evaluator_model.py +58 -0
  167. nat/eval/intermediate_step_adapter.py +99 -0
  168. nat/eval/rag_evaluator/__init__.py +0 -0
  169. nat/eval/rag_evaluator/evaluate.py +178 -0
  170. nat/eval/rag_evaluator/register.py +143 -0
  171. nat/eval/register.py +26 -0
  172. nat/eval/remote_workflow.py +133 -0
  173. nat/eval/runners/__init__.py +14 -0
  174. nat/eval/runners/config.py +39 -0
  175. nat/eval/runners/multi_eval_runner.py +54 -0
  176. nat/eval/runtime_evaluator/__init__.py +14 -0
  177. nat/eval/runtime_evaluator/evaluate.py +123 -0
  178. nat/eval/runtime_evaluator/register.py +100 -0
  179. nat/eval/runtime_event_subscriber.py +52 -0
  180. nat/eval/swe_bench_evaluator/__init__.py +0 -0
  181. nat/eval/swe_bench_evaluator/evaluate.py +215 -0
  182. nat/eval/swe_bench_evaluator/register.py +36 -0
  183. nat/eval/trajectory_evaluator/__init__.py +0 -0
  184. nat/eval/trajectory_evaluator/evaluate.py +75 -0
  185. nat/eval/trajectory_evaluator/register.py +40 -0
  186. nat/eval/tunable_rag_evaluator/__init__.py +0 -0
  187. nat/eval/tunable_rag_evaluator/evaluate.py +242 -0
  188. nat/eval/tunable_rag_evaluator/register.py +52 -0
  189. nat/eval/usage_stats.py +41 -0
  190. nat/eval/utils/__init__.py +0 -0
  191. nat/eval/utils/eval_trace_ctx.py +89 -0
  192. nat/eval/utils/output_uploader.py +140 -0
  193. nat/eval/utils/tqdm_position_registry.py +40 -0
  194. nat/eval/utils/weave_eval.py +193 -0
  195. nat/experimental/__init__.py +0 -0
  196. nat/experimental/decorators/__init__.py +0 -0
  197. nat/experimental/decorators/experimental_warning_decorator.py +154 -0
  198. nat/experimental/test_time_compute/__init__.py +0 -0
  199. nat/experimental/test_time_compute/editing/__init__.py +0 -0
  200. nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +147 -0
  201. nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +204 -0
  202. nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +107 -0
  203. nat/experimental/test_time_compute/functions/__init__.py +0 -0
  204. nat/experimental/test_time_compute/functions/execute_score_select_function.py +105 -0
  205. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +228 -0
  206. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +205 -0
  207. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +146 -0
  208. nat/experimental/test_time_compute/models/__init__.py +0 -0
  209. nat/experimental/test_time_compute/models/editor_config.py +132 -0
  210. nat/experimental/test_time_compute/models/scoring_config.py +112 -0
  211. nat/experimental/test_time_compute/models/search_config.py +120 -0
  212. nat/experimental/test_time_compute/models/selection_config.py +154 -0
  213. nat/experimental/test_time_compute/models/stage_enums.py +43 -0
  214. nat/experimental/test_time_compute/models/strategy_base.py +67 -0
  215. nat/experimental/test_time_compute/models/tool_use_config.py +41 -0
  216. nat/experimental/test_time_compute/models/ttc_item.py +48 -0
  217. nat/experimental/test_time_compute/register.py +35 -0
  218. nat/experimental/test_time_compute/scoring/__init__.py +0 -0
  219. nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +168 -0
  220. nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +168 -0
  221. nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +111 -0
  222. nat/experimental/test_time_compute/search/__init__.py +0 -0
  223. nat/experimental/test_time_compute/search/multi_llm_planner.py +128 -0
  224. nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +122 -0
  225. nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +128 -0
  226. nat/experimental/test_time_compute/selection/__init__.py +0 -0
  227. nat/experimental/test_time_compute/selection/best_of_n_selector.py +63 -0
  228. nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +131 -0
  229. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +157 -0
  230. nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +128 -0
  231. nat/experimental/test_time_compute/selection/threshold_selector.py +58 -0
  232. nat/front_ends/__init__.py +14 -0
  233. nat/front_ends/console/__init__.py +14 -0
  234. nat/front_ends/console/authentication_flow_handler.py +285 -0
  235. nat/front_ends/console/console_front_end_config.py +32 -0
  236. nat/front_ends/console/console_front_end_plugin.py +108 -0
  237. nat/front_ends/console/register.py +25 -0
  238. nat/front_ends/cron/__init__.py +14 -0
  239. nat/front_ends/fastapi/__init__.py +14 -0
  240. nat/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
  241. nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
  242. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +142 -0
  243. nat/front_ends/fastapi/dask_client_mixin.py +65 -0
  244. nat/front_ends/fastapi/fastapi_front_end_config.py +272 -0
  245. nat/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
  246. nat/front_ends/fastapi/fastapi_front_end_plugin.py +247 -0
  247. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +1257 -0
  248. nat/front_ends/fastapi/html_snippets/__init__.py +14 -0
  249. nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
  250. nat/front_ends/fastapi/intermediate_steps_subscriber.py +80 -0
  251. nat/front_ends/fastapi/job_store.py +602 -0
  252. nat/front_ends/fastapi/main.py +64 -0
  253. nat/front_ends/fastapi/message_handler.py +344 -0
  254. nat/front_ends/fastapi/message_validator.py +351 -0
  255. nat/front_ends/fastapi/register.py +25 -0
  256. nat/front_ends/fastapi/response_helpers.py +195 -0
  257. nat/front_ends/fastapi/step_adaptor.py +319 -0
  258. nat/front_ends/fastapi/utils.py +57 -0
  259. nat/front_ends/mcp/__init__.py +14 -0
  260. nat/front_ends/mcp/introspection_token_verifier.py +73 -0
  261. nat/front_ends/mcp/mcp_front_end_config.py +90 -0
  262. nat/front_ends/mcp/mcp_front_end_plugin.py +113 -0
  263. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +268 -0
  264. nat/front_ends/mcp/memory_profiler.py +320 -0
  265. nat/front_ends/mcp/register.py +27 -0
  266. nat/front_ends/mcp/tool_converter.py +290 -0
  267. nat/front_ends/register.py +21 -0
  268. nat/front_ends/simple_base/__init__.py +14 -0
  269. nat/front_ends/simple_base/simple_front_end_plugin_base.py +56 -0
  270. nat/llm/__init__.py +0 -0
  271. nat/llm/aws_bedrock_llm.py +69 -0
  272. nat/llm/azure_openai_llm.py +57 -0
  273. nat/llm/litellm_llm.py +69 -0
  274. nat/llm/nim_llm.py +58 -0
  275. nat/llm/openai_llm.py +54 -0
  276. nat/llm/register.py +27 -0
  277. nat/llm/utils/__init__.py +14 -0
  278. nat/llm/utils/env_config_value.py +93 -0
  279. nat/llm/utils/error.py +17 -0
  280. nat/llm/utils/thinking.py +215 -0
  281. nat/memory/__init__.py +20 -0
  282. nat/memory/interfaces.py +183 -0
  283. nat/memory/models.py +112 -0
  284. nat/meta/pypi.md +58 -0
  285. nat/object_store/__init__.py +20 -0
  286. nat/object_store/in_memory_object_store.py +76 -0
  287. nat/object_store/interfaces.py +84 -0
  288. nat/object_store/models.py +38 -0
  289. nat/object_store/register.py +19 -0
  290. nat/observability/__init__.py +14 -0
  291. nat/observability/exporter/__init__.py +14 -0
  292. nat/observability/exporter/base_exporter.py +449 -0
  293. nat/observability/exporter/exporter.py +78 -0
  294. nat/observability/exporter/file_exporter.py +33 -0
  295. nat/observability/exporter/processing_exporter.py +550 -0
  296. nat/observability/exporter/raw_exporter.py +52 -0
  297. nat/observability/exporter/span_exporter.py +308 -0
  298. nat/observability/exporter_manager.py +335 -0
  299. nat/observability/mixin/__init__.py +14 -0
  300. nat/observability/mixin/batch_config_mixin.py +26 -0
  301. nat/observability/mixin/collector_config_mixin.py +23 -0
  302. nat/observability/mixin/file_mixin.py +288 -0
  303. nat/observability/mixin/file_mode.py +23 -0
  304. nat/observability/mixin/redaction_config_mixin.py +42 -0
  305. nat/observability/mixin/resource_conflict_mixin.py +134 -0
  306. nat/observability/mixin/serialize_mixin.py +61 -0
  307. nat/observability/mixin/tagging_config_mixin.py +62 -0
  308. nat/observability/mixin/type_introspection_mixin.py +496 -0
  309. nat/observability/processor/__init__.py +14 -0
  310. nat/observability/processor/batching_processor.py +308 -0
  311. nat/observability/processor/callback_processor.py +42 -0
  312. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  313. nat/observability/processor/intermediate_step_serializer.py +28 -0
  314. nat/observability/processor/processor.py +74 -0
  315. nat/observability/processor/processor_factory.py +70 -0
  316. nat/observability/processor/redaction/__init__.py +24 -0
  317. nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
  318. nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
  319. nat/observability/processor/redaction/redaction_processor.py +177 -0
  320. nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
  321. nat/observability/processor/span_tagging_processor.py +68 -0
  322. nat/observability/register.py +114 -0
  323. nat/observability/utils/__init__.py +14 -0
  324. nat/observability/utils/dict_utils.py +236 -0
  325. nat/observability/utils/time_utils.py +31 -0
  326. nat/plugins/.namespace +1 -0
  327. nat/profiler/__init__.py +0 -0
  328. nat/profiler/calc/__init__.py +14 -0
  329. nat/profiler/calc/calc_runner.py +626 -0
  330. nat/profiler/calc/calculations.py +288 -0
  331. nat/profiler/calc/data_models.py +188 -0
  332. nat/profiler/calc/plot.py +345 -0
  333. nat/profiler/callbacks/__init__.py +0 -0
  334. nat/profiler/callbacks/agno_callback_handler.py +295 -0
  335. nat/profiler/callbacks/base_callback_class.py +20 -0
  336. nat/profiler/callbacks/langchain_callback_handler.py +297 -0
  337. nat/profiler/callbacks/llama_index_callback_handler.py +205 -0
  338. nat/profiler/callbacks/semantic_kernel_callback_handler.py +238 -0
  339. nat/profiler/callbacks/token_usage_base_model.py +27 -0
  340. nat/profiler/data_frame_row.py +51 -0
  341. nat/profiler/data_models.py +24 -0
  342. nat/profiler/decorators/__init__.py +0 -0
  343. nat/profiler/decorators/framework_wrapper.py +180 -0
  344. nat/profiler/decorators/function_tracking.py +411 -0
  345. nat/profiler/forecasting/__init__.py +0 -0
  346. nat/profiler/forecasting/config.py +18 -0
  347. nat/profiler/forecasting/model_trainer.py +75 -0
  348. nat/profiler/forecasting/models/__init__.py +22 -0
  349. nat/profiler/forecasting/models/forecasting_base_model.py +42 -0
  350. nat/profiler/forecasting/models/linear_model.py +197 -0
  351. nat/profiler/forecasting/models/random_forest_regressor.py +269 -0
  352. nat/profiler/inference_metrics_model.py +28 -0
  353. nat/profiler/inference_optimization/__init__.py +0 -0
  354. nat/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
  355. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +460 -0
  356. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +258 -0
  357. nat/profiler/inference_optimization/data_models.py +386 -0
  358. nat/profiler/inference_optimization/experimental/__init__.py +0 -0
  359. nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +468 -0
  360. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +404 -0
  361. nat/profiler/inference_optimization/llm_metrics.py +212 -0
  362. nat/profiler/inference_optimization/prompt_caching.py +163 -0
  363. nat/profiler/inference_optimization/token_uniqueness.py +107 -0
  364. nat/profiler/inference_optimization/workflow_runtimes.py +72 -0
  365. nat/profiler/intermediate_property_adapter.py +102 -0
  366. nat/profiler/parameter_optimization/__init__.py +0 -0
  367. nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
  368. nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
  369. nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
  370. nat/profiler/parameter_optimization/parameter_selection.py +107 -0
  371. nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
  372. nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
  373. nat/profiler/parameter_optimization/update_helpers.py +66 -0
  374. nat/profiler/profile_runner.py +478 -0
  375. nat/profiler/utils.py +186 -0
  376. nat/registry_handlers/__init__.py +0 -0
  377. nat/registry_handlers/local/__init__.py +0 -0
  378. nat/registry_handlers/local/local_handler.py +176 -0
  379. nat/registry_handlers/local/register_local.py +37 -0
  380. nat/registry_handlers/metadata_factory.py +60 -0
  381. nat/registry_handlers/package_utils.py +570 -0
  382. nat/registry_handlers/pypi/__init__.py +0 -0
  383. nat/registry_handlers/pypi/pypi_handler.py +248 -0
  384. nat/registry_handlers/pypi/register_pypi.py +40 -0
  385. nat/registry_handlers/register.py +20 -0
  386. nat/registry_handlers/registry_handler_base.py +157 -0
  387. nat/registry_handlers/rest/__init__.py +0 -0
  388. nat/registry_handlers/rest/register_rest.py +56 -0
  389. nat/registry_handlers/rest/rest_handler.py +236 -0
  390. nat/registry_handlers/schemas/__init__.py +0 -0
  391. nat/registry_handlers/schemas/headers.py +42 -0
  392. nat/registry_handlers/schemas/package.py +68 -0
  393. nat/registry_handlers/schemas/publish.py +68 -0
  394. nat/registry_handlers/schemas/pull.py +82 -0
  395. nat/registry_handlers/schemas/remove.py +36 -0
  396. nat/registry_handlers/schemas/search.py +91 -0
  397. nat/registry_handlers/schemas/status.py +47 -0
  398. nat/retriever/__init__.py +0 -0
  399. nat/retriever/interface.py +41 -0
  400. nat/retriever/milvus/__init__.py +14 -0
  401. nat/retriever/milvus/register.py +81 -0
  402. nat/retriever/milvus/retriever.py +228 -0
  403. nat/retriever/models.py +77 -0
  404. nat/retriever/nemo_retriever/__init__.py +14 -0
  405. nat/retriever/nemo_retriever/register.py +60 -0
  406. nat/retriever/nemo_retriever/retriever.py +190 -0
  407. nat/retriever/register.py +21 -0
  408. nat/runtime/__init__.py +14 -0
  409. nat/runtime/loader.py +220 -0
  410. nat/runtime/runner.py +292 -0
  411. nat/runtime/session.py +223 -0
  412. nat/runtime/user_metadata.py +130 -0
  413. nat/settings/__init__.py +0 -0
  414. nat/settings/global_settings.py +329 -0
  415. nat/test/.namespace +1 -0
  416. nat/tool/__init__.py +0 -0
  417. nat/tool/chat_completion.py +77 -0
  418. nat/tool/code_execution/README.md +151 -0
  419. nat/tool/code_execution/__init__.py +0 -0
  420. nat/tool/code_execution/code_sandbox.py +267 -0
  421. nat/tool/code_execution/local_sandbox/.gitignore +1 -0
  422. nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +60 -0
  423. nat/tool/code_execution/local_sandbox/__init__.py +13 -0
  424. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +198 -0
  425. nat/tool/code_execution/local_sandbox/sandbox.requirements.txt +6 -0
  426. nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +50 -0
  427. nat/tool/code_execution/register.py +74 -0
  428. nat/tool/code_execution/test_code_execution_sandbox.py +414 -0
  429. nat/tool/code_execution/utils.py +100 -0
  430. nat/tool/datetime_tools.py +82 -0
  431. nat/tool/document_search.py +141 -0
  432. nat/tool/github_tools.py +450 -0
  433. nat/tool/memory_tools/__init__.py +0 -0
  434. nat/tool/memory_tools/add_memory_tool.py +79 -0
  435. nat/tool/memory_tools/delete_memory_tool.py +66 -0
  436. nat/tool/memory_tools/get_memory_tool.py +72 -0
  437. nat/tool/nvidia_rag.py +95 -0
  438. nat/tool/register.py +31 -0
  439. nat/tool/retriever.py +95 -0
  440. nat/tool/server_tools.py +66 -0
  441. nat/utils/__init__.py +0 -0
  442. nat/utils/callable_utils.py +70 -0
  443. nat/utils/data_models/__init__.py +0 -0
  444. nat/utils/data_models/schema_validator.py +58 -0
  445. nat/utils/debugging_utils.py +43 -0
  446. nat/utils/decorators.py +210 -0
  447. nat/utils/dump_distro_mapping.py +32 -0
  448. nat/utils/exception_handlers/__init__.py +0 -0
  449. nat/utils/exception_handlers/automatic_retries.py +342 -0
  450. nat/utils/exception_handlers/schemas.py +114 -0
  451. nat/utils/io/__init__.py +0 -0
  452. nat/utils/io/model_processing.py +28 -0
  453. nat/utils/io/yaml_tools.py +119 -0
  454. nat/utils/log_levels.py +25 -0
  455. nat/utils/log_utils.py +37 -0
  456. nat/utils/metadata_utils.py +74 -0
  457. nat/utils/optional_imports.py +142 -0
  458. nat/utils/producer_consumer_queue.py +178 -0
  459. nat/utils/reactive/__init__.py +0 -0
  460. nat/utils/reactive/base/__init__.py +0 -0
  461. nat/utils/reactive/base/observable_base.py +65 -0
  462. nat/utils/reactive/base/observer_base.py +55 -0
  463. nat/utils/reactive/base/subject_base.py +79 -0
  464. nat/utils/reactive/observable.py +59 -0
  465. nat/utils/reactive/observer.py +76 -0
  466. nat/utils/reactive/subject.py +131 -0
  467. nat/utils/reactive/subscription.py +49 -0
  468. nat/utils/settings/__init__.py +0 -0
  469. nat/utils/settings/global_settings.py +195 -0
  470. nat/utils/string_utils.py +38 -0
  471. nat/utils/type_converter.py +299 -0
  472. nat/utils/type_utils.py +488 -0
  473. nat/utils/url_utils.py +27 -0
  474. nvidia_nat-1.1.0a20251020.dist-info/METADATA +195 -0
  475. nvidia_nat-1.1.0a20251020.dist-info/RECORD +480 -0
  476. nvidia_nat-1.1.0a20251020.dist-info/WHEEL +5 -0
  477. nvidia_nat-1.1.0a20251020.dist-info/entry_points.txt +22 -0
  478. nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
  479. nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE.md +201 -0
  480. nvidia_nat-1.1.0a20251020.dist-info/top_level.txt +2 -0
@@ -0,0 +1,49 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, 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
+ import typing
17
+ from collections.abc import Callable
18
+ from typing import Generic
19
+ from typing import TypeVar
20
+
21
+ if typing.TYPE_CHECKING:
22
+ from nat.utils.reactive.base.subject_base import SubjectBase
23
+
24
+ _T = TypeVar("_T")
25
+
26
+ OnNext = Callable[[_T], None]
27
+ OnError = Callable[[Exception], None]
28
+ OnComplete = Callable[[], None]
29
+
30
+
31
+ class Subscription(Generic[_T]):
32
+ """
33
+ Represents a subscription to a Subject.
34
+ Unsubscribing removes the associated observer from the Subject's subscriber list.
35
+ """
36
+
37
+ def __init__(self, subject: "SubjectBase", observer: object | None): # noqa: F821
38
+ self._subject = subject
39
+ self._observer = observer
40
+ self._unsubscribed = False
41
+
42
+ def unsubscribe(self) -> None:
43
+ """
44
+ Stop receiving further events.
45
+ """
46
+ if not self._unsubscribed and self._observer is not None:
47
+ self._subject._unsubscribe_observer(self._observer)
48
+ self._observer = None
49
+ self._unsubscribed = True
File without changes
@@ -0,0 +1,195 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, 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
+ import logging
17
+
18
+ from pydantic import create_model
19
+
20
+ from nat.cli.type_registry import GlobalTypeRegistry
21
+ from nat.data_models.registry_handler import RegistryHandlerBaseConfig
22
+ from nat.settings.global_settings import GlobalSettings
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+
27
+ def configure_registry_channel(config_type: RegistryHandlerBaseConfig, channel_name: str) -> None:
28
+ """Perform channel updates, gathering input from user and validatinig against the global settings data model.
29
+
30
+ Args:
31
+ config_type (RegistryHandlerBaseConfig): The registry handler configuration object to ensure valid channel
32
+ settings
33
+ channel_name (str): The name to use to reference the remote registry channel.
34
+ """
35
+
36
+ settings = GlobalSettings.get()
37
+
38
+ channel_registry_pre = {}
39
+
40
+ for field, info in config_type.model_fields.items():
41
+
42
+ if (field == "type"):
43
+ continue
44
+
45
+ while (True):
46
+ human_prompt = " ".join(field.title().split("_"))
47
+ user_input = input(f"{human_prompt}: ")
48
+ model_fields = {}
49
+ model_fields[field] = (info.annotation, ...)
50
+ DynamicFieldModel = create_model("DynamicFieldModel", **model_fields)
51
+ dynamic_inputs = {field: user_input}
52
+
53
+ try:
54
+ validated_field_model = DynamicFieldModel(**dynamic_inputs)
55
+ channel_registry_pre[field] = getattr(validated_field_model, field)
56
+ break
57
+ except Exception as e:
58
+ logger.exception(e)
59
+ logger.warning("Invalid '%s' input, input must be of type %s.", field, info.annotation)
60
+
61
+ validated_model = config_type(**channel_registry_pre)
62
+ settings_dict = settings.model_dump(serialize_as_any=True, by_alias=True)
63
+ settings_dict["channels"] = {**settings_dict["channels"], **{channel_name: validated_model}}
64
+
65
+ settings.update_settings(config_obj=settings_dict)
66
+
67
+
68
+ def add_channel_interative(channel_type: str) -> None:
69
+ """Add a remote registry channel to publish/search/pull NAT plugin packages.
70
+
71
+ Args:
72
+ channel_type (str): They type of channel to configure.
73
+ """
74
+
75
+ settings = GlobalSettings.get()
76
+ registry = GlobalTypeRegistry.get()
77
+
78
+ try:
79
+ ChannelConfigType = registry.get_registered_channel_info_by_channel_type(channel_type=channel_type).config_type
80
+ except Exception as e:
81
+ logger.exception("Invalid channel type: %s", e)
82
+ return
83
+
84
+ while (True):
85
+ channel_name = input("Channel Name: ").strip()
86
+ if len(channel_name) < 1:
87
+ logger.warning("Invalid channel name, cannot be empty or whitespace.")
88
+ if (channel_name in settings.channels):
89
+ logger.warning("Channel name '%s' already exists, choose a different name.", channel_name)
90
+ else:
91
+ settings.channels[channel_name] = {}
92
+ break
93
+
94
+ ChannelConfigType = registry.get_registered_channel_info_by_channel_type(channel_type=channel_type).config_type
95
+
96
+ configure_registry_channel(config_type=ChannelConfigType, channel_name=channel_name)
97
+
98
+
99
+ def get_existing_channel_interactive(channel_name: str) -> tuple[str, bool]:
100
+ """Retrieve an existing channel by configured name.
101
+
102
+ Args:
103
+ channel_name (str): The name to use to reference the remote registry channel.
104
+
105
+ Returns:
106
+ tuple[str, bool]: A tuple containing the retrieved channel name and a boolean representing a
107
+ valid match was or was not successful.
108
+ """
109
+
110
+ settings = GlobalSettings.get()
111
+ valid_channel = False
112
+ remote_channels = settings.channels
113
+
114
+ if (len(remote_channels) == 0):
115
+ logger.warning("No are configured channels to remove.")
116
+ return channel_name, valid_channel
117
+
118
+ while (not valid_channel):
119
+
120
+ if (channel_name not in remote_channels):
121
+ logger.warning("Channel name '%s' does not exist, choose a name from %s",
122
+ channel_name,
123
+ settings.channel_names)
124
+ channel_name = input("Channel Name: ").strip()
125
+ continue
126
+
127
+ valid_channel = True
128
+
129
+ return channel_name, valid_channel
130
+
131
+
132
+ def remove_channel(channel_name: str) -> None:
133
+ """Remove a configured registry channel from the global settings.
134
+
135
+ Args:
136
+ channel_name (str): The name to use to reference the remote registry channel.
137
+ """
138
+
139
+ settings = GlobalSettings.get()
140
+ settings_dict = settings.model_dump(serialize_as_any=True, by_alias=True).copy()
141
+ settings_dict["channels"].pop(channel_name)
142
+ settings.update_settings(config_obj=settings_dict)
143
+
144
+
145
+ def remove_channel_interactive(channel_name: str) -> None:
146
+ channel_name, valid_channel = get_existing_channel_interactive(channel_name=channel_name)
147
+ if (not valid_channel):
148
+ return
149
+ remove_channel(channel_name=channel_name)
150
+
151
+
152
+ def match_valid_channel(channel_name: str) -> None:
153
+ """Performs a match by registry channel to perform a channel configuration update.
154
+
155
+ Args:
156
+ channel_name (str): The name to use to reference the remote registry channel.
157
+ """
158
+
159
+ settings = GlobalSettings.get()
160
+ registry = GlobalTypeRegistry.get()
161
+
162
+ if len(settings.channel_names) == 0:
163
+ logger.warning("No channels have been configured, first add a channel.")
164
+ return
165
+
166
+ if (channel_name not in settings.channel_names):
167
+ logger.warning("Provided channel has not yet been configured, choose a different name "
168
+ "from %s .",
169
+ settings.channel_names)
170
+ while (True):
171
+ channel_name = input("Channel Name: ").strip()
172
+ if len(channel_name) < 1:
173
+ logger.warning("Invalid channel name, cannot be empty or whitespace.")
174
+ if (channel_name in settings.channel_names):
175
+ logger.warning("Channel name '%s' already exists, choose a different name.", channel_name)
176
+ else:
177
+ settings.channels[channel_name] = {}
178
+ break
179
+
180
+ channals_settings = settings.channels
181
+ channel_settings = channals_settings.get(channel_name)
182
+ ChannelConfigType = registry.get_registered_channel_info_by_channel_type(
183
+ channel_type=channel_settings.static_type()).config_type
184
+
185
+ configure_registry_channel(config_type=ChannelConfigType, channel_name=channel_name)
186
+
187
+
188
+ def update_channel_interactive(channel_name: str):
189
+ """Launch an interactive session to update a configured channels settings.
190
+
191
+ Args:
192
+ channel_name (str): The name to use to reference the remote registry channel.
193
+ """
194
+
195
+ match_valid_channel(channel_name=channel_name)
@@ -0,0 +1,38 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, 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 typing import Any
17
+
18
+ from pydantic import BaseModel
19
+
20
+
21
+ def convert_to_str(value: Any) -> str:
22
+ """
23
+ Convert a value to a string representation.
24
+ Handles various types including lists, dictionaries, and other objects.
25
+ """
26
+ if isinstance(value, str):
27
+ return value
28
+
29
+ if isinstance(value, list):
30
+ return ", ".join(map(str, value))
31
+ elif isinstance(value, BaseModel):
32
+ return value.model_dump_json(exclude_none=True, exclude_unset=True)
33
+ elif isinstance(value, dict):
34
+ return ", ".join(f"{k}: {v}" for k, v in value.items())
35
+ elif hasattr(value, '__str__'):
36
+ return str(value)
37
+ else:
38
+ raise ValueError(f"Unsupported type for conversion to string: {type(value)}")
@@ -0,0 +1,299 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, 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
+ import logging
17
+ import typing
18
+ from collections import OrderedDict
19
+ from collections.abc import Callable
20
+ from io import TextIOWrapper
21
+
22
+ from nat.utils.type_utils import DecomposedType
23
+
24
+ logger = logging.getLogger(__name__)
25
+
26
+ _T = typing.TypeVar("_T")
27
+
28
+
29
+ class ConvertException(Exception):
30
+ pass
31
+
32
+
33
+ class TypeConverter:
34
+ _global_initialized = False
35
+
36
+ def __init__(self, converters: list[Callable[[typing.Any], typing.Any]], parent: "TypeConverter | None" = None):
37
+ """
38
+ Parameters
39
+ ----------
40
+ converters : list[Callable[[typing.Any], typing.Any]]
41
+ A list of single-argument converter callables annotated with their input param and return type.
42
+ parent : TypeConverter | None
43
+ An optional parent TypeConverter for fallback.
44
+ """
45
+ # dict[to_type, dict[from_type, converter]]
46
+ self._converters: OrderedDict[type, OrderedDict[type, Callable]] = OrderedDict()
47
+ self._indirect_warnings_shown: set[tuple[type, type]] = set()
48
+
49
+ for converter in converters:
50
+ self.add_converter(converter)
51
+
52
+ if parent is None and TypeConverter._global_initialized:
53
+ parent = GlobalTypeConverter.get()
54
+ self._parent = parent
55
+
56
+ def add_converter(self, converter: Callable) -> None:
57
+ """
58
+ Registers a converter. Must have exactly one parameter
59
+ and an annotated return type.
60
+
61
+ Parameters
62
+ ----------
63
+ converter : Callable
64
+ A converter function. Must have exactly one parameter and an annotated return type.
65
+
66
+ Raises
67
+ ------
68
+ ValueError
69
+ If the converter does not have a return type or exactly one argument or the argument has no data type.
70
+ """
71
+ sig = typing.get_type_hints(converter)
72
+ to_type = sig.pop("return", None)
73
+ if to_type is None:
74
+ raise ValueError("Converter must have a return type.")
75
+
76
+ if len(sig) != 1:
77
+ raise ValueError("Converter must have exactly one argument.")
78
+
79
+ from_type = next(iter(sig.values()))
80
+ if from_type is None:
81
+ raise ValueError("Converter's argument must have a data type.")
82
+
83
+ self._converters.setdefault(to_type, OrderedDict())[from_type] = converter
84
+ # to do(MDD): If needed, sort by specificity here.
85
+
86
+ def _convert(self, data: typing.Any, to_type: type[_T]) -> _T | None:
87
+ """
88
+ Attempts to convert `data` into `to_type`. Returns None if no path is found.
89
+ """
90
+ decomposed = DecomposedType(to_type)
91
+
92
+ # 1) If data is already correct type, return it
93
+ if to_type is None or decomposed.is_instance(data):
94
+ return data
95
+
96
+ # 2) If data is a union type, try to convert to each type in the union
97
+ if decomposed.is_union:
98
+ for union_type in decomposed.args:
99
+ result = self._convert(data, union_type)
100
+ if result is not None:
101
+ return result
102
+ return None
103
+
104
+ root = decomposed.root
105
+
106
+ # 2) Attempt direct in *this* converter
107
+ direct_result = self._try_direct_conversion(data, root)
108
+ if direct_result is not None:
109
+ return direct_result
110
+
111
+ # 3) If direct fails entirely, do indirect in *this* converter
112
+ indirect_result = self._try_indirect_convert(data, to_type)
113
+ if indirect_result is not None:
114
+ return indirect_result
115
+
116
+ # 4) If we still haven't succeeded, return None
117
+ return None
118
+
119
+ def convert(self, data: typing.Any, to_type: type[_T]) -> _T:
120
+ """
121
+ Converts or raises ValueError if no conversion path is found.
122
+ We also give the parent a chance if self fails.
123
+
124
+ Parameters
125
+ ----------
126
+ data : typing.Any
127
+ The value to convert.
128
+ to_type : type
129
+ The type to convert the value to.
130
+
131
+ Returns
132
+ -------
133
+ _T
134
+ The converted value.
135
+
136
+ Raises
137
+ ------
138
+ ValueError
139
+ If the value cannot be converted to the specified type.
140
+ """
141
+ result = self._convert(data, to_type)
142
+ if result is None and self._parent:
143
+ # fallback on parent entirely
144
+ return self._parent.convert(data, to_type)
145
+
146
+ if result is not None:
147
+ return result
148
+ raise ValueError(f"Cannot convert type {type(data)} to {to_type}. No match found.")
149
+
150
+ def try_convert(self, data: typing.Any, to_type: type[_T]) -> _T | typing.Any:
151
+ """
152
+ Converts with graceful error handling. If conversion fails, returns the original data
153
+ and continues processing.
154
+
155
+ Parameters
156
+ ----------
157
+ data : typing.Any
158
+ The value to convert.
159
+ to_type : type
160
+ The type to convert the value to.
161
+
162
+ Returns
163
+ -------
164
+ _T | typing.Any
165
+ The converted value, or original value if conversion fails.
166
+ """
167
+ try:
168
+ return self.convert(data, to_type)
169
+ except ValueError:
170
+ logger.warning("Type conversion failed, using original value. From %s to %s", type(data), to_type)
171
+ # Return original data, let downstream code handle it
172
+ return data
173
+
174
+ # -------------------------------------------------
175
+ # INTERNAL DIRECT CONVERSION (with parent fallback)
176
+ # -------------------------------------------------
177
+ def _try_direct_conversion(self, data: typing.Any, target_root_type: type) -> typing.Any | None:
178
+ """
179
+ Tries direct conversion in *this* converter's registry.
180
+ If no match here, we forward to parent's direct conversion
181
+ for recursion up the chain.
182
+ """
183
+ for convert_to_type, to_type_converters in self._converters.items():
184
+ # e.g. if Derived is a subclass of Base, this is valid
185
+ if issubclass(DecomposedType(convert_to_type).root, target_root_type):
186
+ for convert_from_type, from_type_converter in to_type_converters.items():
187
+ if isinstance(data, DecomposedType(convert_from_type).root):
188
+ try:
189
+ return from_type_converter(data)
190
+ except ConvertException:
191
+ pass
192
+
193
+ # If we can't convert directly here, try parent
194
+ if self._parent is not None:
195
+ return self._parent._try_direct_conversion(data, target_root_type)
196
+
197
+ return None
198
+
199
+ # -------------------------------------------------
200
+ # INTERNAL INDIRECT CONVERSION (with parent fallback)
201
+ # -------------------------------------------------
202
+ def _try_indirect_convert(self, data: typing.Any, to_type: type[_T]) -> _T | None:
203
+ """
204
+ Attempt indirect conversion (DFS) in *this* converter.
205
+ If no success, fallback to parent's indirect attempt.
206
+ """
207
+ visited = set()
208
+ final = self._try_indirect_conversion(data, to_type, visited)
209
+ src_type = type(data)
210
+ if final is not None:
211
+ # Warn once if found a chain
212
+ self._maybe_warn_indirect(src_type, to_type)
213
+ return final
214
+
215
+ # If no success, try parent's indirect
216
+ if self._parent is not None:
217
+ parent_final = self._parent._try_indirect_convert(data, to_type)
218
+ if parent_final is not None:
219
+ self._maybe_warn_indirect(src_type, to_type)
220
+ return parent_final
221
+
222
+ return None
223
+
224
+ def _try_indirect_conversion(self, data: typing.Any, to_type: type[_T], visited: set[type]) -> _T | None:
225
+ """
226
+ DFS attempt to find a chain of conversions from type(data) to to_type,
227
+ ignoring parent. If not found, returns None.
228
+ """
229
+ # 1) If data is already correct type
230
+ if isinstance(data, to_type):
231
+ return data
232
+
233
+ current_type = type(data)
234
+ if current_type in visited:
235
+ return None
236
+
237
+ visited.add(current_type)
238
+
239
+ # 2) Attempt each known converter from current_type -> ???, then recurse
240
+ for _, to_type_converters in self._converters.items():
241
+ for convert_from_type, from_type_converter in to_type_converters.items():
242
+ if isinstance(data, convert_from_type):
243
+ try:
244
+ next_data = from_type_converter(data)
245
+ if isinstance(next_data, to_type):
246
+ return next_data
247
+ # else keep going
248
+ deeper = self._try_indirect_conversion(next_data, to_type, visited)
249
+ if deeper is not None:
250
+ return deeper
251
+ except ConvertException:
252
+ pass
253
+
254
+ return None
255
+
256
+ def _maybe_warn_indirect(self, source_type: type, to_type: type):
257
+ """
258
+ Warn once if an indirect path was used between these two types.
259
+ """
260
+ pair = (source_type, to_type)
261
+ if pair not in self._indirect_warnings_shown:
262
+ logger.warning(
263
+ "Indirect type conversion used to convert %s to %s, which may lead to unintended conversions. "
264
+ "Consider adding a direct converter from %s to %s to ensure correctness.",
265
+ source_type,
266
+ to_type,
267
+ source_type,
268
+ to_type)
269
+ self._indirect_warnings_shown.add(pair)
270
+
271
+
272
+ class GlobalTypeConverter:
273
+ _global_converter: TypeConverter = TypeConverter([])
274
+
275
+ @staticmethod
276
+ def get() -> TypeConverter:
277
+ return GlobalTypeConverter._global_converter
278
+
279
+ @staticmethod
280
+ def register_converter(converter: Callable) -> None:
281
+ GlobalTypeConverter._global_converter.add_converter(converter)
282
+
283
+ @staticmethod
284
+ def convert(data, to_type: type[_T]) -> _T:
285
+ return GlobalTypeConverter._global_converter.convert(data, to_type)
286
+
287
+ @staticmethod
288
+ def try_convert(data: typing.Any, to_type: type[_T]) -> _T | typing.Any:
289
+ return GlobalTypeConverter._global_converter.try_convert(data, to_type)
290
+
291
+
292
+ TypeConverter._global_initialized = True
293
+
294
+
295
+ def _text_io_wrapper_to_string(data: TextIOWrapper) -> str:
296
+ return data.read()
297
+
298
+
299
+ GlobalTypeConverter.register_converter(_text_io_wrapper_to_string)