craft-ai-sdk 0.50.1rc1__py3-none-any.whl → 0.51.0__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.

Potentially problematic release.


This version of craft-ai-sdk might be problematic. Click here for more details.

craft_ai_sdk/__init__.py CHANGED
@@ -14,4 +14,4 @@ from .io import ( # noqa: F401
14
14
  )
15
15
 
16
16
 
17
- __version__ = "0.50.1rc1"
17
+ __version__ = "0.51.0"
@@ -1,29 +1,8 @@
1
- import os
2
1
  import warnings
3
2
 
4
- from ..utils import log_func_result, remove_none_values
3
+ from ..utils import log_func_result, remove_none_values, get_execution_id
5
4
  from ..sdk import BaseCraftAiSdk
6
5
 
7
- _execution_context = None
8
-
9
-
10
- def get_execution_id():
11
- global _execution_context
12
- if _execution_context is None:
13
- try:
14
- # File injected in steps
15
- import __craft_internal_execution_context # type: ignore
16
-
17
- _execution_context = __craft_internal_execution_context
18
- except ImportError:
19
- _execution_context = False
20
- if _execution_context:
21
- try:
22
- return _execution_context.current_execution_id.get()
23
- except LookupError:
24
- pass
25
- return os.environ.get("CRAFT_AI_EXECUTION_ID")
26
-
27
6
 
28
7
  @log_func_result("Pipeline metrics definition", get_execution_id)
29
8
  def record_metric_value(sdk: BaseCraftAiSdk, name, value):
@@ -10,6 +10,7 @@ from ..utils import (
10
10
  multipartify,
11
11
  handle_data_store_response,
12
12
  remove_keys_from_dict,
13
+ _datetime_to_timestamp_in_ms,
13
14
  )
