truefoundry 0.4.1rc1__py3-none-any.whl → 0.4.2__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 (31) hide show
  1. truefoundry/common/auth_service_client.py +14 -5
  2. truefoundry/common/constants.py +2 -1
  3. truefoundry/common/credential_file_manager.py +4 -3
  4. truefoundry/common/credential_provider.py +4 -5
  5. truefoundry/common/servicefoundry_client.py +14 -7
  6. truefoundry/common/utils.py +59 -10
  7. truefoundry/deploy/auto_gen/models.py +11 -5
  8. truefoundry/deploy/builder/__init__.py +2 -2
  9. truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +7 -1
  10. truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py +25 -12
  11. truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +8 -2
  12. truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +26 -3
  13. truefoundry/deploy/builder/constants.py +7 -0
  14. truefoundry/deploy/builder/utils.py +32 -0
  15. truefoundry/deploy/cli/commands/deploy_command.py +46 -2
  16. truefoundry/deploy/cli/util.py +4 -1
  17. truefoundry/deploy/lib/auth/servicefoundry_session.py +4 -2
  18. truefoundry/deploy/lib/clients/servicefoundry_client.py +3 -1
  19. truefoundry/deploy/lib/session.py +6 -6
  20. truefoundry/deploy/v2/lib/patched_models.py +4 -0
  21. truefoundry/ml/clients/servicefoundry_client.py +2 -4
  22. truefoundry/ml/mlfoundry_api.py +4 -2
  23. truefoundry/ml/mlfoundry_run.py +33 -16
  24. truefoundry/ml/run_utils.py +0 -14
  25. truefoundry/ml/session.py +9 -8
  26. truefoundry/workflow/example/hello_world_package/workflow.py +2 -2
  27. truefoundry/workflow/example/package/test_workflow.py +14 -15
  28. {truefoundry-0.4.1rc1.dist-info → truefoundry-0.4.2.dist-info}/METADATA +1 -2
  29. {truefoundry-0.4.1rc1.dist-info → truefoundry-0.4.2.dist-info}/RECORD +31 -29
  30. {truefoundry-0.4.1rc1.dist-info → truefoundry-0.4.2.dist-info}/WHEEL +0 -0
  31. {truefoundry-0.4.1rc1.dist-info → truefoundry-0.4.2.dist-info}/entry_points.txt +0 -0
@@ -8,7 +8,7 @@ from truefoundry.common.constants import VERSION_PREFIX
8
8
  from truefoundry.common.entities import DeviceCode, Token
9
9
  from truefoundry.common.exceptions import BadRequestException
10
10
  from truefoundry.common.request_utils import request_handling, requests_retry_session
11
- from truefoundry.common.utils import poll_for_function
11
+ from truefoundry.common.utils import poll_for_function, relogin_error_message
12
12
  from truefoundry.logger import logger
13
13
 
14
14
 
@@ -16,6 +16,7 @@ class AuthServiceClient(ABC):
16
16
  def __init__(self, tenant_name: str):
17
17
  self._tenant_name = tenant_name
18
18
 
19
+ # TODO (chiragjn): Rename base_url to tfy_host
19
20
  @classmethod
20
21
  def from_base_url(cls, base_url: str) -> "AuthServiceClient":
