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,478 @@
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 json
17
+ import logging
18
+ import math
19
+ import os
20
+ import statistics
21
+ from pathlib import Path
22
+ from typing import Any
23
+
24
+ from pydantic import BaseModel
25
+
26
+ from nat.data_models.evaluate import ProfilerConfig
27
+ from nat.data_models.intermediate_step import IntermediateStep
28
+ from nat.profiler.data_models import ProfilerResults
29
+ from nat.profiler.forecasting.model_trainer import ModelTrainer
30
+ from nat.profiler.inference_metrics_model import InferenceMetricsModel
31
+ from nat.profiler.utils import create_standardized_dataframe
32
+ from nat.utils.type_converter import TypeConverter
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class SimpleMetricsHolder(BaseModel):
38
+ workflow_run_time_confidence_intervals: Any
39
+ llm_latency_confidence_intervals: Any
40
+ throughput_estimate_confidence_interval: Any
41
+
42
+
43
+ class InferenceOptimizationHolder(BaseModel):
44
+ confidence_intervals: SimpleMetricsHolder
45
+ common_prefixes: Any
46
+ token_uniqueness: Any
47
+ workflow_runtimes: Any
48
+
49
+
50
+ class ProfilerRunner:
51
+ """
52
+ A utility to run a series of prompts through a NAT workflow for profiling:
53
+
54
+ - can load prompts from a file
55
+ - or generate them via an LLM
56
+ - collect usage stats for each run
57
+ - store them in a configured directory
58
+
59
+ Updated version with additional metrics:
60
+
61
+ - For each request, we collect a list of UsageStatistic objects, store them individually,
62
+ and also keep a final large JSON of all requests.
63
+ - We then compute:
64
+ 1. 90, 95, 99% confidence intervals for the mean total workflow run time.
65
+ 2. 90, 95, 99% confidence intervals for the mean LLM latency.
66
+ 3. 90, 95, 99% estimates of throughput.
67
+
68
+ All computed metrics are saved to a metrics JSON file at the end.
69
+ """
70
+
71
+ def __init__(self, profiler_config: ProfilerConfig, output_dir: Path, write_output: bool = True):
72
+ self.profile_config = profiler_config
73
+ self.output_dir = output_dir
74
+ self.write_output = write_output
75
+ self._converter = TypeConverter([])
76
+
77
+ # Holds per-request data (prompt, output, usage_stats, etc.)
78
+ # This will be saved at the end to a big JSON file
79
+ self.all_requests_data: list[dict] = []
80
+ self.all_steps = []
81
+
82
+ # Ensure output directory
83
+ os.makedirs(output_dir, exist_ok=True)
84
+
85
+ async def run(self, all_steps: list[list[IntermediateStep]]) -> ProfilerResults:
86
+ """
87
+ Main entrypoint: Works on Input DataFrame generated from eval to fit forecasting model,
88
+ writes out combined requests JSON, then computes and saves additional metrics,
89
+ and optionally fits a forecasting model.
90
+ """
91
+ # Yapf and ruff disagree on how to format long imports, disable yapf go with ruff
92
+ from nat.profiler.inference_optimization.bottleneck_analysis.nested_stack_analysis import (
93
+ multi_example_call_profiling,
94
+ ) # yapf: disable
95
+ from nat.profiler.inference_optimization.bottleneck_analysis.simple_stack_analysis import (
96
+ profile_workflow_bottlenecks,
97
+ ) # yapf: disable
98
+ from nat.profiler.inference_optimization.experimental.concurrency_spike_analysis import (
99
+ concurrency_spike_analysis,
100
+ ) # yapf: disable
101
+ from nat.profiler.inference_optimization.experimental.prefix_span_analysis import (
102
+ prefixspan_subworkflow_with_text,
103
+ ) # yapf: disable
104
+ from nat.profiler.inference_optimization.llm_metrics import LLMMetrics
105
+ from nat.profiler.inference_optimization.prompt_caching import get_common_prefixes
106
+ from nat.profiler.inference_optimization.token_uniqueness import compute_inter_query_token_uniqueness_by_llm
107
+ from nat.profiler.inference_optimization.workflow_runtimes import compute_workflow_runtime_metrics
108
+ from nat.profiler.intermediate_property_adapter import IntermediatePropertyAdaptor
109
+
110
+ # Convert the incoming DataFrame to a list of dicts and store
111
+ all_steps = [[IntermediatePropertyAdaptor.from_intermediate_step(step) for step in steps]
112
+ for steps in all_steps] # Add adapter properties to each step
113
+
114
+ self.all_steps = all_steps
115
+ self.all_requests_data = []
116
+ for i, steps in enumerate(all_steps):
117
+ request_data = []
118
+ for step in steps:
119
+ request_data.append(step.model_dump())
120
+ self.all_requests_data.append({"request_number": i, "intermediate_steps": request_data})
121
+
122
+ # Write the final big JSON (all requests)
123
+ if self.write_output:
124
+ final_path = os.path.join(self.output_dir, "all_requests_profiler_traces.json")
125
+ with open(final_path, 'w', encoding='utf-8') as f:
126
+ json.dump(self.all_requests_data, f, indent=2, default=str)
127
+ logger.info("Wrote combined data to: %s", final_path)
128
+
129
+ # ------------------------------------------------------------
130
+ # Generate one standardized dataframe for all usage stats
131
+ # ------------------------------------------------------------
132
+ merged_df = create_standardized_dataframe(all_steps)
133
+
134
+ if self.profile_config.compute_llm_metrics and not merged_df.empty:
135
+ merged_df = LLMMetrics.compute_profiling_metrics(all_steps)
136
+
137
+ output_df = merged_df.copy()
138
+
139
+ if self.profile_config.csv_exclude_io_text and not output_df.empty:
140
+ # Exclude text fields from CSV
141
+ output_df = output_df.drop(columns=['llm_text_input', 'llm_text_output', 'llm_new_token'])
142
+
143
+ # Write this single CSV
144
+ csv_path = os.path.join(self.output_dir, "standardized_data_all.csv")
145
+ output_df.to_csv(csv_path, index=False, encoding='utf-8')
146
+ logger.info("Wrote merged standardized DataFrame to %s", csv_path)
147
+
148
+ # ------------------------------------------------------------
149
+ # Compute and save additional performance metrics
150
+ # ------------------------------------------------------------
151
+ workflow_run_time_ci: InferenceMetricsModel = self._compute_workflow_run_time_confidence_intervals()
152
+
153
+ # 2. 90, 95, 99% confidence intervals of mean LLM latency
154
+ llm_latency_ci: InferenceMetricsModel = self._compute_llm_latency_confidence_intervals()
155
+
156
+ # 3. 90, 95, 99% estimates of throughput
157
+ throughput_ci: InferenceMetricsModel = self._compute_throughput_estimates()
158
+
159
+ # Collect all computed metrics
160
+ simple_metrics = SimpleMetricsHolder(workflow_run_time_confidence_intervals=workflow_run_time_ci.model_dump(),
161
+ llm_latency_confidence_intervals=llm_latency_ci.model_dump(),
162
+ throughput_estimate_confidence_interval=throughput_ci.model_dump())
163
+
164
+ common_prefix_results = token_uniqueness_results = workflow_runtimes_results = None
165
+
166
+ if self.profile_config.prompt_caching_prefixes.enable:
167
+ # ------------------------------------------------------------
168
+ # Compute and save common prefixes
169
+ # ------------------------------------------------------------
170
+
171
+ prefixes = get_common_prefixes(all_steps, self.profile_config.prompt_caching_prefixes.min_frequency)
172
+ common_prefix_results = prefixes
173
+
174
+ if self.profile_config.token_uniqueness_forecast:
175
+ # ------------------------------------------------------------
176
+ # Compute and save inter-query token uniqueness
177
+ # ------------------------------------------------------------
178
+
179
+ uniqueness = compute_inter_query_token_uniqueness_by_llm(all_steps)
180
+ token_uniqueness_results = uniqueness
181
+
182
+ if self.profile_config.workflow_runtime_forecast or self.profile_config.base_metrics:
183
+ # ------------------------------------------------------------
184
+ # Compute and save workflow runtime metrics
185
+ # ------------------------------------------------------------
186
+
187
+ workflow_runtimes = compute_workflow_runtime_metrics(all_steps)
188
+ workflow_runtimes_results = workflow_runtimes
189
+
190
+ inference_optimization_results = InferenceOptimizationHolder(confidence_intervals=simple_metrics,
191
+ common_prefixes=common_prefix_results,
192
+ token_uniqueness=token_uniqueness_results,
193
+ workflow_runtimes=workflow_runtimes_results)
194
+
195
+ if self.write_output and inference_optimization_results:
196
+ # Save to JSON
197
+ optimization_results_path = os.path.join(self.output_dir, "inference_optimization.json")
198
+ with open(optimization_results_path, 'w', encoding='utf-8') as f:
199
+ json.dump(inference_optimization_results.model_dump(), f, indent=2)
200
+ logger.info("Wrote inference optimization results to: %s", optimization_results_path)
201
+
202
+ workflow_profiling_reports = ""
203
+ workflow_profiling_metrics = {}
204
+
205
+ if self.profile_config.bottleneck_analysis.enable_simple_stack:
206
+ # ------------------------------------------------------------
207
+ # Profile workflow bottlenecks
208
+ # ------------------------------------------------------------
209
+
210
+ workflow_bottlenecks = profile_workflow_bottlenecks(all_steps)
211
+ workflow_bottlenecks = workflow_bottlenecks.model_dump()
212
+ workflow_profiling_reports += "\n\n\n" + workflow_bottlenecks["summary"]
213
+ workflow_profiling_metrics["simple_stack_analysis"] = workflow_bottlenecks["stats"]
214
+ logger.info("Simple stack analysis complete")
215
+
216
+ if self.profile_config.bottleneck_analysis.enable_nested_stack:
217
+ # ------------------------------------------------------------
218
+ # Profile workflow bottlenecks with nested stack analysis
219
+ # ------------------------------------------------------------
220
+ nested_bottlenecks = multi_example_call_profiling(all_steps, output_dir=str(self.output_dir))
221
+ workflow_profiling_reports += "\n\n\n" + nested_bottlenecks.textual_report
222
+ workflow_profiling_metrics["nested_stack_analysis"] = nested_bottlenecks.model_dump(
223
+ exclude=["textual_report"])
224
+ logger.info("Nested stack analysis complete")
225
+
226
+ if self.profile_config.concurrency_spike_analysis.enable:
227
+ # ------------------------------------------------------------
228
+ # Profile concurrency spikes
229
+ # ------------------------------------------------------------
230
+ concurrency_metrics = concurrency_spike_analysis(
231
+ all_steps, self.profile_config.concurrency_spike_analysis.spike_threshold)
232
+ workflow_profiling_reports += "\n\n\n" + concurrency_metrics.textual_report
233
+ workflow_profiling_metrics["concurrency_spike_analysis"] = concurrency_metrics.model_dump(
234
+ exclude=["textual_report"])
235
+ logger.info("Concurrency spike analysis complete")
236
+
237
+ if self.profile_config.prefix_span_analysis.enable:
238
+ # ------------------------------------------------------------
239
+ # Profile prefix span analysis
240
+ # ------------------------------------------------------------
241
+ prefix_list = []
242
+ if (self.profile_config.prefix_span_analysis.chain_with_common_prefixes
243
+ and "common_prefixes" in inference_optimization_results):
244
+ logger.info("Using common prefixes for prefix span analysis")
245
+ for _, llm_data in inference_optimization_results["common_prefixes"].items():
246
+ for prefix_data in llm_data["prefix_info"]:
247
+ prefix_list.append(prefix_data["prefix"])
248
+
249
+ prefix_span_analysis = prefixspan_subworkflow_with_text(
250
+ all_steps,
251
+ **self.profile_config.prefix_span_analysis.model_dump(exclude=["enable", "chain_with_common_prefixes"]),
252
+ prefix_list=prefix_list)
253
+
254
+ workflow_profiling_reports += "\n\n\n" + prefix_span_analysis.textual_report
255
+ workflow_profiling_metrics["prefix_span_analysis"] = prefix_span_analysis.model_dump(
256
+ exclude=["textual_report"])
257
+ logger.info("Prefix span analysis complete")
258
+
259
+ if self.write_output and workflow_profiling_reports:
260
+ # Save to text file
261
+ profiling_report_path = os.path.join(self.output_dir, "workflow_profiling_report.txt")
262
+ with open(profiling_report_path, 'w', encoding='utf-8') as f:
263
+ f.write(workflow_profiling_reports)
264
+ logger.info("Wrote workflow profiling report to: %s", profiling_report_path)
265
+
266
+ if self.write_output and workflow_profiling_metrics:
267
+ # Save to JSON
268
+ profiling_metrics_path = os.path.join(self.output_dir, "workflow_profiling_metrics.json")
269
+ with open(profiling_metrics_path, 'w', encoding='utf-8') as f:
270
+ json.dump(workflow_profiling_metrics, f, indent=2)
271
+ logger.info("Wrote workflow profiling metrics to: %s", profiling_metrics_path)
272
+
273
+ if self.profile_config.token_usage_forecast:
274
+ # ------------------------------------------------------------
275
+ # Fit forecasting model and save
276
+ # ------------------------------------------------------------
277
+
278
+ logger.info("Fitting model for forecasting.")
279
+ model_trainer = ModelTrainer()
280
+
281
+ try:
282
+ fitted_model = model_trainer.train(all_steps)
283
+ logger.info("Fitted model for forecasting.")
284
+ except Exception as e:
285
+ logger.exception("Fitting model failed. %s", e)
286
+ return ProfilerResults()
287
+
288
+ if self.write_output:
289
+ os.makedirs(self.output_dir, exist_ok=True)
290
+
291
+ import pickle
292
+ with open(os.path.join(self.output_dir, "fitted_model.pkl"), 'wb') as f:
293
+ pickle.dump(fitted_model, f)
294
+
295
+ logger.info("Saved fitted model to disk.")
296
+
297
+ return ProfilerResults(workflow_runtime_metrics=workflow_runtimes_results, llm_latency_ci=llm_latency_ci)
298
+
299
+ # -------------------------------------------------------------------
300
+ # Confidence Intervals / Metrics
301
+ # -------------------------------------------------------------------
302
+ def _compute_workflow_run_time_confidence_intervals(self) -> InferenceMetricsModel:
303
+ """
304
+ Computes 90, 95, 99% confidence intervals for the mean total workflow run time (in seconds).
305
+ The total workflow run time for each request is the difference between the last and first
306
+ event timestamps in usage_stats.
307
+ """
308
+ run_times = []
309
+ for req_data in self.all_steps:
310
+ # Find the min and max event_timestamp
311
+ timestamps = [u.event_timestamp for u in req_data]
312
+ if not timestamps:
313
+ continue
314
+
315
+ start_time = min(timestamps)
316
+ end_time = max(timestamps)
317
+ run_times.append(end_time - start_time)
318
+
319
+ return self._compute_confidence_intervals(run_times, "Workflow Run Time")
320
+
321
+ def _compute_llm_latency_confidence_intervals(self) -> InferenceMetricsModel:
322
+ """
323
+ Computes 90, 95, 99% confidence intervals for the mean LLM latency.
324
+ LLM latency is defined as the difference between an LLM_END event_timestamp and
325
+ the immediately preceding LLM_START event_timestamp, across all usage_stats.
326
+ """
327
+ latencies = []
328
+ for req_data in self.all_steps:
329
+
330
+ usage_stats_sorted = sorted(req_data, key=lambda x: x.event_timestamp)
331
+
332
+ previous_llm_start_time = None
333
+ for u in usage_stats_sorted:
334
+ event_type = u.event_type.value
335
+ ts = u.event_timestamp
336
+ if event_type == "LLM_START":
337
+ previous_llm_start_time = ts
338
+ elif event_type == "LLM_END" and previous_llm_start_time is not None:
339
+ latencies.append(ts - previous_llm_start_time)
340
+ previous_llm_start_time = None
341
+
342
+ return self._compute_confidence_intervals(latencies, "LLM Latency")
343
+
344
+ def _compute_throughput_estimates(self) -> InferenceMetricsModel:
345
+ """
346
+ Computes 90, 95, 99% confidence intervals for throughput, defined as:
347
+
348
+ | throughput = (total number of requests) / (total time window),
349
+
350
+ where total time window is from the earliest usage_stats event across all requests
351
+ to the latest usage_stats event.
352
+ Note: This is a simple approximate measure of overall throughput for the entire run.
353
+ """
354
+ # Gather min timestamp and max timestamp across ALL requests
355
+ all_timestamps = []
356
+ for req_data in self.all_steps:
357
+ for u in req_data:
358
+ all_timestamps.append(u.event_timestamp)
359
+
360
+ if not all_timestamps:
361
+ return InferenceMetricsModel()
362
+
363
+ min_ts = min(all_timestamps)
364
+ max_ts = max(all_timestamps)
365
+ total_time = max_ts - min_ts
366
+ if total_time <= 0:
367
+ # Can't compute a meaningful throughput if time <= 0
368
+ return InferenceMetricsModel()
369
+
370
+ total_requests = len(self.all_requests_data)
371
+ # Single estimate of throughput
372
+ throughput_value = total_requests / total_time
373
+
374
+ # For confidence intervals of throughput, we do a simplistic assumption:
375
+ # We treat each request's contribution as 1 occurrence, and approximate
376
+ # the distribution as if these arrivals were uniform. This is quite simplified.
377
+ # We can compute a standard error: SE = sqrt(throughput_value / total_time)
378
+ # However, a more accurate approach might require a different method (e.g., Poisson).
379
+ # We'll do a naive normal approximation here.
380
+
381
+ # We'll guess that the standard deviation of #requests is sqrt(N), so stdev_n ~ sqrt(N).
382
+ # stdev_time is quite small though. We'll do a naive approach:
383
+ # We'll treat the throughput as a sample mean with n=total_requests.
384
+ # Then standard error is (throughput_value / sqrt(n)).
385
+ # This is purely heuristic.
386
+ n = total_requests
387
+ if n <= 1:
388
+ return InferenceMetricsModel()
389
+
390
+ # A rough standard error for throughput:
391
+ standard_error = throughput_value / math.sqrt(n)
392
+
393
+ # Build confidence intervals using z-scores for 90%, 95%, 99%
394
+ intervals = {'n': total_requests, 'mean': throughput_value}
395
+ for confidence, zvalue in \
396
+ [("ninetieth_interval", 1.645), ("ninety_fifth_interval", 1.96), ("ninety_ninth_interval", 2.576)]:
397
+ ci_lower = throughput_value - zvalue * standard_error
398
+ ci_upper = throughput_value + zvalue * standard_error
399
+ intervals[confidence] = (max(ci_lower, 0.0), ci_upper)
400
+
401
+ return InferenceMetricsModel(**intervals)
402
+
403
+ def _compute_confidence_intervals(self, data: list[float], metric_name: str) -> InferenceMetricsModel:
404
+ """
405
+ Helper to compute 90, 95, 99 % confidence intervals **and** the empirical
406
+ 90th/95th/99th percentiles (p90/p95/p99) for the mean of a dataset.
407
+ Uses a z-score from the normal approximation for large samples.
408
+
409
+ Returns a dict like::
410
+
411
+ {
412
+ 'ninetieth_interval': (lower, upper),
413
+ 'ninety_fifth_interval': (lower, upper),
414
+ 'ninety_ninth_interval': (lower, upper),
415
+ }
416
+ """
417
+ if not data:
418
+ logger.warning("No data points for %s, cannot compute intervals.", metric_name)
419
+ return InferenceMetricsModel()
420
+
421
+ n = len(data)
422
+ mean_val = statistics.mean(data)
423
+ if n <= 1:
424
+ return InferenceMetricsModel(
425
+ n=n,
426
+ mean=mean_val,
427
+ ninetieth_interval=(mean_val, mean_val),
428
+ ninety_fifth_interval=(mean_val, mean_val),
429
+ ninety_ninth_interval=(mean_val, mean_val),
430
+ p90=mean_val,
431
+ p95=mean_val,
432
+ p99=mean_val,
433
+ )
434
+
435
+ stdev_val = statistics.pstdev(data) # population stdev or use stdev for sample
436
+ # standard error
437
+ se = stdev_val / math.sqrt(n)
438
+
439
+ intervals = {}
440
+ for confidence, zvalue in \
441
+ [("ninetieth_interval", 1.645), ("ninety_fifth_interval", 1.96), ("ninety_ninth_interval", 2.576)]:
442
+ margin = zvalue * se
443
+ lower = mean_val - margin
444
+ upper = mean_val + margin
445
+ intervals[confidence] = (lower, upper)
446
+
447
+ # Optionally, store more info
448
+ intervals["n"] = n
449
+ intervals["mean"] = mean_val
450
+
451
+ # ------------------------------------------------------------------
452
+ # Percentiles
453
+ # ------------------------------------------------------------------
454
+ sorted_data = sorted(data)
455
+
456
+ def _percentile(arr: list[float], pct: float) -> float:
457
+ """
458
+ Linear interpolation between closest ranks.
459
+ pct is given from 0‑100 (e.g. 90 for p90).
460
+ """
461
+ if not arr:
462
+ return 0.0
463
+ k = (len(arr) - 1) * (pct / 100.0)
464
+ f = math.floor(k)
465
+ c = math.ceil(k)
466
+ if f == c:
467
+ return arr[int(k)]
468
+ return arr[f] + (arr[c] - arr[f]) * (k - f)
469
+
470
+ p90_val = _percentile(sorted_data, 90)
471
+ p95_val = _percentile(sorted_data, 95)
472
+ p99_val = _percentile(sorted_data, 99)
473
+
474
+ intervals["p90"] = p90_val
475
+ intervals["p95"] = p95_val
476
+ intervals["p99"] = p99_val
477
+
478
+ return InferenceMetricsModel(**intervals)
nat/profiler/utils.py ADDED
@@ -0,0 +1,186 @@
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 inspect
17
+ import logging
18
+ import re
19
+ from collections.abc import Callable
20
+ from typing import Any
21
+
22
+ import pandas as pd
23
+
24
+ from nat.builder.framework_enum import LLMFrameworkEnum
25
+ from nat.cli.type_registry import RegisteredFunctionGroupInfo
26
+ from nat.cli.type_registry import RegisteredFunctionInfo
27
+ from nat.data_models.intermediate_step import IntermediateStep
28
+ from nat.profiler.data_frame_row import DataFrameRow
29
+
30
+ # A simple set of regex patterns to scan for direct references to LLMFrameworkEnum
31
+ _FRAMEWORK_REGEX_MAP = {t: fr'\b{t._name_}\b' for t in LLMFrameworkEnum}
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ def detect_llm_frameworks_in_build_fn(
37
+ registration: RegisteredFunctionInfo | RegisteredFunctionGroupInfo) -> list[LLMFrameworkEnum]:
38
+ """
39
+ Analyze a function's source (the build_fn) to see which LLM frameworks it uses. Also recurses
40
+ into any additional Python functions that the build_fn calls while passing `builder`, so that
41
+ references to LLMFrameworkEnum in those helper calls are also detected.
42
+
43
+ 1. If `registration.framework_wrappers` is non-empty, we return that first.
44
+ (We do convert them to LLMFrameworkEnum if possible.)
45
+ 2. Otherwise, we attempt to:
46
+
47
+ - Get the build_fn's source via `inspect.getsource(...)`
48
+ - Parse it for references to LLMFrameworkEnum
49
+ - Find any function calls that include the word "builder" in the arguments
50
+
51
+ - Recursively parse those functions' source code for frameworks
52
+
53
+ 3. If we cannot parse the source at all (e.g. OSError), we return a list of all frameworks.
54
+ """
55
+ # ----------------------------------------------------------------
56
+ # 1) If frameworks were explicitly declared in registration.framework_wrappers, use them:
57
+ if registration.framework_wrappers:
58
+ results: list[LLMFrameworkEnum] = []
59
+ for fw_str in registration.framework_wrappers:
60
+ try:
61
+ results.append(LLMFrameworkEnum(fw_str))
62
+ except ValueError:
63
+ # If it's not recognized, ignore or log
64
+ logger.warning("Unrecognized framework %s in registration.framework_wrappers", fw_str)
65
+
66
+ return list(set(results)) # unique
67
+ # ----------------------------------------------------------------
68
+
69
+ # Because we want to recursively parse code, we'll keep track of visited function objects
70
+ visited_fns: set[Callable[..., Any]] = set()
71
+ # We also need a place to store discovered frameworks
72
+ discovered: set[LLMFrameworkEnum] = set()
73
+
74
+ def _parse_source_for_frameworks(src: str) -> None:
75
+ """Check lines for any direct references to LLMFrameworkEnum.* or placeholders in the map."""
76
+ for fw_enum_member, pattern in _FRAMEWORK_REGEX_MAP.items():
77
+ if re.search(pattern, src):
78
+ discovered.add(fw_enum_member)
79
+
80
+ def _find_builder_func_calls(src: str) -> list[str]:
81
+ """
82
+ Look for calls of the form: some_func(..., builder, ...)
83
+ or some_func(..., builder=..., ...)
84
+
85
+ This returns the name of each function we found being called, e.g. 'some_func'.
86
+ It's a naive best-effort approach
87
+ and group(1) is the function name.
88
+ """
89
+ # E.g. foo(builder) or foo( param=..., builder=builder )
90
+ pattern = r'(\w+)\s*\([^)]*\bbuilder\b[^)]*\)'
91
+ return re.findall(pattern, src)
92
+
93
+ def _recurse_parse(fn: Callable[..., Any], visited: set[Callable[..., Any]]) -> None:
94
+ """Recursively parse the source code of `fn`, add discovered frameworks,
95
+ and parse any new functions that get called with 'builder'."""
96
+ if fn in visited:
97
+ return
98
+ visited.add(fn)
99
+
100
+ try:
101
+ src = inspect.getsource(fn)
102
+ except OSError:
103
+ # If we can't parse source, we add all frameworks and bail
104
+ discovered.update([k for k, v in _FRAMEWORK_REGEX_MAP.items()])
105
+ return
106
+
107
+ # parse direct references
108
+ _parse_source_for_frameworks(src)
109
+
110
+ # parse any function calls that pass in "builder"
111
+ child_func_names = _find_builder_func_calls(src)
112
+ if not child_func_names:
113
+ return
114
+
115
+ # We'll try to find these child functions in the same module as `fn`
116
+ mod = inspect.getmodule(fn)
117
+ if not mod:
118
+ return
119
+ # We'll see if the child function is a top-level in that module
120
+ for child_name in child_func_names:
121
+ # get the function object if it exists in the module
122
+ child_obj = getattr(mod, child_name, None)
123
+ if callable(child_obj):
124
+ _recurse_parse(child_obj, visited)
125
+
126
+ # ----------------------------------------------------------------
127
+ # 2) Actually do the BFS/DFS parse on `registration.build_fn`
128
+ main_fn = registration.build_fn
129
+
130
+ try:
131
+ _recurse_parse(main_fn, visited_fns)
132
+ except Exception:
133
+ # If an unexpected error occurs, fallback to "all frameworks"
134
+ discovered.update([k for k, v in _FRAMEWORK_REGEX_MAP.items()])
135
+ # ----------------------------------------------------------------
136
+ if len(discovered) > 0:
137
+ logger.warning(
138
+ "Discovered frameworks: %s in function %s by inspecting "
139
+ "source. It is recommended and more reliable to instead add the used LLMFrameworkEnum "
140
+ "types in the framework_wrappers argument when calling @register_function.",
141
+ discovered,
142
+ main_fn.__name__)
143
+
144
+ return list(discovered)
145
+
146
+
147
+ # -------------------------------------------------------------------
148
+ # Create a single standardized DataFrame for all usage stats
149
+ # -------------------------------------------------------------------
150
+ def create_standardized_dataframe(requests_data: list[list[IntermediateStep]]) -> pd.DataFrame:
151
+ """
152
+ Merge usage stats for *all* requests into one DataFrame, each row representing a usage_stats entry.
153
+ - Include a column 'example_number' to mark which request it originated from.
154
+ """
155
+ all_rows = []
156
+ try:
157
+ for i, steps in enumerate(requests_data):
158
+ for step in steps:
159
+ # Create a DataFrameRow
160
+ all_rows.append(
161
+ DataFrameRow(event_timestamp=step.event_timestamp,
162
+ example_number=i,
163
+ prompt_tokens=step.token_usage.prompt_tokens,
164
+ completion_tokens=step.token_usage.completion_tokens,
165
+ total_tokens=step.token_usage.total_tokens,
166
+ llm_text_input=step.llm_text_input,
167
+ llm_text_output=step.llm_text_output,
168
+ llm_new_token=step.llm_text_chunk,
169
+ llm_name=step.llm_name,
170
+ tool_name=step.tool_name,
171
+ function_name=step.function_name,
172
+ function_id=step.function_id,
173
+ parent_function_name=step.parent_function_name,
174
+ parent_function_id=step.parent_function_id,
175
+ UUID=step.payload.UUID,
176
+ framework=step.framework,
177
+ event_type=step.event_type).model_dump(), )
178
+
179
+ except Exception as e:
180
+ logger.exception("Error creating standardized DataFrame: %s", e)
181
+ return pd.DataFrame()
182
+
183
+ if not all_rows:
184
+ return pd.DataFrame()
185
+
186
+ return pd.DataFrame.from_records(all_rows)
File without changes
File without changes