nvidia-nat 1.3.0.dev2__py3-none-any.whl → 1.3.0rc2__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.
Files changed (250) hide show
  1. aiq/__init__.py +2 -2
  2. nat/agent/base.py +24 -15
  3. nat/agent/dual_node.py +9 -4
  4. nat/agent/prompt_optimizer/prompt.py +68 -0
  5. nat/agent/prompt_optimizer/register.py +149 -0
  6. nat/agent/react_agent/agent.py +79 -47
  7. nat/agent/react_agent/register.py +50 -22
  8. nat/agent/reasoning_agent/reasoning_agent.py +11 -9
  9. nat/agent/register.py +1 -1
  10. nat/agent/rewoo_agent/agent.py +326 -148
  11. nat/agent/rewoo_agent/prompt.py +19 -22
  12. nat/agent/rewoo_agent/register.py +54 -27
  13. nat/agent/tool_calling_agent/agent.py +84 -28
  14. nat/agent/tool_calling_agent/register.py +51 -28
  15. nat/authentication/api_key/api_key_auth_provider.py +2 -2
  16. nat/authentication/credential_validator/bearer_token_validator.py +557 -0
  17. nat/authentication/http_basic_auth/http_basic_auth_provider.py +1 -1
  18. nat/authentication/interfaces.py +5 -2
  19. nat/authentication/oauth2/oauth2_auth_code_flow_provider.py +69 -36
  20. nat/authentication/oauth2/oauth2_resource_server_config.py +124 -0
  21. nat/authentication/register.py +0 -1
  22. nat/builder/builder.py +56 -24
  23. nat/builder/component_utils.py +9 -5
  24. nat/builder/context.py +68 -17
  25. nat/builder/eval_builder.py +16 -11
  26. nat/builder/framework_enum.py +1 -0
  27. nat/builder/front_end.py +1 -1
  28. nat/builder/function.py +378 -8
  29. nat/builder/function_base.py +3 -3
  30. nat/builder/function_info.py +6 -8
  31. nat/builder/user_interaction_manager.py +2 -2
  32. nat/builder/workflow.py +13 -1
  33. nat/builder/workflow_builder.py +281 -76
  34. nat/cli/cli_utils/config_override.py +2 -2
  35. nat/cli/commands/evaluate.py +1 -1
  36. nat/cli/commands/info/info.py +16 -6
  37. nat/cli/commands/info/list_channels.py +1 -1
  38. nat/cli/commands/info/list_components.py +7 -8
  39. nat/cli/commands/mcp/__init__.py +14 -0
  40. nat/cli/commands/mcp/mcp.py +986 -0
  41. nat/cli/commands/object_store/__init__.py +14 -0
  42. nat/cli/commands/object_store/object_store.py +227 -0
  43. nat/cli/commands/optimize.py +90 -0
  44. nat/cli/commands/registry/publish.py +2 -2
  45. nat/cli/commands/registry/pull.py +2 -2
  46. nat/cli/commands/registry/remove.py +2 -2
  47. nat/cli/commands/registry/search.py +15 -17
  48. nat/cli/commands/start.py +16 -5
  49. nat/cli/commands/uninstall.py +1 -1
  50. nat/cli/commands/workflow/templates/config.yml.j2 +14 -13
  51. nat/cli/commands/workflow/templates/pyproject.toml.j2 +4 -1
  52. nat/cli/commands/workflow/templates/register.py.j2 +2 -3
  53. nat/cli/commands/workflow/templates/workflow.py.j2 +35 -21
  54. nat/cli/commands/workflow/workflow_commands.py +62 -22
  55. nat/cli/entrypoint.py +8 -10
  56. nat/cli/main.py +3 -0
  57. nat/cli/register_workflow.py +38 -4
  58. nat/cli/type_registry.py +75 -6
  59. nat/control_flow/__init__.py +0 -0
  60. nat/control_flow/register.py +20 -0
  61. nat/control_flow/router_agent/__init__.py +0 -0
  62. nat/control_flow/router_agent/agent.py +329 -0
  63. nat/control_flow/router_agent/prompt.py +48 -0
  64. nat/control_flow/router_agent/register.py +91 -0
  65. nat/control_flow/sequential_executor.py +166 -0
  66. nat/data_models/agent.py +34 -0
  67. nat/data_models/api_server.py +74 -66
  68. nat/data_models/authentication.py +23 -9
  69. nat/data_models/common.py +1 -1
  70. nat/data_models/component.py +2 -0
  71. nat/data_models/component_ref.py +11 -0
  72. nat/data_models/config.py +41 -17
  73. nat/data_models/dataset_handler.py +1 -1
  74. nat/data_models/discovery_metadata.py +4 -4
  75. nat/data_models/evaluate.py +4 -1
  76. nat/data_models/function.py +34 -0
  77. nat/data_models/function_dependencies.py +14 -6
  78. nat/data_models/gated_field_mixin.py +242 -0
  79. nat/data_models/intermediate_step.py +3 -3
  80. nat/data_models/optimizable.py +119 -0
  81. nat/data_models/optimizer.py +149 -0
  82. nat/data_models/span.py +41 -3
  83. nat/data_models/swe_bench_model.py +1 -1
  84. nat/data_models/temperature_mixin.py +44 -0
  85. nat/data_models/thinking_mixin.py +86 -0
  86. nat/data_models/top_p_mixin.py +44 -0
  87. nat/embedder/nim_embedder.py +1 -1
  88. nat/embedder/openai_embedder.py +1 -1
  89. nat/embedder/register.py +0 -1
  90. nat/eval/config.py +3 -1
  91. nat/eval/dataset_handler/dataset_handler.py +71 -7
  92. nat/eval/evaluate.py +86 -31
  93. nat/eval/evaluator/base_evaluator.py +1 -1
  94. nat/eval/evaluator/evaluator_model.py +13 -0
  95. nat/eval/intermediate_step_adapter.py +1 -1
  96. nat/eval/rag_evaluator/evaluate.py +2 -2
  97. nat/eval/rag_evaluator/register.py +3 -3
  98. nat/eval/register.py +4 -1
  99. nat/eval/remote_workflow.py +3 -3
  100. nat/eval/runtime_evaluator/__init__.py +14 -0
  101. nat/eval/runtime_evaluator/evaluate.py +123 -0
  102. nat/eval/runtime_evaluator/register.py +100 -0
  103. nat/eval/swe_bench_evaluator/evaluate.py +6 -6
  104. nat/eval/trajectory_evaluator/evaluate.py +1 -1
  105. nat/eval/trajectory_evaluator/register.py +1 -1
  106. nat/eval/tunable_rag_evaluator/evaluate.py +4 -7
  107. nat/eval/utils/eval_trace_ctx.py +89 -0
  108. nat/eval/utils/weave_eval.py +18 -9
  109. nat/experimental/decorators/experimental_warning_decorator.py +27 -7
  110. nat/experimental/test_time_compute/functions/plan_select_execute_function.py +7 -3
  111. nat/experimental/test_time_compute/functions/ttc_tool_orchestration_function.py +3 -3
  112. nat/experimental/test_time_compute/functions/ttc_tool_wrapper_function.py +1 -1
  113. nat/experimental/test_time_compute/models/strategy_base.py +5 -4
  114. nat/experimental/test_time_compute/register.py +0 -1
  115. nat/experimental/test_time_compute/selection/llm_based_output_merging_selector.py +1 -3
  116. nat/front_ends/console/authentication_flow_handler.py +82 -30
  117. nat/front_ends/console/console_front_end_plugin.py +8 -5
  118. nat/front_ends/fastapi/auth_flow_handlers/websocket_flow_handler.py +52 -17
  119. nat/front_ends/fastapi/dask_client_mixin.py +65 -0
  120. nat/front_ends/fastapi/fastapi_front_end_config.py +36 -5
  121. nat/front_ends/fastapi/fastapi_front_end_controller.py +4 -4
  122. nat/front_ends/fastapi/fastapi_front_end_plugin.py +135 -4
  123. nat/front_ends/fastapi/fastapi_front_end_plugin_worker.py +452 -282
  124. nat/front_ends/fastapi/job_store.py +518 -99
  125. nat/front_ends/fastapi/main.py +11 -19
  126. nat/front_ends/fastapi/message_handler.py +13 -14
  127. nat/front_ends/fastapi/message_validator.py +19 -19
  128. nat/front_ends/fastapi/response_helpers.py +4 -4
  129. nat/front_ends/fastapi/step_adaptor.py +2 -2
  130. nat/front_ends/fastapi/utils.py +57 -0
  131. nat/front_ends/mcp/introspection_token_verifier.py +73 -0
  132. nat/front_ends/mcp/mcp_front_end_config.py +10 -1
  133. nat/front_ends/mcp/mcp_front_end_plugin.py +45 -13
  134. nat/front_ends/mcp/mcp_front_end_plugin_worker.py +116 -8
  135. nat/front_ends/mcp/tool_converter.py +44 -14
  136. nat/front_ends/register.py +0 -1
  137. nat/front_ends/simple_base/simple_front_end_plugin_base.py +3 -1
  138. nat/llm/aws_bedrock_llm.py +24 -12
  139. nat/llm/azure_openai_llm.py +13 -6
  140. nat/llm/litellm_llm.py +69 -0
  141. nat/llm/nim_llm.py +20 -8
  142. nat/llm/openai_llm.py +14 -6
  143. nat/llm/register.py +4 -1
  144. nat/llm/utils/env_config_value.py +2 -3
  145. nat/llm/utils/thinking.py +215 -0
  146. nat/meta/pypi.md +9 -9
  147. nat/object_store/register.py +0 -1
  148. nat/observability/exporter/base_exporter.py +3 -3
  149. nat/observability/exporter/file_exporter.py +1 -1
  150. nat/observability/exporter/processing_exporter.py +309 -81
  151. nat/observability/exporter/span_exporter.py +35 -15
  152. nat/observability/exporter_manager.py +7 -7
  153. nat/observability/mixin/file_mixin.py +7 -7
  154. nat/observability/mixin/redaction_config_mixin.py +42 -0
  155. nat/observability/mixin/tagging_config_mixin.py +62 -0
  156. nat/observability/mixin/type_introspection_mixin.py +420 -107
  157. nat/observability/processor/batching_processor.py +5 -7
  158. nat/observability/processor/falsy_batch_filter_processor.py +55 -0
  159. nat/observability/processor/processor.py +3 -0
  160. nat/observability/processor/processor_factory.py +70 -0
  161. nat/observability/processor/redaction/__init__.py +24 -0
  162. nat/observability/processor/redaction/contextual_redaction_processor.py +125 -0
  163. nat/observability/processor/redaction/contextual_span_redaction_processor.py +66 -0
  164. nat/observability/processor/redaction/redaction_processor.py +177 -0
  165. nat/observability/processor/redaction/span_header_redaction_processor.py +92 -0
  166. nat/observability/processor/span_tagging_processor.py +68 -0
  167. nat/observability/register.py +6 -4
  168. nat/profiler/calc/calc_runner.py +3 -4
  169. nat/profiler/callbacks/agno_callback_handler.py +1 -1
  170. nat/profiler/callbacks/langchain_callback_handler.py +6 -6
  171. nat/profiler/callbacks/llama_index_callback_handler.py +3 -3
  172. nat/profiler/callbacks/semantic_kernel_callback_handler.py +3 -3
  173. nat/profiler/data_frame_row.py +1 -1
  174. nat/profiler/decorators/framework_wrapper.py +62 -13
  175. nat/profiler/decorators/function_tracking.py +160 -3
  176. nat/profiler/forecasting/models/forecasting_base_model.py +3 -1
  177. nat/profiler/forecasting/models/linear_model.py +1 -1
  178. nat/profiler/forecasting/models/random_forest_regressor.py +1 -1
  179. nat/profiler/inference_optimization/bottleneck_analysis/nested_stack_analysis.py +1 -1
  180. nat/profiler/inference_optimization/bottleneck_analysis/simple_stack_analysis.py +1 -1
  181. nat/profiler/inference_optimization/data_models.py +3 -3
  182. nat/profiler/inference_optimization/experimental/prefix_span_analysis.py +8 -9
  183. nat/profiler/inference_optimization/token_uniqueness.py +1 -1
  184. nat/profiler/parameter_optimization/__init__.py +0 -0
  185. nat/profiler/parameter_optimization/optimizable_utils.py +93 -0
  186. nat/profiler/parameter_optimization/optimizer_runtime.py +67 -0
  187. nat/profiler/parameter_optimization/parameter_optimizer.py +153 -0
  188. nat/profiler/parameter_optimization/parameter_selection.py +107 -0
  189. nat/profiler/parameter_optimization/pareto_visualizer.py +380 -0
  190. nat/profiler/parameter_optimization/prompt_optimizer.py +384 -0
  191. nat/profiler/parameter_optimization/update_helpers.py +66 -0
  192. nat/profiler/profile_runner.py +14 -9
  193. nat/profiler/utils.py +4 -2
  194. nat/registry_handlers/local/local_handler.py +2 -2
  195. nat/registry_handlers/package_utils.py +1 -2
  196. nat/registry_handlers/pypi/pypi_handler.py +23 -26
  197. nat/registry_handlers/register.py +3 -4
  198. nat/registry_handlers/rest/rest_handler.py +12 -13
  199. nat/retriever/milvus/retriever.py +2 -2
  200. nat/retriever/nemo_retriever/retriever.py +1 -1
  201. nat/retriever/register.py +0 -1
  202. nat/runtime/loader.py +2 -2
  203. nat/runtime/runner.py +106 -8
  204. nat/runtime/session.py +69 -8
  205. nat/settings/global_settings.py +16 -5
  206. nat/tool/chat_completion.py +5 -2
  207. nat/tool/code_execution/local_sandbox/local_sandbox_server.py +3 -3
  208. nat/tool/datetime_tools.py +49 -9
  209. nat/tool/document_search.py +2 -2
  210. nat/tool/github_tools.py +450 -0
  211. nat/tool/memory_tools/get_memory_tool.py +1 -1
  212. nat/tool/nvidia_rag.py +1 -1
  213. nat/tool/register.py +2 -9
  214. nat/tool/retriever.py +3 -2
  215. nat/utils/callable_utils.py +70 -0
  216. nat/utils/data_models/schema_validator.py +3 -3
  217. nat/utils/decorators.py +210 -0
  218. nat/utils/exception_handlers/automatic_retries.py +104 -51
  219. nat/utils/exception_handlers/schemas.py +1 -1
  220. nat/utils/io/yaml_tools.py +2 -2
  221. nat/utils/log_levels.py +25 -0
  222. nat/utils/reactive/base/observable_base.py +2 -2
  223. nat/utils/reactive/base/observer_base.py +1 -1
  224. nat/utils/reactive/observable.py +2 -2
  225. nat/utils/reactive/observer.py +4 -4
  226. nat/utils/reactive/subscription.py +1 -1
  227. nat/utils/settings/global_settings.py +6 -8
  228. nat/utils/type_converter.py +4 -3
  229. nat/utils/type_utils.py +9 -5
  230. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/METADATA +42 -18
  231. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/RECORD +238 -196
  232. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/entry_points.txt +1 -0
  233. nat/cli/commands/info/list_mcp.py +0 -304
  234. nat/tool/github_tools/create_github_commit.py +0 -133
  235. nat/tool/github_tools/create_github_issue.py +0 -87
  236. nat/tool/github_tools/create_github_pr.py +0 -106
  237. nat/tool/github_tools/get_github_file.py +0 -106
  238. nat/tool/github_tools/get_github_issue.py +0 -166
  239. nat/tool/github_tools/get_github_pr.py +0 -256
  240. nat/tool/github_tools/update_github_issue.py +0 -100
  241. nat/tool/mcp/exceptions.py +0 -142
  242. nat/tool/mcp/mcp_client.py +0 -255
  243. nat/tool/mcp/mcp_tool.py +0 -96
  244. nat/utils/exception_handlers/mcp.py +0 -211
  245. /nat/{tool/github_tools → agent/prompt_optimizer}/__init__.py +0 -0
  246. /nat/{tool/mcp → authentication/credential_validator}/__init__.py +0 -0
  247. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/WHEEL +0 -0
  248. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/licenses/LICENSE-3rd-party.txt +0 -0
  249. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/licenses/LICENSE.md +0 -0
  250. {nvidia_nat-1.3.0.dev2.dist-info → nvidia_nat-1.3.0rc2.dist-info}/top_level.txt +0 -0
