nvidia-nat 1.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (435) hide show
  1. aiq/__init__.py +66 -0
  2. nat/agent/__init__.py +0 -0
  3. nat/agent/base.py +256 -0
  4. nat/agent/dual_node.py +67 -0
  5. nat/agent/react_agent/__init__.py +0 -0
  6. nat/agent/react_agent/agent.py +363 -0
  7. nat/agent/react_agent/output_parser.py +104 -0
  8. nat/agent/react_agent/prompt.py +44 -0
  9. nat/agent/react_agent/register.py +149 -0
  10. nat/agent/reasoning_agent/__init__.py +0 -0
  11. nat/agent/reasoning_agent/reasoning_agent.py +225 -0
  12. nat/agent/register.py +23 -0
  13. nat/agent/rewoo_agent/__init__.py +0 -0
  14. nat/agent/rewoo_agent/agent.py +415 -0
  15. nat/agent/rewoo_agent/prompt.py +110 -0
  16. nat/agent/rewoo_agent/register.py +157 -0
  17. nat/agent/tool_calling_agent/__init__.py +0 -0
  18. nat/agent/tool_calling_agent/agent.py +119 -0
  19. nat/agent/tool_calling_agent/register.py +106 -0
  20. nat/authentication/__init__.py +14 -0
  21. nat/authentication/api_key/__init__.py +14 -0
  22. nat/authentication/api_key/api_key_auth_provider.py +96 -0
  23. nat/authentication/api_key/api_key_auth_provider_config.py +124 -0
  24. nat/authentication/api_key/register.py +26 -0
  25. nat/authentication/exceptions/__init__.py +14 -0
  26. nat/authentication/exceptions/api_key_exceptions.py +38 -0
  27. nat/authentication/http_basic_auth/__init__.py +0 -0
  28. nat/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
  29. nat/authentication/http_basic_auth/register.py +30 -0
  30. nat/authentication/interfaces.py +93 -0
  31. nat/authentication/oauth2/__init__.py +14 -0
  32. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +107 -0
  33. nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
  34. nat/authentication/oauth2/register.py +25 -0
  35. nat/authentication/register.py +21 -0
  36. nat/builder/__init__.py +0 -0
  37. nat/builder/builder.py +285 -0
  38. nat/builder/component_utils.py +316 -0
  39. nat/builder/context.py +270 -0
  40. nat/builder/embedder.py +24 -0
  41. nat/builder/eval_builder.py +161 -0
  42. nat/builder/evaluator.py +29 -0
  43. nat/builder/framework_enum.py +24 -0
  44. nat/builder/front_end.py +73 -0
  45. nat/builder/function.py +344 -0
  46. nat/builder/function_base.py +380 -0
  47. nat/builder/function_info.py +627 -0
  48. nat/builder/intermediate_step_manager.py +174 -0
  49. nat/builder/llm.py +25 -0
  50. nat/builder/retriever.py +25 -0
  51. nat/builder/user_interaction_manager.py +78 -0
  52. nat/builder/workflow.py +148 -0
  53. nat/builder/workflow_builder.py +1117 -0
  54. nat/cli/__init__.py +14 -0
  55. nat/cli/cli_utils/__init__.py +0 -0
  56. nat/cli/cli_utils/config_override.py +231 -0
  57. nat/cli/cli_utils/validation.py +37 -0
  58. nat/cli/commands/__init__.py +0 -0
  59. nat/cli/commands/configure/__init__.py +0 -0
  60. nat/cli/commands/configure/channel/__init__.py +0 -0
  61. nat/cli/commands/configure/channel/add.py +28 -0
  62. nat/cli/commands/configure/channel/channel.py +34 -0
  63. nat/cli/commands/configure/channel/remove.py +30 -0
  64. nat/cli/commands/configure/channel/update.py +30 -0
  65. nat/cli/commands/configure/configure.py +33 -0
  66. nat/cli/commands/evaluate.py +139 -0
  67. nat/cli/commands/info/__init__.py +14 -0
  68. nat/cli/commands/info/info.py +37 -0
  69. nat/cli/commands/info/list_channels.py +32 -0
  70. nat/cli/commands/info/list_components.py +129 -0
  71. nat/cli/commands/info/list_mcp.py +304 -0
  72. nat/cli/commands/registry/__init__.py +14 -0
  73. nat/cli/commands/registry/publish.py +88 -0
  74. nat/cli/commands/registry/pull.py +118 -0
  75. nat/cli/commands/registry/registry.py +36 -0
  76. nat/cli/commands/registry/remove.py +108 -0
  77. nat/cli/commands/registry/search.py +155 -0
  78. nat/cli/commands/sizing/__init__.py +14 -0
  79. nat/cli/commands/sizing/calc.py +297 -0
  80. nat/cli/commands/sizing/sizing.py +27 -0
  81. nat/cli/commands/start.py +246 -0
  82. nat/cli/commands/uninstall.py +81 -0
  83. nat/cli/commands/validate.py +47 -0
  84. nat/cli/commands/workflow/__init__.py +14 -0
  85. nat/cli/commands/workflow/templates/__init__.py.j2 +0 -0
  86. nat/cli/commands/workflow/templates/config.yml.j2 +16 -0
  87. nat/cli/commands/workflow/templates/pyproject.toml.j2 +22 -0
  88. nat/cli/commands/workflow/templates/register.py.j2 +5 -0
  89. nat/cli/commands/workflow/templates/workflow.py.j2 +36 -0
  90. nat/cli/commands/workflow/workflow.py +37 -0
  91. nat/cli/commands/workflow/workflow_commands.py +317 -0
  92. nat/cli/entrypoint.py +135 -0
  93. nat/cli/main.py +57 -0
  94. nat/cli/register_workflow.py +488 -0
  95. nat/cli/type_registry.py +1000 -0
  96. nat/data_models/__init__.py +14 -0
  97. nat/data_models/api_server.py +716 -0
  98. nat/data_models/authentication.py +231 -0
  99. nat/data_models/common.py +171 -0
  100. nat/data_models/component.py +58 -0
  101. nat/data_models/component_ref.py +168 -0
  102. nat/data_models/config.py +410 -0
  103. nat/data_models/dataset_handler.py +169 -0
  104. nat/data_models/discovery_metadata.py +305 -0
  105. nat/data_models/embedder.py +27 -0
  106. nat/data_models/evaluate.py +127 -0
  107. nat/data_models/evaluator.py +26 -0
  108. nat/data_models/front_end.py +26 -0
  109. nat/data_models/function.py +30 -0
  110. nat/data_models/function_dependencies.py +72 -0
  111. nat/data_models/interactive.py +246 -0
  112. nat/data_models/intermediate_step.py +302 -0
  113. nat/data_models/invocation_node.py +38 -0
  114. nat/data_models/llm.py +27 -0
  115. nat/data_models/logging.py +26 -0
  116. nat/data_models/memory.py +27 -0
  117. nat/data_models/object_store.py +44 -0
  118. nat/data_models/profiler.py +54 -0
  119. nat/data_models/registry_handler.py +26 -0
  120. nat/data_models/retriever.py +30 -0
  121. nat/data_models/retry_mixin.py +35 -0
  122. nat/data_models/span.py +190 -0
  123. nat/data_models/step_adaptor.py +64 -0
  124. nat/data_models/streaming.py +33 -0
  125. nat/data_models/swe_bench_model.py +54 -0
  126. nat/data_models/telemetry_exporter.py +26 -0
  127. nat/data_models/ttc_strategy.py +30 -0
  128. nat/embedder/__init__.py +0 -0
  129. nat/embedder/nim_embedder.py +59 -0
  130. nat/embedder/openai_embedder.py +43 -0
  131. nat/embedder/register.py +22 -0
  132. nat/eval/__init__.py +14 -0
  133. nat/eval/config.py +60 -0
  134. nat/eval/dataset_handler/__init__.py +0 -0
  135. nat/eval/dataset_handler/dataset_downloader.py +106 -0
  136. nat/eval/dataset_handler/dataset_filter.py +52 -0
  137. nat/eval/dataset_handler/dataset_handler.py +367 -0
  138. nat/eval/evaluate.py +510 -0
  139. nat/eval/evaluator/__init__.py +14 -0
  140. nat/eval/evaluator/base_evaluator.py +77 -0
  141. nat/eval/evaluator/evaluator_model.py +45 -0
  142. nat/eval/intermediate_step_adapter.py +99 -0
  143. nat/eval/rag_evaluator/__init__.py +0 -0
  144. nat/eval/rag_evaluator/evaluate.py +178 -0
  145. nat/eval/rag_evaluator/register.py +143 -0
  146. nat/eval/register.py +23 -0
  147. nat/eval/remote_workflow.py +133 -0
  148. nat/eval/runners/__init__.py +14 -0
  149. nat/eval/runners/config.py +39 -0
  150. nat/eval/runners/multi_eval_runner.py +54 -0
  151. nat/eval/runtime_event_subscriber.py +52 -0
  152. nat/eval/swe_bench_evaluator/__init__.py +0 -0
  153. nat/eval/swe_bench_evaluator/evaluate.py +215 -0
  154. nat/eval/swe_bench_evaluator/register.py +36 -0
  155. nat/eval/trajectory_evaluator/__init__.py +0 -0
  156. nat/eval/trajectory_evaluator/evaluate.py +75 -0
  157. nat/eval/trajectory_evaluator/register.py +40 -0
  158. nat/eval/tunable_rag_evaluator/__init__.py +0 -0
  159. nat/eval/tunable_rag_evaluator/evaluate.py +245 -0
  160. nat/eval/tunable_rag_evaluator/register.py +52 -0
  161. nat/eval/usage_stats.py +41 -0
  162. nat/eval/utils/__init__.py +0 -0
  163. nat/eval/utils/output_uploader.py +140 -0
  164. nat/eval/utils/tqdm_position_registry.py +40 -0
  165. nat/eval/utils/weave_eval.py +184 -0
  166. nat/experimental/__init__.py +0 -0
  167. nat/experimental/decorators/__init__.py +0 -0
  168. nat/experimental/decorators/experimental_warning_decorator.py +134 -0
  169. nat/experimental/test_time_compute/__init__.py +0 -0
  170. nat/experimental/test_time_compute/editing/__init__.py +0 -0
  171. nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +147 -0
  172. nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +204 -0
  173. nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +107 -0
  174. nat/experimental/test_time_compute/functions/__init__.py +0 -0
  175. nat/experimental/test_time_compute/functions/execute_score_select_function.py +105 -0
  176. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +224 -0
  177. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +205 -0
  178. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +146 -0
  179. nat/experimental/test_time_compute/models/__init__.py +0 -0
  180. nat/experimental/test_time_compute/models/editor_config.py +132 -0
  181. nat/experimental/test_time_compute/models/scoring_config.py +112 -0
  182. nat/experimental/test_time_compute/models/search_config.py +120 -0
  183. nat/experimental/test_time_compute/models/selection_config.py +154 -0
  184. nat/experimental/test_time_compute/models/stage_enums.py +43 -0
  185. nat/experimental/test_time_compute/models/strategy_base.py +66 -0
  186. nat/experimental/test_time_compute/models/tool_use_config.py +41 -0
  187. nat/experimental/test_time_compute/models/ttc_item.py +48 -0
  188. nat/experimental/test_time_compute/register.py +36 -0
  189. nat/experimental/test_time_compute/scoring/__init__.py +0 -0
  190. nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +168 -0
  191. nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +168 -0
  192. nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +111 -0
  193. nat/experimental/test_time_compute/search/__init__.py +0 -0
  194. nat/experimental/test_time_compute/search/multi_llm_planner.py +128 -0
  195. nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +122 -0
  196. nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +128 -0
  197. nat/experimental/test_time_compute/selection/__init__.py +0 -0
  198. nat/experimental/test_time_compute/selection/best_of_n_selector.py +63 -0
  199. nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +131 -0
  200. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +159 -0
  201. nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +128 -0
  202. nat/experimental/test_time_compute/selection/threshold_selector.py +58 -0
  203. nat/front_ends/__init__.py +14 -0
  204. nat/front_ends/console/__init__.py +14 -0
  205. nat/front_ends/console/authentication_flow_handler.py +233 -0
  206. nat/front_ends/console/console_front_end_config.py +32 -0
  207. nat/front_ends/console/console_front_end_plugin.py +96 -0
  208. nat/front_ends/console/register.py +25 -0
  209. nat/front_ends/cron/__init__.py +14 -0
  210. nat/front_ends/fastapi/__init__.py +14 -0
  211. nat/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
  212. nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
  213. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +107 -0
  214. nat/front_ends/fastapi/fastapi_front_end_config.py +241 -0
  215. nat/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
  216. nat/front_ends/fastapi/fastapi_front_end_plugin.py +116 -0
  217. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +1087 -0
  218. nat/front_ends/fastapi/html_snippets/__init__.py +14 -0
  219. nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
  220. nat/front_ends/fastapi/intermediate_steps_subscriber.py +80 -0
  221. nat/front_ends/fastapi/job_store.py +183 -0
  222. nat/front_ends/fastapi/main.py +72 -0
  223. nat/front_ends/fastapi/message_handler.py +320 -0
  224. nat/front_ends/fastapi/message_validator.py +352 -0
  225. nat/front_ends/fastapi/register.py +25 -0
  226. nat/front_ends/fastapi/response_helpers.py +195 -0
  227. nat/front_ends/fastapi/step_adaptor.py +319 -0
  228. nat/front_ends/mcp/__init__.py +14 -0
  229. nat/front_ends/mcp/mcp_front_end_config.py +36 -0
  230. nat/front_ends/mcp/mcp_front_end_plugin.py +81 -0
  231. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +143 -0
  232. nat/front_ends/mcp/register.py +27 -0
  233. nat/front_ends/mcp/tool_converter.py +241 -0
  234. nat/front_ends/register.py +22 -0
  235. nat/front_ends/simple_base/__init__.py +14 -0
  236. nat/front_ends/simple_base/simple_front_end_plugin_base.py +54 -0
  237. nat/llm/__init__.py +0 -0
  238. nat/llm/aws_bedrock_llm.py +57 -0
  239. nat/llm/nim_llm.py +46 -0
  240. nat/llm/openai_llm.py +46 -0
  241. nat/llm/register.py +23 -0
  242. nat/llm/utils/__init__.py +14 -0
  243. nat/llm/utils/env_config_value.py +94 -0
  244. nat/llm/utils/error.py +17 -0
  245. nat/memory/__init__.py +20 -0
  246. nat/memory/interfaces.py +183 -0
  247. nat/memory/models.py +112 -0
  248. nat/meta/pypi.md +58 -0
  249. nat/object_store/__init__.py +20 -0
  250. nat/object_store/in_memory_object_store.py +76 -0
  251. nat/object_store/interfaces.py +84 -0
  252. nat/object_store/models.py +38 -0
  253. nat/object_store/register.py +20 -0
  254. nat/observability/__init__.py +14 -0
  255. nat/observability/exporter/__init__.py +14 -0
  256. nat/observability/exporter/base_exporter.py +449 -0
  257. nat/observability/exporter/exporter.py +78 -0
  258. nat/observability/exporter/file_exporter.py +33 -0
  259. nat/observability/exporter/processing_exporter.py +322 -0
  260. nat/observability/exporter/raw_exporter.py +52 -0
  261. nat/observability/exporter/span_exporter.py +288 -0
  262. nat/observability/exporter_manager.py +335 -0
  263. nat/observability/mixin/__init__.py +14 -0
  264. nat/observability/mixin/batch_config_mixin.py +26 -0
  265. nat/observability/mixin/collector_config_mixin.py +23 -0
  266. nat/observability/mixin/file_mixin.py +288 -0
  267. nat/observability/mixin/file_mode.py +23 -0
  268. nat/observability/mixin/resource_conflict_mixin.py +134 -0
  269. nat/observability/mixin/serialize_mixin.py +61 -0
  270. nat/observability/mixin/type_introspection_mixin.py +183 -0
  271. nat/observability/processor/__init__.py +14 -0
  272. nat/observability/processor/batching_processor.py +310 -0
  273. nat/observability/processor/callback_processor.py +42 -0
  274. nat/observability/processor/intermediate_step_serializer.py +28 -0
  275. nat/observability/processor/processor.py +71 -0
  276. nat/observability/register.py +96 -0
  277. nat/observability/utils/__init__.py +14 -0
  278. nat/observability/utils/dict_utils.py +236 -0
  279. nat/observability/utils/time_utils.py +31 -0
  280. nat/plugins/.namespace +1 -0
  281. nat/profiler/__init__.py +0 -0
  282. nat/profiler/calc/__init__.py +14 -0
  283. nat/profiler/calc/calc_runner.py +627 -0
  284. nat/profiler/calc/calculations.py +288 -0
  285. nat/profiler/calc/data_models.py +188 -0
  286. nat/profiler/calc/plot.py +345 -0
  287. nat/profiler/callbacks/__init__.py +0 -0
  288. nat/profiler/callbacks/agno_callback_handler.py +295 -0
  289. nat/profiler/callbacks/base_callback_class.py +20 -0
  290. nat/profiler/callbacks/langchain_callback_handler.py +290 -0
  291. nat/profiler/callbacks/llama_index_callback_handler.py +205 -0
  292. nat/profiler/callbacks/semantic_kernel_callback_handler.py +238 -0
  293. nat/profiler/callbacks/token_usage_base_model.py +27 -0
  294. nat/profiler/data_frame_row.py +51 -0
  295. nat/profiler/data_models.py +24 -0
  296. nat/profiler/decorators/__init__.py +0 -0
  297. nat/profiler/decorators/framework_wrapper.py +131 -0
  298. nat/profiler/decorators/function_tracking.py +254 -0
  299. nat/profiler/forecasting/__init__.py +0 -0
  300. nat/profiler/forecasting/config.py +18 -0
  301. nat/profiler/forecasting/model_trainer.py +75 -0
  302. nat/profiler/forecasting/models/__init__.py +22 -0
  303. nat/profiler/forecasting/models/forecasting_base_model.py +40 -0
  304. nat/profiler/forecasting/models/linear_model.py +197 -0
  305. nat/profiler/forecasting/models/random_forest_regressor.py +269 -0
  306. nat/profiler/inference_metrics_model.py +28 -0
  307. nat/profiler/inference_optimization/__init__.py +0 -0
  308. nat/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
  309. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +460 -0
  310. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +258 -0
  311. nat/profiler/inference_optimization/data_models.py +386 -0
  312. nat/profiler/inference_optimization/experimental/__init__.py +0 -0
  313. nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +468 -0
  314. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +405 -0
  315. nat/profiler/inference_optimization/llm_metrics.py +212 -0
  316. nat/profiler/inference_optimization/prompt_caching.py +163 -0
  317. nat/profiler/inference_optimization/token_uniqueness.py +107 -0
  318. nat/profiler/inference_optimization/workflow_runtimes.py +72 -0
  319. nat/profiler/intermediate_property_adapter.py +102 -0
  320. nat/profiler/profile_runner.py +473 -0
  321. nat/profiler/utils.py +184 -0
  322. nat/registry_handlers/__init__.py +0 -0
  323. nat/registry_handlers/local/__init__.py +0 -0
  324. nat/registry_handlers/local/local_handler.py +176 -0
  325. nat/registry_handlers/local/register_local.py +37 -0
  326. nat/registry_handlers/metadata_factory.py +60 -0
  327. nat/registry_handlers/package_utils.py +571 -0
  328. nat/registry_handlers/pypi/__init__.py +0 -0
  329. nat/registry_handlers/pypi/pypi_handler.py +251 -0
  330. nat/registry_handlers/pypi/register_pypi.py +40 -0
  331. nat/registry_handlers/register.py +21 -0
  332. nat/registry_handlers/registry_handler_base.py +157 -0
  333. nat/registry_handlers/rest/__init__.py +0 -0
  334. nat/registry_handlers/rest/register_rest.py +56 -0
  335. nat/registry_handlers/rest/rest_handler.py +237 -0
  336. nat/registry_handlers/schemas/__init__.py +0 -0
  337. nat/registry_handlers/schemas/headers.py +42 -0
  338. nat/registry_handlers/schemas/package.py +68 -0
  339. nat/registry_handlers/schemas/publish.py +68 -0
  340. nat/registry_handlers/schemas/pull.py +82 -0
  341. nat/registry_handlers/schemas/remove.py +36 -0
  342. nat/registry_handlers/schemas/search.py +91 -0
  343. nat/registry_handlers/schemas/status.py +47 -0
  344. nat/retriever/__init__.py +0 -0
  345. nat/retriever/interface.py +41 -0
  346. nat/retriever/milvus/__init__.py +14 -0
  347. nat/retriever/milvus/register.py +81 -0
  348. nat/retriever/milvus/retriever.py +228 -0
  349. nat/retriever/models.py +77 -0
  350. nat/retriever/nemo_retriever/__init__.py +14 -0
  351. nat/retriever/nemo_retriever/register.py +60 -0
  352. nat/retriever/nemo_retriever/retriever.py +190 -0
  353. nat/retriever/register.py +22 -0
  354. nat/runtime/__init__.py +14 -0
  355. nat/runtime/loader.py +220 -0
  356. nat/runtime/runner.py +195 -0
  357. nat/runtime/session.py +162 -0
  358. nat/runtime/user_metadata.py +130 -0
  359. nat/settings/__init__.py +0 -0
  360. nat/settings/global_settings.py +318 -0
  361. nat/test/.namespace +1 -0
  362. nat/tool/__init__.py +0 -0
  363. nat/tool/chat_completion.py +74 -0
  364. nat/tool/code_execution/README.md +151 -0
  365. nat/tool/code_execution/__init__.py +0 -0
  366. nat/tool/code_execution/code_sandbox.py +267 -0
  367. nat/tool/code_execution/local_sandbox/.gitignore +1 -0
  368. nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +60 -0
  369. nat/tool/code_execution/local_sandbox/__init__.py +13 -0
  370. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +198 -0
  371. nat/tool/code_execution/local_sandbox/sandbox.requirements.txt +6 -0
  372. nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +50 -0
  373. nat/tool/code_execution/register.py +74 -0
  374. nat/tool/code_execution/test_code_execution_sandbox.py +414 -0
  375. nat/tool/code_execution/utils.py +100 -0
  376. nat/tool/datetime_tools.py +42 -0
  377. nat/tool/document_search.py +141 -0
  378. nat/tool/github_tools/__init__.py +0 -0
  379. nat/tool/github_tools/create_github_commit.py +133 -0
  380. nat/tool/github_tools/create_github_issue.py +87 -0
  381. nat/tool/github_tools/create_github_pr.py +106 -0
  382. nat/tool/github_tools/get_github_file.py +106 -0
  383. nat/tool/github_tools/get_github_issue.py +166 -0
  384. nat/tool/github_tools/get_github_pr.py +256 -0
  385. nat/tool/github_tools/update_github_issue.py +100 -0
  386. nat/tool/mcp/__init__.py +14 -0
  387. nat/tool/mcp/exceptions.py +142 -0
  388. nat/tool/mcp/mcp_client.py +255 -0
  389. nat/tool/mcp/mcp_tool.py +96 -0
  390. nat/tool/memory_tools/__init__.py +0 -0
  391. nat/tool/memory_tools/add_memory_tool.py +79 -0
  392. nat/tool/memory_tools/delete_memory_tool.py +67 -0
  393. nat/tool/memory_tools/get_memory_tool.py +72 -0
  394. nat/tool/nvidia_rag.py +95 -0
  395. nat/tool/register.py +38 -0
  396. nat/tool/retriever.py +94 -0
  397. nat/tool/server_tools.py +66 -0
  398. nat/utils/__init__.py +0 -0
  399. nat/utils/data_models/__init__.py +0 -0
  400. nat/utils/data_models/schema_validator.py +58 -0
  401. nat/utils/debugging_utils.py +43 -0
  402. nat/utils/dump_distro_mapping.py +32 -0
  403. nat/utils/exception_handlers/__init__.py +0 -0
  404. nat/utils/exception_handlers/automatic_retries.py +289 -0
  405. nat/utils/exception_handlers/mcp.py +211 -0
  406. nat/utils/exception_handlers/schemas.py +114 -0
  407. nat/utils/io/__init__.py +0 -0
  408. nat/utils/io/model_processing.py +28 -0
  409. nat/utils/io/yaml_tools.py +119 -0
  410. nat/utils/log_utils.py +37 -0
  411. nat/utils/metadata_utils.py +74 -0
  412. nat/utils/optional_imports.py +142 -0
  413. nat/utils/producer_consumer_queue.py +178 -0
  414. nat/utils/reactive/__init__.py +0 -0
  415. nat/utils/reactive/base/__init__.py +0 -0
  416. nat/utils/reactive/base/observable_base.py +65 -0
  417. nat/utils/reactive/base/observer_base.py +55 -0
  418. nat/utils/reactive/base/subject_base.py +79 -0
  419. nat/utils/reactive/observable.py +59 -0
  420. nat/utils/reactive/observer.py +76 -0
  421. nat/utils/reactive/subject.py +131 -0
  422. nat/utils/reactive/subscription.py +49 -0
  423. nat/utils/settings/__init__.py +0 -0
  424. nat/utils/settings/global_settings.py +197 -0
  425. nat/utils/string_utils.py +38 -0
  426. nat/utils/type_converter.py +290 -0
  427. nat/utils/type_utils.py +484 -0
  428. nat/utils/url_utils.py +27 -0
  429. nvidia_nat-1.2.0.dist-info/METADATA +365 -0
  430. nvidia_nat-1.2.0.dist-info/RECORD +435 -0
  431. nvidia_nat-1.2.0.dist-info/WHEEL +5 -0
  432. nvidia_nat-1.2.0.dist-info/entry_points.txt +21 -0
  433. nvidia_nat-1.2.0.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
  434. nvidia_nat-1.2.0.dist-info/licenses/LICENSE.md +201 -0
  435. nvidia_nat-1.2.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,151 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ SPDX-License-Identifier: Apache-2.0
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ -->
17
+
18
+ # Code Execution Sandbox
19
+
20
+ A secure, containerized Python code execution environment that allows safe execution of Python code with comprehensive error handling and debugging capabilities.
21
+
22
+ ## Overview
23
+
24
+ The Code Execution Sandbox provides:
25
+ - **Secure code execution** in isolated Docker containers
26
+ - **Multiple input formats** including raw code, dictionary format, and markdown
27
+ - **Dependency management** with pre-installed libraries
28
+ - **Flexible configuration** with customizable timeouts and output limits
29
+ - **Robust debugging** with extensive logging and error reporting
30
+
31
+ ## Quick Start
32
+
33
+ ### Step 1: Start the Sandbox Server
34
+
35
+ Navigate to the local sandbox directory and start the server:
36
+
37
+ ```bash
38
+ cd src/nat/tool/code_execution/local_sandbox
39
+ ./start_local_sandbox.sh
40
+ ```
41
+
42
+ The script will:
43
+ - Build the Docker image if it doesn't exist
44
+ - Start the sandbox server on port 6000
45
+ - Mount your working directory for file operations
46
+
47
+ #### Advanced Usage:
48
+ ```bash
49
+ # Custom container name
50
+ ./start_local_sandbox.sh my-sandbox
51
+
52
+ # Custom output directory
53
+ ./start_local_sandbox.sh my-sandbox /path/to/output
54
+
55
+ # Using environment variable
56
+ export OUTPUT_DATA_PATH=/path/to/output
57
+ ./start_local_sandbox.sh
58
+ ```
59
+
60
+ ### Step 2: Test the Installation
61
+
62
+ Run the comprehensive test suite to verify everything is working:
63
+
64
+ ```bash
65
+ cd src/nat/tool/code_execution
66
+ pytest test_code_execution_sandbox.py
67
+ ```
68
+
69
+ Note: a running instance of a local sandbox is required.
70
+
71
+ ## Using the Code Execution Tool
72
+
73
+ ### Basic Usage
74
+
75
+ The sandbox accepts HTTP POST requests to `http://localhost:6000/execute` with JSON payloads:
76
+
77
+ ```bash
78
+ curl -X POST \
79
+ -H "Content-Type: application/json" \
80
+ -d '{
81
+ "generated_code": "print(\"Hello, World!\")",
82
+ "timeout": 30,
83
+ "language": "python"
84
+ }' \
85
+ http://localhost:6000/execute
86
+ ```
87
+
88
+ ### Supported Input Formats
89
+
90
+ #### 1. Raw Python Code
91
+ ```json
92
+ {
93
+ "generated_code": "import numpy as np\nprint(np.array([1, 2, 3]))",
94
+ "timeout": 30,
95
+ "language": "python"
96
+ }
97
+ ```
98
+
99
+ #### 2. Dictionary Format
100
+ ```json
101
+ {
102
+ "generated_code": "{'generated_code': 'print(\"Hello from dict format\")'}",
103
+ "timeout": 30,
104
+ "language": "python"
105
+ }
106
+ ```
107
+
108
+ #### 3. Markdown Code Blocks
109
+ ```json
110
+ {
111
+ "generated_code": "```python\nprint('Hello from markdown')\n```",
112
+ "timeout": 30,
113
+ "language": "python"
114
+ }
115
+ ```
116
+
117
+ ### Response Format
118
+
119
+ The sandbox returns JSON responses with the following structure:
120
+
121
+ ```json
122
+ {
123
+ "process_status": "completed|error|timeout",
124
+ "stdout": "Standard output content",
125
+ "stderr": "Standard error content"
126
+ }
127
+ ```
128
+
129
+ ## Configuration Options
130
+
131
+ ### Sandbox Configuration
132
+
133
+ - **URI**: Default `http://127.0.0.1:6000`
134
+ - **Timeout**: Default 10 seconds (configurable)
135
+ - **Max Output Characters**: Default 1000 characters
136
+ - **Memory Limit**: 10GB (configurable in Docker)
137
+ - **Working Directory**: Mounted volume for file operations
138
+
139
+ ### Environment Variables
140
+
141
+ - `OUTPUT_DATA_PATH`: Custom path for file operations
142
+ - `SANDBOX_HOST`: Custom sandbox host
143
+ - `SANDBOX_PORT`: Custom sandbox port
144
+
145
+ ## Security Considerations
146
+
147
+ - **Isolated execution**: All code runs in Docker containers
148
+ - **Resource limits**: Memory and CPU limits prevent resource exhaustion
149
+ - **Network isolation**: Containers have limited network access
150
+ - **File system isolation**: Mounted volumes provide controlled file access
151
+ - **Process isolation**: Each execution runs in a separate process
File without changes
@@ -0,0 +1,267 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import abc
16
+ import json
17
+ import logging
18
+ import textwrap
19
+ from typing import Any
20
+ from urllib.parse import urljoin
21
+
22
+ import requests
23
+ import requests.adapters
24
+ from pydantic import HttpUrl
25
+
26
+ from nat.utils.type_utils import override
27
+
28
+ logger = logging.getLogger(__file__)
29
+
30
+
31
+ class Sandbox(abc.ABC):
32
+ """Code execution sandbox.
33
+
34
+ Args:
35
+ host: Optional[str] = '127.0.0.1' - Host of the sandbox server.
36
+ Can also be specified through NEMO_SKILLS_SANDBOX_HOST env var.
37
+ port: Optional[str] = '5000' - Port of the sandbox server.
38
+ Can also be specified through NEMO_SKILLS_SANDBOX_PORT env var.
39
+ ssh_server: Optional[str] = None - SSH server for tunneling requests.
40
+ Useful if server is running on slurm cluster to which there is an ssh access.
41
+ Can also be specified through NEMO_SKILLS_SSH_SERVER env var.
42
+ ssh_key_path: Optional[str] = None - Path to the ssh key for tunneling.
43
+ Can also be specified through NEMO_SKILLS_SSH_KEY_PATH env var.
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ *,
49
+ uri: HttpUrl,
50
+ ):
51
+ self.url: str = self._get_execute_url(uri)
52
+ session = requests.Session()
53
+ adapter = requests.adapters.HTTPAdapter(pool_maxsize=1500, pool_connections=1500, max_retries=3)
54
+ session.mount('http://', adapter)
55
+ session.mount('https://', adapter)
56
+ self.http_session: requests.Session = session
57
+
58
+ def _send_request(self, request: dict[str, Any], timeout_seconds: float) -> dict[str, str]:
59
+ output = self.http_session.post(
60
+ url=self.url,
61
+ data=json.dumps(request),
62
+ timeout=timeout_seconds,
63
+ headers={"Content-Type": "application/json"},
64
+ )
65
+ # retrying 502 errors
66
+ if output.status_code == 502:
67
+ raise requests.exceptions.Timeout
68
+
69
+ return self._parse_request_output(output)
70
+
71
+ @abc.abstractmethod
72
+ def _parse_request_output(self, output: requests.Response) -> dict[str, str]:
73
+ pass
74
+
75
+ @abc.abstractmethod
76
+ def _get_execute_url(self, uri: HttpUrl) -> str:
77
+ pass
78
+
79
+ @abc.abstractmethod
80
+ def _prepare_request(self, generated_code: str, timeout_seconds: float) -> dict[str, Any]:
81
+ pass
82
+
83
+ async def execute_code(
84
+ self,
85
+ generated_code: str,
86
+ timeout_seconds: float = 10.0,
87
+ language: str = "python",
88
+ max_output_characters: int = 1000,
89
+ ) -> dict[str, str]:
90
+
91
+ if language != "python":
92
+ raise ValueError(f"Language {language} not supported")
93
+
94
+ generated_code = generated_code.strip().strip("`")
95
+ code_to_execute = textwrap.dedent("""
96
+ import traceback
97
+ import json
98
+ import os
99
+ import warnings
100
+ import contextlib
101
+ import io
102
+ warnings.filterwarnings('ignore')
103
+ os.environ['OPENBLAS_NUM_THREADS'] = '16'
104
+ """).strip()
105
+
106
+ # Use json.dumps to properly escape the generated_code instead of repr()
107
+ escaped_code = json.dumps(generated_code)
108
+ code_to_execute += textwrap.dedent(f"""
109
+
110
+ generated_code = {escaped_code}
111
+
112
+ stdout = io.StringIO()
113
+ stderr = io.StringIO()
114
+
115
+ with contextlib.redirect_stdout(stdout), contextlib.redirect_stderr(stderr):
116
+ try:
117
+ exec(generated_code)
118
+ status = "completed"
119
+ except Exception:
120
+ status = "error"
121
+ stderr.write(traceback.format_exc())
122
+ stdout = stdout.getvalue()
123
+ stderr = stderr.getvalue()
124
+ if len(stdout) > {max_output_characters}:
125
+ stdout = stdout[:{max_output_characters}] + "<output cut>"
126
+ if len(stderr) > {max_output_characters}:
127
+ stderr = stderr[:{max_output_characters}] + "<output cut>"
128
+ if stdout:
129
+ stdout += "\\n"
130
+ if stderr:
131
+ stderr += "\\n"
132
+ output = {{"process_status": status, "stdout": stdout, "stderr": stderr}}
133
+ print(json.dumps(output))
134
+ """).strip()
135
+ request = self._prepare_request(code_to_execute, timeout_seconds)
136
+ try:
137
+ return self._send_request(request, timeout_seconds)
138
+ except requests.exceptions.Timeout:
139
+ return {"process_status": "timeout", "stdout": "", "stderr": "Timed out\n"}
140
+
141
+
142
+ class LocalSandbox(Sandbox):
143
+ """Locally hosted sandbox."""
144
+
145
+ def __init__(self, *, uri: HttpUrl):
146
+ super().__init__(uri=uri)
147
+
148
+ @override
149
+ def _get_execute_url(self, uri: HttpUrl) -> str:
150
+ return urljoin(str(uri), "execute")
151
+
152
+ @override
153
+ def _parse_request_output(self, output: requests.Response) -> dict[str, str]:
154
+ try:
155
+ output_json = output.json()
156
+ assert isinstance(output_json, dict)
157
+ return output_json
158
+ except json.JSONDecodeError as e:
159
+ logger.exception("Error parsing output: %s. %s", output.text, e)
160
+ return {'process_status': 'error', 'stdout': '', 'stderr': f'Unknown error: {e} \"{output.text}\"'}
161
+
162
+ @override
163
+ def _prepare_request(self,
164
+ generated_code: str,
165
+ timeout_seconds: float,
166
+ language: str = "python",
167
+ **kwargs) -> dict[str, Any]:
168
+ request = {
169
+ "generated_code": generated_code,
170
+ "timeout": timeout_seconds,
171
+ "language": language,
172
+ }
173
+ return request
174
+
175
+ @override
176
+ async def execute_code(
177
+ self,
178
+ generated_code: str,
179
+ timeout_seconds: float = 10.0,
180
+ language: str = "python",
181
+ max_output_characters: int = 1000,
182
+ ) -> dict[str, str]:
183
+ """Override execute_code to bypass the wrapper logic and send user code directly to our server."""
184
+
185
+ logger.debug("Raw input generated_code: %s", generated_code)
186
+
187
+ # The input appears to be a string representation of a dictionary
188
+ # We need to parse it and extract the actual code
189
+ try:
190
+ # Try to evaluate the string as a Python literal (dictionary)
191
+ import ast
192
+ parsed_dict = ast.literal_eval(generated_code)
193
+ if isinstance(parsed_dict, dict) and 'generated_code' in parsed_dict:
194
+ actual_code = parsed_dict['generated_code']
195
+ assert isinstance(actual_code, str)
196
+ logger.debug("Extracted code from dict: %s...", actual_code[:100])
197
+ else:
198
+ # If it's not a dict or doesn't have the expected key, use as-is
199
+ actual_code = generated_code
200
+ logger.debug("Using code as-is: %s...", actual_code[:100])
201
+ except (ValueError, SyntaxError):
202
+ # If parsing fails, use the input as-is
203
+ actual_code = generated_code
204
+ logger.debug("Failed to parse, using as-is: %s...", actual_code[:100])
205
+
206
+ # Clean the actual code more carefully to avoid removing backticks that are part of Python code
207
+ # remove all leading/trailing whitespace -- strip()
208
+ # remove all leading/trailing backticks -- strip("`")
209
+ # may potentially start with python, so just trim from the front.
210
+ POTENTIAL_PREFIXES = ["python"]
211
+ actual_code = actual_code.strip().strip("`")
212
+ for prefix in POTENTIAL_PREFIXES:
213
+ if actual_code.startswith(prefix):
214
+ actual_code = actual_code[len(prefix):]
215
+ break
216
+
217
+ # Send the user's code directly to our server without any wrapper logic
218
+ # Our server already handles stdout/stderr capture and error handling
219
+ request = self._prepare_request(actual_code, timeout_seconds, language)
220
+ try:
221
+ return self._send_request(request, timeout_seconds)
222
+ except requests.exceptions.Timeout:
223
+ return {"process_status": "timeout", "stdout": "", "stderr": "Timed out\n"}
224
+
225
+
226
+ class PistonSandbox(Sandbox):
227
+ """Piston sandbox (https://github.com/engineer-man/piston)"""
228
+
229
+ @override
230
+ def _get_execute_url(self, uri: HttpUrl) -> str:
231
+ return urljoin(str(uri), "execute")
232
+
233
+ @override
234
+ def _parse_request_output(self, output: requests.Response) -> dict[str, str]:
235
+ output_json = output.json()
236
+ assert isinstance(output_json, dict)
237
+ assert 'run' in output_json
238
+ run_json = output_json['run']
239
+ assert isinstance(run_json, dict)
240
+ if run_json["code"] != 0:
241
+ return {'process_status': "error", 'stdout': run_json['stdout'], 'stderr': run_json['stderr']}
242
+ return {'process_status': "completed", 'stdout': run_json['stdout'], 'stderr': run_json['stderr']}
243
+
244
+ @override
245
+ def _prepare_request(self, generated_code: str, timeout_seconds: float, **kwargs) -> dict[str, Any]:
246
+ return {
247
+ "language": "py",
248
+ "version": "3.10.0",
249
+ "files": [{
250
+ "content": generated_code,
251
+ }],
252
+ "stdin": "",
253
+ "args": [],
254
+ "run_timeout": timeout_seconds * 1000.0, # milliseconds
255
+ "compile_memory_limit": -1,
256
+ "run_memory_limit": -1,
257
+ }
258
+
259
+
260
+ def get_sandbox(sandbox_type: str = "local", **kwargs):
261
+ """A helper function to make it easier to set sandbox through cmd."""
262
+ sandboxes = {
263
+ 'local': LocalSandbox,
264
+ 'piston': PistonSandbox,
265
+ }
266
+ sandbox_class = sandboxes[sandbox_type.lower()]
267
+ return sandbox_class(**kwargs)
@@ -0,0 +1 @@
1
+ persistence_test.*
@@ -0,0 +1,60 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Use the base image with Python 3.10 and Flask
16
+ FROM tiangolo/uwsgi-nginx-flask:python3.10
17
+
18
+ # Install dependencies required for Lean 4 and other tools
19
+ RUN apt-get update && \
20
+ apt-get install -y curl git && \
21
+ curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh -s -- -y && \
22
+ /root/.elan/bin/elan toolchain install leanprover/lean4:v4.12.0 && \
23
+ /root/.elan/bin/elan default leanprover/lean4:v4.12.0 && \
24
+ /root/.elan/bin/elan self update
25
+
26
+ # Set environment variables to include Lean and elan/lake in the PATH
27
+ ENV PATH="/root/.elan/bin:$PATH"
28
+
29
+ # Create Lean project directory and initialize a new Lean project with Mathlib4
30
+ RUN mkdir -p /lean4 && cd /lean4 && \
31
+ /root/.elan/bin/lake new my_project && \
32
+ cd my_project && \
33
+ echo 'leanprover/lean4:v4.12.0' > lean-toolchain && \
34
+ echo 'require mathlib from git "https://github.com/leanprover-community/mathlib4" @ "v4.12.0"' >> lakefile.lean
35
+
36
+ # Download and cache Mathlib4 to avoid recompiling, then build the project
37
+ RUN cd /lean4/my_project && \
38
+ /root/.elan/bin/lake exe cache get && \
39
+ /root/.elan/bin/lake build
40
+
41
+ # Set environment variables to include Lean project path
42
+ ENV LEAN_PATH="/lean4/my_project"
43
+ ENV PATH="/lean4/my_project:$PATH"
44
+
45
+ # Set up application code and install Python dependencies
46
+ COPY sandbox.requirements.txt /app/requirements.txt
47
+ RUN pip install --no-cache-dir -r /app/requirements.txt
48
+ COPY local_sandbox_server.py /app/main.py
49
+
50
+ # Set the working directory to /app
51
+ WORKDIR /app
52
+
53
+ # Set Flask app environment variables and ports
54
+ ARG UWSGI_CHEAPER
55
+ ENV UWSGI_CHEAPER=$UWSGI_CHEAPER
56
+
57
+ ARG UWSGI_PROCESSES
58
+ ENV UWSGI_PROCESSES=$UWSGI_PROCESSES
59
+
60
+ ENV LISTEN_PORT=6000
@@ -0,0 +1,13 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,198 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from __future__ import annotations
16
+
17
+ import contextlib
18
+ import logging
19
+ import multiprocessing
20
+ import os
21
+ import resource
22
+ from enum import Enum
23
+ from io import StringIO
24
+
25
+ from flask import Flask
26
+ from flask import Request
27
+ from flask import Response
28
+ from flask import request
29
+ from pydantic import BaseModel
30
+ from pydantic import Field
31
+
32
+ app = Flask(__name__)
33
+ logger = logging.getLogger(__name__)
34
+ logger.setLevel(logging.WARNING)
35
+
36
+
37
+ class CodeExecutionStatus(str, Enum):
38
+ """
39
+ Status of code execution.
40
+ """
41
+ COMPLETED = "completed"
42
+ ERROR = "error"
43
+ TIMEOUT = "timeout"
44
+
45
+
46
+ class CodeExecutionResult(BaseModel):
47
+ """
48
+ Result of code execution.
49
+ """
50
+ process_status: CodeExecutionStatus = Field(default=CodeExecutionStatus.COMPLETED,
51
+ description="Status of the process")
52
+ stdout: str = Field(description="Standard output of the process")
53
+ stderr: str = Field(description="Standard error of the process")
54
+
55
+
56
+ class CodeExecutionResponse(Response):
57
+ """
58
+ Response class that returns a JSON response with the given status code and result.
59
+ """
60
+
61
+ def __init__(self, status_code: int, result: CodeExecutionResult):
62
+ super().__init__(status=status_code, mimetype="application/json", response=result.model_dump_json())
63
+
64
+ @classmethod
65
+ def with_error(cls, status_code: int, error_message: str) -> 'CodeExecutionResponse':
66
+ return cls(status_code,
67
+ CodeExecutionResult(process_status=CodeExecutionStatus.ERROR, stdout="", stderr=error_message))
68
+
69
+
70
+ @app.after_request
71
+ def add_hsts_header(response):
72
+ response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
73
+ response.headers['X-Content-Type-Options'] = 'nosniff'
74
+ response.headers['X-Frame-Options'] = 'SAMEORIGIN'
75
+ response.headers['X-XSS-Protection'] = '1; mode=block'
76
+
77
+ return response
78
+
79
+
80
+ def execute_python(generated_code: str, timeout: float) -> CodeExecutionResult:
81
+ """
82
+ Execute Python code in a subprocess.
83
+
84
+ Args:
85
+ generated_code: The code to execute
86
+ timeout: The timeout for the execution
87
+
88
+ Returns:
89
+ CodeExecutionResult object containing the execution result
90
+ """
91
+
92
+ # running in a separate process to ensure any kind of crashes are properly handled
93
+ queue = multiprocessing.Queue()
94
+ process = multiprocessing.Process(target=execute_code_subprocess, args=(generated_code, queue))
95
+
96
+ process.start()
97
+ # wait until the process finishes or the timeout expires
98
+ process.join(timeout=timeout)
99
+ if process.exitcode is None:
100
+ process.kill()
101
+ return CodeExecutionResult(process_status=CodeExecutionStatus.TIMEOUT, stdout="", stderr="Timed out\n")
102
+
103
+ return queue.get()
104
+
105
+
106
+ # need to memory-limit to avoid common errors of allocating too much
107
+ # but this has to be done in a subprocess to not crush server itself
108
+ def execute_code_subprocess(generated_code: str, queue):
109
+ """
110
+ Execute code in a subprocess.
111
+
112
+ Args:
113
+ generated_code: The code to execute
114
+ queue: The queue to put the result in
115
+ """
116
+
117
+ logger.debug("execute_code_subprocess started, PID: %s", os.getpid())
118
+
119
+ try:
120
+ limit = 1024 * 1024 * 1024 * 10 # 10gb - somehow with a smaller limit the server dies when numpy is used
121
+ resource.setrlimit(resource.RLIMIT_AS, (limit, limit))
122
+ resource.setrlimit(resource.RLIMIT_DATA, (limit, limit))
123
+ except Exception as e:
124
+ logger.error("Failed to set resource limits, PID: %s, error: %s", os.getpid(), e)
125
+
126
+ stdout_capture = StringIO()
127
+ stderr_capture = StringIO()
128
+ try:
129
+ with contextlib.redirect_stdout(stdout_capture), contextlib.redirect_stderr(stderr_capture):
130
+ exec(generated_code, {}) # pylint: disable=W0122
131
+ logger.debug("execute_code_subprocess finished, PID: %s", os.getpid())
132
+ queue.put(CodeExecutionResult(stdout=stdout_capture.getvalue(), stderr=stderr_capture.getvalue()))
133
+ except Exception as e:
134
+ import traceback
135
+ with contextlib.redirect_stderr(stderr_capture):
136
+ traceback.print_exc()
137
+ logger.debug("execute_code_subprocess failed, PID: %s, error: %s", os.getpid(), e)
138
+ queue.put(
139
+ CodeExecutionResult(process_status=CodeExecutionStatus.ERROR,
140
+ stdout=stdout_capture.getvalue(),
141
+ stderr=stderr_capture.getvalue()))
142
+
143
+
144
+ def do_execute(request: Request) -> CodeExecutionResponse:
145
+ """
146
+ Main function to handle execution requests.
147
+
148
+ Args:
149
+ request: Request object containing the execution request
150
+
151
+ Returns:
152
+ CodeExecutionResponse object containing the execution result
153
+ """
154
+ try:
155
+ # Check if request has JSON data
156
+ if not request.is_json:
157
+ return CodeExecutionResponse.with_error(400, "Request must be JSON")
158
+
159
+ # Get JSON data safely
160
+ json_data = request.get_json(silent=True)
161
+
162
+ if json_data is None:
163
+ return CodeExecutionResponse.with_error(400, "Invalid JSON data")
164
+
165
+ # Check for required fields
166
+ if 'generated_code' not in json_data:
167
+ return CodeExecutionResponse.with_error(400, "Missing required field: generated_code")
168
+
169
+ if 'timeout' not in json_data:
170
+ return CodeExecutionResponse.with_error(400, "Missing required field: timeout")
171
+
172
+ if 'language' not in json_data:
173
+ return CodeExecutionResponse.with_error(400, "Missing required field: language")
174
+
175
+ generated_code: str | None = json_data.get('generated_code', None)
176
+ assert generated_code is not None
177
+ timeout: float | None = json_data.get('timeout', None)
178
+ assert timeout is not None
179
+ language: str | None = json_data.get('language', None)
180
+ assert language is not None
181
+
182
+ if language != 'python':
183
+ return CodeExecutionResponse.with_error(400, "Only python execution is supported")
184
+
185
+ return CodeExecutionResponse(200, execute_python(generated_code, timeout))
186
+
187
+ except Exception as e:
188
+ return CodeExecutionResponse.with_error(500, f"Server error: {str(e)}")
189
+
190
+
191
+ # Main Flask endpoint to handle execution requests
192
+ @app.route("/execute", methods=["POST"])
193
+ def execute():
194
+ return do_execute(request)
195
+
196
+
197
+ if __name__ == '__main__':
198
+ app.run(port=6000)