truefoundry 0.3.4rc1__py3-none-any.whl → 0.4.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 truefoundry might be problematic. Click here for more details.

Files changed (253) hide show
  1. truefoundry/__init__.py +2 -0
  2. truefoundry/autodeploy/agents/developer.py +1 -1
  3. truefoundry/autodeploy/agents/project_identifier.py +2 -2
  4. truefoundry/autodeploy/agents/tester.py +1 -1
  5. truefoundry/autodeploy/cli.py +1 -1
  6. truefoundry/autodeploy/tools/list_files.py +1 -1
  7. truefoundry/cli/__main__.py +3 -17
  8. truefoundry/common/__init__.py +0 -0
  9. truefoundry/{deploy/lib/auth → common}/auth_service_client.py +50 -40
  10. truefoundry/common/constants.py +12 -0
  11. truefoundry/{deploy/lib/auth → common}/credential_file_manager.py +7 -7
  12. truefoundry/{deploy/lib/auth → common}/credential_provider.py +10 -23
  13. truefoundry/common/entities.py +124 -0
  14. truefoundry/common/exceptions.py +12 -0
  15. truefoundry/common/request_utils.py +84 -0
  16. truefoundry/common/servicefoundry_client.py +91 -0
  17. truefoundry/common/utils.py +56 -0
  18. truefoundry/deploy/auto_gen/models.py +4 -6
  19. truefoundry/deploy/cli/cli.py +3 -1
  20. truefoundry/deploy/cli/commands/apply_command.py +1 -1
  21. truefoundry/deploy/cli/commands/build_command.py +1 -1
  22. truefoundry/deploy/cli/commands/deploy_command.py +1 -1
  23. truefoundry/deploy/cli/commands/login_command.py +2 -2
  24. truefoundry/deploy/cli/commands/patch_application_command.py +1 -1
  25. truefoundry/deploy/cli/commands/patch_command.py +1 -1
  26. truefoundry/deploy/cli/commands/terminate_comand.py +1 -1
  27. truefoundry/deploy/cli/util.py +1 -1
  28. truefoundry/deploy/function_service/remote/remote.py +1 -1
  29. truefoundry/deploy/lib/auth/servicefoundry_session.py +2 -2
  30. truefoundry/deploy/lib/clients/servicefoundry_client.py +120 -159
  31. truefoundry/deploy/lib/const.py +1 -35
  32. truefoundry/deploy/lib/exceptions.py +0 -16
  33. truefoundry/deploy/lib/model/entity.py +1 -112
  34. truefoundry/deploy/lib/session.py +14 -42
  35. truefoundry/deploy/lib/util.py +0 -37
  36. truefoundry/{python_deploy_codegen.py → deploy/python_deploy_codegen.py} +2 -2
  37. truefoundry/deploy/v2/lib/deploy.py +3 -3
  38. truefoundry/deploy/v2/lib/deployable_patched_models.py +1 -1
  39. truefoundry/langchain/truefoundry_chat.py +1 -1
  40. truefoundry/langchain/truefoundry_embeddings.py +1 -1
  41. truefoundry/langchain/truefoundry_llm.py +1 -1
  42. truefoundry/langchain/utils.py +0 -41
  43. truefoundry/ml/__init__.py +37 -6
  44. truefoundry/ml/artifact/__init__.py +0 -0
  45. truefoundry/ml/artifact/truefoundry_artifact_repo.py +1161 -0
  46. truefoundry/ml/autogen/__init__.py +0 -0
  47. truefoundry/ml/autogen/client/__init__.py +370 -0
  48. truefoundry/ml/autogen/client/api/__init__.py +16 -0
  49. truefoundry/ml/autogen/client/api/auth_api.py +184 -0
  50. truefoundry/ml/autogen/client/api/deprecated_api.py +605 -0
  51. truefoundry/ml/autogen/client/api/experiments_api.py +1944 -0
  52. truefoundry/ml/autogen/client/api/health_api.py +299 -0
  53. truefoundry/ml/autogen/client/api/metrics_api.py +371 -0
  54. truefoundry/ml/autogen/client/api/mlfoundry_artifacts_api.py +7213 -0
  55. truefoundry/ml/autogen/client/api/python_deployment_config_api.py +201 -0
  56. truefoundry/ml/autogen/client/api/run_artifacts_api.py +231 -0
  57. truefoundry/ml/autogen/client/api/runs_api.py +2919 -0
  58. truefoundry/ml/autogen/client/api_client.py +822 -0
  59. truefoundry/ml/autogen/client/api_response.py +30 -0
  60. truefoundry/ml/autogen/client/configuration.py +489 -0
  61. truefoundry/ml/autogen/client/exceptions.py +161 -0
  62. truefoundry/ml/autogen/client/models/__init__.py +341 -0
  63. truefoundry/ml/autogen/client/models/add_custom_metrics_to_model_version_request_dto.py +69 -0
  64. truefoundry/ml/autogen/client/models/add_features_to_model_version_request_dto.py +83 -0
  65. truefoundry/ml/autogen/client/models/agent.py +125 -0
  66. truefoundry/ml/autogen/client/models/agent_app.py +118 -0
  67. truefoundry/ml/autogen/client/models/agent_open_api_tool.py +143 -0
  68. truefoundry/ml/autogen/client/models/agent_open_api_tool_with_fqn.py +144 -0
  69. truefoundry/ml/autogen/client/models/agent_with_fqn.py +127 -0
  70. truefoundry/ml/autogen/client/models/artifact_dto.py +115 -0
  71. truefoundry/ml/autogen/client/models/artifact_response_dto.py +75 -0
  72. truefoundry/ml/autogen/client/models/artifact_type.py +39 -0
  73. truefoundry/ml/autogen/client/models/artifact_version_dto.py +141 -0
  74. truefoundry/ml/autogen/client/models/artifact_version_response_dto.py +77 -0
  75. truefoundry/ml/autogen/client/models/artifact_version_status.py +35 -0
  76. truefoundry/ml/autogen/client/models/assistant_message.py +89 -0
  77. truefoundry/ml/autogen/client/models/authorize_user_for_model_request_dto.py +69 -0
  78. truefoundry/ml/autogen/client/models/authorize_user_for_model_version_request_dto.py +69 -0
  79. truefoundry/ml/autogen/client/models/blob_storage_reference.py +93 -0
  80. truefoundry/ml/autogen/client/models/body_get_search_runs_get.py +72 -0
  81. truefoundry/ml/autogen/client/models/chat_prompt.py +156 -0
  82. truefoundry/ml/autogen/client/models/chat_prompt_messages_inner.py +171 -0
  83. truefoundry/ml/autogen/client/models/columns_dto.py +73 -0
  84. truefoundry/ml/autogen/client/models/content.py +153 -0
  85. truefoundry/ml/autogen/client/models/content1.py +153 -0
  86. truefoundry/ml/autogen/client/models/content2.py +174 -0
  87. truefoundry/ml/autogen/client/models/content2_any_of_inner.py +150 -0
  88. truefoundry/ml/autogen/client/models/create_artifact_request_dto.py +74 -0
  89. truefoundry/ml/autogen/client/models/create_artifact_response_dto.py +65 -0
  90. truefoundry/ml/autogen/client/models/create_artifact_version_request_dto.py +74 -0
  91. truefoundry/ml/autogen/client/models/create_artifact_version_response_dto.py +65 -0
  92. truefoundry/ml/autogen/client/models/create_dataset_request_dto.py +76 -0
  93. truefoundry/ml/autogen/client/models/create_experiment_request_dto.py +94 -0
  94. truefoundry/ml/autogen/client/models/create_experiment_response_dto.py +67 -0
  95. truefoundry/ml/autogen/client/models/create_model_version_request_dto.py +95 -0
  96. truefoundry/ml/autogen/client/models/create_multi_part_upload_for_dataset_request_dto.py +73 -0
  97. truefoundry/ml/autogen/client/models/create_multi_part_upload_for_dataset_response_dto.py +79 -0
  98. truefoundry/ml/autogen/client/models/create_multi_part_upload_request_dto.py +73 -0
  99. truefoundry/ml/autogen/client/models/create_python_deployment_config_request_dto.py +72 -0
  100. truefoundry/ml/autogen/client/models/create_python_deployment_config_response_dto.py +67 -0
  101. truefoundry/ml/autogen/client/models/create_run_request_dto.py +97 -0
  102. truefoundry/ml/autogen/client/models/create_run_response_dto.py +75 -0
  103. truefoundry/ml/autogen/client/models/dataset_dto.py +112 -0
  104. truefoundry/ml/autogen/client/models/dataset_response_dto.py +75 -0
  105. truefoundry/ml/autogen/client/models/delete_artifact_versions_request_dto.py +65 -0
  106. truefoundry/ml/autogen/client/models/delete_dataset_request_dto.py +74 -0
  107. truefoundry/ml/autogen/client/models/delete_model_version_request_dto.py +65 -0
  108. truefoundry/ml/autogen/client/models/delete_run_request.py +65 -0
  109. truefoundry/ml/autogen/client/models/delete_tag_request_dto.py +68 -0
  110. truefoundry/ml/autogen/client/models/experiment_dto.py +127 -0
  111. truefoundry/ml/autogen/client/models/experiment_id_request_dto.py +67 -0
  112. truefoundry/ml/autogen/client/models/experiment_response_dto.py +75 -0
  113. truefoundry/ml/autogen/client/models/experiment_tag_dto.py +69 -0
  114. truefoundry/ml/autogen/client/models/feature_dto.py +68 -0
  115. truefoundry/ml/autogen/client/models/feature_value_type.py +35 -0
  116. truefoundry/ml/autogen/client/models/file_info_dto.py +76 -0
  117. truefoundry/ml/autogen/client/models/finalize_artifact_version_request_dto.py +101 -0
  118. truefoundry/ml/autogen/client/models/get_experiment_response_dto.py +88 -0
  119. truefoundry/ml/autogen/client/models/get_latest_run_log_response_dto.py +75 -0
  120. truefoundry/ml/autogen/client/models/get_metric_history_response.py +79 -0
  121. truefoundry/ml/autogen/client/models/get_signed_url_for_dataset_write_request_dto.py +68 -0
  122. truefoundry/ml/autogen/client/models/get_signed_urls_for_artifact_version_read_request_dto.py +68 -0
  123. truefoundry/ml/autogen/client/models/get_signed_urls_for_artifact_version_read_response_dto.py +81 -0
  124. truefoundry/ml/autogen/client/models/get_signed_urls_for_artifact_version_write_request_dto.py +69 -0
  125. truefoundry/ml/autogen/client/models/get_signed_urls_for_artifact_version_write_response_dto.py +83 -0
  126. truefoundry/ml/autogen/client/models/get_signed_urls_for_dataset_read_request_dto.py +68 -0
  127. truefoundry/ml/autogen/client/models/get_signed_urls_for_dataset_read_response_dto.py +81 -0
  128. truefoundry/ml/autogen/client/models/get_signed_urls_for_dataset_write_response_dto.py +81 -0
  129. truefoundry/ml/autogen/client/models/get_tenant_id_response_dto.py +73 -0
  130. truefoundry/ml/autogen/client/models/http_validation_error.py +82 -0
  131. truefoundry/ml/autogen/client/models/image_content_part.py +87 -0
  132. truefoundry/ml/autogen/client/models/image_url.py +75 -0
  133. truefoundry/ml/autogen/client/models/internal_metadata.py +180 -0
  134. truefoundry/ml/autogen/client/models/latest_run_log_dto.py +78 -0
  135. truefoundry/ml/autogen/client/models/list_artifact_versions_request_dto.py +107 -0
  136. truefoundry/ml/autogen/client/models/list_artifact_versions_response_dto.py +87 -0
  137. truefoundry/ml/autogen/client/models/list_artifacts_request_dto.py +96 -0
  138. truefoundry/ml/autogen/client/models/list_artifacts_response_dto.py +86 -0
  139. truefoundry/ml/autogen/client/models/list_colums_response_dto.py +75 -0
  140. truefoundry/ml/autogen/client/models/list_datasets_request_dto.py +78 -0
  141. truefoundry/ml/autogen/client/models/list_datasets_response_dto.py +86 -0
  142. truefoundry/ml/autogen/client/models/list_experiments_response_dto.py +86 -0
  143. truefoundry/ml/autogen/client/models/list_files_for_artifact_version_request_dto.py +76 -0
  144. truefoundry/ml/autogen/client/models/list_files_for_artifact_versions_response_dto.py +82 -0
  145. truefoundry/ml/autogen/client/models/list_files_for_dataset_request_dto.py +76 -0
  146. truefoundry/ml/autogen/client/models/list_files_for_dataset_response_dto.py +82 -0
  147. truefoundry/ml/autogen/client/models/list_latest_run_logs_response_dto.py +82 -0
  148. truefoundry/ml/autogen/client/models/list_metric_history_request_dto.py +69 -0
  149. truefoundry/ml/autogen/client/models/list_metric_history_response_dto.py +84 -0
  150. truefoundry/ml/autogen/client/models/list_model_version_response_dto.py +87 -0
  151. truefoundry/ml/autogen/client/models/list_model_versions_request_dto.py +93 -0
  152. truefoundry/ml/autogen/client/models/list_models_request_dto.py +89 -0
  153. truefoundry/ml/autogen/client/models/list_models_response_dto.py +84 -0
  154. truefoundry/ml/autogen/client/models/list_run_artifacts_response_dto.py +84 -0
  155. truefoundry/ml/autogen/client/models/list_run_logs_response_dto.py +82 -0
  156. truefoundry/ml/autogen/client/models/list_seed_experiments_response_dto.py +81 -0
  157. truefoundry/ml/autogen/client/models/log_batch_request_dto.py +106 -0
  158. truefoundry/ml/autogen/client/models/log_metric_request_dto.py +80 -0
  159. truefoundry/ml/autogen/client/models/log_param_request_dto.py +76 -0
  160. truefoundry/ml/autogen/client/models/method.py +37 -0
  161. truefoundry/ml/autogen/client/models/metric_collection_dto.py +82 -0
  162. truefoundry/ml/autogen/client/models/metric_dto.py +76 -0
  163. truefoundry/ml/autogen/client/models/mime_type.py +37 -0
  164. truefoundry/ml/autogen/client/models/model_configuration.py +103 -0
  165. truefoundry/ml/autogen/client/models/model_dto.py +122 -0
  166. truefoundry/ml/autogen/client/models/model_response_dto.py +75 -0
  167. truefoundry/ml/autogen/client/models/model_schema_dto.py +85 -0
  168. truefoundry/ml/autogen/client/models/model_version_dto.py +170 -0
  169. truefoundry/ml/autogen/client/models/model_version_response_dto.py +75 -0
  170. truefoundry/ml/autogen/client/models/multi_part_upload_dto.py +107 -0
  171. truefoundry/ml/autogen/client/models/multi_part_upload_response_dto.py +79 -0
  172. truefoundry/ml/autogen/client/models/multi_part_upload_storage_provider.py +34 -0
  173. truefoundry/ml/autogen/client/models/notify_artifact_version_failure_dto.py +65 -0
  174. truefoundry/ml/autogen/client/models/openapi_spec.py +152 -0
  175. truefoundry/ml/autogen/client/models/param_dto.py +66 -0
  176. truefoundry/ml/autogen/client/models/parameters.py +84 -0
  177. truefoundry/ml/autogen/client/models/prediction_type.py +34 -0
  178. truefoundry/ml/autogen/client/models/resolve_agent_app_response_dto.py +75 -0
  179. truefoundry/ml/autogen/client/models/restore_run_request_dto.py +65 -0
  180. truefoundry/ml/autogen/client/models/run_data_dto.py +104 -0
  181. truefoundry/ml/autogen/client/models/run_dto.py +84 -0
  182. truefoundry/ml/autogen/client/models/run_info_dto.py +105 -0
  183. truefoundry/ml/autogen/client/models/run_log_dto.py +90 -0
  184. truefoundry/ml/autogen/client/models/run_log_input_dto.py +80 -0
  185. truefoundry/ml/autogen/client/models/run_response_dto.py +75 -0
  186. truefoundry/ml/autogen/client/models/run_tag_dto.py +66 -0
  187. truefoundry/ml/autogen/client/models/search_runs_request_dto.py +94 -0
  188. truefoundry/ml/autogen/client/models/search_runs_response_dto.py +84 -0
  189. truefoundry/ml/autogen/client/models/set_experiment_tag_request_dto.py +73 -0
  190. truefoundry/ml/autogen/client/models/set_tag_request_dto.py +76 -0
  191. truefoundry/ml/autogen/client/models/signed_url_dto.py +69 -0
  192. truefoundry/ml/autogen/client/models/stop.py +152 -0
  193. truefoundry/ml/autogen/client/models/store_run_logs_request_dto.py +83 -0
  194. truefoundry/ml/autogen/client/models/system_message.py +89 -0
  195. truefoundry/ml/autogen/client/models/text.py +153 -0
  196. truefoundry/ml/autogen/client/models/text_content_part.py +84 -0
  197. truefoundry/ml/autogen/client/models/update_artifact_version_request_dto.py +74 -0
  198. truefoundry/ml/autogen/client/models/update_dataset_request_dto.py +74 -0
  199. truefoundry/ml/autogen/client/models/update_experiment_request_dto.py +74 -0
  200. truefoundry/ml/autogen/client/models/update_model_version_request_dto.py +93 -0
  201. truefoundry/ml/autogen/client/models/update_run_request_dto.py +78 -0
  202. truefoundry/ml/autogen/client/models/update_run_response_dto.py +75 -0
  203. truefoundry/ml/autogen/client/models/url.py +153 -0
  204. truefoundry/ml/autogen/client/models/user_message.py +89 -0
  205. truefoundry/ml/autogen/client/models/validation_error.py +87 -0
  206. truefoundry/ml/autogen/client/models/validation_error_loc_inner.py +154 -0
  207. truefoundry/ml/autogen/client/rest.py +426 -0
  208. truefoundry/ml/autogen/client_README.md +320 -0
  209. truefoundry/ml/cli/__init__.py +0 -0
  210. truefoundry/ml/cli/cli.py +18 -0
  211. truefoundry/ml/cli/commands/__init__.py +3 -0
  212. truefoundry/ml/cli/commands/download.py +87 -0
  213. truefoundry/ml/clients/__init__.py +0 -0
  214. truefoundry/ml/clients/entities.py +8 -0
  215. truefoundry/ml/clients/servicefoundry_client.py +45 -0
  216. truefoundry/ml/clients/utils.py +122 -0
  217. truefoundry/ml/constants.py +84 -0
  218. truefoundry/ml/entities.py +62 -0
  219. truefoundry/ml/enums.py +70 -0
  220. truefoundry/ml/env_vars.py +9 -0
  221. truefoundry/ml/exceptions.py +8 -0
  222. truefoundry/ml/git_info.py +60 -0
  223. truefoundry/ml/internal_namespace.py +52 -0
  224. truefoundry/ml/log_types/__init__.py +4 -0
  225. truefoundry/ml/log_types/artifacts/artifact.py +431 -0
  226. truefoundry/ml/log_types/artifacts/constants.py +33 -0
  227. truefoundry/ml/log_types/artifacts/dataset.py +384 -0
  228. truefoundry/ml/log_types/artifacts/general_artifact.py +110 -0
  229. truefoundry/ml/log_types/artifacts/model.py +611 -0
  230. truefoundry/ml/log_types/artifacts/model_extras.py +48 -0
  231. truefoundry/ml/log_types/artifacts/utils.py +161 -0
  232. truefoundry/ml/log_types/image/__init__.py +3 -0
  233. truefoundry/ml/log_types/image/constants.py +8 -0
  234. truefoundry/ml/log_types/image/image.py +357 -0
  235. truefoundry/ml/log_types/image/image_normalizer.py +102 -0
  236. truefoundry/ml/log_types/image/types.py +68 -0
  237. truefoundry/ml/log_types/plot.py +281 -0
  238. truefoundry/ml/log_types/pydantic_base.py +10 -0
  239. truefoundry/ml/log_types/utils.py +12 -0
  240. truefoundry/ml/logger.py +17 -0
  241. truefoundry/ml/mlfoundry_api.py +1575 -0
  242. truefoundry/ml/mlfoundry_run.py +1203 -0
  243. truefoundry/ml/run_utils.py +93 -0
  244. truefoundry/ml/session.py +168 -0
  245. truefoundry/ml/validation_utils.py +346 -0
  246. truefoundry/pydantic_v1.py +8 -1
  247. truefoundry/workflow/__init__.py +16 -1
  248. {truefoundry-0.3.4rc1.dist-info → truefoundry-0.4.0.dist-info}/METADATA +21 -14
  249. truefoundry-0.4.0.dist-info/RECORD +344 -0
  250. truefoundry/deploy/lib/clients/utils.py +0 -41
  251. truefoundry-0.3.4rc1.dist-info/RECORD +0 -136
  252. {truefoundry-0.3.4rc1.dist-info → truefoundry-0.4.0.dist-info}/WHEEL +0 -0
  253. {truefoundry-0.3.4rc1.dist-info → truefoundry-0.4.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1575 @@
1
+ import os
2
+ import time
3
+ import uuid
4
+ from pathlib import Path
5
+ from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union
6
+
7
+ import coolname
8
+ import pandas as pd
9
+
10
+ from truefoundry.ml import constants, env_vars
11
+ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
12
+ ArtifactDto,
13
+ ArtifactType,
14
+ CreateDatasetRequestDto,
15
+ CreateExperimentRequestDto,
16
+ CreateRunRequestDto,
17
+ DatasetDto,
18
+ ExperimentsApi,
19
+ ListArtifactsRequestDto,
20
+ ListArtifactVersionsRequestDto,
21
+ ListDatasetsRequestDto,
22
+ ListModelVersionsRequestDto,
23
+ MlfoundryArtifactsApi,
24
+ ModelDto,
25
+ RunsApi,
26
+ RunTagDto,
27
+ SearchRunsRequestDto,
28
+ )
29
+ from truefoundry.ml.autogen.client.exceptions import (
30
+ ApiException,
31
+ NotFoundException,
32
+ )
33
+ from truefoundry.ml.clients.servicefoundry_client import ServiceFoundryServiceClient
34
+ from truefoundry.ml.enums import ModelFramework, ViewType
35
+ from truefoundry.ml.exceptions import MlFoundryException
36
+ from truefoundry.ml.internal_namespace import NAMESPACE
37
+ from truefoundry.ml.log_types.artifacts.artifact import ArtifactPath, ArtifactVersion
38
+ from truefoundry.ml.log_types.artifacts.dataset import DataDirectory
39
+ from truefoundry.ml.log_types.artifacts.general_artifact import _log_artifact_version
40
+ from truefoundry.ml.log_types.artifacts.model import ModelVersion, _log_model_version
41
+ from truefoundry.ml.logger import logger
42
+ from truefoundry.ml.mlfoundry_run import MlFoundryRun
43
+ from truefoundry.ml.session import (
44
+ Session,
45
+ _get_api_client,
46
+ get_active_session,
47
+ init_session,
48
+ )
49
+ from truefoundry.ml.validation_utils import (
50
+ _validate_ml_repo_description,
51
+ _validate_ml_repo_name,
52
+ _validate_run_name,
53
+ )
54
+
55
+ _SEARCH_MAX_RESULTS_DEFAULT = 1000
56
+
57
+
58
+ def _get_internal_env_vars_values() -> Dict[str, str]:
59
+ env = {}
60
+ for env_var_name in env_vars.INTERNAL_ENV_VARS:
61
+ value = os.getenv(env_var_name)
62
+ if value:
63
+ env[env_var_name] = value
64
+
65
+ return env
66
+
67
+
68
+ def _resolve_version(version: Union[int, str]) -> int:
69
+ if not isinstance(version, int) and not (
70
+ isinstance(version, str) and version.isnumeric()
71
+ ):
72
+ raise MlFoundryException(
73
+ f"version must be an integer or string containing numbers only. Got {version!r}"
74
+ )
75
+ final_version = int(version)
76
+ if final_version <= 0:
77
+ raise ValueError("version must be greater than 0")
78
+ return final_version
79
+
80
+
81
+ class MlFoundry:
82
+ """MlFoundry."""
83
+
84
+ # TODO (chiragjn): Don't allow session as None here!
85
+ def __init__(self, session: Session):
86
+ """__init__
87
+
88
+ Args:
89
+ session (Optional[Session], optional): Session instance to get auth credentials from
90
+ """
91
+ self._tracking_uri: str = session.tracking_uri
92
+ self._api_client = _get_api_client(session=session)
93
+ self._experiments_api = ExperimentsApi(api_client=self._api_client)
94
+ self._runs_api = RunsApi(api_client=self._api_client)
95
+ self._mlfoundry_artifacts_api = MlfoundryArtifactsApi(
96
+ api_client=self._api_client
97
+ )
98
+
99
+ def _get_ml_repo_id(self, ml_repo: str) -> str:
100
+ """_get_ml_repo_id.
101
+
102
+ Args:
103
+ ml_repo (str): The name of the ML Repo.
104
+
105
+ Returns:
106
+ str: The id of the ML Repo.
107
+ """
108
+ try:
109
+ _ml_repo_instance = self._experiments_api.get_experiment_by_name_get(
110
+ experiment_name=ml_repo
111
+ )
112
+ ml_repo_instance = _ml_repo_instance.experiment
113
+ except NotFoundException:
114
+ err_msg = (
115
+ f"ML Repo Does Not Exist for name: {ml_repo}. You may either "
116
+ "create it from the dashboard or using client.create_ml_repo('<ml_repo_name>')"
117
+ )
118
+ raise MlFoundryException(err_msg) from None
119
+ except ApiException as e:
120
+ err_msg = (
121
+ f"Error happened in getting ML Repo based on name: "
122
+ f"{ml_repo}. Error details: {e}"
123
+ )
124
+ raise MlFoundryException(err_msg) from e
125
+
126
+ return ml_repo_instance.experiment_id
127
+
128
+ def list_ml_repos(self) -> List[str]:
129
+ """Returns a list of names of ML Repos accessible by the current user.
130
+
131
+ Returns:
132
+ List[str]: A list of names of ML Repos
133
+ """
134
+ # TODO (chiragjn): This API should yield ML Repo Entities instead of just names
135
+ # Kinda useless without it
136
+ ml_repos_names = []
137
+ done, page_token, max_results = False, None, 25
138
+ while not done:
139
+ try:
140
+ _ml_repos = self._experiments_api.list_experiments_get(
141
+ view_type=ViewType.ALL.value,
142
+ max_results=max_results,
143
+ page_token=page_token,
144
+ )
145
+ except ApiException as e:
146
+ err_msg = f"Error happened in fetching ML Repos. Error details: {e}"
147
+ raise MlFoundryException(err_msg) from e
148
+ else:
149
+ ml_repos = _ml_repos.experiments
150
+ page_token = _ml_repos.next_page_token
151
+ for ml_repo in ml_repos:
152
+ # ML Repo with experiment_id 0 represents default ML Repo which we are removing.
153
+ if ml_repo.experiment_id != "0":
154
+ ml_repos_names.append(ml_repo.name)
155
+ if not ml_repos or page_token is None:
156
+ done = True
157
+ return ml_repos_names
158
+
159
+ def create_ml_repo(
160
+ self,
161
+ name: str,
162
+ storage_integration_fqn: str,
163
+ description: Optional[str] = None,
164
+ ):
165
+ """Creates an ML Repository.
166
+
167
+ Args:
168
+ name (str): The name of the Repository you want to create.
169
+ storage_integration_fqn(str): The storage integration FQN to use for the experiment
170
+ for saving artifacts.
171
+ description (str): A description for ML Repo.
172
+
173
+ Examples:
174
+
175
+ ### Create Repository
176
+ ```python
177
+ from truefoundry.ml import get_client
178
+
179
+ client = get_client()
180
+
181
+ client.create_ml_repo(ml_repo="my-repo")
182
+ ```
183
+ """
184
+ _validate_ml_repo_name(ml_repo_name=name)
185
+ if description:
186
+ _validate_ml_repo_description(description=description)
187
+ try:
188
+ _ml_repo_instance = self._experiments_api.get_experiment_by_name_get(
189
+ experiment_name=name
190
+ )
191
+ existing_ml_repo = _ml_repo_instance.experiment
192
+ except NotFoundException:
193
+ existing_ml_repo = None
194
+
195
+ if not existing_ml_repo:
196
+ try:
197
+ self._experiments_api.create_experiment_post(
198
+ create_experiment_request_dto=CreateExperimentRequestDto(
199
+ name=name,
200
+ description=description,
201
+ storage_integration_fqn=storage_integration_fqn,
202
+ )
203
+ )
204
+ except ApiException as e:
205
+ err_msg = f"Error happened in creating ML Repo with name: {name}. Error details: {e}"
206
+ raise MlFoundryException(err_msg) from e
207
+ return
208
+
209
+ session = get_active_session()
210
+ if session is None:
211
+ raise MlFoundryException(
212
+ "No active session found. Perhaps you are not logged in?\n"
213
+ "Please log in using `tfy login [--host HOST] --relogin"
214
+ )
215
+ servicefoundry_client = ServiceFoundryServiceClient(
216
+ tracking_uri=self.get_tracking_uri(),
217
+ token=session.token.access_token,
218
+ )
219
+
220
+ assert existing_ml_repo.storage_integration_id is not None
221
+ try:
222
+ existing_storage_integration = (
223
+ servicefoundry_client.get_integration_from_id(
224
+ existing_ml_repo.storage_integration_id
225
+ )
226
+ )
227
+ except Exception as e:
228
+ raise MlFoundryException(
229
+ "Error in getting storage integration for ML Repo"
230
+ ) from e
231
+
232
+ if existing_storage_integration["fqn"] != storage_integration_fqn:
233
+ raise MlFoundryException(
234
+ f"ML Repo with same name already exists with storage integration:"
235
+ f"{existing_storage_integration['fqn']}. Cannot update the storage integration to: "
236
+ f"{storage_integration_fqn}"
237
+ )
238
+
239
+ def create_run(
240
+ self,
241
+ ml_repo: str,
242
+ run_name: Optional[str] = None,
243
+ tags: Optional[Dict[str, Any]] = None,
244
+ **kwargs,
245
+ ) -> MlFoundryRun:
246
+ """Initialize a `run`.
247
+
248
+ In a machine learning experiment `run` represents a single experiment
249
+ conducted under a ML Repo.
250
+ Args:
251
+ ml_repo (str): The name of the ML Repo under which the run will be created.
252
+ ml_repo should only contain alphanumerics (a-z,A-Z,0-9) or hyphen (-).
253
+ The user must have `ADMIN` or `WRITE` access to this ML Repo.
254
+ run_name (Optional[str], optional): The name of the run. If not passed, a randomly
255
+ generated name is assigned to the run. Under a ML Repo, all runs should have
256
+ a unique name. If the passed `run_name` is already used under a ML Repo, the
257
+ `run_name` will be de-duplicated by adding a suffix.
258
+ run name should only contain alphanumerics (a-z,A-Z,0-9) or hyphen (-).
259
+ tags (Optional[Dict[str, Any]], optional): Optional tags to attach with
260
+ this run. Tags are key-value pairs.
261
+ kwargs:
262
+
263
+ Returns:
264
+ MlFoundryRun: An instance of `MlFoundryRun` class which represents a `run`.
265
+
266
+ Examples:
267
+
268
+ ### Create a run under current user.
269
+ ```python
270
+ from truefoundry.ml import get_client
271
+
272
+ client = get_client()
273
+
274
+ tags = {"model_type": "svm"}
275
+ run = client.create_run(
276
+ ml_repo="my-classification-project", run_name="svm-with-rbf-kernel", tags=tags
277
+ )
278
+
279
+ run.end()
280
+ ```
281
+
282
+ ### Creating a run using context manager.
283
+ ```python
284
+ from truefoundry.ml import get_client
285
+
286
+ client = get_client()
287
+ with client.create_run(
288
+ ml_repo="my-classification-project", run_name="svm-with-rbf-kernel"
289
+ ) as run:
290
+ # ...
291
+ # Model training code
292
+ ...
293
+ # `run` will be automatically marked as `FINISHED` or `FAILED`.
294
+ ```
295
+
296
+ ### Create a run in a ML Repo owned by a different user.
297
+ ```python
298
+ from truefoundry.ml import get_client
299
+
300
+ client = get_client()
301
+
302
+ tags = {"model_type": "svm"}
303
+ run = client.create_run(
304
+ ml_repo="my-classification-project",
305
+ run_name="svm-with-rbf-kernel",
306
+ tags=tags,
307
+ )
308
+ run.end()
309
+ ```
310
+ """
311
+ if not run_name:
312
+ run_name = coolname.generate_slug(2)
313
+ logger.info(
314
+ f"No run_name given. Using a randomly generated name {run_name}."
315
+ " You can pass your own using the `run_name` argument"
316
+ )
317
+ _validate_run_name(name=run_name)
318
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
319
+ if tags is not None:
320
+ NAMESPACE.validate_namespace_not_used(tags.keys())
321
+ else:
322
+ tags = {}
323
+
324
+ tags.update(_get_internal_env_vars_values())
325
+ _run = self._runs_api.create_run_post(
326
+ CreateRunRequestDto(
327
+ user_id="unknown", # This does not matter, because on server we use the id from token
328
+ start_time=int(
329
+ time.time() * 1000
330
+ ), # TODO (chiragjn): computing start time should be on server side!
331
+ experiment_id=ml_repo_id,
332
+ name=run_name,
333
+ tags=[RunTagDto(key=k, value=v) for k, v in tags.items()],
334
+ )
335
+ )
336
+ run = _run.run
337
+ mlf_run_id = run.info.run_id
338
+ kwargs.setdefault("auto_end", True)
339
+ mlf_run = MlFoundryRun(experiment_id=ml_repo_id, run_id=mlf_run_id, **kwargs)
340
+ mlf_run._add_git_info()
341
+ mlf_run._add_python_truefoundry_version()
342
+ logger.info(f"Run {run.info.fqn!r} has started.")
343
+ logger.info(f"Link to the dashboard for the run: {mlf_run.dashboard_link}")
344
+ return mlf_run
345
+
346
+ def get_run_by_id(self, run_id: str) -> MlFoundryRun:
347
+ """Get an existing `run` by the `run_id`.
348
+
349
+ Args:
350
+ run_id (str): run_id or fqn of an existing `run`.
351
+
352
+ Returns:
353
+ MlFoundryRun: An instance of `MlFoundryRun` class which represents a `run`.
354
+
355
+ Examples:
356
+
357
+ ### Get run by the run id
358
+ ```python
359
+ from truefoundry.ml import get_client
360
+
361
+ client = get_client()
362
+
363
+ run = client.get_run_by_id(run_id='a8f6dafd70aa4baf9437a33c52d7ee90')
364
+ ```
365
+ """
366
+ if run_id == "" or (not isinstance(run_id, str)):
367
+ raise MlFoundryException(
368
+ f"run_id must be string type and not empty. "
369
+ f"Got {type(run_id)} type with value {run_id}"
370
+ )
371
+ if "/" in run_id:
372
+ return self.get_run_by_fqn(run_id)
373
+ _run = self._runs_api.get_run_get(run_id=run_id)
374
+ run = _run.run
375
+ mlfoundry_run = MlFoundryRun._from_dto(run)
376
+ logger.info(
377
+ f"Link to the dashboard for the run: {mlfoundry_run.dashboard_link}"
378
+ )
379
+ return mlfoundry_run
380
+
381
+ def get_run_by_fqn(self, run_fqn: str) -> MlFoundryRun:
382
+ """Get an existing `run` by `fqn`.
383
+
384
+ `fqn` stands for Fully Qualified Name. A run `fqn` has the following pattern:
385
+ tenant_name/ml_repo/run_name
386
+
387
+ If a run `svm` under the ML Repo `cat-classifier` in `truefoundry` tenant,
388
+ the `fqn` will be `truefoundry/cat-classifier/svm`.
389
+
390
+ Args:
391
+ run_fqn (str): `fqn` of an existing run.
392
+
393
+ Returns:
394
+ MlFoundryRun: An instance of `MlFoundryRun` class which represents a `run`.
395
+
396
+ Examples:
397
+
398
+ ### get run by run fqn
399
+ ```python
400
+ from truefoundry.ml import get_client
401
+
402
+ client = get_client()
403
+
404
+ run = client.get_run_by_fqn(run_fqn='truefoundry/my-repo/svm')
405
+ ```
406
+ """
407
+ _run = self._runs_api.get_run_by_fqn_get(run_fqn=run_fqn)
408
+ run = _run.run
409
+ mlfoundry_run = MlFoundryRun._from_dto(run)
410
+ logger.info(
411
+ f"Link to the dashboard for the run: {mlfoundry_run.dashboard_link}"
412
+ )
413
+ return mlfoundry_run
414
+
415
+ def get_run_by_name(
416
+ self,
417
+ ml_repo: str,
418
+ run_name: str,
419
+ ) -> MlFoundryRun:
420
+ """Get an existing `run` by `run_name`.
421
+
422
+ Args:
423
+ ml_repo (str): name of the ml_repo of which the run is part of.
424
+ run_name (str): the name of the run required
425
+
426
+ Returns:
427
+ MlFoundryRun: An instance of `MlFoundryRun` class which represents a `run`.
428
+
429
+ Examples:
430
+
431
+ ### get run by name
432
+ ```python
433
+ from truefoundry.ml import get_client
434
+
435
+ client = get_client()
436
+
437
+ run = client.get_run_by_name(run_name='svm', ml_repo='my-repo')
438
+ ```
439
+ """
440
+ _run = self._runs_api.get_run_by_name_get(
441
+ experiment_id=None,
442
+ run_name=run_name,
443
+ experiment_name=ml_repo,
444
+ )
445
+ run = _run.run
446
+ mlfoundry_run = MlFoundryRun._from_dto(run)
447
+ logger.info(
448
+ f"Link to the dashboard for the run: {mlfoundry_run.dashboard_link}"
449
+ )
450
+ return mlfoundry_run
451
+
452
+ def get_all_runs(
453
+ self,
454
+ ml_repo: str,
455
+ ) -> pd.DataFrame:
456
+ """Returns all the run name and id present under a ML Repo.
457
+
458
+ The user must have `READ` access to the ML Repo.
459
+
460
+ Args:
461
+ ml_repo (str): Name of the ML Repo.
462
+ Returns:
463
+ pd.DataFrame: dataframe with two columns- run_id and run_name
464
+
465
+ Examples:
466
+
467
+ ### get all the runs from a ml_repo
468
+ ```python
469
+ from truefoundry.ml import get_client
470
+
471
+ client = get_client()
472
+
473
+ run = client.get_all_runs(ml_repo='my-repo')
474
+ ```
475
+ """
476
+ runs = []
477
+ for run in self.search_runs(ml_repo=ml_repo):
478
+ runs.append((run.run_id, run.run_name))
479
+
480
+ if len(runs) == 0:
481
+ return pd.DataFrame(
482
+ columns=[constants.RUN_ID_COL_NAME, constants.RUN_NAME_COL_NAME]
483
+ )
484
+
485
+ return pd.DataFrame(
486
+ runs, columns=[constants.RUN_ID_COL_NAME, constants.RUN_NAME_COL_NAME]
487
+ )
488
+
489
+ def search_runs(
490
+ self,
491
+ ml_repo: str,
492
+ filter_string: str = "",
493
+ run_view_type: ViewType = ViewType.ACTIVE_ONLY.value,
494
+ order_by: Sequence[str] = ("attribute.start_time DESC",),
495
+ job_run_name: Optional[str] = None,
496
+ ) -> Iterator[MlFoundryRun]:
497
+ """
498
+ The user must have `READ` access to the ML Repo.
499
+ Returns an iterator that returns a MLFoundryRun on each next call.
500
+ All the runs under a ML Repo which matches the filter string and the run_view_type are returned.
501
+
502
+ Args:
503
+ ml_repo (str): Name of the ML Repo.
504
+ filter_string (str, optional):
505
+ Filter query string, defaults to searching all runs.
506
+ Identifier required in the LHS of a search expression.
507
+ Signifies an entity to compare against. An identifier has two parts separated by a period: the
508
+ type of the entity and the name of the entity.
509
+ The type of the entity is metrics, params, attributes, or tags.
510
+ The entity name can contain alphanumeric characters and special characters.
511
+ You can search using two run attributes : status and artifact_uri. Both attributes have string values.
512
+ When a metric, parameter, or tag name contains a special character like hyphen, space, period,
513
+ and so on, enclose the entity name in double quotes or backticks,
514
+ params."model-type" or params.`model-type`
515
+
516
+ run_view_type (str, optional): one of the following values "ACTIVE_ONLY", "DELETED_ONLY", or "ALL" runs.
517
+ order_by (List[str], optional):
518
+ List of columns to order by (e.g., "metrics.rmse"). Currently supported values
519
+ are metric.key, parameter.key, tag.key, attribute.key. The ``order_by`` column
520
+ can contain an optional ``DESC`` or ``ASC`` value. The default is ``ASC``.
521
+ The default ordering is to sort by ``start_time DESC``.
522
+
523
+ job_run_name (str): Name of the job which are associated with the runs to get that runs
524
+
525
+ Returns:
526
+ Iterator[MlFoundryRun]: MLFoundryRuns matching the search query.
527
+
528
+ Examples:
529
+
530
+ ```python
531
+ from truefoundry.ml import get_client
532
+
533
+ client = get_client()
534
+ with client.create_run(ml_repo="my-project", run_name="run-1") as run1:
535
+ run1.log_metrics(metric_dict={"accuracy": 0.74, "loss": 0.6})
536
+ run1.log_params({"model": "LogisticRegression", "lambda": "0.001"})
537
+
538
+ with client.create_run(ml_repo="my-project", run_name="run-2") as run2:
539
+ run2.log_metrics(metric_dict={"accuracy": 0.8, "loss": 0.4})
540
+ run2.log_params({"model": "SVM"})
541
+
542
+ # Search for the subset of runs with logged accuracy metric greater than 0.75
543
+ filter_string = "metrics.accuracy > 0.75"
544
+ runs = client.search_runs(ml_repo="my-project", filter_string=filter_string)
545
+
546
+ # Search for the subset of runs with logged accuracy metric greater than 0.7
547
+ filter_string = "metrics.accuracy > 0.7"
548
+ runs = client.search_runs(ml_repo="my-project", filter_string=filter_string)
549
+
550
+ # Search for the subset of runs with logged accuracy metric greater than 0.7 and model="LogisticRegression"
551
+ filter_string = "metrics.accuracy > 0.7 and params.model = 'LogisticRegression'"
552
+ runs = client.search_runs(ml_repo="my-project", filter_string=filter_string)
553
+
554
+ # Search for the subset of runs with logged accuracy metric greater than 0.7 and
555
+ # order by accuracy in Descending order
556
+ filter_string = "metrics.accuracy > 0.7"
557
+ order_by = ["metric.accuracy DESC"]
558
+ runs = client.search_runs(
559
+ ml_repo="my-project", filter_string=filter_string, order_by=order_by
560
+ )
561
+
562
+ filter_string = "metrics.accuracy > 0.7"
563
+ runs = client.search_runs(
564
+ ml_repo="transformers", order_by=order_by ,job_run_name='job_run_name', filter_string=filter_string
565
+ )
566
+
567
+ order_by = ["metric.accuracy DESC"]
568
+ runs = client.search_runs(
569
+ ml_repo="my-project", filter_string=filter_string, order_by=order_by, max_results=10
570
+ )
571
+ ```
572
+ """
573
+ _validate_ml_repo_name(ml_repo_name=ml_repo)
574
+ try:
575
+ _ml_repo_obj = self._experiments_api.get_experiment_by_name_get(
576
+ experiment_name=ml_repo
577
+ )
578
+ ml_repo_obj = _ml_repo_obj.experiment
579
+ except ApiException as e:
580
+ raise MlFoundryException(
581
+ f"ML Repo with name {ml_repo} does not exist or your user does not have permission to access it: {e}"
582
+ ) from e
583
+
584
+ ml_repo_id = ml_repo_obj.experiment_id
585
+
586
+ page_token = None
587
+ done = False
588
+ if job_run_name:
589
+ if filter_string == "":
590
+ filter_string = f"tags.TFY_INTERNAL_JOB_RUN_NAME = '{job_run_name}'"
591
+ else:
592
+ filter_string += (
593
+ f" and tags.TFY_INTERNAL_JOB_RUN_NAME = '{job_run_name}'"
594
+ )
595
+ while not done:
596
+ runs_page = self._runs_api.search_runs_post(
597
+ SearchRunsRequestDto(
598
+ experiment_ids=[ml_repo_id],
599
+ filter=filter_string,
600
+ run_view_type=run_view_type,
601
+ max_results=_SEARCH_MAX_RESULTS_DEFAULT,
602
+ order_by=order_by,
603
+ page_token=page_token,
604
+ )
605
+ )
606
+ runs = runs_page.runs
607
+ page_token = runs_page.next_page_token
608
+
609
+ for run in runs:
610
+ yield MlFoundryRun._from_dto(run)
611
+ if not runs or page_token is None:
612
+ done = True
613
+
614
+ def get_tracking_uri(self) -> str:
615
+ """
616
+ Get the current tracking URI.
617
+
618
+ Returns:
619
+ The tracking URI.
620
+
621
+ Examples:
622
+
623
+ ```python
624
+ import tempfile
625
+ from truefoundry.ml import get_client
626
+
627
+ client = get_client()
628
+ tracking_uri = client.get_tracking_uri()
629
+ print("Current tracking uri: {}".format(tracking_uri))
630
+ ```
631
+ """
632
+ return self._tracking_uri
633
+
634
+ def get_model_version(
635
+ self,
636
+ ml_repo: str,
637
+ name: str,
638
+ version: Union[str, int] = constants.LATEST_ARTIFACT_OR_MODEL_VERSION,
639
+ ) -> Optional[ModelVersion]:
640
+ """
641
+ Get the model version to download contents or load it in memory
642
+
643
+ Args:
644
+ ml_repo (str): ML Repo to which model is logged
645
+ name (str): Model Name
646
+ version (str | int): Model Version to fetch (default is the latest version)
647
+
648
+ Returns:
649
+ ModelVersion: The ModelVersion instance of the model.
650
+
651
+ Examples:
652
+
653
+ ### Sklearn
654
+
655
+ ```python
656
+ # See `truefoundry.ml.mlfoundry_api.MlFoundry.log_model` examples to understand model logging
657
+ import tempfile
658
+ import joblib
659
+ from truefoundry.ml import get_client
660
+
661
+ client = get_client()
662
+ model_version = client.get_model_version(
663
+ ml_repo="my-classification-project",
664
+ name="my-sklearn-model",
665
+ version=1
666
+ )
667
+
668
+ # Download the model to disk
669
+ temp = tempfile.TemporaryDirectory()
670
+ download_info = model_version.download(path=temp.name)
671
+ print(download_info.model_dir, download_info.model_filename)
672
+
673
+ # Deserialize and Load
674
+ model = joblib.load(
675
+ os.path.join(download_info.model_dir, download_info.model_filename)
676
+ )
677
+ ```
678
+
679
+ ### Huggingface Transformers
680
+
681
+ ```python
682
+ # See `truefoundry.ml.mlfoundry_api.MlFoundry.log_model` examples to understand model logging
683
+ import torch
684
+ from transformers import pipeline
685
+
686
+ from truefoundry.ml import get_client
687
+
688
+ client = get_client()
689
+ model_version = client.get_model_version(
690
+ ml_repo="my-llm-project",
691
+ name="my-transformers-model",
692
+ version=1
693
+ )
694
+
695
+ # Download the model to disk
696
+ temp = tempfile.TemporaryDirectory()
697
+ download_info = model_version.download(path=temp.name)
698
+ print(download_info.model_dir)
699
+
700
+ # Deserialize and Load
701
+ pln = pipeline("text-generation", model=download_info.model_dir, torch_dtype=torch.float16)
702
+ ```
703
+ """
704
+ resolved_version = None
705
+ if version != constants.LATEST_ARTIFACT_OR_MODEL_VERSION:
706
+ resolved_version = _resolve_version(version=version)
707
+
708
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
709
+
710
+ _model_version = self._mlfoundry_artifacts_api.get_model_version_by_name_get(
711
+ experiment_id=int(ml_repo_id),
712
+ model_name=name,
713
+ version=resolved_version,
714
+ )
715
+ model_version = _model_version.model_version
716
+ _model = self._mlfoundry_artifacts_api.get_model_get(id=model_version.model_id)
717
+ model = _model.model
718
+
719
+ return ModelVersion(
720
+ model_version=model_version,
721
+ model=model,
722
+ )
723
+
724
+ def get_model_version_by_fqn(self, fqn: str) -> ModelVersion:
725
+ """
726
+ Get the model version to download contents or load it in memory
727
+
728
+ Args:
729
+ fqn (str): Fully qualified name of the model version.
730
+
731
+ Returns:
732
+ ModelVersion: The ModelVersion instance of the model.
733
+
734
+ Examples:
735
+
736
+ ### Sklearn
737
+
738
+ ```python
739
+ # See `truefoundry.ml.mlfoundry_api.MlFoundry.log_model` examples to understand model logging
740
+ import tempfile
741
+ import joblib
742
+ from truefoundry.ml import get_client
743
+
744
+ client = get_client()
745
+ model_version = client.get_model_version_by_fqn(
746
+ fqn="model:truefoundry/my-classification-project/my-sklearn-model:1"
747
+ )
748
+
749
+ # Download the model to disk
750
+ temp = tempfile.TemporaryDirectory()
751
+ download_info = model_version.download(path=temp.name)
752
+ print(download_info.model_dir, download_info.model_filename)
753
+
754
+ # Deserialize and Load
755
+ model = joblib.load(
756
+ os.path.join(download_info.model_dir, download_info.model_filename)
757
+ )
758
+ ```
759
+
760
+ ### Huggingface Transformers
761
+
762
+ ```python
763
+ # See `truefoundry.ml.mlfoundry_api.MlFoundry.log_model` examples to understand model logging
764
+ import torch
765
+ from transformers import pipeline
766
+
767
+ from truefoundry.ml import get_client
768
+
769
+ client = get_client()
770
+ model_version = client.get_model_version_by_fqn(
771
+ fqn="model:truefoundry/my-llm-project/my-transformers-model:1"
772
+ )
773
+ # Download the model to disk
774
+ temp = tempfile.TemporaryDirectory()
775
+ download_info = model_version.download(path=temp.name)
776
+ print(download_info.model_dir)
777
+
778
+ # Deserialize and Load
779
+ pln = pipeline("text-generation", model=download_info.model_dir, torch_dtype=torch.float16)
780
+ ```
781
+ """
782
+ return ModelVersion.from_fqn(fqn=fqn)
783
+
784
+ def list_model_versions(self, ml_repo: str, name: str) -> Iterator[ModelVersion]:
785
+ """
786
+ Get all the version of a model to download contents or load them in memory
787
+
788
+ Args:
789
+ ml_repo (str): Repository in which the model is stored.
790
+ name (str): Name of the model whose version is required
791
+
792
+ Returns:
793
+ Iterator[ModelVersion]: An iterator that yields non deleted model versions
794
+ of a model under a given ml_repo sorted reverse by the version number
795
+
796
+ Examples:
797
+
798
+ ```python
799
+ from truefoundry.ml import get_client
800
+
801
+ client = get_client()
802
+ model_versions = client.list_model_version(ml_repo="my-repo", name="svm")
803
+
804
+ for model_version in model_versions:
805
+ print(model_version)
806
+ ```
807
+ """
808
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
809
+ try:
810
+ _model = self._mlfoundry_artifacts_api.get_model_by_name_get(
811
+ experiment_id=int(ml_repo_id), name=name
812
+ )
813
+ except NotFoundException as e:
814
+ err_msg = (
815
+ f"Model Does Not Exist for ml_repo={ml_repo}, name={name}. Error: {e}"
816
+ )
817
+ raise MlFoundryException(err_msg) from e
818
+ model = _model.model
819
+ return self._list_model_versions_by_id(model=model)
820
+
821
+ def list_model_versions_by_fqn(self, model_fqn: str) -> Iterator[ModelVersion]:
822
+ """
823
+ List versions for a given model
824
+
825
+ Args:
826
+ model_fqn: FQN of the Model to list versions for.
827
+ A model_fqn looks like `model:{org}/{user}/{project}/{artifact_name}`
828
+ or `model:{user}/{project}/{artifact_name}`
829
+
830
+ Returns:
831
+ Iterator[ModelVersion]: An iterator that yields non deleted model versions
832
+ under the given model_fqn sorted reverse by the version number
833
+
834
+ Yields:
835
+ ModelVersion: An instance of `truefoundry.ml.ModelVersion`
836
+
837
+ Examples:
838
+
839
+ ```python
840
+ from truefoundry.ml import get_client
841
+
842
+ client = get_client()
843
+ model_fqn = "model:org/my-project/my-model"
844
+ for mv in client.list_model_versions(model_fqn=model_fqn):
845
+ print(mv.name, mv.version, mv.description)
846
+ ```
847
+ """
848
+ _model = self._mlfoundry_artifacts_api.get_model_by_fqn_get(fqn=model_fqn)
849
+ model = _model.model
850
+ return self._list_model_versions_by_id(model=model)
851
+
852
+ def _list_model_versions_by_id(
853
+ self,
854
+ model_id: Optional[uuid.UUID] = None,
855
+ model: Optional[ModelDto] = None,
856
+ ) -> Iterator[ModelVersion]:
857
+ if model and not model_id:
858
+ model_id = model.id
859
+ elif not model and model_id:
860
+ _model = self._mlfoundry_artifacts_api.get_model_get(id=str(model_id))
861
+ model = _model.model
862
+ else:
863
+ raise MlFoundryException(
864
+ "Exactly one of model_id or model should be passed"
865
+ )
866
+
867
+ max_results, page_token, done = 10, None, False
868
+ while not done:
869
+ _model_versions = self._mlfoundry_artifacts_api.list_model_versions_post(
870
+ list_model_versions_request_dto=ListModelVersionsRequestDto(
871
+ model_id=str(model_id),
872
+ max_results=max_results,
873
+ page_token=page_token,
874
+ )
875
+ )
876
+ model_versions = _model_versions.model_versions
877
+ page_token = _model_versions.next_page_token
878
+ for model_version in model_versions:
879
+ yield ModelVersion(model_version=model_version, model=model)
880
+ if not model_versions or not page_token:
881
+ done = True
882
+
883
+ def get_artifact_version(
884
+ self,
885
+ ml_repo: str,
886
+ name: str,
887
+ artifact_type: Optional[ArtifactType] = ArtifactType.ARTIFACT,
888
+ version: Union[str, int] = constants.LATEST_ARTIFACT_OR_MODEL_VERSION,
889
+ ) -> Optional[ArtifactVersion]:
890
+ """
891
+ Get the model version to download contents or load it in memory
892
+
893
+ Args:
894
+ ml_repo (str): ML Repo to which artifact is logged
895
+ name (str): Artifact Name
896
+ artifact_type (str): The type of artifact to fetch (acceptable values: "artifact", "model", "plot", "image")
897
+ version (str | int): Artifact Version to fetch (default is the latest version)
898
+
899
+ Returns:
900
+ ArtifactVersion : An ArtifactVersion instance of the artifact
901
+
902
+ Examples:
903
+
904
+ ```python
905
+ import tempfile
906
+ from truefoundry.ml import get_client
907
+
908
+ client = get_client()
909
+ artifact_version = client.get_artifact_version(ml_repo="ml-repo-name", name="artifact-name", version=1)
910
+
911
+ # download the artifact to disk
912
+ temp = tempfile.TemporaryDirectory()
913
+ download_path = artifact_version.download(path=temp.name)
914
+ print(download_path)
915
+ ```
916
+ """
917
+ resolved_version = None
918
+ if version != constants.LATEST_ARTIFACT_OR_MODEL_VERSION:
919
+ resolved_version = _resolve_version(version=version)
920
+
921
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
922
+
923
+ _artifact_version = (
924
+ self._mlfoundry_artifacts_api.get_artifact_version_by_name_get(
925
+ experiment_id=int(ml_repo_id),
926
+ artifact_name=name,
927
+ artifact_type=artifact_type,
928
+ version=resolved_version,
929
+ )
930
+ )
931
+ artifact_version = _artifact_version.artifact_version
932
+ _artifact = self._mlfoundry_artifacts_api.get_artifact_by_id_get(
933
+ id=artifact_version.artifact_id
934
+ )
935
+ artifact = _artifact.artifact
936
+
937
+ return ArtifactVersion(
938
+ artifact_version=artifact_version,
939
+ artifact=artifact,
940
+ )
941
+
942
+ def get_artifact_version_by_fqn(self, fqn: str) -> ArtifactVersion:
943
+ """
944
+ Get the artifact version to download contents
945
+
946
+ Args:
947
+ fqn (str): Fully qualified name of the artifact version.
948
+
949
+ Returns:
950
+ ArtifactVersion : An ArtifactVersion instance of the artifact
951
+
952
+ Examples:
953
+
954
+ ```python
955
+ import tempfile
956
+ from truefoundry.ml import get_client
957
+
958
+ client = get_client()
959
+ artifact_version = client.get_artifact_version_by_fqn(
960
+ fqn="artifact:truefoundry/my-classification-project/sklearn-artifact:1"
961
+ )
962
+
963
+ # download the artifact to disk
964
+ temp = tempfile.TemporaryDirectory()
965
+ download_path = artifact_version.download(path=temp.name)
966
+ print(download_path)
967
+ ```
968
+ """
969
+ return ArtifactVersion.from_fqn(fqn=fqn)
970
+
971
+ def list_artifact_versions(
972
+ self,
973
+ ml_repo: str,
974
+ name: str,
975
+ artifact_type: Optional[ArtifactType] = ArtifactType.ARTIFACT,
976
+ ) -> Iterator[ArtifactVersion]:
977
+ """
978
+ Get all the version of na artifact to download contents or load them in memory
979
+
980
+ Args:
981
+ ml_repo (str): Repository in which the model is stored.
982
+ name (str): Name of the artifact whose version is required
983
+ artifact_type (ArtifactType): Type of artifact you want for example model, image, etc.
984
+
985
+ Returns:
986
+ Iterator[ArtifactVersion]: An iterator that yields non deleted artifact-versions
987
+ of an artifact under a given ml_repo sorted reverse by the version number
988
+
989
+ Examples:
990
+
991
+ ```python
992
+ from truefoundry.ml import get_client
993
+
994
+ client = get_client()
995
+ artifact_versions = client.list_artifact_versions(ml_repo="my-repo", name="artifact-name")
996
+
997
+ for artifact_version in artifact_versions:
998
+ print(artifact_version)
999
+ ```
1000
+ """
1001
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1002
+ _artifacts = self._mlfoundry_artifacts_api.list_artifacts_post(
1003
+ list_artifacts_request_dto=ListArtifactsRequestDto(
1004
+ experiment_id=ml_repo_id,
1005
+ name=name,
1006
+ artifact_types=[artifact_type] if artifact_type else None,
1007
+ max_results=1,
1008
+ )
1009
+ )
1010
+ artifacts = _artifacts.artifacts
1011
+ if not artifacts or len(artifacts) == 0:
1012
+ err_msg = f"Artifact Does Not Exist for ml_repo={ml_repo}, name={name}, type={artifact_type}"
1013
+ raise MlFoundryException(err_msg)
1014
+ return self._list_artifact_versions_by_id(artifact=artifacts[0])
1015
+
1016
+ def list_artifact_versions_by_fqn(
1017
+ self, artifact_fqn: str
1018
+ ) -> Iterator[ArtifactVersion]:
1019
+ """
1020
+ List versions for a given artifact
1021
+
1022
+ Args:
1023
+ artifact_fqn: FQN of the Artifact to list versions for.
1024
+ An artifact_fqn looks like `{artifact_type}:{org}/{user}/{project}/{artifact_name}`
1025
+ or `{artifact_type}:{user}/{project}/{artifact_name}`
1026
+
1027
+ where artifact_type can be on of ("model", "image", "plot")
1028
+
1029
+ Returns:
1030
+ Iterator[ArtifactVersion]: An iterator that yields non deleted artifact versions
1031
+ under the given artifact_fqn sorted reverse by the version number
1032
+
1033
+ Yields:
1034
+ ArtifactVersion: An instance of `truefoundry.ml.ArtifactVersion`
1035
+
1036
+ Examples:
1037
+
1038
+ ```python
1039
+ from truefoundry.ml import get_client
1040
+
1041
+ client = get_client()
1042
+ artifact_fqn = "artifact:org/my-project/my-artifact"
1043
+ for av in client.list_artifact_versions(artifact_fqn=artifact_fqn):
1044
+ print(av.name, av.version, av.description)
1045
+ ```
1046
+ """
1047
+
1048
+ _artifact = self._mlfoundry_artifacts_api.get_artifact_by_fqn_get(
1049
+ fqn=artifact_fqn
1050
+ )
1051
+ artifact = _artifact.artifact
1052
+ return self._list_artifact_versions_by_id(artifact=artifact)
1053
+
1054
+ def _list_artifact_versions_by_id(
1055
+ self,
1056
+ artifact_id: Optional[uuid.UUID] = None,
1057
+ artifact: Optional[ArtifactDto] = None,
1058
+ ) -> Iterator[ArtifactVersion]:
1059
+ if artifact and not artifact_id:
1060
+ artifact_id = artifact.id
1061
+ elif not artifact and artifact_id:
1062
+ _artifact = self._mlfoundry_artifacts_api.get_artifact_by_id_get(
1063
+ id=str(artifact_id)
1064
+ )
1065
+ artifact = _artifact.artifact
1066
+ else:
1067
+ raise MlFoundryException(
1068
+ "Exactly one of artifact_id or artifact should be passed"
1069
+ )
1070
+
1071
+ max_results, page_token, done = 10, None, False
1072
+ while not done:
1073
+ _artifact_versions = (
1074
+ self._mlfoundry_artifacts_api.list_artifact_versions_post(
1075
+ list_artifact_versions_request_dto=ListArtifactVersionsRequestDto(
1076
+ artifact_id=str(artifact_id),
1077
+ max_results=max_results,
1078
+ page_token=page_token,
1079
+ )
1080
+ )
1081
+ )
1082
+ artifact_versions = _artifact_versions.artifact_versions
1083
+ page_token = _artifact_versions.next_page_token
1084
+ for artifact_version in artifact_versions:
1085
+ yield ArtifactVersion(
1086
+ artifact_version=artifact_version, artifact=artifact
1087
+ )
1088
+ if not artifact_versions or not page_token:
1089
+ done = True
1090
+
1091
+ def log_artifact(
1092
+ self,
1093
+ ml_repo: str,
1094
+ name: str,
1095
+ artifact_paths: List[
1096
+ Union[Tuple[str], Tuple[str, Optional[str]], ArtifactPath]
1097
+ ],
1098
+ description: Optional[str] = None,
1099
+ metadata: Optional[Dict[str, Any]] = None,
1100
+ progress: Optional[bool] = None,
1101
+ ) -> Optional[ArtifactVersion]:
1102
+ """Logs an artifact for the current `ml_repo`.
1103
+
1104
+ An `artifact` is a list of local files and directories.
1105
+ This function packs the mentioned files and directories in `artifact_paths`
1106
+ and uploads them to remote storage linked to the ml_repo
1107
+
1108
+ Args:
1109
+ ml_repo (str): Name of the ML Repo to which an artifact is to be logged.
1110
+ name (str): Name of the Artifact. If an artifact with this name already exists under the current ml_repo,
1111
+ the logged artifact will be added as a new version under that `name`. If no artifact exist with
1112
+ the given `name`, the given artifact will be logged as version 1.
1113
+ artifact_paths (List[truefoundry.ml.ArtifactPath], optional): A list of pairs
1114
+ of (source path, destination path) to add files and folders
1115
+ to the artifact version contents. The first member of the pair should be a file or directory path
1116
+ and the second member should be the path inside the artifact contents to upload to.
1117
+ progress (bool): value to show progress bar, defaults to None.
1118
+
1119
+ ```python
1120
+
1121
+ from truefoundry.ml import get_client, ArtifactPath
1122
+
1123
+ client = get_client()
1124
+ client.log_artifact(
1125
+ ml_repo="sample-repo",
1126
+ name="xyz",
1127
+ artifact_paths=[
1128
+ ArtifactPath("foo.txt", "foo/bar/foo.txt"),
1129
+ ArtifactPath("tokenizer/", "foo/tokenizer/"),
1130
+ ArtifactPath('bar.text'),
1131
+ ('bar.txt', ),
1132
+ ('foo.txt', 'a/foo.txt')
1133
+ ]
1134
+ )
1135
+ ```
1136
+
1137
+ would result in
1138
+
1139
+ ```
1140
+ .
1141
+ └── foo/
1142
+ ├── bar/
1143
+ │ └── foo.txt
1144
+ └── tokenizer/
1145
+ └── # contents of tokenizer/ directory will be uploaded here
1146
+ ```
1147
+ description (Optional[str], optional): arbitrary text upto 1024 characters to store as description.
1148
+ This field can be updated at any time after logging. Defaults to `None`
1149
+ metadata (Optional[Dict[str, Any]], optional): arbitrary json serializable dictionary to store metadata.
1150
+ For example, you can use this to store metrics, params, notes.
1151
+ This field can be updated at any time after logging. Defaults to `None`
1152
+
1153
+ Returns:
1154
+ truefoundry.ml.ArtifactVersion: an instance of `ArtifactVersion` that can be used to download the files,
1155
+ or update attributes like description, metadata.
1156
+
1157
+ Examples:
1158
+
1159
+ ```python
1160
+ import os
1161
+ from truefoundry.ml import get_client, ArtifactPath
1162
+
1163
+ with open("artifact.txt", "w") as f:
1164
+ f.write("hello-world")
1165
+
1166
+ client = get_client()
1167
+ ml_repo = "sample-repo"
1168
+
1169
+ client.create_ml_repo(ml_repo=ml_repo)
1170
+ client.log_artifact(
1171
+ ml_repo=ml_repo,
1172
+ name="hello-world-file",
1173
+ artifact_paths=[ArtifactPath('artifact.txt', 'a/b/')]
1174
+ )
1175
+ ```
1176
+ """
1177
+ if not artifact_paths:
1178
+ raise MlFoundryException(
1179
+ "artifact_paths cannot be empty, atleast one artifact_path must be passed"
1180
+ )
1181
+
1182
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1183
+ artifact_version = _log_artifact_version(
1184
+ run=None,
1185
+ mlfoundry_artifacts_api=self._mlfoundry_artifacts_api,
1186
+ ml_repo_id=ml_repo_id,
1187
+ name=name,
1188
+ artifact_paths=artifact_paths,
1189
+ description=description,
1190
+ metadata=metadata,
1191
+ step=None,
1192
+ progress=progress,
1193
+ )
1194
+ logger.info(f"Logged artifact successfully with fqn {artifact_version.fqn!r}")
1195
+ return artifact_version
1196
+
1197
+ def log_model(
1198
+ self,
1199
+ *,
1200
+ ml_repo: str,
1201
+ name: str,
1202
+ model_file_or_folder: str,
1203
+ framework: Optional[Union[ModelFramework, str]],
1204
+ additional_files: Sequence[Tuple[Union[str, Path], Optional[str]]] = (),
1205
+ description: Optional[str] = None,
1206
+ metadata: Optional[Dict[str, Any]] = None,
1207
+ progress: Optional[bool] = None,
1208
+ ) -> ModelVersion:
1209
+ """
1210
+ Serialize and log a versioned model under the current ml_repo. Each logged model generates a new version
1211
+ associated with the given `name` and linked to the current run. Multiple versions of the model can be
1212
+ logged as separate versions under the same `name`.
1213
+
1214
+ Args:
1215
+ ml_repo (str): Name of the ML Repo to which an artifact is to be logged.
1216
+ name (str): Name of the model. If a model with this name already exists under the current ML Repo,
1217
+ the logged model will be added as a new version under that `name`. If no models exist with the given
1218
+ `name`, the given model will be logged as version 1.
1219
+ model_file_or_folder (str): Path to either a single file or a folder containing model files. This folder
1220
+ is usually created using serialization methods of libraries or frameworks e.g. `joblib.dump`,
1221
+ `model.save_pretrained(...)`, `torch.save(...)`, `model.save(...)`
1222
+ framework (Union[enums.ModelFramework, str]): Model Framework. Ex:- pytorch, sklearn, tensorflow etc.
1223
+ The full list of supported frameworks can be found in `truefoundry.ml.enums.ModelFramework`.
1224
+ Can also be `None` when `model` is `None`.
1225
+ additional_files (Sequence[Tuple[Union[str, Path], Optional[str]]], optional): A list of pairs
1226
+ of (source path, destination path) to add additional files and folders
1227
+ to the model version contents. The first member of the pair should be a file or directory path
1228
+ and the second member should be the path inside the model versions contents to upload to.
1229
+ The model version contents are arranged like follows
1230
+ .
1231
+ └── model/
1232
+ └── # model files are serialized here
1233
+ └── # any additional files and folders can be added here.
1234
+
1235
+ You can also add additional files to model/ subdirectory by specifying the destination path as model/
1236
+
1237
+ ```python
1238
+ run.log_model(
1239
+ name="xyz",
1240
+ model_file_or_folder="clf.joblib",
1241
+ framework="sklearn",
1242
+ additional_files=[("foo.txt", "foo/bar/foo.txt"), ("tokenizer/", "foo/tokenizer/")]
1243
+ )
1244
+ ```
1245
+
1246
+ would result in
1247
+
1248
+ ```
1249
+ .
1250
+ ├── model/
1251
+ │ └── clf.joblib # if `model_file_or_folder` is a folder, contents will be added here
1252
+ └── foo/
1253
+ ├── bar/
1254
+ │ └── foo.txt
1255
+ └── tokenizer/
1256
+ └── # contents of tokenizer/ directory will be uploaded here
1257
+ ```
1258
+ description (Optional[str], optional): arbitrary text upto 1024 characters to store as description.
1259
+ This field can be updated at any time after logging. Defaults to `None`
1260
+ metadata (Optional[Dict[str, Any]], optional): arbitrary json serializable dictionary to store metadata.
1261
+ For example, you can use this to store metrics, params, notes.
1262
+ This field can be updated at any time after logging. Defaults to `None`
1263
+ progress (bool): value to show progress bar, defaults to None.
1264
+
1265
+ Returns:
1266
+ truefoundry.ml.ModelVersion: an instance of `ModelVersion` that can be used to download the files,
1267
+ load the model, or update attributes like description, metadata, schema.
1268
+
1269
+ Examples:
1270
+
1271
+ ### Sklearn
1272
+
1273
+ ```python
1274
+ from truefoundry.ml import get_client
1275
+ from truefoundry.ml.enums import ModelFramework
1276
+
1277
+ import joblib
1278
+ import numpy as np
1279
+ from sklearn.pipeline import make_pipeline
1280
+ from sklearn.preprocessing import StandardScaler
1281
+ from sklearn.svm import SVC
1282
+
1283
+ X = np.array([[-1, -1], [-2, -1], [1, 1], [2, 1]])
1284
+ y = np.array([1, 1, 2, 2])
1285
+ clf = make_pipeline(StandardScaler(), SVC(gamma='auto'))
1286
+ clf.fit(X, y)
1287
+ joblib.dump(clf, "sklearn-pipeline.joblib")
1288
+
1289
+ client = get_client()
1290
+ client.create_ml_repo( # This is only required once
1291
+ ml_repo="my-classification-project",
1292
+ # This controls which bucket is used.
1293
+ # You can get this from Integrations > Blob Storage. `None` picks the default
1294
+ storage_integration_fqn=None
1295
+ )
1296
+ model_version = client.log_model(
1297
+ ml_repo="my-classification-project",
1298
+ name="my-sklearn-model",
1299
+ model_file_or_folder="sklearn-pipeline.joblib",
1300
+ framework=ModelFramework.SKLEARN,
1301
+ metadata={"accuracy": 0.99, "f1": 0.80},
1302
+ step=1, # step number, useful when using iterative algorithms like SGD
1303
+ )
1304
+ print(model_version.fqn)
1305
+ ```
1306
+
1307
+ ### Huggingface Transformers
1308
+
1309
+ ```python
1310
+ from truefoundry.ml import get_client
1311
+ from truefoundry.ml.enums import ModelFramework
1312
+
1313
+ import torch
1314
+ from transformers import AutoTokenizer, AutoConfig, pipeline, AutoModelForCausalLM
1315
+ pln = pipeline(
1316
+ "text-generation",
1317
+ model_file_or_folder="EleutherAI/pythia-70m",
1318
+ tokenizer="EleutherAI/pythia-70m",
1319
+ torch_dtype=torch.float16
1320
+ )
1321
+ pln.model.save_pretrained("my-transformers-model")
1322
+ pln.tokenizer.save_pretrained("my-transformers-model")
1323
+
1324
+ client = get_client()
1325
+ client.create_ml_repo( # This is only required once
1326
+ ml_repo="my-llm-project",
1327
+ # This controls which bucket is used.
1328
+ # You can get this from Integrations > Blob Storage. `None` picks the default
1329
+ storage_integration_fqn=None
1330
+ )
1331
+ model_version = client.log_model(
1332
+ ml_repo="my-llm-project",
1333
+ name="my-transformers-model",
1334
+ model_file_or_folder="my-transformers-model/",
1335
+ framework=ModelFramework.TRANSFORMERS
1336
+ )
1337
+ print(model_version.fqn)
1338
+ ```
1339
+
1340
+ """
1341
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1342
+
1343
+ model_version = _log_model_version(
1344
+ run=None,
1345
+ mlfoundry_artifacts_api=self._mlfoundry_artifacts_api,
1346
+ ml_repo_id=ml_repo_id,
1347
+ name=name,
1348
+ model_file_or_folder=model_file_or_folder,
1349
+ framework=framework,
1350
+ additional_files=additional_files,
1351
+ description=description,
1352
+ metadata=metadata,
1353
+ step=None,
1354
+ progress=progress,
1355
+ )
1356
+ logger.info(f"Logged model successfully with fqn {model_version.fqn!r}")
1357
+ return model_version
1358
+
1359
+ # Datasets API
1360
+ def create_data_directory(
1361
+ self,
1362
+ ml_repo: str,
1363
+ name: str,
1364
+ description: Optional[str] = None,
1365
+ metadata: Optional[Dict[str, Any]] = None,
1366
+ ) -> DataDirectory:
1367
+ """
1368
+ Create DataDirectory to Upload the files
1369
+
1370
+ Args:
1371
+ ml_repo (str): Name of the ML Repo in which you want to create data_directory
1372
+ name (str): Name of the DataDirectory to be created.
1373
+ description (str): Description for the DataDirectory.
1374
+ metadata (Dict <str>: Any): Metadata about the data_directory in Dictionary form.
1375
+
1376
+ Returns:
1377
+ DataDirectory : An instance of class DataDirectory
1378
+
1379
+ Examples:
1380
+
1381
+ ```python
1382
+ from truefoundry.ml import get_client
1383
+
1384
+ client = get_client()
1385
+ data_directory = client.create_data_directory(name="<data_directory-name>", ml_repo="<repo-name>")
1386
+ print(data_directory.fqn)
1387
+ ```
1388
+ """
1389
+ if name == "" or not isinstance(name, str):
1390
+ raise MlFoundryException(
1391
+ f"DataDirectory name must be string type and not empty. "
1392
+ f"Got {type(name)} type with value {name}"
1393
+ )
1394
+
1395
+ if ml_repo == "" or not isinstance(ml_repo, str):
1396
+ raise MlFoundryException(
1397
+ f"ML repo must be string type and not empty. "
1398
+ f"Got {type(ml_repo)} type with value {ml_repo}"
1399
+ )
1400
+
1401
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1402
+ # TODO: Add get_data_directory_by_name on server
1403
+ _datasets = self._mlfoundry_artifacts_api.list_datasets_post(
1404
+ list_datasets_request_dto=ListDatasetsRequestDto(
1405
+ experiment_id=ml_repo_id, name=name, max_results=1
1406
+ )
1407
+ )
1408
+ datasets = _datasets.datasets
1409
+ if datasets is not None and len(datasets) > 0:
1410
+ logger.warning(
1411
+ f"Data Directory with the name {name} already exists in ML Repo {ml_repo}, "
1412
+ f"returning the original instance of DataDirectory instead"
1413
+ )
1414
+ return DataDirectory(dataset=datasets[0])
1415
+
1416
+ _dataset = self._mlfoundry_artifacts_api.create_dataset_post(
1417
+ create_dataset_request_dto=CreateDatasetRequestDto(
1418
+ name=name,
1419
+ experiment_id=ml_repo_id,
1420
+ description=description,
1421
+ dataset_metadata=metadata,
1422
+ )
1423
+ )
1424
+ dataset = _dataset.dataset
1425
+ return DataDirectory(dataset=dataset)
1426
+
1427
+ def get_data_directory_by_fqn(
1428
+ self,
1429
+ fqn: str,
1430
+ ) -> DataDirectory:
1431
+ """
1432
+ Get the DataDirectory by DataDirectory FQN
1433
+
1434
+ Args:
1435
+ fqn (str): Fully qualified name of the artifact version.
1436
+
1437
+ Returns:
1438
+ DataDirectory : An instance of class DataDirectory
1439
+
1440
+ Examples:
1441
+
1442
+ ```python
1443
+ from truefoundry.ml import get_client, DataDirectoryPath
1444
+
1445
+ client = get_client()
1446
+ data_directory = client.get_data_directory_by_fqn(fqn="<data-dir-fqn>")
1447
+ with open("artifact.txt", "w") as f:
1448
+ f.write("hello-world")
1449
+
1450
+ data_directory.add_files(
1451
+ artifact_paths=[DataDirectoryPath('artifact.txt', 'a/b/')]
1452
+ )
1453
+ # print the path of files and folder in the data_directory
1454
+ for file in data_directory.list_files():
1455
+ print(file.path)
1456
+ ```
1457
+ """
1458
+
1459
+ _dataset = self._mlfoundry_artifacts_api.get_dataset_by_fqn_get(fqn=fqn)
1460
+ dataset = _dataset.dataset
1461
+ return DataDirectory(dataset)
1462
+
1463
+ def get_data_directory(
1464
+ self,
1465
+ ml_repo: str,
1466
+ name: str,
1467
+ ) -> DataDirectory:
1468
+ """Get an existing `data_directory` by `name`.
1469
+ Args:
1470
+ ml_repo (str): name of the ML Repo the data-directory is part of.
1471
+ name (str): the name of the data-directory
1472
+ Returns:
1473
+ DataDirectory: An instance of class DataDirectory
1474
+ Examples:
1475
+ ```python
1476
+ from truefoundry.ml import get_client
1477
+ client = get_client()
1478
+ data_directory = client.get_data_directory(ml_repo='my-repo', name="<data-directory-name>")
1479
+ with open("artifact.txt", "w") as f:
1480
+ f.write("hello-world")
1481
+ data_directory.add_files(
1482
+ artifact_paths=[DataDirectoryPath('artifact.txt', 'a/b/')]
1483
+ )
1484
+ # print the path of files and folder in the data_directory
1485
+ for file in data_directory.list_files():
1486
+ print(file.path)
1487
+ ```
1488
+ """
1489
+ if ml_repo == "" or not isinstance(ml_repo, str):
1490
+ raise MlFoundryException(
1491
+ f"ML repo must be string type and not empty. "
1492
+ f"Got {type(ml_repo)} type with value {ml_repo}"
1493
+ )
1494
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1495
+ _datasets = self._mlfoundry_artifacts_api.list_datasets_post(
1496
+ list_datasets_request_dto=ListDatasetsRequestDto(
1497
+ experiment_id=ml_repo_id,
1498
+ name=name,
1499
+ max_results=1,
1500
+ ),
1501
+ )
1502
+ datasets = _datasets.datasets
1503
+ if not datasets or len(datasets) == 0:
1504
+ raise MlFoundryException(
1505
+ f"No data directory found with name {name} under ML Repo {ml_repo}"
1506
+ )
1507
+
1508
+ return DataDirectory(dataset=datasets[0])
1509
+
1510
+ def list_data_directories(
1511
+ self,
1512
+ ml_repo: str,
1513
+ ) -> Iterator[DataDirectory]:
1514
+ """
1515
+ Get the list of DataDirectory in a ml_repo
1516
+
1517
+ Args:
1518
+ ml_repo (str): Name of the ML Repository
1519
+
1520
+ Returns:
1521
+ DataDirectory : An instance of class DataDirectory
1522
+
1523
+ Examples:
1524
+
1525
+ ```python
1526
+ from truefoundry.ml import get_client
1527
+
1528
+ client = get_client()
1529
+ data_directories = client.list_data_directories(ml_repo="<ml-repo-nam>")
1530
+
1531
+ for data_directory in data_directories:
1532
+ print(data_directory.name)
1533
+ ```
1534
+ """
1535
+ if ml_repo == "" or not isinstance(ml_repo, str):
1536
+ raise MlFoundryException(
1537
+ f"ML repo must be string type and not empty. "
1538
+ f"Got {type(ml_repo)} type with value {ml_repo}"
1539
+ )
1540
+ ml_repo_id = self._get_ml_repo_id(ml_repo=ml_repo)
1541
+ max_results, page_token, done = 10, None, False
1542
+ while not done:
1543
+ _datasets = self._mlfoundry_artifacts_api.list_datasets_post(
1544
+ list_datasets_request_dto=ListDatasetsRequestDto(
1545
+ experiment_id=ml_repo_id,
1546
+ max_results=max_results,
1547
+ page_token=page_token,
1548
+ )
1549
+ )
1550
+ datasets: List[DatasetDto] = _datasets.datasets or []
1551
+ page_token = _datasets.next_page_token
1552
+ for dataset in datasets:
1553
+ yield DataDirectory(dataset=dataset)
1554
+ if not datasets or not page_token:
1555
+ done = True
1556
+
1557
+
1558
+ def get_client() -> MlFoundry:
1559
+ """Initializes and returns the mlfoundry client.
1560
+
1561
+
1562
+ Returns:
1563
+ MlFoundry: Instance of `MlFoundry` class which represents a `run`.
1564
+
1565
+ Examples:
1566
+
1567
+ ### Get client
1568
+ ```python
1569
+ from truefoundry.ml import get_client
1570
+
1571
+ client = get_client()
1572
+ ```
1573
+ """
1574
+ session = init_session()
1575
+ return MlFoundry(session=session)