@@ -13,21 +13,37 @@
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
15
 
16
+ import asyncio
16
17
  import logging
17
18
  import os
19
+ import sys
18
20
  import tempfile
19
21
  import typing
20
22
 
21
23
  from nat.builder.front_end import FrontEndBase
24
+ from nat.front_ends.fastapi.dask_client_mixin import DaskClientMixin
22
25
  from nat.front_ends.fastapi.fastapi_front_end_config import FastApiFrontEndConfig
23
26
  from nat.front_ends.fastapi.fastapi_front_end_plugin_worker import FastApiFrontEndPluginWorkerBase
24
27
  from nat.front_ends.fastapi.main import get_app
28
+ from nat.front_ends.fastapi.utils import get_class_name
25
29
  from nat.utils.io.yaml_tools import yaml_dump
30
+ from nat.utils.log_levels import LOG_LEVELS
31
+
32
+ if (typing.TYPE_CHECKING):
33
+ from nat.data_models.config import Config
26
34
 
27
35
  logger = logging.getLogger(__name__)
28
36
 
29
37
 
30
- class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
38
+ class FastApiFrontEndPlugin(DaskClientMixin, FrontEndBase[FastApiFrontEndConfig]):
39
+
40
+ def __init__(self, full_config: "Config"):
41
+ super().__init__(full_config)
42
+
43
+ # This attribute is set if dask is installed, and an external cluster is not used (scheduler_address is None)
44
+ self._cluster = None
45
+ self._periodic_cleanup_future = None
46
+ self._scheduler_address = None
31
47
 
