zenml-nightly 0.68.0.dev20241027__py3-none-any.whl → 0.68.1.dev20241101__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 (125) hide show
  1. README.md +17 -11
  2. RELEASE_NOTES.md +9 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +1 -1
  5. zenml/analytics/context.py +16 -1
  6. zenml/analytics/utils.py +18 -7
  7. zenml/artifacts/utils.py +40 -216
  8. zenml/cli/__init__.py +63 -90
  9. zenml/cli/base.py +3 -3
  10. zenml/cli/login.py +951 -0
  11. zenml/cli/server.py +462 -353
  12. zenml/cli/service_accounts.py +4 -4
  13. zenml/cli/stack.py +77 -2
  14. zenml/cli/stack_components.py +5 -16
  15. zenml/cli/user_management.py +0 -12
  16. zenml/cli/utils.py +24 -77
  17. zenml/client.py +46 -14
  18. zenml/config/compiler.py +1 -0
  19. zenml/config/global_config.py +9 -0
  20. zenml/config/pipeline_configurations.py +2 -1
  21. zenml/config/pipeline_run_configuration.py +2 -1
  22. zenml/constants.py +3 -9
  23. zenml/enums.py +1 -1
  24. zenml/exceptions.py +11 -0
  25. zenml/integrations/github/code_repositories/github_code_repository.py +1 -1
  26. zenml/login/__init__.py +16 -0
  27. zenml/login/credentials.py +346 -0
  28. zenml/login/credentials_store.py +603 -0
  29. zenml/login/pro/__init__.py +16 -0
  30. zenml/login/pro/client.py +496 -0
  31. zenml/login/pro/constants.py +34 -0
  32. zenml/login/pro/models.py +25 -0
  33. zenml/login/pro/organization/__init__.py +14 -0
  34. zenml/login/pro/organization/client.py +79 -0
  35. zenml/login/pro/organization/models.py +32 -0
  36. zenml/login/pro/tenant/__init__.py +14 -0
  37. zenml/login/pro/tenant/client.py +92 -0
  38. zenml/login/pro/tenant/models.py +174 -0
  39. zenml/login/pro/utils.py +121 -0
  40. zenml/{cli → login}/web_login.py +64 -28
  41. zenml/materializers/base_materializer.py +43 -9
  42. zenml/materializers/built_in_materializer.py +1 -1
  43. zenml/metadata/metadata_types.py +49 -0
  44. zenml/model/model.py +0 -38
  45. zenml/models/__init__.py +3 -0
  46. zenml/models/v2/base/base.py +12 -8
  47. zenml/models/v2/base/filter.py +9 -0
  48. zenml/models/v2/core/artifact_version.py +49 -10
  49. zenml/models/v2/core/component.py +54 -19
  50. zenml/models/v2/core/flavor.py +13 -13
  51. zenml/models/v2/core/model.py +3 -1
  52. zenml/models/v2/core/model_version.py +3 -5
  53. zenml/models/v2/core/model_version_artifact.py +3 -1
  54. zenml/models/v2/core/model_version_pipeline_run.py +3 -1
  55. zenml/models/v2/core/pipeline.py +3 -1
  56. zenml/models/v2/core/pipeline_run.py +23 -1
  57. zenml/models/v2/core/run_template.py +3 -1
  58. zenml/models/v2/core/stack.py +7 -3
  59. zenml/models/v2/core/step_run.py +43 -2
  60. zenml/models/v2/misc/auth_models.py +11 -2
  61. zenml/models/v2/misc/server_models.py +2 -0
  62. zenml/orchestrators/base_orchestrator.py +8 -4
  63. zenml/orchestrators/step_launcher.py +1 -0
  64. zenml/orchestrators/step_run_utils.py +10 -2
  65. zenml/orchestrators/step_runner.py +67 -55
  66. zenml/orchestrators/utils.py +45 -22
  67. zenml/pipelines/pipeline_decorator.py +5 -0
  68. zenml/pipelines/pipeline_definition.py +206 -160
  69. zenml/pipelines/run_utils.py +11 -10
  70. zenml/services/local/local_daemon_entrypoint.py +4 -4
  71. zenml/services/service.py +2 -2
  72. zenml/stack/stack.py +2 -6
  73. zenml/stack/stack_component.py +2 -7
  74. zenml/stack/utils.py +26 -14
  75. zenml/steps/base_step.py +8 -2
  76. zenml/steps/step_context.py +0 -3
  77. zenml/steps/step_invocation.py +14 -5
  78. zenml/steps/utils.py +1 -0
  79. zenml/utils/materializer_utils.py +1 -1
  80. zenml/utils/requirements_utils.py +71 -0
  81. zenml/utils/singleton.py +15 -3
  82. zenml/utils/source_utils.py +39 -2
  83. zenml/utils/visualization_utils.py +1 -1
  84. zenml/zen_server/auth.py +44 -39
  85. zenml/zen_server/deploy/__init__.py +7 -7
  86. zenml/zen_server/deploy/base_provider.py +46 -73
  87. zenml/zen_server/deploy/{local → daemon}/__init__.py +3 -3
  88. zenml/zen_server/deploy/{local/local_provider.py → daemon/daemon_provider.py} +44 -63
  89. zenml/zen_server/deploy/{local/local_zen_server.py → daemon/daemon_zen_server.py} +50 -22
  90. zenml/zen_server/deploy/deployer.py +90 -171
  91. zenml/zen_server/deploy/deployment.py +20 -12
  92. zenml/zen_server/deploy/docker/docker_provider.py +9 -28
  93. zenml/zen_server/deploy/docker/docker_zen_server.py +19 -3
  94. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  95. zenml/zen_server/deploy/helm/README.md +2 -2
  96. zenml/zen_server/exceptions.py +11 -0
  97. zenml/zen_server/jwt.py +9 -9
  98. zenml/zen_server/routers/auth_endpoints.py +30 -8
  99. zenml/zen_server/routers/stack_components_endpoints.py +1 -1
  100. zenml/zen_server/routers/workspaces_endpoints.py +1 -1
  101. zenml/zen_server/template_execution/runner_entrypoint_configuration.py +7 -4
  102. zenml/zen_server/template_execution/utils.py +6 -61
  103. zenml/zen_server/utils.py +64 -36
  104. zenml/zen_stores/base_zen_store.py +4 -49
  105. zenml/zen_stores/migrations/versions/0.68.1_release.py +23 -0
  106. zenml/zen_stores/migrations/versions/c22561cbb3a9_add_artifact_unique_constraints.py +86 -0
  107. zenml/zen_stores/rest_zen_store.py +325 -147
  108. zenml/zen_stores/schemas/api_key_schemas.py +9 -4
  109. zenml/zen_stores/schemas/artifact_schemas.py +21 -2
  110. zenml/zen_stores/schemas/artifact_visualization_schemas.py +1 -1
  111. zenml/zen_stores/schemas/component_schemas.py +49 -6
  112. zenml/zen_stores/schemas/device_schemas.py +9 -4
  113. zenml/zen_stores/schemas/flavor_schemas.py +1 -1
  114. zenml/zen_stores/schemas/model_schemas.py +1 -1
  115. zenml/zen_stores/schemas/service_schemas.py +1 -1
  116. zenml/zen_stores/schemas/step_run_schemas.py +1 -1
  117. zenml/zen_stores/schemas/trigger_schemas.py +1 -1
  118. zenml/zen_stores/sql_zen_store.py +393 -140
  119. zenml/zen_stores/template_utils.py +3 -1
  120. {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/METADATA +18 -12
  121. {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/RECORD +124 -107
  122. zenml/api.py +0 -60
  123. {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/LICENSE +0 -0
  124. {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/WHEEL +0 -0
  125. {zenml_nightly-0.68.0.dev20241027.dist-info → zenml_nightly-0.68.1.dev20241101.dist-info}/entry_points.txt +0 -0
@@ -86,8 +86,8 @@ def _create_api_key(
86
86
  f"The API key value is: '{api_key.key}'\nPlease store it safely as "
87
87
  "it will not be shown again.\nTo configure a ZenML client to use "
88
88
  "this API key, run:\n\n"
89
- f"zenml connect --url {zen_store.config.url} --api-key \\\n"
90
- f" '{api_key.key}'\n"
89
+ f"zenml login {zen_store.config.url} --api-key \n\n"
90
+ f"and enter the following API key when prompted: {api_key.key}\n"
91
91
  )
92
92
 
93
93
 
@@ -543,8 +543,8 @@ def rotate_api_key(
543
543
  f"The new API key value is: '{api_key.key}'\nPlease store it "
544
544
  "safely as it will not be shown again.\nTo configure a ZenML "
545
545
  "client to use this API key, run:\n\n"
546
- f"zenml connect --url {zen_store.config.url} --api-key \\\n"
547
- f" '{api_key.key}'\n"
546
+ f"zenml login {zen_store.config.url} --api-key \n\n"
547
+ f"and enter the following API key when prompted: {api_key.key}\n"
548
548
  )
549
549
 
550
550
 
zenml/cli/stack.py CHANGED
@@ -75,6 +75,7 @@ from zenml.models.v2.core.service_connector import (
75
75
  from zenml.service_connectors.service_connector_utils import (
76
76
  get_resources_options_from_resource_model_for_full_stack,
77
77
  )
78
+ from zenml.utils import requirements_utils
78
79
  from zenml.utils.dashboard_utils import get_component_url, get_stack_url
79
80
  from zenml.utils.yaml_utils import read_yaml, write_yaml
80
81
 
@@ -428,7 +429,7 @@ def register_stack(
428
429
  object_type=component_type.value.replace("_", " "),
429
430
  choices=[
430
431
  [
431
- component.flavor,
432
+ component.flavor_name,
432
433
  component.name,
433
434
  component.configuration or "",
434
435
  component.connector_resource_id,
@@ -1624,7 +1625,7 @@ Stack [{deployed_stack.stack.name}]({get_stack_url(deployed_stack.stack)}):\n"""
1624
1625
  if components:
1625
1626
  component = components[0]
1626
1627
  stack_desc += (
1627
- f" * `{component.flavor}` {component_type.value}: "
1628
+ f" * `{component.flavor_name}` {component_type.value}: "
1628
1629
  f"[{component.name}]({get_component_url(component)})\n"
1629
1630
  )
1630
1631
 
@@ -1944,3 +1945,77 @@ def _get_stack_component_info(
1944
1945
  service_connector_index=service_connector_index,
1945
1946
  service_connector_resource_id=service_connector_resource_id,
1946
1947
  )
1948
+
1949
+
1950
+ @stack.command(
1951
+ name="export-requirements", help="Export the stack requirements."
1952
+ )
1953
+ @click.argument(
1954
+ "stack_name_or_id",
1955
+ type=click.STRING,
1956
+ required=False,
1957
+ )
1958
+ @click.option(
1959
+ "--output-file",
1960
+ "-o",
1961
+ "output_file",
1962
+ type=str,
1963
+ required=False,
1964
+ help="File to which to export the stack requirements. If not "
1965
+ "provided, the requirements will be printed to stdout instead.",
1966
+ )
1967
+ @click.option(
1968
+ "--overwrite",
1969
+ "-ov",
1970
+ "overwrite",
1971
+ type=bool,
1972
+ required=False,
1973
+ is_flag=True,
1974
+ help="Overwrite the output file if it already exists. This option is "
1975
+ "only valid if the output file is provided.",
1976
+ )
1977
+ def export_requirements(
1978
+ stack_name_or_id: Optional[str] = None,
1979
+ output_file: Optional[str] = None,
1980
+ overwrite: bool = False,
1981
+ ) -> None:
1982
+ """Exports stack requirements so they can be installed using pip.
1983
+
1984
+ Args:
1985
+ stack_name_or_id: Stack name or ID. If not given, the active stack will
1986
+ be used.
1987
+ output_file: Optional path to the requirements output file.
1988
+ overwrite: Overwrite the output file if it already exists. This option
1989
+ is only valid if the output file is provided.
1990
+ """
1991
+ try:
1992
+ stack_model: "StackResponse" = Client().get_stack(
1993
+ name_id_or_prefix=stack_name_or_id
1994
+ )
1995
+ except KeyError as err:
1996
+ cli_utils.error(str(err))
1997
+
1998
+ requirements, _ = requirements_utils.get_requirements_for_stack(
1999
+ stack_model
2000
+ )
2001
+
2002
+ if not requirements:
2003
+ cli_utils.declare(f"Stack `{stack_model.name}` has no requirements.")
2004
+ return
2005
+
2006
+ if output_file:
2007
+ try:
2008
+ with open(output_file, "x") as f:
2009
+ f.write("\n".join(requirements))
2010
+ except FileExistsError:
2011
+ if overwrite or cli_utils.confirmation(
2012
+ "A file already exists at the specified path. "
2013
+ "Would you like to overwrite it?"
2014
+ ):
2015
+ with open(output_file, "w") as f:
2016
+ f.write("\n".join(requirements))
2017
+ cli_utils.declare(
2018
+ f"Requirements for stack `{stack_model.name}` exported to {output_file}."
2019
+ )
2020
+ else:
2021
+ click.echo(" ".join(requirements), nl=False)
@@ -123,10 +123,9 @@ def generate_stack_component_describe_command(
123
123
  active_component_id = active_components[0].id
124
124
 
125
125
  if component_.connector:
126
- # We also need the flavor to get the connector requirements
127
- connector_requirements = client.get_flavor_by_name_and_type(
128
- name=component_.flavor, component_type=component_type
129
- ).connector_requirements
126
+ connector_requirements = (
127
+ component_.flavor.connector_requirements
128
+ )
130
129
  else:
131
130
  connector_requirements = None
132
131
 
@@ -570,7 +569,7 @@ def generate_stack_component_copy_command(
570
569
 
571
570
  copied_component = client.create_stack_component(
572
571
  name=target_component,
573
- flavor=component_to_copy.flavor,
572
+ flavor=component_to_copy.flavor_name,
574
573
  component_type=component_to_copy.type,
575
574
  configuration=component_to_copy.configuration,
576
575
  labels=component_to_copy.labels,
@@ -1366,17 +1365,7 @@ def connect_stack_component_with_service_connector(
1366
1365
  except KeyError as err:
1367
1366
  cli_utils.error(str(err))
1368
1367
 
1369
- try:
1370
- flavor_model = client.get_flavor_by_name_and_type(
1371
- name=component_model.flavor, component_type=component_type
1372
- )
1373
- except KeyError as err:
1374
- cli_utils.error(
1375
- f"Could not find flavor '{component_model.flavor}' for "
1376
- f"{display_name} '{name_id_or_prefix}': {str(err)}"
1377
- )
1378
-
1379
- requirements = flavor_model.connector_requirements
1368
+ requirements = component_model.flavor.connector_requirements
1380
1369
 
1381
1370
  if not requirements:
1382
1371
  cli_utils.error(
@@ -367,18 +367,6 @@ def change_user_password(
367
367
  f"Successfully updated password for active user '{active_user.name}'."
368
368
  )
369
369
 
370
- store = GlobalConfiguration().store_configuration
371
- if store.type == StoreType.REST:
372
- from zenml.zen_stores.rest_zen_store import RestZenStoreConfiguration
373
-
374
- assert isinstance(store, RestZenStoreConfiguration)
375
-
376
- if store.password is not None:
377
- cli_utils.declare(
378
- "You may need to log in again with your new password by "
379
- "running `zenml connect`."
380
- )
381
-
382
370
 
383
371
  @user.command(
384
372
  "deactivate",
zenml/cli/utils.py CHANGED
@@ -79,7 +79,6 @@ from zenml.services import BaseService, ServiceState
79
79
  from zenml.stack import StackComponent
80
80
  from zenml.stack.stack_component import StackComponentConfig
81
81
  from zenml.utils import secret_utils
82
- from zenml.zen_server.deploy import ServerDeployment
83
82
 
84
83
  if TYPE_CHECKING:
85
84
  from uuid import UUID
@@ -223,6 +222,8 @@ def print_table(
223
222
  caption: Caption of the table.
224
223
  columns: Optional column configurations to be used in the table.
225
224
  """
225
+ from rich.text import Text
226
+
226
227
  column_keys = {key: None for dict_ in obj for key in dict_}
227
228
  column_names = [columns.get(key, key.upper()) for key in column_keys]
228
229
  rich_table = table.Table(
@@ -241,10 +242,19 @@ def print_table(
241
242
  if key is None:
242
243
  values.append(None)
243
244
  else:
244
- value = str(dict_.get(key) or " ")
245
- # escape text when square brackets are used
246
- if "[" in value:
247
- value = escape(value)
245
+ v = dict_.get(key) or " "
246
+ if isinstance(v, str) and (
247
+ v.startswith("http://") or v.startswith("https://")
248
+ ):
249
+ # Display the URL as a hyperlink in a way that doesn't break
250
+ # the URL when it needs to be wrapped over multiple lines
251
+ value: Union[str, Text] = Text(v, style=f"link {v}")
252
+ else:
253
+ value = str(v)
254
+ # Escape text when square brackets are used, but allow
255
+ # links to be decorated as rich style links
256
+ if "[" in value and "[link=" not in value:
257
+ value = escape(value)
248
258
  values.append(value)
249
259
  rich_table.add_row(*values)
250
260
  if len(rich_table.columns) > 1:
@@ -258,6 +268,7 @@ def print_pydantic_models(
258
268
  exclude_columns: Optional[List[str]] = None,
259
269
  active_models: Optional[List[T]] = None,
260
270
  show_active: bool = False,
271
+ rename_columns: Dict[str, str] = {},
261
272
  ) -> None:
262
273
  """Prints the list of Pydantic models in a table.
263
274
 
@@ -270,6 +281,7 @@ def print_pydantic_models(
270
281
  active_models: Optional list of active models of the given type T.
271
282
  show_active: Flag to decide whether to append the active model on the
272
283
  top of the list.
284
+ rename_columns: Optional dictionary to rename columns.
273
285
  """
274
286
  if exclude_columns is None:
275
287
  exclude_columns = list()
@@ -326,6 +338,8 @@ def print_pydantic_models(
326
338
 
327
339
  for k in include_columns:
328
340
  value = getattr(model, k)
341
+ if k in rename_columns:
342
+ k = rename_columns[k]
329
343
  # In case the response model contains nested `BaseResponse`s
330
344
  # we want to attempt to represent them by name, if they contain
331
345
  # such a field, else the id is used
@@ -350,6 +364,7 @@ def print_pydantic_models(
350
364
  items[k] = [str(v) for v in value]
351
365
  else:
352
366
  items[k] = str(value)
367
+
353
368
  # prepend an active marker if a function to mark active was passed
354
369
  if not active_models and not show_active:
355
370
  return items
@@ -613,7 +628,7 @@ def print_stack_component_configuration(
613
628
 
614
629
  declare(
615
630
  f"{component.type.value.title()} '{component.name}' of flavor "
616
- f"'{component.flavor}' with id '{component.id}' is owned by "
631
+ f"'{component.flavor_name}' with id '{component.id}' is owned by "
617
632
  f"user '{user_name}'."
618
633
  )
619
634
 
@@ -1391,74 +1406,6 @@ def print_served_model_configuration(
1391
1406
  console.print(rich_table)
1392
1407
 
1393
1408
 
1394
- def print_server_deployment_list(servers: List["ServerDeployment"]) -> None:
1395
- """Print a table with a list of ZenML server deployments.
1396
-
1397
- Args:
1398
- servers: list of ZenML server deployments
1399
- """
1400
- server_dicts = []
1401
- for server in servers:
1402
- status = ""
1403
- url = ""
1404
- connected = ""
1405
- if server.status:
1406
- status = get_service_state_emoji(server.status.status)
1407
- if server.status.url:
1408
- url = server.status.url
1409
- if server.status.connected:
1410
- connected = ":point_left:"
1411
- server_dicts.append(
1412
- {
1413
- "STATUS": status,
1414
- "NAME": server.config.name,
1415
- "PROVIDER": server.config.provider.value,
1416
- "URL": url,
1417
- "CONNECTED": connected,
1418
- }
1419
- )
1420
- print_table(server_dicts)
1421
-
1422
-
1423
- def print_server_deployment(server: "ServerDeployment") -> None:
1424
- """Prints the configuration and status of a ZenML server deployment.
1425
-
1426
- Args:
1427
- server: Server deployment to print
1428
- """
1429
- server_name = server.config.name
1430
- title_ = f"ZenML server '{server_name}'"
1431
-
1432
- rich_table = table.Table(
1433
- box=box.HEAVY_EDGE,
1434
- title=title_,
1435
- show_header=False,
1436
- show_lines=True,
1437
- )
1438
- rich_table.add_column("", overflow="fold")
1439
- rich_table.add_column("", overflow="fold")
1440
-
1441
- server_info = []
1442
-
1443
- if server.status:
1444
- server_info.extend(
1445
- [
1446
- ("URL", server.status.url or ""),
1447
- ("STATUS", get_service_state_emoji(server.status.status)),
1448
- ("STATUS_MESSAGE", server.status.status_message or ""),
1449
- (
1450
- "CONNECTED",
1451
- ":white_check_mark:" if server.status.connected else "",
1452
- ),
1453
- ]
1454
- )
1455
-
1456
- for item in server_info:
1457
- rich_table.add_row(*item)
1458
-
1459
- console.print(rich_table)
1460
-
1461
-
1462
1409
  def describe_pydantic_object(schema_json: Dict[str, Any]) -> None:
1463
1410
  """Describes a Pydantic object based on the dict-representation of its schema.
1464
1411
 
@@ -1632,7 +1579,7 @@ def print_components_table(
1632
1579
  "ACTIVE": ":point_right:" if is_active else "",
1633
1580
  "NAME": component.name,
1634
1581
  "COMPONENT ID": component.id,
1635
- "FLAVOR": component.flavor,
1582
+ "FLAVOR": component.flavor_name,
1636
1583
  "OWNER": f"{component.user.name if component.user else '-'}",
1637
1584
  }
1638
1585
  configurations.append(component_config)
@@ -2719,8 +2666,8 @@ def print_model_url(url: Optional[str]) -> None:
2719
2666
  warning(
2720
2667
  "You can display various ZenML entities including pipelines, "
2721
2668
  "runs, stacks and much more on the ZenML Dashboard. "
2722
- "You can try it locally, by running `zenml up`, or remotely, "
2723
- "by deploying ZenML on the infrastructure of your choice."
2669
+ "You can try it locally, by running `zenml local --local`, or "
2670
+ "remotely, by deploying ZenML on the infrastructure of your choice."
2724
2671
  )
2725
2672
 
2726
2673
 
zenml/client.py CHANGED
@@ -346,6 +346,8 @@ class Client(metaclass=ClientMetaClass):
346
346
  """
347
347
 
348
348
  _active_user: Optional["UserResponse"] = None
349
+ _active_workspace: Optional["WorkspaceResponse"] = None
350
+ _active_stack: Optional["StackResponse"] = None
349
351
 
350
352
  def __init__(
351
353
  self,
@@ -1113,9 +1115,13 @@ class Client(metaclass=ClientMetaClass):
1113
1115
  Raises:
1114
1116
  RuntimeError: If the active workspace is not set.
1115
1117
  """
1116
- if ENV_ZENML_ACTIVE_WORKSPACE_ID in os.environ:
1117
- workspace_id = os.environ[ENV_ZENML_ACTIVE_WORKSPACE_ID]
1118
- return self.get_workspace(workspace_id)
1118
+ if workspace_id := os.environ.get(ENV_ZENML_ACTIVE_WORKSPACE_ID):
1119
+ if not self._active_workspace or self._active_workspace.id != UUID(
1120
+ workspace_id
1121
+ ):
1122
+ self._active_workspace = self.get_workspace(workspace_id)
1123
+
1124
+ return self._active_workspace
1119
1125
 
1120
1126
  from zenml.constants import DEFAULT_WORKSPACE_NAME
1121
1127
 
@@ -1465,8 +1471,13 @@ class Client(metaclass=ClientMetaClass):
1465
1471
  Raises:
1466
1472
  RuntimeError: If the active stack is not set.
1467
1473
  """
1468
- if ENV_ZENML_ACTIVE_STACK_ID in os.environ:
1469
- return self.get_stack(os.environ[ENV_ZENML_ACTIVE_STACK_ID])
1474
+ if env_stack_id := os.environ.get(ENV_ZENML_ACTIVE_STACK_ID):
1475
+ if not self._active_stack or self._active_stack.id != UUID(
1476
+ env_stack_id
1477
+ ):
1478
+ self._active_stack = self.get_stack(env_stack_id)
1479
+
1480
+ return self._active_stack
1470
1481
 
1471
1482
  stack_id: Optional[UUID] = None
1472
1483
 
@@ -1534,6 +1545,8 @@ class Client(metaclass=ClientMetaClass):
1534
1545
  remote_components: List[str] = []
1535
1546
  assert stack.components is not None
1536
1547
  for component_type, components in stack.components.items():
1548
+ component_flavor: Union[FlavorResponse, str]
1549
+
1537
1550
  for component in components:
1538
1551
  if isinstance(component, UUID):
1539
1552
  component_response = self.get_stack_component(
@@ -1554,7 +1567,7 @@ class Client(metaclass=ClientMetaClass):
1554
1567
 
1555
1568
  configuration = validate_stack_component_config(
1556
1569
  configuration_dict=component_config,
1557
- flavor_name=component_flavor,
1570
+ flavor=component_flavor,
1558
1571
  component_type=component_type,
1559
1572
  # Always enforce validation of custom flavors
1560
1573
  validate_custom_flavors=True,
@@ -1563,13 +1576,18 @@ class Client(metaclass=ClientMetaClass):
1563
1576
  # `validate_custom_flavors=True` above
1564
1577
  assert configuration is not None
1565
1578
  warn_if_config_server_mismatch(configuration)
1579
+ flavor_name = (
1580
+ component_flavor.name
1581
+ if isinstance(component_flavor, FlavorResponse)
1582
+ else component_flavor
1583
+ )
1566
1584
  if configuration.is_local:
1567
1585
  local_components.append(
1568
- f"{component_type.value}: {component_flavor}"
1586
+ f"{component_type.value}: {flavor_name}"
1569
1587
  )
1570
1588
  elif configuration.is_remote:
1571
1589
  remote_components.append(
1572
- f"{component_type.value}: {component_flavor}"
1590
+ f"{component_type.value}: {flavor_name}"
1573
1591
  )
1574
1592
 
1575
1593
  if local_components and remote_components:
@@ -1906,6 +1924,7 @@ class Client(metaclass=ClientMetaClass):
1906
1924
  user_id: Optional[Union[str, UUID]] = None,
1907
1925
  connector_id: Optional[Union[str, UUID]] = None,
1908
1926
  stack_id: Optional[Union[str, UUID]] = None,
1927
+ user: Optional[Union[UUID, str]] = None,
1909
1928
  hydrate: bool = False,
1910
1929
  ) -> Page[ComponentResponse]:
1911
1930
  """Lists all registered stack components.
@@ -1925,6 +1944,7 @@ class Client(metaclass=ClientMetaClass):
1925
1944
  connector_id: The id of the connector to filter by.
1926
1945
  stack_id: The id of the stack to filter by.
1927
1946
  name: The name of the component to filter by.
1947
+ user: The ID of name of the user to filter by.
1928
1948
  hydrate: Flag deciding whether to hydrate the output model(s)
1929
1949
  by including metadata fields in the response.
1930
1950
 
@@ -1946,6 +1966,7 @@ class Client(metaclass=ClientMetaClass):
1946
1966
  id=id,
1947
1967
  created=created,
1948
1968
  updated=updated,
1969
+ user=user,
1949
1970
  )
1950
1971
  component_filter_model.set_scope_workspace(self.active_workspace.id)
1951
1972
 
@@ -1982,7 +2003,7 @@ class Client(metaclass=ClientMetaClass):
1982
2003
 
1983
2004
  validated_config = validate_stack_component_config(
1984
2005
  configuration_dict=configuration,
1985
- flavor_name=flavor,
2006
+ flavor=flavor,
1986
2007
  component_type=component_type,
1987
2008
  # Always enforce validation of custom flavors
1988
2009
  validate_custom_flavors=True,
@@ -2083,7 +2104,7 @@ class Client(metaclass=ClientMetaClass):
2083
2104
 
2084
2105
  validated_config = validate_stack_component_config(
2085
2106
  configuration_dict=existing_configuration,
2086
- flavor_name=component.flavor,
2107
+ flavor=component.flavor,
2087
2108
  component_type=component.type,
2088
2109
  # Always enforce validation of custom flavors
2089
2110
  validate_custom_flavors=True,
@@ -3778,6 +3799,7 @@ class Client(metaclass=ClientMetaClass):
3778
3799
  code_repository: Optional[Union[UUID, str]] = None,
3779
3800
  model: Optional[Union[UUID, str]] = None,
3780
3801
  stack: Optional[Union[UUID, str]] = None,
3802
+ stack_component: Optional[Union[UUID, str]] = None,
3781
3803
  hydrate: bool = False,
3782
3804
  ) -> Page[PipelineRunResponse]:
3783
3805
  """List all pipeline runs.
@@ -3816,6 +3838,7 @@ class Client(metaclass=ClientMetaClass):
3816
3838
  code_repository: Filter by code repository name/ID.
3817
3839
  model: Filter by model name/ID.
3818
3840
  stack: Filter by stack name/ID.
3841
+ stack_component: Filter by stack component name/ID.
3819
3842
  hydrate: Flag deciding whether to hydrate the output model(s)
3820
3843
  by including metadata fields in the response.
3821
3844
 
@@ -3854,6 +3877,7 @@ class Client(metaclass=ClientMetaClass):
3854
3877
  code_repository=code_repository,
3855
3878
  stack=stack,
3856
3879
  model=model,
3880
+ stack_component=stack_component,
3857
3881
  templatable=templatable,
3858
3882
  )
3859
3883
  runs_filter_model.set_scope_workspace(self.active_workspace.id)
@@ -3919,7 +3943,7 @@ class Client(metaclass=ClientMetaClass):
3919
3943
  workspace_id: Optional[Union[str, UUID]] = None,
3920
3944
  user_id: Optional[Union[str, UUID]] = None,
3921
3945
  model_version_id: Optional[Union[str, UUID]] = None,
3922
- num_outputs: Optional[Union[int, str]] = None,
3946
+ model: Optional[Union[UUID, str]] = None,
3923
3947
  hydrate: bool = False,
3924
3948
  ) -> Page[StepRunResponse]:
3925
3949
  """List all pipelines.
@@ -3940,11 +3964,11 @@ class Client(metaclass=ClientMetaClass):
3940
3964
  deployment_id: The id of the deployment to filter by.
3941
3965
  original_step_run_id: The id of the original step run to filter by.
3942
3966
  model_version_id: The ID of the model version to filter by.
3967
+ model: Filter by model name/ID.
3943
3968
  name: The name of the step run to filter by.
3944
3969
  cache_key: The cache key of the step run to filter by.
3945
3970
  code_hash: The code hash of the step run to filter by.
3946
3971
  status: The name of the run to filter by.
3947
- num_outputs: The number of outputs for the step run
3948
3972
  hydrate: Flag deciding whether to hydrate the output model(s)
3949
3973
  by including metadata fields in the response.
3950
3974
 
@@ -3971,7 +3995,7 @@ class Client(metaclass=ClientMetaClass):
3971
3995
  workspace_id=workspace_id,
3972
3996
  user_id=user_id,
3973
3997
  model_version_id=model_version_id,
3974
- num_outputs=num_outputs,
3998
+ model=model,
3975
3999
  )
3976
4000
  step_run_filter_model.set_scope_workspace(self.active_workspace.id)
3977
4001
  return self.zen_store.list_run_steps(
@@ -7243,6 +7267,7 @@ class Client(metaclass=ClientMetaClass):
7243
7267
  NotImplementedError: If the client is not connected to a ZenML
7244
7268
  server.
7245
7269
  """
7270
+ from zenml.login.credentials_store import get_credentials_store
7246
7271
  from zenml.zen_stores.rest_zen_store import RestZenStore
7247
7272
 
7248
7273
  zen_store = self.zen_store
@@ -7251,8 +7276,15 @@ class Client(metaclass=ClientMetaClass):
7251
7276
  "API key configuration is only supported if connected to a "
7252
7277
  "ZenML server."
7253
7278
  )
7279
+
7280
+ credentials_store = get_credentials_store()
7254
7281
  assert isinstance(zen_store, RestZenStore)
7255
- zen_store.set_api_key(api_key=key)
7282
+
7283
+ credentials_store.set_api_key(server_url=zen_store.url, api_key=key)
7284
+
7285
+ # Force a re-authentication to start using the new API key
7286
+ # right away.
7287
+ zen_store.authenticate(force=True)
7256
7288
 
7257
7289
  def list_api_keys(
7258
7290
  self,
zenml/config/compiler.py CHANGED
@@ -208,6 +208,7 @@ class Compiler:
208
208
  enable_artifact_visualization=config.enable_artifact_visualization,
209
209
  enable_step_logs=config.enable_step_logs,
210
210
  settings=config.settings,
211
+ tags=config.tags,
211
212
  extra=config.extra,
212
213
  model=config.model,
213
214
  parameters=config.parameters,
@@ -689,6 +689,15 @@ class GlobalConfiguration(BaseModel, metaclass=GlobalConfigMetaClass):
689
689
  self._configure_store(config, skip_default_registrations, **kwargs)
690
690
  logger.info("Updated the global store configuration.")
691
691
 
692
+ @property
693
+ def is_initialized(self) -> bool:
694
+ """Check if the global configuration is initialized.
695
+
696
+ Returns:
697
+ `True` if the global configuration is initialized.
698
+ """
699
+ return self._zen_store is not None
700
+
692
701
  @property
693
702
  def zen_store(self) -> "BaseZenStore":
694
703
  """Initialize and/or return the global zen store.
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Pipeline configuration classes."""
15
15
 
16
- from typing import TYPE_CHECKING, Any, Dict, Optional
16
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
17
17
 
18
18
  from pydantic import SerializeAsAny, field_validator
19
19
 
@@ -39,6 +39,7 @@ class PipelineConfigurationUpdate(StrictBaseModel):
39
39
  enable_artifact_visualization: Optional[bool] = None
40
40
  enable_step_logs: Optional[bool] = None
41
41
  settings: Dict[str, SerializeAsAny[BaseSettings]] = {}
42
+ tags: Optional[List[str]] = None
42
43
  extra: Dict[str, Any] = {}
43
44
  failure_hook_source: Optional[SourceWithValidator] = None
44
45
  success_hook_source: Optional[SourceWithValidator] = None
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Pipeline run configuration class."""
15
15
 
16
- from typing import Any, Dict, Optional, Union
16
+ from typing import Any, Dict, List, Optional, Union
17
17
  from uuid import UUID
18
18
 
19
19
  from pydantic import Field, SerializeAsAny
@@ -45,6 +45,7 @@ class PipelineRunConfiguration(
45
45
  )
46
46
  steps: Dict[str, StepConfigurationUpdate] = {}
47
47
  settings: Dict[str, SerializeAsAny[BaseSettings]] = {}
48
+ tags: Optional[List[str]] = None
48
49
  extra: Dict[str, Any] = {}
49
50
  model: Optional[Model] = None
50
51
  parameters: Optional[Dict[str, Any]] = None
zenml/constants.py CHANGED
@@ -134,6 +134,7 @@ APP_NAME = "zenml"
134
134
  # Environment variables
135
135
  ENV_ZENML_LOGGING_COLORS_DISABLED = "ZENML_LOGGING_COLORS_DISABLED"
136
136
  ENV_ZENML_ANALYTICS_OPT_IN = "ZENML_ANALYTICS_OPT_IN"
137
+ ENV_ZENML_USER_ID = "ZENML_USER_ID"
137
138
  ENV_ZENML_CONFIG_PATH = "ZENML_CONFIG_PATH"
138
139
  ENV_ZENML_DEBUG = "ZENML_DEBUG"
139
140
  ENV_ZENML_LOGGING_VERBOSITY = "ZENML_LOGGING_VERBOSITY"
@@ -163,7 +164,6 @@ ENV_ZENML_DISABLE_CLIENT_SERVER_MISMATCH_WARNING = (
163
164
  ENV_ZENML_DISABLE_WORKSPACE_WARNINGS = "ZENML_DISABLE_WORKSPACE_WARNINGS"
164
165
  ENV_ZENML_SKIP_IMAGE_BUILDER_DEFAULT = "ZENML_SKIP_IMAGE_BUILDER_DEFAULT"
165
166
  ENV_ZENML_SERVER = "ZENML_SERVER"
166
- ENV_ZENML_LOCAL_SERVER = "ZENML_LOCAL_SERVER"
167
167
  ENV_ZENML_ENFORCE_TYPE_ANNOTATIONS = "ZENML_ENFORCE_TYPE_ANNOTATIONS"
168
168
  ENV_ZENML_ENABLE_IMPLICIT_AUTH_METHODS = "ZENML_ENABLE_IMPLICIT_AUTH_METHODS"
169
169
  ENV_ZENML_DISABLE_STEP_LOGS_STORAGE = "ZENML_DISABLE_STEP_LOGS_STORAGE"
@@ -186,6 +186,7 @@ ENV_ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK = (
186
186
  "ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK"
187
187
  )
188
188
  ENV_ZENML_PREVENT_CLIENT_SIDE_CACHING = "ZENML_PREVENT_CLIENT_SIDE_CACHING"
189
+ ENV_ZENML_DISABLE_CREDENTIALS_DISK_CACHING = "DISABLE_CREDENTIALS_DISK_CACHING"
189
190
 
190
191
  # Logging variables
191
192
  IS_DEBUG_ENV: bool = handle_bool_env_var(ENV_ZENML_DEBUG, default=False)
@@ -336,6 +337,7 @@ API_TOKEN = "/api_token"
336
337
  ARTIFACTS = "/artifacts"
337
338
  ARTIFACT_VERSIONS = "/artifact_versions"
338
339
  ARTIFACT_VISUALIZATIONS = "/artifact_visualizations"
340
+ AUTH = "/auth"
339
341
  CODE_REFERENCES = "/code_references"
340
342
  CODE_REPOSITORIES = "/code_repositories"
341
343
  COMPONENT_TYPES = "/component-types"
@@ -468,11 +470,3 @@ BANNED_NAME_CHARACTERS = "\t\n\r\v\f"
468
470
 
469
471
 
470
472
  STACK_DEPLOYMENT_API_TOKEN_EXPIRATION = 60 * 6 # 6 hours
471
-
472
- # ZenML Pro
473
- ZENML_PRO_CONNECTION_ISSUES_SUSPENDED_PAUSED_TENANT_HINT = (
474
- "\nHINT: Since you are trying to communicate with the ZenML Pro Tenant, "
475
- "please make sure that your tenant is in RUNNING state on your "
476
- "Organization page. If the tenant is PAUSED you can `Resume` it via UI "
477
- "and try again."
478
- )
zenml/enums.py CHANGED
@@ -198,7 +198,7 @@ class SecretValidationLevel(StrEnum):
198
198
  class ServerProviderType(StrEnum):
199
199
  """ZenML server providers."""
200
200
 
201
- LOCAL = "local"
201
+ DAEMON = "daemon"
202
202
  DOCKER = "docker"
203
203
 
204
204