anyscale 0.26.31__py3-none-any.whl → 0.26.33__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 (53) hide show
  1. anyscale/_private/anyscale_client/anyscale_client.py +15 -0
  2. anyscale/_private/anyscale_client/common.py +12 -1
  3. anyscale/_private/anyscale_client/fake_anyscale_client.py +24 -0
  4. anyscale/_private/docgen/__main__.py +2 -0
  5. anyscale/_private/docgen/models.md +2 -2
  6. anyscale/api.py +22 -0
  7. anyscale/aws_iam_policies.py +0 -3
  8. anyscale/client/README.md +22 -1
  9. anyscale/client/openapi_client/__init__.py +17 -0
  10. anyscale/client/openapi_client/api/default_api.py +611 -157
  11. anyscale/client/openapi_client/models/__init__.py +17 -0
  12. anyscale/client/openapi_client/models/baseimagesenum.py +68 -1
  13. anyscale/client/openapi_client/models/cli_usage_payload.py +440 -0
  14. anyscale/client/openapi_client/models/cluster_operation.py +266 -0
  15. anyscale/client/openapi_client/models/cluster_operation_type.py +101 -0
  16. anyscale/client/openapi_client/models/clusteroperation_response.py +121 -0
  17. anyscale/client/openapi_client/models/commit_ledger_item_type.py +111 -0
  18. anyscale/client/openapi_client/models/commit_ledger_record_v2.py +207 -0
  19. anyscale/client/openapi_client/models/complexity_level.py +101 -0
  20. anyscale/client/openapi_client/models/credit_grant_record_v2.py +181 -0
  21. anyscale/client/openapi_client/models/credit_ledger_item_type.py +104 -0
  22. anyscale/client/openapi_client/models/credit_ledger_record_v2.py +207 -0
  23. anyscale/client/openapi_client/models/credit_record_commit_v2.py +410 -0
  24. anyscale/client/openapi_client/models/credit_record_credit_v2.py +410 -0
  25. anyscale/client/openapi_client/models/credit_type.py +100 -0
  26. anyscale/client/openapi_client/models/credits_v2.py +355 -0
  27. anyscale/client/openapi_client/models/operation_error.py +123 -0
  28. anyscale/client/openapi_client/models/operation_progress.py +123 -0
  29. anyscale/client/openapi_client/models/operation_result.py +150 -0
  30. anyscale/client/openapi_client/models/supportedbaseimagesenum.py +68 -1
  31. anyscale/client/openapi_client/models/workspace_template.py +115 -3
  32. anyscale/client/openapi_client/models/workspace_template_readme.py +59 -3
  33. anyscale/cloud/__init__.py +16 -0
  34. anyscale/cloud/_private/cloud_sdk.py +33 -0
  35. anyscale/cloud/commands.py +35 -0
  36. anyscale/commands/cloud_commands.py +35 -0
  37. anyscale/commands/command_examples.py +6 -0
  38. anyscale/commands/list_util.py +100 -38
  39. anyscale/integrations.py +0 -20
  40. anyscale/scripts.py +1 -0
  41. anyscale/sdk/anyscale_client/models/baseimagesenum.py +68 -1
  42. anyscale/sdk/anyscale_client/models/supportedbaseimagesenum.py +68 -1
  43. anyscale/shared_anyscale_utils/headers.py +4 -0
  44. anyscale/shared_anyscale_utils/latest_ray_version.py +1 -1
  45. anyscale/telemetry.py +424 -0
  46. anyscale/version.py +1 -1
  47. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/METADATA +1 -1
  48. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/RECORD +53 -35
  49. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/LICENSE +0 -0
  50. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/NOTICE +0 -0
  51. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/WHEEL +0 -0
  52. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/entry_points.txt +0 -0
  53. {anyscale-0.26.31.dist-info → anyscale-0.26.33.dist-info}/top_level.txt +0 -0
@@ -35,16 +35,20 @@ class WorkspaceTemplateReadme(object):
35
35
  openapi_types = {
36
36
  'content': 'str',
37
37
  'title': 'str',
38
- 'description': 'str'
38
+ 'description': 'str',
39
+ 'icon_type': 'str',
40
+ 'icon_bg_color': 'str'
39
41
  }