32
48
  def get_worker_class(self) -> type[FastApiFrontEndPluginWorkerBase]:
33
49
  from nat.front_ends.fastapi.fastapi_front_end_plugin_worker import FastApiFrontEndPluginWorker
@@ -42,7 +58,45 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
42
58
 
43
59
  worker_class = self.get_worker_class()
44
60
 
45
- return f"{worker_class.__module__}.{worker_class.__qualname__}"
61
+ return get_class_name(worker_class)
62
+
63
+ @staticmethod
64
+ async def _periodic_cleanup(scheduler_address: str,
65
+ db_url: str,
66
+ sleep_time_sec: int = 300,
67
+ log_level: int = logging.INFO):
68
+ from nat.front_ends.fastapi.job_store import JobStore
69
+
70
+ job_store = JobStore(scheduler_address=scheduler_address, db_url=db_url)
71
+
72
+ logging.basicConfig(level=log_level)
73
+ logger.info("Starting periodic cleanup of expired jobs every %d seconds", sleep_time_sec)
74
+ while True:
75
+ await asyncio.sleep(sleep_time_sec)
76
+
77
+ try:
78
+ await job_store.cleanup_expired_jobs()
79
+ logger.debug("Expired jobs cleaned up")
80
+ except: # noqa: E722
81
+ logger.exception("Error during job cleanup")
82
+
83
+ async def _submit_cleanup_task(self, scheduler_address: str, db_url: str, log_level: int = logging.INFO):
84
+ """Submit a cleanup task to the cluster to remove the job after expiry."""
85
+ logger.debug("Submitting periodic cleanup task to Dask cluster at %s", scheduler_address)
86
+ async with self.client(self._scheduler_address) as client:
87
+ self._periodic_cleanup_future = client.submit(self._periodic_cleanup,
88
+ scheduler_address=self._scheduler_address,
89
+ db_url=db_url,
90
+ log_level=log_level)
91
+
92
+ @staticmethod
93
+ def _setup_worker():
94
+ """
95
+ Setup function to be run in each worker process. This moves each worker into it's own process group.
96
+ This fixes an issue where a Ctrl-C in the terminal sends a SIGINT to all workers, which then causes the
97
+ workers to exit before the main process can shutdown the cluster gracefully.
98
+ """
99
+ os.setsid()
46
100
 
