truefoundry 0.5.1rc7__py3-none-any.whl → 0.5.2rc1__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/constants.py +9 -0
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +4 -2
- truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +7 -5
- truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +87 -28
- truefoundry/deploy/builder/constants.py +8 -0
- truefoundry/deploy/builder/utils.py +9 -4
- truefoundry/deploy/cli/commands/apply_command.py +9 -2
- truefoundry/deploy/lib/clients/servicefoundry_client.py +2 -2
- truefoundry/deploy/lib/dao/apply.py +21 -6
- truefoundry/ml/__init__.py +8 -4
- truefoundry/ml/cli/commands/model_init.py +1 -1
- truefoundry/ml/mlfoundry_api.py +2 -1
- truefoundry/ml/mlfoundry_run.py +2 -1
- truefoundry/ml/model_framework.py +47 -10
- {truefoundry-0.5.1rc7.dist-info → truefoundry-0.5.2rc1.dist-info}/METADATA +1 -1
- {truefoundry-0.5.1rc7.dist-info → truefoundry-0.5.2rc1.dist-info}/RECORD +18 -18
- {truefoundry-0.5.1rc7.dist-info → truefoundry-0.5.2rc1.dist-info}/WHEEL +0 -0
- {truefoundry-0.5.1rc7.dist-info → truefoundry-0.5.2rc1.dist-info}/entry_points.txt +0 -0
truefoundry/common/constants.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import enum
|
|
1
2
|
import os
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Optional
|
|
@@ -15,6 +16,11 @@ TFY_INTERNAL_SIGNED_URL_SERVER_HOST_ENV_KEY = "TFY_INTERNAL_SIGNED_URL_SERVER_HO
|
|
|
15
16
|
TFY_INTERNAL_SIGNED_URL_SERVER_TOKEN_ENV_KEY = "TFY_INTERNAL_SIGNED_URL_SERVER_TOKEN"
|
|
16
17
|
|
|
17
18
|
|
|
19
|
+
class PythonPackageManager(str, enum.Enum):
|
|
20
|
+
PIP = "pip"
|
|
21
|
+
UV = "uv"
|
|
22
|
+
|
|
23
|
+
|
|
18
24
|
class TrueFoundrySdkEnv(BaseSettings):
|
|
19
25
|
# Note: Every field in this class should have a default value
|
|
20
26
|
# Never expect the user to set these values
|
|
@@ -46,6 +52,9 @@ class TrueFoundrySdkEnv(BaseSettings):
|
|
|
46
52
|
# For local development, this enables futher configuration via _TFYServersConfig
|
|
47
53
|
TFY_CLI_LOCAL_DEV_MODE: bool = False
|
|
48
54
|
|
|
55
|
+
TFY_PYTHON_BUILD_PACKAGE_MANAGER: PythonPackageManager = PythonPackageManager.PIP
|
|
56
|
+
TFY_PYTHON_BUILD_UV_IMAGE_URI: str = "ghcr.io/astral-sh/uv:latest"
|
|
57
|
+
|
|
49
58
|
|
|
50
59
|
ENV_VARS = TrueFoundrySdkEnv()
|
|
51
60
|
API_SERVER_RELATIVE_PATH = "api/svc"
|
|
@@ -8,7 +8,7 @@ from truefoundry.deploy.builder.builders.tfy_notebook_buildpack.dockerfile_templ
|
|
|
8
8
|
NotebookImageBuild,
|
|
9
9
|
generate_dockerfile_content,
|
|
10
10
|
)
|
|
11
|
-
from truefoundry.deploy.builder.utils import
|
|
11
|
+
from truefoundry.deploy.builder.utils import has_python_package_manager_conf_secret
|
|
12
12
|
|
|
13
13
|
__all__ = ["generate_dockerfile_content", "build"]
|
|
14
14
|
|
|
@@ -38,7 +38,9 @@ def build(
|
|
|
38
38
|
build_configuration: NotebookImageBuild,
|
|
39
39
|
extra_opts: Optional[List[str]] = None,
|
|
40
40
|
):
|
|
41
|
-
mount_pip_conf_secret =
|
|
41
|
+
mount_pip_conf_secret = (
|
|
42
|
+
has_python_package_manager_conf_secret(extra_opts) if extra_opts else False
|
|
43
|
+
)
|
|
42
44
|
with TemporaryDirectory() as local_dir:
|
|
43
45
|
docker_build_configuration = _convert_to_dockerfile_build_config(
|
|
44
46
|
build_configuration,
|
|
@@ -7,7 +7,7 @@ from truefoundry.deploy.builder.builders import dockerfile
|
|
|
7
7
|
from truefoundry.deploy.builder.builders.tfy_python_buildpack.dockerfile_template import (
|
|
8
8
|
generate_dockerfile_content,
|
|
9
9
|
)
|
|
10
|
-
from truefoundry.deploy.builder.utils import
|
|
10
|
+
from truefoundry.deploy.builder.utils import has_python_package_manager_conf_secret
|
|
11
11
|
|
|
12
12
|
__all__ = ["generate_dockerfile_content", "build"]
|
|
13
13
|
|
|
@@ -15,11 +15,11 @@ __all__ = ["generate_dockerfile_content", "build"]
|
|
|
15
15
|
def _convert_to_dockerfile_build_config(
|
|
16
16
|
build_configuration: PythonBuild,
|
|
17
17
|
dockerfile_path: str,
|
|
18
|
-
|
|
18
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
19
19
|
) -> DockerFileBuild:
|
|
20
20
|
dockerfile_content = generate_dockerfile_content(
|
|
21
21
|
build_configuration=build_configuration,
|
|
22
|
-
|
|
22
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
23
23
|
)
|
|
24
24
|
with open(dockerfile_path, "w", encoding="utf8") as fp:
|
|
25
25
|
fp.write(dockerfile_content)
|
|
@@ -36,12 +36,14 @@ def build(
|
|
|
36
36
|
build_configuration: PythonBuild,
|
|
37
37
|
extra_opts: Optional[List[str]] = None,
|
|
38
38
|
):
|
|
39
|
-
|
|
39
|
+
mount_python_package_manager_conf_secret = (
|
|
40
|
+
has_python_package_manager_conf_secret(extra_opts) if extra_opts else False
|
|
41
|
+
)
|
|
40
42
|
with TemporaryDirectory() as local_dir:
|
|
41
43
|
docker_build_configuration = _convert_to_dockerfile_build_config(
|
|
42
44
|
build_configuration,
|
|
43
45
|
dockerfile_path=os.path.join(local_dir, "Dockerfile"),
|
|
44
|
-
|
|
46
|
+
mount_python_package_manager_conf_secret=mount_python_package_manager_conf_secret,
|
|
45
47
|
)
|
|
46
48
|
dockerfile.build(
|
|
47
49
|
tag=tag,
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import shlex
|
|
2
3
|
from typing import Dict, List, Optional
|
|
3
4
|
|
|
4
5
|
from mako.template import Template
|
|
5
6
|
|
|
6
|
-
from truefoundry.common.constants import ENV_VARS
|
|
7
|
+
from truefoundry.common.constants import ENV_VARS, PythonPackageManager
|
|
7
8
|
from truefoundry.deploy.auto_gen.models import PythonBuild
|
|
8
9
|
from truefoundry.deploy.builder.constants import (
|
|
9
10
|
PIP_CONF_BUILDKIT_SECRET_MOUNT,
|
|
10
11
|
PIP_CONF_SECRET_MOUNT_AS_ENV,
|
|
12
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT,
|
|
13
|
+
UV_CONF_SECRET_MOUNT_AS_ENV,
|
|
11
14
|
)
|
|
12
15
|
from truefoundry.deploy.v2.lib.patched_models import CUDAVersion
|
|
13
16
|
|
|
@@ -22,8 +25,8 @@ RUN ${apt_install_command}
|
|
|
22
25
|
% if requirements_path is not None:
|
|
23
26
|
COPY ${requirements_path} ${requirements_destination_path}
|
|
24
27
|
% endif
|
|
25
|
-
% if
|
|
26
|
-
RUN ${
|
|
28
|
+
% if python_packages_install_command is not None:
|
|
29
|
+
RUN ${package_manager_config_secret_mount} ${python_packages_install_command}
|
|
27
30
|
% endif
|
|
28
31
|
COPY . /app
|
|
29
32
|
WORKDIR /app
|
|
@@ -114,44 +117,91 @@ def generate_pip_install_command(
|
|
|
114
117
|
mount_pip_conf_secret: bool = False,
|
|
115
118
|
) -> Optional[str]:
|
|
116
119
|
upgrade_pip_command = "python -m pip install -U pip setuptools wheel"
|
|
117
|
-
|
|
118
|
-
|
|
120
|
+
envs = []
|
|
121
|
+
if mount_pip_conf_secret:
|
|
122
|
+
envs.append(PIP_CONF_SECRET_MOUNT_AS_ENV)
|
|
123
|
+
|
|
124
|
+
command = ["python", "-m", "pip", "install", "--use-pep517", "--no-cache-dir"]
|
|
125
|
+
args = []
|
|
119
126
|
if requirements_path:
|
|
120
|
-
|
|
127
|
+
args.append("-r")
|
|
128
|
+
args.append(requirements_path)
|
|
121
129
|
|
|
122
130
|
if pip_packages:
|
|
123
|
-
|
|
124
|
-
final_pip_install_command or pip_install_base_command
|
|
125
|
-
)
|
|
126
|
-
final_pip_install_command += " " + " ".join(
|
|
127
|
-
f"'{package}'" for package in pip_packages
|
|
128
|
-
)
|
|
131
|
+
args.extend(pip_packages)
|
|
129
132
|
|
|
130
|
-
if not
|
|
133
|
+
if not args:
|
|
131
134
|
return None
|
|
132
135
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
final_pip_install_command = shlex.join(envs + command + args)
|
|
137
|
+
final_docker_run_command = " && ".join(
|
|
138
|
+
[upgrade_pip_command, final_pip_install_command]
|
|
139
|
+
)
|
|
140
|
+
return final_docker_run_command
|
|
137
141
|
|
|
138
|
-
|
|
142
|
+
|
|
143
|
+
def generate_uv_pip_install_command(
|
|
144
|
+
requirements_path: Optional[str],
|
|
145
|
+
pip_packages: Optional[List[str]],
|
|
146
|
+
mount_uv_conf_secret: bool = False,
|
|
147
|
+
) -> Optional[str]:
|
|
148
|
+
upgrade_pip_command = "python -m pip install -U pip setuptools wheel"
|
|
149
|
+
uv_mount = f"--mount=from={ENV_VARS.TFY_PYTHON_BUILD_UV_IMAGE_URI},source=/uv,target=/usr/local/bin/uv"
|
|
150
|
+
envs = [
|
|
151
|
+
"UV_SYSTEM_PYTHON=true",
|
|
152
|
+
"UV_LINK_MODE=copy",
|
|
153
|
+
"UV_PYTHON_DOWNLOADS=never",
|
|
154
|
+
"UV_INDEX_STRATEGY=unsafe-best-match",
|
|
155
|
+
]
|
|
156
|
+
if mount_uv_conf_secret:
|
|
157
|
+
envs.append(UV_CONF_SECRET_MOUNT_AS_ENV)
|
|
158
|
+
|
|
159
|
+
command = ["uv", "pip", "install", "--no-cache-dir"]
|
|
160
|
+
|
|
161
|
+
args = []
|
|
162
|
+
|
|
163
|
+
if requirements_path:
|
|
164
|
+
args.append("-r")
|
|
165
|
+
args.append(requirements_path)
|
|
166
|
+
|
|
167
|
+
if pip_packages:
|
|
168
|
+
args.extend(pip_packages)
|
|
169
|
+
|
|
170
|
+
if not args:
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
uv_pip_install_command = shlex.join(envs + command + args)
|
|
174
|
+
shell_commands = " && ".join([upgrade_pip_command, uv_pip_install_command])
|
|
175
|
+
final_docker_run_command = " ".join([uv_mount, shell_commands])
|
|
176
|
+
|
|
177
|
+
return final_docker_run_command
|
|
139
178
|
|
|
140
179
|
|
|
141
180
|
def generate_dockerfile_content(
|
|
142
181
|
build_configuration: PythonBuild,
|
|
143
|
-
|
|
182
|
+
package_manager: str = ENV_VARS.TFY_PYTHON_BUILD_PACKAGE_MANAGER,
|
|
183
|
+
mount_python_package_manager_conf_secret: bool = False,
|
|
144
184
|
) -> str:
|
|
145
185
|
# TODO (chiragjn): Handle recursive references to other requirements files e.g. `-r requirements-gpu.txt`
|
|
146
186
|
requirements_path = resolve_requirements_txt_path(build_configuration)
|
|
147
187
|
requirements_destination_path = (
|
|
148
188
|
"/tmp/requirements.txt" if requirements_path else None
|
|
149
189
|
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
190
|
+
if package_manager == PythonPackageManager.PIP.value:
|
|
191
|
+
python_packages_install_command = generate_pip_install_command(
|
|
192
|
+
requirements_path=requirements_destination_path,
|
|
193
|
+
pip_packages=build_configuration.pip_packages,
|
|
194
|
+
mount_pip_conf_secret=mount_python_package_manager_conf_secret,
|
|
195
|
+
)
|
|
196
|
+
elif package_manager == PythonPackageManager.UV.value:
|
|
197
|
+
python_packages_install_command = generate_uv_pip_install_command(
|
|
198
|
+
requirements_path=requirements_destination_path,
|
|
199
|
+
pip_packages=build_configuration.pip_packages,
|
|
200
|
+
mount_uv_conf_secret=mount_python_package_manager_conf_secret,
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
204
|
+
|
|
155
205
|
apt_install_command = generate_apt_install_command(
|
|
156
206
|
apt_packages=build_configuration.apt_packages
|
|
157
207
|
)
|
|
@@ -161,13 +211,22 @@ def generate_dockerfile_content(
|
|
|
161
211
|
"apt_install_command": apt_install_command,
|
|
162
212
|
"requirements_path": requirements_path,
|
|
163
213
|
"requirements_destination_path": requirements_destination_path,
|
|
164
|
-
"
|
|
214
|
+
"python_packages_install_command": python_packages_install_command,
|
|
165
215
|
}
|
|
166
216
|
|
|
167
|
-
if
|
|
168
|
-
|
|
217
|
+
if mount_python_package_manager_conf_secret:
|
|
218
|
+
if package_manager == PythonPackageManager.PIP.value:
|
|
219
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
220
|
+
PIP_CONF_BUILDKIT_SECRET_MOUNT
|
|
221
|
+
)
|
|
222
|
+
elif package_manager == PythonPackageManager.UV.value:
|
|
223
|
+
template_args["package_manager_config_secret_mount"] = (
|
|
224
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
raise ValueError(f"Unsupported package manager: {package_manager}")
|
|
169
228
|
else:
|
|
170
|
-
template_args["
|
|
229
|
+
template_args["package_manager_config_secret_mount"] = ""
|
|
171
230
|
|
|
172
231
|
if build_configuration.cuda_version:
|
|
173
232
|
template = CUDA_DOCKERFILE_TEMPLATE
|
|
@@ -5,3 +5,11 @@ PIP_CONF_BUILDKIT_SECRET_MOUNT = (
|
|
|
5
5
|
PIP_CONF_SECRET_MOUNT_AS_ENV = (
|
|
6
6
|
f"PIP_CONFIG_FILE=/run/secrets/{BUILDKIT_SECRET_MOUNT_PIP_CONF_ID}"
|
|
7
7
|
)
|
|
8
|
+
|
|
9
|
+
BUILDKIT_SECRET_MOUNT_UV_CONF_ID = "uv.toml"
|
|
10
|
+
UV_CONF_BUILDKIT_SECRET_MOUNT = (
|
|
11
|
+
f"--mount=type=secret,id={BUILDKIT_SECRET_MOUNT_UV_CONF_ID}"
|
|
12
|
+
)
|
|
13
|
+
UV_CONF_SECRET_MOUNT_AS_ENV = (
|
|
14
|
+
f"UV_CONFIG_FILE=/run/secrets/{BUILDKIT_SECRET_MOUNT_UV_CONF_ID}"
|
|
15
|
+
)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from typing import List, Optional
|
|
2
2
|
|
|
3
|
-
from truefoundry.deploy.builder.constants import
|
|
3
|
+
from truefoundry.deploy.builder.constants import (
|
|
4
|
+
BUILDKIT_SECRET_MOUNT_PIP_CONF_ID,
|
|
5
|
+
BUILDKIT_SECRET_MOUNT_UV_CONF_ID,
|
|
6
|
+
)
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
def _get_id_from_buildkit_secret_value(value: str) -> Optional[str]:
|
|
@@ -19,14 +22,16 @@ def _get_id_from_buildkit_secret_value(value: str) -> Optional[str]:
|
|
|
19
22
|
return None
|
|
20
23
|
|
|
21
24
|
|
|
22
|
-
def
|
|
25
|
+
def has_python_package_manager_conf_secret(docker_build_extra_args: List[str]) -> bool:
|
|
23
26
|
args = [arg.strip() for arg in docker_build_extra_args]
|
|
24
27
|
for i, arg in enumerate(docker_build_extra_args):
|
|
25
28
|
if (
|
|
26
29
|
arg == "--secret"
|
|
27
30
|
and i + 1 < len(args)
|
|
28
|
-
and
|
|
29
|
-
|
|
31
|
+
and (
|
|
32
|
+
_get_id_from_buildkit_secret_value(args[i + 1])
|
|
33
|
+
in (BUILDKIT_SECRET_MOUNT_PIP_CONF_ID, BUILDKIT_SECRET_MOUNT_UV_CONF_ID)
|
|
34
|
+
)
|
|
30
35
|
):
|
|
31
36
|
return True
|
|
32
37
|
return False
|
|
@@ -30,13 +30,20 @@ from truefoundry.deploy.lib.model.entity import ApplyResult
|
|
|
30
30
|
required=True,
|
|
31
31
|
multiple=True,
|
|
32
32
|
)
|
|
33
|
+
@click.option(
|
|
34
|
+
"--dry-run",
|
|
35
|
+
"--dry_run",
|
|
36
|
+
is_flag=True,
|
|
37
|
+
show_default=True,
|
|
38
|
+
help="Simulate the process without actually applying the manifest",
|
|
39
|
+
)
|
|
33
40
|
@handle_exception_wrapper
|
|
34
|
-
def apply_command(files: Tuple[str, ...]):
|
|
41
|
+
def apply_command(files: Tuple[str, ...], dry_run: bool = False):
|
|
35
42
|
apply_results: List[ApplyResult] = []
|
|
36
43
|
client = ServiceFoundryServiceClient()
|
|
37
44
|
for file in files:
|
|
38
45
|
with console.status(PROMPT_APPLYING_MANIFEST.format(file), spinner="dots"):
|
|
39
|
-
for apply_result in apply_lib.apply_manifest_file(file, client):
|
|
46
|
+
for apply_result in apply_lib.apply_manifest_file(file, client, dry_run):
|
|
40
47
|
if apply_result.success:
|
|
41
48
|
console.print(f"[green]\u2714 {apply_result.message}[/]")
|
|
42
49
|
else:
|
|
@@ -680,9 +680,9 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
|
|
|
680
680
|
return parse_obj_as(List[Deployment], response)
|
|
681
681
|
|
|
682
682
|
@check_min_cli_version
|
|
683
|
-
def apply(self, manifest: Dict[str, Any]):
|
|
683
|
+
def apply(self, manifest: Dict[str, Any], dry_run: bool = False):
|
|
684
684
|
url = f"{self._api_server_url}/{VERSION_PREFIX}/apply"
|
|
685
|
-
body = {"manifest": manifest}
|
|
685
|
+
body = {"manifest": manifest, "dryRun": dry_run}
|
|
686
686
|
response = session_with_retries().put(
|
|
687
687
|
url, headers=self._get_header(), json=body
|
|
688
688
|
)
|
|
@@ -15,6 +15,7 @@ def _apply_manifest(
|
|
|
15
15
|
client: Optional[ServiceFoundryServiceClient] = None,
|
|
16
16
|
filename: Optional[str] = None,
|
|
17
17
|
index: Optional[int] = None,
|
|
18
|
+
dry_run: bool = False,
|
|
18
19
|
) -> ApplyResult:
|
|
19
20
|
client = client or ServiceFoundryServiceClient()
|
|
20
21
|
|
|
@@ -32,29 +33,38 @@ def _apply_manifest(
|
|
|
32
33
|
message=f"Failed to apply manifest{file_metadata}. Error: {ex}",
|
|
33
34
|
)
|
|
34
35
|
|
|
36
|
+
prefix = "[Dry Run] " if dry_run else ""
|
|
37
|
+
suffix = " (No changes were applied)" if dry_run else ""
|
|
35
38
|
try:
|
|
36
|
-
client.apply(manifest.dict())
|
|
39
|
+
client.apply(manifest.dict(), dry_run)
|
|
40
|
+
|
|
37
41
|
return ApplyResult(
|
|
38
42
|
success=True,
|
|
39
|
-
message=
|
|
43
|
+
message=(
|
|
44
|
+
f"{prefix}Successfully configured manifest {manifest.name} of type {manifest.type}.{suffix}"
|
|
45
|
+
),
|
|
40
46
|
)
|
|
41
47
|
except Exception as ex:
|
|
42
48
|
return ApplyResult(
|
|
43
49
|
success=False,
|
|
44
|
-
message=
|
|
50
|
+
message=(
|
|
51
|
+
f"{prefix}Failed to apply manifest {manifest.name} of type {manifest.type}. Error: {ex}.{suffix}"
|
|
52
|
+
),
|
|
45
53
|
)
|
|
46
54
|
|
|
47
55
|
|
|
48
56
|
def apply_manifest(
|
|
49
57
|
manifest: Dict[str, Any],
|
|
50
58
|
client: Optional[ServiceFoundryServiceClient] = None,
|
|
59
|
+
dry_run: bool = False,
|
|
51
60
|
) -> ApplyResult:
|
|
52
|
-
return _apply_manifest(manifest=manifest, client=client)
|
|
61
|
+
return _apply_manifest(manifest=manifest, client=client, dry_run=dry_run)
|
|
53
62
|
|
|
54
63
|
|
|
55
64
|
def apply_manifest_file(
|
|
56
65
|
filepath: str,
|
|
57
66
|
client: Optional[ServiceFoundryServiceClient] = None,
|
|
67
|
+
dry_run: bool = False,
|
|
58
68
|
) -> Iterator[ApplyResult]:
|
|
59
69
|
client = client or ServiceFoundryServiceClient()
|
|
60
70
|
filename = Path(filepath).name
|
|
@@ -67,14 +77,19 @@ def apply_manifest_file(
|
|
|
67
77
|
message=f"Failed to read file {filepath} as a valid YAML file. Error: {ex}",
|
|
68
78
|
)
|
|
69
79
|
else:
|
|
80
|
+
prefix = "[Dry Run] " if dry_run else ""
|
|
70
81
|
for index, manifest in enumerate(manifests_it):
|
|
71
82
|
if not isinstance(manifest, dict):
|
|
72
83
|
yield ApplyResult(
|
|
73
84
|
success=False,
|
|
74
|
-
message=f"Failed to apply manifest at index {index} from file {filename}. Error: A manifest must be a dict, got type {type(manifest)}",
|
|
85
|
+
message=f"{prefix}Failed to apply manifest at index {index} from file {filename}. Error: A manifest must be a dict, got type {type(manifest)}",
|
|
75
86
|
)
|
|
76
87
|
continue
|
|
77
88
|
|
|
78
89
|
yield _apply_manifest(
|
|
79
|
-
manifest=manifest,
|
|
90
|
+
manifest=manifest,
|
|
91
|
+
client=client,
|
|
92
|
+
filename=filename,
|
|
93
|
+
index=index,
|
|
94
|
+
dry_run=dry_run,
|
|
80
95
|
)
|
truefoundry/ml/__init__.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
from truefoundry.ml.autogen.client.models import (
|
|
2
|
-
|
|
1
|
+
from truefoundry.ml.autogen.client.models import ( # type: ignore[attr-defined]
|
|
2
|
+
InferMethodName,
|
|
3
3
|
ModelVersionEnvironment,
|
|
4
4
|
SklearnModelSchema,
|
|
5
5
|
XGBoostModelSchema,
|
|
6
6
|
)
|
|
7
|
+
from truefoundry.ml.autogen.entities.artifacts import LibraryName
|
|
7
8
|
from truefoundry.ml.enums import (
|
|
8
9
|
DataSlice,
|
|
9
10
|
FileFormat,
|
|
@@ -40,6 +41,7 @@ from truefoundry.ml.model_framework import (
|
|
|
40
41
|
TransformersFramework,
|
|
41
42
|
XGBoostFramework,
|
|
42
43
|
sklearn_infer_schema,
|
|
44
|
+
xgboost_infer_schema,
|
|
43
45
|
)
|
|
44
46
|
|
|
45
47
|
__all__ = [
|
|
@@ -51,9 +53,11 @@ __all__ = [
|
|
|
51
53
|
"DataSlice",
|
|
52
54
|
"FastAIFramework",
|
|
53
55
|
"FileFormat",
|
|
56
|
+
"get_client",
|
|
54
57
|
"GluonFramework",
|
|
55
58
|
"H2OFramework",
|
|
56
59
|
"Image",
|
|
60
|
+
"InferMethodName",
|
|
57
61
|
"KerasFramework",
|
|
58
62
|
"LibraryName",
|
|
59
63
|
"LightGBMFramework",
|
|
@@ -68,6 +72,7 @@ __all__ = [
|
|
|
68
72
|
"PaddleFramework",
|
|
69
73
|
"Plot",
|
|
70
74
|
"PyTorchFramework",
|
|
75
|
+
"sklearn_infer_schema",
|
|
71
76
|
"SklearnFramework",
|
|
72
77
|
"SklearnModelSchema",
|
|
73
78
|
"SpaCyFramework",
|
|
@@ -75,10 +80,9 @@ __all__ = [
|
|
|
75
80
|
"TensorFlowFramework",
|
|
76
81
|
"TransformersFramework",
|
|
77
82
|
"ViewType",
|
|
83
|
+
"xgboost_infer_schema",
|
|
78
84
|
"XGBoostFramework",
|
|
79
85
|
"XGBoostModelSchema",
|
|
80
|
-
"get_client",
|
|
81
|
-
"sklearn_infer_schema",
|
|
82
86
|
]
|
|
83
87
|
|
|
84
88
|
init_logger()
|
|
@@ -6,7 +6,7 @@ import rich_click as click
|
|
|
6
6
|
from truefoundry.deploy.cli.console import console
|
|
7
7
|
from truefoundry.deploy.cli.const import COMMAND_CLS
|
|
8
8
|
from truefoundry.deploy.cli.util import handle_exception_wrapper
|
|
9
|
-
from truefoundry.ml.autogen.client
|
|
9
|
+
from truefoundry.ml.autogen.client import ModelServer # type: ignore[attr-defined]
|
|
10
10
|
from truefoundry.ml.cli.utils import (
|
|
11
11
|
AppName,
|
|
12
12
|
NonEmptyString,
|
truefoundry/ml/mlfoundry_api.py
CHANGED
|
@@ -18,7 +18,7 @@ from typing import (
|
|
|
18
18
|
import coolname
|
|
19
19
|
|
|
20
20
|
from truefoundry.common.utils import ContextualDirectoryManager, relogin_error_message
|
|
21
|
-
from truefoundry.ml import
|
|
21
|
+
from truefoundry.ml import constants
|
|
22
22
|
from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
23
23
|
ArtifactDto,
|
|
24
24
|
ArtifactType,
|
|
@@ -35,6 +35,7 @@ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
|
35
35
|
MlfoundryArtifactsApi,
|
|
36
36
|
ModelDto,
|
|
37
37
|
ModelServer,
|
|
38
|
+
ModelVersionEnvironment,
|
|
38
39
|
RunsApi,
|
|
39
40
|
RunTagDto,
|
|
40
41
|
SearchRunsRequestDto,
|
truefoundry/ml/mlfoundry_run.py
CHANGED
|
@@ -18,7 +18,7 @@ from urllib.parse import urljoin, urlsplit
|
|
|
18
18
|
|
|
19
19
|
from truefoundry import version
|
|
20
20
|
from truefoundry.common.utils import relogin_error_message
|
|
21
|
-
from truefoundry.ml import
|
|
21
|
+
from truefoundry.ml import constants
|
|
22
22
|
from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
23
23
|
ArtifactType,
|
|
24
24
|
DeleteRunRequest,
|
|
@@ -29,6 +29,7 @@ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
|
|
|
29
29
|
MetricDto,
|
|
30
30
|
MetricsApi,
|
|
31
31
|
MlfoundryArtifactsApi,
|
|
32
|
+
ModelVersionEnvironment,
|
|
32
33
|
ParamDto,
|
|
33
34
|
RunDataDto,
|
|
34
35
|
RunDto,
|
|
@@ -35,6 +35,7 @@ from truefoundry.pydantic_v1 import BaseModel, Field
|
|
|
35
35
|
|
|
36
36
|
if TYPE_CHECKING:
|
|
37
37
|
from sklearn.base import BaseEstimator
|
|
38
|
+
from xgboost import Booster, XGBModel
|
|
38
39
|
|
|
39
40
|
# Map serialization format to corresponding pip packages
|
|
40
41
|
SERIALIZATION_FORMAT_TO_PACKAGES_NAME_MAP = {
|
|
@@ -467,6 +468,24 @@ def auto_update_model_framework_details(
|
|
|
467
468
|
)
|
|
468
469
|
|
|
469
470
|
|
|
471
|
+
def _infer_schema(
|
|
472
|
+
model_input: Any,
|
|
473
|
+
model: Union["BaseEstimator", "Booster", "XGBModel"],
|
|
474
|
+
infer_method_name: str = "predict",
|
|
475
|
+
) -> Dict[str, Any]:
|
|
476
|
+
if not hasattr(model, infer_method_name):
|
|
477
|
+
raise ValueError(
|
|
478
|
+
f"Model does not have the method '{infer_method_name}' to infer the schema."
|
|
479
|
+
)
|
|
480
|
+
model_infer_method = getattr(model, infer_method_name)
|
|
481
|
+
model_output = model_infer_method(model_input)
|
|
482
|
+
|
|
483
|
+
model_signature = infer_signature(
|
|
484
|
+
model_input=model_input, model_output=model_output
|
|
485
|
+
)
|
|
486
|
+
return model_signature.to_dict()
|
|
487
|
+
|
|
488
|
+
|
|
470
489
|
def sklearn_infer_schema(
|
|
471
490
|
model_input: Any,
|
|
472
491
|
model: "BaseEstimator",
|
|
@@ -483,19 +502,37 @@ def sklearn_infer_schema(
|
|
|
483
502
|
Returns:
|
|
484
503
|
SklearnModelSchema: The inferred schema of the Sklearn model.
|
|
485
504
|
"""
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
f"Model does not have the method '{infer_method_name}' to infer the schema."
|
|
489
|
-
)
|
|
490
|
-
model_infer_method = getattr(model, infer_method_name)
|
|
491
|
-
model_output = model_infer_method(model_input)
|
|
492
|
-
|
|
493
|
-
model_signature = infer_signature(
|
|
494
|
-
model_input=model_input, model_output=model_output
|
|
505
|
+
model_signature_json = _infer_schema(
|
|
506
|
+
model_input=model_input, model=model, infer_method_name=infer_method_name
|
|
495
507
|
)
|
|
496
|
-
model_signature_json = model_signature.to_dict()
|
|
497
508
|
return autogen_artifacts.SklearnModelSchema(
|
|
498
509
|
infer_method_name=infer_method_name,
|
|
499
510
|
inputs=json.loads(model_signature_json["inputs"]),
|
|
500
511
|
outputs=json.loads(model_signature_json["outputs"]),
|
|
501
512
|
)
|
|
513
|
+
|
|
514
|
+
|
|
515
|
+
def xgboost_infer_schema(
|
|
516
|
+
model_input: Any,
|
|
517
|
+
model: Union["Booster", "XGBModel"],
|
|
518
|
+
infer_method_name: str = "predict",
|
|
519
|
+
) -> autogen_artifacts.XGBoostModelSchema:
|
|
520
|
+
"""
|
|
521
|
+
Infer the schema of an XGBoost model.
|
|
522
|
+
|
|
523
|
+
Args:
|
|
524
|
+
model_input (Any): The input data to be used for schema inference.
|
|
525
|
+
model (Any): The XGBoost model instance.
|
|
526
|
+
infer_method_name (str): The name of the method to be used for schema inference.
|
|
527
|
+
Eg: predict (default), predict_proba
|
|
528
|
+
Returns:
|
|
529
|
+
XGBoostModelSchema: The inferred schema of the XGBoost model.
|
|
530
|
+
"""
|
|
531
|
+
model_signature_json = _infer_schema(
|
|
532
|
+
model_input=model_input, model=model, infer_method_name=infer_method_name
|
|
533
|
+
)
|
|
534
|
+
return autogen_artifacts.XGBoostModelSchema(
|
|
535
|
+
infer_method_name=infer_method_name,
|
|
536
|
+
inputs=json.loads(model_signature_json["inputs"]),
|
|
537
|
+
outputs=json.loads(model_signature_json["outputs"]),
|
|
538
|
+
)
|
|
@@ -27,7 +27,7 @@ truefoundry/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
27
27
|
truefoundry/cli/__main__.py,sha256=-NkhYlT3mC5MhtekueKAvCw-sWvguj0LJRpXWzvvFjc,727
|
|
28
28
|
truefoundry/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
truefoundry/common/auth_service_client.py,sha256=RRiGUqITxeVYwKZLc923zJP-61UAvFtVlMaG2HJBvXc,7940
|
|
30
|
-
truefoundry/common/constants.py,sha256=
|
|
30
|
+
truefoundry/common/constants.py,sha256=z96d99N_tP3om3EayB_MVOsJRmYVeNm4bBbYIW61Ta0,2556
|
|
31
31
|
truefoundry/common/credential_file_manager.py,sha256=1yEk1Zm2xS4G0VDFwKSZ4w0VUrcPWQ1nJnoBaz9xyKA,4251
|
|
32
32
|
truefoundry/common/credential_provider.py,sha256=Aht7hFLsnyRgMR34dRbzln7dor0WYSeA8ej8ApNmnKM,4148
|
|
33
33
|
truefoundry/common/entities.py,sha256=8O-EGPk4PKqnyoFMKUTxISCU19rz0KBnfRDJU695DhY,3797
|
|
@@ -40,17 +40,17 @@ truefoundry/deploy/auto_gen/models.py,sha256=8848BDbq2hO8Y75LsBH3cS0vi8qEOKU5x6o
|
|
|
40
40
|
truefoundry/deploy/builder/__init__.py,sha256=1qjHMNBE1poRCZW0WrG46dFM1f1IlivD5352qzsioMU,4953
|
|
41
41
|
truefoundry/deploy/builder/builders/__init__.py,sha256=tlFLXqyDaKLd4iZbo4Hcu_8gOmgtL6drnXpbmQ6x1P8,636
|
|
42
42
|
truefoundry/deploy/builder/builders/dockerfile.py,sha256=AXXTziCkaqIhuM_bwyD1vT1znOwemN1TKgU7eyo-KuM,1522
|
|
43
|
-
truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py,sha256=
|
|
43
|
+
truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py,sha256=x_GwRFKz-Kb4-ZlxOFjBlr0mTgUDe_hVeG4dsIbHo8c,1796
|
|
44
44
|
truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py,sha256=rQgdvKmAT9HArVW4TAG5yd2QTKRs3S5LJ9RQbc_EkHE,2518
|
|
45
|
-
truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py,sha256=
|
|
46
|
-
truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py,sha256=
|
|
47
|
-
truefoundry/deploy/builder/constants.py,sha256=
|
|
45
|
+
truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py,sha256=9r1PYahn-HfzpMth6NkvJoycmmHQpSl0vIVZxWF12xI,1864
|
|
46
|
+
truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py,sha256=xa5Y-xf9WzXTJwbp5NnlvJu-g57KeSuy8XNGlW7z9rk,9462
|
|
47
|
+
truefoundry/deploy/builder/constants.py,sha256=amUkHoHvVKzGv0v_knfiioRuKiJM0V0xW0diERgWiI0,508
|
|
48
48
|
truefoundry/deploy/builder/docker_service.py,sha256=OI8efqK0Gnoii8bcHihpA2StwHVzsMREfBk7NvMR4hY,3950
|
|
49
|
-
truefoundry/deploy/builder/utils.py,sha256=
|
|
49
|
+
truefoundry/deploy/builder/utils.py,sha256=D68-bqM0NQx-Elg-56mtkENyVyg9faZ9tgTmBuo1Sjs,1076
|
|
50
50
|
truefoundry/deploy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
51
|
truefoundry/deploy/cli/cli.py,sha256=BuAW-R98oz1wzPDg00fPgrK1UDoCfy0Tu6pWW9Ud1Ns,2887
|
|
52
52
|
truefoundry/deploy/cli/commands/__init__.py,sha256=-i3ltscehEO0hy-Cf6gPLaiobfv8tZetPKaaCyR9B3M,1364
|
|
53
|
-
truefoundry/deploy/cli/commands/apply_command.py,sha256=
|
|
53
|
+
truefoundry/deploy/cli/commands/apply_command.py,sha256=AL8LBv2ZO01efG97_q9kYG2XjukyadUBw5huyhw_k64,2060
|
|
54
54
|
truefoundry/deploy/cli/commands/build_command.py,sha256=DQ7NARgkIgV4z0Zdnl3zMDKU_fSkN5-FkjCQypgmWpo,1255
|
|
55
55
|
truefoundry/deploy/cli/commands/build_logs_command.py,sha256=WrPOlFec_wwuzdJmKZ8mjca-oFVvxgfblcqj2LlhWJA,2804
|
|
56
56
|
truefoundry/deploy/cli/commands/create_command.py,sha256=ZjA4EP1jHYuVE1zx0kN-giBr3y0sEiXnu8xMsNyD2Rg,1850
|
|
@@ -82,11 +82,11 @@ truefoundry/deploy/json_util.py,sha256=x_-7YYQ4_HUIJ8ofOcclAp9JWhgTWjR9Th6Q0FuRq
|
|
|
82
82
|
truefoundry/deploy/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
83
|
truefoundry/deploy/lib/auth/servicefoundry_session.py,sha256=5TCYPunAygtn5mb0mp_VcWKEalKMKPbyWMWer-Vty2g,1916
|
|
84
84
|
truefoundry/deploy/lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
|
-
truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=
|
|
85
|
+
truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=yhHsHo9T0mTbum7X2sOuNVf-am1AdtzyK0KBQkdC0io,26212
|
|
86
86
|
truefoundry/deploy/lib/const.py,sha256=repGJLuoMqtzeq5tCjjkN4bH187FVHVKI30BricOlvc,244
|
|
87
87
|
truefoundry/deploy/lib/dao/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
88
88
|
truefoundry/deploy/lib/dao/application.py,sha256=xUqF34abde71YZo9gkR-WvVJi1C6nBeGBNw4kL5FdIo,9226
|
|
89
|
-
truefoundry/deploy/lib/dao/apply.py,sha256=
|
|
89
|
+
truefoundry/deploy/lib/dao/apply.py,sha256=Jadfa3DcbIRz-Bb4Tlixed179HLDnmDXOlrvSxfEgQ0,3021
|
|
90
90
|
truefoundry/deploy/lib/dao/version.py,sha256=AtdW_4O1DPUKdfv2qy6iUJsZ_95vM6z0AqeEy3WDKs8,1130
|
|
91
91
|
truefoundry/deploy/lib/dao/workspace.py,sha256=jm8UWytwVajVcrYyHSTCwWYDYl-RHuk0zAf9Caj4GzQ,2356
|
|
92
92
|
truefoundry/deploy/lib/exceptions.py,sha256=ZT2o3mar3BYtnjKHn2wf4rBGEaFIgf-zkQmzDicg6Ps,236
|
|
@@ -107,7 +107,7 @@ truefoundry/deploy/v2/lib/models.py,sha256=pSolLMTArDuYpeNsmeeS5DWliloN_iCDfZSpR
|
|
|
107
107
|
truefoundry/deploy/v2/lib/patched_models.py,sha256=NTU8J_CwdvEuF9zNXwFyN3suOp_196Wrm75DDy5qcXE,14184
|
|
108
108
|
truefoundry/deploy/v2/lib/source.py,sha256=VHKuFREiixUP40D3Mrz-TA70spu1M0RbCzl--qwlFaY,9263
|
|
109
109
|
truefoundry/logger.py,sha256=u-YCNjg5HBwE70uQcpjIG64Ghos-K2ulTWaxC03BSj4,714
|
|
110
|
-
truefoundry/ml/__init__.py,sha256=
|
|
110
|
+
truefoundry/ml/__init__.py,sha256=ssUEIs8BixPWxynKoeSh-dkRl6AtLXG0PBGYnUR5Az8,2217
|
|
111
111
|
truefoundry/ml/artifact/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
112
112
|
truefoundry/ml/artifact/truefoundry_artifact_repo.py,sha256=FksxhUpRHb9pgWZmAB16DhXqkAL6UIAPA1S3RJUApQU,46201
|
|
113
113
|
truefoundry/ml/autogen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -319,7 +319,7 @@ truefoundry/ml/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
|
319
319
|
truefoundry/ml/cli/cli.py,sha256=ckBcjUpqfhgrPE1okqT_G2iouOLt-0KjpLhHp2YdVFU,256
|
|
320
320
|
truefoundry/ml/cli/commands/__init__.py,sha256=diDUiRUX4l6TtNLI4iF-ZblczkELM7FRViJ-8gGNJQY,82
|
|
321
321
|
truefoundry/ml/cli/commands/download.py,sha256=cbz9KijiLKXj4-twlig3xZLTVRNm4fnjwpy0leZr31w,2342
|
|
322
|
-
truefoundry/ml/cli/commands/model_init.py,sha256=
|
|
322
|
+
truefoundry/ml/cli/commands/model_init.py,sha256=cKqXPWS_uxHnWJE-Nye6VE2Ct_-tgmfyTRVqXyCnROA,2684
|
|
323
323
|
truefoundry/ml/cli/utils.py,sha256=j6_mZ4Spn114mz3P4QQ8jx0tmorXIuyQnHXVUSDvZi4,1035
|
|
324
324
|
truefoundry/ml/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
325
325
|
truefoundry/ml/clients/entities.py,sha256=sNP4DnAVdQoMfc06s0r3VTzKHTo7jmxAOuTlQOVmsMs,151
|
|
@@ -347,9 +347,9 @@ truefoundry/ml/log_types/plot.py,sha256=HuYvvRA5r8V0xAIuuqMME2IHb9d3SfGHUiuEkOP3
|
|
|
347
347
|
truefoundry/ml/log_types/pydantic_base.py,sha256=eBlw_AEyAz4iJKDP4zgJOCFWcldwQqpf7FADW1jzIQY,272
|
|
348
348
|
truefoundry/ml/log_types/utils.py,sha256=xjJ21jdPScvFmw3TbVh5NCzbzJwaqiXJyiiT4xxX1EI,335
|
|
349
349
|
truefoundry/ml/logger.py,sha256=VT-BF3BnBYTWVq87O58F0c8uXMu94gYzsiFlGY3_7Ao,458
|
|
350
|
-
truefoundry/ml/mlfoundry_api.py,sha256=
|
|
351
|
-
truefoundry/ml/mlfoundry_run.py,sha256=
|
|
352
|
-
truefoundry/ml/model_framework.py,sha256=
|
|
350
|
+
truefoundry/ml/mlfoundry_api.py,sha256=avuGgjbRnQXk774Vd3Dx7n99bbrOkL9pob9DI-vNvWE,62368
|
|
351
|
+
truefoundry/ml/mlfoundry_run.py,sha256=scHjdjojRGbE4ZZlByZTFrcBTamRbm_JGidaSX9Gi9o,44601
|
|
352
|
+
truefoundry/ml/model_framework.py,sha256=xHmVFI3Xcd6z6BH9TyZY8kFiw3Fp70423_M_wTYxd5Y,18841
|
|
353
353
|
truefoundry/ml/run_utils.py,sha256=0W208wSLUrbdfk2pjNcZlkUi9bNxG2JORqoe-5rVqHI,2423
|
|
354
354
|
truefoundry/ml/session.py,sha256=F83GTC5WwGBjnJ69Ct8MqMnlutYc56JCc6YhEY1Wl-A,5394
|
|
355
355
|
truefoundry/ml/validation_utils.py,sha256=J5atNhcJLvKj64ralSV9Y5Fv1Rt4SE237ICdP9-7sP4,12149
|
|
@@ -370,7 +370,7 @@ truefoundry/workflow/remote_filesystem/tfy_signed_url_client.py,sha256=5mBCIc-ON
|
|
|
370
370
|
truefoundry/workflow/remote_filesystem/tfy_signed_url_fs.py,sha256=Hf6Dk6Fu6P7DqsK5ULgraf9DStjgigf-kjaRAMBW-RU,8680
|
|
371
371
|
truefoundry/workflow/task.py,sha256=ToitYiKcNzFCtOVQwz1W8sRjbR97eVS7vQBdbgUQtKg,1779
|
|
372
372
|
truefoundry/workflow/workflow.py,sha256=WaTqUjhwfAXDWu4E5ehuwAxrCbDJkoAf1oWmR2E9Qy0,4575
|
|
373
|
-
truefoundry-0.5.
|
|
374
|
-
truefoundry-0.5.
|
|
375
|
-
truefoundry-0.5.
|
|
376
|
-
truefoundry-0.5.
|
|
373
|
+
truefoundry-0.5.2rc1.dist-info/METADATA,sha256=U7jLn1N-Cv3EkpRaIK3ud2UVOc4VjBBQK6VXX0EL16Y,2887
|
|
374
|
+
truefoundry-0.5.2rc1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
375
|
+
truefoundry-0.5.2rc1.dist-info/entry_points.txt,sha256=TXvUxQkI6zmqJuycPsyxEIMr3oqfDjgrWj0m_9X12x4,95
|
|
376
|
+
truefoundry-0.5.2rc1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|