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.
- aiq/__init__.py +66 -0
- nat/agent/__init__.py +0 -0
- nat/agent/base.py +265 -0
- nat/agent/dual_node.py +72 -0
- nat/agent/prompt_optimizer/__init__.py +0 -0
- nat/agent/prompt_optimizer/prompt.py +68 -0
- nat/agent/prompt_optimizer/register.py +149 -0
- nat/agent/react_agent/__init__.py +0 -0
- nat/agent/react_agent/agent.py +394 -0
- nat/agent/react_agent/output_parser.py +104 -0
- nat/agent/react_agent/prompt.py +44 -0
- nat/agent/react_agent/register.py +168 -0
- nat/agent/reasoning_agent/__init__.py +0 -0
- nat/agent/reasoning_agent/reasoning_agent.py +227 -0
- nat/agent/register.py +23 -0
- nat/agent/rewoo_agent/__init__.py +0 -0
- nat/agent/rewoo_agent/agent.py +593 -0
- nat/agent/rewoo_agent/prompt.py +107 -0
- nat/agent/rewoo_agent/register.py +175 -0
- nat/agent/tool_calling_agent/__init__.py +0 -0
- nat/agent/tool_calling_agent/agent.py +246 -0
- nat/agent/tool_calling_agent/register.py +129 -0
- nat/authentication/__init__.py +14 -0
- nat/authentication/api_key/__init__.py +14 -0
- nat/authentication/api_key/api_key_auth_provider.py +96 -0
- nat/authentication/api_key/api_key_auth_provider_config.py +124 -0
- nat/authentication/api_key/register.py +26 -0
- nat/authentication/credential_validator/__init__.py +14 -0
- nat/authentication/credential_validator/bearer_token_validator.py +557 -0
- nat/authentication/exceptions/__init__.py +14 -0
- nat/authentication/exceptions/api_key_exceptions.py +38 -0
- nat/authentication/http_basic_auth/__init__.py +0 -0
- nat/authentication/http_basic_auth/http_basic_auth_provider.py +81 -0
- nat/authentication/http_basic_auth/register.py +30 -0
- nat/authentication/interfaces.py +96 -0
- nat/authentication/oauth2/__init__.py +14 -0
- nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +140 -0
- nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +39 -0
- nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
- nat/authentication/oauth2/register.py +25 -0
- nat/authentication/register.py +20 -0
- nat/builder/__init__.py +0 -0
- nat/builder/builder.py +317 -0
- nat/builder/component_utils.py +320 -0
- nat/builder/context.py +321 -0
- nat/builder/embedder.py +24 -0
- nat/builder/eval_builder.py +166 -0
- nat/builder/evaluator.py +29 -0
- nat/builder/framework_enum.py +25 -0
- nat/builder/front_end.py +73 -0
- nat/builder/function.py +714 -0
- nat/builder/function_base.py +380 -0
- nat/builder/function_info.py +625 -0
- nat/builder/intermediate_step_manager.py +206 -0
- nat/builder/llm.py +25 -0
- nat/builder/retriever.py +25 -0
- nat/builder/user_interaction_manager.py +78 -0
- nat/builder/workflow.py +160 -0
- nat/builder/workflow_builder.py +1365 -0
- nat/cli/__init__.py +14 -0
- nat/cli/cli_utils/__init__.py +0 -0
- nat/cli/cli_utils/config_override.py +231 -0
- nat/cli/cli_utils/validation.py +37 -0
- nat/cli/commands/__init__.py +0 -0
- nat/cli/commands/configure/__init__.py +0 -0
- nat/cli/commands/configure/channel/__init__.py +0 -0
- nat/cli/commands/configure/channel/add.py +28 -0
- nat/cli/commands/configure/channel/channel.py +34 -0
- nat/cli/commands/configure/channel/remove.py +30 -0
- nat/cli/commands/configure/channel/update.py +30 -0
- nat/cli/commands/configure/configure.py +33 -0
- nat/cli/commands/evaluate.py +139 -0
- nat/cli/commands/info/__init__.py +14 -0
- nat/cli/commands/info/info.py +47 -0
- nat/cli/commands/info/list_channels.py +32 -0
- nat/cli/commands/info/list_components.py +128 -0
- nat/cli/commands/mcp/__init__.py +14 -0
- nat/cli/commands/mcp/mcp.py +986 -0
- nat/cli/commands/object_store/__init__.py +14 -0
- nat/cli/commands/object_store/object_store.py +227 -0
- nat/cli/commands/optimize.py +90 -0
- nat/cli/commands/registry/__init__.py +14 -0
- nat/cli/commands/registry/publish.py +88 -0
- nat/cli/commands/registry/pull.py +118 -0
- nat/cli/commands/registry/registry.py +36 -0
- nat/cli/commands/registry/remove.py +108 -0
- nat/cli/commands/registry/search.py +153 -0
- nat/cli/commands/sizing/__init__.py +14 -0
- nat/cli/commands/sizing/calc.py +297 -0
- nat/cli/commands/sizing/sizing.py +27 -0
- nat/cli/commands/start.py +257 -0
- nat/cli/commands/uninstall.py +81 -0
- nat/cli/commands/validate.py +47 -0
- nat/cli/commands/workflow/__init__.py +14 -0
- nat/cli/commands/workflow/templates/__init__.py.j2 +0 -0
- nat/cli/commands/workflow/templates/config.yml.j2 +17 -0
- nat/cli/commands/workflow/templates/pyproject.toml.j2 +25 -0
- nat/cli/commands/workflow/templates/register.py.j2 +4 -0
- nat/cli/commands/workflow/templates/workflow.py.j2 +50 -0
- nat/cli/commands/workflow/workflow.py +37 -0
- nat/cli/commands/workflow/workflow_commands.py +403 -0
- nat/cli/entrypoint.py +141 -0
- nat/cli/main.py +60 -0
- nat/cli/register_workflow.py +522 -0
- nat/cli/type_registry.py +1069 -0
- nat/control_flow/__init__.py +0 -0
- nat/control_flow/register.py +20 -0
- nat/control_flow/router_agent/__init__.py +0 -0
- nat/control_flow/router_agent/agent.py +329 -0
- nat/control_flow/router_agent/prompt.py +48 -0
- nat/control_flow/router_agent/register.py +91 -0
- nat/control_flow/sequential_executor.py +166 -0
- nat/data_models/__init__.py +14 -0
- nat/data_models/agent.py +34 -0
- nat/data_models/api_server.py +843 -0
- nat/data_models/authentication.py +245 -0
- nat/data_models/common.py +171 -0
- nat/data_models/component.py +60 -0
- nat/data_models/component_ref.py +179 -0
- nat/data_models/config.py +434 -0
- nat/data_models/dataset_handler.py +169 -0
- nat/data_models/discovery_metadata.py +305 -0
- nat/data_models/embedder.py +27 -0
- nat/data_models/evaluate.py +130 -0
- nat/data_models/evaluator.py +26 -0
- nat/data_models/front_end.py +26 -0
- nat/data_models/function.py +64 -0
- nat/data_models/function_dependencies.py +80 -0
- nat/data_models/gated_field_mixin.py +242 -0
- nat/data_models/interactive.py +246 -0
- nat/data_models/intermediate_step.py +302 -0
- nat/data_models/invocation_node.py +38 -0
- nat/data_models/llm.py +27 -0
- nat/data_models/logging.py +26 -0
- nat/data_models/memory.py +27 -0
- nat/data_models/object_store.py +44 -0
- nat/data_models/optimizable.py +119 -0
- nat/data_models/optimizer.py +149 -0
- nat/data_models/profiler.py +54 -0
- nat/data_models/registry_handler.py +26 -0
- nat/data_models/retriever.py +30 -0
- nat/data_models/retry_mixin.py +35 -0
- nat/data_models/span.py +228 -0
- nat/data_models/step_adaptor.py +64 -0
- nat/data_models/streaming.py +33 -0
- nat/data_models/swe_bench_model.py +54 -0
- nat/data_models/telemetry_exporter.py +26 -0
- nat/data_models/temperature_mixin.py +44 -0
- nat/data_models/thinking_mixin.py +86 -0
- nat/data_models/top_p_mixin.py +44 -0
- nat/data_models/ttc_strategy.py +30 -0
- nat/embedder/__init__.py +0 -0
- nat/embedder/azure_openai_embedder.py +46 -0
- nat/embedder/nim_embedder.py +59 -0
- nat/embedder/openai_embedder.py +42 -0
- nat/embedder/register.py +22 -0
- nat/eval/__init__.py +14 -0
- nat/eval/config.py +62 -0
- nat/eval/dataset_handler/__init__.py +0 -0
- nat/eval/dataset_handler/dataset_downloader.py +106 -0
- nat/eval/dataset_handler/dataset_filter.py +52 -0
- nat/eval/dataset_handler/dataset_handler.py +431 -0
- nat/eval/evaluate.py +565 -0
- nat/eval/evaluator/__init__.py +14 -0
- nat/eval/evaluator/base_evaluator.py +77 -0
- nat/eval/evaluator/evaluator_model.py +58 -0
- nat/eval/intermediate_step_adapter.py +99 -0
- nat/eval/rag_evaluator/__init__.py +0 -0
- nat/eval/rag_evaluator/evaluate.py +178 -0
- nat/eval/rag_evaluator/register.py +143 -0
- nat/eval/register.py +26 -0
- nat/eval/remote_workflow.py +133 -0
- nat/eval/runners/__init__.py +14 -0
- nat/eval/runners/config.py +39 -0
- nat/eval/runners/multi_eval_runner.py +54 -0
- nat/eval/runtime_evaluator/__init__.py +14 -0
- nat/eval/runtime_evaluator/evaluate.py +123 -0
- nat/eval/runtime_evaluator/register.py +100 -0
- nat/eval/runtime_event_subscriber.py +52 -0
- nat/eval/swe_bench_evaluator/__init__.py +0 -0
- nat/eval/swe_bench_evaluator/evaluate.py +215 -0
- nat/eval/swe_bench_evaluator/register.py +36 -0
- nat/eval/trajectory_evaluator/__init__.py +0 -0
- nat/eval/trajectory_evaluator/evaluate.py +75 -0
- nat/eval/trajectory_evaluator/register.py +40 -0
- nat/eval/tunable_rag_evaluator/__init__.py +0 -0
- nat/eval/tunable_rag_evaluator/evaluate.py +242 -0
- nat/eval/tunable_rag_evaluator/register.py +52 -0
- nat/eval/usage_stats.py +41 -0
- nat/eval/utils/__init__.py +0 -0
- nat/eval/utils/eval_trace_ctx.py +89 -0
- nat/eval/utils/output_uploader.py +140 -0
- nat/eval/utils/tqdm_position_registry.py +40 -0
- nat/eval/utils/weave_eval.py +193 -0
- nat/experimental/__init__.py +0 -0
- nat/experimental/decorators/__init__.py +0 -0
- nat/experimental/decorators/experimental_warning_decorator.py +154 -0
- nat/experimental/test_time_compute/__init__.py +0 -0
- nat/experimental/test_time_compute/editing/__init__.py +0 -0
- nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +147 -0
- nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +204 -0
- nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +107 -0
- nat/experimental/test_time_compute/functions/__init__.py +0 -0
- nat/experimental/test_time_compute/functions/execute_score_select_function.py +105 -0
- nat/experimental/test_time_compute/functions/plan_select_execute_function.py +228 -0
- nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +205 -0
- nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +146 -0
- nat/experimental/test_time_compute/models/__init__.py +0 -0
- nat/experimental/test_time_compute/models/editor_config.py +132 -0
- nat/experimental/test_time_compute/models/scoring_config.py +112 -0
- nat/experimental/test_time_compute/models/search_config.py +120 -0
- nat/experimental/test_time_compute/models/selection_config.py +154 -0
- nat/experimental/test_time_compute/models/stage_enums.py +43 -0
- nat/experimental/test_time_compute/models/strategy_base.py +67 -0
- nat/experimental/test_time_compute/models/tool_use_config.py +41 -0
- nat/experimental/test_time_compute/models/ttc_item.py +48 -0
- nat/experimental/test_time_compute/register.py +35 -0
- nat/experimental/test_time_compute/scoring/__init__.py +0 -0
- nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +168 -0
- nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +168 -0
- nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +111 -0
- nat/experimental/test_time_compute/search/__init__.py +0 -0
- nat/experimental/test_time_compute/search/multi_llm_planner.py +128 -0
- nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +122 -0
- nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +128 -0
- nat/experimental/test_time_compute/selection/__init__.py +0 -0
- nat/experimental/test_time_compute/selection/best_of_n_selector.py +63 -0
- nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +131 -0
- nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +157 -0
- nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +128 -0
- nat/experimental/test_time_compute/selection/threshold_selector.py +58 -0
- nat/front_ends/__init__.py +14 -0
- nat/front_ends/console/__init__.py +14 -0
- nat/front_ends/console/authentication_flow_handler.py +285 -0
- nat/front_ends/console/console_front_end_config.py +32 -0
- nat/front_ends/console/console_front_end_plugin.py +108 -0
- nat/front_ends/console/register.py +25 -0
- nat/front_ends/cron/__init__.py +14 -0
- nat/front_ends/fastapi/__init__.py +14 -0
- nat/front_ends/fastapi/auth_flow_handlers/__init__.py +0 -0
- nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +27 -0
- nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +142 -0
- nat/front_ends/fastapi/dask_client_mixin.py +65 -0
- nat/front_ends/fastapi/fastapi_front_end_config.py +272 -0
- nat/front_ends/fastapi/fastapi_front_end_controller.py +68 -0
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +247 -0
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +1257 -0
- nat/front_ends/fastapi/html_snippets/__init__.py +14 -0
- nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +35 -0
- nat/front_ends/fastapi/intermediate_steps_subscriber.py +80 -0
- nat/front_ends/fastapi/job_store.py +602 -0
- nat/front_ends/fastapi/main.py +64 -0
- nat/front_ends/fastapi/message_handler.py +344 -0
- nat/front_ends/fastapi/message_validator.py +351 -0
- nat/front_ends/fastapi/register.py +25 -0
- nat/front_ends/fastapi/response_helpers.py +195 -0
- nat/front_ends/fastapi/step_adaptor.py +319 -0
- nat/front_ends/fastapi/utils.py +57 -0
- nat/front_ends/mcp/__init__.py +14 -0
- nat/front_ends/mcp/introspection_token_verifier.py +73 -0
- nat/front_ends/mcp/mcp_front_end_config.py +90 -0
- nat/front_ends/mcp/mcp_front_end_plugin.py +113 -0
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +268 -0
- nat/front_ends/mcp/memory_profiler.py +320 -0
- nat/front_ends/mcp/register.py +27 -0
- nat/front_ends/mcp/tool_converter.py +290 -0
- nat/front_ends/register.py +21 -0
- nat/front_ends/simple_base/__init__.py +14 -0
- nat/front_ends/simple_base/simple_front_end_plugin_base.py +56 -0
- nat/llm/__init__.py +0 -0
- nat/llm/aws_bedrock_llm.py +69 -0
- nat/llm/azure_openai_llm.py +57 -0
- nat/llm/litellm_llm.py +69 -0
- nat/llm/nim_llm.py +58 -0
- nat/llm/openai_llm.py +54 -0
- nat/llm/register.py +27 -0
- nat/llm/utils/__init__.py +14 -0
- nat/llm/utils/env_config_value.py +93 -0
- nat/llm/utils/error.py +17 -0
- nat/llm/utils/thinking.py +215 -0
- nat/memory/__init__.py +20 -0
- nat/memory/interfaces.py +183 -0
- nat/memory/models.py +112 -0
- nat/meta/pypi.md +58 -0
- nat/object_store/__init__.py +20 -0
- nat/object_store/in_memory_object_store.py +76 -0
- nat/object_store/interfaces.py +84 -0
- nat/object_store/models.py +38 -0
- nat/object_store/register.py +19 -0
- nat/observability/__init__.py +14 -0
- nat/observability/exporter/__init__.py +14 -0
- nat/observability/exporter/base_exporter.py +449 -0
- nat/observability/exporter/exporter.py +78 -0
- nat/observability/exporter/file_exporter.py +33 -0
- nat/observability/exporter/processing_exporter.py +550 -0
- nat/observability/exporter/raw_exporter.py +52 -0
- nat/observability/exporter/span_exporter.py +308 -0
- nat/observability/exporter_manager.py +335 -0
- nat/observability/mixin/__init__.py +14 -0
- nat/observability/mixin/batch_config_mixin.py +26 -0
- nat/observability/mixin/collector_config_mixin.py +23 -0
- nat/observability/mixin/file_mixin.py +288 -0
- nat/observability/mixin/file_mode.py +23 -0
- nat/observability/mixin/redaction_config_mixin.py +42 -0
- nat/observability/mixin/resource_conflict_mixin.py +134 -0
- nat/observability/mixin/serialize_mixin.py +61 -0
- nat/observability/mixin/tagging_config_mixin.py +62 -0
- nat/observability/mixin/type_introspection_mixin.py +496 -0
- nat/observability/processor/__init__.py +14 -0
- nat/observability/processor/batching_processor.py +308 -0
- nat/observability/processor/callback_processor.py +42 -0
- nat/observability/processor/falsy_batch_filter_processor.py +55 -0
- nat/observability/processor/intermediate_step_serializer.py +28 -0
- nat/observability/processor/processor.py +74 -0
- nat/observability/processor/processor_factory.py +70 -0
- nat/observability/processor/redaction/__init__.py +24 -0
- nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
- nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
- nat/observability/processor/redaction/redaction_processor.py +177 -0
- nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
- nat/observability/processor/span_tagging_processor.py +68 -0
- nat/observability/register.py +114 -0
- nat/observability/utils/__init__.py +14 -0
- nat/observability/utils/dict_utils.py +236 -0
- nat/observability/utils/time_utils.py +31 -0
- nat/plugins/.namespace +1 -0
- nat/profiler/__init__.py +0 -0
- nat/profiler/calc/__init__.py +14 -0
- nat/profiler/calc/calc_runner.py +626 -0
- nat/profiler/calc/calculations.py +288 -0
- nat/profiler/calc/data_models.py +188 -0
- nat/profiler/calc/plot.py +345 -0
- nat/profiler/callbacks/__init__.py +0 -0
- nat/profiler/callbacks/agno_callback_handler.py +295 -0
- nat/profiler/callbacks/base_callback_class.py +20 -0
- nat/profiler/callbacks/langchain_callback_handler.py +297 -0
- nat/profiler/callbacks/llama_index_callback_handler.py +205 -0
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +238 -0
- nat/profiler/callbacks/token_usage_base_model.py +27 -0
- nat/profiler/data_frame_row.py +51 -0
- nat/profiler/data_models.py +24 -0
- nat/profiler/decorators/__init__.py +0 -0
- nat/profiler/decorators/framework_wrapper.py +180 -0
- nat/profiler/decorators/function_tracking.py +411 -0
- nat/profiler/forecasting/__init__.py +0 -0
- nat/profiler/forecasting/config.py +18 -0
- nat/profiler/forecasting/model_trainer.py +75 -0
- nat/profiler/forecasting/models/__init__.py +22 -0
- nat/profiler/forecasting/models/forecasting_base_model.py +42 -0
- nat/profiler/forecasting/models/linear_model.py +197 -0
- nat/profiler/forecasting/models/random_forest_regressor.py +269 -0
- nat/profiler/inference_metrics_model.py +28 -0
- nat/profiler/inference_optimization/__init__.py +0 -0
- nat/profiler/inference_optimization/bottleneck_analysis/__init__.py +0 -0
- nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +460 -0
- nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +258 -0
- nat/profiler/inference_optimization/data_models.py +386 -0
- nat/profiler/inference_optimization/experimental/__init__.py +0 -0
- nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +468 -0
- nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +404 -0
- nat/profiler/inference_optimization/llm_metrics.py +212 -0
- nat/profiler/inference_optimization/prompt_caching.py +163 -0
- nat/profiler/inference_optimization/token_uniqueness.py +107 -0
- nat/profiler/inference_optimization/workflow_runtimes.py +72 -0
- nat/profiler/intermediate_property_adapter.py +102 -0
- nat/profiler/parameter_optimization/__init__.py +0 -0
- nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
- nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
- nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
- nat/profiler/parameter_optimization/parameter_selection.py +107 -0
- nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
- nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
- nat/profiler/parameter_optimization/update_helpers.py +66 -0
- nat/profiler/profile_runner.py +478 -0
- nat/profiler/utils.py +186 -0
- nat/registry_handlers/__init__.py +0 -0
- nat/registry_handlers/local/__init__.py +0 -0
- nat/registry_handlers/local/local_handler.py +176 -0
- nat/registry_handlers/local/register_local.py +37 -0
- nat/registry_handlers/metadata_factory.py +60 -0
- nat/registry_handlers/package_utils.py +570 -0
- nat/registry_handlers/pypi/__init__.py +0 -0
- nat/registry_handlers/pypi/pypi_handler.py +248 -0
- nat/registry_handlers/pypi/register_pypi.py +40 -0
- nat/registry_handlers/register.py +20 -0
- nat/registry_handlers/registry_handler_base.py +157 -0
- nat/registry_handlers/rest/__init__.py +0 -0
- nat/registry_handlers/rest/register_rest.py +56 -0
- nat/registry_handlers/rest/rest_handler.py +236 -0
- nat/registry_handlers/schemas/__init__.py +0 -0
- nat/registry_handlers/schemas/headers.py +42 -0
- nat/registry_handlers/schemas/package.py +68 -0
- nat/registry_handlers/schemas/publish.py +68 -0
- nat/registry_handlers/schemas/pull.py +82 -0
- nat/registry_handlers/schemas/remove.py +36 -0
- nat/registry_handlers/schemas/search.py +91 -0
- nat/registry_handlers/schemas/status.py +47 -0
- nat/retriever/__init__.py +0 -0
- nat/retriever/interface.py +41 -0
- nat/retriever/milvus/__init__.py +14 -0
- nat/retriever/milvus/register.py +81 -0
- nat/retriever/milvus/retriever.py +228 -0
- nat/retriever/models.py +77 -0
- nat/retriever/nemo_retriever/__init__.py +14 -0
- nat/retriever/nemo_retriever/register.py +60 -0
- nat/retriever/nemo_retriever/retriever.py +190 -0
- nat/retriever/register.py +21 -0
- nat/runtime/__init__.py +14 -0
- nat/runtime/loader.py +220 -0
- nat/runtime/runner.py +292 -0
- nat/runtime/session.py +223 -0
- nat/runtime/user_metadata.py +130 -0
- nat/settings/__init__.py +0 -0
- nat/settings/global_settings.py +329 -0
- nat/test/.namespace +1 -0
- nat/tool/__init__.py +0 -0
- nat/tool/chat_completion.py +77 -0
- nat/tool/code_execution/README.md +151 -0
- nat/tool/code_execution/__init__.py +0 -0
- nat/tool/code_execution/code_sandbox.py +267 -0
- nat/tool/code_execution/local_sandbox/.gitignore +1 -0
- nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +60 -0
- nat/tool/code_execution/local_sandbox/__init__.py +13 -0
- nat/tool/code_execution/local_sandbox/local_sandbox_server.py +198 -0
- nat/tool/code_execution/local_sandbox/sandbox.requirements.txt +6 -0
- nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +50 -0
- nat/tool/code_execution/register.py +74 -0
- nat/tool/code_execution/test_code_execution_sandbox.py +414 -0
- nat/tool/code_execution/utils.py +100 -0
- nat/tool/datetime_tools.py +82 -0
- nat/tool/document_search.py +141 -0
- nat/tool/github_tools.py +450 -0
- nat/tool/memory_tools/__init__.py +0 -0
- nat/tool/memory_tools/add_memory_tool.py +79 -0
- nat/tool/memory_tools/delete_memory_tool.py +66 -0
- nat/tool/memory_tools/get_memory_tool.py +72 -0
- nat/tool/nvidia_rag.py +95 -0
- nat/tool/register.py +31 -0
- nat/tool/retriever.py +95 -0
- nat/tool/server_tools.py +66 -0
- nat/utils/__init__.py +0 -0
- nat/utils/callable_utils.py +70 -0
- nat/utils/data_models/__init__.py +0 -0
- nat/utils/data_models/schema_validator.py +58 -0
- nat/utils/debugging_utils.py +43 -0
- nat/utils/decorators.py +210 -0
- nat/utils/dump_distro_mapping.py +32 -0
- nat/utils/exception_handlers/__init__.py +0 -0
- nat/utils/exception_handlers/automatic_retries.py +342 -0
- nat/utils/exception_handlers/schemas.py +114 -0
- nat/utils/io/__init__.py +0 -0
- nat/utils/io/model_processing.py +28 -0
- nat/utils/io/yaml_tools.py +119 -0
- nat/utils/log_levels.py +25 -0
- nat/utils/log_utils.py +37 -0
- nat/utils/metadata_utils.py +74 -0
- nat/utils/optional_imports.py +142 -0
- nat/utils/producer_consumer_queue.py +178 -0
- nat/utils/reactive/__init__.py +0 -0
- nat/utils/reactive/base/__init__.py +0 -0
- nat/utils/reactive/base/observable_base.py +65 -0
- nat/utils/reactive/base/observer_base.py +55 -0
- nat/utils/reactive/base/subject_base.py +79 -0
- nat/utils/reactive/observable.py +59 -0
- nat/utils/reactive/observer.py +76 -0
- nat/utils/reactive/subject.py +131 -0
- nat/utils/reactive/subscription.py +49 -0
- nat/utils/settings/__init__.py +0 -0
- nat/utils/settings/global_settings.py +195 -0
- nat/utils/string_utils.py +38 -0
- nat/utils/type_converter.py +299 -0
- nat/utils/type_utils.py +488 -0
- nat/utils/url_utils.py +27 -0
- nvidia_nat-1.1.0a20251020.dist-info/METADATA +195 -0
- nvidia_nat-1.1.0a20251020.dist-info/RECORD +480 -0
- nvidia_nat-1.1.0a20251020.dist-info/WHEEL +5 -0
- nvidia_nat-1.1.0a20251020.dist-info/entry_points.txt +22 -0
- nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE-3rd-party.txt +5478 -0
- nvidia_nat-1.1.0a20251020.dist-info/licenses/LICENSE.md +201 -0
- 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
|