47
101
  async def run(self):
48
102
 
@@ -52,6 +106,59 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
52
106
  # Get as dict
53
107
  config_dict = self.full_config.model_dump(mode="json", by_alias=True, round_trip=True)
54
108
 
109
+ # Three possible cases:
110
+ # 1. Dask is installed and scheduler_address is None, we create a LocalCluster
111
+ # 2. Dask is installed and scheduler_address is set, we use the existing cluster
112
+ # 3. Dask is not installed, we skip the cluster setup
113
+ dask_log_level = LOG_LEVELS.get(self.front_end_config.dask_log_level.upper(), logging.WARNING)
114
+ dask_logger = logging.getLogger("distributed")
115
+ dask_logger.setLevel(dask_log_level)
116
+
117
+ self._scheduler_address = self.front_end_config.scheduler_address
118
+ if self._scheduler_address is None:
119
+ try:
120
+
121
+ from dask.distributed import LocalCluster
122
+
123
+ self._cluster = LocalCluster(processes=True,
124
+ silence_logs=dask_log_level,
125
+ n_workers=self.front_end_config.max_running_async_jobs,
126
+ threads_per_worker=1)
127
+
128
+ self._scheduler_address = self._cluster.scheduler.address
129
+
130
+ with self.blocking_client(self._scheduler_address) as client:
131
+ # Client.run submits a function to be run on each worker
132
+ client.run(self._setup_worker)
133
+
134
+ logger.info("Created local Dask cluster with scheduler at %s", self._scheduler_address)
135
+
136
+ except ImportError:
137
+ logger.warning("Dask is not installed, async execution and evaluation will not be available.")
138
+
139
+ if self._scheduler_address is not None:
140
+ # If we are here then either the user provided a scheduler address, or we created a LocalCluster
141
+
142
+ from nat.front_ends.fastapi.job_store import Base
143
+ from nat.front_ends.fastapi.job_store import get_db_engine
144
+
145
+ db_engine = get_db_engine(self.front_end_config.db_url, use_async=True)
146
+ async with db_engine.begin() as conn:
147
+ await conn.run_sync(Base.metadata.create_all, checkfirst=True) # create tables if they do not exist
148
+
149
+ # If self.front_end_config.db_url is None, then we need to get the actual url from the engine
150
+ db_url = str(db_engine.url)
151
+ await self._submit_cleanup_task(scheduler_address=self._scheduler_address,
152
+ db_url=db_url,
153
+ log_level=dask_log_level)
154
+
155
+ # Set environment variabls such that the worker subprocesses will know how to connect to dask and to
156
+ # the database
157
+ os.environ.update({
158
+ "NAT_DASK_SCHEDULER_ADDRESS": self._scheduler_address,
159
+ "NAT_JOB_STORE_DB_URL": db_url,
160
+ })
161
+
55
162
  # Write to YAML file