14
15
  from .steps import (
15
16
  _add_inputs_outputs_in_message,
@@ -219,7 +220,7 @@ def create_pipeline(
219
220
  outputs,
220
221
  pipeline_name=pipeline_name,
221
222
  )
222
- files = _prepare_create_step_files(container_config, data, function_path)
223
+ files = _prepare_create_step_files(sdk, container_config, data, function_path)
223
224
 
224
225
  log_action(
225
226
  sdk,
@@ -414,3 +415,47 @@ def download_pipeline_local_folder(sdk: BaseCraftAiSdk, pipeline_name, folder):
414
415
  f.write(object_content)
415
416
  else:
416
417
  raise ValueError("'folder' must be a string")
418
+
419
+
420
+ @log_func_result("Pipeline logs")
421
+ def get_pipeline_logs(
422
+ sdk: BaseCraftAiSdk,
423
+ pipeline_name,
424
+ from_datetime=None,
425
+ to_datetime=None,
426
+ limit=None,
427
+ ):
428
+ """Get the logs of a pipeline.
429
+
430
+ Args:
431
+ pipeline_name (:obj:`str`): Name of the pipeline.
432
+ from_datetime (:obj:`datetime.time`, optional): Datetime from which the logs
433
+ are collected.
434
+ to_datetime (:obj:`datetime.time`, optional): Datetime until which the logs
435
+ are collected.
436
+ limit (:obj:`int`, optional): Maximum number of logs that are collected.
437
+
438
+ Returns:
439
+ :obj:`list` of :obj:`dict`: List of collected logs represented as dict with
440
+ the following keys:
441
+
442
+ * ``"timestamp"`` (:obj:`str`): Timestamp of the log.
443
+ * ``"message"`` (:obj:`str`): Log message.
444
+ """
445
+ url = f"{sdk.base_environment_api_url}/pipelines/{pipeline_name}/logs"
446
+
447
+ data = {}
448
+ if from_datetime is not None:
449
+ data["from"] = _datetime_to_timestamp_in_ms(from_datetime)
450
+ if to_datetime is not None:
451
+ data["to"] = _datetime_to_timestamp_in_ms(to_datetime)
452
+ if limit is not None:
453
+ data["limit"] = limit
454
+
455
+ log_action(
456
+ sdk,
457
+ "Please wait while logs are being fetched. This may take a while...",
458
+ )
459
+ logs = sdk._post(url, json=data)
460
+
461
+ return logs
@@ -14,6 +14,7 @@ from ..utils import (
14
14
  multipartify,
15
15
  remove_none_values,
16
16
  handle_data_store_response,
17
+ _datetime_to_timestamp_in_ms,
17
18
  )
18
19
  from ..constants import CREATION_REQUESTS_RETRY_INTERVAL
19
20
 
@@ -73,17 +74,30 @@ def _prepare_create_step_data(
73
74
  return data
74
75
 
75
76
 
76
- def _prepare_create_step_files(container_config, data, function_path):
77
- files = {}
78
- if container_config.get("local_folder") is not None:
79
- included = list(data.get("container_config", {}).get("included_folders", ["/"]))
80
- included.append(data.get("container_config", {}).get("requirements_path"))
81
- included.append(function_path)
82
- tar_data = _compress_folder_to_memory(
83
- container_config["local_folder"], include=list(filter(None, included))
84
- )
85
- files["step_file"] = tar_data
86
- return files
77
+ def _prepare_create_step_files(sdk, container_config, data, function_path):
78
+ if "local_folder" not in container_config:
79
+ return {}
80
+
81
+ url = f"{sdk.base_environment_api_url}/project-information"
82
+ project_information = sdk._get(url)
83
+
84
+ included = [
85
+ *(
86
+ data.get("container_config", {}).get(
87
+ "included_folders", project_information.get("included_folders")
88
+ )
89
+ or []
90
+ ),
91
+ data.get("container_config", {}).get(
92
+ "requirements_path", project_information.get("requirements_path")
93
+ ),
94
+ function_path,
95
+ ]
96
+
97
+ tar_data = _compress_folder_to_memory(
98
+ container_config["local_folder"], include=list(filter(None, included))
99
+ )
100
+ return {"step_file": tar_data}
87
101
 
88
102
 
89
103
  def _add_inputs_outputs_in_message(message, inputs, outputs):
@@ -286,7 +300,7 @@ def create_step(
286
300
  outputs,
287
301
  step_name=step_name,
288
302
  )
289
- files = _prepare_create_step_files(container_config, data, function_path)
303
+ files = _prepare_create_step_files(sdk, container_config, data, function_path)
290
304
 
291
305
  log_action(
292
306
  sdk,
@@ -403,15 +417,6 @@ def list_steps(sdk: BaseCraftAiSdk):
403
417
  * ``"step_name"`` (:obj:`str`): Name of the step.
404
418
  * ``"status"`` (:obj:`str`): either ``"creation_pending"`` or ``"ready"``.
405
419
  * ``"created_at"`` (:obj:`str`): The creation date in ISO format.
406
- * ``"updated_at"`` (:obj:`str`): The last update date in ISO format.
407
- * ``"repository_branch"`` (:obj:`str`): The branch of the
408
- repository where the step was built.
409
- * ``"repository_url"`` (:obj:`str`): The url of the repository
410
- where the step was built.
411
- * ``"commit_id"`` (:obj:`str`): The commit id on which the step was
412
- built.
413
- * ``"origin"`` (:obj:`str`): The origin of the step, can be
414
- ``"git_repository"`` or ``"local"``.
415
420
  """
416
421
  url = f"{sdk.base_environment_api_url}/steps"
417
422
 
@@ -474,3 +479,47 @@ def download_step_local_folder(sdk: BaseCraftAiSdk, step_name, folder):
474
479
  f.write(object_content)
475
480
  else:
476
481
  raise ValueError("'folder' must be a string")
482
+
483
+
484
+ @log_func_result("Step logs")
485
+ def get_step_logs(
486
+ sdk: BaseCraftAiSdk,
487
+ step_name,
488
+ from_datetime=None,
489
+ to_datetime=None,
490
+ limit=None,
491
+ ):
492
+ """Get the logs of a step.
493
+
494
+ Args:
495
+ step_name (:obj:`str`): Name of the step.
496
+ from_datetime (:obj:`datetime.time`, optional): Datetime from which the logs
497
+ are collected.
498
+ to_datetime (:obj:`datetime.time`, optional): Datetime until which the logs
499
+ are collected.
500
+ limit (:obj:`int`, optional): Maximum number of logs that are collected.
501
+
502
+ Returns:
503
+ :obj:`list` of :obj:`dict`: List of collected logs represented as dict with
504
+ the following keys:
505
+
506
+ * ``"timestamp"`` (:obj:`str`): Timestamp of the log.
507
+ * ``"message"`` (:obj:`str`): Log message.
508
+ """
509
+ url = f"{sdk.base_environment_api_url}/steps/{step_name}/logs"
510
+
511
+ data = {}
512
+ if from_datetime is not None:
513
+ data["from"] = _datetime_to_timestamp_in_ms(from_datetime)
514
+ if to_datetime is not None:
515
+ data["to"] = _datetime_to_timestamp_in_ms(to_datetime)
516
+ if limit is not None:
517
+ data["limit"] = limit
518
+
519
+ log_action(
520
+ sdk,
521
+ "Please wait while logs are being fetched. This may take a while...",
522
+ )
523
+ logs = sdk._post(url, json=data)
524
+
525
+ return logs
craft_ai_sdk/sdk.py CHANGED
@@ -59,6 +59,7 @@ class CraftAiSdk(BaseCraftAiSdk):
59
59
  list_steps,
60
60
  delete_step,
61
61
  download_step_local_folder,
62
+ get_step_logs,
62
63
  )
63
64
  from .core.pipelines import (
64
65
  create_pipeline,
@@ -66,6 +67,7 @@ class CraftAiSdk(BaseCraftAiSdk):
66
67
  list_pipelines,
67
68
  delete_pipeline,
68
69
  download_pipeline_local_folder,
70
+ get_pipeline_logs,
69
71
  )
70
72
  from .core.pipeline_executions import (
71
73
  run_pipeline,
@@ -128,7 +130,7 @@ class CraftAiSdk(BaseCraftAiSdk):
128
130
  os.environ.get("CRAFT_AI__MULTIPART_PART_SIZE__B", str(38 * 256 * 1024))
129
131
  )
130
132
  _access_token_margin = timedelta(seconds=30)
131
- _version = "0.50.1rc1" # Would be better to share it somewhere
133
+ _version = "0.51.0" # Would be better to share it somewhere
132
134
 
133
135
  def __init__(
134
136
  self,
craft_ai_sdk/utils.py CHANGED
@@ -8,9 +8,30 @@ from typing import Callable, Iterable, Union
8
8
  import xml.etree.ElementTree as ET
9
9
  from requests import RequestException, Response
10
10
  from json import JSONDecodeError
11
+ import os
11
12
 
12
13
  from .exceptions import SdkException
13
14
 
15
+ _execution_context = None
16
+
17
+
18
+ def get_execution_id():
19
+ global _execution_context
20
+ if _execution_context is None:
21
+ try:
22
+ # File injected in steps
23
+ import __craft_internal_execution_context # type: ignore
24
+
25
+ _execution_context = __craft_internal_execution_context
26
+ except ImportError:
27
+ _execution_context = False
28
+ if _execution_context:
29
+ try:
30
+ return _execution_context.current_execution_id.get()
31
+ except LookupError:
32
+ pass
33
+ return os.environ.get("CRAFT_AI_EXECUTION_ID")
34
+
14
35
 
15
36
  def handle_data_store_response(response):
16
37
  """Return the content of a response received from the datastore
@@ -64,8 +85,19 @@ def _parse_json_response(response):
64
85
  def _raise_craft_ai_error_from_response(response: Response):
65
86
  try:
66
87
  error_content = response.json()
88
+ error_message = error_content.get("message", "The server returned an error")
89
+
90
+ # Permission denied inside a running execution
91
+ if response.status_code == 403 and get_execution_id() is not None:
92
+ error_message = (
93
+ "Insufficient permissions. This is probably because "
94
+ "you called an SDK function that is not permitted from "
95
+ "inside a running deployment or execution, even if it "
96
+ "works from your computer. Original error: " + error_message
97
+ )
98
+
67
99
  raise SdkException(
68
- message=error_content.get("message", "The server returned an error"),
100
+ message=error_message,
69
101
  status_code=response.status_code,
70
102
  name=error_content.get("name"),
71
103
  request_id=error_content.get("request_id"),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: craft-ai-sdk
3
- Version: 0.50.1rc1
3
+ Version: 0.51.0
4
4
  Summary: Craft AI MLOps platform SDK
5
5
  Home-page: https://www.craft.ai/
6
6
  License: Apache-2.0
@@ -0,0 +1,23 @@
1
+ craft_ai_sdk/__init__.py,sha256=qg_JDJYK4OgWeyWNOpo9HXcld5y9QJ-iN1LF26Y9590,404
2
+ craft_ai_sdk/constants.py,sha256=KHRaIHA9tHxjwluo7zBF2UZpePQaDI57SrBs4Ob1g7Q,607
3
+ craft_ai_sdk/core/data_store.py,sha256=kI0nUIXfkz-9ENVEeFW70_PGhBNB_S-nntJJkKhGRoQ,8464
4
+ craft_ai_sdk/core/deployments.py,sha256=p717fy73OoynUuDKO6XYLfLu5stBHH73xQw6HLm8jUY,25804
5
+ craft_ai_sdk/core/endpoints.py,sha256=ElVcjnrjWD4_Wo5fOwRh1apUuL5IB7DM-FMH-1poP9w,4563
6
+ craft_ai_sdk/core/environment_variables.py,sha256=wy0U7y0C7tRXOXOiWGz0vX5ajHSmOb_7J1fXlSahGpI,2045
7
+ craft_ai_sdk/core/pipeline_executions.py,sha256=7RRKQUxXUCI871mmFCO4N4RG0RvX5MZ3Fss3doCb8mQ,18314
8
+ craft_ai_sdk/core/pipeline_metrics.py,sha256=Tt5p6guHAf7nhq1wvJG3t7WqsYKqrNQ7sZcFKqXYmdM,6465
9
+ craft_ai_sdk/core/pipelines.py,sha256=-eU_vKoxFQiDXWZgvetI577ZOeJWR5jpVgqMBohDNUw,19756
10
+ craft_ai_sdk/core/resource_metrics.py,sha256=yaco_AZ4Wu5sxLRKsA3qt2R3-PMs2e32EGO0gsHtq2Y,2336
11
+ craft_ai_sdk/core/steps.py,sha256=pvC2bEPF-9XGugdYAQ2LhdlnKZvC3giRm9ZrX3ZDMLs,21406
12
+ craft_ai_sdk/core/users.py,sha256=Qi3xg7Vzk_0MepccNIZbFzjI0bBqouqkFStTuejUY6s,509
13
+ craft_ai_sdk/exceptions.py,sha256=IC-JfZmmmaTsbMCgirOEByRmWnatQLjKe8BErRkuwM0,1075
14
+ craft_ai_sdk/io.py,sha256=y9lg5unGIwQICAPuKUdUPDAJCsxe2lbseIlmiWka2Ec,11865
15
+ craft_ai_sdk/sdk.py,sha256=W9oOeCKCvcZgttFNlEwAJElh2d4OQp3SViQmuaCyy7U,10068
16
+ craft_ai_sdk/utils.py,sha256=zo4fdexMeRspFHG-abmH858-eKzqZzSJ5tdxNBPzgpU,10551
17
+ craft_ai_sdk/warnings.py,sha256=xJXbUR66SVbG_ZO7rxpAZ2hkGxdUtM5JAx6JoWg77Qs,6678
18
+ documentation.pdf,sha256=39aslpaNhNb9p-PvaMS5Oxo1VWZw7VCbfRZbp_eXDBc,327435
19
+ craft_ai_sdk-0.51.0.dist-info/LICENSE,sha256=_2oYRJic9lZK05LceuJ9aZZw5mPHYc1WQhJiVS-oGFU,10754
20
+ craft_ai_sdk-0.51.0.dist-info/METADATA,sha256=RXUNVJKV0yeVqwbZDEJgj4fehCH4LPm1tvhwYmIPuMg,1563
21
+ craft_ai_sdk-0.51.0.dist-info/entry_points.txt,sha256=eV9YD0zCAb88_wNMDV99sRxVKVC-WOQF3b1Pepaytcg,385
22
+ craft_ai_sdk-0.51.0.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
23
+ craft_ai_sdk-0.51.0.dist-info/RECORD,,
documentation.pdf CHANGED
Binary file
@@ -1,23 +0,0 @@
1
- craft_ai_sdk/__init__.py,sha256=9Wu68jkKuIHXm1GZuZ1EL29KpE_oOyKr0kShd0YqLwU,407
2
- craft_ai_sdk/constants.py,sha256=KHRaIHA9tHxjwluo7zBF2UZpePQaDI57SrBs4Ob1g7Q,607
3
- craft_ai_sdk/core/data_store.py,sha256=kI0nUIXfkz-9ENVEeFW70_PGhBNB_S-nntJJkKhGRoQ,8464
4
- craft_ai_sdk/core/deployments.py,sha256=p717fy73OoynUuDKO6XYLfLu5stBHH73xQw6HLm8jUY,25804
5
- craft_ai_sdk/core/endpoints.py,sha256=ElVcjnrjWD4_Wo5fOwRh1apUuL5IB7DM-FMH-1poP9w,4563
6
- craft_ai_sdk/core/environment_variables.py,sha256=wy0U7y0C7tRXOXOiWGz0vX5ajHSmOb_7J1fXlSahGpI,2045
7
- craft_ai_sdk/core/pipeline_executions.py,sha256=7RRKQUxXUCI871mmFCO4N4RG0RvX5MZ3Fss3doCb8mQ,18314
8
- craft_ai_sdk/core/pipeline_metrics.py,sha256=fTkiabCD5wATWCMNI_S_MFSlQm55aI7qPh_3Vg6SUIs,7032
9
- craft_ai_sdk/core/pipelines.py,sha256=7QwnLDVfMMYqYKHzsJhYHXbhBYEgwYDk_ZLdD-uzzKs,18383
10
- craft_ai_sdk/core/resource_metrics.py,sha256=yaco_AZ4Wu5sxLRKsA3qt2R3-PMs2e32EGO0gsHtq2Y,2336
11
- craft_ai_sdk/core/steps.py,sha256=e_-Kb88raOWRKXLbRzTaJVgVOs4LXA2ziXC6z-PYtlg,20343
12
- craft_ai_sdk/core/users.py,sha256=Qi3xg7Vzk_0MepccNIZbFzjI0bBqouqkFStTuejUY6s,509
13
- craft_ai_sdk/exceptions.py,sha256=IC-JfZmmmaTsbMCgirOEByRmWnatQLjKe8BErRkuwM0,1075
14
- craft_ai_sdk/io.py,sha256=y9lg5unGIwQICAPuKUdUPDAJCsxe2lbseIlmiWka2Ec,11865
15
- craft_ai_sdk/sdk.py,sha256=4x4S6jwfMfllnyvxNCtdcBWqYrT-4F_gL2tiQpeSPmE,10021
16
- craft_ai_sdk/utils.py,sha256=3BTHNv3OgxtfiJnmf8U_pne-Bky4vtX33CvN_GD_NKM,9460
17
- craft_ai_sdk/warnings.py,sha256=xJXbUR66SVbG_ZO7rxpAZ2hkGxdUtM5JAx6JoWg77Qs,6678
18
- documentation.pdf,sha256=U2K_3cfkwKsSBI225v5_7yI-Avuv18p17oZYUhS7Xk0,327165
19
- craft_ai_sdk-0.50.1rc1.dist-info/LICENSE,sha256=_2oYRJic9lZK05LceuJ9aZZw5mPHYc1WQhJiVS-oGFU,10754
20
- craft_ai_sdk-0.50.1rc1.dist-info/METADATA,sha256=EYOoFbGLFDcnEfz49RNrY8GChiO2Y_i9gqJEV4oulY0,1566
21
- craft_ai_sdk-0.50.1rc1.dist-info/entry_points.txt,sha256=eV9YD0zCAb88_wNMDV99sRxVKVC-WOQF3b1Pepaytcg,385
22
- craft_ai_sdk-0.50.1rc1.dist-info/WHEEL,sha256=vVCvjcmxuUltf8cYhJ0sJMRDLr1XsPuxEId8YDzbyCY,88
23
- craft_ai_sdk-0.50.1rc1.dist-info/RECORD,,