21
22
  from truefoundry.common.servicefoundry_client import (
@@ -54,7 +55,9 @@ class ServiceFoundryServerAuthServiceClient(AuthServiceClient):
54
55
  if not token.refresh_token:
55
56
  # TODO: Add a way to propagate error messages without traceback to the output interface side
56
57
  raise Exception(
57
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
58
+ relogin_error_message(
59
+ "Unable to resume login session.", host=host_arg_str
60
+ )
58
61
  )
59
62
  url = f"{self._api_server_url}/{VERSION_PREFIX}/oauth2/token"
60
63
  data = {
@@ -70,7 +73,9 @@ class ServiceFoundryServerAuthServiceClient(AuthServiceClient):
70
73
  return Token.parse_obj(response_data)
71
74
  except BadRequestException as ex:
72
75
  raise Exception(
73
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
76
+ relogin_error_message(
77
+ "Unable to resume login session.", host=host_arg_str
78
+ )
74
79
  ) from ex
75
80
 
76
81
  def get_device_code(self) -> DeviceCode:
@@ -127,7 +132,9 @@ class AuthServerServiceClient(AuthServiceClient):
127
132
  if not token.refresh_token:
128
133
  # TODO: Add a way to propagate error messages without traceback to the output interface side
129
134
  raise Exception(
130
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
135
+ relogin_error_message(
136
+ "Unable to resume login session.", host=host_arg_str
137
+ )
131
138
  )
132
139
  url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/token/refresh"
133
140
  data = {
@@ -141,7 +148,9 @@ class AuthServerServiceClient(AuthServiceClient):
141
148
  return Token.parse_obj(response_data)
142
149
  except BadRequestException as ex:
143
150
  raise Exception(
144
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
151
+ relogin_error_message(
152
+ "Unable to resume login session.", host=host_arg_str
153
+ )
145
154
  ) from ex
146
155
 
147
156
  def get_device_code(self) -> DeviceCode:
@@ -5,8 +5,9 @@ CREDENTIAL_FILEPATH = TFY_CONFIG_DIR / "credentials.json"
5
5
 
6
6
  TFY_HOST_ENV_KEY = "TFY_HOST"
7
7
  TFY_API_KEY_ENV_KEY = "TFY_API_KEY"
8
- SERVICEFOUNDRY_SERVER_URL_ENV_KEY = "SERVICEFOUNDRY_SERVER_URL"
8
+ TFY_CLI_LOCAL_DEV_MODE_ENV_KEY = "TFY_CLI_LOCAL_DEV_MODE"
9
9
 
10
10
  API_SERVER_RELATIVE_PATH = "api/svc"
11
+ MLFOUNDRY_SERVER_RELATIVE_PATH = "api/ml"
11
12
  VERSION_PREFIX = "v1"
12
13
  SERVICEFOUNDRY_CLIENT_MAX_RETRIES = 2
@@ -10,6 +10,7 @@ from filelock import FileLock, Timeout
10
10
 
11
11
  from truefoundry.common.constants import CREDENTIAL_FILEPATH
12
12
  from truefoundry.common.entities import CredentialsFileContent
13
+ from truefoundry.common.utils import relogin_error_message
13
14
  from truefoundry.logger import logger
14
15
 
15
16
 
@@ -87,9 +88,9 @@ class CredentialsFileManager:
87
88
  return CredentialsFileContent.parse_file(self._credentials_file_path)
88
89
  except Exception as ex:
89
90
  raise Exception(
90
- "Error while reading the credentials file "
91
- f"{self._credentials_file_path}. Please login again "
92
- "using `tfy login --relogin` or `tfy.login(relogin=True)` function"
91
+ relogin_error_message(
92
+ f"Error while reading the credentials file {self._credentials_file_path}"
93
+ )
93
94
  ) from ex
94
95
 
95
96
  @_ensure_lock_taken
@@ -6,7 +6,7 @@ from truefoundry.common.auth_service_client import AuthServiceClient
6
6
  from truefoundry.common.constants import TFY_API_KEY_ENV_KEY
7
7
  from truefoundry.common.credential_file_manager import CredentialsFileManager
8
8
  from truefoundry.common.entities import CredentialsFileContent, Token
9
- from truefoundry.common.utils import resolve_base_url
9
+ from truefoundry.common.utils import resolve_tfy_host
10
10
  from truefoundry.logger import logger
11
11
 
12
12
  TOKEN_REFRESH_LOCK = threading.RLock()
@@ -17,6 +17,7 @@ class CredentialProvider(ABC):
17
17
  @abstractmethod
18
18
  def token(self) -> Token: ...
19
19
 
20
+ # TODO (chiragjn): Rename base_url to tfy_host
20
21
  @property
21
22
  @abstractmethod
22
23
  def base_url(self) -> str: ...
@@ -34,10 +35,8 @@ class EnvCredentialProvider(CredentialProvider):
34
35
  raise Exception(
35
36
  f"Value of {TFY_API_KEY_ENV_KEY} env var should be non-empty string"
36
37
  )
37
- # TODO: Read host from cred file as well.
38
- base_url = resolve_base_url().strip("/")
39
- self._host = base_url
40
- self._auth_service = AuthServiceClient.from_base_url(base_url=base_url)
38
+ self._host = resolve_tfy_host()
39
+ self._auth_service = AuthServiceClient.from_base_url(base_url=self._host)
41
40
  self._token: Token = Token(access_token=api_key, refresh_token=None) # type: ignore[call-arg]
42
41
 
43
42
  @staticmethod
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import functools
4
- from urllib.parse import urlparse
5
4
 
6
5
  import requests
7
6
  from packaging import version
@@ -16,7 +15,7 @@ from truefoundry.common.entities import (
16
15
  )
17
16
  from truefoundry.common.request_utils import request_handling, requests_retry_session
18
17
  from truefoundry.common.utils import (
19
- append_servicefoundry_path_to_base_url,
18
+ get_tfy_servers_config,
20
19
  timed_lru_cache,
21
20
  )
22
21
  from truefoundry.logger import logger
@@ -49,10 +48,10 @@ def session_with_retries() -> requests.Session:
49
48
 
50
49
 
51
50
  @timed_lru_cache(seconds=30 * 60)
52
- def _cached_get_tenant_info(api_server_url: str) -> TenantInfo:
51
+ def _cached_get_tenant_info(api_server_url: str, tenant_host: str) -> TenantInfo:
53
52
  res = session_with_retries().get(
54
53
  url=f"{api_server_url}/{VERSION_PREFIX}/tenant-id",
55
- params={"hostName": urlparse(api_server_url).netloc},
54
+ params={"hostName": tenant_host},
56
55
  )
57
56
  res = request_handling(res)
58
57
  return TenantInfo.parse_obj(res)
@@ -68,9 +67,12 @@ def _cached_get_python_sdk_config(api_server_url: str) -> PythonSDKConfig:
68
67
 
69
68
 
70
69
  class ServiceFoundryServiceClient:
70
+ # TODO (chiragjn): Rename base_url to tfy_host
71
71
  def __init__(self, base_url: str):
72
72
  self._base_url = base_url.strip("/")
73
- self._api_server_url = append_servicefoundry_path_to_base_url(self._base_url)
73
+ tfy_servers_config = get_tfy_servers_config(self._base_url)
74
+ self._tenant_host = tfy_servers_config.tenant_host
75
+ self._api_server_url = tfy_servers_config.servicefoundry_server_url
74
76
 
75
77
  @property
76
78
  def base_url(self) -> str:
@@ -78,11 +80,16 @@ class ServiceFoundryServiceClient:
78
80
 
79
81
  @property
80
82
  def tenant_info(self) -> TenantInfo:
81
- return _cached_get_tenant_info(self._api_server_url)
83
+ return _cached_get_tenant_info(
84
+ self._api_server_url,
85
+ tenant_host=self._tenant_host,
86
+ )
82
87
 
83
88
  @property
84
89
  def python_sdk_config(self) -> PythonSDKConfig:
85
- return _cached_get_python_sdk_config(self._api_server_url)
90
+ return _cached_get_python_sdk_config(
91
+ self._api_server_url,
92
+ )
86
93
 
87
94
  @functools.cached_property
88
95
  def _min_cli_version_required(self) -> str:
@@ -3,17 +3,62 @@ import time
3
3
  from functools import lru_cache, wraps
4
4
  from time import monotonic_ns
5
5
  from typing import Callable, Generator, Optional, TypeVar
6
- from urllib.parse import urljoin, urlsplit
6
+ from urllib.parse import urljoin, urlparse
7
7
 
8
8
  from truefoundry.common.constants import (
9
9
  API_SERVER_RELATIVE_PATH,
10
- SERVICEFOUNDRY_SERVER_URL_ENV_KEY,
10
+ MLFOUNDRY_SERVER_RELATIVE_PATH,
11
+ TFY_CLI_LOCAL_DEV_MODE_ENV_KEY,
11
12
  TFY_HOST_ENV_KEY,
12
13
  )
14
+ from truefoundry.pydantic_v1 import BaseSettings
13
15
 
14
16
  T = TypeVar("T")
15
17
 
16
18
 
19
+ class _TFYServersConfig(BaseSettings):
20
+ class Config:
21
+ env_prefix = "TFY_CLI_LOCAL_"
22
+ env_file = ".tfy-cli-local.env"
23
+
24
+ tenant_host: str
25
+ servicefoundry_server_url: str
26
+ mlfoundry_server_url: str
27
+
28
+ @classmethod
29
+ def from_base_url(cls, base_url: str) -> "_TFYServersConfig":
30
+ base_url = base_url.strip("/")
31
+ return cls(
32
+ tenant_host=urlparse(base_url).netloc,
33
+ servicefoundry_server_url=urljoin(base_url, API_SERVER_RELATIVE_PATH),
34
+ mlfoundry_server_url=urljoin(base_url, MLFOUNDRY_SERVER_RELATIVE_PATH),
35
+ )
36
+
37
+
38
+ _tfy_servers_config = None
39
+
40
+
41
+ def get_tfy_servers_config(base_url: str) -> _TFYServersConfig:
42
+ global _tfy_servers_config
43
+ if _tfy_servers_config is None:
44
+ if os.getenv(TFY_CLI_LOCAL_DEV_MODE_ENV_KEY):
45
+ _tfy_servers_config = _TFYServersConfig() # type: ignore[call-arg]
46
+ else:
47
+ _tfy_servers_config = _TFYServersConfig.from_base_url(base_url)
48
+ return _tfy_servers_config
49
+
50
+
51
+ def relogin_error_message(message: str, host: str = "HOST") -> str:
52
+ suffix = ""
53
+ if host == "HOST":
54
+ suffix = " where HOST is TrueFoundry platform URL"
55
+ return (
56
+ f"{message}\n"
57
+ f"Please login again using `tfy login --host {host} --relogin` "
58
+ f"or `truefoundry.login(host={host!r}, relogin=True)` function" + suffix
59
+ )
60
+
61
+
17
62
  def timed_lru_cache(
18
63
  seconds: int = 300, maxsize: Optional[int] = None
19
64
  ) -> Callable[[Callable[..., T]], Callable[..., T]]:
@@ -42,15 +87,19 @@ def poll_for_function(
42
87
  time.sleep(poll_after_secs)
43
88
 
44
89
 
45
- def resolve_base_url(host: Optional[str] = None) -> str:
46
- if not host and not os.getenv(TFY_HOST_ENV_KEY):
90
+ def validate_tfy_host(tfy_host: str) -> None:
91
+ if not (tfy_host.startswith("https://") or tfy_host.startswith("http://")):
47
92
  raise ValueError(
48
- f"Either `host` should be provided by --host <value>, or `{TFY_HOST_ENV_KEY}` env must be set"
93
+ f"Invalid host {tfy_host!r}. It should start with https:// or http://"
49
94
  )
50
- return host or os.getenv(TFY_HOST_ENV_KEY)
51
95
 
52
96
 
53
- def append_servicefoundry_path_to_base_url(base_url: str):
54
- if urlsplit(base_url).netloc.startswith("localhost"):
55
- return os.getenv(SERVICEFOUNDRY_SERVER_URL_ENV_KEY)
56
- return urljoin(base_url, API_SERVER_RELATIVE_PATH)
97
+ def resolve_tfy_host(tfy_host: Optional[str] = None) -> str:
98
+ if not tfy_host and not os.getenv(TFY_HOST_ENV_KEY):
99
+ raise ValueError(
100
+ f"Either `host` should be provided using `--host <value>`, or `{TFY_HOST_ENV_KEY}` env must be set"
101
+ )
102
+ tfy_host = tfy_host or os.getenv(TFY_HOST_ENV_KEY)
103
+ tfy_host = tfy_host.strip("/")
104
+ validate_tfy_host(tfy_host)
105
+ return tfy_host
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: application.json
3
- # timestamp: 2024-09-29T10:28:22+00:00
3
+ # timestamp: 2024-10-09T13:00:44+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -363,10 +363,12 @@ class HelmRepo(BaseModel):
363
363
  description="+label=Helm repository URL\n+sort=1\n+message=Needs to be a valid URL.",
364
364
  )
365
365
  chart: str = Field(
366
- ..., description="+label=Chart name\n+sort=2\n+usage=The helm chart name"
366
+ ...,
367
+ description='+label=Chart name\n+sort=2\n+usage=The helm chart name\n+uiType=InputSelect\n+uiProps={"creatable":true, "searchable":true}',
367
368
  )
368
369
  version: str = Field(
369
- ..., description="+label=Version\n+sort=3\n+usage=Helm chart version"
370
+ ...,
371
+ description='+label=Version\n+sort=3\n+usage=Helm chart version\n+uiType=InputSelect\n+uiProps={"creatable":true, "searchable":true}',
370
372
  )
371
373
 
372
374
 
@@ -448,10 +450,14 @@ class JobAlert(BaseModel):
448
450
  +label=Alert
449
451
  """
450
452
 
451
- notification_channel: str = Field(
453
+ notification_channel: constr(min_length=1) = Field(
452
454
  ...,
453
455
  description='+label=Notification Channel\n+usage=Specify the notification channel to send alerts to\n+uiType=IntegrationSelect\n+uiProps={"integrationType":"notification-channel"}\n+sort=660',
454
456
  )
457
+ to_emails: Optional[List[constr(min_length=1)]] = Field(
458
+ None,
459
+ description="+label=To Emails\n+usage=List of recipients' email addresses if the notification channel is Email.\n+docs=Specify the emails to send alerts to\n+sort=665",
460
+ )
455
461
  on_start: bool = Field(
456
462
  False,
457
463
  description="+label=On Start\n+usage=Send an alert when the job starts\n+sort=670",
@@ -1546,7 +1552,7 @@ class SSHServer(BaseWorkbenchInput):
1546
1552
  image: WorkbenchImage
1547
1553
  ssh_public_key: str = Field(
1548
1554
  ...,
1549
- description="+label: SSH Public Key\n+usage=Add Your SSH Public Key, this will be used to authenticate you to the SSH Server. You can find it using `cat ~/.ssh/id_rsa.pub`\n+sort=4",
1555
+ description="+label: SSH Public Key\n+usage=Add Your SSH Public Key, this will be used to authenticate you to the SSH Server. \\\nYou can find it using `cat ~/.ssh/id_rsa.pub` in Mac/Linux or `type $home\\.ssh\\id_rsa.pub` in Windows Powershell. \\\nYou can also generate a new SSH key pair using `ssh-keygen -t rsa` in your local terminal. (same for both Mac/Linux and Windows Powershell)\n+uiType=TextArea\n+sort=4",
1550
1556
  )
1551
1557
 
1552
1558
 
@@ -1,4 +1,4 @@
1
- from typing import Dict, List, Optional, Union
1
+ from typing import Any, Dict, List, Optional, Union
2
2
 
3
3
  from truefoundry.deploy.auto_gen.models import (
4
4
  DockerFileBuild,
@@ -24,7 +24,7 @@ class _BuildConfig(BaseModel):
24
24
 
25
25
 
26
26
  def build(
27
- build_configuration: Union[BaseModel, Dict],
27
+ build_configuration: Union[BaseModel, Dict[str, Any]],
28
28
  tag: str,
29
29
  extra_opts: Optional[List[str]] = None,
30
30
  ):
@@ -8,16 +8,20 @@ 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 has_pip_conf_secret
11
12
 
12
13
  __all__ = ["generate_dockerfile_content", "build"]
13
14
 
14
15
 
15
16
  def _convert_to_dockerfile_build_config(
16
- build_configuration: NotebookImageBuild, local_dir: str
17
+ build_configuration: NotebookImageBuild,
18
+ local_dir: str,
19
+ mount_pip_conf_secret: bool = False,
17
20
  ) -> DockerFileBuild:
18
21
  dockerfile_content = generate_dockerfile_content(
19
22
  build_configuration=build_configuration,
20
23
  local_dir=local_dir,
24
+ mount_pip_conf_secret=mount_pip_conf_secret,
21
25
  )
22
26
  dockerfile_path = os.path.join(local_dir, "Dockerfile")
23
27
  with open(dockerfile_path, "w", encoding="utf8") as fp:
@@ -34,10 +38,12 @@ def build(
34
38
  build_configuration: NotebookImageBuild,
35
39
  extra_opts: Optional[List[str]] = None,
36
40
  ):
41
+ mount_pip_conf_secret = has_pip_conf_secret(extra_opts) if extra_opts else False
37
42
  with TemporaryDirectory() as local_dir:
38
43
  docker_build_configuration = _convert_to_dockerfile_build_config(
39
44
  build_configuration,
40
45
  local_dir=local_dir,
46
+ mount_pip_conf_secret=mount_pip_conf_secret,
41
47
  )
42
48
  dockerfile.build(
43
49
  tag=tag,
@@ -4,6 +4,10 @@ from typing import Literal, Optional
4
4
 
5
5
  from mako.template import Template
6
6
 
7
+ from truefoundry.deploy.builder.constants import (
8
+ PIP_CONF_BUILDKIT_SECRET_MOUNT,
9
+ PIP_CONF_SECRET_MOUNT_AS_ENV,
10
+ )
7
11
  from truefoundry.pydantic_v1 import BaseModel
8
12
 
9
13
 
@@ -28,31 +32,40 @@ USER $NB_UID
28
32
 
29
33
 
30
34
  def generate_build_script_docker_commands(
31
- build_script: Optional[str], local_dir: str
35
+ build_script: Optional[str],
36
+ local_dir: str,
37
+ mount_pip_conf_secret: bool = False,
32
38
  ) -> Optional[str]:
33
39
  if not build_script:
34
40
  return None
35
- build_script_path = None
36
- if build_script:
37
- # we add build script's hash to the file name to ensure docker cache invalidation
38
- script_hash = hashlib.sha256(build_script.encode("utf-8")).hexdigest()
39
- build_script_path = os.path.join(local_dir, f"build-script-{script_hash}.sh")
40
- with open(build_script_path, "w") as fp:
41
- fp.write(build_script)
42
- build_script_path = os.path.relpath(build_script_path, local_dir)
41
+ # we add build script's hash to the file name to ensure docker cache invalidation
42
+ script_hash = hashlib.sha256(build_script.encode("utf-8")).hexdigest()
43
+ build_script_path = os.path.join(local_dir, f"build-script-{script_hash}.sh")
44
+ with open(build_script_path, "w") as fp:
45
+ fp.write(build_script)
46
+ build_script_path = os.path.relpath(build_script_path, local_dir)
47
+ buildkit_secret_mounts = ""
48
+ environment_variables = ["DEBIAN_FRONTEND=noninteractive"]
49
+ if mount_pip_conf_secret:
50
+ buildkit_secret_mounts = PIP_CONF_BUILDKIT_SECRET_MOUNT
51
+ environment_variables.append(PIP_CONF_SECRET_MOUNT_AS_ENV)
52
+ all_environment_variables = " ".join(environment_variables)
43
53
  run_build_script_command = f"""\
44
54
  COPY {build_script_path} /tmp/user-build-script.sh
45
- RUN mkdir -p /var/log/ && DEBIAN_FRONTEND=noninteractive bash -ex /tmp/user-build-script.sh 2>&1 | tee /var/log/user-build-script-output.log
55
+ RUN {buildkit_secret_mounts} mkdir -p /var/log/ && {all_environment_variables} bash -ex /tmp/user-build-script.sh 2>&1 | tee /var/log/user-build-script-output.log
46
56
  """
47
57
  return run_build_script_command
48
58
 
49
59
 
50
60
  def generate_dockerfile_content(
51
- build_configuration: NotebookImageBuild, local_dir: str
61
+ build_configuration: NotebookImageBuild,
62
+ local_dir: str,
63
+ mount_pip_conf_secret: bool = False,
52
64
  ) -> str:
53
65
  build_script_docker_commands = generate_build_script_docker_commands(
54
66
  build_script=build_configuration.build_script,
55
67
  local_dir=local_dir,
68
+ mount_pip_conf_secret=mount_pip_conf_secret,
56
69
  )
57
70
 
58
71
  template_args = {
@@ -62,5 +75,5 @@ def generate_dockerfile_content(
62
75
 
63
76
  template = DOCKERFILE_TEMPLATE
64
77
 
65
- dockerfile_content = template.render(**template_args)
78
+ dockerfile_content: str = template.render(**template_args)
66
79
  return dockerfile_content
@@ -7,6 +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 has_pip_conf_secret
10
11
 
11
12
  __all__ = ["generate_dockerfile_content", "build"]
12
13
 
@@ -14,9 +15,11 @@ __all__ = ["generate_dockerfile_content", "build"]
14
15
  def _convert_to_dockerfile_build_config(
15
16
  build_configuration: PythonBuild,
16
17
  dockerfile_path: str,
18
+ mount_pip_conf_secret: bool = False,
17
19
  ) -> DockerFileBuild:
18
20
  dockerfile_content = generate_dockerfile_content(
19
- build_configuration=build_configuration
21
+ build_configuration=build_configuration,
22
+ mount_pip_conf_secret=mount_pip_conf_secret,
20
23
  )
21
24
  with open(dockerfile_path, "w", encoding="utf8") as fp:
22
25
  fp.write(dockerfile_content)
@@ -33,9 +36,12 @@ def build(
33
36
  build_configuration: PythonBuild,
34
37
  extra_opts: Optional[List[str]] = None,
35
38
  ):
39
+ mount_pip_conf_secret = has_pip_conf_secret(extra_opts) if extra_opts else False
36
40
  with TemporaryDirectory() as local_dir:
37
41
  docker_build_configuration = _convert_to_dockerfile_build_config(
38
- build_configuration, dockerfile_path=os.path.join(local_dir, "Dockerfile")
42
+ build_configuration,
43
+ dockerfile_path=os.path.join(local_dir, "Dockerfile"),
44
+ mount_pip_conf_secret=mount_pip_conf_secret,
39
45
  )
40
46
  dockerfile.build(
41
47
  tag=tag,
@@ -4,6 +4,10 @@ from typing import Dict, List, Optional
4
4
  from mako.template import Template
5
5
 
6
6
  from truefoundry.deploy.auto_gen.models import PythonBuild
7
+ from truefoundry.deploy.builder.constants import (
8
+ PIP_CONF_BUILDKIT_SECRET_MOUNT,
9
+ PIP_CONF_SECRET_MOUNT_AS_ENV,
10
+ )
7
11
  from truefoundry.deploy.v2.lib.patched_models import CUDAVersion
8
12
 
9
13
  # TODO (chiragjn): Switch to a non-root user inside the container
@@ -18,7 +22,7 @@ COPY ${requirements_path} ${requirements_destination_path}
18
22
  % endif
19
23
 
20
24
  % if pip_install_command is not None:
21
- RUN ${pip_install_command}
25
+ RUN ${pip_config_secret_mount} ${pip_install_command}
22
26
  % endif
23
27
 
24
28
  COPY . /app
@@ -27,7 +31,7 @@ WORKDIR /app
27
31
 
28
32
  DOCKERFILE_TEMPLATE = Template(
29
33
  """
30
- FROM --platform=linux/amd64 python:${python_version}
34
+ FROM --platform=linux/amd64 public.ecr.aws/docker/library/python:${python_version}
31
35
  ENV PATH=/virtualenvs/venv/bin:$PATH
32
36
  RUN apt update && \
33
37
  DEBIAN_FRONTEND=noninteractive apt install -y --no-install-recommends git && \
@@ -65,6 +69,11 @@ CUDA_VERSION_TO_IMAGE_TAG: Dict[str, str] = {
65
69
  CUDAVersion.CUDA_12_0_CUDNN8.value: "12.0.1-cudnn8-runtime-ubuntu22.04",
66
70
  CUDAVersion.CUDA_12_1_CUDNN8.value: "12.1.1-cudnn8-runtime-ubuntu22.04",
67
71
  CUDAVersion.CUDA_12_2_CUDNN8.value: "12.2.2-cudnn8-runtime-ubuntu22.04",
72
+ CUDAVersion.CUDA_12_3_CUDNN9.value: "12.3.2-cudnn9-runtime-ubuntu22.04",
73
+ # From 12.4+ onwards, the image tags drop the cudnn version
74
+ CUDAVersion.CUDA_12_4_CUDNN9.value: "12.4.1-cudnn-runtime-ubuntu22.04",
75
+ CUDAVersion.CUDA_12_5_CUDNN9.value: "12.5.1-cudnn-runtime-ubuntu22.04",
76
+ CUDAVersion.CUDA_12_6_CUDNN9.value: "12.6.1-cudnn-runtime-ubuntu22.04",
68
77
  }
69
78
 
70
79
 
@@ -100,7 +109,9 @@ def generate_apt_install_command(apt_packages: Optional[List[str]]) -> Optional[
100
109
 
101
110
 
102
111
  def generate_pip_install_command(
103
- requirements_path: Optional[str], pip_packages: Optional[List[str]]
112
+ requirements_path: Optional[str],
113
+ pip_packages: Optional[List[str]],
114
+ mount_pip_conf_secret: bool = False,
104
115
  ) -> Optional[str]:
105
116
  upgrade_pip_command = "python -m pip install -U pip setuptools wheel"
106
117
  final_pip_install_command = None
@@ -119,11 +130,17 @@ def generate_pip_install_command(
119
130
  if not final_pip_install_command:
120
131
  return None
121
132
 
133
+ if mount_pip_conf_secret:
134
+ final_pip_install_command = (
135
+ f"{PIP_CONF_SECRET_MOUNT_AS_ENV} {final_pip_install_command}"
136
+ )
137
+
122
138
  return " && ".join([upgrade_pip_command, final_pip_install_command])
123
139
 
124
140
 
125
141
  def generate_dockerfile_content(
126
142
  build_configuration: PythonBuild,
143
+ mount_pip_conf_secret: bool = False,
127
144
  ) -> str:
128
145
  # TODO (chiragjn): Handle recursive references to other requirements files e.g. `-r requirements-gpu.txt`
129
146
  requirements_path = resolve_requirements_txt_path(build_configuration)
@@ -133,6 +150,7 @@ def generate_dockerfile_content(
133
150
  pip_install_command = generate_pip_install_command(
134
151
  requirements_path=requirements_destination_path,
135
152
  pip_packages=build_configuration.pip_packages,
153
+ mount_pip_conf_secret=mount_pip_conf_secret,
136
154
  )
137
155
  apt_install_command = generate_apt_install_command(
138
156
  apt_packages=build_configuration.apt_packages
@@ -146,6 +164,11 @@ def generate_dockerfile_content(
146
164
  "pip_install_command": pip_install_command,
147
165
  }
148
166
 
167
+ if mount_pip_conf_secret:
168
+ template_args["pip_config_secret_mount"] = PIP_CONF_BUILDKIT_SECRET_MOUNT
169
+ else:
170
+ template_args["pip_config_secret_mount"] = ""
171
+
149
172
  if build_configuration.cuda_version:
150
173
  template = CUDA_DOCKERFILE_TEMPLATE
151
174
  template_args["nvidia_cuda_image_tag"] = CUDA_VERSION_TO_IMAGE_TAG.get(
@@ -0,0 +1,7 @@
1
+ BUILDKIT_SECRET_MOUNT_PIP_CONF_ID = "pip.conf"
2
+ PIP_CONF_BUILDKIT_SECRET_MOUNT = (
3
+ f"--mount=type=secret,id={BUILDKIT_SECRET_MOUNT_PIP_CONF_ID}"
4
+ )
5
+ PIP_CONF_SECRET_MOUNT_AS_ENV = (
6
+ f"PIP_CONFIG_FILE=/run/secrets/{BUILDKIT_SECRET_MOUNT_PIP_CONF_ID}"
7
+ )
@@ -0,0 +1,32 @@
1
+ from typing import List, Optional
2
+
3
+ from truefoundry.deploy.builder.constants import BUILDKIT_SECRET_MOUNT_PIP_CONF_ID
4
+
5
+
6
+ def _get_id_from_buildkit_secret_value(value: str) -> Optional[str]:
7
+ parts = value.split(",")
8
+ secret_config = {}
9
+ for part in parts:
10
+ kv = part.split("=", 1)
11
+ if len(kv) != 2:
12
+ continue
13
+ key, value = kv
14
+ secret_config[key] = value
15
+
16
+ if "id" in secret_config and "src" in secret_config:
17
+ return secret_config["id"]
18
+
19
+ return None
20
+
21
+
22
+ def has_pip_conf_secret(docker_build_extra_args: List[str]) -> bool:
23
+ args = [arg.strip() for arg in docker_build_extra_args]
24
+ for i, arg in enumerate(docker_build_extra_args):
25
+ if (
26
+ arg == "--secret"
27
+ and i + 1 < len(args)
28
+ and _get_id_from_buildkit_secret_value(args[i + 1])
29
+ == BUILDKIT_SECRET_MOUNT_PIP_CONF_ID
30
+ ):
31
+ return True
32
+ return False
@@ -9,7 +9,7 @@ from click.exceptions import ClickException
9
9
 
10
10
  from truefoundry.autodeploy.cli import cli as autodeploy_cli
11
11
  from truefoundry.autodeploy.exception import InvalidRequirementsException
12
- from truefoundry.deploy.cli.const import GROUP_CLS
12
+ from truefoundry.deploy.cli.const import COMMAND_CLS, GROUP_CLS
13
13
  from truefoundry.deploy.cli.util import handle_exception_wrapper
14
14
 
15
15
 
@@ -54,8 +54,13 @@ def _get_default_spec_file():
54
54
  default=True,
55
55
  help="Wait and tail the deployment progress",
56
56
  )
57
+ @click.pass_context
57
58
  @handle_exception_wrapper
58
- def deploy_command(file: str, workspace_fqn: Optional[str], wait: bool):
59
+ def deploy_command(
60
+ ctx: click.Context, file: str, workspace_fqn: Optional[str], wait: bool
61
+ ):
62
+ if ctx.invoked_subcommand is not None:
63
+ return
59
64
  from truefoundry.deploy.lib.auth.servicefoundry_session import ServiceFoundrySession
60
65
  from truefoundry.deploy.v2.lib.deployable_patched_models import Application
61
66
 
@@ -98,5 +103,44 @@ def deploy_command(file: str, workspace_fqn: Optional[str], wait: bool):
98
103
  raise UsageError(message=str(e)) from e
99
104
 
100
105
 
106
+ @deploy_command.command(name="workflow", cls=COMMAND_CLS, help="Deploy a Workflow")
107
+ @click.option(
108
+ "-n",
109
+ "--name",
110
+ required=True,
111
+ help="Name of the Workflow",
112
+ )
113
+ @click.option(
114
+ "-f",
115
+ "--file",
116
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
117
+ required=True,
118
+ help="Path to the Workflow file. e.g. workflow.py",
119
+ )
120
+ @click.option(
121
+ "-w",
122
+ "--workspace-fqn",
123
+ "--workspace_fqn",
124
+ required=True,
125
+ help="FQN of the Workspace to deploy to.",
126
+ )
127
+ @handle_exception_wrapper
128
+ def deploy_workflow_command(name: str, workflow_file: str, workspace_fqn: str):
129
+ from truefoundry.deploy.lib.auth.servicefoundry_session import ServiceFoundrySession
130
+
131
+ try:
132
+ _ = ServiceFoundrySession()
133
+ except Exception as e:
134
+ raise ClickException(message=str(e)) from e
135
+
136
+ from truefoundry.deploy.v2.lib.deployable_patched_models import Workflow
137
+
138
+ workflow = Workflow(
139
+ name=name,
140
+ workflow_file_path=workflow_file,
141
+ )
142
+ workflow.deploy(workspace_fqn=workspace_fqn)
143
+
144
+
101
145
  def get_deploy_command():
102
146
  return deploy_command
@@ -35,9 +35,12 @@ def handle_exception(exception):
35
35
  border_style="red",
36
36
  )
37
37
  elif isinstance(exception, ConnectionError):
38
+ loc = ""
39
+ if exception.request:
40
+ loc = f" at {exception.request.url}"
38
41
  print_dict_as_table_panel(
39
42
  {
40
- "Error": "Couldn't connect to TrueFoundry. Please make sure that the provided `--host` is correct."
43
+ "Error": f"Couldn't connect to TrueFoundry{loc}. Please make sure that the provided `--host` is correct."
41
44
  },
42
45
  title="Command Failed",
43
46
  border_style="red",
@@ -8,6 +8,7 @@ from truefoundry.common.credential_provider import (
8
8
  FileCredentialProvider,
9
9
  )
10
10
  from truefoundry.common.entities import UserInfo
11
+ from truefoundry.common.utils import relogin_error_message
11
12
  from truefoundry.logger import logger
12
13
 
13
14
  ACTIVE_SESSION: Optional[ServiceFoundrySession] = None
@@ -41,8 +42,9 @@ class ServiceFoundrySession:
41
42
  break
42
43
  if final_cred_provider is None:
43
44
  raise Exception(
44
- "Please login again using `tfy login --relogin`"
45
- "or `tfy.login(relogin=True)` function"
45
+ relogin_error_message(
46
+ "No active session found. Perhaps you are not logged in?",
47
+ )
46
48
  )
47
49
  return final_cred_provider
48
50
 
@@ -76,6 +76,7 @@ def _upload_packaged_code(metadata, package_file):
76
76
 
77
77
 
78
78
  class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
79
+ # TODO (chiragjn): Rename base_url to tfy_host
79
80
  def __init__(self, init_session: bool = True, base_url: Optional[str] = None):
80
81
  self._session: Optional[ServiceFoundrySession] = None
81
82
  if init_session:
@@ -595,8 +596,9 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
595
596
 
596
597
  def trigger_workflow(self, application_id: str, inputs: Dict[str, Any]):
597
598
  url = f"{self._api_server_url}/{VERSION_PREFIX}/workflow/{application_id}/executions"
599
+ body = {"inputs": inputs}
598
600
  response = session_with_retries().post(
599
- url, json=inputs, headers=self._get_header()
601
+ url, json=body, headers=self._get_header()
600
602
  )
601
603
  response = request_handling(response)
602
604
  return response
@@ -8,7 +8,7 @@ from truefoundry.common.constants import TFY_API_KEY_ENV_KEY, TFY_HOST_ENV_KEY
8
8
  from truefoundry.common.credential_file_manager import CredentialsFileManager
9
9
  from truefoundry.common.credential_provider import EnvCredentialProvider
10
10
  from truefoundry.common.entities import CredentialsFileContent, Token
11
- from truefoundry.common.utils import resolve_base_url
11
+ from truefoundry.common.utils import relogin_error_message, resolve_tfy_host
12
12
  from truefoundry.deploy.io.output_callback import OutputCallBack
13
13
  from truefoundry.deploy.lib.const import (
14
14
  RICH_OUTPUT_CALLBACK,
@@ -43,7 +43,7 @@ def login(
43
43
  "Login will just save the credentials on disk."
44
44
  )
45
45
 
46
- host = resolve_base_url(host).strip("/")
46
+ host = resolve_tfy_host(host)
47
47
 
48
48
  with CredentialsFileManager() as cred_file:
49
49
  if not relogin and cred_file.exists():
@@ -58,10 +58,10 @@ def login(
58
58
  user_info = cred_file_content.to_token().to_user_info()
59
59
  user_name_display_info = user_info.email or user_info.user_type.value
60
60
  output_hook.print_line(
61
- f"Already logged in to {cred_file_content.host!r} as "
62
- f"{user_info.user_id!r} ({user_name_display_info})\n"
63
- "Please use `tfy login --relogin` or `tfy.login(relogin=True)` "
64
- "to force relogin"
61
+ relogin_error_message(
62
+ f"Already logged in to {cred_file_content.host!r} as {user_info.user_id!r} ({user_name_display_info})",
63
+ host=host,
64
+ )
65
65
  )
66
66
  return False
67
67
 
@@ -84,6 +84,10 @@ class CUDAVersion(str, enum.Enum):
84
84
  CUDA_12_0_CUDNN8 = "12.0-cudnn8"
85
85
  CUDA_12_1_CUDNN8 = "12.1-cudnn8"
86
86
  CUDA_12_2_CUDNN8 = "12.2-cudnn8"
87
+ CUDA_12_3_CUDNN9 = "12.3-cudnn9"
88
+ CUDA_12_4_CUDNN9 = "12.4-cudnn9"
89
+ CUDA_12_5_CUDNN9 = "12.5-cudnn9"
90
+ CUDA_12_6_CUDNN9 = "12.6-cudnn9"
87
91
 
88
92
 
89
93
  class GPUType(str, enum.Enum):
@@ -12,15 +12,13 @@ from truefoundry.ml.clients.entities import (
12
12
  )
13
13
  from truefoundry.ml.clients.utils import http_request_safe
14
14
  from truefoundry.ml.exceptions import MlFoundryException
15
- from truefoundry.ml.run_utils import append_servicefoundry_path_to_tracking_uri
16
15
 
17
16
 
18
17
  class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
18
+ # TODO (chiragjn): Rename tracking_uri to tfy_host
19
19
  def __init__(self, tracking_uri: str, token: Optional[str] = None):
20
20
  super().__init__(base_url=tracking_uri)
21
- self.host_creds = HostCreds(
22
- host=append_servicefoundry_path_to_tracking_uri(tracking_uri), token=token
23
- )
21
+ self.host_creds = HostCreds(host=self._api_server_url, token=token)
24
22
 
25
23
  def get_integration_from_id(self, integration_id: str):
26
24
  integration_id = integration_id or ""
@@ -7,6 +7,7 @@ from typing import Any, Dict, Iterator, List, Optional, Sequence, Tuple, Union
7
7
  import coolname
8
8
  import pandas as pd
9
9
 
10
+ from truefoundry.common.utils import relogin_error_message
10
11
  from truefoundry.ml import constants, env_vars
11
12
  from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
12
13
  ArtifactDto,
@@ -213,8 +214,9 @@ class MlFoundry:
213
214
  session = get_active_session()
214
215
  if session is None:
215
216
  raise MlFoundryException(
216
- "No active session found. Perhaps you are not logged in?\n"
217
- "Please log in using `tfy login [--host HOST] --relogin"
217
+ relogin_error_message(
218
+ "No active session found. Perhaps you are not logged in?",
219
+ )
218
220
  )
219
221
  servicefoundry_client = ServiceFoundryServiceClient(
220
222
  tracking_uri=self.get_tracking_uri(),
@@ -19,6 +19,7 @@ from typing import (
19
19
  from urllib.parse import urljoin, urlsplit
20
20
 
21
21
  from truefoundry import version
22
+ from truefoundry.common.utils import relogin_error_message
22
23
  from truefoundry.ml import constants, enums
23
24
  from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
24
25
  ArtifactType,
@@ -51,6 +52,8 @@ from truefoundry.ml.run_utils import ParamsType, flatten_dict, process_params
51
52
  from truefoundry.ml.session import ACTIVE_RUNS, _get_api_client, get_active_session
52
53
  from truefoundry.ml.validation_utils import (
53
54
  MAX_ENTITY_KEY_LENGTH,
55
+ MAX_METRICS_PER_BATCH,
56
+ MAX_PARAMS_TAGS_PER_BATCH,
54
57
  _validate_batch_log_data,
55
58
  )
56
59
 
@@ -222,8 +225,9 @@ class MlFoundryRun:
222
225
  session = get_active_session()
223
226
  if session is None:
224
227
  raise MlFoundryException(
225
- "No active session found. Perhaps you are not logged in?\n"
226
- "Please log in using `tfy login [--host HOST] --relogin"
228
+ relogin_error_message(
229
+ "No active session found. Perhaps you are not logged in?",
230
+ )
227
231
  )
228
232
  base_url = "{uri.scheme}://{uri.netloc}/".format(
229
233
  uri=urlsplit(session.tracking_uri)
@@ -636,12 +640,16 @@ class MlFoundryRun:
636
640
  return
637
641
 
638
642
  try:
639
- _validate_batch_log_data(metrics=metrics, params=[], tags=[])
640
- self._runs_api.log_run_batch_post(
641
- log_batch_request_dto=LogBatchRequestDto(
642
- run_id=self.run_id, metrics=metrics, params=[], tags=[]
643
+ for i in range(0, len(metrics), MAX_METRICS_PER_BATCH):
644
+ metrics_batch = metrics[i : i + MAX_METRICS_PER_BATCH]
645
+
646
+ _validate_batch_log_data(metrics=metrics_batch, params=[], tags=[])
647
+
648
+ self._runs_api.log_run_batch_post(
649
+ log_batch_request_dto=LogBatchRequestDto(
650
+ run_id=self.run_id, metrics=metrics_batch, params=[], tags=[]
651
+ )
643
652
  )
644
- )
645
653
  except Exception as e:
646
654
  raise MlFoundryException(str(e)) from e
647
655
 
@@ -723,12 +731,17 @@ class MlFoundryRun:
723
731
  if len(params) == 0:
724
732
  logger.warning("Cannot log empty params dictionary")
725
733
 
726
- _validate_batch_log_data(metrics=[], params=params, tags=[])
727
- self._runs_api.log_run_batch_post(
728
- log_batch_request_dto=LogBatchRequestDto(
729
- run_id=self.run_id, metrics=[], params=params, tags=[]
734
+ for i in range(0, len(params), MAX_PARAMS_TAGS_PER_BATCH):
735
+ params_batch = params[i : i + MAX_PARAMS_TAGS_PER_BATCH]
736
+
737
+ _validate_batch_log_data(metrics=[], params=params_batch, tags=[])
738
+ logger.debug("Logging parameters: %s", params_batch)
739
+
740
+ self._runs_api.log_run_batch_post(
741
+ log_batch_request_dto=LogBatchRequestDto(
742
+ run_id=self.run_id, metrics=[], params=params_batch, tags=[]
743
+ )
730
744
  )
731
- )
732
745
  except Exception as e:
733
746
  raise MlFoundryException(str(e)) from e
734
747
  logger.info("Parameters logged successfully")
@@ -765,11 +778,15 @@ class MlFoundryRun:
765
778
  tags_arr = [
766
779
  RunTagDto(key=key, value=str(value)) for key, value in tags.items()
767
780
  ]
768
- self._runs_api.log_run_batch_post(
769
- log_batch_request_dto=LogBatchRequestDto(
770
- run_id=self.run_id, metrics=[], params=[], tags=tags_arr
781
+ for i in range(0, len(tags_arr), MAX_PARAMS_TAGS_PER_BATCH):
782
+ tags_batch = tags_arr[i : i + MAX_PARAMS_TAGS_PER_BATCH]
783
+
784
+ _validate_batch_log_data(metrics=[], params=[], tags=tags_batch)
785
+ self._runs_api.log_run_batch_post(
786
+ log_batch_request_dto=LogBatchRequestDto(
787
+ run_id=self.run_id, metrics=[], params=[], tags=tags_batch
788
+ )
771
789
  )
772
- )
773
790
  except Exception as e:
774
791
  raise MlFoundryException(str(e)) from e
775
792
  logger.info("Tags set successfully")
@@ -1,9 +1,7 @@
1
1
  import argparse
2
2
  import importlib
3
3
  import json
4
- import os
5
4
  from typing import Any, Dict, List, Mapping, Optional, Tuple, Union
6
- from urllib.parse import urljoin, urlsplit
7
5
 
8
6
  import numpy as np
9
7
 
@@ -21,18 +19,6 @@ def get_module(
21
19
  raise MlFoundryException(msg) from ex
22
20
 
23
21
 
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
22
  class NumpyEncoder(json.JSONEncoder):
37
23
  """Special json encoder for numpy types"""
38
24
 
truefoundry/ml/session.py CHANGED
@@ -10,6 +10,7 @@ from truefoundry.common.credential_provider import (
10
10
  )
11
11
  from truefoundry.common.entities import Token, UserInfo
12
12
  from truefoundry.common.request_utils import urllib3_retry
13
+ from truefoundry.common.utils import get_tfy_servers_config, relogin_error_message
13
14
  from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
14
15
  ApiClient,
15
16
  Configuration,
@@ -17,9 +18,6 @@ from truefoundry.ml.autogen.client import ( # type: ignore[attr-defined]
17
18
  from truefoundry.ml.clients.entities import HostCreds
18
19
  from truefoundry.ml.exceptions import MlFoundryException
19
20
  from truefoundry.ml.logger import logger
20
- from truefoundry.ml.run_utils import (
21
- append_path_to_rest_tracking_uri,
22
- )
23
21
 
24
22
  if TYPE_CHECKING:
25
23
  from truefoundry.ml.mlfoundry_run import MlFoundryRun
@@ -83,6 +81,7 @@ class Session:
83
81
  self._assert_not_closed()
84
82
  return self._user_info
85
83
 
84
+ # TODO (chiragjn): Rename tracking_uri to tfy_host
86
85
  @property
87
86
  def tracking_uri(self) -> str:
88
87
  return self._cred_provider.base_url
@@ -97,7 +96,7 @@ class Session:
97
96
  )
98
97
 
99
98
  def get_host_creds(self) -> HostCreds:
100
- tracking_uri = append_path_to_rest_tracking_uri(self._cred_provider.base_url)
99
+ tracking_uri = get_tfy_servers_config(self.tracking_uri).mlfoundry_server_url
101
100
  return HostCreds(
102
101
  host=tracking_uri, token=self._cred_provider.token.access_token
103
102
  )
@@ -120,8 +119,9 @@ def _get_api_client(
120
119
  return ApiClient()
121
120
  else:
122
121
  raise MlFoundryException(
123
- "No active session found. Perhaps you are not logged in?\n"
124
- "Please log in using `tfy login [--host HOST] --relogin"
122
+ relogin_error_message(
123
+ "No active session found. Perhaps you are not logged in?",
124
+ )
125
125
  )
126
126
 
127
127
  creds = session.get_host_creds()
@@ -144,8 +144,9 @@ def init_session() -> Session:
144
144
  break
145
145
  if final_cred_provider is None:
146
146
  raise MlFoundryException(
147
- "Please login using `tfy login` command "
148
- "or `truefoundry.login()` function call"
147
+ relogin_error_message(
148
+ "No active session found. Perhaps you are not logged in?",
149
+ )
149
150
  )
150
151
  new_session = Session(cred_provider=final_cred_provider)
151
152
 
@@ -5,9 +5,9 @@ from truefoundry.workflow import PythonTaskConfig, TaskPythonBuild, task, workfl
5
5
  task_config=PythonTaskConfig(
6
6
  image=TaskPythonBuild(
7
7
  python_version="3.9",
8
- pip_packages=["truefoundry[workflow]==0.3.0rc7"],
8
+ pip_packages=["truefoundry[workflow]"],
9
9
  ),
10
- service_account="tfy-flyte-dataplane-devtest-s3",
10
+ service_account="tfy-workflows-sa",
11
11
  )
12
12
  )
13
13
  def say_hello() -> str:
@@ -7,7 +7,6 @@ from truefoundry.deploy import Image, NvidiaGPU, Resources
7
7
  from truefoundry.workflow import (
8
8
  ContainerTask,
9
9
  ContainerTaskConfig,
10
- ExecutionConfig,
11
10
  FlyteDirectory,
12
11
  PythonTaskConfig,
13
12
  TaskPythonBuild,
@@ -20,10 +19,10 @@ from truefoundry.workflow import (
20
19
  cpu_task_config = PythonTaskConfig(
21
20
  image=TaskPythonBuild(
22
21
  python_version="3.9",
23
- pip_packages=["truefoundry[workflow]==0.3.0rc7"],
22
+ pip_packages=["truefoundry[workflow]==0.4.1"],
24
23
  ),
25
24
  resources=Resources(cpu_request=0.45),
26
- service_account="tfy-flyte-dataplane-devtest-s3",
25
+ service_account="tfy-workflows-sa",
27
26
  )
28
27
 
29
28
 
@@ -49,7 +48,7 @@ def train_tokenizer() -> str:
49
48
  task_config=PythonTaskConfig(
50
49
  image=TaskPythonBuild(
51
50
  python_version="3.9",
52
- pip_packages=["truefoundry[workflow]==0.3.0rc7", "pynvml==11.5.0"],
51
+ pip_packages=["truefoundry[workflow]==0.4.1", "pynvml==11.5.0"],
53
52
  cuda_version="11.5-cudnn8",
54
53
  ),
55
54
  env={
@@ -57,16 +56,16 @@ def train_tokenizer() -> str:
57
56
  "NVIDIA_VISIBLE_DEVICES": "all",
58
57
  },
59
58
  resources=Resources(cpu_request=0.45, devices=[NvidiaGPU(name="T4", count=1)]),
60
- service_account="tfy-flyte-dataplane-devtest-s3",
59
+ service_account="tfy-workflows-sa",
61
60
  ),
62
61
  )
63
62
  def train_model(tokenizer: str) -> Tuple[FlyteDirectory, str]:
64
63
  print("Training model")
65
64
  import flytekit
66
- from pynvml import nvmlDeviceGetCount, nvmlInit
65
+ # from pynvml import nvmlDeviceGetCount, nvmlInit
67
66
 
68
- nvmlInit()
69
- assert nvmlDeviceGetCount() > 0
67
+ # nvmlInit()
68
+ # assert nvmlDeviceGetCount() > 0
70
69
 
71
70
  working_dir = flytekit.current_context().working_directory
72
71
  local_dir = Path(os.path.join(working_dir, "csv_files"))
@@ -102,10 +101,10 @@ echo = ContainerTask(
102
101
  name="echo",
103
102
  task_config=ContainerTaskConfig(
104
103
  image=Image(
105
- image_uri="bash:4.1",
104
+ image_uri="docker.io/bash:4.1",
106
105
  command=["echo", "hello"],
107
106
  ),
108
- service_account="tfy-flyte-dataplane-devtest-s3",
107
+ service_account="tfy-workflows-sa",
109
108
  ),
110
109
  )
111
110
 
@@ -117,11 +116,11 @@ def random(tokenizer: str) -> Tuple[FlyteDirectory, str]:
117
116
 
118
117
 
119
118
  @workflow(
120
- execution_configs=[
121
- ExecutionConfig(
122
- schedule="*/10 * * * *",
123
- )
124
- ]
119
+ # execution_configs=[
120
+ # ExecutionConfig(
121
+ # schedule="*/10 * * * *",
122
+ # )
123
+ # ]
125
124
  )
126
125
  def train(
127
126
  tokenizer: str = "",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: truefoundry
3
- Version: 0.4.1rc1
3
+ Version: 0.4.2
4
4
  Summary: Truefoundry CLI
5
5
  Author: Abhishek Choudhary
6
6
  Author-email: abhishek@truefoundry.com
@@ -31,7 +31,6 @@ Requires-Dist: openai (>=1.16.2,<2.0.0)
31
31
  Requires-Dist: packaging (>=20.0,<25.0)
32
32
  Requires-Dist: pandas (>=1.0.0,<3.0.0) ; python_version < "3.10"
33
33
  Requires-Dist: pandas (>=1.4.0,<3.0.0) ; python_version >= "3.10"
34
- Requires-Dist: psutil (>=5.9.0,<7.0.0)
35
34
  Requires-Dist: pydantic (>=1.8.2,<3.0.0)
36
35
  Requires-Dist: pygments (>=2.12.0,<3.0.0)
37
36
  Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
@@ -26,25 +26,27 @@ truefoundry/autodeploy/utils/pydantic_compat.py,sha256=hEAUy5kLjhPdzw7yGZ2iXGMXb
26
26
  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
- truefoundry/common/auth_service_client.py,sha256=upUTo6VODAaOKtszCQZNWnPpRT8CEhpCq9RLBJ0RrRY,7840
30
- truefoundry/common/constants.py,sha256=kHP5ebp7EMfEYaOC-K8T_XPfYi7mXttYgrXYS8bOgkg,359
31
- truefoundry/common/credential_file_manager.py,sha256=MRn5h9Gm4pxZEF_XT-_b2agMBiqyw0o2SLpq9ty_1lY,4255
32
- truefoundry/common/credential_provider.py,sha256=UHr3tH39wGjhC3XdRUdgW7IZeh0cHSIEYJMVO8B4eyw,4107
29
+ truefoundry/common/auth_service_client.py,sha256=tZOa0NdATnItsMeTnEnUeTZQIgUJtpU-nvLdWtB4Px8,7978
30
+ truefoundry/common/constants.py,sha256=JT8wcc9IxX3K_xbzXgxTp1FMQfKwnLam5XoJjhiCw1M,395
31
+ truefoundry/common/credential_file_manager.py,sha256=1yEk1Zm2xS4G0VDFwKSZ4w0VUrcPWQ1nJnoBaz9xyKA,4251
32
+ truefoundry/common/credential_provider.py,sha256=YQ6HKl8ZZFTg48vBZMauEAnM6IrEO3oOzM2DA47N-P0,4071
33
33
  truefoundry/common/entities.py,sha256=8O-EGPk4PKqnyoFMKUTxISCU19rz0KBnfRDJU695DhY,3797
34
34
  truefoundry/common/exceptions.py,sha256=ePpiQ_zmWe4e94gOgeMiyP_AZnKwjEBfyXsB5ScGYcI,329
35
35
  truefoundry/common/request_utils.py,sha256=-ss8033PClJOMJuS5Ue1zCFRVK7ZW9vjtva1b5G9uok,2849
36
- truefoundry/common/servicefoundry_client.py,sha256=ornmmhjSY2y-NJeBcoHPKxX3q9Z9XilcHERFSFk-dSU,2886
37
- truefoundry/common/utils.py,sha256=DbyLQM4WiKxzIStg2Ekv7fLs8amZR0HtsF75uqy8tMc,1741
36
+ truefoundry/common/servicefoundry_client.py,sha256=klohrELTFTJX-jvl9LGtdIoJiZMZRuQQB4Ho7PJ7u3s,3091
37
+ truefoundry/common/utils.py,sha256=lFiDEVoQDJoZPEN4Zun4GQz2TsfdbXfNHI41BErETq8,3320
38
38
  truefoundry/deploy/__init__.py,sha256=ugawKF2G02EmEXX35oZ2tec12d9oWN28Sf6mtGGIERY,2281
39
- truefoundry/deploy/auto_gen/models.py,sha256=I4-tV2V74mljN7lH606pCD5LArK6MvND_i7Vt_aRI5Q,79952
40
- truefoundry/deploy/builder/__init__.py,sha256=a1qR6nicHGcxRaeNTxWRsmDs8zsmXc7j13-I8q0qqVk,4938
39
+ truefoundry/deploy/auto_gen/models.py,sha256=AjbjNFwWDTIOjN_E1fSUgP-ej9nsb2OpZPhsnIRAVGs,80621
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=OK2lCwaPfhh7ALjNqfFBOTv7B9B3e9WDLoaSm0LWEkk,1436
44
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py,sha256=cEjpcz2L9lEEoEcF0rzuRpgXwNkuce8dbvdBvhTqDYk,1946
45
- truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py,sha256=n7MwUzIyn3mFqVkQqOuMeswmSPsydpYH4XMV5c8qAdQ,1381
46
- truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py,sha256=vFmFeK38-t8booJEGREapEjrIL8xnyOQeRSJQq7d3ZQ,6183
43
+ truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py,sha256=UmMcTY-8MrLY3H5owpn6ax-VePQl4MiMTmHlQ9qEtQw,1742
44
+ truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py,sha256=nRG3yXhIQ3Taq1B1loQWazQd05A3nrp5zKDYvNZh87s,2487
45
+ truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py,sha256=XNJ3MKWqyWIbFNnUQMpB8oVC5Pt5Wsm_bRKbvkXRIG8,1696
46
+ truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py,sha256=dccCNOUKbK1X8F4f2J4ddSTCturvzQKx6H23xxJdCTk,7202
47
+ truefoundry/deploy/builder/constants.py,sha256=eIukBjD6I4KvEmAPpdbPlPPr76yhS-uNr3RVFkzEdgs,257
47
48
  truefoundry/deploy/builder/docker_service.py,sha256=vQS15790njzlFJZ3JW6txYLBdT11ltxqqpf78ZFL_Ng,5208
49
+ truefoundry/deploy/builder/utils.py,sha256=9RZnkhoHFTRUt_x3nck0aVz7cLpzA3jiwQH-ZZZrjf8,938
48
50
  truefoundry/deploy/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
51
  truefoundry/deploy/cli/cli.py,sha256=o19n7MsMzXXXG3GlpRYaxX3l9Xvtof-iPK4-y31Dw9k,2811
50
52
  truefoundry/deploy/cli/commands/__init__.py,sha256=oOstv608ueOJTxR3qlQbZBIdo-FsFe2O0NYNOXi01-s,1276
@@ -53,7 +55,7 @@ truefoundry/deploy/cli/commands/build_command.py,sha256=DQ7NARgkIgV4z0Zdnl3zMDKU
53
55
  truefoundry/deploy/cli/commands/build_logs_command.py,sha256=WrPOlFec_wwuzdJmKZ8mjca-oFVvxgfblcqj2LlhWJA,2804
54
56
  truefoundry/deploy/cli/commands/create_command.py,sha256=ZjA4EP1jHYuVE1zx0kN-giBr3y0sEiXnu8xMsNyD2Rg,1850
55
57
  truefoundry/deploy/cli/commands/delete_command.py,sha256=4tyIameA1pVu9uZZNJPK6rqdV-cJogf51iCCrG-9noI,2390
56
- truefoundry/deploy/cli/commands/deploy_command.py,sha256=f3Bd0EClT4y6Eahw0pX2MFKiRD29j5kHlWVK5rbvK20,2858
58
+ truefoundry/deploy/cli/commands/deploy_command.py,sha256=ixn3uQoDRfJ9D8GOMwXJ9W9VbyNce9Qzb3Mx7h5InRM,4018
57
59
  truefoundry/deploy/cli/commands/get_command.py,sha256=w7h5C4bJpmHJ99rgiGg9J_X0xi8aZUeB6Q-SoZUV1tg,5979
58
60
  truefoundry/deploy/cli/commands/list_command.py,sha256=cFARY22L5xspBX7TWsx41IF4RiRMok7KBwv7hQRFXNs,4498
59
61
  truefoundry/deploy/cli/commands/login_command.py,sha256=WV4Ad8PJ2_yNfCJi1VWW2GNwU6khZ2sWZbKebxJ7oVM,1038
@@ -68,7 +70,7 @@ truefoundry/deploy/cli/config.py,sha256=tf8w4UfVzcC6eYkENvuuCPYt_V3sqVpO1bclORV9
68
70
  truefoundry/deploy/cli/console.py,sha256=9-dMy4YPisCJQziRKTg8Qa0UJnOGl1soiUnJjsnLDvE,242
69
71
  truefoundry/deploy/cli/const.py,sha256=dVHPo1uAiDSSMXwXoT2mR5kNQjExT98QNVRz98Hz_Ts,510
70
72
  truefoundry/deploy/cli/display_util.py,sha256=gq8EBdpBMHUzYQp_hxOg9EOYi08FIHgOVFCqXqC2tuo,3044
71
- truefoundry/deploy/cli/util.py,sha256=n2-YzdYkyJV1w7XzzWCLtksoaY35I6tM58T_qNpm4vY,3587
73
+ truefoundry/deploy/cli/util.py,sha256=bxbNEX3mbhtDpXw3b2GAnbxe0xvCF9N1sCMK6yatz0Q,3689
72
74
  truefoundry/deploy/core/__init__.py,sha256=j61bMWj4BkWihdssKMSFhieo7afJDtpc7qO7zk1rDB4,140
73
75
  truefoundry/deploy/core/login.py,sha256=N2VrW3nlBzoyoYulkipxwQvCpjBhi3sfsmhxK1ktWhg,236
74
76
  truefoundry/deploy/core/logout.py,sha256=TpWLq4_DsxYS5GX2OJQGDhekNOfiOLb-vO5khQueHXw,80
@@ -88,9 +90,9 @@ truefoundry/deploy/io/output_callback.py,sha256=V2YwUFec4G4a67lM4r-x_64AqdOVNo_9
88
90
  truefoundry/deploy/io/rich_output_callback.py,sha256=TJLiRD-EnFVwgcepxR7WN0koKqW1X2DevETPhNPi_nU,829
89
91
  truefoundry/deploy/json_util.py,sha256=x_-7YYQ4_HUIJ8ofOcclAp9JWhgTWjR9Th6Q0FuRqGk,175
90
92
  truefoundry/deploy/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
- truefoundry/deploy/lib/auth/servicefoundry_session.py,sha256=d4M56O_ZftFeDRiyRci11QABU9B1HW2eVOeVIrFvevE,1842
93
+ truefoundry/deploy/lib/auth/servicefoundry_session.py,sha256=5TCYPunAygtn5mb0mp_VcWKEalKMKPbyWMWer-Vty2g,1916
92
94
  truefoundry/deploy/lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
93
- truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=fgou4wjuWJzqQgW7iatbSCvmstSjY0iUJldQK5vsruE,26018
95
+ truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=vFrd5yeStjT_uWL3JxHrx0iAmNpuv26K7IEnsF1RMM8,26101
94
96
  truefoundry/deploy/lib/clients/shell_client.py,sha256=tMrc0Ha1DmGtUCJrZD8eusOzfe8R_WIe6AAH7nxL0xA,461
95
97
  truefoundry/deploy/lib/const.py,sha256=FCQfnO7IecB1ikQHdLGNvvubq_iF900C9l5TJtDfvFc,314
96
98
  truefoundry/deploy/lib/dao/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -103,7 +105,7 @@ truefoundry/deploy/lib/logs_utils.py,sha256=SQxRv3jDDmgHdOUMhlMaAPGYskybnBUMpst7
103
105
  truefoundry/deploy/lib/messages.py,sha256=nhp0bCYf_XpUM68hTq5lBY-__vtEyV2uP7NgnJXJ_Vg,925
104
106
  truefoundry/deploy/lib/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
105
107
  truefoundry/deploy/lib/model/entity.py,sha256=8J8yd98iWtSy8giShdDRNyzbN1UgSXx4XtmZLljdWnE,8552
106
- truefoundry/deploy/lib/session.py,sha256=e0iA-fWPBzxm5JqT4hnfv4NOpYexbrtRLSWLqqVtFzs,4986
108
+ truefoundry/deploy/lib/session.py,sha256=Vg6rCA315T0yS0xG4ayJ84Ia_9ZfibH8utOSwPBMAmw,4953
107
109
  truefoundry/deploy/lib/util.py,sha256=RlL3bjZu5Z0LU_OKYaMVfcMU8k7_rmkAp89_0CrZDLk,1520
108
110
  truefoundry/deploy/lib/win32.py,sha256=1RcvPTdlOAJ48rt8rCbE2Ufha2ztRqBAE9dueNXArrY,5009
109
111
  truefoundry/deploy/python_deploy_codegen.py,sha256=Ok7ufDY2x3aMJv9KpaRqxiS-ZI-kxBWauIUHst-ug7E,4020
@@ -113,7 +115,7 @@ truefoundry/deploy/v2/lib/deploy.py,sha256=HIcY3SzQ5lWl7avuuKi3J0Z-PBES6Sf4hgMK-
113
115
  truefoundry/deploy/v2/lib/deploy_workflow.py,sha256=WhlrBuRf7r83qoQdTZSQzHt635fw9D4_qQIqusFWGag,12372
114
116
  truefoundry/deploy/v2/lib/deployable_patched_models.py,sha256=MROgMxhn9hDEAKwJSWl3iz12tUVvRKzEtqF2QUT6dAk,3343
115
117
  truefoundry/deploy/v2/lib/models.py,sha256=pSolLMTArDuYpeNsmeeS5DWliloN_iCDfZSpRllMHUg,1120
116
- truefoundry/deploy/v2/lib/patched_models.py,sha256=qL3N7y6zf7jywx6uY-yMxsH65NB367V_82o47ZYJt1I,13820
118
+ truefoundry/deploy/v2/lib/patched_models.py,sha256=sokVDUdnhe3qx6dXlHM0shbf6HvSlF72-mvi8Lzt_Y8,13968
117
119
  truefoundry/deploy/v2/lib/source.py,sha256=VHKuFREiixUP40D3Mrz-TA70spu1M0RbCzl--qwlFaY,9263
118
120
  truefoundry/langchain/__init__.py,sha256=zeYKxKrQhfYXQuBec3wvB_ZqKowDUUjLUKUhbiu9ZFs,558
119
121
  truefoundry/langchain/deprecated.py,sha256=8tfLHXwcifGl7DYhMNfzc4zRVCVqEgARg5BsbZp11NE,10835
@@ -296,7 +298,7 @@ truefoundry/ml/cli/commands/__init__.py,sha256=diDUiRUX4l6TtNLI4iF-ZblczkELM7FRV
296
298
  truefoundry/ml/cli/commands/download.py,sha256=cbz9KijiLKXj4-twlig3xZLTVRNm4fnjwpy0leZr31w,2342
297
299
  truefoundry/ml/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
298
300
  truefoundry/ml/clients/entities.py,sha256=sNP4DnAVdQoMfc06s0r3VTzKHTo7jmxAOuTlQOVmsMs,151
299
- truefoundry/ml/clients/servicefoundry_client.py,sha256=lK7mepLNRHHS65Eebynk5HyCMSxqmduXRHO2k8pTIK0,1680
301
+ truefoundry/ml/clients/servicefoundry_client.py,sha256=nmKrWBd7P0VEDGjPHZ8_scX3Xo9tv3wZSWlTIBwGptU,1597
300
302
  truefoundry/ml/clients/utils.py,sha256=c0LdC8moejs-Zm30hu1sCqifLEmqhdq4SfZ_m0nUIDk,4402
301
303
  truefoundry/ml/constants.py,sha256=vDq72d4C9FSWqr9MMdjgTF4TuyNFApvo_6RVsSeAjB4,2837
302
304
  truefoundry/ml/entities.py,sha256=si5GAqZsWzKu5MPrU4Hk6se7bebHOYhTiNw69ai-Uk8,1485
@@ -322,25 +324,25 @@ truefoundry/ml/log_types/plot.py,sha256=oFnXNb2o5fVF0zsnRjvqjSjLaphQWUnQCdw72e2u
322
324
  truefoundry/ml/log_types/pydantic_base.py,sha256=eBlw_AEyAz4iJKDP4zgJOCFWcldwQqpf7FADW1jzIQY,272
323
325
  truefoundry/ml/log_types/utils.py,sha256=xjJ21jdPScvFmw3TbVh5NCzbzJwaqiXJyiiT4xxX1EI,335
324
326
  truefoundry/ml/logger.py,sha256=VT-BF3BnBYTWVq87O58F0c8uXMu94gYzsiFlGY3_7Ao,458
325
- truefoundry/ml/mlfoundry_api.py,sha256=8K8te6aLBpwZ-Qg-ALEROiI5FWgPVycyAi_VZaFaLRg,60801
326
- truefoundry/ml/mlfoundry_run.py,sha256=CTUgf2-kPANie9Kog_rJRvpkDfotVzWTHvBg2ofv_FA,44341
327
- truefoundry/ml/run_utils.py,sha256=dzbQ_TGkSCYO-gg8tlr5tglR0p2SIQdy0wl4IEQE5JA,2899
328
- truefoundry/ml/session.py,sha256=gASORQ7TZ3kqAyqG64YPfn5Z8Uo2BjOvfhMmTX-X7uM,5263
327
+ truefoundry/ml/mlfoundry_api.py,sha256=I2T8tXeAIWpD8EH05fm80mNyX6cs2S1ORI4qoo0HTpQ,60847
328
+ truefoundry/ml/mlfoundry_run.py,sha256=rNJl130iJkpjW3MNoe5-d_J9VJJQBqWHEJCfYeiZCbE,45123
329
+ truefoundry/ml/run_utils.py,sha256=0W208wSLUrbdfk2pjNcZlkUi9bNxG2JORqoe-5rVqHI,2423
330
+ truefoundry/ml/session.py,sha256=Rvq419hdLNBt0KUzKn7jGvKKg9LI_gafOHcw-lFceAE,5330
329
331
  truefoundry/ml/validation_utils.py,sha256=XBSUd9OoyriWJpT3M5LKz17iWY3yVMr3hM5vdaVjtf0,12082
330
332
  truefoundry/pydantic_v1.py,sha256=jSuhGtz0Mbk1qYu8jJ1AcnIDK4oxUsdhALc4spqstmM,345
331
333
  truefoundry/version.py,sha256=bqiT4Q-VWrTC6P4qfK43mez-Ppf-smWfrl6DcwV7mrw,137
332
334
  truefoundry/workflow/__init__.py,sha256=hkDVrnG95duO-9XEmdjgrsvyhjQUVqyeQ1-dADB1QuE,935
333
335
  truefoundry/workflow/container_task.py,sha256=8arieePsX4__OnG337hOtCiNgJwtKJJCsZcmFmCBJtk,402
334
336
  truefoundry/workflow/example/deploy.sh,sha256=wfbPRrCi04WYRqCf4g-Xo12uWbcqPD6G_Tz0lV0jU_U,60
335
- truefoundry/workflow/example/hello_world_package/workflow.py,sha256=ACWJJWTY5NhYyU_dAEfdSqXgyLuNXPRDpOBuUkTEzhU,459
336
- truefoundry/workflow/example/package/test_workflow.py,sha256=-CeCcMyI3fjRGSkzfmmnLcE2aFxceCUpEXx8dql8MBE,4015
337
+ truefoundry/workflow/example/hello_world_package/workflow.py,sha256=IkRKfPY5BcvLPo_PVuNbZKK9PPJ93LRkzb1a3RKQYOw,435
338
+ truefoundry/workflow/example/package/test_workflow.py,sha256=TUcvYCGz4IbzF9bQJAfB-6Wd2wx-0RKcuIpUG0t6d8c,3972
337
339
  truefoundry/workflow/example/truefoundry.yaml,sha256=LlPrMADSPJsiXRoK76N_RVjX1bnZ3FH1u2jXrwLfR9I,226
338
340
  truefoundry/workflow/example/workflow.yaml,sha256=YtYdKXMuW_08gfEo21XSculj2MGI2lfEnGF8qCT8NKE,2858
339
341
  truefoundry/workflow/map_task.py,sha256=2m3qGXQ90k9LdS45q8dqCCECc3qr8t2m_LMCVd1mZ7g,1737
340
342
  truefoundry/workflow/python_task.py,sha256=SRXRLC4vdBqGjhkwuaY39LEWN6iPCpJAuW17URRdWTY,1128
341
343
  truefoundry/workflow/task.py,sha256=ToitYiKcNzFCtOVQwz1W8sRjbR97eVS7vQBdbgUQtKg,1779
342
344
  truefoundry/workflow/workflow.py,sha256=WaTqUjhwfAXDWu4E5ehuwAxrCbDJkoAf1oWmR2E9Qy0,4575
343
- truefoundry-0.4.1rc1.dist-info/METADATA,sha256=Pl4VeIlCAJqjQIsSZtxBKd-1tdkdb9rhKSDmVHhnHJ0,3140
344
- truefoundry-0.4.1rc1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
345
- truefoundry-0.4.1rc1.dist-info/entry_points.txt,sha256=TXvUxQkI6zmqJuycPsyxEIMr3oqfDjgrWj0m_9X12x4,95
346
- truefoundry-0.4.1rc1.dist-info/RECORD,,
345
+ truefoundry-0.4.2.dist-info/METADATA,sha256=xI1S8haO5ttWh1UKrtXXLMYuylRMF8AqV5mgoXgk7Vc,3098
346
+ truefoundry-0.4.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
347
+ truefoundry-0.4.2.dist-info/entry_points.txt,sha256=TXvUxQkI6zmqJuycPsyxEIMr3oqfDjgrWj0m_9X12x4,95
348
+ truefoundry-0.4.2.dist-info/RECORD,,