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,23 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from pydantic import BaseModel
17
+ from pydantic import Field
18
+
19
+
20
+ class CollectorConfigMixin(BaseModel):
21
+ """Mixin for telemetry exporters that require a project name and endpoint when exporting to a collector service."""
22
+ project: str = Field(description="The project name to associate the telemetry traces.")
23
+ endpoint: str = Field(description="The endpoint of the telemetry collector service.")
@@ -0,0 +1,288 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import asyncio
17
+ import logging
18
+ from datetime import datetime
19
+ from pathlib import Path
20
+ from typing import Any
21
+
22
+ from nat.observability.mixin.file_mode import FileMode
23
+ from nat.observability.mixin.resource_conflict_mixin import ResourceConflictMixin
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class FileExportMixin(ResourceConflictMixin):
29
+ """Mixin for file-based exporters.
30
+
31
+ This mixin provides file I/O functionality for exporters that need to write
32
+ serialized data to local files, with support for file overwriting and rolling logs.
33
+
34
+ Automatically detects and prevents file path conflicts between multiple instances
35
+ by raising ResourceConflictError during initialization.
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ *args,
41
+ output_path,
42
+ project,
43
+ mode: FileMode = FileMode.APPEND,
44
+ enable_rolling: bool = False,
45
+ max_file_size: int = 10 * 1024 * 1024, # 10MB default
46
+ max_files: int = 5,
47
+ cleanup_on_init: bool = False,
48
+ **kwargs):
49
+ """Initialize the file exporter with the specified output_path and project.
50
+
51
+ Args:
52
+ output_path (str): The path to the output file or directory (if rolling enabled).
53
+ project (str): The project name for metadata.
54
+ mode (str): Either "append" or "overwrite". Defaults to "append".
55
+ enable_rolling (bool): Enable rolling log files. Defaults to False.
56
+ max_file_size (int): Maximum file size in bytes before rolling. Defaults to 10MB.
57
+ max_files (int): Maximum number of rolled files to keep. Defaults to 5.
58
+ cleanup_on_init (bool): Clean up old files during initialization. Defaults to False.
59
+
60
+ Raises:
61
+ ResourceConflictError: If another FileExportMixin instance is already using
62
+ the same file path or would create conflicting files.
63
+ """
64
+ self._filepath = Path(output_path)
65
+ self._project = project
66
+ self._mode = mode
67
+ self._enable_rolling = enable_rolling
68
+ self._max_file_size = max_file_size
69
+ self._max_files = max_files
70
+ self._cleanup_on_init = cleanup_on_init
71
+ self._lock = asyncio.Lock()
72
+ self._first_write = True
73
+
74
+ # Initialize file paths first, then check for conflicts via ResourceConflictMixin
75
+ self._setup_file_paths()
76
+
77
+ # This calls _register_resources() which will check for conflicts
78
+ super().__init__(*args, **kwargs)
79
+
80
+ def _setup_file_paths(self):
81
+ """Setup file paths using the project name."""
82
+
83
+ if self._enable_rolling:
84
+ # If rolling is enabled, output_path should be a directory
85
+ self._base_dir = self._filepath if self._filepath.is_dir(
86
+ ) or not self._filepath.suffix else self._filepath.parent
87
+ self._base_filename = self._filepath.stem if self._filepath.suffix else f"{self._project}_export"
88
+ self._file_extension = self._filepath.suffix or ".log"
89
+ self._base_dir.mkdir(parents=True, exist_ok=True)
90
+ self._current_file_path = self._base_dir / f"{self._base_filename}{self._file_extension}"
91
+
92
+ # Perform initial cleanup if requested
93
+ if self._cleanup_on_init:
94
+ self._cleanup_old_files_sync()
95
+ else:
96
+ # Traditional single file mode
97
+ self._filepath.parent.mkdir(parents=True, exist_ok=True)
98
+ self._current_file_path = self._filepath
99
+
100
+ # For single file mode with overwrite, remove existing file
101
+ if self._mode == FileMode.OVERWRITE and self._cleanup_on_init and self._current_file_path.exists():
102
+ try:
103
+ self._current_file_path.unlink()
104
+ logger.info("Cleaned up existing file: %s", self._current_file_path)
105
+ except OSError as e:
106
+ logger.exception("Error removing existing file %s: %s", self._current_file_path, e)
107
+
108
+ def _get_resource_identifiers(self) -> dict[str, Any]:
109
+ """Return the file resources this instance will use.
110
+
111
+ Returns:
112
+ dict with file_path and optionally cleanup_pattern for rolling files.
113
+ """
114
+ identifiers = {"file_path": str(self._current_file_path.resolve())}
115
+
116
+ # Add cleanup pattern for rolling files
117
+ if self._enable_rolling:
118
+ cleanup_pattern = f"{self._base_filename}_*{self._file_extension}"
119
+ pattern_key = f"{self._base_dir.resolve()}:{cleanup_pattern}"
120
+ identifiers["cleanup_pattern"] = pattern_key
121
+
122
+ return identifiers
123
+
124
+ def _format_conflict_error(self, resource_type: str, identifier: Any, existing_instance: Any) -> str:
125
+ """Format user-friendly error messages for file conflicts."""
126
+ match resource_type:
127
+ case "file_path":
128
+ return (f"File path conflict detected: '{self._current_file_path}' is already in use by another "
129
+ f"FileExportMixin instance (project: '{existing_instance._project}'). "
130
+ f"Use different project names or output paths to avoid conflicts.")
131
+ case "cleanup_pattern":
132
+ return (f"Rolling file cleanup conflict detected: Both instances would use pattern "
133
+ f"'{self._base_filename}_*{self._file_extension}' in directory '{self._base_dir}', "
134
+ f"causing one to delete the other's files. "
135
+ f"Current instance (project: '{self._project}'), "
136
+ f"existing instance (project: '{existing_instance._project}'). "
137
+ f"Use different project names or directories to avoid conflicts.")
138
+ case _:
139
+ return f"Unknown file resource conflict: {resource_type} = {identifier}"
140
+
141
+ def _cleanup_old_files_sync(self) -> None:
142
+ """Synchronous version of cleanup for use during initialization."""
143
+ try:
144
+ # Find all rolled files matching our pattern
145
+ pattern = f"{self._base_filename}_*{self._file_extension}"
146
+ rolled_files = list(self._base_dir.glob(pattern))
147
+
148
+ # Sort by modification time (newest first)
149
+ rolled_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
150
+
151
+ # Remove files beyond max_files limit
152
+ for old_file in rolled_files[self._max_files:]:
153
+ try:
154
+ old_file.unlink()
155
+ logger.info("Cleaned up old log file during init: %s", old_file)
156
+ except OSError as e:
157
+ logger.exception("Error removing old file %s: %s", old_file, e)
158
+
159
+ except Exception as e:
160
+ logger.exception("Error during initialization cleanup: %s", e)
161
+
162
+ async def _should_roll_file(self) -> bool:
163
+ """Check if the current file should be rolled based on size."""
164
+ if not self._enable_rolling:
165
+ return False
166
+
167
+ try:
168
+ if self._current_file_path.exists():
169
+ stat = self._current_file_path.stat()
170
+ return stat.st_size >= self._max_file_size
171
+ except OSError:
172
+ pass
173
+ return False
174
+
175
+ async def _roll_file(self) -> None:
176
+ """Roll the current file by renaming it with a timestamp and cleaning up old files."""
177
+ if not self._current_file_path.exists():
178
+ return
179
+
180
+ # Generate timestamped filename with microsecond precision
181
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
182
+ rolled_filename = f"{self._base_filename}_{timestamp}{self._file_extension}"
183
+ rolled_path = self._base_dir / rolled_filename
184
+
185
+ try:
186
+ # Rename current file
187
+ self._current_file_path.rename(rolled_path)
188
+ logger.info("Rolled log file to: %s", rolled_path)
189
+
190
+ # Clean up old files
191
+ await self._cleanup_old_files()
192
+
193
+ except OSError as e:
194
+ logger.exception("Error rolling file %s: %s", self._current_file_path, e)
195
+
196
+ async def _cleanup_old_files(self) -> None:
197
+ """Remove old rolled files beyond the maximum count."""
198
+ try:
199
+ # Find all rolled files matching our pattern
200
+ pattern = f"{self._base_filename}_*{self._file_extension}"
201
+ rolled_files = list(self._base_dir.glob(pattern))
202
+
203
+ # Sort by modification time (newest first)
204
+ rolled_files.sort(key=lambda f: f.stat().st_mtime, reverse=True)
205
+
206
+ # Remove files beyond max_files limit
207
+ for old_file in rolled_files[self._max_files:]:
208
+ try:
209
+ old_file.unlink()
210
+ logger.info("Cleaned up old log file: %s", old_file)
211
+ except OSError as e:
212
+ logger.exception("Error removing old file %s: %s", old_file, e)
213
+
214
+ except Exception as e:
215
+ logger.exception("Error during cleanup: %s", e)
216
+
217
+ async def export_processed(self, item: str | list[str]) -> None:
218
+ """Export a processed string or list of strings.
219
+
220
+ Args:
221
+ item (str | list[str]): The string or list of strings to export.
222
+ """
223
+ try:
224
+ # Lazy import to avoid slow startup times
225
+ import aiofiles
226
+
227
+ async with self._lock:
228
+ # Check if we need to roll the file
229
+ if await self._should_roll_file():
230
+ await self._roll_file()
231
+
232
+ # Determine file mode
233
+ if self._first_write and self._mode == FileMode.OVERWRITE:
234
+ file_mode = "w"
235
+ self._first_write = False
236
+ else:
237
+ file_mode = "a"
238
+
239
+ async with aiofiles.open(self._current_file_path, mode=file_mode) as f:
240
+ if isinstance(item, list):
241
+ # Handle list of strings
242
+ for single_item in item:
243
+ await f.write(single_item)
244
+ await f.write("\n")
245
+ else:
246
+ # Handle single string
247
+ await f.write(item)
248
+ await f.write("\n")
249
+
250
+ except Exception as e:
251
+ logger.exception("Error exporting event: %s", e)
252
+
253
+ def get_current_file_path(self) -> Path:
254
+ """Get the current file path being written to.
255
+
256
+ Returns:
257
+ Path: The current file path being written to.
258
+ """
259
+ return self._current_file_path
260
+
261
+ def get_file_info(self) -> dict:
262
+ """Get information about the current file and rolling configuration.
263
+
264
+ Returns:
265
+ dict: A dictionary containing the current file path, mode, rolling enabled, cleanup on init,
266
+ effective project name, and additional rolling configuration if enabled.
267
+ """
268
+ info = {
269
+ "current_file": str(self._current_file_path),
270
+ "mode": self._mode,
271
+ "rolling_enabled": self._enable_rolling,
272
+ "cleanup_on_init": self._cleanup_on_init,
273
+ "project": self._project,
274
+ "effective_project": self._project,
275
+ }
276
+
277
+ if self._enable_rolling:
278
+ info.update({
279
+ "max_file_size": self._max_file_size,
280
+ "max_files": self._max_files,
281
+ "base_directory": str(self._base_dir),
282
+ })
283
+
284
+ # Add current file size if it exists
285
+ if self._current_file_path.exists():
286
+ info["current_file_size"] = self._current_file_path.stat().st_size
287
+
288
+ return info
@@ -0,0 +1,23 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from enum import StrEnum
17
+
18
+
19
+ class FileMode(StrEnum):
20
+ """File write modes for FileExportMixin."""
21
+
22
+ APPEND = "append"
23
+ OVERWRITE = "overwrite"
@@ -0,0 +1,42 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from pydantic import BaseModel
17
+ from pydantic import Field
18
+
19
+
20
+ class RedactionConfigMixin(BaseModel):
21
+ """Mixin for basic redaction configuration.
22
+
23
+ Provides core redaction functionality that can be used standalone
24
+ or inherited by specialized redaction mixins.
25
+ """
26
+ redaction_enabled: bool = Field(default=False, description="Whether to enable redaction processing.")
27
+ redaction_value: str = Field(default="[REDACTED]", description="Value to replace redacted attributes with.")
28
+ redaction_attributes: list[str] = Field(default_factory=lambda: ["input.value", "output.value", "nat.metadata"],
29
+ description="Attributes to redact when redaction is triggered.")
30
+ force_redaction: bool = Field(default=False, description="Always redact regardless of other conditions.")
31
+ redaction_tag: str | None = Field(default=None, description="Tag to add to spans when redaction is triggered.")
32
+
33
+
34
+ class HeaderRedactionConfigMixin(RedactionConfigMixin):
35
+ """Mixin for header-based redaction configuration.
36
+
37
+ Inherits core redaction fields (redaction_enabled, redaction_attributes, force_redaction)
38
+ and adds header-specific configuration for authentication-based redaction decisions.
39
+
40
+ Note: The callback function must be provided directly to the processor at runtime.
41
+ """
42
+ redaction_headers: list[str] = Field(default_factory=list, description="Headers to check for redaction decisions.")
@@ -0,0 +1,134 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+ import weakref
18
+ from abc import ABC
19
+ from abc import abstractmethod
20
+ from typing import Any
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class ResourceConflictError(ValueError):
26
+ """Raised when multiple exporter instances would conflict over the same resource."""
27
+ pass
28
+
29
+
30
+ class ResourceConflictMixin(ABC):
31
+ """Abstract mixin for detecting resource conflicts between exporter instances.
32
+
33
+ This mixin provides a framework for exporters to detect when multiple instances
34
+ would conflict over the same resources (files, database tables, API endpoints, etc.).
35
+ Each concrete implementation defines what constitutes a resource conflict for that
36
+ exporter type.
37
+
38
+ The mixin maintains class-level registries using weakrefs for automatic cleanup
39
+ when instances are garbage collected.
40
+ """
41
+
42
+ # Each subclass gets its own registry - prevents cross-contamination
43
+ _registries: dict[type, dict[str, weakref.ref]] = {}
44
+
45
+ def __init__(self, *args, **kwargs):
46
+ super().__init__(*args, **kwargs)
47
+ # Register this instance's resources and check for conflicts
48
+ self._register_resources()
49
+
50
+ @abstractmethod
51
+ def _get_resource_identifiers(self) -> dict[str, Any]:
52
+ """Return dict of resource_type -> identifier that this instance will use.
53
+
54
+ Examples:
55
+ Files: {"file_path": "/logs/app.log", "cleanup_pattern": "app_*.log"}
56
+ Phoenix: {"project_endpoint": "my_project@http://localhost:6006"}
57
+ Database: {"table_name": "events", "connection": "postgresql://..."}
58
+
59
+ Returns:
60
+ dict[str, Any]: Dict mapping resource type names to unique identifiers for those resources.
61
+ """
62
+ pass
63
+
64
+ @abstractmethod
65
+ def _format_conflict_error(self, resource_type: str, identifier: Any, existing_instance: Any) -> str:
66
+ """Format a user-friendly error message for a resource conflict.
67
+
68
+ Args:
69
+ resource_type (str): The type of resource that conflicts (e.g., "file_path", "project_endpoint")
70
+ identifier (Any): The identifier for this resource
71
+ existing_instance (Any): The existing instance that conflicts with this one
72
+
73
+ Returns:
74
+ A clear error message explaining the conflict and how to resolve it.
75
+ """
76
+ pass
77
+
78
+ def _register_resources(self):
79
+ """Register this instance's resources and check for conflicts.
80
+
81
+ Raises:
82
+ ResourceConflictError: If any resource conflicts with an existing instance.
83
+ """
84
+ # Get our class-specific registry
85
+ cls = type(self)
86
+ if cls not in self._registries:
87
+ self._registries[cls] = {}
88
+ registry = self._registries[cls]
89
+
90
+ # Clean up dead references first
91
+ self._cleanup_dead_references(registry)
92
+
93
+ # Check each resource for conflicts
94
+ resources = self._get_resource_identifiers()
95
+ for resource_type, identifier in resources.items():
96
+ resource_key = f"{resource_type}:{identifier}"
97
+
98
+ # Check for existing instance using this resource
99
+ if resource_key in registry:
100
+ existing_ref = registry[resource_key]
101
+ existing_instance = existing_ref()
102
+ if existing_instance is not None:
103
+ error_msg = self._format_conflict_error(resource_type, identifier, existing_instance)
104
+ raise ResourceConflictError(error_msg)
105
+
106
+ # Register this instance for this resource
107
+ registry[resource_key] = weakref.ref(self, lambda ref, key=resource_key: registry.pop(key, None))
108
+
109
+ logger.debug("Registered %d resources for %s", len(resources), self.__class__.__name__)
110
+
111
+ def _cleanup_dead_references(self, registry: dict[str, weakref.ref]):
112
+ """Remove dead weakref entries from the registry.
113
+
114
+ Args:
115
+ registry (dict[str, weakref.ref]): The registry to clean up.
116
+ """
117
+ dead_keys = [key for key, ref in registry.items() if ref() is None]
118
+ for key in dead_keys:
119
+ registry.pop(key, None)
120
+
121
+ @classmethod
122
+ def get_active_resource_count(cls) -> int:
123
+ """Get the number of active resources registered for this class.
124
+
125
+ Returns:
126
+ int: Number of active resource registrations.
127
+ """
128
+ if cls not in cls._registries:
129
+ return 0
130
+
131
+ registry = cls._registries[cls]
132
+ # Clean up and count live references
133
+ live_count = sum(1 for ref in registry.values() if ref() is not None)
134
+ return live_count
@@ -0,0 +1,61 @@
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
+ from typing import Any
18
+
19
+ from pydantic import BaseModel
20
+ from pydantic import TypeAdapter
21
+
22
+
23
+ class SerializeMixin:
24
+
25
+ def _process_streaming_output(self, input_value: Any) -> Any:
26
+ """
27
+ Serialize a list of values to a JSON string.
28
+ """
29
+ if isinstance(input_value, BaseModel):
30
+ return json.loads(TypeAdapter(type(input_value)).dump_json(input_value).decode('utf-8'))
31
+ if isinstance(input_value, dict):
32
+ return input_value
33
+ return input_value
34
+
35
+ def _serialize_payload(self, input_value: Any) -> tuple[str, bool]:
36
+ """
37
+ Serialize the input value to a string. Returns a tuple with the serialized value and a boolean indicating if the
38
+ serialization is JSON or a string.
39
+
40
+ Args:
41
+ input_value (Any): The input value to serialize.
42
+
43
+ Returns:
44
+ tuple[str, bool]: A tuple with the serialized value and a boolean indicating if the serialization is
45
+ JSON or a string.
46
+ """
47
+ try:
48
+ if isinstance(input_value, BaseModel):
49
+ return TypeAdapter(type(input_value)).dump_json(input_value).decode('utf-8'), True
50
+ if isinstance(input_value, dict):
51
+ return json.dumps(input_value), True
52
+ if isinstance(input_value, list):
53
+ serialized_list = []
54
+ for value in input_value:
55
+ serialized_value = self._process_streaming_output(value)
56
+ serialized_list.append(serialized_value)
57
+ return json.dumps(serialized_list), True
58
+ return str(input_value), False
59
+ except Exception:
60
+ # Fallback to string representation if we can't serialize using pydantic
61
+ return str(input_value), False
@@ -0,0 +1,62 @@
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 sys
17
+ from collections.abc import Mapping
18
+ from enum import Enum
19
+ from typing import Generic
20
+ from typing import TypeVar
21
+
22
+ from pydantic import BaseModel
23
+ from pydantic import Field
24
+
25
+ if sys.version_info >= (3, 12):
26
+ from typing import TypedDict
27
+ else:
28
+ from typing_extensions import TypedDict
29
+
30
+ TagMappingT = TypeVar("TagMappingT", bound=Mapping)
31
+
32
+
33
+ class BaseTaggingConfigMixin(BaseModel, Generic[TagMappingT]):
34
+ """Base mixin for tagging spans."""
35
+ tags: TagMappingT | None = Field(default=None, description="Tags to add to the span.")
36
+
37
+
38
+ class PrivacyLevel(str, Enum):
39
+ """Privacy level for the traces."""
40
+ NONE = "none"
41
+ LOW = "low"
42
+ MEDIUM = "medium"
43
+ HIGH = "high"
44
+
45
+
46
+ PrivacyTagSchema = TypedDict(
47
+ "PrivacyTagSchema",
48
+ {
49
+ "privacy.level": PrivacyLevel,
50
+ },
51
+ total=True,
52
+ )
53
+
54
+
55
+ class PrivacyTaggingConfigMixin(BaseTaggingConfigMixin[PrivacyTagSchema]):
56
+ """Mixin for privacy level tagging on spans."""
57
+ pass
58
+
59
+
60
+ class CustomTaggingConfigMixin(BaseTaggingConfigMixin[dict[str, str]]):
61
+ """Mixin for string key-value tagging on spans."""
62
+ pass