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
nat/utils/decorators.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
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
|
+
"""Deprecation utilities.
|
|
16
|
+
|
|
17
|
+
This module provides helpers to standardize deprecation signaling across the
|
|
18
|
+
codebase:
|
|
19
|
+
|
|
20
|
+
- ``issue_deprecation_warning``: Builds and emits a single deprecation message
|
|
21
|
+
per function using the standard logging pipeline.
|
|
22
|
+
- ``deprecated``: A decorator that wraps sync/async functions and generators to
|
|
23
|
+
log a one-time deprecation message upon first use. It supports optional
|
|
24
|
+
metadata, a planned removal version, a suggested replacement, and an
|
|
25
|
+
optional feature name label.
|
|
26
|
+
|
|
27
|
+
Messages are emitted via ``logging.getLogger(__name__).warning`` (not
|
|
28
|
+
``warnings.warn``) so they appear in normal application logs and respect global
|
|
29
|
+
logging configuration. Each unique function logs at most once per process.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
import functools
|
|
33
|
+
import inspect
|
|
34
|
+
import logging
|
|
35
|
+
from collections.abc import AsyncGenerator
|
|
36
|
+
from collections.abc import Callable
|
|
37
|
+
from collections.abc import Generator
|
|
38
|
+
from typing import Any
|
|
39
|
+
from typing import TypeVar
|
|
40
|
+
from typing import overload
|
|
41
|
+
|
|
42
|
+
logger = logging.getLogger(__name__)
|
|
43
|
+
|
|
44
|
+
_warning_issued = set()
|
|
45
|
+
|
|
46
|
+
# Type variables for overloads
|
|
47
|
+
F = TypeVar('F', bound=Callable[..., Any])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def issue_deprecation_warning(function_name: str,
|
|
51
|
+
removal_version: str | None = None,
|
|
52
|
+
replacement: str | None = None,
|
|
53
|
+
reason: str | None = None,
|
|
54
|
+
feature_name: str | None = None,
|
|
55
|
+
metadata: dict[str, Any] | None = None) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Log a deprecation warning message for the function.
|
|
58
|
+
|
|
59
|
+
A warning is emitted only once per function. When a ``metadata`` dict
|
|
60
|
+
is supplied, it is appended to the log entry to provide extra context
|
|
61
|
+
(e.g., version, author, feature flag).
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
function_name: The name of the deprecated function
|
|
65
|
+
removal_version: The version when the function will be removed
|
|
66
|
+
replacement: What to use instead of this function
|
|
67
|
+
reason: Why the function is being deprecated
|
|
68
|
+
feature_name: Optional name of the feature that is deprecated
|
|
69
|
+
metadata: Optional dictionary of metadata to log with the warning
|
|
70
|
+
"""
|
|
71
|
+
if function_name not in _warning_issued:
|
|
72
|
+
# Build the deprecation message
|
|
73
|
+
if feature_name:
|
|
74
|
+
warning_message = f"{feature_name} is deprecated"
|
|
75
|
+
else:
|
|
76
|
+
warning_message = f"Function {function_name} is deprecated"
|
|
77
|
+
|
|
78
|
+
if removal_version:
|
|
79
|
+
warning_message += f" and will be removed in version {removal_version}"
|
|
80
|
+
else:
|
|
81
|
+
warning_message += " and will be removed in a future release"
|
|
82
|
+
|
|
83
|
+
warning_message += "."
|
|
84
|
+
|
|
85
|
+
if reason:
|
|
86
|
+
warning_message += f" Reason: {reason}."
|
|
87
|
+
|
|
88
|
+
if replacement:
|
|
89
|
+
warning_message += f" Use '{replacement}' instead."
|
|
90
|
+
|
|
91
|
+
if metadata:
|
|
92
|
+
warning_message += f" | Metadata: {metadata}"
|
|
93
|
+
|
|
94
|
+
# Issue warning and save function name to avoid duplicate warnings
|
|
95
|
+
logger.warning(warning_message)
|
|
96
|
+
_warning_issued.add(function_name)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# Overloads for different function types
|
|
100
|
+
@overload
|
|
101
|
+
def deprecated(func: F,
|
|
102
|
+
*,
|
|
103
|
+
removal_version: str | None = None,
|
|
104
|
+
replacement: str | None = None,
|
|
105
|
+
reason: str | None = None,
|
|
106
|
+
feature_name: str | None = None,
|
|
107
|
+
metadata: dict[str, Any] | None = None) -> F:
|
|
108
|
+
"""Overload for direct decorator usage (when called without parentheses)."""
|
|
109
|
+
...
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
@overload
|
|
113
|
+
def deprecated(*,
|
|
114
|
+
removal_version: str | None = None,
|
|
115
|
+
replacement: str | None = None,
|
|
116
|
+
reason: str | None = None,
|
|
117
|
+
feature_name: str | None = None,
|
|
118
|
+
metadata: dict[str, Any] | None = None) -> Callable[[F], F]:
|
|
119
|
+
"""Overload for decorator factory usage (when called with parentheses)."""
|
|
120
|
+
...
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def deprecated(func: Any = None,
|
|
124
|
+
*,
|
|
125
|
+
removal_version: str | None = None,
|
|
126
|
+
replacement: str | None = None,
|
|
127
|
+
reason: str | None = None,
|
|
128
|
+
feature_name: str | None = None,
|
|
129
|
+
metadata: dict[str, Any] | None = None) -> Any:
|
|
130
|
+
"""
|
|
131
|
+
Decorator that can wrap any type of function (sync, async, generator,
|
|
132
|
+
async generator) and logs a deprecation warning.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
func: The function to be decorated.
|
|
136
|
+
removal_version: The version when the function will be removed
|
|
137
|
+
replacement: What to use instead of this function
|
|
138
|
+
reason: Why the function is being deprecated
|
|
139
|
+
feature_name: Optional name of the feature that is deprecated. If provided, the warning will be
|
|
140
|
+
prefixed with "The <feature_name> feature is deprecated".
|
|
141
|
+
metadata: Optional dictionary of metadata to log with the warning. This can include information
|
|
142
|
+
like version, author, etc. If provided, the metadata will be
|
|
143
|
+
logged alongside the deprecation warning.
|
|
144
|
+
"""
|
|
145
|
+
function_name: str = f"{func.__module__}.{func.__qualname__}" if func else "<unknown_function>"
|
|
146
|
+
|
|
147
|
+
# If called as @deprecated(...) but not immediately passed a function
|
|
148
|
+
if func is None:
|
|
149
|
+
|
|
150
|
+
def decorator_wrapper(actual_func):
|
|
151
|
+
return deprecated(actual_func,
|
|
152
|
+
removal_version=removal_version,
|
|
153
|
+
replacement=replacement,
|
|
154
|
+
reason=reason,
|
|
155
|
+
feature_name=feature_name,
|
|
156
|
+
metadata=metadata)
|
|
157
|
+
|
|
158
|
+
return decorator_wrapper
|
|
159
|
+
|
|
160
|
+
# --- Validate metadata ---
|
|
161
|
+
if metadata is not None:
|
|
162
|
+
if not isinstance(metadata, dict):
|
|
163
|
+
raise TypeError("metadata must be a dict[str, Any].")
|
|
164
|
+
if any(not isinstance(k, str) for k in metadata.keys()):
|
|
165
|
+
raise TypeError("All metadata keys must be strings.")
|
|
166
|
+
|
|
167
|
+
# --- Now detect the function type and wrap accordingly ---
|
|
168
|
+
if inspect.isasyncgenfunction(func):
|
|
169
|
+
# ---------------------
|
|
170
|
+
# ASYNC GENERATOR
|
|
171
|
+
# ---------------------
|
|
172
|
+
|
|
173
|
+
@functools.wraps(func)
|
|
174
|
+
async def async_gen_wrapper(*args, **kwargs) -> AsyncGenerator[Any, Any]:
|
|
175
|
+
issue_deprecation_warning(function_name, removal_version, replacement, reason, feature_name, metadata)
|
|
176
|
+
async for item in func(*args, **kwargs):
|
|
177
|
+
yield item # yield the original item
|
|
178
|
+
|
|
179
|
+
return async_gen_wrapper
|
|
180
|
+
|
|
181
|
+
if inspect.iscoroutinefunction(func):
|
|
182
|
+
# ---------------------
|
|
183
|
+
# ASYNC FUNCTION
|
|
184
|
+
# ---------------------
|
|
185
|
+
@functools.wraps(func)
|
|
186
|
+
async def async_wrapper(*args, **kwargs) -> Any:
|
|
187
|
+
issue_deprecation_warning(function_name, removal_version, replacement, reason, feature_name, metadata)
|
|
188
|
+
result = await func(*args, **kwargs)
|
|
189
|
+
return result
|
|
190
|
+
|
|
191
|
+
return async_wrapper
|
|
192
|
+
|
|
193
|
+
if inspect.isgeneratorfunction(func):
|
|
194
|
+
# ---------------------
|
|
195
|
+
# SYNC GENERATOR
|
|
196
|
+
# ---------------------
|
|
197
|
+
@functools.wraps(func)
|
|
198
|
+
def sync_gen_wrapper(*args, **kwargs) -> Generator[Any, Any, Any]:
|
|
199
|
+
issue_deprecation_warning(function_name, removal_version, replacement, reason, feature_name, metadata)
|
|
200
|
+
yield from func(*args, **kwargs) # yield the original item
|
|
201
|
+
|
|
202
|
+
return sync_gen_wrapper
|
|
203
|
+
|
|
204
|
+
@functools.wraps(func)
|
|
205
|
+
def sync_wrapper(*args, **kwargs) -> Any:
|
|
206
|
+
issue_deprecation_warning(function_name, removal_version, replacement, reason, feature_name, metadata)
|
|
207
|
+
result = func(*args, **kwargs)
|
|
208
|
+
return result
|
|
209
|
+
|
|
210
|
+
return sync_wrapper
|
|
@@ -0,0 +1,32 @@
|
|
|
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 argparse
|
|
17
|
+
import json
|
|
18
|
+
|
|
19
|
+
from nat.runtime.loader import get_all_entrypoints_distro_mapping
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def dump_distro_mapping(path: str):
|
|
23
|
+
mapping = get_all_entrypoints_distro_mapping()
|
|
24
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
25
|
+
json.dump(mapping, f, indent=4)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
parser = argparse.ArgumentParser()
|
|
30
|
+
parser.add_argument("--path", type=str, required=True)
|
|
31
|
+
args = parser.parse_args()
|
|
32
|
+
dump_distro_mapping(args.path)
|
|
File without changes
|
|
@@ -0,0 +1,342 @@
|
|
|
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
|
+
import asyncio
|
|
16
|
+
import copy
|
|
17
|
+
import functools
|
|
18
|
+
import inspect
|
|
19
|
+
import logging
|
|
20
|
+
import re
|
|
21
|
+
import time
|
|
22
|
+
import types
|
|
23
|
+
from collections.abc import Callable
|
|
24
|
+
from collections.abc import Iterable
|
|
25
|
+
from collections.abc import Sequence
|
|
26
|
+
from typing import Any
|
|
27
|
+
from typing import TypeVar
|
|
28
|
+
|
|
29
|
+
T = TypeVar("T")
|
|
30
|
+
Exc = tuple[type[BaseException], ...] # exception classes
|
|
31
|
+
CodePattern = int | str | range # for retry_codes argument
|
|
32
|
+
logger = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
35
|
+
# Helpers: status-code extraction & pattern matching
|
|
36
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
37
|
+
_CODE_ATTRS = ("code", "status", "status_code", "http_status")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_status_code(exc: BaseException) -> int | None:
|
|
41
|
+
"""Return a numeric status code found inside *exc*, else None."""
|
|
42
|
+
for attr in _CODE_ATTRS:
|
|
43
|
+
if hasattr(exc, attr):
|
|
44
|
+
try:
|
|
45
|
+
return int(getattr(exc, attr))
|
|
46
|
+
except (TypeError, ValueError):
|
|
47
|
+
pass
|
|
48
|
+
if exc.args:
|
|
49
|
+
try:
|
|
50
|
+
return int(exc.args[0])
|
|
51
|
+
except (TypeError, ValueError):
|
|
52
|
+
pass
|
|
53
|
+
return None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _pattern_to_regex(pat: str) -> re.Pattern[str]:
|
|
57
|
+
"""
|
|
58
|
+
Convert simple wildcard pattern (“4xx”, “5*”, “40x”) to a ^regex$.
|
|
59
|
+
Rule: ‘x’ or ‘*’ ⇒ any digit.
|
|
60
|
+
"""
|
|
61
|
+
escaped = re.escape(pat)
|
|
62
|
+
return re.compile("^" + escaped.replace(r"\*", r"\d").replace("x", r"\d") + "$")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _code_matches(code: int, pat: CodePattern) -> bool:
|
|
66
|
+
if isinstance(pat, int):
|
|
67
|
+
return code == pat
|
|
68
|
+
if isinstance(pat, range):
|
|
69
|
+
return code in pat
|
|
70
|
+
return bool(_pattern_to_regex(pat).match(str(code)))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
74
|
+
# Unified retry-decision helper
|
|
75
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
76
|
+
def _want_retry(
|
|
77
|
+
exc: BaseException,
|
|
78
|
+
*,
|
|
79
|
+
code_patterns: Sequence[CodePattern] | None,
|
|
80
|
+
msg_substrings: Sequence[str] | None,
|
|
81
|
+
) -> bool:
|
|
82
|
+
"""
|
|
83
|
+
Return True if the exception satisfies *either* (when provided):
|
|
84
|
+
• code_patterns – matches status-code pattern(s)
|
|
85
|
+
• msg_substrings – contains any of the substrings (case-insensitive)
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
if not code_patterns and not msg_substrings:
|
|
89
|
+
logger.info("Retrying on exception %s without extra filters", exc)
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
# -------- status-code filter --------
|
|
93
|
+
if code_patterns is not None:
|
|
94
|
+
code = _extract_status_code(exc)
|
|
95
|
+
if any(_code_matches(code, p) for p in code_patterns):
|
|
96
|
+
logger.info("Retrying on exception %s with matched code %s", exc, code)
|
|
97
|
+
return True
|
|
98
|
+
|
|
99
|
+
# -------- message filter -----------
|
|
100
|
+
if msg_substrings is not None:
|
|
101
|
+
msg = str(exc).lower()
|
|
102
|
+
if any(s.lower() in msg for s in msg_substrings):
|
|
103
|
+
logger.info("Retrying on exception %s with matched message %s", exc, msg)
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
110
|
+
# Core decorator factory (sync / async / (a)gen)
|
|
111
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
112
|
+
def _retry_decorator(
|
|
113
|
+
*,
|
|
114
|
+
retries: int = 3,
|
|
115
|
+
base_delay: float = 0.25,
|
|
116
|
+
backoff: float = 2.0,
|
|
117
|
+
retry_on: Exc = (Exception, ),
|
|
118
|
+
retry_codes: Sequence[CodePattern] | None = None,
|
|
119
|
+
retry_on_messages: Sequence[str] | None = None,
|
|
120
|
+
deepcopy: bool = False,
|
|
121
|
+
instance_context_aware: bool = False,
|
|
122
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
|
123
|
+
"""
|
|
124
|
+
Build a decorator that retries with exponential back-off *iff*:
|
|
125
|
+
|
|
126
|
+
• the raised exception is an instance of one of `retry_on`
|
|
127
|
+
• AND `_want_retry()` returns True (i.e. matches codes/messages filters)
|
|
128
|
+
|
|
129
|
+
If both `retry_codes` and `retry_on_messages` are None, all exceptions are retried.
|
|
130
|
+
|
|
131
|
+
deepcopy:
|
|
132
|
+
If True, each retry receives deep‑copied *args and **kwargs* to avoid
|
|
133
|
+
mutating shared state between attempts.
|
|
134
|
+
|
|
135
|
+
instance_context_aware:
|
|
136
|
+
If True, the decorator will check for a retry context flag on the first
|
|
137
|
+
argument (assumed to be 'self'). If the flag is set, retries are skipped
|
|
138
|
+
to prevent retry storms in nested method calls.
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
def decorate(fn: Callable[..., T]) -> Callable[..., T]:
|
|
142
|
+
use_deepcopy = deepcopy
|
|
143
|
+
use_context_aware = instance_context_aware
|
|
144
|
+
|
|
145
|
+
class _RetryContext:
|
|
146
|
+
"""Context manager for instance-level retry gating."""
|
|
147
|
+
|
|
148
|
+
__slots__ = ("_obj", "_enabled", "_active")
|
|
149
|
+
|
|
150
|
+
def __init__(self, args: tuple[Any, ...]):
|
|
151
|
+
self._obj = args[0] if (use_context_aware and args) else None
|
|
152
|
+
self._enabled = bool(self._obj)
|
|
153
|
+
self._active = False
|
|
154
|
+
|
|
155
|
+
def __enter__(self):
|
|
156
|
+
if not self._enabled:
|
|
157
|
+
return False
|
|
158
|
+
try:
|
|
159
|
+
# If already in retry context, signal caller to skip retries
|
|
160
|
+
if getattr(self._obj, "_in_retry_context", False):
|
|
161
|
+
return True
|
|
162
|
+
object.__setattr__(self._obj, "_in_retry_context", True)
|
|
163
|
+
self._active = True
|
|
164
|
+
return False
|
|
165
|
+
except Exception:
|
|
166
|
+
# If we cannot set the attribute, behave as if context isn't enabled
|
|
167
|
+
self._enabled = False
|
|
168
|
+
return False
|
|
169
|
+
|
|
170
|
+
def __exit__(self, _exc_type, _exc, _tb):
|
|
171
|
+
if self._enabled and self._active:
|
|
172
|
+
try:
|
|
173
|
+
object.__setattr__(self._obj, "_in_retry_context", False)
|
|
174
|
+
except Exception:
|
|
175
|
+
pass
|
|
176
|
+
|
|
177
|
+
async def _call_with_retry_async(*args, **kw) -> T:
|
|
178
|
+
with _RetryContext(args) as already_in_context:
|
|
179
|
+
if already_in_context:
|
|
180
|
+
return await fn(*args, **kw)
|
|
181
|
+
delay = base_delay
|
|
182
|
+
for attempt in range(retries):
|
|
183
|
+
call_args = copy.deepcopy(args) if use_deepcopy else args
|
|
184
|
+
call_kwargs = copy.deepcopy(kw) if use_deepcopy else kw
|
|
185
|
+
try:
|
|
186
|
+
return await fn(*call_args, **call_kwargs)
|
|
187
|
+
except retry_on as exc:
|
|
188
|
+
if (not _want_retry(exc, code_patterns=retry_codes, msg_substrings=retry_on_messages)
|
|
189
|
+
or attempt == retries - 1):
|
|
190
|
+
raise
|
|
191
|
+
await asyncio.sleep(delay)
|
|
192
|
+
delay *= backoff
|
|
193
|
+
|
|
194
|
+
async def _agen_with_retry(*args, **kw):
|
|
195
|
+
with _RetryContext(args) as already_in_context:
|
|
196
|
+
if already_in_context:
|
|
197
|
+
async for item in fn(*args, **kw):
|
|
198
|
+
yield item
|
|
199
|
+
return
|
|
200
|
+
delay = base_delay
|
|
201
|
+
for attempt in range(retries):
|
|
202
|
+
call_args = copy.deepcopy(args) if use_deepcopy else args
|
|
203
|
+
call_kwargs = copy.deepcopy(kw) if use_deepcopy else kw
|
|
204
|
+
try:
|
|
205
|
+
async for item in fn(*call_args, **call_kwargs):
|
|
206
|
+
yield item
|
|
207
|
+
return
|
|
208
|
+
except retry_on as exc:
|
|
209
|
+
if (not _want_retry(exc, code_patterns=retry_codes, msg_substrings=retry_on_messages)
|
|
210
|
+
or attempt == retries - 1):
|
|
211
|
+
raise
|
|
212
|
+
await asyncio.sleep(delay)
|
|
213
|
+
delay *= backoff
|
|
214
|
+
|
|
215
|
+
def _gen_with_retry(*args, **kw) -> Iterable[Any]:
|
|
216
|
+
with _RetryContext(args) as already_in_context:
|
|
217
|
+
if already_in_context:
|
|
218
|
+
yield from fn(*args, **kw)
|
|
219
|
+
return
|
|
220
|
+
delay = base_delay
|
|
221
|
+
for attempt in range(retries):
|
|
222
|
+
call_args = copy.deepcopy(args) if use_deepcopy else args
|
|
223
|
+
call_kwargs = copy.deepcopy(kw) if use_deepcopy else kw
|
|
224
|
+
try:
|
|
225
|
+
yield from fn(*call_args, **call_kwargs)
|
|
226
|
+
return
|
|
227
|
+
except retry_on as exc:
|
|
228
|
+
if (not _want_retry(exc, code_patterns=retry_codes, msg_substrings=retry_on_messages)
|
|
229
|
+
or attempt == retries - 1):
|
|
230
|
+
raise
|
|
231
|
+
time.sleep(delay)
|
|
232
|
+
delay *= backoff
|
|
233
|
+
|
|
234
|
+
def _sync_with_retry(*args, **kw) -> T:
|
|
235
|
+
with _RetryContext(args) as already_in_context:
|
|
236
|
+
if already_in_context:
|
|
237
|
+
return fn(*args, **kw)
|
|
238
|
+
delay = base_delay
|
|
239
|
+
for attempt in range(retries):
|
|
240
|
+
call_args = copy.deepcopy(args) if use_deepcopy else args
|
|
241
|
+
call_kwargs = copy.deepcopy(kw) if use_deepcopy else kw
|
|
242
|
+
try:
|
|
243
|
+
return fn(*call_args, **call_kwargs)
|
|
244
|
+
except retry_on as exc:
|
|
245
|
+
if (not _want_retry(exc, code_patterns=retry_codes, msg_substrings=retry_on_messages)
|
|
246
|
+
or attempt == retries - 1):
|
|
247
|
+
raise
|
|
248
|
+
time.sleep(delay)
|
|
249
|
+
delay *= backoff
|
|
250
|
+
|
|
251
|
+
# Decide which wrapper to return
|
|
252
|
+
if inspect.iscoroutinefunction(fn):
|
|
253
|
+
wrapper = _call_with_retry_async
|
|
254
|
+
elif inspect.isasyncgenfunction(fn):
|
|
255
|
+
wrapper = _agen_with_retry
|
|
256
|
+
elif inspect.isgeneratorfunction(fn):
|
|
257
|
+
wrapper = _gen_with_retry
|
|
258
|
+
else:
|
|
259
|
+
wrapper = _sync_with_retry
|
|
260
|
+
|
|
261
|
+
return functools.wraps(fn)(wrapper) # type: ignore[return-value]
|
|
262
|
+
|
|
263
|
+
return decorate
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
267
|
+
# Public helper : patch_with_retry
|
|
268
|
+
# ──────────────────────────────────────────────────────────────────────────────
|
|
269
|
+
def patch_with_retry(
|
|
270
|
+
obj: Any,
|
|
271
|
+
*,
|
|
272
|
+
retries: int = 3,
|
|
273
|
+
base_delay: float = 0.25,
|
|
274
|
+
backoff: float = 2.0,
|
|
275
|
+
retry_on: Exc = (Exception, ),
|
|
276
|
+
retry_codes: Sequence[CodePattern] | None = None,
|
|
277
|
+
retry_on_messages: Sequence[str] | None = None,
|
|
278
|
+
deepcopy: bool = False,
|
|
279
|
+
) -> Any:
|
|
280
|
+
"""
|
|
281
|
+
Patch *obj* instance-locally so **every public method** retries on failure.
|
|
282
|
+
|
|
283
|
+
Extra filters
|
|
284
|
+
-------------
|
|
285
|
+
retry_codes
|
|
286
|
+
Same as before – ints, ranges, or wildcard strings (“4xx”, “5*”…).
|
|
287
|
+
retry_on_messages
|
|
288
|
+
List of *substring* patterns. We retry only if **any** pattern
|
|
289
|
+
appears (case-insensitive) in `str(exc)`.
|
|
290
|
+
deepcopy:
|
|
291
|
+
If True, each retry receives deep‑copied *args and **kwargs* to avoid
|
|
292
|
+
mutating shared state between attempts.
|
|
293
|
+
"""
|
|
294
|
+
deco = _retry_decorator(
|
|
295
|
+
retries=retries,
|
|
296
|
+
base_delay=base_delay,
|
|
297
|
+
backoff=backoff,
|
|
298
|
+
retry_on=retry_on,
|
|
299
|
+
retry_codes=retry_codes,
|
|
300
|
+
retry_on_messages=retry_on_messages,
|
|
301
|
+
deepcopy=deepcopy,
|
|
302
|
+
instance_context_aware=True, # Prevent retry storms
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Choose attribute source: the *class* to avoid triggering __getattr__
|
|
306
|
+
cls = obj if inspect.isclass(obj) else type(obj)
|
|
307
|
+
cls_name = getattr(cls, "__name__", str(cls))
|
|
308
|
+
|
|
309
|
+
for name, _ in inspect.getmembers(cls, callable):
|
|
310
|
+
descriptor = inspect.getattr_static(cls, name)
|
|
311
|
+
|
|
312
|
+
# Skip dunders, privates and all descriptors we must not wrap
|
|
313
|
+
if (name.startswith("_") or isinstance(descriptor, property | staticmethod | classmethod)):
|
|
314
|
+
continue
|
|
315
|
+
|
|
316
|
+
original = descriptor.__func__ if isinstance(descriptor, types.MethodType) else descriptor
|
|
317
|
+
wrapped = deco(original)
|
|
318
|
+
|
|
319
|
+
try: # instance‑level first
|
|
320
|
+
if not inspect.isclass(obj):
|
|
321
|
+
object.__setattr__(obj, name, types.MethodType(wrapped, obj))
|
|
322
|
+
continue
|
|
323
|
+
except Exception as exc:
|
|
324
|
+
logger.info(
|
|
325
|
+
"Instance‑level patch failed for %s.%s (%s); "
|
|
326
|
+
"falling back to class‑level patch.",
|
|
327
|
+
cls_name,
|
|
328
|
+
name,
|
|
329
|
+
exc,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
try: # class‑level fallback
|
|
333
|
+
setattr(cls, name, wrapped)
|
|
334
|
+
except Exception as exc:
|
|
335
|
+
logger.info(
|
|
336
|
+
"Cannot patch method %s.%s with automatic retries: %s",
|
|
337
|
+
cls_name,
|
|
338
|
+
name,
|
|
339
|
+
exc,
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
return obj
|