40
42
 
41
43
  attribute_map = {
42
44
  'content': 'content',
43
45
  'title': 'title',
44
- 'description': 'description'
46
+ 'description': 'description',
47
+ 'icon_type': 'icon_type',
48
+ 'icon_bg_color': 'icon_bg_color'
45
49
  }
46
50
 
47
- def __init__(self, content=None, title=None, description=None, local_vars_configuration=None): # noqa: E501
51
+ def __init__(self, content=None, title=None, description=None, icon_type=None, icon_bg_color=None, local_vars_configuration=None): # noqa: E501
48
52
  """WorkspaceTemplateReadme - a model defined in OpenAPI""" # noqa: E501
49
53
  if local_vars_configuration is None:
50
54
  local_vars_configuration = Configuration()
@@ -53,11 +57,17 @@ class WorkspaceTemplateReadme(object):
53
57
  self._content = None
54
58
  self._title = None
55
59
  self._description = None
60
+ self._icon_type = None
61
+ self._icon_bg_color = None
56
62
  self.discriminator = None
57
63
 
58
64
  self.content = content
59
65
  self.title = title
60
66
  self.description = description
67
+ if icon_type is not None:
68
+ self.icon_type = icon_type
69
+ if icon_bg_color is not None:
70
+ self.icon_bg_color = icon_bg_color
61
71
 
62
72
  @property
63
73
  def content(self):
@@ -134,6 +144,52 @@ class WorkspaceTemplateReadme(object):
134
144
 
135
145
  self._description = description
136
146
 
147
+ @property
148
+ def icon_type(self):
149
+ """Gets the icon_type of this WorkspaceTemplateReadme. # noqa: E501
150
+
151
+ The remix icon name of the icon for the workspace template # noqa: E501
152
+
153
+ :return: The icon_type of this WorkspaceTemplateReadme. # noqa: E501
154
+ :rtype: str
155
+ """
156
+ return self._icon_type
157
+
158
+ @icon_type.setter
159
+ def icon_type(self, icon_type):
160
+ """Sets the icon_type of this WorkspaceTemplateReadme.
161
+
162
+ The remix icon name of the icon for the workspace template # noqa: E501
163
+
164
+ :param icon_type: The icon_type of this WorkspaceTemplateReadme. # noqa: E501
165
+ :type: str
166
+ """
167
+
168
+ self._icon_type = icon_type
169
+
170
+ @property
171
+ def icon_bg_color(self):
172
+ """Gets the icon_bg_color of this WorkspaceTemplateReadme. # noqa: E501
173
+
174
+ The background color of the icon for the workspace template # noqa: E501
175
+
176
+ :return: The icon_bg_color of this WorkspaceTemplateReadme. # noqa: E501
177
+ :rtype: str
178
+ """
179
+ return self._icon_bg_color
180
+
181
+ @icon_bg_color.setter
182
+ def icon_bg_color(self, icon_bg_color):
183
+ """Sets the icon_bg_color of this WorkspaceTemplateReadme.
184
+
185
+ The background color of the icon for the workspace template # noqa: E501
186
+
187
+ :param icon_bg_color: The icon_bg_color of this WorkspaceTemplateReadme. # noqa: E501
188
+ :type: str
189
+ """
190
+
191
+ self._icon_bg_color = icon_bg_color
192
+
137
193
  def to_dict(self):
138
194
  """Returns the model properties as a dict"""
139
195
  result = {}
@@ -14,9 +14,12 @@ from anyscale.cloud.commands import (
14
14
  _GET_ARG_DOCSTRINGS,
15
15
  _GET_DEFAULT_EXAMPLE,
16
16
  _GET_EXAMPLE,
17
+ _TERMINATE_SYSTEM_CLUSTER_ARG_DOCSTRINGS,
18
+ _TERMINATE_SYSTEM_CLUSTER_EXAMPLE,
17
19
  add_collaborators,
18
20
  get,
19
21
  get_default,
22
+ terminate_system_cluster,
20
23
  )
21
24
  from anyscale.cloud.models import Cloud, CreateCloudCollaborator
22
25
  from anyscale.connect import ClientBuilder
