truefoundry 0.5.0rc7__py3-none-any.whl → 0.5.1rc2__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.
- truefoundry/common/utils.py +73 -1
- truefoundry/deploy/__init__.py +5 -0
- truefoundry/deploy/cli/cli.py +2 -0
- truefoundry/deploy/cli/commands/__init__.py +1 -0
- truefoundry/deploy/cli/commands/deploy_init_command.py +22 -0
- truefoundry/deploy/lib/dao/application.py +2 -1
- truefoundry/deploy/v2/lib/patched_models.py +8 -0
- truefoundry/ml/__init__.py +14 -12
- truefoundry/ml/autogen/client/__init__.py +5 -0
- truefoundry/ml/autogen/client/api/mlfoundry_artifacts_api.py +161 -0
- truefoundry/ml/autogen/client/models/__init__.py +5 -0
- truefoundry/ml/autogen/client/models/artifact_version_manifest.py +2 -2
- truefoundry/ml/autogen/client/models/export_deployment_files_request_dto.py +82 -0
- truefoundry/ml/autogen/client/models/infer_method_name.py +34 -0
- truefoundry/ml/autogen/client/models/model_server.py +34 -0
- truefoundry/ml/autogen/client/models/model_version_environment.py +1 -1
- truefoundry/ml/autogen/client/models/model_version_manifest.py +3 -3
- truefoundry/ml/autogen/client/models/sklearn_framework.py +17 -1
- truefoundry/ml/autogen/client/models/transformers_framework.py +2 -2
- truefoundry/ml/autogen/client/models/xg_boost_framework.py +6 -1
- truefoundry/ml/autogen/client_README.md +4 -0
- truefoundry/ml/autogen/entities/artifacts.py +29 -7
- truefoundry/ml/cli/commands/model_init.py +97 -0
- truefoundry/ml/cli/utils.py +34 -0
- truefoundry/ml/log_types/artifacts/model.py +63 -24
- truefoundry/ml/log_types/artifacts/utils.py +37 -1
- truefoundry/ml/mlfoundry_api.py +74 -78
- truefoundry/ml/mlfoundry_run.py +0 -30
- truefoundry/ml/model_framework.py +257 -3
- truefoundry/ml/validation_utils.py +2 -0
- {truefoundry-0.5.0rc7.dist-info → truefoundry-0.5.1rc2.dist-info}/METADATA +1 -5
- {truefoundry-0.5.0rc7.dist-info → truefoundry-0.5.1rc2.dist-info}/RECORD +34 -46
- truefoundry/deploy/function_service/__init__.py +0 -3
- truefoundry/deploy/function_service/__main__.py +0 -27
- truefoundry/deploy/function_service/app.py +0 -92
- truefoundry/deploy/function_service/build.py +0 -45
- truefoundry/deploy/function_service/remote/__init__.py +0 -6
- truefoundry/deploy/function_service/remote/context.py +0 -3
- truefoundry/deploy/function_service/remote/method.py +0 -67
- truefoundry/deploy/function_service/remote/remote.py +0 -144
- truefoundry/deploy/function_service/route.py +0 -137
- truefoundry/deploy/function_service/service.py +0 -113
- truefoundry/deploy/function_service/utils.py +0 -53
- truefoundry/langchain/__init__.py +0 -12
- truefoundry/langchain/deprecated.py +0 -302
- truefoundry/langchain/truefoundry_chat.py +0 -130
- truefoundry/langchain/truefoundry_embeddings.py +0 -171
- truefoundry/langchain/truefoundry_llm.py +0 -106
- truefoundry/langchain/utils.py +0 -44
- truefoundry/ml/log_types/artifacts/model_extras.py +0 -48
- {truefoundry-0.5.0rc7.dist-info → truefoundry-0.5.1rc2.dist-info}/WHEEL +0 -0
- {truefoundry-0.5.0rc7.dist-info → truefoundry-0.5.1rc2.dist-info}/entry_points.txt +0 -0
truefoundry/ml/mlfoundry_api.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import time
|
|
3
3
|
import uuid
|
|
4
|
-
|
|
4
|
+
import zipfile
|
|
5
|
+
from io import BytesIO
|
|
5
6
|
from typing import (
|
|
6
7
|
TYPE_CHECKING,
|
|
7
8
|
Any,
|
|
@@ -15,9 +16,8 @@ from typing import (
|
|
|
15
16
|
)
|
|
16
17
|
|
|
17
18
|
import coolname
|
|
18
|
-
import pandas as pd
|
|
19
19
|
|
|
20
|
-
from truefoundry.common.utils import relogin_error_message
|
|
20
|
+
from truefoundry.common.utils import ContextualDirectoryManager, relogin_error_message
|
|
21
21
|
from truefoundry.ml import ModelVersionEnvironment, constants
|
|
22
22
|
from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
23
23
|
ArtifactDto,
|
|
@@ -27,12 +27,14 @@ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
|
27
27
|
CreateRunRequestDto,
|
|
28
28
|
DatasetDto,
|
|
29
29
|
ExperimentsApi,
|
|
30
|
+
ExportDeploymentFilesRequestDto,
|
|
30
31
|
ListArtifactsRequestDto,
|
|
31
32
|
ListArtifactVersionsRequestDto,
|
|
32
33
|
ListDatasetsRequestDto,
|
|
33
34
|
ListModelVersionsRequestDto,
|
|
34
35
|
MlfoundryArtifactsApi,
|
|
35
36
|
ModelDto,
|
|
37
|
+
ModelServer,
|
|
36
38
|
RunsApi,
|
|
37
39
|
RunTagDto,
|
|
38
40
|
SearchRunsRequestDto,
|
|
@@ -75,7 +77,7 @@ if TYPE_CHECKING:
|
|
|
75
77
|
from truefoundry.ml import ModelFrameworkType
|
|
76
78
|
|
|
77
79
|
_SEARCH_MAX_RESULTS_DEFAULT = 1000
|
|
78
|
-
|
|
80
|
+
_ML_FOUNDRY_API_REQUEST_TIMEOUT = 10
|
|
79
81
|
_INTERNAL_ENV_VARS = [
|
|
80
82
|
"TFY_INTERNAL_APPLICATION_ID",
|
|
81
83
|
"TFY_INTERNAL_JOB_RUN_NAME",
|
|
@@ -476,43 +478,6 @@ class MlFoundry:
|
|
|
476
478
|
)
|
|
477
479
|
return mlfoundry_run
|
|
478
480
|
|
|
479
|
-
def get_all_runs(
|
|
480
|
-
self,
|
|
481
|
-
ml_repo: str,
|
|
482
|
-
) -> pd.DataFrame:
|
|
483
|
-
"""Returns all the run name and id present under a ML Repo.
|
|
484
|
-
|
|
485
|
-
The user must have `READ` access to the ML Repo.
|
|
486
|
-
|
|
487
|
-
Args:
|
|
488
|
-
ml_repo (str): Name of the ML Repo.
|
|
489
|
-
Returns:
|
|
490
|
-
pd.DataFrame: dataframe with two columns- run_id and run_name
|
|
491
|
-
|
|
492
|
-
Examples:
|
|
493
|
-
|
|
494
|
-
### get all the runs from a ml_repo
|
|
495
|
-
```python
|
|
496
|
-
from truefoundry.ml import get_client
|
|
497
|
-
|
|
498
|
-
client = get_client()
|
|
499
|
-
|
|
500
|
-
run = client.get_all_runs(ml_repo='my-repo')
|
|
501
|
-
```
|
|
502
|
-
"""
|
|
503
|
-
runs = []
|
|
504
|
-
for run in self.search_runs(ml_repo=ml_repo):
|
|
505
|
-
runs.append((run.run_id, run.run_name))
|
|
506
|
-
|
|
507
|
-
if len(runs) == 0:
|
|
508
|
-
return pd.DataFrame(
|
|
509
|
-
columns=[constants.RUN_ID_COL_NAME, constants.RUN_NAME_COL_NAME]
|
|
510
|
-
)
|
|
511
|
-
|
|
512
|
-
return pd.DataFrame(
|
|
513
|
-
runs, columns=[constants.RUN_ID_COL_NAME, constants.RUN_NAME_COL_NAME]
|
|
514
|
-
)
|
|
515
|
-
|
|
516
481
|
def search_runs(
|
|
517
482
|
self,
|
|
518
483
|
ml_repo: str,
|
|
@@ -658,6 +623,74 @@ class MlFoundry:
|
|
|
658
623
|
"""
|
|
659
624
|
return self._tracking_uri
|
|
660
625
|
|
|
626
|
+
def _initialize_model_server(
|
|
627
|
+
self,
|
|
628
|
+
name: str,
|
|
629
|
+
model_version_fqn: str,
|
|
630
|
+
workspace_fqn: str,
|
|
631
|
+
model_server: ModelServer,
|
|
632
|
+
output_dir: Optional[str] = None,
|
|
633
|
+
) -> str:
|
|
634
|
+
"""
|
|
635
|
+
Initialize the model server for deployment.
|
|
636
|
+
|
|
637
|
+
Args:
|
|
638
|
+
name (str): Name of the application.
|
|
639
|
+
model_version_fqn (str): Fully Qualified Name of the model version.
|
|
640
|
+
workspace_fqn (str): Fully Qualified Name of the workspace.
|
|
641
|
+
model_server (ModelServer): Server type for deployment (e.g., TRITON).
|
|
642
|
+
output_dir (Optional[str]): Directory where the model files will be extracted.
|
|
643
|
+
Defaults to the current working directory.
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
str: Path to the directory where the model files are extracted.
|
|
647
|
+
|
|
648
|
+
Raises:
|
|
649
|
+
MlFoundryException: If an error occurs during API calls, directory creation, or file extraction.
|
|
650
|
+
"""
|
|
651
|
+
|
|
652
|
+
# Using get_with_http_info to get the response object and extract the raw data
|
|
653
|
+
try:
|
|
654
|
+
export_deployment_files_request_dto = ExportDeploymentFilesRequestDto(
|
|
655
|
+
model_version_fqn=model_version_fqn,
|
|
656
|
+
workspace_fqn=workspace_fqn,
|
|
657
|
+
service_name=name,
|
|
658
|
+
model_server=model_server,
|
|
659
|
+
)
|
|
660
|
+
response = self._mlfoundry_artifacts_api.export_deployment_files_by_fqn_post_with_http_info(
|
|
661
|
+
export_deployment_files_request_dto=export_deployment_files_request_dto,
|
|
662
|
+
_preload_content=False,
|
|
663
|
+
_request_timeout=_ML_FOUNDRY_API_REQUEST_TIMEOUT,
|
|
664
|
+
)
|
|
665
|
+
except ApiException as e:
|
|
666
|
+
err_msg = (
|
|
667
|
+
f"Failed to fetch deployment files for name={name}, "
|
|
668
|
+
f"model_version_fqn={model_version_fqn}, workspace_fqn={workspace_fqn}. "
|
|
669
|
+
f"Error: {e}"
|
|
670
|
+
)
|
|
671
|
+
raise MlFoundryException(err_msg) from e
|
|
672
|
+
|
|
673
|
+
output_dir = os.path.abspath(output_dir or os.getcwd())
|
|
674
|
+
codegen_dir = os.path.join(output_dir, name)
|
|
675
|
+
if codegen_dir == output_dir:
|
|
676
|
+
raise ValueError("Name cannot be empty, please provide a valid name")
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
with ContextualDirectoryManager(dir_path=codegen_dir) as dir_path:
|
|
680
|
+
with zipfile.ZipFile(BytesIO(response.raw_data), mode="r") as zip_file:
|
|
681
|
+
zip_file.extractall(dir_path)
|
|
682
|
+
except FileExistsError as e:
|
|
683
|
+
err_msg = (
|
|
684
|
+
f"Deployment directory {codegen_dir!r} already exists. "
|
|
685
|
+
"Please choose a different deployment name or delete the existing directory."
|
|
686
|
+
)
|
|
687
|
+
raise MlFoundryException(err_msg) from e
|
|
688
|
+
except zipfile.BadZipFile as e:
|
|
689
|
+
raise MlFoundryException(
|
|
690
|
+
f"Failed to extract model files. Error: {e}"
|
|
691
|
+
) from e
|
|
692
|
+
return codegen_dir
|
|
693
|
+
|
|
661
694
|
def get_model_version(
|
|
662
695
|
self,
|
|
663
696
|
ml_repo: str,
|
|
@@ -1227,7 +1260,6 @@ class MlFoundry:
|
|
|
1227
1260
|
ml_repo: str,
|
|
1228
1261
|
name: str,
|
|
1229
1262
|
model_file_or_folder: Union[str, BlobStorageDirectory],
|
|
1230
|
-
additional_files: Sequence[Tuple[Union[str, Path], Optional[str]]] = (),
|
|
1231
1263
|
description: Optional[str] = None,
|
|
1232
1264
|
metadata: Optional[Dict[str, Any]] = None,
|
|
1233
1265
|
progress: Optional[bool] = None,
|
|
@@ -1263,41 +1295,6 @@ class MlFoundry:
|
|
|
1263
1295
|
Can also be `None` if the framework is not known or not supported.
|
|
1264
1296
|
**Deprecated**: Prefer `ModelFrameworkType` over `enums.ModelFramework`.
|
|
1265
1297
|
|
|
1266
|
-
additional_files (Sequence[Tuple[Union[str, Path], Optional[str]]], optional): A list of pairs
|
|
1267
|
-
of (source path, destination path) to add additional files and folders
|
|
1268
|
-
to the model version contents. The first member of the pair should be a file or directory path
|
|
1269
|
-
and the second member should be the path inside the model versions contents to upload to.
|
|
1270
|
-
The model version contents are arranged like follows
|
|
1271
|
-
.
|
|
1272
|
-
└── model/
|
|
1273
|
-
└── # model files are serialized here
|
|
1274
|
-
└── # any additional files and folders can be added here.
|
|
1275
|
-
|
|
1276
|
-
You can also add additional files to model/ subdirectory by specifying the destination path as model/
|
|
1277
|
-
|
|
1278
|
-
```python
|
|
1279
|
-
from truefoundry.ml import TensorFlowFramework
|
|
1280
|
-
|
|
1281
|
-
run.log_model(
|
|
1282
|
-
name="xyz",
|
|
1283
|
-
model_file_or_folder="clf.joblib",
|
|
1284
|
-
framework=TensorFlowFramework(),
|
|
1285
|
-
additional_files=[("foo.txt", "foo/bar/foo.txt"), ("tokenizer/", "foo/tokenizer/")]
|
|
1286
|
-
)
|
|
1287
|
-
```
|
|
1288
|
-
|
|
1289
|
-
would result in
|
|
1290
|
-
|
|
1291
|
-
```
|
|
1292
|
-
.
|
|
1293
|
-
├── model/
|
|
1294
|
-
│ └── clf.joblib # if `model_file_or_folder` is a folder, contents will be added here
|
|
1295
|
-
└── foo/
|
|
1296
|
-
├── bar/
|
|
1297
|
-
│ └── foo.txt
|
|
1298
|
-
└── tokenizer/
|
|
1299
|
-
└── # contents of tokenizer/ directory will be uploaded here
|
|
1300
|
-
```
|
|
1301
1298
|
description (Optional[str], optional): arbitrary text upto 1024 characters to store as description.
|
|
1302
1299
|
This field can be updated at any time after logging. Defaults to `None`
|
|
1303
1300
|
metadata (Optional[Dict[str, Any]], optional): arbitrary json serializable dictionary to store metadata.
|
|
@@ -1387,7 +1384,6 @@ class MlFoundry:
|
|
|
1387
1384
|
ml_repo_id=ml_repo_id,
|
|
1388
1385
|
name=name,
|
|
1389
1386
|
model_file_or_folder=model_file_or_folder,
|
|
1390
|
-
additional_files=additional_files,
|
|
1391
1387
|
description=description,
|
|
1392
1388
|
metadata=metadata,
|
|
1393
1389
|
step=None,
|
truefoundry/ml/mlfoundry_run.py
CHANGED
|
@@ -3,7 +3,6 @@ import os
|
|
|
3
3
|
import platform
|
|
4
4
|
import re
|
|
5
5
|
import time
|
|
6
|
-
from pathlib import Path
|
|
7
6
|
from typing import (
|
|
8
7
|
TYPE_CHECKING,
|
|
9
8
|
Any,
|
|
@@ -12,7 +11,6 @@ from typing import (
|
|
|
12
11
|
Iterator,
|
|
13
12
|
List,
|
|
14
13
|
Optional,
|
|
15
|
-
Sequence,
|
|
16
14
|
Tuple,
|
|
17
15
|
Union,
|
|
18
16
|
)
|
|
@@ -930,7 +928,6 @@ class MlFoundryRun:
|
|
|
930
928
|
*,
|
|
931
929
|
name: str,
|
|
932
930
|
model_file_or_folder: Union[str, BlobStorageDirectory],
|
|
933
|
-
additional_files: Sequence[Tuple[Union[str, Path], Optional[str]]] = (),
|
|
934
931
|
description: Optional[str] = None,
|
|
935
932
|
metadata: Optional[Dict[str, Any]] = None,
|
|
936
933
|
step: int = 0,
|
|
@@ -965,33 +962,7 @@ class MlFoundryRun:
|
|
|
965
962
|
Supported frameworks can be found in `truefoundry.ml.enums.ModelFramework`.
|
|
966
963
|
Can also be `None` if the framework is not known or not supported.
|
|
967
964
|
**Deprecated**: Prefer `ModelFrameworkType` over `enums.ModelFramework`.
|
|
968
|
-
additional_files (Sequence[Tuple[Union[str, Path], Optional[str]]], optional): A list of pairs
|
|
969
|
-
of (source path, destination path) to add additional files and folders
|
|
970
|
-
to the model version contents. The first member of the pair should be a file or directory path
|
|
971
|
-
and the second member should be the path inside the model versions contents to upload to.
|
|
972
|
-
The model version contents are arranged like follows
|
|
973
|
-
.
|
|
974
|
-
└── model/
|
|
975
|
-
└── # model files are serialized here
|
|
976
|
-
└── # any additional files and folders can be added here.
|
|
977
|
-
|
|
978
|
-
You can also add additional files to model/ subdirectory by specifying the destination path as model/
|
|
979
965
|
|
|
980
|
-
```
|
|
981
|
-
E.g. >>> run.log_model(
|
|
982
|
-
... name="xyz", model_file_or_folder="clf.joblib", framework="sklearn",
|
|
983
|
-
... additional_files=[("foo.txt", "foo/bar/foo.txt"), ("tokenizer/", "foo/tokenizer/")]
|
|
984
|
-
... )
|
|
985
|
-
would result in
|
|
986
|
-
.
|
|
987
|
-
├── model/
|
|
988
|
-
│ └── clf.joblib # if `model_file_or_folder` is a folder, contents will be added here
|
|
989
|
-
└── foo/
|
|
990
|
-
├── bar/
|
|
991
|
-
│ └── foo.txt
|
|
992
|
-
└── tokenizer/
|
|
993
|
-
└── # contents of tokenizer/ directory will be uploaded here
|
|
994
|
-
```
|
|
995
966
|
description (Optional[str], optional): arbitrary text upto 1024 characters to store as description.
|
|
996
967
|
This field can be updated at any time after logging. Defaults to `None`
|
|
997
968
|
metadata (Optional[Dict[str, Any]], optional): arbitrary json serializable dictionary to store metadata.
|
|
@@ -1084,7 +1055,6 @@ class MlFoundryRun:
|
|
|
1084
1055
|
run=self,
|
|
1085
1056
|
name=name,
|
|
1086
1057
|
model_file_or_folder=model_file_or_folder,
|
|
1087
|
-
additional_files=additional_files,
|
|
1088
1058
|
description=description,
|
|
1089
1059
|
metadata=metadata,
|
|
1090
1060
|
step=step,
|
|
@@ -1,10 +1,63 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import warnings
|
|
2
|
-
from
|
|
3
|
-
|
|
4
|
-
from
|
|
3
|
+
from collections import OrderedDict
|
|
4
|
+
from pickle import load as pickle_load
|
|
5
|
+
from typing import Any, Callable, Dict, List, Literal, Optional, Type, Union, get_args
|
|
6
|
+
|
|
7
|
+
from truefoundry.common.utils import (
|
|
8
|
+
get_python_version_major_minor,
|
|
9
|
+
list_pip_packages_installed,
|
|
10
|
+
)
|
|
11
|
+
from truefoundry.ml.autogen.client import SerializationFormat
|
|
5
12
|
from truefoundry.ml.autogen.entities import artifacts as autogen_artifacts
|
|
13
|
+
from truefoundry.ml.enums import ModelFramework
|
|
14
|
+
from truefoundry.ml.log_types.artifacts.utils import (
|
|
15
|
+
get_single_file_path_if_only_one_in_directory,
|
|
16
|
+
to_unix_path,
|
|
17
|
+
)
|
|
6
18
|
from truefoundry.pydantic_v1 import BaseModel, Field
|
|
7
19
|
|
|
20
|
+
# Map serialization format to corresponding pip packages
|
|
21
|
+
SERIALIZATION_FORMAT_TO_PACKAGES_NAME_MAP = {
|
|
22
|
+
SerializationFormat.JOBLIB: ["joblib"],
|
|
23
|
+
SerializationFormat.CLOUDPICKLE: ["cloudpickle"],
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class _SerializationFormatLoaderRegistry:
|
|
28
|
+
def __init__(self):
|
|
29
|
+
# An OrderedDict is used to maintain the order of loaders based on priority
|
|
30
|
+
# The loaders are added in the following order:
|
|
31
|
+
# 1. joblib (if available)
|
|
32
|
+
# 2. cloudpickle (if available)
|
|
33
|
+
# 3. pickle (default fallback)
|
|
34
|
+
# This ensures that when looking up a loader, it follows the correct loading priority.
|
|
35
|
+
self._loader_map: Dict[SerializationFormat, Callable[[bytes], object]] = (
|
|
36
|
+
OrderedDict()
|
|
37
|
+
)
|
|
38
|
+
try:
|
|
39
|
+
from joblib import load as joblib_load
|
|
40
|
+
|
|
41
|
+
self._loader_map[SerializationFormat.JOBLIB] = joblib_load
|
|
42
|
+
except ImportError:
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
from cloudpickle import load as cloudpickle_load
|
|
47
|
+
|
|
48
|
+
self._loader_map[SerializationFormat.CLOUDPICKLE] = cloudpickle_load
|
|
49
|
+
except ImportError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
# Add pickle loader as a fallback
|
|
53
|
+
self._loader_map[SerializationFormat.PICKLE] = pickle_load
|
|
54
|
+
|
|
55
|
+
def get_loader_map(self) -> Dict[SerializationFormat, Callable[[bytes], object]]:
|
|
56
|
+
return self._loader_map
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
_serialization_format_loader_registry = _SerializationFormatLoaderRegistry()
|
|
60
|
+
|
|
8
61
|
|
|
9
62
|
class FastAIFramework(autogen_artifacts.FastAIFramework):
|
|
10
63
|
"""FastAI model Framework"""
|
|
@@ -167,3 +220,204 @@ class _ModelFramework(BaseModel):
|
|
|
167
220
|
return None
|
|
168
221
|
|
|
169
222
|
return cls.parse_obj(obj).__root__
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
# Mapping of model frameworks to pip packages
|
|
226
|
+
_MODEL_FRAMEWORK_TO_PIP_PACKAGES: Dict[Type[ModelFrameworkType], List[str]] = {
|
|
227
|
+
SklearnFramework: ["scikit-learn", "numpy", "pandas"],
|
|
228
|
+
XGBoostFramework: ["xgboost", "numpy", "pandas"],
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def _get_required_framework_pip_packages(framework: "ModelFrameworkType") -> List[str]:
|
|
233
|
+
"""
|
|
234
|
+
Fetches the pip packages required for a given model framework.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
framework ("ModelFrameworkType"): The model framework for which to fetch the pip packages.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
List[str]: The list of pip packages required for the given model framework.
|
|
241
|
+
If no packages are found for the framework type, returns an empty list.
|
|
242
|
+
"""
|
|
243
|
+
return _MODEL_FRAMEWORK_TO_PIP_PACKAGES.get(framework.__class__, [])
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _detect_model_serialization_format(
|
|
247
|
+
model_file_path: str,
|
|
248
|
+
) -> Optional[SerializationFormat]:
|
|
249
|
+
"""
|
|
250
|
+
The function will attempt to load the model using each framework's loader and return the first successful one.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
model_file_path (str): The path to the file to be loaded.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Optional[SerializationFormat]: The serialization format if successfully loaded, None otherwise.
|
|
257
|
+
"""
|
|
258
|
+
# Attempt to load the model using each framework
|
|
259
|
+
for (
|
|
260
|
+
serialization_format,
|
|
261
|
+
loader,
|
|
262
|
+
) in _serialization_format_loader_registry.get_loader_map().items():
|
|
263
|
+
try:
|
|
264
|
+
with open(model_file_path, "rb") as f:
|
|
265
|
+
loader(f)
|
|
266
|
+
return serialization_format
|
|
267
|
+
except Exception:
|
|
268
|
+
continue
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _fetch_framework_specific_pip_packages(
|
|
273
|
+
framework: "ModelFrameworkType",
|
|
274
|
+
) -> List[str]:
|
|
275
|
+
"""
|
|
276
|
+
Fetch the pip packages required for the given framework, including any dependencies
|
|
277
|
+
related to the framework's serialization format.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
framework: The framework object (e.g., SklearnFramework, XGBoostFramework).
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
List[str]: A list of pip packages for the given framework and environment,
|
|
284
|
+
including any dependencies based on the serialization format
|
|
285
|
+
(e.g., ['numpy==1.19.5', ...]).
|
|
286
|
+
"""
|
|
287
|
+
framework_package_names = _get_required_framework_pip_packages(framework=framework)
|
|
288
|
+
|
|
289
|
+
# Add serialization format dependencies if applicable
|
|
290
|
+
if isinstance(framework, (SklearnFramework, XGBoostFramework)):
|
|
291
|
+
framework_package_names.extend(
|
|
292
|
+
SERIALIZATION_FORMAT_TO_PACKAGES_NAME_MAP.get(
|
|
293
|
+
framework.serialization_format, []
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
return [
|
|
297
|
+
f"{package.name}=={package.version}"
|
|
298
|
+
for package in list_pip_packages_installed(
|
|
299
|
+
filter_package_names=framework_package_names
|
|
300
|
+
)
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def auto_update_environment_details(
|
|
305
|
+
environment: autogen_artifacts.ModelVersionEnvironment,
|
|
306
|
+
framework: Optional[ModelFrameworkType],
|
|
307
|
+
):
|
|
308
|
+
"""
|
|
309
|
+
Auto fetch the environment details if not provided, based on the provided environment and framework.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
environment: The environment object that holds environment details like python_version and pip_packages.
|
|
313
|
+
framework: The framework object (e.g., SklearnFramework, XGBoostFramework) that may affect pip_package fetching.
|
|
314
|
+
"""
|
|
315
|
+
# Auto fetch python_version if not provided
|
|
316
|
+
if not environment.python_version:
|
|
317
|
+
environment.python_version = get_python_version_major_minor()
|
|
318
|
+
|
|
319
|
+
# Framework-specific pip_package handling
|
|
320
|
+
if framework and not environment.pip_packages:
|
|
321
|
+
environment.pip_packages = _fetch_framework_specific_pip_packages(framework)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def _validate_and_get_absolute_model_filepath(
|
|
325
|
+
model_file_or_folder: str,
|
|
326
|
+
model_filepath: Optional[str] = None,
|
|
327
|
+
) -> Optional[str]:
|
|
328
|
+
# If no model_filepath is set, resolve it from the directory
|
|
329
|
+
if not model_filepath:
|
|
330
|
+
# If model_filepath is not set, resolve it based on these cases:
|
|
331
|
+
# - Case 1: model_file_or_folder/model.joblib -> model.joblib
|
|
332
|
+
# - Case 2: model_file_or_folder/folder/model.joblib -> folder/model.joblib
|
|
333
|
+
# - Case 3: model_file_or_folder/folder/model.joblib, model_file_or_folder/config.json -> None
|
|
334
|
+
return get_single_file_path_if_only_one_in_directory(model_file_or_folder)
|
|
335
|
+
|
|
336
|
+
# If model_filepath is already set, validate and resolve it:
|
|
337
|
+
# - Case 1: Resolve the absolute file path of the model file relative to the provided directory.
|
|
338
|
+
# Example: If model_file_or_folder is '/root/models' and model_filepath is 'model.joblib',
|
|
339
|
+
# the resolved model file path would be '/root/models/model.joblib'. Validate it.
|
|
340
|
+
#
|
|
341
|
+
# - Case 2: If model_filepath is a relative path, resolve it to an absolute path based on the provided directory.
|
|
342
|
+
# Example: If model_file_or_folder is '/root/models' and model_filepath is 'subfolder/model.joblib',
|
|
343
|
+
# the resolved path would be '/root/models/subfolder/model.joblib'. Validate it.
|
|
344
|
+
#
|
|
345
|
+
# - Case 3: Verify that the resolved model file exists and is a valid file.
|
|
346
|
+
# Example: If the resolved path is '/root/models/model.joblib', check if the file exists.
|
|
347
|
+
# If it does not exist, raise a FileNotFoundError.
|
|
348
|
+
#
|
|
349
|
+
# - Case 4: Ensure the resolved model file is located within the specified directory or is the directory itself.
|
|
350
|
+
# Example: If the resolved path is '/root/models/model.joblib' and model_file_or_folder is '/root/models',
|
|
351
|
+
# the resolved path is valid. If the file lies outside '/root/models', raise a ValueError.
|
|
352
|
+
#
|
|
353
|
+
|
|
354
|
+
# If model_filepath is set, Resolve the absolute path of the model file (It can be a relative path or absolute path)
|
|
355
|
+
model_dir = (
|
|
356
|
+
os.path.dirname(model_file_or_folder)
|
|
357
|
+
if os.path.isfile(model_file_or_folder)
|
|
358
|
+
else model_file_or_folder
|
|
359
|
+
)
|
|
360
|
+
absolute_model_filepath = os.path.abspath(os.path.join(model_dir, model_filepath))
|
|
361
|
+
|
|
362
|
+
# Validate if resolve valid is within the provided directory or is the same as it
|
|
363
|
+
if not (
|
|
364
|
+
absolute_model_filepath == model_file_or_folder
|
|
365
|
+
or absolute_model_filepath.startswith(model_file_or_folder + os.sep)
|
|
366
|
+
):
|
|
367
|
+
raise ValueError(
|
|
368
|
+
f"model_filepath '{model_filepath}' must be relative to "
|
|
369
|
+
f"{model_file_or_folder}. Resolved path '{absolute_model_filepath}' is invalid."
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
if not os.path.isfile(absolute_model_filepath):
|
|
373
|
+
raise FileNotFoundError(f"Model file not found: {absolute_model_filepath}")
|
|
374
|
+
|
|
375
|
+
return absolute_model_filepath
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _validate_and_resolve_model_filepath(
|
|
379
|
+
model_file_or_folder: str,
|
|
380
|
+
model_filepath: Optional[str] = None,
|
|
381
|
+
) -> Optional[str]:
|
|
382
|
+
absolute_model_filepath = _validate_and_get_absolute_model_filepath(
|
|
383
|
+
model_file_or_folder=model_file_or_folder, model_filepath=model_filepath
|
|
384
|
+
)
|
|
385
|
+
if absolute_model_filepath:
|
|
386
|
+
return to_unix_path(
|
|
387
|
+
os.path.relpath(absolute_model_filepath, model_file_or_folder)
|
|
388
|
+
if os.path.isdir(model_file_or_folder)
|
|
389
|
+
else os.path.basename(absolute_model_filepath)
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def auto_update_model_framework_details(
|
|
394
|
+
framework: "ModelFrameworkType", model_file_or_folder: str
|
|
395
|
+
):
|
|
396
|
+
"""
|
|
397
|
+
Auto update the model framework details based on the provided model file or folder path.
|
|
398
|
+
|
|
399
|
+
Args:
|
|
400
|
+
framework: The framework object (e.g., SklearnFramework, XGBoostFramework) to update.
|
|
401
|
+
model_file_or_folder: The path to the model file or folder.
|
|
402
|
+
"""
|
|
403
|
+
|
|
404
|
+
# Ensure the model file or folder path is an absolute path
|
|
405
|
+
model_file_or_folder = os.path.abspath(model_file_or_folder)
|
|
406
|
+
|
|
407
|
+
if isinstance(framework, (SklearnFramework, XGBoostFramework)):
|
|
408
|
+
framework.model_filepath = _validate_and_resolve_model_filepath(
|
|
409
|
+
model_file_or_folder=model_file_or_folder,
|
|
410
|
+
model_filepath=framework.model_filepath,
|
|
411
|
+
)
|
|
412
|
+
if framework.model_filepath:
|
|
413
|
+
absolute_model_filepath = (
|
|
414
|
+
model_file_or_folder
|
|
415
|
+
if os.path.isfile(model_file_or_folder)
|
|
416
|
+
else os.path.join(model_file_or_folder, framework.model_filepath)
|
|
417
|
+
)
|
|
418
|
+
framework.serialization_format = (
|
|
419
|
+
framework.serialization_format
|
|
420
|
+
or _detect_model_serialization_format(
|
|
421
|
+
model_file_path=absolute_model_filepath
|
|
422
|
+
)
|
|
423
|
+
)
|
|
@@ -29,6 +29,8 @@ _ML_REPO_NAME_REGEX = re.compile(r"^[a-zA-Z][a-zA-Z0-9\-]{1,98}[a-zA-Z0-9]$")
|
|
|
29
29
|
_RUN_NAME_REGEX = re.compile(r"^[a-zA-Z0-9-]*$")
|
|
30
30
|
_RUN_LOG_LOG_TYPE_REGEX = re.compile(r"^[a-zA-Z0-9-/]*$")
|
|
31
31
|
_RUN_LOG_KEY_REGEX = re.compile(r"^[a-zA-Z0-9-_]*$")
|
|
32
|
+
_APP_NAME_REGEX = re.compile(r"^[a-z][a-z0-9\\-]{1,30}[a-z0-9]$")
|
|
33
|
+
|
|
32
34
|
|
|
33
35
|
MAX_PARAMS_TAGS_PER_BATCH = 100
|
|
34
36
|
MAX_METRICS_PER_BATCH = 1000
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: truefoundry
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.1rc2
|
|
4
4
|
Summary: Truefoundry CLI
|
|
5
5
|
Author: Abhishek Choudhary
|
|
6
6
|
Author-email: abhishek@truefoundry.com
|
|
@@ -19,7 +19,6 @@ Requires-Dist: aenum (>=3.0.0,<4.0.0)
|
|
|
19
19
|
Requires-Dist: click (>=7.0.0,<9.0.0)
|
|
20
20
|
Requires-Dist: coolname (>=1.1.0,<2.0.0)
|
|
21
21
|
Requires-Dist: docker (>=6.1.2,<8.0.0)
|
|
22
|
-
Requires-Dist: fastapi (>=0.56.0,<0.200.0)
|
|
23
22
|
Requires-Dist: filelock (>=3.8.0,<4.0.0)
|
|
24
23
|
Requires-Dist: flytekit (==1.13.13) ; extra == "workflow"
|
|
25
24
|
Requires-Dist: gitignorefile (>=1.1.2,<2.0.0)
|
|
@@ -29,8 +28,6 @@ Requires-Dist: numpy (>=1.23.0,<2.0.0) ; python_version < "3.12"
|
|
|
29
28
|
Requires-Dist: numpy (>=1.26.0,<2.0.0) ; python_version >= "3.12"
|
|
30
29
|
Requires-Dist: openai (>=1.16.2,<2.0.0)
|
|
31
30
|
Requires-Dist: packaging (>=20.0,<25.0)
|
|
32
|
-
Requires-Dist: pandas (>=1.0.0,<3.0.0) ; python_version < "3.10"
|
|
33
|
-
Requires-Dist: pandas (>=1.4.0,<3.0.0) ; python_version >= "3.10"
|
|
34
31
|
Requires-Dist: pydantic (>=1.8.2,<3.0.0)
|
|
35
32
|
Requires-Dist: pygments (>=2.12.0,<3.0.0)
|
|
36
33
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
@@ -46,7 +43,6 @@ Requires-Dist: scipy (>=1.5.0,<2.0.0) ; python_version < "3.12"
|
|
|
46
43
|
Requires-Dist: tqdm (>=4.0.0,<5.0.0)
|
|
47
44
|
Requires-Dist: typing-extensions (>=4.0)
|
|
48
45
|
Requires-Dist: urllib3 (>=1.26.18,<3)
|
|
49
|
-
Requires-Dist: uvicorn (>=0.13.0,<1.0.0)
|
|
50
46
|
Requires-Dist: yq (>=3.1.0,<4.0.0)
|
|
51
47
|
Description-Content-Type: text/markdown
|
|
52
48
|
|