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,962 @@
|
|
|
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
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import inspect
|
|
19
|
+
import logging
|
|
20
|
+
from collections.abc import Callable
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
from nat.builder.builder import Builder
|
|
24
|
+
from nat.builder.function import Function
|
|
25
|
+
from nat.data_models.component import ComponentGroup
|
|
26
|
+
from nat.data_models.component_ref import FunctionRef
|
|
27
|
+
from nat.middleware.dynamic.dynamic_middleware_config import DynamicMiddlewareConfig
|
|
28
|
+
from nat.middleware.function_middleware import FunctionMiddleware
|
|
29
|
+
from nat.middleware.function_middleware import FunctionMiddlewareChain
|
|
30
|
+
from nat.middleware.middleware import FunctionMiddlewareContext
|
|
31
|
+
from nat.middleware.middleware import InvocationContext
|
|
32
|
+
from nat.middleware.utils.workflow_inventory import COMPONENT_FUNCTION_ALLOWLISTS
|
|
33
|
+
from nat.middleware.utils.workflow_inventory import DiscoveredComponent
|
|
34
|
+
from nat.middleware.utils.workflow_inventory import DiscoveredFunction
|
|
35
|
+
from nat.middleware.utils.workflow_inventory import RegisteredComponentMethod
|
|
36
|
+
from nat.middleware.utils.workflow_inventory import RegisteredFunction
|
|
37
|
+
from nat.middleware.utils.workflow_inventory import WorkflowInventory
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger(__name__)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DynamicFunctionMiddleware(FunctionMiddleware):
|
|
43
|
+
"""Middleware extends FunctionMiddleware to provide dynamic discovery and
|
|
44
|
+
interception of all workflow components, including functions and components, without requiring explicit
|
|
45
|
+
per-component configuration.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, config: DynamicMiddlewareConfig, builder: Builder):
|
|
49
|
+
"""Initialize middleware and discover workflow functions.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config: Middleware configuration
|
|
53
|
+
builder: Workflow builder
|
|
54
|
+
"""
|
|
55
|
+
super().__init__()
|
|
56
|
+
self._config = config
|
|
57
|
+
self._builder = builder
|
|
58
|
+
|
|
59
|
+
self._registered_callables: dict[str, RegisteredFunction | RegisteredComponentMethod] = {}
|
|
60
|
+
|
|
61
|
+
self._builder_get_llm: Callable | None = None
|
|
62
|
+
self._builder_get_embedder: Callable | None = None
|
|
63
|
+
self._builder_get_retriever: Callable | None = None
|
|
64
|
+
self._builder_get_memory: Callable | None = None
|
|
65
|
+
self._builder_get_object_store: Callable | None = None
|
|
66
|
+
self._builder_get_auth_provider: Callable | None = None
|
|
67
|
+
self._builder_get_function: Callable | None = None
|
|
68
|
+
|
|
69
|
+
self._workflow_inventory: WorkflowInventory = WorkflowInventory()
|
|
70
|
+
|
|
71
|
+
self._component_allowlists: dict[ComponentGroup, set[str]] = self._build_component_allowlists()
|
|
72
|
+
|
|
73
|
+
self._discover_workflow()
|
|
74
|
+
|
|
75
|
+
# ==================== FunctionMiddleware Interface Implementation ====================
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def enabled(self) -> bool:
|
|
79
|
+
"""Whether this middleware should execute.
|
|
80
|
+
|
|
81
|
+
Returns config.enabled value. Framework checks this before invoking
|
|
82
|
+
any middleware methods.
|
|
83
|
+
"""
|
|
84
|
+
return self._config.enabled
|
|
85
|
+
|
|
86
|
+
async def pre_invoke(self, context: InvocationContext) -> InvocationContext | None: # noqa: ARG002
|
|
87
|
+
"""Transform inputs before function execution.
|
|
88
|
+
|
|
89
|
+
Default implementation passes through unchanged. Override in subclass
|
|
90
|
+
to add input transformation logic.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
context: Invocation context (Pydantic model) containing:
|
|
94
|
+
- function_context: Static function metadata (frozen)
|
|
95
|
+
- original_args: What entered the middleware chain (frozen)
|
|
96
|
+
- original_kwargs: What entered the middleware chain (frozen)
|
|
97
|
+
- modified_args: Current args (mutable)
|
|
98
|
+
- modified_kwargs: Current kwargs (mutable)
|
|
99
|
+
- output: None (function not yet called)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
InvocationContext: Return the (modified) context to signal changes
|
|
103
|
+
None: Pass through unchanged (framework uses current context state)
|
|
104
|
+
"""
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
async def post_invoke(self, context: InvocationContext) -> InvocationContext | None: # noqa: ARG002
|
|
108
|
+
"""Transform output after function execution.
|
|
109
|
+
|
|
110
|
+
Default implementation passes through unchanged. Override in subclass
|
|
111
|
+
to add output transformation logic.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
context: Invocation context (Pydantic model) containing:
|
|
115
|
+
- function_context: Static function metadata (frozen)
|
|
116
|
+
- original_args: What entered the middleware chain (frozen)
|
|
117
|
+
- original_kwargs: What entered the middleware chain (frozen)
|
|
118
|
+
- modified_args: What the function received (mutable)
|
|
119
|
+
- modified_kwargs: What the function received (mutable)
|
|
120
|
+
- output: Current output value (mutable)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
InvocationContext: Return the (modified) context to signal changes
|
|
124
|
+
None: Pass through unchanged (framework uses current context.output)
|
|
125
|
+
"""
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
# ==================== Component Discovery and Registration ====================
|
|
129
|
+
|
|
130
|
+
async def _discover_and_register_llm(self, llm_name: str, wrapper_type: Any) -> Any:
|
|
131
|
+
"""Intercept LLM creation and register allowlisted component functions with middleware.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
llm_name: LLM component name
|
|
135
|
+
wrapper_type: LLM framework wrapper type
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
The LLM client instance
|
|
139
|
+
"""
|
|
140
|
+
# Call the original get_llm to get the actual LLM client
|
|
141
|
+
llm_client = await self._get_builder_get_llm()(llm_name, wrapper_type)
|
|
142
|
+
|
|
143
|
+
if not self._should_intercept_llm(llm_name):
|
|
144
|
+
return llm_client
|
|
145
|
+
|
|
146
|
+
if any(client.name == llm_name for client in self._workflow_inventory.llms):
|
|
147
|
+
return llm_client
|
|
148
|
+
|
|
149
|
+
all_functions = self._get_callable_functions(llm_client, component_type='llm')
|
|
150
|
+
|
|
151
|
+
discovered_component = DiscoveredComponent(name=llm_name,
|
|
152
|
+
component_type=ComponentGroup.LLMS,
|
|
153
|
+
instance=llm_client,
|
|
154
|
+
config=None,
|
|
155
|
+
callable_functions=all_functions)
|
|
156
|
+
self._workflow_inventory.llms.append(discovered_component)
|
|
157
|
+
|
|
158
|
+
for function_name in all_functions:
|
|
159
|
+
try:
|
|
160
|
+
self._register_component_function(discovered_component, function_name)
|
|
161
|
+
except Exception:
|
|
162
|
+
logger.debug("Failed to register component function '%s' on LLM '%s'",
|
|
163
|
+
function_name,
|
|
164
|
+
llm_name,
|
|
165
|
+
exc_info=True)
|
|
166
|
+
|
|
167
|
+
return llm_client
|
|
168
|
+
|
|
169
|
+
async def _discover_and_register_embedder(self, embedder_name: str, wrapper_type: Any) -> Any:
|
|
170
|
+
"""Intercept embedder creation and register allowlisted component functions with middleware.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
embedder_name: Embedder component name
|
|
174
|
+
wrapper_type: Embedder framework wrapper type
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
The Embedder client instance
|
|
178
|
+
"""
|
|
179
|
+
# Call the original get_embedder to get the actual embedder client
|
|
180
|
+
embedder_client = await self._get_builder_get_embedder()(embedder_name, wrapper_type)
|
|
181
|
+
|
|
182
|
+
if not self._should_intercept_embedder(embedder_name):
|
|
183
|
+
return embedder_client
|
|
184
|
+
|
|
185
|
+
if any(client.name == embedder_name for client in self._workflow_inventory.embedders):
|
|
186
|
+
return embedder_client
|
|
187
|
+
|
|
188
|
+
all_functions = self._get_callable_functions(embedder_client, component_type='embedder')
|
|
189
|
+
|
|
190
|
+
embedder_config = getattr(embedder_client, 'config', None)
|
|
191
|
+
discovered_component = DiscoveredComponent(name=embedder_name,
|
|
192
|
+
component_type=ComponentGroup.EMBEDDERS,
|
|
193
|
+
instance=embedder_client,
|
|
194
|
+
config=embedder_config,
|
|
195
|
+
callable_functions=all_functions)
|
|
196
|
+
self._workflow_inventory.embedders.append(discovered_component)
|
|
197
|
+
|
|
198
|
+
for function_name in all_functions:
|
|
199
|
+
try:
|
|
200
|
+
self._register_component_function(discovered_component, function_name)
|
|
201
|
+
except Exception:
|
|
202
|
+
logger.debug("Failed to register component function '%s' on embedder '%s'",
|
|
203
|
+
function_name,
|
|
204
|
+
embedder_name,
|
|
205
|
+
exc_info=True)
|
|
206
|
+
|
|
207
|
+
return embedder_client
|
|
208
|
+
|
|
209
|
+
async def _discover_and_register_retriever(self, retriever_name: str, wrapper_type: Any = None):
|
|
210
|
+
"""Intercept retriever creation and register allowlisted component functions with middleware.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
retriever_name: Retriever component name
|
|
214
|
+
wrapper_type: Retriever framework wrapper type
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
The retriever client instance
|
|
218
|
+
"""
|
|
219
|
+
retriever_client = await self._get_builder_get_retriever()(retriever_name, wrapper_type)
|
|
220
|
+
|
|
221
|
+
if not self._should_intercept_retriever(retriever_name):
|
|
222
|
+
return retriever_client
|
|
223
|
+
|
|
224
|
+
if any(client.name == retriever_name for client in self._workflow_inventory.retrievers):
|
|
225
|
+
return retriever_client
|
|
226
|
+
|
|
227
|
+
all_functions = self._get_callable_functions(retriever_client, component_type='retriever')
|
|
228
|
+
|
|
229
|
+
retriever_config = getattr(retriever_client, 'config', None)
|
|
230
|
+
discovered_component = DiscoveredComponent(name=retriever_name,
|
|
231
|
+
component_type=ComponentGroup.RETRIEVERS,
|
|
232
|
+
instance=retriever_client,
|
|
233
|
+
config=retriever_config,
|
|
234
|
+
callable_functions=all_functions)
|
|
235
|
+
self._workflow_inventory.retrievers.append(discovered_component)
|
|
236
|
+
|
|
237
|
+
for function_name in all_functions:
|
|
238
|
+
try:
|
|
239
|
+
self._register_component_function(discovered_component, function_name)
|
|
240
|
+
except Exception:
|
|
241
|
+
logger.debug("Failed to register component function '%s' on retriever '%s'",
|
|
242
|
+
function_name,
|
|
243
|
+
retriever_name,
|
|
244
|
+
exc_info=True)
|
|
245
|
+
|
|
246
|
+
return retriever_client
|
|
247
|
+
|
|
248
|
+
async def _discover_and_register_memory(self, memory_name: str):
|
|
249
|
+
"""Intercept memory creation and register allowlisted component functions with middleware.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
memory_name: Memory component name
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
The memory client instance
|
|
256
|
+
"""
|
|
257
|
+
memory_client = await self._get_builder_get_memory_client()(memory_name)
|
|
258
|
+
|
|
259
|
+
if not self._should_intercept_memory(memory_name):
|
|
260
|
+
return memory_client
|
|
261
|
+
|
|
262
|
+
if any(client.name == memory_name for client in self._workflow_inventory.memory):
|
|
263
|
+
return memory_client
|
|
264
|
+
|
|
265
|
+
all_functions = self._get_callable_functions(memory_client, component_type='memory')
|
|
266
|
+
|
|
267
|
+
memory_config = getattr(memory_client, 'config', None)
|
|
268
|
+
discovered_component = DiscoveredComponent(name=memory_name,
|
|
269
|
+
component_type=ComponentGroup.MEMORY,
|
|
270
|
+
instance=memory_client,
|
|
271
|
+
config=memory_config,
|
|
272
|
+
callable_functions=all_functions)
|
|
273
|
+
self._workflow_inventory.memory.append(discovered_component)
|
|
274
|
+
|
|
275
|
+
for function_name in all_functions:
|
|
276
|
+
try:
|
|
277
|
+
self._register_component_function(discovered_component, function_name)
|
|
278
|
+
except Exception:
|
|
279
|
+
logger.debug("Failed to register component function '%s' on memory '%s'",
|
|
280
|
+
function_name,
|
|
281
|
+
memory_name,
|
|
282
|
+
exc_info=True)
|
|
283
|
+
|
|
284
|
+
return memory_client
|
|
285
|
+
|
|
286
|
+
async def _discover_and_register_object_store(self, object_store_name: str) -> Any:
|
|
287
|
+
"""Intercept object store creation and register allowlisted component functions with middleware.
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
object_store_name: Object store component name
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
The object store client instance
|
|
295
|
+
"""
|
|
296
|
+
store_client = await self._get_builder_get_object_store()(object_store_name)
|
|
297
|
+
|
|
298
|
+
if not self._should_intercept_object_store(object_store_name):
|
|
299
|
+
return store_client
|
|
300
|
+
|
|
301
|
+
if any(client.name == object_store_name for client in self._workflow_inventory.object_stores):
|
|
302
|
+
return store_client
|
|
303
|
+
|
|
304
|
+
all_functions = self._get_callable_functions(store_client, component_type='object_store')
|
|
305
|
+
|
|
306
|
+
store_config = getattr(store_client, 'config', None)
|
|
307
|
+
discovered_component = DiscoveredComponent(name=object_store_name,
|
|
308
|
+
component_type=ComponentGroup.OBJECT_STORES,
|
|
309
|
+
instance=store_client,
|
|
310
|
+
config=store_config,
|
|
311
|
+
callable_functions=all_functions)
|
|
312
|
+
self._workflow_inventory.object_stores.append(discovered_component)
|
|
313
|
+
|
|
314
|
+
# Register all functions - filtering happens in _register_component_function
|
|
315
|
+
for function_name in all_functions:
|
|
316
|
+
try:
|
|
317
|
+
self._register_component_function(discovered_component, function_name)
|
|
318
|
+
except Exception:
|
|
319
|
+
logger.debug("Failed to register component function '%s' on object store '%s'",
|
|
320
|
+
function_name,
|
|
321
|
+
object_store_name,
|
|
322
|
+
exc_info=True)
|
|
323
|
+
|
|
324
|
+
return store_client
|
|
325
|
+
|
|
326
|
+
async def _discover_and_register_auth_provider(self, auth_provider_name: str) -> Any:
|
|
327
|
+
"""Intercept auth provider creation and register allowlisted component functions with middleware.
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
Args:
|
|
331
|
+
auth_provider_name: Auth provider component name
|
|
332
|
+
|
|
333
|
+
Returns:
|
|
334
|
+
The auth provider client instance
|
|
335
|
+
"""
|
|
336
|
+
auth_client = await self._get_builder_get_auth_provider()(auth_provider_name)
|
|
337
|
+
|
|
338
|
+
if not self._should_intercept_auth_provider(auth_provider_name):
|
|
339
|
+
return auth_client
|
|
340
|
+
|
|
341
|
+
if any(client.name == auth_provider_name for client in self._workflow_inventory.auth_providers):
|
|
342
|
+
return auth_client
|
|
343
|
+
|
|
344
|
+
all_functions = self._get_callable_functions(auth_client, component_type='auth')
|
|
345
|
+
|
|
346
|
+
auth_config = getattr(auth_client, 'config', None)
|
|
347
|
+
discovered_component = DiscoveredComponent(name=auth_provider_name,
|
|
348
|
+
component_type=ComponentGroup.AUTHENTICATION,
|
|
349
|
+
instance=auth_client,
|
|
350
|
+
config=auth_config,
|
|
351
|
+
callable_functions=all_functions)
|
|
352
|
+
self._workflow_inventory.auth_providers.append(discovered_component)
|
|
353
|
+
|
|
354
|
+
# Register all functions - filtering happens in _register_component_function
|
|
355
|
+
for function_name in all_functions:
|
|
356
|
+
try:
|
|
357
|
+
self._register_component_function(discovered_component, function_name)
|
|
358
|
+
except Exception:
|
|
359
|
+
logger.debug("Failed to register component function '%s' on auth provider '%s'",
|
|
360
|
+
function_name,
|
|
361
|
+
auth_provider_name,
|
|
362
|
+
exc_info=True)
|
|
363
|
+
|
|
364
|
+
return auth_client
|
|
365
|
+
|
|
366
|
+
async def _discover_and_register_function(self, name: str | FunctionRef) -> Function:
|
|
367
|
+
"""Intercept workflow function and register with middleware.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
name: Function name or reference
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
The function instance
|
|
374
|
+
"""
|
|
375
|
+
function = await self._get_builder_get_function()(name)
|
|
376
|
+
|
|
377
|
+
if not self._config.register_workflow_functions:
|
|
378
|
+
return function
|
|
379
|
+
|
|
380
|
+
func_name = str(name)
|
|
381
|
+
|
|
382
|
+
if any(f.name == func_name for f in self._workflow_inventory.workflow_functions):
|
|
383
|
+
return function
|
|
384
|
+
|
|
385
|
+
func_config = self._builder.get_function_config(name)
|
|
386
|
+
discovered_function = DiscoveredFunction(name=func_name, config=func_config, instance=function)
|
|
387
|
+
self._workflow_inventory.workflow_functions.append(discovered_function)
|
|
388
|
+
|
|
389
|
+
# Register with middleware
|
|
390
|
+
self._register_function(discovered_function)
|
|
391
|
+
|
|
392
|
+
return function
|
|
393
|
+
|
|
394
|
+
# ==================== Internal Discovery and Registration ====================
|
|
395
|
+
|
|
396
|
+
def _discover_workflow(self) -> None:
|
|
397
|
+
"""Discover workflow functions and patch builder methods for runtime interception."""
|
|
398
|
+
# Patch all builder for runtime discovery and registration
|
|
399
|
+
self._patch_components()
|
|
400
|
+
|
|
401
|
+
# Discover registered functions not listed in the config
|
|
402
|
+
self._discover_functions()
|
|
403
|
+
|
|
404
|
+
def _discover_functions(self) -> None:
|
|
405
|
+
"""Discover and register workflow functions already in the builder."""
|
|
406
|
+
if not self._config.register_workflow_functions:
|
|
407
|
+
return
|
|
408
|
+
|
|
409
|
+
if not hasattr(self._builder, '_functions'):
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
# Discover functions already registered
|
|
413
|
+
for func_name, configured_func in self._builder._functions.items(): # type: ignore
|
|
414
|
+
# Skip if already in inventory
|
|
415
|
+
if any(func.name == func_name for func in self._workflow_inventory.workflow_functions):
|
|
416
|
+
continue
|
|
417
|
+
|
|
418
|
+
# Add to inventory
|
|
419
|
+
discovered_function = DiscoveredFunction(name=func_name,
|
|
420
|
+
config=configured_func.config,
|
|
421
|
+
instance=configured_func.instance)
|
|
422
|
+
self._workflow_inventory.workflow_functions.append(discovered_function)
|
|
423
|
+
|
|
424
|
+
# Register with middleware
|
|
425
|
+
self._register_function(discovered_function)
|
|
426
|
+
|
|
427
|
+
# ==================== Helper Methods for Interception ====================
|
|
428
|
+
|
|
429
|
+
def _should_intercept_llm(self, llm_name: str) -> bool:
|
|
430
|
+
"""Check if LLM should be intercepted based on config.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
llm_name: Name of the LLM to check
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
True if should intercept, False otherwise
|
|
437
|
+
"""
|
|
438
|
+
# Check if already registered
|
|
439
|
+
if any(client.name == llm_name for client in self._workflow_inventory.llms):
|
|
440
|
+
return False
|
|
441
|
+
|
|
442
|
+
# If register_llms is True, intercept all LLMs
|
|
443
|
+
if self._config.register_llms:
|
|
444
|
+
return True
|
|
445
|
+
|
|
446
|
+
# Otherwise, only intercept if explicitly configured
|
|
447
|
+
return self._config.llms is not None and llm_name in self._config.llms
|
|
448
|
+
|
|
449
|
+
def _should_intercept_embedder(self, embedder_name: str) -> bool:
|
|
450
|
+
"""Check if embedder should be intercepted based on config.
|
|
451
|
+
|
|
452
|
+
Args:
|
|
453
|
+
embedder_name: Name of the embedder to check
|
|
454
|
+
|
|
455
|
+
Returns:
|
|
456
|
+
True if should intercept, False otherwise
|
|
457
|
+
"""
|
|
458
|
+
# Check if already registered
|
|
459
|
+
if any(client.name == embedder_name for client in self._workflow_inventory.embedders):
|
|
460
|
+
return False
|
|
461
|
+
|
|
462
|
+
# If register_embedders is True, intercept all embedders
|
|
463
|
+
if self._config.register_embedders:
|
|
464
|
+
return True
|
|
465
|
+
|
|
466
|
+
# Otherwise, only intercept if explicitly configured
|
|
467
|
+
return self._config.embedders is not None and embedder_name in self._config.embedders
|
|
468
|
+
|
|
469
|
+
def _should_intercept_retriever(self, retriever_name: str) -> bool:
|
|
470
|
+
"""Check if retriever should be intercepted based on config.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
retriever_name: Name of the retriever to check
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
True if should intercept, False otherwise
|
|
477
|
+
"""
|
|
478
|
+
# Check if already registered
|
|
479
|
+
if any(client.name == retriever_name for client in self._workflow_inventory.retrievers):
|
|
480
|
+
return False
|
|
481
|
+
|
|
482
|
+
# If register_retrievers is True, intercept all retrievers
|
|
483
|
+
if self._config.register_retrievers:
|
|
484
|
+
return True
|
|
485
|
+
|
|
486
|
+
# Otherwise, only intercept if explicitly configured
|
|
487
|
+
return self._config.retrievers is not None and retriever_name in self._config.retrievers
|
|
488
|
+
|
|
489
|
+
def _should_intercept_memory(self, memory_name: str) -> bool:
|
|
490
|
+
"""Check if memory provider should be intercepted based on config.
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
memory_name: Name of the memory provider to check
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
True if should intercept, False otherwise
|
|
497
|
+
"""
|
|
498
|
+
# Check if already registered
|
|
499
|
+
if any(client.name == memory_name for client in self._workflow_inventory.memory):
|
|
500
|
+
return False
|
|
501
|
+
|
|
502
|
+
# If register_memory is True, intercept all memory providers
|
|
503
|
+
if self._config.register_memory:
|
|
504
|
+
return True
|
|
505
|
+
|
|
506
|
+
# Otherwise, only intercept if explicitly configured
|
|
507
|
+
return self._config.memory is not None and memory_name in self._config.memory
|
|
508
|
+
|
|
509
|
+
def _should_intercept_object_store(self, store_name: str) -> bool:
|
|
510
|
+
"""Check if object store should be intercepted based on config.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
store_name: Name of the object store to check
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
True if should intercept, False otherwise
|
|
517
|
+
"""
|
|
518
|
+
# Check if already registered
|
|
519
|
+
if any(client.name == store_name for client in self._workflow_inventory.object_stores):
|
|
520
|
+
return False
|
|
521
|
+
|
|
522
|
+
# If register_object_stores is True, intercept all object stores
|
|
523
|
+
if self._config.register_object_stores:
|
|
524
|
+
return True
|
|
525
|
+
|
|
526
|
+
# Otherwise, only intercept if explicitly configured
|
|
527
|
+
return self._config.object_stores is not None and store_name in self._config.object_stores
|
|
528
|
+
|
|
529
|
+
def _should_intercept_auth_provider(self, auth_name: str) -> bool:
|
|
530
|
+
"""Check if auth provider should be intercepted based on config.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
auth_name: Name of the auth provider to check
|
|
534
|
+
|
|
535
|
+
Returns:
|
|
536
|
+
True if should intercept, False otherwise
|
|
537
|
+
"""
|
|
538
|
+
# Check if already registered
|
|
539
|
+
if any(client.name == auth_name for client in self._workflow_inventory.auth_providers):
|
|
540
|
+
return False
|
|
541
|
+
|
|
542
|
+
# If register_auth_providers is True, intercept all auth providers
|
|
543
|
+
if self._config.register_auth_providers:
|
|
544
|
+
return True
|
|
545
|
+
|
|
546
|
+
# Otherwise, only intercept if explicitly configured
|
|
547
|
+
return self._config.auth_providers is not None and auth_name in self._config.auth_providers
|
|
548
|
+
|
|
549
|
+
def _register_function(self, discovered: DiscoveredFunction) -> None:
|
|
550
|
+
"""Register a discovered workflow function with this middleware.
|
|
551
|
+
|
|
552
|
+
Args:
|
|
553
|
+
discovered: A DiscoveredFunction from the workflow inventory
|
|
554
|
+
"""
|
|
555
|
+
registration_key = discovered.name
|
|
556
|
+
|
|
557
|
+
if registration_key in self._registered_callables:
|
|
558
|
+
logger.debug("Function '%s' already registered, skipping", registration_key)
|
|
559
|
+
return
|
|
560
|
+
|
|
561
|
+
# Add this middleware to the function's existing middleware chain
|
|
562
|
+
existing_middleware = list(discovered.instance.middleware)
|
|
563
|
+
existing_middleware.append(self)
|
|
564
|
+
discovered.instance.configure_middleware(existing_middleware)
|
|
565
|
+
|
|
566
|
+
self._registered_callables[registration_key] = RegisteredFunction(key=registration_key,
|
|
567
|
+
function_instance=discovered.instance)
|
|
568
|
+
|
|
569
|
+
def _register_component_function(self, discovered: DiscoveredComponent, function_name: str) -> None:
|
|
570
|
+
"""Register a specific component function from a discovered component.
|
|
571
|
+
|
|
572
|
+
Args:
|
|
573
|
+
discovered: A DiscoveredComponent from the workflow inventory
|
|
574
|
+
function_name: Name of the component function to register
|
|
575
|
+
"""
|
|
576
|
+
component = discovered.instance
|
|
577
|
+
component_name = discovered.name
|
|
578
|
+
|
|
579
|
+
# Validate function exists
|
|
580
|
+
if not hasattr(component, function_name):
|
|
581
|
+
raise ValueError(f"Component function '{function_name}' does not exist on component '{component_name}'")
|
|
582
|
+
|
|
583
|
+
# Validate function is in discovered callable_functions
|
|
584
|
+
if function_name not in discovered.callable_functions:
|
|
585
|
+
raise ValueError(
|
|
586
|
+
f"Component function '{function_name}' was not discovered as callable on '{component_name}'. "
|
|
587
|
+
f"Available functions: {sorted(discovered.callable_functions)}")
|
|
588
|
+
|
|
589
|
+
# Check allowlist - only auto-register functions in the allowlist (includes user customizations)
|
|
590
|
+
allowlist = self._component_allowlists.get(discovered.component_type, set())
|
|
591
|
+
if function_name not in allowlist:
|
|
592
|
+
logger.debug("Component function '%s.%s' not in allowlist for %s, skipping auto-registration",
|
|
593
|
+
component_name,
|
|
594
|
+
function_name,
|
|
595
|
+
discovered.component_type.value)
|
|
596
|
+
return
|
|
597
|
+
|
|
598
|
+
# Check if already registered
|
|
599
|
+
registration_key = f"{component_name}.{function_name}"
|
|
600
|
+
if registration_key in self._registered_callables:
|
|
601
|
+
logger.debug("Component function '%s' already registered, skipping", registration_key)
|
|
602
|
+
return
|
|
603
|
+
|
|
604
|
+
# Store original callable before wrapping
|
|
605
|
+
original_callable = getattr(component, function_name)
|
|
606
|
+
|
|
607
|
+
# Wrap it with middleware
|
|
608
|
+
wrapped_function = self._configure_component_function_middleware(discovered, function_name)
|
|
609
|
+
|
|
610
|
+
# Replace the function on the component instance
|
|
611
|
+
object.__setattr__(component, function_name, wrapped_function)
|
|
612
|
+
|
|
613
|
+
self._registered_callables[registration_key] = RegisteredComponentMethod(key=registration_key,
|
|
614
|
+
component_instance=component,
|
|
615
|
+
function_name=function_name,
|
|
616
|
+
original_callable=original_callable)
|
|
617
|
+
logger.debug("Registered component function '%s'", registration_key)
|
|
618
|
+
|
|
619
|
+
def get_registered(self, key: str) -> RegisteredFunction | RegisteredComponentMethod | None:
|
|
620
|
+
"""Get a registered callable by its key.
|
|
621
|
+
|
|
622
|
+
Args:
|
|
623
|
+
key: The registration key (for example, "my_llm.invoke" or "calculator__add")
|
|
624
|
+
|
|
625
|
+
Returns:
|
|
626
|
+
The RegisteredFunction or RegisteredComponentMethod if found, None otherwise
|
|
627
|
+
"""
|
|
628
|
+
return self._registered_callables.get(key)
|
|
629
|
+
|
|
630
|
+
def get_registered_keys(self) -> list[str]:
|
|
631
|
+
"""Get all registered callable keys.
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
List of all registration keys currently tracked by this middleware
|
|
635
|
+
"""
|
|
636
|
+
return list(self._registered_callables.keys())
|
|
637
|
+
|
|
638
|
+
def unregister(self, registered: RegisteredFunction | RegisteredComponentMethod) -> None:
|
|
639
|
+
"""Unregister a callable from middleware interception.
|
|
640
|
+
|
|
641
|
+
Args:
|
|
642
|
+
registered: The registered function or component method to unregister
|
|
643
|
+
|
|
644
|
+
Raises:
|
|
645
|
+
ValueError: If not currently registered
|
|
646
|
+
"""
|
|
647
|
+
if registered.key not in self._registered_callables:
|
|
648
|
+
raise ValueError(f"'{registered.key}' is not registered")
|
|
649
|
+
|
|
650
|
+
if isinstance(registered, RegisteredFunction):
|
|
651
|
+
# Remove this middleware from the function's middleware chain
|
|
652
|
+
chain = [m for m in registered.function_instance.middleware if m is not self]
|
|
653
|
+
registered.function_instance.configure_middleware(chain)
|
|
654
|
+
logger.debug("Unregistered workflow function '%s' from middleware interception", registered.key)
|
|
655
|
+
|
|
656
|
+
elif isinstance(registered, RegisteredComponentMethod):
|
|
657
|
+
# Restore original callable on the component instance
|
|
658
|
+
object.__setattr__(registered.component_instance, registered.function_name, registered.original_callable)
|
|
659
|
+
logger.debug("Unregistered component method '%s.%s' from middleware interception",
|
|
660
|
+
type(registered.component_instance).__name__,
|
|
661
|
+
registered.function_name)
|
|
662
|
+
|
|
663
|
+
del self._registered_callables[registered.key]
|
|
664
|
+
|
|
665
|
+
def _configure_component_function_middleware(self, discovered: DiscoveredComponent, function_name: str) -> Any:
|
|
666
|
+
"""Wrap a component function with middleware interception.
|
|
667
|
+
|
|
668
|
+
Args:
|
|
669
|
+
discovered: The DiscoveredComponent from the workflow inventory
|
|
670
|
+
function_name: Name of the component function to wrap
|
|
671
|
+
|
|
672
|
+
Returns:
|
|
673
|
+
Wrapped component function
|
|
674
|
+
"""
|
|
675
|
+
component_instance = discovered.instance
|
|
676
|
+
component_name = discovered.name
|
|
677
|
+
original_function = getattr(component_instance, function_name)
|
|
678
|
+
|
|
679
|
+
# Verify function has __name__
|
|
680
|
+
if not hasattr(original_function, '__name__'):
|
|
681
|
+
raise RuntimeError(
|
|
682
|
+
f"Component function '{function_name}' on component '{component_name}' has no __name__ attribute")
|
|
683
|
+
|
|
684
|
+
registration_key = f"{component_name}.{function_name}"
|
|
685
|
+
|
|
686
|
+
# Check if already registered - return original function to prevent nested wrapping
|
|
687
|
+
if registration_key in self._registered_callables:
|
|
688
|
+
return original_function
|
|
689
|
+
|
|
690
|
+
# Extract metadata safely - defaults to None for missing/inaccessible attributes
|
|
691
|
+
component_config = self._extract_component_attributes(discovered, 'config')
|
|
692
|
+
description = self._extract_component_attributes(discovered, 'description')
|
|
693
|
+
input_schema = self._extract_component_attributes(discovered, 'input_schema')
|
|
694
|
+
single_output_schema = self._extract_component_attributes(discovered, 'single_output_schema')
|
|
695
|
+
stream_output_schema = self._extract_component_attributes(discovered, 'stream_output_schema')
|
|
696
|
+
|
|
697
|
+
# Create static metadata context (original args/kwargs captured by orchestration)
|
|
698
|
+
context = FunctionMiddlewareContext(name=function_name,
|
|
699
|
+
config=component_config,
|
|
700
|
+
description=description,
|
|
701
|
+
input_schema=input_schema,
|
|
702
|
+
single_output_schema=single_output_schema,
|
|
703
|
+
stream_output_schema=stream_output_schema)
|
|
704
|
+
|
|
705
|
+
chain = FunctionMiddlewareChain(middleware=[self], context=context)
|
|
706
|
+
|
|
707
|
+
if inspect.isasyncgenfunction(original_function):
|
|
708
|
+
wrapped_function = chain.build_stream(original_function)
|
|
709
|
+
else:
|
|
710
|
+
wrapped_function = chain.build_single(original_function)
|
|
711
|
+
|
|
712
|
+
return wrapped_function
|
|
713
|
+
|
|
714
|
+
# ==================== Helper Methods ====================
|
|
715
|
+
|
|
716
|
+
def _build_component_allowlists(self) -> dict[ComponentGroup, set[str]]:
|
|
717
|
+
"""Build component allowlists from config (merged with defaults).
|
|
718
|
+
|
|
719
|
+
Returns:
|
|
720
|
+
Dict mapping ComponentGroup enums to sets of allowed function names
|
|
721
|
+
"""
|
|
722
|
+
if self._config.allowed_component_functions is None:
|
|
723
|
+
# No custom config, use defaults
|
|
724
|
+
return {k: v.copy() for k, v in COMPONENT_FUNCTION_ALLOWLISTS.items()}
|
|
725
|
+
|
|
726
|
+
allowed = self._config.allowed_component_functions
|
|
727
|
+
return {
|
|
728
|
+
ComponentGroup.LLMS: allowed.llms, # type: ignore[dict-item]
|
|
729
|
+
ComponentGroup.EMBEDDERS: allowed.embedders, # type: ignore[dict-item]
|
|
730
|
+
ComponentGroup.RETRIEVERS: allowed.retrievers, # type: ignore[dict-item]
|
|
731
|
+
ComponentGroup.MEMORY: allowed.memory, # type: ignore[dict-item]
|
|
732
|
+
ComponentGroup.OBJECT_STORES: allowed.object_stores, # type: ignore[dict-item]
|
|
733
|
+
ComponentGroup.AUTHENTICATION: allowed.authentication, # type: ignore[dict-item]
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
def _extract_component_attributes(self, discovered: DiscoveredComponent, attr_name: str) -> Any:
|
|
737
|
+
"""Safely extract an attribute from a discovered component's instance.
|
|
738
|
+
|
|
739
|
+
Args:
|
|
740
|
+
discovered: DiscoveredComponent containing the component instance
|
|
741
|
+
attr_name: Name of the attribute to extract from the component instance
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
Attribute value or None if it cannot be safely extracted
|
|
745
|
+
"""
|
|
746
|
+
try:
|
|
747
|
+
obj = discovered.instance
|
|
748
|
+
# Check class-level attribute to avoid triggering async properties
|
|
749
|
+
class_attr = getattr(type(obj), attr_name, None)
|
|
750
|
+
if isinstance(class_attr, property):
|
|
751
|
+
return None
|
|
752
|
+
|
|
753
|
+
value = getattr(obj, attr_name, None)
|
|
754
|
+
if callable(value) or inspect.iscoroutine(value):
|
|
755
|
+
return None
|
|
756
|
+
return value
|
|
757
|
+
except Exception:
|
|
758
|
+
return None
|
|
759
|
+
|
|
760
|
+
# ==================== Helper Methods ====================
|
|
761
|
+
|
|
762
|
+
def _get_callable_functions(self, instance: Any, component_type: str | None = None) -> set[str]:
|
|
763
|
+
"""Get all callable functions from component instance that can be safely wrapped.
|
|
764
|
+
|
|
765
|
+
This discovers ALL potentially wrappable component functions without allowlist filtering.
|
|
766
|
+
Safety checks ensure only valid, callable, bound functions are included.
|
|
767
|
+
|
|
768
|
+
Args:
|
|
769
|
+
instance: The component instance to introspect
|
|
770
|
+
component_type: Type of component (for logging/metadata, not filtering)
|
|
771
|
+
|
|
772
|
+
Returns:
|
|
773
|
+
Set of all valid component function names that could be wrapped
|
|
774
|
+
"""
|
|
775
|
+
functions = set()
|
|
776
|
+
|
|
777
|
+
for function_name in dir(instance):
|
|
778
|
+
# Skip private/dunder functions
|
|
779
|
+
if function_name.startswith('_'):
|
|
780
|
+
continue
|
|
781
|
+
|
|
782
|
+
try:
|
|
783
|
+
# Must pass basic validity checks (no errors)
|
|
784
|
+
if not self._is_valid_wrappable_function(instance, function_name):
|
|
785
|
+
continue
|
|
786
|
+
|
|
787
|
+
# Passed all safety checks - this component function CAN be wrapped
|
|
788
|
+
functions.add(function_name)
|
|
789
|
+
|
|
790
|
+
except Exception:
|
|
791
|
+
logger.debug("Skipping function '%s' due to introspection error", function_name, exc_info=True)
|
|
792
|
+
continue
|
|
793
|
+
|
|
794
|
+
return functions
|
|
795
|
+
|
|
796
|
+
def _is_valid_wrappable_function(self, instance: Any, function_name: str) -> bool:
|
|
797
|
+
"""Check if a component function passes all safety checks for wrapping.
|
|
798
|
+
|
|
799
|
+
This is the gatekeeper for what CAN be wrapped (not what SHOULD be).
|
|
800
|
+
|
|
801
|
+
Args:
|
|
802
|
+
instance: The component instance
|
|
803
|
+
function_name: Name of the component function to check
|
|
804
|
+
|
|
805
|
+
Returns:
|
|
806
|
+
True if component function is safe to wrap, False otherwise
|
|
807
|
+
"""
|
|
808
|
+
try:
|
|
809
|
+
instance_class = type(instance)
|
|
810
|
+
|
|
811
|
+
# Check if function exists
|
|
812
|
+
if not hasattr(instance, function_name):
|
|
813
|
+
return False
|
|
814
|
+
|
|
815
|
+
# Get class-level attribute to check type
|
|
816
|
+
class_attr = getattr(instance_class, function_name, None)
|
|
817
|
+
|
|
818
|
+
# Skip properties
|
|
819
|
+
if isinstance(class_attr, property):
|
|
820
|
+
return False
|
|
821
|
+
|
|
822
|
+
# Skip static/class methods
|
|
823
|
+
if isinstance(class_attr, (staticmethod, classmethod)): # noqa: UP038
|
|
824
|
+
return False
|
|
825
|
+
|
|
826
|
+
# Get instance attribute
|
|
827
|
+
attr = getattr(instance, function_name, None)
|
|
828
|
+
if attr is None or not callable(attr):
|
|
829
|
+
return False
|
|
830
|
+
|
|
831
|
+
# Must be a bound method (component function)
|
|
832
|
+
if not inspect.ismethod(attr):
|
|
833
|
+
return False
|
|
834
|
+
|
|
835
|
+
# Must be bound to our instance
|
|
836
|
+
if not hasattr(attr, '__self__') or attr.__self__ is not instance:
|
|
837
|
+
return False
|
|
838
|
+
|
|
839
|
+
# Must have a valid signature
|
|
840
|
+
try:
|
|
841
|
+
inspect.signature(attr)
|
|
842
|
+
except (ValueError, TypeError):
|
|
843
|
+
return False
|
|
844
|
+
|
|
845
|
+
return True
|
|
846
|
+
|
|
847
|
+
except Exception:
|
|
848
|
+
return False
|
|
849
|
+
|
|
850
|
+
def _patch_components(self):
|
|
851
|
+
"""Patch builder getter methods to enable runtime discovery and registration."""
|
|
852
|
+
self._patch_get_llm()
|
|
853
|
+
self._patch_get_embedder()
|
|
854
|
+
self._patch_get_retriever()
|
|
855
|
+
self._patch_get_memory()
|
|
856
|
+
self._patch_get_object_store()
|
|
857
|
+
self._patch_get_auth_provider()
|
|
858
|
+
self._patch_get_function()
|
|
859
|
+
|
|
860
|
+
def _patch_get_llm(self):
|
|
861
|
+
"""Patch builder.get_llm() for runtime LLM interception."""
|
|
862
|
+
if not hasattr(self._builder, 'get_llm'):
|
|
863
|
+
raise RuntimeError("Builder does not have 'get_llm' method. Cannot patch LLM creation.")
|
|
864
|
+
|
|
865
|
+
self._builder_get_llm = self._builder.get_llm
|
|
866
|
+
self._builder.get_llm = self._discover_and_register_llm
|
|
867
|
+
|
|
868
|
+
def _patch_get_embedder(self):
|
|
869
|
+
"""Patch builder.get_embedder() for runtime embedder interception."""
|
|
870
|
+
if not hasattr(self._builder, 'get_embedder'):
|
|
871
|
+
raise RuntimeError("Builder does not have 'get_embedder' method. Cannot patch embedder creation.")
|
|
872
|
+
|
|
873
|
+
self._builder_get_embedder = self._builder.get_embedder
|
|
874
|
+
self._builder.get_embedder = self._discover_and_register_embedder
|
|
875
|
+
|
|
876
|
+
def _patch_get_retriever(self):
|
|
877
|
+
"""Patch builder.get_retriever() for runtime retriever interception."""
|
|
878
|
+
if not hasattr(self._builder, 'get_retriever'):
|
|
879
|
+
raise RuntimeError("Builder does not have 'get_retriever' method. Cannot patch retriever creation.")
|
|
880
|
+
|
|
881
|
+
self._builder_get_retriever = self._builder.get_retriever
|
|
882
|
+
self._builder.get_retriever = self._discover_and_register_retriever
|
|
883
|
+
|
|
884
|
+
def _patch_get_memory(self):
|
|
885
|
+
"""Patch builder.get_memory_client() for runtime memory provider interception."""
|
|
886
|
+
if not hasattr(self._builder, 'get_memory_client'):
|
|
887
|
+
raise RuntimeError("Builder does not have 'get_memory_client' method. Cannot patch memory creation.")
|
|
888
|
+
|
|
889
|
+
self._builder_get_memory = self._builder.get_memory_client
|
|
890
|
+
self._builder.get_memory_client = self._discover_and_register_memory
|
|
891
|
+
|
|
892
|
+
def _patch_get_object_store(self):
|
|
893
|
+
"""Patch builder.get_object_store_client() for runtime object store interception."""
|
|
894
|
+
if not hasattr(self._builder, 'get_object_store_client'):
|
|
895
|
+
raise RuntimeError("Builder does not have 'get_object_store_client' method. "
|
|
896
|
+
"Cannot patch object store creation.")
|
|
897
|
+
|
|
898
|
+
self._builder_get_object_store = self._builder.get_object_store_client
|
|
899
|
+
self._builder.get_object_store_client = self._discover_and_register_object_store
|
|
900
|
+
|
|
901
|
+
def _patch_get_auth_provider(self):
|
|
902
|
+
"""Patch builder.get_auth_provider() for runtime auth provider interception."""
|
|
903
|
+
if not hasattr(self._builder, 'get_auth_provider'):
|
|
904
|
+
raise RuntimeError("Builder does not have 'get_auth_provider' method. Cannot patch auth provider creation.")
|
|
905
|
+
|
|
906
|
+
self._builder_get_auth_provider = self._builder.get_auth_provider
|
|
907
|
+
self._builder.get_auth_provider = self._discover_and_register_auth_provider
|
|
908
|
+
|
|
909
|
+
def _patch_get_function(self):
|
|
910
|
+
"""Patch builder.get_function() for runtime function interception."""
|
|
911
|
+
if not hasattr(self._builder, 'get_function'):
|
|
912
|
+
raise RuntimeError("Builder does not have 'get_function' method. Cannot patch function retrieval.")
|
|
913
|
+
|
|
914
|
+
self._builder_get_function = self._builder.get_function
|
|
915
|
+
self._builder.get_function = self._discover_and_register_function
|
|
916
|
+
|
|
917
|
+
# ==================== Original Method Getters ====================
|
|
918
|
+
|
|
919
|
+
def _get_builder_get_llm(self):
|
|
920
|
+
"""Return original builder.get_llm method."""
|
|
921
|
+
if self._builder_get_llm is None:
|
|
922
|
+
raise RuntimeError("get_llm has not been patched yet")
|
|
923
|
+
return self._builder_get_llm
|
|
924
|
+
|
|
925
|
+
def _get_builder_get_embedder(self):
|
|
926
|
+
"""Return original builder.get_embedder method."""
|
|
927
|
+
if self._builder_get_embedder is None:
|
|
928
|
+
raise RuntimeError("get_embedder has not been patched yet")
|
|
929
|
+
return self._builder_get_embedder
|
|
930
|
+
|
|
931
|
+
def _get_builder_get_retriever(self):
|
|
932
|
+
"""Return original builder.get_retriever method."""
|
|
933
|
+
if self._builder_get_retriever is None:
|
|
934
|
+
raise RuntimeError("get_retriever has not been patched yet")
|
|
935
|
+
return self._builder_get_retriever
|
|
936
|
+
|
|
937
|
+
def _get_builder_get_memory_client(self):
|
|
938
|
+
"""Return original builder.get_memory_client method."""
|
|
939
|
+
if self._builder_get_memory is None:
|
|
940
|
+
raise RuntimeError("get_memory_client has not been patched yet")
|
|
941
|
+
return self._builder_get_memory
|
|
942
|
+
|
|
943
|
+
def _get_builder_get_object_store(self):
|
|
944
|
+
"""Return original builder.get_object_store_client method."""
|
|
945
|
+
if self._builder_get_object_store is None:
|
|
946
|
+
raise RuntimeError("get_object_store_client has not been patched yet")
|
|
947
|
+
return self._builder_get_object_store
|
|
948
|
+
|
|
949
|
+
def _get_builder_get_auth_provider(self):
|
|
950
|
+
"""Return original builder.get_auth_provider method."""
|
|
951
|
+
if self._builder_get_auth_provider is None:
|
|
952
|
+
raise RuntimeError("get_auth_provider has not been patched yet")
|
|
953
|
+
return self._builder_get_auth_provider
|
|
954
|
+
|
|
955
|
+
def _get_builder_get_function(self):
|
|
956
|
+
"""Return original builder.get_function method."""
|
|
957
|
+
if self._builder_get_function is None:
|
|
958
|
+
raise RuntimeError("get_function has not been patched yet")
|
|
959
|
+
return self._builder_get_function
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
__all__ = ["DynamicFunctionMiddleware"]
|