@@ -73,6 +76,18 @@ class CloudSDK:
73
76
  """
74
77
  return self._private_sdk.get_default()
75
78
 
79
+ @sdk_docs(
80
+ doc_py_example=_TERMINATE_SYSTEM_CLUSTER_EXAMPLE,
81
+ arg_docstrings=_TERMINATE_SYSTEM_CLUSTER_ARG_DOCSTRINGS,
82
+ )
83
+ def terminate_system_cluster(self, cloud_id: str, wait: bool = False) -> str:
84
+ """
85
+ Terminate the system cluster for a given cloud.
86
+
87
+ :return: ID of the terminated system cluster.
88
+ """
89
+ return self._private_sdk.terminate_system_cluster(cloud_id, wait)
90
+
76
91
 
77
92
  # Note: indentation here matches that of connect.py::ClientBuilder.
78
93
  BUILDER_HELP_FOOTER = """
@@ -94,6 +109,7 @@ class CloudModule(ModuleType):
94
109
  self.add_collaborators = add_collaborators
95
110
  self.get = get
96
111
  self.get_default = get_default
112
+ self.terminate_system_cluster = terminate_system_cluster
97
113
 
98
114
  # Expose Anyscale connect
99
115
  self.new_builder = self._new_builder()
@@ -1,6 +1,7 @@
1
1
  from typing import List, Optional
2
2
 
3
3
  from anyscale._private.sdk.base_sdk import BaseSDK
4
+ from anyscale.cli_logger import BlockLogger
4
5
  from anyscale.client.openapi_client.models import (
5
6
  Cloud as CloudModel,
6
7
  CreateCloudCollaborator as CreateCloudCollaboratorModel,
@@ -11,6 +12,10 @@ from anyscale.cloud.models import (
11
12
  ComputeStack,
12
13
  CreateCloudCollaborator,
13
14
  )
15
+ from anyscale.sdk.anyscale_client.models import ClusterState
16
+
17
+
18
+ logger = BlockLogger()
14
19
 
15
20
 
16
21
  class PrivateCloudSDK(BaseSDK):
@@ -80,3 +85,31 @@ class PrivateCloudSDK(BaseSDK):
80
85
  is_default=openapi_cloud.is_default,
81
86
  compute_stack=compute_stack,
82
87
  )