56
163
  yaml_dump(config_dict, config_file)
57
164
 
@@ -70,13 +177,25 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
70
177
 
71
178
  reload_excludes = ["./.*"]
72
179
 
180
+ # By default, Uvicorn uses "auto" event loop policy, which prefers `uvloop` if installed. However,
181
+ # uvloop’s event loop policy for macOS doesn’t provide a child watcher (which is needed for MCP server),
182
+ # so setting loop="asyncio" forces Uvicorn to use the standard event loop, which includes child-watcher
183
+ # support.
184
+ if sys.platform == "darwin" or sys.platform.startswith("linux"):
185
+ # For macOS
186
+ event_loop_policy = "asyncio"
187
+ else:
188
+ # For non-macOS platforms
189
+ event_loop_policy = "auto"
190
+
73
191
  uvicorn.run("nat.front_ends.fastapi.main:get_app",
74
192
  host=self.front_end_config.host,
75
193
  port=self.front_end_config.port,
76
194
  workers=self.front_end_config.workers,
77
195
  reload=self.front_end_config.reload,
78
196
  factory=True,
79
- reload_excludes=reload_excludes)
197
+ reload_excludes=reload_excludes,
198
+ loop=event_loop_policy)
80
199
 
81
200
  else:
82
201
  app = get_app()
@@ -110,7 +229,19 @@ class FastApiFrontEndPlugin(FrontEndBase[FastApiFrontEndConfig]):
110
229
  StandaloneApplication(app, options=options).run()
111
230
 
112
231
  finally:
232
+ logger.debug("Shutting down")
233
+ if self._periodic_cleanup_future is not None:
234
+ logger.info("Cancelling periodic cleanup task.")
235
+ # Use the scheduler address, because self._cluster is None if an external cluster is used
236
+ async with self.client(self._scheduler_address) as client:
237
+ await client.cancel([self._periodic_cleanup_future], asynchronous=True, force=True)
238
+
239
+ if self._cluster is not None:
240
+ # Only shut down the cluster if we created it
241
+ logger.debug("Closing Local Dask cluster.")
242
+ self._cluster.close()
243
+
113
244
  try:
114
245
  os.remove(config_file_name)
115
246
  except OSError as e:
116
- logger.error(f"Warning: Failed to delete temp file {config_file_name}: {e}")
247
+ logger.exception(f"Warning: Failed to delete temp file {config_file_name}: {e}")