nvidia-nat 1.4.0a20251120__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 +511 -1
- nat/builder/child_builder.py +385 -0
- nat/builder/component_utils.py +28 -4
- nat/builder/context.py +17 -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 +2 -1
- nat/builder/front_end.py +1 -1
- nat/builder/function.py +40 -3
- 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 +1 -1
- nat/builder/workflow_builder.py +536 -424
- 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 +183 -5
- nat/cli/type_registry.py +169 -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 +7 -1
- nat/data_models/component_ref.py +34 -1
- nat/data_models/config.py +62 -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 +1 -1
- 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 +3 -1
- 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 +1 -1
- 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 +217 -80
- 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 +1 -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 +195 -60
- 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 +5 -5
- nat/middleware/cache/__init__.py +14 -0
- nat/middleware/{cache_middleware.py → cache/cache_middleware.py} +39 -42
- 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 +236 -52
- 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 +142 -28
- 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 +7 -20
- 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 +16 -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 +1 -1
- nat/retriever/milvus/retriever.py +1 -1
- 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 +1 -5
- nat/runtime/session.py +451 -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 +1 -1
- 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.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/METADATA +39 -14
- 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 -155
- nat/front_ends/mcp/mcp_front_end_plugin_worker.py +0 -388
- 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.0a20251120.dist-info/RECORD +0 -488
- nvidia_nat-1.4.0a20251120.dist-info/entry_points.txt +0 -23
- {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/WHEEL +0 -0
- {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
- {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/licenses/LICENSE.md +0 -0
- {nvidia_nat-1.4.0a20251120.dist-info → nvidia_nat-1.4.0a20260113.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,440 @@
|
|
|
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
|
+
Output Verifier Defense Middleware.
|
|
17
|
+
|
|
18
|
+
This middleware uses an LLM to verify function outputs for correctness and security.
|
|
19
|
+
It can detect incorrect results, malicious content, and provide corrections automatically.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import logging
|
|
24
|
+
import re
|
|
25
|
+
from collections.abc import AsyncIterator
|
|
26
|
+
from typing import Any
|
|
27
|
+
|
|
28
|
+
from pydantic import Field
|
|
29
|
+
|
|
30
|
+
from nat.middleware.defense.defense_middleware import DefenseMiddleware
|
|
31
|
+
from nat.middleware.defense.defense_middleware import DefenseMiddlewareConfig
|
|
32
|
+
from nat.middleware.defense.defense_middleware_data_models import OutputVerificationResult
|
|
33
|
+
from nat.middleware.function_middleware import CallNext
|
|
34
|
+
from nat.middleware.function_middleware import CallNextStream
|
|
35
|
+
from nat.middleware.middleware import FunctionMiddlewareContext
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class OutputVerifierMiddlewareConfig(DefenseMiddlewareConfig, name="output_verifier"):
|
|
41
|
+
"""Configuration for Output Verifier middleware.
|
|
42
|
+
|
|
43
|
+
This middleware analyzes function outputs using an LLM to verify correctness,
|
|
44
|
+
detect security threats, and provide corrections when needed.
|
|
45
|
+
|
|
46
|
+
Actions:
|
|
47
|
+
- 'partial_compliance': Detect and log threats, but allow content to pass through
|
|
48
|
+
- 'refusal': Block output if threat detected (hard stop)
|
|
49
|
+
- 'redirection': Replace incorrect output with correct answer from LLM
|
|
50
|
+
|
|
51
|
+
Note: Only output analysis is currently supported (target_location='output').
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
llm_name: str = Field(description="Name of the LLM to use for verification (must be defined in llms section)")
|
|
55
|
+
|
|
56
|
+
threshold: float = Field(default=0.7, description="Confidence threshold for threat detection (0.0-1.0)")
|
|
57
|
+
|
|
58
|
+
tool_description: str | None = Field(
|
|
59
|
+
default=None, description="Description of what the tool/function does (optional, helps LLM verify correctness)")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class OutputVerifierMiddleware(DefenseMiddleware):
|
|
63
|
+
"""Verification middleware using an LLM for correctness and security.
|
|
64
|
+
|
|
65
|
+
This middleware uses NAT's LLM system to verify function outputs for:
|
|
66
|
+
|
|
67
|
+
* Correctness and reasonableness
|
|
68
|
+
* Security validation (detecting malicious content and manipulated values)
|
|
69
|
+
* Providing automatic corrections when errors are detected
|
|
70
|
+
|
|
71
|
+
Only output analysis is currently supported (``target_location='output'``).
|
|
72
|
+
|
|
73
|
+
Streaming Behavior:
|
|
74
|
+
For 'refusal' and 'redirection' actions, chunks are buffered and checked
|
|
75
|
+
before yielding to prevent incorrect content from being streamed to clients.
|
|
76
|
+
For 'partial_compliance' action, chunks are yielded immediately; violations
|
|
77
|
+
are logged but content passes through.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, config: OutputVerifierMiddlewareConfig, builder):
|
|
81
|
+
"""Initialize output verifier middleware.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
config: Configuration for output verifier middleware
|
|
85
|
+
builder: Builder instance for loading LLMs
|
|
86
|
+
"""
|
|
87
|
+
super().__init__(config, builder)
|
|
88
|
+
# Store config with correct type for linter
|
|
89
|
+
self.config: OutputVerifierMiddlewareConfig = config
|
|
90
|
+
|
|
91
|
+
# Output Verifier only supports output analysis
|
|
92
|
+
if config.target_location == "input":
|
|
93
|
+
raise ValueError("OutputVerifierMiddleware only supports target_location='output'. "
|
|
94
|
+
"Input analysis is not yet supported.")
|
|
95
|
+
|
|
96
|
+
self._llm = None # Lazy loaded LLM
|
|
97
|
+
|
|
98
|
+
async def _get_llm(self):
|
|
99
|
+
"""Lazy load the LLM when first needed."""
|
|
100
|
+
if self._llm is None:
|
|
101
|
+
self._llm = await self._get_llm_for_defense(self.config.llm_name)
|
|
102
|
+
return self._llm
|
|
103
|
+
|
|
104
|
+
def _extract_json_from_response(self, response_text: str) -> str:
|
|
105
|
+
"""Extract JSON from LLM response, handling markdown code blocks.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
response_text: Raw response from LLM
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Extracted JSON string
|
|
112
|
+
"""
|
|
113
|
+
# Remove markdown code blocks
|
|
114
|
+
if "```json" in response_text:
|
|
115
|
+
response_text = response_text.split("```json")[1].split("```")[0].strip()
|
|
116
|
+
elif "```" in response_text:
|
|
117
|
+
response_text = response_text.split("```")[1].split("```")[0].strip()
|
|
118
|
+
|
|
119
|
+
# Extract JSON object (handles nested braces)
|
|
120
|
+
json_match = re.search(r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}', response_text, re.DOTALL)
|
|
121
|
+
if json_match:
|
|
122
|
+
return json_match.group(0)
|
|
123
|
+
|
|
124
|
+
return response_text
|
|
125
|
+
|
|
126
|
+
async def _analyze_content(self,
|
|
127
|
+
content: Any,
|
|
128
|
+
content_type: str,
|
|
129
|
+
inputs: Any = None,
|
|
130
|
+
function_name: str | None = None) -> OutputVerificationResult:
|
|
131
|
+
"""Check content for threats using the configured LLM.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
content: The content to analyze
|
|
135
|
+
content_type: Either 'input' or 'output' (for logging only)
|
|
136
|
+
inputs: Optional function inputs for context (helps LLM calculate correct answers)
|
|
137
|
+
function_name: Name of the function being verified (for context)
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
OutputVerificationResult with threat detection info and should_refuse flag.
|
|
141
|
+
"""
|
|
142
|
+
content_str = str(content)
|
|
143
|
+
|
|
144
|
+
# Build structured system prompt
|
|
145
|
+
system_prompt = """You are a function output verifier that validates correctness and detects security threats.
|
|
146
|
+
|
|
147
|
+
Your task:
|
|
148
|
+
1. Verify if the output is correct and reasonable given the function name and inputs
|
|
149
|
+
2. Detect suspicious or manipulated values (extreme values, unexpected types, malicious content)
|
|
150
|
+
3. If incorrect, provide the correct output
|
|
151
|
+
4. Only report high confidence when certain
|
|
152
|
+
|
|
153
|
+
Respond ONLY with valid JSON in this exact format:
|
|
154
|
+
{"threat_detected": true/false, "confidence": 0.0-1.0, "reason": "brief explanation",
|
|
155
|
+
"correct_answer": "correct value if wrong, null if correct"}"""
|
|
156
|
+
|
|
157
|
+
# Build user prompt with function context
|
|
158
|
+
user_prompt_parts = []
|
|
159
|
+
|
|
160
|
+
if function_name:
|
|
161
|
+
user_prompt_parts.append(f"Function: {function_name}")
|
|
162
|
+
|
|
163
|
+
if self.config.tool_description:
|
|
164
|
+
user_prompt_parts.append(f"Description: {self.config.tool_description}")
|
|
165
|
+
|
|
166
|
+
if inputs is not None:
|
|
167
|
+
user_prompt_parts.append(f"Inputs: {inputs}")
|
|
168
|
+
|
|
169
|
+
user_prompt_parts.append(f"{content_type.capitalize()}: {content_str}")
|
|
170
|
+
|
|
171
|
+
prompt = "\n".join(user_prompt_parts)
|
|
172
|
+
|
|
173
|
+
response_text = None
|
|
174
|
+
try:
|
|
175
|
+
# Get the LLM (lazy loaded)
|
|
176
|
+
llm = await self._get_llm()
|
|
177
|
+
|
|
178
|
+
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": prompt}]
|
|
179
|
+
|
|
180
|
+
response = await llm.ainvoke(messages)
|
|
181
|
+
|
|
182
|
+
# Extract text from response
|
|
183
|
+
if hasattr(response, 'content'):
|
|
184
|
+
response_text = response.content.strip()
|
|
185
|
+
elif isinstance(response, str):
|
|
186
|
+
response_text = response.strip()
|
|
187
|
+
else:
|
|
188
|
+
response_text = str(response).strip()
|
|
189
|
+
|
|
190
|
+
# Extract and parse JSON
|
|
191
|
+
json_str = self._extract_json_from_response(response_text)
|
|
192
|
+
result = json.loads(json_str)
|
|
193
|
+
|
|
194
|
+
threat_detected = result.get("threat_detected", False)
|
|
195
|
+
confidence = float(result.get("confidence", 0.0))
|
|
196
|
+
|
|
197
|
+
return OutputVerificationResult(threat_detected=threat_detected,
|
|
198
|
+
confidence=confidence,
|
|
199
|
+
reason=result.get("reason", "Unknown"),
|
|
200
|
+
correct_answer=result.get("correct_answer"),
|
|
201
|
+
content_type=content_type,
|
|
202
|
+
should_refuse=threat_detected and confidence >= self.config.threshold,
|
|
203
|
+
error=False)
|
|
204
|
+
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.exception("Output Verifier analysis failed for %s: %s", content_type, e)
|
|
207
|
+
logger.debug(
|
|
208
|
+
"Output Verifier failed response length: %s",
|
|
209
|
+
len(response_text) if response_text else 0,
|
|
210
|
+
)
|
|
211
|
+
return OutputVerificationResult(threat_detected=False,
|
|
212
|
+
confidence=0.0,
|
|
213
|
+
reason=f"Analysis failed: {e}",
|
|
214
|
+
correct_answer=None,
|
|
215
|
+
content_type=content_type,
|
|
216
|
+
should_refuse=False,
|
|
217
|
+
error=True)
|
|
218
|
+
|
|
219
|
+
async def _handle_threat(self,
|
|
220
|
+
content: Any,
|
|
221
|
+
analysis_result: OutputVerificationResult,
|
|
222
|
+
context: FunctionMiddlewareContext) -> Any:
|
|
223
|
+
"""Handle detected threat based on configured action.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
content: The threatening content
|
|
227
|
+
analysis_result: Detection result from LLM.
|
|
228
|
+
context: Function context
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
Handled content (blocked, sanitized/corrected, or original)
|
|
232
|
+
"""
|
|
233
|
+
logger.warning("Output Verifier detected threat in %s: %s (confidence=%s)",
|
|
234
|
+
context.name,
|
|
235
|
+
analysis_result.reason,
|
|
236
|
+
analysis_result.confidence)
|
|
237
|
+
|
|
238
|
+
action = self.config.action
|
|
239
|
+
|
|
240
|
+
if action == "refusal":
|
|
241
|
+
logger.error("Output Verifier refusing output of %s: %s", context.name, analysis_result.reason)
|
|
242
|
+
raise ValueError(f"Content blocked by security policy: {analysis_result.reason}")
|
|
243
|
+
|
|
244
|
+
elif action == "redirection":
|
|
245
|
+
# Redirection = Replace with correct answer if available
|
|
246
|
+
correct_answer = analysis_result.correct_answer
|
|
247
|
+
|
|
248
|
+
if correct_answer is not None:
|
|
249
|
+
# Try to convert to same type as original content
|
|
250
|
+
# Handle both numeric types and string representations of numbers (for streaming)
|
|
251
|
+
if isinstance(content, int | float):
|
|
252
|
+
try:
|
|
253
|
+
correct_answer = float(correct_answer)
|
|
254
|
+
except (ValueError, TypeError):
|
|
255
|
+
logger.warning("Could not convert '%s' to number", correct_answer)
|
|
256
|
+
elif isinstance(content, str):
|
|
257
|
+
# In streaming mode, content is a string - try to parse as number if possible
|
|
258
|
+
try:
|
|
259
|
+
# Check if content string represents a number
|
|
260
|
+
float(content)
|
|
261
|
+
# If so, convert correct_answer to float to preserve numeric type
|
|
262
|
+
correct_answer = float(correct_answer)
|
|
263
|
+
except (ValueError, TypeError):
|
|
264
|
+
# Not a numeric string, keep correct_answer as-is
|
|
265
|
+
pass
|
|
266
|
+
|
|
267
|
+
logger.info("Output Verifier redirecting %s: Incorrect: %s → Corrected: %s",
|
|
268
|
+
context.name,
|
|
269
|
+
content,
|
|
270
|
+
correct_answer)
|
|
271
|
+
return correct_answer
|
|
272
|
+
else:
|
|
273
|
+
# No correction available, return string message
|
|
274
|
+
logger.info("Redirecting %s (no correction available)", context.name)
|
|
275
|
+
return "[Content blocked: unable to provide corrected value]"
|
|
276
|
+
|
|
277
|
+
else: # action == "partial_compliance"
|
|
278
|
+
logger.warning("Threat logged for %s: %s", context.name, analysis_result.reason)
|
|
279
|
+
return content
|
|
280
|
+
|
|
281
|
+
async def _process_output_verification(
|
|
282
|
+
self,
|
|
283
|
+
value: Any,
|
|
284
|
+
location: str,
|
|
285
|
+
context: FunctionMiddlewareContext,
|
|
286
|
+
inputs: Any = None,
|
|
287
|
+
) -> Any:
|
|
288
|
+
"""Process output verification and handling for a given value.
|
|
289
|
+
|
|
290
|
+
This is a common helper method that handles:
|
|
291
|
+
- Field extraction (if target_field is specified)
|
|
292
|
+
- Output verification analysis
|
|
293
|
+
- Threat handling (refusal, redirection, partial_compliance)
|
|
294
|
+
- Applying corrected value back to original structure
|
|
295
|
+
|
|
296
|
+
Args:
|
|
297
|
+
value: The value to analyze (input or output)
|
|
298
|
+
location: Either "input" or "output" (for logging)
|
|
299
|
+
context: Function context metadata
|
|
300
|
+
inputs: Original function inputs (for analysis context)
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
The value after output verification handling (may be unchanged, corrected, or raise exception)
|
|
304
|
+
"""
|
|
305
|
+
# Extract field from value if target_field is specified
|
|
306
|
+
content_to_analyze, field_info = self._extract_field_from_value(value)
|
|
307
|
+
|
|
308
|
+
# Check the output (either extracted field or entire value)
|
|
309
|
+
logger.info("OutputVerifierMiddleware: Checking %s %s for %s",
|
|
310
|
+
f"field '{self.config.target_field}'" if field_info else "entire",
|
|
311
|
+
location,
|
|
312
|
+
context.name)
|
|
313
|
+
output_result = await self._analyze_content(content_to_analyze,
|
|
314
|
+
location,
|
|
315
|
+
inputs=inputs,
|
|
316
|
+
function_name=context.name)
|
|
317
|
+
|
|
318
|
+
if not output_result.should_refuse:
|
|
319
|
+
# Content verified as correct, return original value
|
|
320
|
+
logger.info("OutputVerifierMiddleware: Verified %s of %s as correct (confidence=%s)",
|
|
321
|
+
location,
|
|
322
|
+
context.name,
|
|
323
|
+
output_result.confidence)
|
|
324
|
+
return value
|
|
325
|
+
|
|
326
|
+
# Threat detected - handle based on action
|
|
327
|
+
sanitized_content = await self._handle_threat(content_to_analyze, output_result, context)
|
|
328
|
+
|
|
329
|
+
# If field was extracted, apply sanitized value back to original structure
|
|
330
|
+
if field_info is not None:
|
|
331
|
+
return self._apply_field_result_to_value(value, field_info, sanitized_content)
|
|
332
|
+
else:
|
|
333
|
+
# No field extraction - return sanitized content directly
|
|
334
|
+
return sanitized_content
|
|
335
|
+
|
|
336
|
+
async def function_middleware_invoke(self,
|
|
337
|
+
*args: Any,
|
|
338
|
+
call_next: CallNext,
|
|
339
|
+
context: FunctionMiddlewareContext,
|
|
340
|
+
**kwargs: Any) -> Any:
|
|
341
|
+
"""Apply output verifier to function invocation.
|
|
342
|
+
|
|
343
|
+
Analyzes function outputs for correctness and security, with auto-correction.
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
args: Positional arguments passed to the function (first arg is typically the input value).
|
|
347
|
+
call_next: Next middleware/function to call.
|
|
348
|
+
context: Function metadata.
|
|
349
|
+
kwargs: Keyword arguments passed to the function.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Function output (potentially corrected, blocked, or sanitized).
|
|
353
|
+
"""
|
|
354
|
+
value = args[0] if args else None
|
|
355
|
+
|
|
356
|
+
# Check if defense should apply to this function
|
|
357
|
+
if not self._should_apply_defense(context.name):
|
|
358
|
+
logger.debug("OutputVerifierMiddleware: Skipping %s (not targeted)", context.name)
|
|
359
|
+
return await call_next(value, *args[1:], **kwargs)
|
|
360
|
+
|
|
361
|
+
try:
|
|
362
|
+
# Call the function
|
|
363
|
+
output = await call_next(value, *args[1:], **kwargs)
|
|
364
|
+
|
|
365
|
+
# Process output verification (handles field extraction, analysis, and application)
|
|
366
|
+
output = await self._process_output_verification(output, "output", context, inputs=value)
|
|
367
|
+
|
|
368
|
+
return output
|
|
369
|
+
|
|
370
|
+
except Exception:
|
|
371
|
+
logger.error(
|
|
372
|
+
"Failed to apply output verification to function %s",
|
|
373
|
+
context.name,
|
|
374
|
+
exc_info=True,
|
|
375
|
+
)
|
|
376
|
+
raise
|
|
377
|
+
|
|
378
|
+
async def function_middleware_stream(self,
|
|
379
|
+
*args: Any,
|
|
380
|
+
call_next: CallNextStream,
|
|
381
|
+
context: FunctionMiddlewareContext,
|
|
382
|
+
**kwargs: Any) -> AsyncIterator[Any]:
|
|
383
|
+
"""Apply output verifier to streaming function.
|
|
384
|
+
|
|
385
|
+
For 'refusal' and 'redirection' actions: Chunks are buffered and checked before yielding.
|
|
386
|
+
For 'partial_compliance' action: Chunks are yielded immediately; violations are logged.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
args: Positional arguments passed to the function (first arg is typically the input value).
|
|
390
|
+
call_next: Next middleware/function to call.
|
|
391
|
+
context: Function metadata.
|
|
392
|
+
kwargs: Keyword arguments passed to the function.
|
|
393
|
+
|
|
394
|
+
Yields:
|
|
395
|
+
Function output chunks (potentially corrected, blocked, or sanitized).
|
|
396
|
+
"""
|
|
397
|
+
value = args[0] if args else None
|
|
398
|
+
|
|
399
|
+
# Check if defense should apply to this function
|
|
400
|
+
if not self._should_apply_defense(context.name):
|
|
401
|
+
logger.debug("OutputVerifierMiddleware: Skipping %s (not targeted)", context.name)
|
|
402
|
+
async for chunk in call_next(value, *args[1:], **kwargs):
|
|
403
|
+
yield chunk
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
try:
|
|
407
|
+
buffer_chunks = self.config.action in ("refusal", "redirection")
|
|
408
|
+
accumulated_chunks: list[Any] = []
|
|
409
|
+
|
|
410
|
+
async for chunk in call_next(value, *args[1:], **kwargs):
|
|
411
|
+
if buffer_chunks:
|
|
412
|
+
accumulated_chunks.append(chunk)
|
|
413
|
+
else:
|
|
414
|
+
# partial_compliance: stream through, but still accumulate for analysis/logging
|
|
415
|
+
yield chunk
|
|
416
|
+
accumulated_chunks.append(chunk)
|
|
417
|
+
|
|
418
|
+
full_output_str = "".join(chunk if isinstance(chunk, str) else str(chunk) for chunk in accumulated_chunks)
|
|
419
|
+
|
|
420
|
+
# Process output verification (handles field extraction, analysis, and application)
|
|
421
|
+
processed_output = await self._process_output_verification(full_output_str, "output", context, inputs=value)
|
|
422
|
+
|
|
423
|
+
processed_str = str(processed_output)
|
|
424
|
+
if self.config.action == "redirection" and processed_str != full_output_str:
|
|
425
|
+
# Redirected/corrected: yield replacement once (and stop).
|
|
426
|
+
yield processed_output
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
if buffer_chunks:
|
|
430
|
+
# refusal: would have raised; safe content: preserve chunking
|
|
431
|
+
for chunk in accumulated_chunks:
|
|
432
|
+
yield chunk
|
|
433
|
+
|
|
434
|
+
except Exception:
|
|
435
|
+
logger.error(
|
|
436
|
+
"Failed to apply output verification to streaming function %s",
|
|
437
|
+
context.name,
|
|
438
|
+
exc_info=True,
|
|
439
|
+
)
|
|
440
|
+
raise
|