88
+
89
+ def terminate_system_cluster(self, cloud_id: str, wait: bool) -> str:
90
+ resp = self.client.terminate_system_cluster(cloud_id)
91
+ if wait:
92
+ self._wait_for_system_cluster_status(cloud_id, ClusterState.TERMINATED)
93
+ else:
94
+ logger.info(f"System cluster termination initiated for cloud {cloud_id}.")
95
+ return resp.result.cluster_id
96
+
97
+ def _wait_for_system_cluster_status(
98
+ self,
99
+ cloud_id: str,
100
+ goal_status: str,
101
+ timeout_s: int = 500,
102
+ interval_s: int = 10,
103
+ ) -> bool:
104
+ self.logger.info("Waiting for system cluster termination...", end="")
105
+ for _ in self.timer.poll(timeout_s=timeout_s, interval_s=interval_s):
106
+ status = self.client.describe_system_workload_get_status(cloud_id)
107
+ if status == goal_status:
108
+ print(".")
109
+ self.logger.info(f"System cluster for cloud '{cloud_id}' is {status}.")
110
+ return True
111
+ else:
112
+ print(".", end="")
113
+ raise TimeoutError(
114
+ f"Timed out waiting for system cluster termination for cloud '{cloud_id}'. Last seen status: {status}."
115
+ )
@@ -114,3 +114,38 @@ def get_default(*, _private_sdk: Optional[PrivateCloudSDK] = None) -> Optional[C
114
114
  :return: The default `Cloud` object if it exists, otherwise `None`.
115
115
  """
116
116
  return _private_sdk.get_default() # type: ignore
117
+
118
+
119
+ _TERMINATE_SYSTEM_CLUSTER_EXAMPLE = """
120
+ import anyscale
121
+
122
+ # Terminate the system cluster for the cloud with the specified ID
123
+ anyscale.cloud.terminate_system_cluster(cloud_id="cloud_id", wait=True)
124
+ """
125
+
126
+ _TERMINATE_SYSTEM_CLUSTER_ARG_DOCSTRINGS = {
127
+ "cloud_id": "The ID of the cloud whose system cluster should be terminated.",
128
+ "wait": "If True, wait for the system cluster to be terminated before returning. Defaults to False.",
129
+ }
130
+
131
+
132
+ @sdk_command(
133
+ _CLOUD_SDK_SINGLETON_KEY,
134
+ PrivateCloudSDK,
135
+ doc_py_example=_TERMINATE_SYSTEM_CLUSTER_EXAMPLE,
136
+ arg_docstrings=_TERMINATE_SYSTEM_CLUSTER_ARG_DOCSTRINGS,
137
+ )
138
+ def terminate_system_cluster(
139
+ cloud_id: str,
140
+ wait: Optional[bool] = False,
141
+ *,
142
+ _private_sdk: Optional[PrivateCloudSDK] = None,
143
+ ) -> str:
144
+ """
145
+ Terminate the system cluster for the specified cloud.
146
+
147
+ :param cloud: The name of the cloud whose system cluster should be terminated.
148
+ :param wait: If True, wait for the system cluster to be terminated before returning. Defaults to False.
149
+ :return: ID of the terminated system cluster.
150
+ """
151
+ return _private_sdk.terminate_system_cluster(cloud_id, wait) # type: ignore
@@ -1269,3 +1269,38 @@ def generate_jobs_report(
1269
1269
  )
1270
1270
  except ValueError as e:
1271
1271
  log.error(f"Error generating jobs report: {e}")
1272
+
1273
+
1274
+ @cloud_cli.command(
1275
+ name="terminate-system-cluster",
1276
+ help="Terminate the system cluster for a specific given cloud.",
1277
+ cls=AnyscaleCommand,
1278
+ example=command_examples.CLOUD_TERMINATE_SYSTEM_CLUSTER_EXAMPLE,
1279
+ )
1280
+ @click.option(
1281
+ "--cloud-id",
1282
+ "--id",
1283
+ help="ID of the cloud to terminate the system cluster for.",
1284
+ type=str,
1285
+ required=True,
1286
+ )
1287
+ @click.option(
1288
+ "-w",
1289
+ "--wait",
1290
+ required=False,
1291
+ default=False,
1292
+ type=bool,
1293
+ is_flag=True,
1294
+ help="Block this CLI command and print logs until the job finishes.",
1295
+ )
1296
+ def terminate_system_cluster(cloud_id: str, wait: Optional[bool]) -> None:
1297
+ """
1298
+ Terminate the system cluster for a specific cloud.
1299
+
1300
+ :param cloud_id: The ID of the cloud to terminate the system cluster for.
1301
+ :param wait: If True, wait for the system cluster to be terminated before returning. Defaults to False.
1302
+ """
1303
+ try:
1304
+ anyscale.cloud.terminate_system_cluster(cloud_id, wait)
1305
+ except ValueError as e:
1306
+ log.error(f"Error terminating system cluster: {e}")
@@ -673,6 +673,12 @@ is_default: true
673
673
  compute_stack: VM
674
674
  """
675
675
 
676
+ CLOUD_TERMINATE_SYSTEM_CLUSTER_EXAMPLE = """\
677
+ $ anyscale cloud terminate-system-cluster --cloud-id cloud_id --wait
678
+ (anyscale +1.3s) Waiting for system cluster termination............
679
+ (anyscale +1m22.9s) System cluster for cloud 'cloud_id' is Terminated.
680
+ """
681
+
676
682
  SERVICE_ARCHIVE_EXAMPLE = """\
677
683
  $ anyscale service archive --name my_service
678
684
  """
@@ -32,7 +32,47 @@ def _paginate(iterator: Iterator[Any], page_size: Optional[int]) -> Iterator[Lis
32
32
  yield page
33
33
 
34
34
 
35
- def display_list( # noqa: PLR0913
35
+ def _render_page(
36
+ page: List[Any],
37
+ item_formatter: Callable[[Any], Dict[str, Any]],
38
+ table_creator: Callable[[bool], Table],
39
+ json_output: bool,
40
+ is_first: bool,
41
+ page_num: int,
42
+ console: Console,
43
+ ) -> int:
44
+ """Render a single page of items."""
45
+ if page_num > 1: # Only show page number for pages after first
46
+ console.print(f"[dim]Page {page_num}[/dim]")
47
+
48
+ rows = [item_formatter(item) for item in page]
49
+ if json_output:
50
+ json_str = json_dumps(rows, indent=2, cls=AnyscaleJSONEncoder)
51
+ console.print_json(json=json_str)
52
+ else:
53
+ tbl = table_creator(is_first)
54
+ for row in rows:
55
+ tbl.add_row(*row.values())
56
+ console.print(tbl)
57
+
58
+ return len(page)
59
+
60
+
61
+ def _should_continue_pagination(
62
+ page_size: int, current_page_size: int, console: Console
63
+ ) -> bool:
64
+ """Prompt user to continue pagination if needed."""
65
+ if current_page_size < page_size:
66
+ return False # Last page, no need to prompt
67
+
68
+ console.print()
69
+ console.print(
70
+ "[dim]Press [bold]Enter[/bold] to continue, [bold]q[/bold] to quit…[/]"
71
+ )
72
+ return input("> ").strip().lower() != "q"
73
+
74
+
75
+ def display_list( # noqa: PLR0913, PLR0912
36
76
  iterator: Iterator[Any],
37
77
  item_formatter: Callable[[Any], Dict[str, Any]],
38
78
  table_creator: Callable[[bool], Table],
@@ -65,56 +105,78 @@ def display_list( # noqa: PLR0913
65
105
  total_count = 0
66
106
  pages = _paginate(iterator, page_size if interactive else max_items)
67
107
 
68
- # fetch first page under spinner
108
+ # Start interactive session if needed
109
+ if interactive:
110
+ try:
111
+ from anyscale.telemetry import start_interactive_session
112
+
113
+ start_interactive_session()
114
+ except Exception: # noqa: BLE001
115
+ pass
116
+
117
+ # Fetch and render first page
69
118
  with console.status("Retrieving items…", spinner="dots"):
70
119
  try:
71
120
  first_page = next(pages)
72
121
  except StopIteration:
73
122
  first_page = []
74
123
 
75
- def _render(page: List[Any], is_first: bool, page_num: int):
76
- nonlocal total_count
77
- total_count += len(page)
78
- if interactive:
79
- console.print(f"[dim]Page {page_num}[/dim]")
80
- rows = [item_formatter(item) for item in page]
81
- if json_output:
82
- json_str = json_dumps(rows, indent=2, cls=AnyscaleJSONEncoder)
83
- console.print_json(json=json_str)
84
- else:
85
- tbl = table_creator(is_first)
86
- for row in rows:
87
- tbl.add_row(*row.values())
88
- console.print(tbl)
89
-
90
- # render first page
91
124
  if first_page:
92
- _render(first_page, True, page_num=1)
125
+ total_count += _render_page(
126
+ first_page, item_formatter, table_creator, json_output, True, 1, console
127
+ )
93
128
 
94
- # non-interactive: stop after first page
129
+ # For interactive commands, mark when command logic completes
130
+ if interactive:
131
+ try:
132
+ from anyscale.telemetry import mark_command_complete
133
+
134
+ mark_command_complete()
135
+ except Exception: # noqa: BLE001
136
+ pass
137
+
138
+ # Non-interactive: stop after first page
95
139
  if not interactive:
96
140
  return total_count
97
141
 
98
- # interactive: prompt after full first page
99
- if len(first_page) == page_size:
100
- console.print()
101
- console.print(
102
- "[dim]Press [bold]Enter[/bold] to continue, [bold]q[/bold] to quit…[/]"
103
- )
104
- if input("> ").strip().lower() == "q":
105
- return total_count
142
+ # Interactive: check if user wants to continue
143
+ if not _should_continue_pagination(page_size, len(first_page), console):
144
+ return total_count
106
145
 
107
- # render remaining pages
146
+ # Render remaining pages with correct telemetry timing
108
147
  page_num = 2
109
- for page in pages:
110
- _render(page, False, page_num)
111
- if len(page) == page_size:
112
- console.print()
113
- console.print(
114
- "[dim]Press [bold]Enter[/bold] to continue, [bold]q[/bold] to quit…[/]"
115
- )
116
- if input("> ").strip().lower() == "q":
117
- break
148
+ while True:
149
+ # Start page fetch timing and generate new trace ID BEFORE fetching
150
+ try:
151
+ from anyscale.telemetry import mark_page_fetch_start
152
+
153
+ mark_page_fetch_start(page_num)
154
+ except Exception: # noqa: BLE001
155
+ pass
156
+
157
+ # Now fetch the page (with the new trace ID)
158
+ try:
159
+ page = next(pages)
160
+ except StopIteration:
161
+ break
162
+
163
+ # Render the page
164
+ total_count += _render_page(
165
+ page, item_formatter, table_creator, json_output, False, page_num, console
166
+ )
167
+
168
+ # Complete page fetch telemetry
169
+ try:
170
+ from anyscale.telemetry import mark_page_fetch_complete
171
+
172
+ mark_page_fetch_complete(page_num)
173
+ except Exception: # noqa: BLE001
174
+ pass
175
+
176
+ # Check if user wants to continue or if this was the last page
177
+ if not _should_continue_pagination(page_size, len(page), console):
178
+ break
179
+
118
180
  page_num += 1
119
181
 
120
182
  return total_count
anyscale/integrations.py CHANGED
@@ -20,8 +20,6 @@ WANDB_API_KEY_NAME = "WANDB_API_KEY_NAME" # pragma: allowlist secret
20
20
  WANDB_PROJECT_NAME = "WANDB_PROJECT_NAME"
21
21
  WANDB_GROUP_NAME = "WANDB_GROUP_NAME"
22
22
 
23
- FLAG_WANDB_INTEGRATION_PROTOTYPE = "wandb-integration-prototype"
24
-
25
23
  log = BlockLogger() # Anyscale CLI Logger
26
24
 
27
25
 
@@ -131,13 +129,6 @@ def wandb_setup_api_key_hook() -> Optional[str]:
131
129
  be called by the OSS WandbLoggerCallback. Because this is called
132
130
  before wandb.init(), any other setup can also be done here.
133
131
  """
134
- api_client = get_auth_api_client(log_output=False).api_client
135
- feature_flag_on = api_client.check_is_feature_flag_on_api_v2_userinfo_check_is_feature_flag_on_get(
136
- FLAG_WANDB_INTEGRATION_PROTOTYPE
137
- ).result.is_on
138
- if not feature_flag_on:
139
- return None
140
-
141
132
  protected_api_key = wandb_get_api_key()
142
133
 
143
134
  try:
@@ -171,11 +162,6 @@ def set_wandb_project_group_env_vars():
171
162
  for production jobs, workspaces, and Ray jobs.
172
163
  """
173
164
  api_client = get_auth_api_client(log_output=False).api_client
174
- feature_flag_on = api_client.check_is_feature_flag_on_api_v2_userinfo_check_is_feature_flag_on_get(
175
- FLAG_WANDB_INTEGRATION_PROTOTYPE
176
- ).result.is_on
177
- if not feature_flag_on:
178
- return
179
165
 
180
166
  wandb_project_default = None
181
167
  wandb_group_default = None
@@ -224,12 +210,6 @@ def wandb_send_run_info_hook(run: Any) -> None:
224
210
  api_client = auth_api_client.api_client
225
211
  anyscale_api_client = auth_api_client.anyscale_api_client
226
212
 
227
- feature_flag_on = api_client.check_is_feature_flag_on_api_v2_userinfo_check_is_feature_flag_on_get(
228
- FLAG_WANDB_INTEGRATION_PROTOTYPE
229
- ).result.is_on
230
- if not feature_flag_on:
231
- return
232
-
233
213
  try:
234
214
  import wandb
235
215
  except ImportError:
anyscale/scripts.py CHANGED
@@ -43,6 +43,7 @@ from anyscale.commands.user_commands import user_cli
43
43
  from anyscale.commands.workspace_commands import workspace_cli
44
44
  from anyscale.commands.workspace_commands_v2 import workspace_cli as workspace_cli_v2
45
45
  import anyscale.conf
46
+ import anyscale.telemetry # IMPORTANT: auto-patches click instrumentation on import
46
47
  from anyscale.utils.cli_version_check_util import log_warning_if_version_needs_upgrade
47
48
 
48
49