nvidia-nat 1.4.0a20251112__py3-none-any.whl → 1.4.0a20260113__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 +1 -1
- nat/{front_ends/mcp → agent/auto_memory_wrapper}/__init__.py +1 -1
- nat/agent/auto_memory_wrapper/agent.py +278 -0
- nat/agent/auto_memory_wrapper/register.py +227 -0
- nat/agent/auto_memory_wrapper/state.py +30 -0
- nat/agent/base.py +1 -1
- nat/agent/dual_node.py +1 -1
- nat/agent/prompt_optimizer/prompt.py +1 -1
- nat/agent/prompt_optimizer/register.py +1 -1
- nat/agent/react_agent/agent.py +16 -9
- nat/agent/react_agent/output_parser.py +2 -2
- nat/agent/react_agent/prompt.py +3 -2
- nat/agent/react_agent/register.py +2 -2
- nat/agent/react_agent/register_per_user_agent.py +104 -0
- nat/agent/reasoning_agent/reasoning_agent.py +1 -1
- nat/agent/register.py +3 -1
- nat/agent/responses_api_agent/__init__.py +1 -1
- nat/agent/responses_api_agent/register.py +1 -1
- nat/agent/rewoo_agent/agent.py +9 -4
- nat/agent/rewoo_agent/prompt.py +1 -1
- nat/agent/rewoo_agent/register.py +1 -1
- nat/agent/tool_calling_agent/agent.py +5 -4
- nat/agent/tool_calling_agent/register.py +1 -1
- nat/authentication/__init__.py +1 -1
- nat/authentication/api_key/__init__.py +1 -1
- nat/authentication/api_key/api_key_auth_provider.py +1 -1
- nat/authentication/api_key/api_key_auth_provider_config.py +22 -7
- nat/authentication/api_key/register.py +1 -1
- nat/authentication/credential_validator/__init__.py +1 -1
- nat/authentication/credential_validator/bearer_token_validator.py +1 -1
- nat/authentication/exceptions/__init__.py +1 -1
- nat/authentication/exceptions/api_key_exceptions.py +1 -1
- nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
- nat/authentication/http_basic_auth/register.py +1 -1
- nat/authentication/interfaces.py +1 -1
- nat/authentication/oauth2/__init__.py +1 -1
- nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +1 -1
- nat/authentication/oauth2/oauth2_auth_code_flow_provider_config.py +1 -1
- nat/authentication/oauth2/oauth2_resource_server_config.py +1 -1
- nat/authentication/oauth2/register.py +1 -1
- nat/authentication/register.py +1 -1
- nat/builder/builder.py +563 -1
- nat/builder/child_builder.py +385 -0
- nat/builder/component_utils.py +34 -4
- nat/builder/context.py +34 -1
- nat/builder/embedder.py +1 -1
- nat/builder/eval_builder.py +19 -7
- nat/builder/evaluator.py +1 -1
- nat/builder/framework_enum.py +3 -1
- nat/builder/front_end.py +1 -1
- nat/builder/function.py +113 -5
- nat/builder/function_base.py +1 -1
- nat/builder/function_info.py +1 -1
- nat/builder/intermediate_step_manager.py +1 -1
- nat/builder/llm.py +1 -1
- nat/builder/per_user_workflow_builder.py +843 -0
- nat/builder/retriever.py +1 -1
- nat/builder/sync_builder.py +571 -0
- nat/builder/user_interaction_manager.py +1 -1
- nat/builder/workflow.py +5 -3
- nat/builder/workflow_builder.py +619 -378
- nat/cli/__init__.py +1 -1
- nat/cli/cli_utils/config_override.py +1 -1
- nat/cli/cli_utils/validation.py +32 -1
- nat/cli/commands/configure/channel/add.py +1 -1
- nat/cli/commands/configure/channel/channel.py +1 -1
- nat/cli/commands/configure/channel/remove.py +1 -1
- nat/cli/commands/configure/channel/update.py +1 -1
- nat/cli/commands/configure/configure.py +1 -1
- nat/cli/commands/evaluate.py +87 -13
- nat/cli/commands/finetune.py +132 -0
- nat/cli/commands/info/__init__.py +1 -1
- nat/cli/commands/info/info.py +1 -1
- nat/cli/commands/info/list_channels.py +1 -1
- nat/cli/commands/info/list_components.py +1 -1
- nat/cli/commands/object_store/__init__.py +1 -1
- nat/cli/commands/object_store/object_store.py +1 -1
- nat/cli/commands/optimize.py +1 -1
- nat/cli/commands/{mcp → red_teaming}/__init__.py +1 -1
- nat/cli/commands/red_teaming/red_teaming.py +138 -0
- nat/cli/commands/red_teaming/red_teaming_utils.py +73 -0
- nat/cli/commands/registry/__init__.py +1 -1
- nat/cli/commands/registry/publish.py +1 -1
- nat/cli/commands/registry/pull.py +1 -1
- nat/cli/commands/registry/registry.py +1 -1
- nat/cli/commands/registry/remove.py +1 -1
- nat/cli/commands/registry/search.py +1 -1
- nat/cli/commands/sizing/__init__.py +1 -1
- nat/cli/commands/sizing/calc.py +1 -1
- nat/cli/commands/sizing/sizing.py +1 -1
- nat/cli/commands/start.py +1 -1
- nat/cli/commands/uninstall.py +1 -1
- nat/cli/commands/validate.py +1 -1
- nat/cli/commands/workflow/__init__.py +1 -1
- nat/cli/commands/workflow/workflow.py +1 -1
- nat/cli/commands/workflow/workflow_commands.py +3 -2
- nat/cli/entrypoint.py +15 -37
- nat/cli/main.py +2 -2
- nat/cli/plugin_loader.py +69 -0
- nat/cli/register_workflow.py +233 -5
- nat/cli/type_registry.py +237 -3
- nat/control_flow/register.py +1 -1
- nat/control_flow/router_agent/agent.py +1 -1
- nat/control_flow/router_agent/prompt.py +1 -1
- nat/control_flow/router_agent/register.py +1 -1
- nat/control_flow/sequential_executor.py +28 -7
- nat/data_models/__init__.py +1 -1
- nat/data_models/agent.py +1 -1
- nat/data_models/api_server.py +38 -3
- nat/data_models/authentication.py +1 -1
- nat/data_models/common.py +1 -1
- nat/data_models/component.py +9 -1
- nat/data_models/component_ref.py +45 -1
- nat/data_models/config.py +78 -1
- nat/data_models/dataset_handler.py +15 -2
- nat/data_models/discovery_metadata.py +1 -1
- nat/data_models/embedder.py +1 -1
- nat/data_models/evaluate.py +6 -1
- nat/data_models/evaluator.py +1 -1
- nat/data_models/finetuning.py +260 -0
- nat/data_models/front_end.py +1 -1
- nat/data_models/function.py +15 -2
- nat/data_models/function_dependencies.py +1 -1
- nat/data_models/gated_field_mixin.py +1 -1
- nat/data_models/interactive.py +1 -1
- nat/data_models/intermediate_step.py +29 -2
- nat/data_models/invocation_node.py +1 -1
- nat/data_models/llm.py +1 -1
- nat/data_models/logging.py +1 -1
- nat/data_models/memory.py +1 -1
- nat/data_models/middleware.py +37 -0
- nat/data_models/object_store.py +1 -1
- nat/data_models/openai_mcp.py +1 -1
- nat/data_models/optimizable.py +1 -1
- nat/data_models/optimizer.py +1 -1
- nat/data_models/profiler.py +1 -1
- nat/data_models/registry_handler.py +1 -1
- nat/data_models/retriever.py +1 -1
- nat/data_models/retry_mixin.py +1 -1
- nat/data_models/runtime_enum.py +26 -0
- nat/data_models/span.py +1 -1
- nat/data_models/step_adaptor.py +1 -1
- nat/data_models/streaming.py +1 -1
- nat/data_models/swe_bench_model.py +1 -1
- nat/data_models/telemetry_exporter.py +1 -1
- nat/data_models/thinking_mixin.py +1 -1
- nat/data_models/ttc_strategy.py +1 -1
- nat/embedder/azure_openai_embedder.py +1 -1
- nat/embedder/nim_embedder.py +1 -1
- nat/embedder/openai_embedder.py +1 -1
- nat/embedder/register.py +1 -1
- nat/eval/__init__.py +1 -1
- nat/eval/config.py +8 -1
- nat/eval/dataset_handler/dataset_downloader.py +1 -1
- nat/eval/dataset_handler/dataset_filter.py +1 -1
- nat/eval/dataset_handler/dataset_handler.py +4 -2
- nat/eval/evaluate.py +226 -81
- nat/eval/evaluator/__init__.py +1 -1
- nat/eval/evaluator/base_evaluator.py +2 -2
- nat/eval/evaluator/evaluator_model.py +3 -2
- nat/eval/intermediate_step_adapter.py +1 -1
- nat/eval/llm_validator.py +336 -0
- nat/eval/rag_evaluator/evaluate.py +17 -10
- nat/eval/rag_evaluator/register.py +1 -1
- nat/eval/red_teaming_evaluator/__init__.py +14 -0
- nat/eval/red_teaming_evaluator/data_models.py +66 -0
- nat/eval/red_teaming_evaluator/evaluate.py +327 -0
- nat/eval/red_teaming_evaluator/filter_conditions.py +75 -0
- nat/eval/red_teaming_evaluator/register.py +55 -0
- nat/eval/register.py +2 -1
- nat/eval/remote_workflow.py +1 -1
- nat/eval/runners/__init__.py +1 -1
- nat/eval/runners/config.py +1 -1
- nat/eval/runners/multi_eval_runner.py +1 -1
- nat/eval/runners/red_teaming_runner/__init__.py +24 -0
- nat/eval/runners/red_teaming_runner/config.py +282 -0
- nat/eval/runners/red_teaming_runner/report_utils.py +707 -0
- nat/eval/runners/red_teaming_runner/runner.py +867 -0
- nat/eval/runtime_evaluator/__init__.py +1 -1
- nat/eval/runtime_evaluator/evaluate.py +1 -1
- nat/eval/runtime_evaluator/register.py +1 -1
- nat/eval/runtime_event_subscriber.py +1 -1
- nat/eval/swe_bench_evaluator/evaluate.py +1 -1
- nat/eval/swe_bench_evaluator/register.py +1 -1
- nat/eval/trajectory_evaluator/evaluate.py +2 -2
- nat/eval/trajectory_evaluator/register.py +1 -1
- nat/eval/tunable_rag_evaluator/evaluate.py +5 -5
- nat/eval/tunable_rag_evaluator/register.py +1 -1
- nat/eval/usage_stats.py +1 -1
- nat/eval/utils/eval_trace_ctx.py +1 -1
- nat/eval/utils/output_uploader.py +1 -1
- nat/eval/utils/tqdm_position_registry.py +1 -1
- nat/eval/utils/weave_eval.py +1 -1
- nat/experimental/decorators/experimental_warning_decorator.py +1 -1
- nat/experimental/test_time_compute/editing/iterative_plan_refinement_editor.py +1 -1
- nat/experimental/test_time_compute/editing/llm_as_a_judge_editor.py +1 -1
- nat/experimental/test_time_compute/editing/motivation_aware_summarization.py +1 -1
- nat/experimental/test_time_compute/functions/execute_score_select_function.py +1 -1
- nat/experimental/test_time_compute/functions/multi_llm_judge_function.py +88 -0
- nat/experimental/test_time_compute/functions/plan_select_execute_function.py +1 -1
- nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +1 -1
- nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
- nat/experimental/test_time_compute/models/editor_config.py +1 -1
- nat/experimental/test_time_compute/models/scoring_config.py +1 -1
- nat/experimental/test_time_compute/models/search_config.py +20 -2
- nat/experimental/test_time_compute/models/selection_config.py +33 -2
- nat/experimental/test_time_compute/models/stage_enums.py +1 -1
- nat/experimental/test_time_compute/models/strategy_base.py +1 -1
- nat/experimental/test_time_compute/models/tool_use_config.py +1 -1
- nat/experimental/test_time_compute/models/ttc_item.py +1 -1
- nat/experimental/test_time_compute/register.py +4 -1
- nat/experimental/test_time_compute/scoring/llm_based_agent_scorer.py +1 -1
- nat/experimental/test_time_compute/scoring/llm_based_plan_scorer.py +1 -1
- nat/experimental/test_time_compute/scoring/motivation_aware_scorer.py +1 -1
- nat/experimental/test_time_compute/search/multi_llm_generation.py +115 -0
- nat/experimental/test_time_compute/search/multi_llm_planner.py +1 -1
- nat/experimental/test_time_compute/search/multi_query_retrieval_search.py +1 -1
- nat/experimental/test_time_compute/search/single_shot_multi_plan_planner.py +1 -1
- nat/experimental/test_time_compute/selection/best_of_n_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_agent_output_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_based_plan_selector.py +1 -1
- nat/experimental/test_time_compute/selection/llm_judge_selection.py +127 -0
- nat/experimental/test_time_compute/selection/threshold_selector.py +1 -1
- nat/finetuning/__init__.py +24 -0
- nat/finetuning/finetuning_runtime.py +143 -0
- nat/finetuning/interfaces/__init__.py +24 -0
- nat/finetuning/interfaces/finetuning_runner.py +261 -0
- nat/finetuning/interfaces/trainer_adapter.py +103 -0
- nat/finetuning/interfaces/trajectory_builder.py +115 -0
- nat/finetuning/utils/__init__.py +15 -0
- nat/finetuning/utils/parsers/__init__.py +15 -0
- nat/finetuning/utils/parsers/adk_parser.py +141 -0
- nat/finetuning/utils/parsers/base_parser.py +238 -0
- nat/finetuning/utils/parsers/common.py +91 -0
- nat/finetuning/utils/parsers/langchain_parser.py +267 -0
- nat/finetuning/utils/parsers/llama_index_parser.py +218 -0
- nat/front_ends/__init__.py +1 -1
- nat/front_ends/console/__init__.py +1 -1
- nat/front_ends/console/authentication_flow_handler.py +1 -1
- nat/front_ends/console/console_front_end_config.py +4 -1
- nat/front_ends/console/console_front_end_plugin.py +5 -4
- nat/front_ends/console/register.py +1 -1
- nat/front_ends/cron/__init__.py +1 -1
- nat/front_ends/fastapi/__init__.py +1 -1
- nat/front_ends/fastapi/async_job.py +128 -0
- nat/front_ends/fastapi/auth_flow_handlers/http_flow_handler.py +1 -1
- nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +13 -9
- nat/front_ends/fastapi/dask_client_mixin.py +1 -1
- nat/front_ends/fastapi/fastapi_front_end_config.py +23 -1
- nat/front_ends/fastapi/fastapi_front_end_controller.py +1 -1
- nat/front_ends/fastapi/fastapi_front_end_plugin.py +25 -30
- nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +318 -59
- nat/front_ends/fastapi/html_snippets/__init__.py +1 -1
- nat/front_ends/fastapi/html_snippets/auth_code_grant_success.py +1 -1
- nat/front_ends/fastapi/intermediate_steps_subscriber.py +12 -1
- nat/front_ends/fastapi/job_store.py +23 -11
- nat/front_ends/fastapi/main.py +1 -1
- nat/front_ends/fastapi/message_handler.py +27 -4
- nat/front_ends/fastapi/message_validator.py +54 -2
- nat/front_ends/fastapi/register.py +1 -1
- nat/front_ends/fastapi/response_helpers.py +16 -15
- nat/front_ends/fastapi/step_adaptor.py +1 -1
- nat/front_ends/fastapi/utils.py +1 -1
- nat/front_ends/register.py +1 -2
- nat/front_ends/simple_base/__init__.py +1 -1
- nat/front_ends/simple_base/simple_front_end_plugin_base.py +6 -4
- nat/llm/aws_bedrock_llm.py +1 -1
- nat/llm/azure_openai_llm.py +10 -1
- nat/llm/dynamo_llm.py +363 -0
- nat/llm/huggingface_llm.py +177 -0
- nat/llm/litellm_llm.py +1 -1
- nat/llm/nim_llm.py +1 -1
- nat/llm/openai_llm.py +1 -1
- nat/llm/register.py +3 -1
- nat/llm/utils/__init__.py +1 -1
- nat/llm/utils/env_config_value.py +1 -1
- nat/llm/utils/error.py +1 -1
- nat/llm/utils/thinking.py +1 -1
- nat/memory/__init__.py +1 -1
- nat/memory/interfaces.py +1 -1
- nat/memory/models.py +1 -1
- nat/meta/pypi.md +1 -1
- nat/middleware/__init__.py +35 -0
- nat/middleware/cache/__init__.py +14 -0
- nat/middleware/cache/cache_middleware.py +253 -0
- nat/middleware/cache/cache_middleware_config.py +44 -0
- nat/middleware/cache/register.py +33 -0
- nat/middleware/defense/__init__.py +14 -0
- nat/middleware/defense/defense_middleware.py +362 -0
- nat/middleware/defense/defense_middleware_content_guard.py +455 -0
- nat/middleware/defense/defense_middleware_data_models.py +91 -0
- nat/middleware/defense/defense_middleware_output_verifier.py +440 -0
- nat/middleware/defense/defense_middleware_pii.py +356 -0
- nat/middleware/defense/register.py +82 -0
- nat/middleware/dynamic/__init__.py +14 -0
- nat/middleware/dynamic/dynamic_function_middleware.py +962 -0
- nat/middleware/dynamic/dynamic_middleware_config.py +132 -0
- nat/middleware/dynamic/register.py +34 -0
- nat/middleware/function_middleware.py +370 -0
- nat/middleware/logging/__init__.py +14 -0
- nat/middleware/logging/logging_middleware.py +67 -0
- nat/middleware/logging/logging_middleware_config.py +28 -0
- nat/middleware/logging/register.py +33 -0
- nat/middleware/middleware.py +298 -0
- nat/middleware/red_teaming/__init__.py +14 -0
- nat/middleware/red_teaming/red_teaming_middleware.py +344 -0
- nat/middleware/red_teaming/red_teaming_middleware_config.py +112 -0
- nat/middleware/red_teaming/register.py +47 -0
- nat/middleware/register.py +22 -0
- nat/middleware/utils/__init__.py +14 -0
- nat/middleware/utils/workflow_inventory.py +155 -0
- nat/object_store/__init__.py +1 -1
- nat/object_store/in_memory_object_store.py +1 -1
- nat/object_store/interfaces.py +1 -1
- nat/object_store/models.py +1 -1
- nat/object_store/register.py +1 -1
- nat/observability/__init__.py +1 -1
- nat/observability/exporter/__init__.py +1 -1
- nat/observability/exporter/base_exporter.py +1 -1
- nat/observability/exporter/exporter.py +1 -1
- nat/observability/exporter/file_exporter.py +1 -1
- nat/observability/exporter/processing_exporter.py +1 -1
- nat/observability/exporter/raw_exporter.py +1 -1
- nat/observability/exporter/span_exporter.py +7 -1
- nat/observability/exporter_manager.py +1 -1
- nat/observability/mixin/__init__.py +1 -1
- nat/observability/mixin/batch_config_mixin.py +1 -1
- nat/observability/mixin/collector_config_mixin.py +1 -1
- nat/observability/mixin/file_mixin.py +1 -1
- nat/observability/mixin/file_mode.py +1 -1
- nat/observability/mixin/redaction_config_mixin.py +1 -1
- nat/observability/mixin/resource_conflict_mixin.py +1 -1
- nat/observability/mixin/serialize_mixin.py +1 -1
- nat/observability/mixin/tagging_config_mixin.py +1 -1
- nat/observability/mixin/type_introspection_mixin.py +1 -1
- nat/observability/processor/__init__.py +1 -1
- nat/observability/processor/batching_processor.py +1 -1
- nat/observability/processor/callback_processor.py +1 -1
- nat/observability/processor/falsy_batch_filter_processor.py +1 -1
- nat/observability/processor/intermediate_step_serializer.py +1 -1
- nat/observability/processor/processor.py +1 -1
- nat/observability/processor/processor_factory.py +1 -1
- nat/observability/processor/redaction/__init__.py +1 -1
- nat/observability/processor/redaction/contextual_redaction_processor.py +1 -1
- nat/observability/processor/redaction/contextual_span_redaction_processor.py +1 -1
- nat/observability/processor/redaction/redaction_processor.py +1 -1
- nat/observability/processor/redaction/span_header_redaction_processor.py +1 -1
- nat/observability/processor/span_tagging_processor.py +1 -1
- nat/observability/register.py +1 -1
- nat/observability/utils/__init__.py +1 -1
- nat/observability/utils/dict_utils.py +1 -1
- nat/observability/utils/time_utils.py +1 -1
- nat/profiler/calc/__init__.py +1 -1
- nat/profiler/calc/calc_runner.py +3 -3
- nat/profiler/calc/calculations.py +1 -1
- nat/profiler/calc/data_models.py +1 -1
- nat/profiler/calc/plot.py +30 -3
- nat/profiler/callbacks/agno_callback_handler.py +1 -1
- nat/profiler/callbacks/base_callback_class.py +1 -1
- nat/profiler/callbacks/langchain_callback_handler.py +33 -3
- nat/profiler/callbacks/llama_index_callback_handler.py +13 -10
- nat/profiler/callbacks/semantic_kernel_callback_handler.py +1 -1
- nat/profiler/callbacks/token_usage_base_model.py +1 -1
- nat/profiler/data_frame_row.py +1 -1
- nat/profiler/data_models.py +1 -1
- nat/profiler/decorators/framework_wrapper.py +32 -1
- nat/profiler/decorators/function_tracking.py +1 -1
- nat/profiler/forecasting/config.py +1 -1
- nat/profiler/forecasting/model_trainer.py +1 -1
- nat/profiler/forecasting/models/__init__.py +1 -1
- nat/profiler/forecasting/models/forecasting_base_model.py +1 -1
- nat/profiler/forecasting/models/linear_model.py +1 -1
- nat/profiler/forecasting/models/random_forest_regressor.py +1 -1
- nat/profiler/inference_metrics_model.py +1 -1
- nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +1 -1
- nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
- nat/profiler/inference_optimization/data_models.py +1 -1
- nat/profiler/inference_optimization/experimental/concurrency_spike_analysis.py +1 -1
- nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +1 -1
- nat/profiler/inference_optimization/llm_metrics.py +1 -1
- nat/profiler/inference_optimization/prompt_caching.py +1 -1
- nat/profiler/inference_optimization/token_uniqueness.py +1 -1
- nat/profiler/inference_optimization/workflow_runtimes.py +1 -1
- nat/profiler/intermediate_property_adapter.py +1 -1
- nat/profiler/parameter_optimization/optimizable_utils.py +1 -1
- nat/profiler/parameter_optimization/optimizer_runtime.py +1 -1
- nat/profiler/parameter_optimization/parameter_optimizer.py +1 -1
- nat/profiler/parameter_optimization/parameter_selection.py +1 -1
- nat/profiler/parameter_optimization/pareto_visualizer.py +1 -1
- nat/profiler/parameter_optimization/prompt_optimizer.py +1 -1
- nat/profiler/parameter_optimization/update_helpers.py +1 -1
- nat/profiler/profile_runner.py +1 -1
- nat/profiler/utils.py +1 -1
- nat/registry_handlers/local/local_handler.py +1 -1
- nat/registry_handlers/local/register_local.py +1 -1
- nat/registry_handlers/metadata_factory.py +1 -1
- nat/registry_handlers/package_utils.py +1 -1
- nat/registry_handlers/pypi/pypi_handler.py +1 -1
- nat/registry_handlers/pypi/register_pypi.py +1 -1
- nat/registry_handlers/register.py +1 -1
- nat/registry_handlers/registry_handler_base.py +1 -1
- nat/registry_handlers/rest/register_rest.py +1 -1
- nat/registry_handlers/rest/rest_handler.py +1 -1
- nat/registry_handlers/schemas/headers.py +1 -1
- nat/registry_handlers/schemas/package.py +1 -1
- nat/registry_handlers/schemas/publish.py +1 -1
- nat/registry_handlers/schemas/pull.py +1 -1
- nat/registry_handlers/schemas/remove.py +1 -1
- nat/registry_handlers/schemas/search.py +1 -1
- nat/registry_handlers/schemas/status.py +1 -1
- nat/retriever/interface.py +1 -1
- nat/retriever/milvus/__init__.py +1 -1
- nat/retriever/milvus/register.py +12 -4
- nat/retriever/milvus/retriever.py +103 -41
- nat/retriever/models.py +1 -1
- nat/retriever/nemo_retriever/__init__.py +1 -1
- nat/retriever/nemo_retriever/register.py +1 -1
- nat/retriever/nemo_retriever/retriever.py +5 -5
- nat/retriever/register.py +1 -1
- nat/runtime/__init__.py +1 -1
- nat/runtime/loader.py +10 -3
- nat/runtime/metrics.py +180 -0
- nat/runtime/runner.py +13 -6
- nat/runtime/session.py +458 -32
- nat/runtime/user_metadata.py +1 -1
- nat/settings/global_settings.py +1 -1
- nat/tool/chat_completion.py +1 -1
- nat/tool/code_execution/README.md +1 -1
- nat/tool/code_execution/code_sandbox.py +2 -2
- nat/tool/code_execution/local_sandbox/Dockerfile.sandbox +1 -1
- nat/tool/code_execution/local_sandbox/__init__.py +1 -1
- nat/tool/code_execution/local_sandbox/local_sandbox_server.py +1 -1
- nat/tool/code_execution/local_sandbox/start_local_sandbox.sh +1 -1
- nat/tool/code_execution/register.py +1 -1
- nat/tool/code_execution/utils.py +1 -1
- nat/tool/datetime_tools.py +1 -1
- nat/tool/document_search.py +1 -1
- nat/tool/github_tools.py +1 -1
- nat/tool/memory_tools/add_memory_tool.py +1 -1
- nat/tool/memory_tools/delete_memory_tool.py +1 -1
- nat/tool/memory_tools/get_memory_tool.py +1 -1
- nat/tool/nvidia_rag.py +2 -2
- nat/tool/register.py +1 -1
- nat/tool/retriever.py +1 -1
- nat/tool/server_tools.py +1 -1
- nat/utils/__init__.py +8 -5
- nat/utils/callable_utils.py +1 -1
- nat/utils/data_models/schema_validator.py +1 -1
- nat/utils/debugging_utils.py +1 -1
- nat/utils/decorators.py +1 -1
- nat/utils/dump_distro_mapping.py +1 -1
- nat/utils/exception_handlers/automatic_retries.py +3 -3
- nat/utils/exception_handlers/schemas.py +1 -1
- nat/utils/io/model_processing.py +1 -1
- nat/utils/io/supress_logs.py +33 -0
- nat/utils/io/yaml_tools.py +1 -1
- nat/utils/log_levels.py +1 -1
- nat/utils/log_utils.py +13 -1
- nat/utils/metadata_utils.py +1 -1
- nat/utils/optional_imports.py +1 -1
- nat/utils/producer_consumer_queue.py +1 -1
- nat/utils/reactive/base/observable_base.py +1 -1
- nat/utils/reactive/base/observer_base.py +1 -1
- nat/utils/reactive/base/subject_base.py +1 -1
- nat/utils/reactive/observable.py +1 -1
- nat/utils/reactive/observer.py +1 -1
- nat/utils/reactive/subject.py +1 -1
- nat/utils/reactive/subscription.py +1 -1
- nat/utils/responses_api.py +1 -1
- nat/utils/settings/global_settings.py +1 -1
- nat/utils/string_utils.py +1 -1
- nat/utils/type_converter.py +18 -5
- nat/utils/type_utils.py +1 -1
- nat/utils/url_utils.py +1 -1
- {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/METADATA +46 -15
- nvidia_nat-1.4.0a20260113.dist-info/RECORD +547 -0
- nvidia_nat-1.4.0a20260113.dist-info/entry_points.txt +38 -0
- nat/cli/commands/mcp/mcp.py +0 -986
- nat/front_ends/mcp/introspection_token_verifier.py +0 -73
- nat/front_ends/mcp/mcp_front_end_config.py +0 -109
- nat/front_ends/mcp/mcp_front_end_plugin.py +0 -151
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +0 -362
- nat/front_ends/mcp/memory_profiler.py +0 -320
- nat/front_ends/mcp/register.py +0 -27
- nat/front_ends/mcp/tool_converter.py +0 -321
- nvidia_nat-1.4.0a20251112.dist-info/RECORD +0 -481
- nvidia_nat-1.4.0a20251112.dist-info/entry_points.txt +0 -22
- {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.4.0a20251112.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025-2026, 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
|
+
Base Defense Middleware.
|
|
17
|
+
|
|
18
|
+
This module provides a utility base class for defense middleware with common
|
|
19
|
+
configuration and helper methods. Each defense middleware implements its own
|
|
20
|
+
core logic based on its specific defense strategy (LLM-based, rule-based, etc.).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import secrets
|
|
25
|
+
from typing import Any
|
|
26
|
+
from typing import Literal
|
|
27
|
+
from typing import cast
|
|
28
|
+
|
|
29
|
+
from jsonpath_ng import parse
|
|
30
|
+
from pydantic import BaseModel
|
|
31
|
+
from pydantic import Field
|
|
32
|
+
|
|
33
|
+
from nat.builder.framework_enum import LLMFrameworkEnum
|
|
34
|
+
from nat.data_models.middleware import FunctionMiddlewareBaseConfig
|
|
35
|
+
from nat.middleware.function_middleware import FunctionMiddleware
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MultipleTargetFieldMatchesError(ValueError):
|
|
41
|
+
"""Raised when a JSONPath matches multiple fields and strategy='error'."""
|
|
42
|
+
|
|
43
|
+
def __init__(self, target_field: str | None) -> None:
|
|
44
|
+
super().__init__(f"Multiple matches found for target_field={target_field!r}")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class UnknownTargetFieldResolutionStrategyError(ValueError):
|
|
48
|
+
"""Raised when an unknown target_field_resolution_strategy is configured."""
|
|
49
|
+
|
|
50
|
+
def __init__(self, strategy: str) -> None:
|
|
51
|
+
super().__init__(f"Unknown target_field_resolution_strategy={strategy!r}")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class DefenseMiddlewareConfig(FunctionMiddlewareBaseConfig):
|
|
55
|
+
"""Base configuration for defense middleware.
|
|
56
|
+
|
|
57
|
+
Actions use safety domain terminology:
|
|
58
|
+
- 'partial_compliance': Comply with user request with warning (monitoring mode)
|
|
59
|
+
- 'refusal': Refuse user request (hard refusal)
|
|
60
|
+
- 'redirection': Redirect user request to a safe place; provide a safer response
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
action: Literal["partial_compliance", "refusal", "redirection"] = Field(
|
|
64
|
+
default="partial_compliance",
|
|
65
|
+
description=("Action to take when threat detected. "
|
|
66
|
+
"Options: 'partial_compliance' (log with warning), 'refusal' (block), "
|
|
67
|
+
"'redirection' (sanitize/replace with safe content)"))
|
|
68
|
+
|
|
69
|
+
llm_wrapper_type: LLMFrameworkEnum | str = Field(
|
|
70
|
+
default=LLMFrameworkEnum.LANGCHAIN,
|
|
71
|
+
description="Framework wrapper type for LLM (langchain, llama_index, crewai, etc.). "
|
|
72
|
+
"Only needed for LLM-based defenses.")
|
|
73
|
+
|
|
74
|
+
target_function_or_group: str | None = Field(
|
|
75
|
+
default=None,
|
|
76
|
+
description="Optional function or function group to target. "
|
|
77
|
+
"If None, defense applies to all functions. "
|
|
78
|
+
"Examples: 'my_calculator', 'my_calculator.divide', 'llm_agent.generate'")
|
|
79
|
+
|
|
80
|
+
target_location: Literal["output"] = Field(
|
|
81
|
+
default="output",
|
|
82
|
+
description=("Whether to analyze function input or output. "
|
|
83
|
+
"Currently only 'output' is supported (analyze after function call). "
|
|
84
|
+
"Input analysis is not yet supported."))
|
|
85
|
+
|
|
86
|
+
target_field: str | None = Field(
|
|
87
|
+
default=None,
|
|
88
|
+
description=(
|
|
89
|
+
"Optional JSONPath expression to target specific fields within complex types (dict/list/BaseModel). "
|
|
90
|
+
"If None and value is complex type, defense applies to entire value. "
|
|
91
|
+
"If None and value is simple type (str/int/float), defense applies directly. "
|
|
92
|
+
"Examples: '$.result', '[0]', '$.data.message', 'numbers[0]'"))
|
|
93
|
+
|
|
94
|
+
target_field_resolution_strategy: Literal["error", "first", "last", "random", "all"] = Field(
|
|
95
|
+
default="error",
|
|
96
|
+
description=("Strategy for handling multiple JSONPath matches when target_field is specified. "
|
|
97
|
+
"Options: 'error' (raise error if multiple matches), 'first' (use first match), "
|
|
98
|
+
"'last' (use last match), 'random' (use random match), 'all' (analyze all matches)"))
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class DefenseMiddleware(FunctionMiddleware):
|
|
102
|
+
"""Utility base class for defense middleware.
|
|
103
|
+
|
|
104
|
+
This base class provides:
|
|
105
|
+
|
|
106
|
+
* Common configuration fields (action, check_input, check_output, llm_wrapper_type)
|
|
107
|
+
* Helper methods for LLM loading (for LLM-based defenses)
|
|
108
|
+
* Access to builder for any resources needed
|
|
109
|
+
|
|
110
|
+
Unlike an abstract base class, this does NOT enforce a specific pattern.
|
|
111
|
+
Each defense middleware implements its own invoke/stream logic based on
|
|
112
|
+
its specific defense strategy:
|
|
113
|
+
|
|
114
|
+
* LLM-based analysis (guard models, verifiers)
|
|
115
|
+
* Rule-based detection (regex, signatures)
|
|
116
|
+
* Heuristic-based checks
|
|
117
|
+
* Statistical anomaly detection
|
|
118
|
+
* etc.
|
|
119
|
+
|
|
120
|
+
Each defense owns its core logic, just like red_teaming_middleware does.
|
|
121
|
+
|
|
122
|
+
LLM Wrapper Types:
|
|
123
|
+
The ``llm_wrapper_type`` config field supports different framework wrappers:
|
|
124
|
+
langchain (default) for LangChain/LangGraph-based workflows,
|
|
125
|
+
llama_index for LlamaIndex-based workflows, crewai for CrewAI-based
|
|
126
|
+
workflows, semantic_kernel for Semantic Kernel-based workflows, and
|
|
127
|
+
agno, adk, strands for other supported frameworks.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
def __init__(self, config: DefenseMiddlewareConfig, builder):
|
|
131
|
+
"""Initialize defense middleware.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
config: Configuration for the defense middleware
|
|
135
|
+
builder: Builder instance for loading LLMs and other resources
|
|
136
|
+
"""
|
|
137
|
+
super().__init__(is_final=False)
|
|
138
|
+
self.config = config
|
|
139
|
+
self.builder = builder
|
|
140
|
+
|
|
141
|
+
logger.info(f"{self.__class__.__name__} initialized: "
|
|
142
|
+
f"action={config.action}, target={config.target_function_or_group}")
|
|
143
|
+
|
|
144
|
+
def _should_apply_defense(self, context_name: str) -> bool:
|
|
145
|
+
"""Check if defense should be applied to this function based on targeting configuration.
|
|
146
|
+
|
|
147
|
+
This method mirrors the targeting logic from RedTeamingMiddleware to provide
|
|
148
|
+
consistent behavior between attack and defense middleware.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
context_name: The name of the function from context (e.g., "calculator.add").
|
|
152
|
+
For workflow-level middleware, this will be "<workflow>"
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if defense should be applied, False otherwise
|
|
156
|
+
|
|
157
|
+
Examples:
|
|
158
|
+
- target=None → defends all functions and workflow
|
|
159
|
+
- target="my_calculator" → defends all functions in my_calculator group
|
|
160
|
+
- target="my_calculator.divide" → defends only the divide function
|
|
161
|
+
- target="<workflow>" or "workflow" → defends only at workflow level
|
|
162
|
+
"""
|
|
163
|
+
# If no target specified, defend all functions
|
|
164
|
+
if self.config.target_function_or_group is None:
|
|
165
|
+
return True
|
|
166
|
+
|
|
167
|
+
target = self.config.target_function_or_group
|
|
168
|
+
|
|
169
|
+
# Group targeting - match if context starts with the group name
|
|
170
|
+
# Handle both "group.function" and just "function" in context
|
|
171
|
+
if "." in context_name and "." not in target:
|
|
172
|
+
context_group = context_name.split(".", 1)[0]
|
|
173
|
+
return context_group == target
|
|
174
|
+
|
|
175
|
+
if context_name == "<workflow>":
|
|
176
|
+
return target in {"<workflow>", "workflow"}
|
|
177
|
+
|
|
178
|
+
# Exact match for specific function or group
|
|
179
|
+
return context_name == target
|
|
180
|
+
|
|
181
|
+
async def _get_llm_for_defense(self, llm_name: str, wrapper_type: LLMFrameworkEnum | str | None = None):
|
|
182
|
+
"""Helper to lazy load an LLM for defense purposes.
|
|
183
|
+
|
|
184
|
+
This is a utility method for LLM-based defenses. Not all defenses
|
|
185
|
+
will use this - some may use rule-based or other detection methods.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
llm_name: Name of the LLM to load
|
|
189
|
+
wrapper_type: Framework wrapper type (defaults to config.llm_wrapper_type if not specified)
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
The loaded LLM instance with the specified framework wrapper
|
|
193
|
+
"""
|
|
194
|
+
if wrapper_type is None:
|
|
195
|
+
wrapper_type = self.config.llm_wrapper_type
|
|
196
|
+
|
|
197
|
+
return await self.builder.get_llm(llm_name, wrapper_type=wrapper_type)
|
|
198
|
+
|
|
199
|
+
def _resolve_multiple_field_matches(self, matches):
|
|
200
|
+
"""Resolve multiple JSONPath matches based on resolution strategy.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
matches: List of JSONPath match objects
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of matches based on resolution strategy
|
|
207
|
+
"""
|
|
208
|
+
strategy = self.config.target_field_resolution_strategy
|
|
209
|
+
|
|
210
|
+
if strategy == "error":
|
|
211
|
+
raise MultipleTargetFieldMatchesError(self.config.target_field)
|
|
212
|
+
elif strategy == "first":
|
|
213
|
+
return [matches[0]]
|
|
214
|
+
elif strategy == "last":
|
|
215
|
+
return [matches[-1]]
|
|
216
|
+
elif strategy == "random":
|
|
217
|
+
return [secrets.choice(matches)]
|
|
218
|
+
elif strategy == "all":
|
|
219
|
+
return matches
|
|
220
|
+
else:
|
|
221
|
+
raise UnknownTargetFieldResolutionStrategyError(strategy)
|
|
222
|
+
|
|
223
|
+
def _extract_field_from_value(self, value: Any) -> tuple[Any, dict | None]:
|
|
224
|
+
"""Extract field(s) from value using JSONPath if target_field is specified.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
value: The value to extract fields from (can be simple or complex type).
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
A tuple of (content_to_analyze, field_info_dict) where content_to_analyze
|
|
231
|
+
is the extracted field value(s) or original value if no targeting, and
|
|
232
|
+
field_info_dict contains target_field, matches, and original_value if
|
|
233
|
+
field was extracted, or None otherwise.
|
|
234
|
+
"""
|
|
235
|
+
# If no target_field specified, analyze entire value
|
|
236
|
+
if self.config.target_field is None:
|
|
237
|
+
return value, None
|
|
238
|
+
|
|
239
|
+
# If value is simple type, target_field doesn't apply (analyze entire value)
|
|
240
|
+
if isinstance(value, str | int | float | bool):
|
|
241
|
+
logger.debug(
|
|
242
|
+
"target_field '%s' specified but value is simple type (%s). "
|
|
243
|
+
"Analyzing entire value instead.",
|
|
244
|
+
self.config.target_field,
|
|
245
|
+
type(value).__name__)
|
|
246
|
+
return value, None
|
|
247
|
+
|
|
248
|
+
# For complex types, extract field using JSONPath
|
|
249
|
+
if not isinstance(value, dict | list | BaseModel):
|
|
250
|
+
logger.warning(
|
|
251
|
+
"target_field '%s' specified but value type '%s' is not supported for field extraction. "
|
|
252
|
+
"Analyzing entire value instead.",
|
|
253
|
+
self.config.target_field,
|
|
254
|
+
type(value).__name__)
|
|
255
|
+
return value, None
|
|
256
|
+
|
|
257
|
+
# Convert BaseModel to dict for JSONPath processing
|
|
258
|
+
original_type = type(value)
|
|
259
|
+
is_basemodel = isinstance(value, BaseModel)
|
|
260
|
+
if is_basemodel:
|
|
261
|
+
value_dict = value.model_dump()
|
|
262
|
+
else:
|
|
263
|
+
value_dict = value
|
|
264
|
+
|
|
265
|
+
# Parse JSONPath and find matches
|
|
266
|
+
try:
|
|
267
|
+
jsonpath_expr = parse(self.config.target_field)
|
|
268
|
+
matches = jsonpath_expr.find(value_dict)
|
|
269
|
+
|
|
270
|
+
if len(matches) == 0:
|
|
271
|
+
logger.warning("No matches found for target_field '%s' in value. Analyzing entire value instead.",
|
|
272
|
+
self.config.target_field)
|
|
273
|
+
return value, None
|
|
274
|
+
|
|
275
|
+
# Resolve multiple matches based on strategy
|
|
276
|
+
if len(matches) > 1:
|
|
277
|
+
matches = self._resolve_multiple_field_matches(matches)
|
|
278
|
+
|
|
279
|
+
# Extract field values
|
|
280
|
+
if len(matches) == 1:
|
|
281
|
+
# Single match - return the value directly
|
|
282
|
+
extracted_value = matches[0].value
|
|
283
|
+
else:
|
|
284
|
+
# Multiple matches (strategy="all") - return list of values
|
|
285
|
+
extracted_value = [match.value for match in matches]
|
|
286
|
+
|
|
287
|
+
field_info = {
|
|
288
|
+
"target_field": self.config.target_field,
|
|
289
|
+
"matches": matches,
|
|
290
|
+
"original_value": value,
|
|
291
|
+
"is_basemodel": is_basemodel,
|
|
292
|
+
"original_type": original_type
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
logger.debug("Extracted field '%s' from value: %s -> %s", self.config.target_field, value, extracted_value)
|
|
296
|
+
|
|
297
|
+
return extracted_value, field_info
|
|
298
|
+
|
|
299
|
+
except Exception as e: # noqa: BLE001 - jsonpath-ng may raise multiple exception types; fallback is intentional.
|
|
300
|
+
logger.warning("Failed to extract field '%s' from value: %s. Analyzing entire value instead.",
|
|
301
|
+
self.config.target_field,
|
|
302
|
+
e)
|
|
303
|
+
return value, None
|
|
304
|
+
|
|
305
|
+
def _apply_field_result_to_value(self, original_value: Any, field_info: dict, analysis_result: Any) -> Any:
|
|
306
|
+
"""Apply analysis result back to original value if field was extracted.
|
|
307
|
+
|
|
308
|
+
This is used when defense needs to modify the value based on field analysis.
|
|
309
|
+
For example, if analyzing $.result and need to replace it with sanitized value.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
original_value: The original complex value
|
|
313
|
+
field_info: Field info dict from _extract_field_from_value (None if no field extraction)
|
|
314
|
+
analysis_result: The result from defense analysis (could be sanitized value)
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Modified value with field updated, or original value if no field extraction
|
|
318
|
+
"""
|
|
319
|
+
if field_info is None:
|
|
320
|
+
# No field extraction - return analysis result directly
|
|
321
|
+
return analysis_result
|
|
322
|
+
|
|
323
|
+
# Reconstruct value with updated field
|
|
324
|
+
matches = field_info["matches"]
|
|
325
|
+
is_basemodel = field_info["is_basemodel"]
|
|
326
|
+
original_type = field_info["original_type"]
|
|
327
|
+
|
|
328
|
+
# Get the dict representation
|
|
329
|
+
if is_basemodel:
|
|
330
|
+
value_dict = original_value.model_dump()
|
|
331
|
+
# Create a copy to avoid modifying original
|
|
332
|
+
elif isinstance(original_value, dict):
|
|
333
|
+
value_dict = original_value.copy()
|
|
334
|
+
elif isinstance(original_value, list):
|
|
335
|
+
value_dict = list(original_value)
|
|
336
|
+
else:
|
|
337
|
+
value_dict = original_value
|
|
338
|
+
|
|
339
|
+
# Update field(s) with analysis result
|
|
340
|
+
if len(matches) == 1:
|
|
341
|
+
# Single match - update single field
|
|
342
|
+
matches[0].full_path.update(value_dict, analysis_result)
|
|
343
|
+
# Multiple matches - update all fields (analysis_result should be a list)
|
|
344
|
+
elif isinstance(analysis_result, list) and len(analysis_result) == len(matches):
|
|
345
|
+
for match, result_value in zip(matches, analysis_result, strict=True):
|
|
346
|
+
match.full_path.update(value_dict, result_value)
|
|
347
|
+
else:
|
|
348
|
+
logger.warning("Cannot apply analysis result to multiple fields: "
|
|
349
|
+
"expected list of %d values, got %s",
|
|
350
|
+
len(matches),
|
|
351
|
+
type(analysis_result).__name__)
|
|
352
|
+
return original_value
|
|
353
|
+
|
|
354
|
+
# Reconstruct BaseModel if original was BaseModel
|
|
355
|
+
if is_basemodel:
|
|
356
|
+
assert isinstance(value_dict, dict)
|
|
357
|
+
return cast(type[BaseModel], original_type)(**value_dict)
|
|
358
|
+
|
|
359
|
+
return value_dict
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
__all__ = ["DefenseMiddleware", "DefenseMiddlewareConfig"]
|