truefoundry 0.3.4__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 +6 -2
  247. truefoundry/workflow/__init__.py +16 -1
  248. {truefoundry-0.3.4.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.4.dist-info/RECORD +0 -136
  252. {truefoundry-0.3.4.dist-info → truefoundry-0.4.0.dist-info}/WHEEL +0 -0
  253. {truefoundry-0.3.4.dist-info → truefoundry-0.4.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,93 @@
1
+ import argparse
2
+ import importlib
3
+ import json
4
+ import os
5
+ from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
6
+ from urllib.parse import urljoin, urlsplit
7
+
8
+ import numpy as np
9
+
10
+ from truefoundry.ml.exceptions import MlFoundryException
11
+
12
+
13
+ def get_module(
14
+ module_name: str, error_message: Optional[str] = None, required: bool = False
15
+ ):
16
+ try:
17
+ return importlib.import_module(module_name)
18
+ except Exception as ex:
19
+ msg = error_message or f"Error importing module {module_name}"
20
+ if required:
21
+ raise MlFoundryException(msg) from ex
22
+
23
+
24
+ def append_path_to_rest_tracking_uri(tracking_uri: str):
25
+ if urlsplit(tracking_uri).netloc.startswith("localhost"):
26
+ return tracking_uri
27
+ return urljoin(tracking_uri, "/api/ml")
28
+
29
+
30
+ def append_servicefoundry_path_to_tracking_uri(tracking_uri: str):
31
+ if urlsplit(tracking_uri).netloc.startswith("localhost"):
32
+ return os.getenv("SERVICEFOUNDRY_SERVER_URL")
33
+ return urljoin(tracking_uri, "/api/svc")
34
+
35
+
36
+ class NumpyEncoder(json.JSONEncoder):
37
+ """Special json encoder for numpy types"""
38
+
39
+ def default(self, obj):
40
+ if isinstance(obj, np.integer):
41
+ return int(obj)
42
+ elif isinstance(obj, np.floating):
43
+ return float(obj)
44
+ elif isinstance(obj, np.ndarray):
45
+ return obj.tolist()
46
+ return json.JSONEncoder.default(self, obj)
47
+
48
+
49
+ ParamsType = Union[Mapping[str, Any], argparse.Namespace]
50
+
51
+
52
+ def process_params(params: ParamsType) -> Mapping[str, Any]:
53
+ if isinstance(params, Mapping):
54
+ return params
55
+ if isinstance(params, argparse.Namespace):
56
+ return vars(params)
57
+ # TODO: add absl support if required
58
+ # move to a different file then
59
+ raise MlFoundryException(
60
+ "params should be either argparse.Namespace or a Mapping (dict) type"
61
+ )
62
+
63
+
64
+ def flatten_dict(
65
+ input_dict: Mapping[Any, Any], parent_key: str = "", sep: str = "."
66
+ ) -> Dict[str, Any]:
67
+ """Flatten hierarchical dict, e.g. ``{'a': {'b': 'c'}} -> {'a.b': 'c'}``.
68
+ All the keys will be converted to str.
69
+
70
+ Args:
71
+ input_dict: Dictionary containing the keys
72
+ parent_key: Prefix to add to the keys. Defaults to ``''``.
73
+ sep: Delimiter to express the hierarchy. Defaults to ``'.'``.
74
+
75
+ Returns:
76
+ Flattened dict.
77
+
78
+ Examples:
79
+ >>> flatten_dict({'a': {'b': 'c'}})
80
+ {'a.b': 'c'}
81
+ >>> flatten_dict({'a': {'b': 123}})
82
+ {'a.b': 123}
83
+ >>> flatten_dict({'a': {'b': 'c'}}, parent_key="param")
84
+ {'param.a.b': 'c'}
85
+ """
86
+ new_dict_items: List[Tuple[str, Any]] = []
87
+ for k, v in input_dict.items():
88
+ new_key = parent_key + sep + str(k) if parent_key else k
89
+ if isinstance(v, Mapping):
90
+ new_dict_items.extend(flatten_dict(v, new_key, sep=sep).items())
91
+ else:
92
+ new_dict_items.append((new_key, v))
93
+ return dict(new_dict_items)
@@ -0,0 +1,168 @@
1
+ import atexit
2
+ import threading
3
+ import weakref
4
+ from typing import TYPE_CHECKING, Dict, Optional
5
+
6
+ from truefoundry.common.credential_provider import (
7
+ CredentialProvider,
8
+ EnvCredentialProvider,
9
+ FileCredentialProvider,
10
+ )
11
+ from truefoundry.common.entities import Token, UserInfo
12
+ from truefoundry.common.request_utils import urllib3_retry
13
+ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
14
+ ApiClient,
15
+ Configuration,
16
+ )
17
+ from truefoundry.ml.clients.entities import HostCreds
18
+ from truefoundry.ml.exceptions import MlFoundryException
19
+ from truefoundry.ml.logger import logger
20
+ from truefoundry.ml.run_utils import (
21
+ append_path_to_rest_tracking_uri,
22
+ )
23
+
24
+ if TYPE_CHECKING:
25
+ from truefoundry.ml.mlfoundry_run import MlFoundryRun
26
+
27
+ SESSION_LOCK = threading.RLock()
28
+
29
+
30
+ class ActiveRuns:
31
+ def __init__(self):
32
+ self._active_runs: Dict[str, weakref.ReferenceType["MlFoundryRun"]] = {}
33
+
34
+ def add_run(self, run: "MlFoundryRun"):
35
+ with SESSION_LOCK:
36
+ self._active_runs[run.run_id] = weakref.ref(run)
37
+
38
+ def remove_run(self, run: "MlFoundryRun"):
39
+ with SESSION_LOCK:
40
+ if run.run_id in self._active_runs:
41
+ del self._active_runs[run.run_id]
42
+
43
+ def close_active_runs(self):
44
+ with SESSION_LOCK:
45
+ for run_ref in list(self._active_runs.values()):
46
+ run = run_ref()
47
+ if run and run.auto_end:
48
+ run.end()
49
+ self._active_runs.clear()
50
+
51
+
52
+ ACTIVE_RUNS = ActiveRuns()
53
+ atexit.register(ACTIVE_RUNS.close_active_runs)
54
+
55
+
56
+ class Session:
57
+ def __init__(self, cred_provider: CredentialProvider):
58
+ # Note: Whenever a new session is initialized all the active runs are ended
59
+ self._closed = False
60
+ self._cred_provider: Optional[CredentialProvider] = cred_provider
61
+ self._user_info: Optional[UserInfo] = self._cred_provider.token.to_user_info()
62
+
63
+ def close(self):
64
+ logger.debug("Closing existing session")
65
+ self._closed = True
66
+ self._user_info = None
67
+ self._cred_provider = None
68
+
69
+ def _assert_not_closed(self):
70
+ if self._closed:
71
+ raise MlFoundryException(
72
+ "This session has been deactivated.\n"
73
+ "At a time only one `client` (received from "
74
+ "`truefoundry.ml.get_client()` function call) can be used"
75
+ )
76
+
77
+ @property
78
+ def token(self) -> Token:
79
+ return self._cred_provider.token
80
+
81
+ @property
82
+ def user_info(self) -> UserInfo:
83
+ self._assert_not_closed()
84
+ return self._user_info
85
+
86
+ @property
87
+ def tracking_uri(self) -> str:
88
+ return self._cred_provider.base_url
89
+
90
+ def __eq__(self, other: object) -> bool:
91
+ if not isinstance(other, Session):
92
+ return False
93
+ return (
94
+ type(self._cred_provider) == type(other._cred_provider) # noqa: E721
95
+ and self.user_info == other.user_info
96
+ and self.tracking_uri == other.tracking_uri
97
+ )
98
+
99
+ def get_host_creds(self) -> HostCreds:
100
+ tracking_uri = append_path_to_rest_tracking_uri(self._cred_provider.base_url)
101
+ return HostCreds(
102
+ host=tracking_uri, token=self._cred_provider.token.access_token
103
+ )
104
+
105
+
106
+ ACTIVE_SESSION: Optional[Session] = None
107
+
108
+
109
+ def get_active_session() -> Optional[Session]:
110
+ return ACTIVE_SESSION
111
+
112
+
113
+ def _get_api_client(
114
+ session: Optional[Session] = None,
115
+ allow_anonymous: bool = False,
116
+ ) -> ApiClient:
117
+ session = session or get_active_session()
118
+ if session is None:
119
+ if allow_anonymous:
120
+ return ApiClient()
121
+ else:
122
+ raise MlFoundryException(
123
+ "No active session found. Perhaps you are not logged in?\n"
124
+ "Please log in using `tfy login [--host HOST] --relogin"
125
+ )
126
+
127
+ creds = session.get_host_creds()
128
+ configuration = Configuration(
129
+ host=creds.host.rstrip("/"),
130
+ access_token=creds.token,
131
+ )
132
+ configuration.retries = urllib3_retry(retries=2)
133
+ api_client = ApiClient(configuration=configuration)
134
+ api_client.user_agent = "truefoundry-cli"
135
+ return api_client
136
+
137
+
138
+ def init_session() -> Session:
139
+ with SESSION_LOCK:
140
+ final_cred_provider = None
141
+ for cred_provider in [EnvCredentialProvider, FileCredentialProvider]:
142
+ if cred_provider.can_provide():
143
+ final_cred_provider = cred_provider()
144
+ break
145
+ if final_cred_provider is None:
146
+ raise MlFoundryException(
147
+ "Please login using `tfy login` command "
148
+ "or `truefoundry.login()` function call"
149
+ )
150
+ new_session = Session(cred_provider=final_cred_provider)
151
+
152
+ global ACTIVE_SESSION
153
+ if ACTIVE_SESSION and ACTIVE_SESSION == new_session:
154
+ return ACTIVE_SESSION
155
+
156
+ ACTIVE_RUNS.close_active_runs()
157
+
158
+ if ACTIVE_SESSION:
159
+ ACTIVE_SESSION.close()
160
+ ACTIVE_SESSION = new_session
161
+
162
+ logger.info(
163
+ "Logged in to %r as %r (%s)",
164
+ ACTIVE_SESSION.tracking_uri,
165
+ ACTIVE_SESSION.user_info.user_id,
166
+ ACTIVE_SESSION.user_info.email or ACTIVE_SESSION.user_info.user_type.value,
167
+ )
168
+ return ACTIVE_SESSION
@@ -0,0 +1,346 @@
1
+ """
2
+ Utilities for validating user inputs such as metric names and parameter names.
3
+ """
4
+
5
+ import json
6
+ import numbers
7
+ import posixpath
8
+ import re
9
+ from operator import xor
10
+ from typing import List, Optional, Union
11
+
12
+ from truefoundry.ml import MlFoundryException
13
+ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
14
+ MetricDto,
15
+ ParamDto,
16
+ RunTagDto,
17
+ )
18
+
19
+ _VALID_PARAM_AND_METRIC_NAMES = re.compile(r"^[/\w.\- ]*$")
20
+
21
+ # Regex for valid run IDs: must be an alphanumeric string of length 1 to 256.
22
+ _BAD_CHARACTERS_MESSAGE = (
23
+ "Names may only contain alphanumerics, underscores (_), dashes (-), periods (.),"
24
+ " spaces ( ), and slashes (/)."
25
+ )
26
+ _RUN_ID_REGEX = re.compile(r"^[a-zA-Z0-9][\w\-]{0,255}$")
27
+ _ML_REPO_ID_REGEX = re.compile(r"^[a-zA-Z0-9][\w\-]{0,63}$")
28
+ _ML_REPO_NAME_REGEX = re.compile(r"^[a-zA-Z][a-zA-Z0-9\-]{1,98}[a-zA-Z0-9]$")
29
+ _RUN_NAME_REGEX = re.compile(r"^[a-zA-Z0-9-]*$")
30
+ _RUN_LOG_LOG_TYPE_REGEX = re.compile(r"^[a-zA-Z0-9-/]*$")
31
+ _RUN_LOG_KEY_REGEX = re.compile(r"^[a-zA-Z0-9-_]*$")
32
+
33
+ MAX_PARAMS_TAGS_PER_BATCH = 100
34
+ MAX_METRICS_PER_BATCH = 1000
35
+ MAX_ENTITIES_PER_BATCH = 1000
36
+ MAX_BATCH_LOG_REQUEST_SIZE = int(1e6)
37
+ MAX_PARAM_VAL_LENGTH = 1000
38
+ MAX_TAG_VAL_LENGTH = 5000
39
+ MAX_ML_REPO_TAG_KEY_LENGTH = 250
40
+ MAX_ML_REPO_TAG_VAL_LENGTH = 5000
41
+ MAX_ENTITY_KEY_LENGTH = 250
42
+ MAX_RUN_LOG_KEY_NAME_LENGTH = 128
43
+ MAX_RUN_LOG_LOG_TYPE_LENGTH = 128
44
+ MAX_ML_REPO_DESCRIPTION_LENGTH = 512
45
+ MAX_RUN_DESCRIPTION_LENGTH = 512
46
+ MAX_ML_REPOS_LISTED_PER_PAGE = 100
47
+
48
+
49
+ def is_string_type(item):
50
+ return isinstance(item, str)
51
+
52
+
53
+ def bad_path_message(name: str):
54
+ return (
55
+ f"Names may be treated as files in certain cases, "
56
+ f"and must not resolve to other names when treated as such. "
57
+ f"This name would resolve to {posixpath.normpath(name)!r}"
58
+ )
59
+
60
+
61
+ def path_not_unique(name: str):
62
+ norm = posixpath.normpath(name)
63
+ return norm != name or norm == "." or norm.startswith("..") or norm.startswith("/")
64
+
65
+
66
+ def _validate_metric_name(name: str):
67
+ """Check that `name` is a valid metric name and raise an exception if it isn't."""
68
+ if name is None or not _VALID_PARAM_AND_METRIC_NAMES.match(name):
69
+ raise MlFoundryException(
70
+ f"Invalid metric name: {name!r}. {_BAD_CHARACTERS_MESSAGE}",
71
+ )
72
+ if path_not_unique(name):
73
+ raise MlFoundryException(
74
+ f"Invalid metric name: {name!r}. {bad_path_message(name)}",
75
+ )
76
+
77
+
78
+ def _validate_run_name(name: Optional[str]):
79
+ if not name or not _RUN_NAME_REGEX.match(name):
80
+ raise MlFoundryException(
81
+ f"Invalid run name {name!r}. Name should not empty string "
82
+ f"and may only contain alphanumerics or dashes (-)",
83
+ )
84
+ _validate_length_limit("Run name", MAX_ENTITY_KEY_LENGTH, name)
85
+
86
+
87
+ def _is_numeric(value):
88
+ """
89
+ Returns True if the passed-in value is numeric.
90
+ """
91
+ # Note that `isinstance(bool_value, numbers.Number)` returns `True` because `bool` is a
92
+ # subclass of `int`.
93
+ return not isinstance(value, bool) and isinstance(value, numbers.Number)
94
+
95
+
96
+ def _validate_metric(
97
+ key: str,
98
+ value: Optional[Union[int, float]],
99
+ timestamp: Optional[int],
100
+ step: Optional[int],
101
+ ):
102
+ """
103
+ Check that a param with the specified key, value, timestamp is valid and raise an exception if
104
+ it isn't.
105
+ """
106
+ _validate_metric_name(key)
107
+ _validate_length_limit("Metric name", MAX_ENTITY_KEY_LENGTH, key)
108
+ # value must be a Number
109
+ # since bool is an instance of Number check for bool additionally
110
+ if not _is_numeric(value):
111
+ raise MlFoundryException(
112
+ f"Got invalid value {value} for metric {key!r} (step={step}). "
113
+ f"Please specify value as a valid double (64-bit floating point)",
114
+ )
115
+
116
+ if not isinstance(timestamp, int) or timestamp < 0:
117
+ raise MlFoundryException(
118
+ f"Got invalid timestamp {timestamp} for metric {key!r} (value={value}). "
119
+ f"Timestamp must be a non-negative long (64-bit integer)",
120
+ )
121
+
122
+ if not isinstance(step, int):
123
+ raise MlFoundryException(
124
+ f"Got invalid step {step} for metric {key!r} (value={value}). "
125
+ f"Step must be a valid long (64-bit integer).",
126
+ )
127
+
128
+
129
+ def _validate_run_log_input(
130
+ key: str,
131
+ timestamp: int,
132
+ step: int,
133
+ log_type: str,
134
+ value: Optional[str],
135
+ artifact_path: Optional[str],
136
+ ):
137
+ # reusing the validation defined for metric: str
138
+ _validate_metric(key=key, value=0, timestamp=timestamp, step=step)
139
+ _validate_length_limit("RunLog log type", MAX_RUN_LOG_LOG_TYPE_LENGTH, log_type)
140
+ _validate_length_limit("RunLog log key", MAX_RUN_LOG_KEY_NAME_LENGTH, key)
141
+ if not log_type or not _RUN_LOG_LOG_TYPE_REGEX.match(log_type):
142
+ raise MlFoundryException(
143
+ f"Invalid run log_type: {log_type}",
144
+ )
145
+
146
+ if not key or not _RUN_LOG_KEY_REGEX.match(key):
147
+ raise MlFoundryException(
148
+ f"Invalid run log key: {key!r} should only contain alphanumeric, hyphen or underscore"
149
+ )
150
+
151
+ if not xor(bool(artifact_path), bool(value)):
152
+ raise MlFoundryException(
153
+ "Either artifact_path or value should be empty. "
154
+ f"artifact_path={artifact_path!r}, value={value!r}",
155
+ )
156
+ if value is not None:
157
+ try:
158
+ json.loads(value)
159
+ except ValueError as e:
160
+ raise MlFoundryException(
161
+ f"Key {key!r} does not contain a valid json: {e}",
162
+ ) from e
163
+
164
+
165
+ def _validate_param(key: str, value: str):
166
+ """
167
+ Check that a param with the specified key & value is valid and raise an exception if it
168
+ isn't.
169
+ """
170
+ _validate_param_name(key)
171
+ _validate_length_limit("Param key", MAX_ENTITY_KEY_LENGTH, key)
172
+ _validate_length_limit("Param value", MAX_PARAM_VAL_LENGTH, value)
173
+
174
+
175
+ def _validate_tag(key: str, value: str):
176
+ """
177
+ Check that a tag with the specified key & value is valid and raise an exception if it isn't.
178
+ """
179
+ _validate_tag_name(key)
180
+ _validate_length_limit("Tag key", MAX_ENTITY_KEY_LENGTH, key)
181
+ _validate_length_limit("Tag value", MAX_TAG_VAL_LENGTH, value)
182
+
183
+
184
+ def _validate_ml_repo_tag(key: str, value: str):
185
+ """
186
+ Check that a tag with the specified key & value is valid and raise an exception if it isn't.
187
+ """
188
+ _validate_tag_name(key)
189
+ _validate_length_limit("Tag key", MAX_ML_REPO_TAG_KEY_LENGTH, key)
190
+ _validate_length_limit("Tag value", MAX_ML_REPO_TAG_VAL_LENGTH, value)
191
+
192
+
193
+ def _validate_list_ml_repos_max_results(max_results):
194
+ """
195
+ Check that `max_results` is within an acceptable range and raise an exception if it isn't.
196
+ """
197
+ if max_results is None:
198
+ return
199
+
200
+ if max_results < 1:
201
+ raise MlFoundryException(
202
+ f"Invalid value for request parameter max_results. "
203
+ f"It must be at least 1, but got value {max_results}",
204
+ )
205
+
206
+ if max_results > MAX_ML_REPOS_LISTED_PER_PAGE:
207
+ raise MlFoundryException(
208
+ f"Invalid value for request parameter max_results. "
209
+ f"It must be at most {MAX_ML_REPOS_LISTED_PER_PAGE}, but got value {max_results}",
210
+ )
211
+
212
+
213
+ def _validate_param_keys_unique(params: List[ParamDto]):
214
+ """Ensures that duplicate param keys are not present in the `log_batch()` params argument"""
215
+ unique_keys = []
216
+ dupe_keys = []
217
+ for param in params:
218
+ if param.key not in unique_keys:
219
+ unique_keys.append(param.key)
220
+ else:
221
+ dupe_keys.append(param.key)
222
+
223
+ if dupe_keys:
224
+ raise MlFoundryException(
225
+ f"Duplicate parameter keys have been submitted: {dupe_keys}. Please ensure "
226
+ f"the request contains only one param value per param key.",
227
+ )
228
+
229
+
230
+ def _validate_param_name(name: str):
231
+ """Check that `name` is a valid parameter name and raise an exception if it isn't."""
232
+ if name is None or not _VALID_PARAM_AND_METRIC_NAMES.match(name):
233
+ raise MlFoundryException(
234
+ f"Invalid parameter name: {name!r}. {_BAD_CHARACTERS_MESSAGE}",
235
+ )
236
+ if path_not_unique(name):
237
+ raise MlFoundryException(
238
+ f"Invalid parameter name: {name!r}. {bad_path_message(name)}",
239
+ )
240
+
241
+
242
+ def _validate_tag_name(name: str):
243
+ """Check that `name` is a valid tag name and raise an exception if it isn't."""
244
+ # Reuse param & metric check.
245
+ if name is None or not _VALID_PARAM_AND_METRIC_NAMES.match(name):
246
+ raise MlFoundryException(
247
+ f"Invalid tag name: {name!r}. {_BAD_CHARACTERS_MESSAGE}",
248
+ )
249
+ if path_not_unique(name):
250
+ raise MlFoundryException(
251
+ f"Invalid tag name: {name!r}. {bad_path_message(name)}",
252
+ )
253
+
254
+
255
+ def _validate_length_limit(entity_name: str, limit: int, value: str):
256
+ if len(value) > limit:
257
+ raise MlFoundryException(
258
+ f"{entity_name} {value!r} had length {len(value)} which exceeded length limit of {limit}",
259
+ )
260
+
261
+
262
+ def _validate_run_id(run_id: str):
263
+ """Check that `run_id` is a valid run ID and raise an exception if it isn't."""
264
+ if _RUN_ID_REGEX.match(run_id) is None:
265
+ raise MlFoundryException(
266
+ f"Invalid run ID: {run_id!r}",
267
+ )
268
+
269
+
270
+ def _validate_ml_repo_id(ml_repo_id: str):
271
+ """Check that `ml_repo_id`is a valid string or None, raise an exception if it isn't."""
272
+ if ml_repo_id is not None and _ML_REPO_ID_REGEX.match(ml_repo_id) is None:
273
+ raise MlFoundryException(
274
+ f"Invalid ml_repo ID: {ml_repo_id!r}",
275
+ )
276
+
277
+
278
+ def _validate_batch_limit(entity_name: str, limit: int, length: int):
279
+ if length > limit:
280
+ error_msg = (
281
+ f"A batch logging request can contain at most {limit} {entity_name}. "
282
+ f"Got {length} {entity_name}. "
283
+ f"Please split up {entity_name} across multiple requests and try again."
284
+ )
285
+ raise MlFoundryException(error_msg)
286
+
287
+
288
+ def _validate_batch_log_limits(
289
+ metrics: List[MetricDto], params: List[ParamDto], tags: List[RunTagDto]
290
+ ):
291
+ """Validate that the provided batched logging arguments are within expected limits."""
292
+ _validate_batch_limit(
293
+ entity_name="metrics", limit=MAX_METRICS_PER_BATCH, length=len(metrics)
294
+ )
295
+ _validate_batch_limit(
296
+ entity_name="params", limit=MAX_PARAMS_TAGS_PER_BATCH, length=len(params)
297
+ )
298
+ _validate_batch_limit(
299
+ entity_name="tags", limit=MAX_PARAMS_TAGS_PER_BATCH, length=len(tags)
300
+ )
301
+ total_length = len(metrics) + len(params) + len(tags)
302
+ _validate_batch_limit(
303
+ entity_name="metrics, params, and tags",
304
+ limit=MAX_ENTITIES_PER_BATCH,
305
+ length=total_length,
306
+ )
307
+
308
+
309
+ def _validate_batch_log_data(
310
+ metrics: List[MetricDto], params: List[ParamDto], tags: List[RunTagDto]
311
+ ):
312
+ if len(metrics) == 0 and len(params) == 0 and len(tags) == 0:
313
+ return
314
+ _validate_batch_log_limits(metrics=metrics, params=params, tags=tags)
315
+ for metric in metrics:
316
+ _validate_metric(metric.key, metric.value, metric.timestamp, metric.step)
317
+ if len(params) > 1:
318
+ _validate_param_keys_unique(params)
319
+ for param in params:
320
+ _validate_param(param.key, param.value)
321
+ for tag in tags:
322
+ _validate_tag(tag.key, tag.value)
323
+
324
+
325
+ def _validate_ml_repo_name(ml_repo_name: str):
326
+ """Check that `ml_repo_name` is a valid string and raise an exception if it isn't."""
327
+ if ml_repo_name == "" or ml_repo_name is None or not is_string_type(ml_repo_name):
328
+ raise MlFoundryException(
329
+ f"ml_repo must be string type and not empty. "
330
+ f"Got {type(ml_repo_name)} type with value {ml_repo_name!r}"
331
+ )
332
+
333
+ if not _ML_REPO_NAME_REGEX.match(ml_repo_name):
334
+ raise MlFoundryException(
335
+ f"Invalid ML Repo name {ml_repo_name!r}. Name may only contain alphanumerics or dashes (-)",
336
+ )
337
+
338
+
339
+ def _validate_ml_repo_description(description: str):
340
+ _validate_length_limit(
341
+ "ML Repo Description", MAX_ML_REPO_DESCRIPTION_LENGTH, description
342
+ )
343
+
344
+
345
+ def _validate_run_description(description: str):
346
+ _validate_length_limit("Run Description", MAX_RUN_DESCRIPTION_LENGTH, description)
@@ -1,8 +1,12 @@
1
1
  try:
2
2
  # pydantic >1.10.18
3
3
  from pydantic.v1 import * # noqa: F403
4
- from pydantic.v1 import utils # noqa: F401
4
+ from pydantic.v1 import ConstrainedStr, utils # noqa: F401
5
5
  except ImportError:
6
6
  # pydantic <=1.10.17
7
7
  from pydantic import * # noqa: F403
8
- from pydantic import utils # noqa: F401
8
+ from pydantic import ConstrainedStr, utils # noqa: F401
9
+
10
+
11
+ class NonEmptyStr(ConstrainedStr):
12
+ min_length: int = 1
@@ -1,5 +1,5 @@
1
1
  try:
2
- from flytekit import task
2
+ from flytekit import task as _
3
3
  except ImportError:
4
4
  print("To use workflows, please run 'pip install truefoundry[workflow]'.")
5
5
 
@@ -17,3 +17,18 @@ from truefoundry.workflow.map_task import map_task
17
17
  from truefoundry.workflow.python_task import PythonFunctionTask
18
18
  from truefoundry.workflow.task import task
19
19
  from truefoundry.workflow.workflow import ExecutionConfig, workflow
20
+
21
+ __all__ = [
22
+ "task",
23
+ "ContainerTask",
24
+ "PythonFunctionTask",
25
+ "map_task",
26
+ "workflow",
27
+ "conditional",
28
+ "FlyteDirectory",
29
+ "TaskDockerFileBuild",
30
+ "TaskPythonBuild",
31
+ "ContainerTaskConfig",
32
+ "PythonTaskConfig",
33
+ "ExecutionConfig",
34
+ ]