truefoundry 0.6.6__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of truefoundry might be problematic. Click here for more details.
- truefoundry/_client.py +1 -1
- truefoundry/cli/__main__.py +2 -0
- truefoundry/common/entities.py +6 -2
- truefoundry/common/session.py +8 -3
- truefoundry/deploy/__init__.py +1 -0
- truefoundry/deploy/_autogen/models.py +48 -16
- truefoundry/deploy/cli/commands/get_command.py +10 -196
- truefoundry/deploy/cli/commands/k8s_exec_credential_command.py +64 -0
- truefoundry/deploy/cli/commands/kubeconfig_command.py +129 -0
- truefoundry/deploy/cli/commands/utils.py +118 -0
- truefoundry/deploy/io/no_output_callback.py +27 -0
- truefoundry/deploy/lib/clients/servicefoundry_client.py +8 -0
- truefoundry/deploy/lib/model/entity.py +5 -0
- truefoundry/deploy/lib/session.py +4 -2
- truefoundry/deploy/v2/lib/deploy.py +5 -29
- truefoundry/deploy/v2/lib/patched_models.py +5 -88
- truefoundry/ml/prompt_utils.py +14 -9
- truefoundry/workflow/__init__.py +4 -1
- {truefoundry-0.6.6.dist-info → truefoundry-0.7.0.dist-info}/METADATA +3 -3
- {truefoundry-0.6.6.dist-info → truefoundry-0.7.0.dist-info}/RECORD +22 -18
- {truefoundry-0.6.6.dist-info → truefoundry-0.7.0.dist-info}/WHEEL +0 -0
- {truefoundry-0.6.6.dist-info → truefoundry-0.7.0.dist-info}/entry_points.txt +0 -0
truefoundry/_client.py
CHANGED
truefoundry/cli/__main__.py
CHANGED
|
@@ -14,6 +14,7 @@ from truefoundry.deploy.cli.commands import (
|
|
|
14
14
|
get_delete_command,
|
|
15
15
|
get_deploy_command,
|
|
16
16
|
get_deploy_init_command,
|
|
17
|
+
get_get_command,
|
|
17
18
|
get_login_command,
|
|
18
19
|
get_logout_command,
|
|
19
20
|
get_patch_application_command,
|
|
@@ -88,6 +89,7 @@ def create_truefoundry_cli() -> click.Group:
|
|
|
88
89
|
cli.add_command(get_trigger_command())
|
|
89
90
|
cli.add_command(get_terminate_command())
|
|
90
91
|
cli.add_command(get_ml_cli())
|
|
92
|
+
cli.add_command(get_get_command())
|
|
91
93
|
|
|
92
94
|
if not (sys.platform.startswith("win32") or sys.platform.startswith("cygwin")):
|
|
93
95
|
cli.add_command(get_patch_command())
|
truefoundry/common/entities.py
CHANGED
|
@@ -58,10 +58,14 @@ class Token(BaseModel):
|
|
|
58
58
|
assert self.decoded_value is not None
|
|
59
59
|
return self.decoded_value["tenantName"]
|
|
60
60
|
|
|
61
|
+
@property
|
|
62
|
+
def exp(self) -> int:
|
|
63
|
+
assert self.decoded_value is not None
|
|
64
|
+
return self.decoded_value["exp"]
|
|
65
|
+
|
|
61
66
|
def is_going_to_be_expired(self, buffer_in_seconds: int = 120) -> bool:
|
|
62
67
|
assert self.decoded_value is not None
|
|
63
|
-
|
|
64
|
-
return (exp - time.time()) < buffer_in_seconds
|
|
68
|
+
return (self.exp - time.time()) < buffer_in_seconds
|
|
65
69
|
|
|
66
70
|
def to_user_info(self) -> UserInfo:
|
|
67
71
|
assert self.decoded_value is not None
|
truefoundry/common/session.py
CHANGED
|
@@ -8,7 +8,7 @@ from truefoundry.common.credential_provider import (
|
|
|
8
8
|
EnvCredentialProvider,
|
|
9
9
|
FileCredentialProvider,
|
|
10
10
|
)
|
|
11
|
-
from truefoundry.common.entities import UserInfo
|
|
11
|
+
from truefoundry.common.entities import Token, UserInfo
|
|
12
12
|
from truefoundry.common.utils import relogin_error_message
|
|
13
13
|
from truefoundry.logger import logger
|
|
14
14
|
|
|
@@ -20,7 +20,7 @@ class Session:
|
|
|
20
20
|
def __init__(self) -> None:
|
|
21
21
|
self._closed = False
|
|
22
22
|
self._cred_provider: Optional[CredentialProvider] = self._get_cred_provider()
|
|
23
|
-
self._user_info: Optional[UserInfo] = self.
|
|
23
|
+
self._user_info: Optional[UserInfo] = self.token.to_user_info()
|
|
24
24
|
|
|
25
25
|
@staticmethod
|
|
26
26
|
def _get_cred_provider() -> CredentialProvider:
|
|
@@ -69,7 +69,12 @@ class Session:
|
|
|
69
69
|
@property
|
|
70
70
|
def access_token(self) -> str:
|
|
71
71
|
assert self._cred_provider is not None
|
|
72
|
-
return self.
|
|
72
|
+
return self.token.access_token
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def token(self) -> Token:
|
|
76
|
+
assert self._cred_provider is not None
|
|
77
|
+
return self._cred_provider.token
|
|
73
78
|
|
|
74
79
|
@property
|
|
75
80
|
def tfy_host(self) -> str:
|
truefoundry/deploy/__init__.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# generated by datamodel-codegen:
|
|
2
2
|
# filename: application.json
|
|
3
|
-
# timestamp: 2025-04-
|
|
3
|
+
# timestamp: 2025-04-21T06:26:10+00:00
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
@@ -360,6 +360,13 @@ class Image(BaseModel):
|
|
|
360
360
|
)
|
|
361
361
|
|
|
362
362
|
|
|
363
|
+
class JobEvent(str, Enum):
|
|
364
|
+
START = "START"
|
|
365
|
+
SUCCEEDED = "SUCCEEDED"
|
|
366
|
+
FAILED = "FAILED"
|
|
367
|
+
TERMINATED = "TERMINATED"
|
|
368
|
+
|
|
369
|
+
|
|
363
370
|
class Claim(BaseModel):
|
|
364
371
|
key: str
|
|
365
372
|
values: List[str]
|
|
@@ -466,10 +473,6 @@ class NodeSelector(BaseModel):
|
|
|
466
473
|
"""
|
|
467
474
|
|
|
468
475
|
type: Literal["node_selector"] = Field(..., description="")
|
|
469
|
-
gpu_type: Optional[str] = Field(
|
|
470
|
-
None,
|
|
471
|
-
description="Name of the Nvidia GPU. One of [P4, P100, V100, T4, A10G, A100_40GB, A100_80GB]\nOne instance of the card contains the following amount of memory -\nP4: 8 GB, P100: 16 GB, V100: 16 GB, T4: 16 GB, A10G: 24 GB, A100_40GB: 40GB, A100_80GB: 80 GB",
|
|
472
|
-
)
|
|
473
476
|
instance_families: Optional[List[str]] = Field(
|
|
474
477
|
None,
|
|
475
478
|
description="Instance family of the underlying machine to use. Multiple instance families can be supplied.\nThe workload is guaranteed to be scheduled on one of them.",
|
|
@@ -699,10 +702,6 @@ class Resources(BaseModel):
|
|
|
699
702
|
2000,
|
|
700
703
|
description="Disk storage limit. The unit of memory is in megabytes(MB). Exceeding this limit will result in eviction.\nIt should be greater than the request. This is ephemeral storage and will be wiped out on pod restarts or eviction",
|
|
701
704
|
)
|
|
702
|
-
gpu_count: Optional[conint(ge=0, le=16)] = Field(
|
|
703
|
-
None,
|
|
704
|
-
description="Count of GPUs to provide to the application\nNote the exact count and max count available for a given GPU type depends on cloud provider and cluster type.",
|
|
705
|
-
)
|
|
706
705
|
shared_memory_size: Optional[conint(ge=64, le=2000000)] = Field(
|
|
707
706
|
None,
|
|
708
707
|
description="Define the shared memory requirements for your workload. Machine learning libraries like Pytorch can use Shared Memory\nfor inter-process communication. If you use this, we will mount a `tmpfs` backed volume at the `/dev/shm` directory.\nAny usage will also count against the workload's memory limit (`resources.memory_limit`) along with your workload's memory usage.\nIf the overall usage goes above `resources.memory_limit` the user process may get killed.\nShared Memory Size cannot be more than the defined Memory Limit for the workload.",
|
|
@@ -829,6 +828,16 @@ class ServiceAutoscaling(BaseAutoscaling):
|
|
|
829
828
|
)
|
|
830
829
|
|
|
831
830
|
|
|
831
|
+
class SlackBot(BaseModel):
|
|
832
|
+
type: Literal["slack-bot"] = Field(..., description="")
|
|
833
|
+
notification_channel: constr(min_length=1) = Field(
|
|
834
|
+
..., description="Specify the notification channel to send alerts to"
|
|
835
|
+
)
|
|
836
|
+
channels: List[constr(regex=r"^#[a-z0-9\-_]{2,80}$")] = Field(
|
|
837
|
+
..., description="List of channels to send messages to."
|
|
838
|
+
)
|
|
839
|
+
|
|
840
|
+
|
|
832
841
|
class SlackWebhook(BaseModel):
|
|
833
842
|
type: Literal["slack-webhook"] = Field(..., description="")
|
|
834
843
|
notification_channel: constr(min_length=1) = Field(
|
|
@@ -856,6 +865,26 @@ class SparkExecutorFixedInstances(BaseModel):
|
|
|
856
865
|
count: conint(ge=0, le=500) = Field(1, description="Number of instances to start")
|
|
857
866
|
|
|
858
867
|
|
|
868
|
+
class SparkImage(BaseModel):
|
|
869
|
+
"""
|
|
870
|
+
Describes that we are using a pre-built image stored in a Docker Image registry
|
|
871
|
+
"""
|
|
872
|
+
|
|
873
|
+
type: Literal["image"] = Field(..., description="")
|
|
874
|
+
spark_version: str = Field(
|
|
875
|
+
"3.5.2",
|
|
876
|
+
description="Spark version should match the spark version installed in the image.",
|
|
877
|
+
)
|
|
878
|
+
image_uri: constr(regex=r"^\S*$") = Field(
|
|
879
|
+
...,
|
|
880
|
+
description="The image URI. Specify the name of the image and the tag.\nIf the image is in Dockerhub, you can skip registry-url (for e.g. `tensorflow/tensorflow`).\nYou can use an image from a private registry using Advanced fields",
|
|
881
|
+
)
|
|
882
|
+
docker_registry: Optional[str] = Field(
|
|
883
|
+
None,
|
|
884
|
+
description="FQN of the container registry. If you can't find your registry here,\nadd it through the [Integrations](/integrations?tab=docker-registry) page",
|
|
885
|
+
)
|
|
886
|
+
|
|
887
|
+
|
|
859
888
|
class SparkJobJavaEntrypoint(BaseModel):
|
|
860
889
|
type: Literal["java"] = Field(..., description="")
|
|
861
890
|
main_application_file: str = Field(
|
|
@@ -1031,6 +1060,13 @@ class WorkbenchImage(BaseModel):
|
|
|
1031
1060
|
)
|
|
1032
1061
|
|
|
1033
1062
|
|
|
1063
|
+
class WorkflowEvent(str, Enum):
|
|
1064
|
+
SUCCEEDED = "SUCCEEDED"
|
|
1065
|
+
FAILED = "FAILED"
|
|
1066
|
+
ABORTED = "ABORTED"
|
|
1067
|
+
TIMED_OUT = "TIMED_OUT"
|
|
1068
|
+
|
|
1069
|
+
|
|
1034
1070
|
class ArtifactsDownload(BaseModel):
|
|
1035
1071
|
"""
|
|
1036
1072
|
Download and cache models in a volume to enhance loading speeds and reduce costs by avoiding repeated downloads. [Docs](https://docs.truefoundry.com/docs/download-and-cache-models)
|
|
@@ -1385,14 +1421,10 @@ class SparkJob(BaseModel):
|
|
|
1385
1421
|
name: constr(regex=r"^[a-z](?:[a-z0-9]|-(?!-)){1,30}[a-z0-9]$") = Field(
|
|
1386
1422
|
..., description="Name of the job"
|
|
1387
1423
|
)
|
|
1424
|
+
image: SparkImage
|
|
1388
1425
|
entrypoint: Union[
|
|
1389
1426
|
SparkJobPythonEntrypoint, SparkJobScalaEntrypoint, SparkJobJavaEntrypoint
|
|
1390
1427
|
] = Field(..., description="")
|
|
1391
|
-
image: Image
|
|
1392
|
-
spark_version: str = Field(
|
|
1393
|
-
"3.5.2",
|
|
1394
|
-
description="Spark version should match the spark version installed in the image.",
|
|
1395
|
-
)
|
|
1396
1428
|
driver_config: SparkDriverConfig
|
|
1397
1429
|
executor_config: SparkExecutorConfig
|
|
1398
1430
|
env: Optional[Dict[str, Any]] = Field(
|
|
@@ -1452,7 +1484,7 @@ class WorkflowAlert(BaseModel):
|
|
|
1452
1484
|
Describes the configuration for the workflow alerts
|
|
1453
1485
|
"""
|
|
1454
1486
|
|
|
1455
|
-
notification_target: Optional[Union[Email, SlackWebhook]] = None
|
|
1487
|
+
notification_target: Optional[Union[Email, SlackWebhook, SlackBot]] = None
|
|
1456
1488
|
on_completion: bool = Field(
|
|
1457
1489
|
False, description="Send an alert when the job completes"
|
|
1458
1490
|
)
|
|
@@ -1519,7 +1551,7 @@ class JobAlert(BaseModel):
|
|
|
1519
1551
|
None,
|
|
1520
1552
|
description="List of recipients' email addresses if the notification channel is Email.",
|
|
1521
1553
|
)
|
|
1522
|
-
notification_target: Optional[Union[Email, SlackWebhook]] = None
|
|
1554
|
+
notification_target: Optional[Union[Email, SlackWebhook, SlackBot]] = None
|
|
1523
1555
|
on_start: bool = Field(False, description="Send an alert when the job starts")
|
|
1524
1556
|
on_completion: bool = Field(
|
|
1525
1557
|
False, description="Send an alert when the job completes"
|
|
@@ -1,19 +1,11 @@
|
|
|
1
|
-
import contextlib
|
|
2
|
-
import sys
|
|
3
|
-
from enum import Enum
|
|
4
|
-
|
|
5
1
|
import rich_click as click
|
|
6
|
-
import yaml
|
|
7
|
-
from rich.pretty import pprint
|
|
8
2
|
|
|
9
|
-
from truefoundry.cli.
|
|
10
|
-
from truefoundry.
|
|
11
|
-
from truefoundry.cli.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
from truefoundry.deploy.
|
|
15
|
-
from truefoundry.deploy.lib.dao import version as version_lib
|
|
16
|
-
from truefoundry.deploy.lib.dao import workspace as workspace_lib
|
|
3
|
+
from truefoundry.cli.const import GROUP_CLS
|
|
4
|
+
from truefoundry.common.utils import is_internal_env_set
|
|
5
|
+
from truefoundry.deploy.cli.commands.k8s_exec_credential_command import (
|
|
6
|
+
k8s_exec_credential_command,
|
|
7
|
+
)
|
|
8
|
+
from truefoundry.deploy.cli.commands.kubeconfig_command import kubeconfig_command
|
|
17
9
|
|
|
18
10
|
# TODO (chiragjn): --json should disable all non json console prints
|
|
19
11
|
|
|
@@ -26,191 +18,13 @@ def get_command():
|
|
|
26
18
|
|
|
27
19
|
\b
|
|
28
20
|
Supported resources:
|
|
29
|
-
-
|
|
30
|
-
- Application
|
|
31
|
-
- Application Version
|
|
21
|
+
- Kubeconfig
|
|
32
22
|
"""
|
|
33
23
|
pass
|
|
34
24
|
|
|
35
25
|
|
|
36
|
-
@click.command(name="workspace", cls=COMMAND_CLS, help="Get Workspace details")
|
|
37
|
-
@click.option(
|
|
38
|
-
"-w",
|
|
39
|
-
"--workspace-fqn",
|
|
40
|
-
"--workspace_fqn",
|
|
41
|
-
type=click.STRING,
|
|
42
|
-
default=None,
|
|
43
|
-
help="FQN of the Workspace",
|
|
44
|
-
required=True,
|
|
45
|
-
)
|
|
46
|
-
@handle_exception_wrapper
|
|
47
|
-
def get_workspace(workspace_fqn):
|
|
48
|
-
workspace = workspace_lib.get_workspace_by_fqn(workspace_fqn=workspace_fqn)
|
|
49
|
-
if CliConfig.get("json"):
|
|
50
|
-
print_json(data=workspace.dict())
|
|
51
|
-
else:
|
|
52
|
-
print_entity_obj("Workspace", workspace)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@click.command(name="application", cls=COMMAND_CLS, help="Get Application details")
|
|
56
|
-
@click.option(
|
|
57
|
-
"--application-fqn",
|
|
58
|
-
"--application_fqn",
|
|
59
|
-
type=click.STRING,
|
|
60
|
-
default=None,
|
|
61
|
-
help="FQN of the application",
|
|
62
|
-
required=True,
|
|
63
|
-
)
|
|
64
|
-
@handle_exception_wrapper
|
|
65
|
-
def get_application(application_fqn):
|
|
66
|
-
application = application_lib.get_application(application_fqn=application_fqn)
|
|
67
|
-
if CliConfig.get("json"):
|
|
68
|
-
print_json(data=application.dict())
|
|
69
|
-
else:
|
|
70
|
-
print_entity_obj(
|
|
71
|
-
"Application",
|
|
72
|
-
application,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
@click.command(
|
|
77
|
-
name="application-version", cls=COMMAND_CLS, help="Get Application Version details"
|
|
78
|
-
)
|
|
79
|
-
@click.option(
|
|
80
|
-
"--application-fqn",
|
|
81
|
-
"--application_fqn",
|
|
82
|
-
type=click.STRING,
|
|
83
|
-
default=None,
|
|
84
|
-
help="FQN of the application",
|
|
85
|
-
required=True,
|
|
86
|
-
)
|
|
87
|
-
@click.option(
|
|
88
|
-
"--version",
|
|
89
|
-
type=click.STRING,
|
|
90
|
-
default=None,
|
|
91
|
-
help="Version number of the application deployment",
|
|
92
|
-
required=True,
|
|
93
|
-
)
|
|
94
|
-
@handle_exception_wrapper
|
|
95
|
-
def get_version(application_fqn, version):
|
|
96
|
-
version = version_lib.get_version(application_fqn=application_fqn, version=version)
|
|
97
|
-
if CliConfig.get("json"):
|
|
98
|
-
print_json(data=version.dict())
|
|
99
|
-
else:
|
|
100
|
-
print_entity_obj("Version", version)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@click.command(
|
|
104
|
-
name="spec", cls=COMMAND_CLS, help="Get YAML/Python Spec for an Application Version"
|
|
105
|
-
)
|
|
106
|
-
@click.option(
|
|
107
|
-
"--application-fqn",
|
|
108
|
-
"--application_fqn",
|
|
109
|
-
type=click.STRING,
|
|
110
|
-
default=None,
|
|
111
|
-
help="FQN of the application",
|
|
112
|
-
required=True,
|
|
113
|
-
)
|
|
114
|
-
@click.option(
|
|
115
|
-
"--version",
|
|
116
|
-
type=click.STRING,
|
|
117
|
-
default=None,
|
|
118
|
-
help="Version number of the application deployment",
|
|
119
|
-
required=True,
|
|
120
|
-
)
|
|
121
|
-
@click.option(
|
|
122
|
-
"-o",
|
|
123
|
-
"--output",
|
|
124
|
-
type=click.Choice(
|
|
125
|
-
[
|
|
126
|
-
"yml",
|
|
127
|
-
"yaml",
|
|
128
|
-
"json",
|
|
129
|
-
"py",
|
|
130
|
-
"python",
|
|
131
|
-
]
|
|
132
|
-
),
|
|
133
|
-
default="yaml",
|
|
134
|
-
help="Output format for the spec",
|
|
135
|
-
required=False,
|
|
136
|
-
)
|
|
137
|
-
@handle_exception_wrapper
|
|
138
|
-
def get_spec(application_fqn, version, output):
|
|
139
|
-
version = version_lib.get_version(application_fqn=application_fqn, version=version)
|
|
140
|
-
if output in ["yml", "yaml"]:
|
|
141
|
-
yaml.safe_dump(version.manifest.dict(), sys.stdout, indent=2)
|
|
142
|
-
elif output in ["json"]:
|
|
143
|
-
print_json(version.manifest.dict())
|
|
144
|
-
elif output in ["py", "python"]:
|
|
145
|
-
from truefoundry.deploy.v2.lib.deployable_patched_models import Application
|
|
146
|
-
|
|
147
|
-
manifest = version.manifest.dict()
|
|
148
|
-
instance = Application.parse_obj(manifest).__root__
|
|
149
|
-
|
|
150
|
-
# TODO (chiragjn): Can we somehow just enable `use_enum_values` on all Pydantic classes?
|
|
151
|
-
|
|
152
|
-
@contextlib.contextmanager
|
|
153
|
-
def _monkey_patch_enum_repr():
|
|
154
|
-
def new_repr(self):
|
|
155
|
-
# return "%r" % (self._value_)
|
|
156
|
-
return "%s.%s" % (self.__class__.__name__, self._name_)
|
|
157
|
-
|
|
158
|
-
enum_subclasses = [
|
|
159
|
-
es
|
|
160
|
-
for es in Enum.__subclasses__()
|
|
161
|
-
if es.__module__.startswith("truefoundry.")
|
|
162
|
-
]
|
|
163
|
-
original_reprs = []
|
|
164
|
-
for es in enum_subclasses:
|
|
165
|
-
original_reprs.append(es)
|
|
166
|
-
es.__repr__ = new_repr
|
|
167
|
-
|
|
168
|
-
yield
|
|
169
|
-
|
|
170
|
-
for es, og_repr in zip(enum_subclasses, original_reprs):
|
|
171
|
-
es.__repr__ = og_repr
|
|
172
|
-
|
|
173
|
-
with _monkey_patch_enum_repr():
|
|
174
|
-
pprint(
|
|
175
|
-
instance,
|
|
176
|
-
indent_guides=False,
|
|
177
|
-
max_length=88,
|
|
178
|
-
expand_all=True,
|
|
179
|
-
console=console,
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
@click.command(name="job-run", cls=COMMAND_CLS, help="Get Job Run")
|
|
184
|
-
@click.option(
|
|
185
|
-
"--application-fqn",
|
|
186
|
-
"--application_fqn",
|
|
187
|
-
type=click.STRING,
|
|
188
|
-
default=None,
|
|
189
|
-
help="FQN of the application",
|
|
190
|
-
required=True,
|
|
191
|
-
)
|
|
192
|
-
@click.option(
|
|
193
|
-
"--job-run-name",
|
|
194
|
-
"--job_run_name",
|
|
195
|
-
type=click.STRING,
|
|
196
|
-
default=None,
|
|
197
|
-
help="Run name of the job",
|
|
198
|
-
required=True,
|
|
199
|
-
)
|
|
200
|
-
@handle_exception_wrapper
|
|
201
|
-
def get_job_run(application_fqn, job_run_name):
|
|
202
|
-
job_run = application_lib.get_job_run(
|
|
203
|
-
application_fqn=application_fqn, job_run_name=job_run_name
|
|
204
|
-
)
|
|
205
|
-
if CliConfig.get("json"):
|
|
206
|
-
print_json(data=job_run.dict())
|
|
207
|
-
else:
|
|
208
|
-
print_entity_obj("Job Run", job_run)
|
|
209
|
-
|
|
210
|
-
|
|
211
26
|
def get_get_command():
|
|
212
|
-
get_command.add_command(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
get_command.add_command(get_job_run)
|
|
27
|
+
get_command.add_command(kubeconfig_command)
|
|
28
|
+
if is_internal_env_set():
|
|
29
|
+
get_command.add_command(k8s_exec_credential_command)
|
|
216
30
|
return get_command
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Any, Dict, Optional
|
|
4
|
+
from urllib.parse import urlparse
|
|
5
|
+
|
|
6
|
+
import rich_click as click
|
|
7
|
+
|
|
8
|
+
from truefoundry.cli.const import COMMAND_CLS
|
|
9
|
+
from truefoundry.cli.util import handle_exception_wrapper
|
|
10
|
+
from truefoundry.common.session import Session
|
|
11
|
+
from truefoundry.deploy.cli.commands.utils import (
|
|
12
|
+
CONTEXT_NAME_FORMAT,
|
|
13
|
+
get_cluster_server_url,
|
|
14
|
+
get_kubeconfig_content,
|
|
15
|
+
get_kubeconfig_path,
|
|
16
|
+
)
|
|
17
|
+
from truefoundry.deploy.io.no_output_callback import NoOutputCallBack
|
|
18
|
+
from truefoundry.deploy.lib.session import login
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@click.command(
|
|
22
|
+
name="k8s-exec-credential",
|
|
23
|
+
cls=COMMAND_CLS,
|
|
24
|
+
help="Generate a Kubernetes exec credential for the specified cluster user",
|
|
25
|
+
)
|
|
26
|
+
@click.option(
|
|
27
|
+
"-c",
|
|
28
|
+
"--cluster",
|
|
29
|
+
type=str,
|
|
30
|
+
required=True,
|
|
31
|
+
help="The cluster id from TrueFoundry",
|
|
32
|
+
)
|
|
33
|
+
@handle_exception_wrapper
|
|
34
|
+
def k8s_exec_credential_command(cluster: str) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Generate a Kubernetes exec credential for the specified cluster.
|
|
37
|
+
This command retrieves the cluster server URL from the kubeconfig file,
|
|
38
|
+
"""
|
|
39
|
+
path = get_kubeconfig_path()
|
|
40
|
+
kubeconfig: Dict[str, Any] = get_kubeconfig_content(path=path)
|
|
41
|
+
server_url: Optional[str] = get_cluster_server_url(kubeconfig, cluster)
|
|
42
|
+
if not server_url:
|
|
43
|
+
raise click.ClickException(
|
|
44
|
+
f"\nContext {CONTEXT_NAME_FORMAT.format(cluster=cluster)!r} for cluster {cluster!r} not found in kubeconfig. \n\nPlease run 'tfy get kubeconfig --cluster {cluster}' first."
|
|
45
|
+
)
|
|
46
|
+
host: str = f"{urlparse(server_url).scheme}://{urlparse(server_url).netloc}"
|
|
47
|
+
login(host=host, output_hook=NoOutputCallBack())
|
|
48
|
+
|
|
49
|
+
session = Session.new()
|
|
50
|
+
token: str = session.access_token
|
|
51
|
+
|
|
52
|
+
exec_credential: Dict[str, Any] = {
|
|
53
|
+
"kind": "ExecCredential",
|
|
54
|
+
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
55
|
+
"spec": {},
|
|
56
|
+
"status": {
|
|
57
|
+
"expirationTimestamp": datetime.fromtimestamp(
|
|
58
|
+
session.token.exp, tz=timezone.utc
|
|
59
|
+
).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
60
|
+
"token": token,
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
print(json.dumps(exec_credential, indent=4))
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
from typing import Any, Dict, Optional
|
|
2
|
+
from urllib.parse import urljoin
|
|
3
|
+
|
|
4
|
+
import questionary
|
|
5
|
+
import rich_click as click
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from truefoundry.cli.const import COMMAND_CLS
|
|
9
|
+
from truefoundry.cli.util import handle_exception_wrapper
|
|
10
|
+
from truefoundry.common.session import Session
|
|
11
|
+
from truefoundry.deploy.cli.commands.utils import (
|
|
12
|
+
CONTEXT_NAME_FORMAT,
|
|
13
|
+
add_update_cluster_context,
|
|
14
|
+
get_cluster_server_url,
|
|
15
|
+
get_kubeconfig_content,
|
|
16
|
+
get_kubeconfig_path,
|
|
17
|
+
save_kubeconfig,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _select_cluster(cluster: Optional[str] = None) -> str:
|
|
24
|
+
"""
|
|
25
|
+
Retrieve available clusters and either return the specified one after validation
|
|
26
|
+
or allow the user to interactively select from the list.
|
|
27
|
+
"""
|
|
28
|
+
from truefoundry.deploy.lib.clients.servicefoundry_client import (
|
|
29
|
+
ServiceFoundryServiceClient,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
clusters = ServiceFoundryServiceClient().list_clusters()
|
|
33
|
+
|
|
34
|
+
if not clusters:
|
|
35
|
+
raise click.ClickException("No clusters found in your account.")
|
|
36
|
+
|
|
37
|
+
if cluster:
|
|
38
|
+
if not any(c.id == cluster for c in clusters):
|
|
39
|
+
raise click.ClickException(
|
|
40
|
+
f"Cluster {cluster} not found. Either it does not exist or you might not be autthorized to access it"
|
|
41
|
+
)
|
|
42
|
+
return cluster
|
|
43
|
+
|
|
44
|
+
choices = {cluster.id: cluster for cluster in clusters}
|
|
45
|
+
cluster = questionary.select(
|
|
46
|
+
"Available Clusters:", choices=list(choices.keys())
|
|
47
|
+
).ask()
|
|
48
|
+
if not cluster:
|
|
49
|
+
raise click.ClickException("No cluster selected.")
|
|
50
|
+
return cluster
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _construct_k8s_proxy_server(host: str, cluster: str) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Construct the Kubernetes proxy server URL.
|
|
56
|
+
"""
|
|
57
|
+
return urljoin(host, f"api/svc/v1/k8s/proxy/{cluster}")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _should_update_existing_context(cluster: str, kubeconfig: Dict[str, Any]) -> bool:
|
|
61
|
+
"""
|
|
62
|
+
Prompt the user whether to overwrite an existing kubeconfig context.
|
|
63
|
+
"""
|
|
64
|
+
server_url = get_cluster_server_url(kubeconfig, cluster)
|
|
65
|
+
if server_url is not None:
|
|
66
|
+
console.print(
|
|
67
|
+
f"\nContext {CONTEXT_NAME_FORMAT.format(cluster=cluster)!r} for cluster {cluster!r} already exists in kubeconfig.\n"
|
|
68
|
+
)
|
|
69
|
+
return click.confirm(
|
|
70
|
+
text="Do you want to update the context?", default=False, err=True
|
|
71
|
+
)
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@click.command(name="kubeconfig", cls=COMMAND_CLS)
|
|
76
|
+
@click.option(
|
|
77
|
+
"-c",
|
|
78
|
+
"--cluster",
|
|
79
|
+
type=str,
|
|
80
|
+
required=False,
|
|
81
|
+
help="The cluster id from TrueFoundry. If not provided, an interactive prompt will list available clusters",
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"--overwrite",
|
|
85
|
+
is_flag=True,
|
|
86
|
+
default=False,
|
|
87
|
+
show_default=True,
|
|
88
|
+
help="Overwrites existing cluster entry without prompting",
|
|
89
|
+
)
|
|
90
|
+
@handle_exception_wrapper
|
|
91
|
+
def kubeconfig_command(cluster: Optional[str] = None, overwrite: bool = False) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Update kubeconfig file to access cluster attached to TrueFoundry Control Plane.
|
|
94
|
+
|
|
95
|
+
By default, credentials are written to ~/.kube/config. You can provide an alternate path by setting the KUBECONFIG environment variable. If KUBECONFIG contains multiple paths, the first one is used.
|
|
96
|
+
"""
|
|
97
|
+
session = Session.new()
|
|
98
|
+
cluster = _select_cluster(cluster)
|
|
99
|
+
|
|
100
|
+
path = get_kubeconfig_path()
|
|
101
|
+
kubeconfig = get_kubeconfig_content(path=path)
|
|
102
|
+
|
|
103
|
+
if not overwrite and not _should_update_existing_context(cluster, kubeconfig):
|
|
104
|
+
console.print(
|
|
105
|
+
"Existing context found. Use '--overwrite' to force update the context."
|
|
106
|
+
)
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
k8s_proxy_server = _construct_k8s_proxy_server(session.tfy_host, cluster)
|
|
110
|
+
context_name = add_update_cluster_context(
|
|
111
|
+
kubeconfig,
|
|
112
|
+
cluster,
|
|
113
|
+
k8s_proxy_server,
|
|
114
|
+
exec_command=[
|
|
115
|
+
"tfy",
|
|
116
|
+
"--json",
|
|
117
|
+
"get",
|
|
118
|
+
"k8s-exec-credential",
|
|
119
|
+
"--cluster",
|
|
120
|
+
cluster,
|
|
121
|
+
],
|
|
122
|
+
envs={"TFY_INTERNAL": "1"},
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
save_kubeconfig(kubeconfig, path=path)
|
|
126
|
+
console.print(
|
|
127
|
+
f"\nUpdated kubeconfig at {str(path)!r} with context {context_name!r} for cluster {cluster!r}\n\n"
|
|
128
|
+
f"Run 'kubectl config use-context {context_name}' to use this context.\n"
|
|
129
|
+
)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
import yaml
|
|
6
|
+
|
|
7
|
+
DEFAULT_KUBECONFIG_PATH: Path = Path.home() / ".kube" / "config"
|
|
8
|
+
CLUSTER_NAME_FORMAT: str = "tfy-{cluster}-cluster"
|
|
9
|
+
USER_NAME_FORMAT: str = "tfy-{cluster}-user"
|
|
10
|
+
CONTEXT_NAME_FORMAT: str = "tfy-{cluster}-context"
|
|
11
|
+
KUBE_CONFIG_CONTENT = {
|
|
12
|
+
"apiVersion": "v1",
|
|
13
|
+
"kind": "Config",
|
|
14
|
+
"clusters": [],
|
|
15
|
+
"users": [],
|
|
16
|
+
"contexts": [],
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_kubeconfig_path() -> Path:
|
|
21
|
+
"""
|
|
22
|
+
Returns the kubeconfig path to use.
|
|
23
|
+
If KUBECONFIG is set, returns the first path from the environment variable.
|
|
24
|
+
Otherwise, returns the default kubeconfig path.
|
|
25
|
+
"""
|
|
26
|
+
kubeconfig_env = os.environ.get("KUBECONFIG")
|
|
27
|
+
if kubeconfig_env:
|
|
28
|
+
# Use the first path in KUBECONFIG if multiple are provided
|
|
29
|
+
first_path = kubeconfig_env.split(os.pathsep)[0]
|
|
30
|
+
return Path(first_path)
|
|
31
|
+
return DEFAULT_KUBECONFIG_PATH
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def get_kubeconfig_content(path: Path = DEFAULT_KUBECONFIG_PATH) -> Dict[str, Any]:
|
|
35
|
+
if path.exists():
|
|
36
|
+
with open(path, "r") as f:
|
|
37
|
+
return yaml.safe_load(f) or KUBE_CONFIG_CONTENT
|
|
38
|
+
else:
|
|
39
|
+
return KUBE_CONFIG_CONTENT
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def save_kubeconfig(config: Dict[str, Any], path: Path) -> None:
|
|
43
|
+
config["apiVersion"] = "v1"
|
|
44
|
+
config["kind"] = "Config"
|
|
45
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
46
|
+
with open(path, "w") as f:
|
|
47
|
+
yaml.safe_dump(config, f, default_flow_style=False)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def add_update_cluster_context(
|
|
51
|
+
config: Dict[str, Any],
|
|
52
|
+
cluster: str,
|
|
53
|
+
server_url: str,
|
|
54
|
+
exec_command: List[str],
|
|
55
|
+
envs: Optional[Dict[str, str]] = None,
|
|
56
|
+
) -> str:
|
|
57
|
+
"""
|
|
58
|
+
Adds a new cluster context to the given kubeconfig dictionary using exec-based authentication.
|
|
59
|
+
"""
|
|
60
|
+
cluster_name: str = CLUSTER_NAME_FORMAT.format(cluster=cluster)
|
|
61
|
+
user_name: str = USER_NAME_FORMAT.format(cluster=cluster)
|
|
62
|
+
context_name: str = CONTEXT_NAME_FORMAT.format(cluster=cluster)
|
|
63
|
+
|
|
64
|
+
# Add or update cluster
|
|
65
|
+
config["clusters"] = [
|
|
66
|
+
c for c in config.get("clusters", []) if c["name"] != cluster_name
|
|
67
|
+
]
|
|
68
|
+
config["clusters"].append(
|
|
69
|
+
{
|
|
70
|
+
"name": cluster_name,
|
|
71
|
+
"cluster": {"server": server_url},
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Add or update user with exec command
|
|
76
|
+
config["users"] = [u for u in config.get("users", []) if u["name"] != user_name]
|
|
77
|
+
config["users"].append(
|
|
78
|
+
{
|
|
79
|
+
"name": user_name,
|
|
80
|
+
"user": {
|
|
81
|
+
"exec": {
|
|
82
|
+
"apiVersion": "client.authentication.k8s.io/v1beta1",
|
|
83
|
+
"command": exec_command[0],
|
|
84
|
+
"args": exec_command[1:],
|
|
85
|
+
"env": (
|
|
86
|
+
[{"name": k, "value": v} for k, v in envs.items()]
|
|
87
|
+
if envs
|
|
88
|
+
else []
|
|
89
|
+
),
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
# Add or update context
|
|
96
|
+
config["contexts"] = [
|
|
97
|
+
c for c in config.get("contexts", []) if c["name"] != context_name
|
|
98
|
+
]
|
|
99
|
+
config["contexts"].append(
|
|
100
|
+
{"name": context_name, "context": {"cluster": cluster_name, "user": user_name}}
|
|
101
|
+
)
|
|
102
|
+
return context_name
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_cluster_context(
|
|
106
|
+
config: Dict[str, Any], cluster: str
|
|
107
|
+
) -> Optional[Dict[str, Any]]:
|
|
108
|
+
cluster_name: str = CLUSTER_NAME_FORMAT.format(cluster=cluster)
|
|
109
|
+
return next(
|
|
110
|
+
(c for c in config.get("clusters", []) if c["name"] == cluster_name), None
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_cluster_server_url(config: Dict[str, Any], cluster: str) -> Optional[str]:
|
|
115
|
+
cluster: Optional[Dict[str, Any]] = get_cluster_context(config, cluster)
|
|
116
|
+
if cluster:
|
|
117
|
+
return cluster["cluster"].get("server")
|
|
118
|
+
return None
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from typing import Any, List, Optional
|
|
2
|
+
|
|
3
|
+
from truefoundry.deploy.io.output_callback import OutputCallBack
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class NoOutputCallBack(OutputCallBack):
|
|
7
|
+
def print_header(self, line: Any) -> None:
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
def _print_separator(self) -> None:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
def print_line(self, line: str) -> None:
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
def print_lines_in_panel(
|
|
17
|
+
self, lines: List[str], header: Optional[str] = None
|
|
18
|
+
) -> None:
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def print_code_in_panel(
|
|
22
|
+
self, lines: List[str], header: Optional[str] = None
|
|
23
|
+
) -> None:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
def print(self, line: Any) -> None:
|
|
27
|
+
pass
|
|
@@ -32,6 +32,7 @@ from truefoundry.deploy._autogen import models as autogen_models
|
|
|
32
32
|
from truefoundry.deploy.io.output_callback import OutputCallBack
|
|
33
33
|
from truefoundry.deploy.lib.model.entity import (
|
|
34
34
|
Application,
|
|
35
|
+
Cluster,
|
|
35
36
|
CreateDockerRepositoryResponse,
|
|
36
37
|
Deployment,
|
|
37
38
|
DockerRegistryCredentials,
|
|
@@ -110,6 +111,13 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
|
|
|
110
111
|
)
|
|
111
112
|
return request_handling(response)
|
|
112
113
|
|
|
114
|
+
@check_min_cli_version
|
|
115
|
+
def list_clusters(self) -> List[Cluster]:
|
|
116
|
+
url = f"{self._api_server_url}/{VERSION_PREFIX}/clusters"
|
|
117
|
+
response = session_with_retries().get(url, headers=self._get_headers())
|
|
118
|
+
response = request_handling(response)
|
|
119
|
+
return parse_obj_as(List[Cluster], response["data"])
|
|
120
|
+
|
|
113
121
|
@check_min_cli_version
|
|
114
122
|
def list_workspaces(
|
|
115
123
|
self,
|
|
@@ -70,7 +70,9 @@ def login(
|
|
|
70
70
|
else:
|
|
71
71
|
auth_service = AuthServiceClient.from_tfy_host(tfy_host=host)
|
|
72
72
|
# interactive login
|
|
73
|
-
token = _login_with_device_code(
|
|
73
|
+
token = _login_with_device_code(
|
|
74
|
+
base_url=host, auth_service=auth_service, output_hook=output_hook
|
|
75
|
+
)
|
|
74
76
|
|
|
75
77
|
cred_file_content = CredentialsFileContent(
|
|
76
78
|
access_token=token.access_token,
|
|
@@ -97,7 +99,7 @@ def logout(
|
|
|
97
99
|
|
|
98
100
|
|
|
99
101
|
def get_access_token():
|
|
100
|
-
#
|
|
102
|
+
# Get the access token from the session
|
|
101
103
|
session = Session.new()
|
|
102
104
|
return session.access_token
|
|
103
105
|
|
|
@@ -189,30 +189,6 @@ def _deploy_wait_handler( # noqa: C901
|
|
|
189
189
|
return last_status_printed
|
|
190
190
|
|
|
191
191
|
|
|
192
|
-
def _warn_when_gpu_selected_without_cuda(component: Component):
|
|
193
|
-
is_python_build_without_cuda = (
|
|
194
|
-
hasattr(component, "image")
|
|
195
|
-
and isinstance(component.image, autogen_models.Build)
|
|
196
|
-
and isinstance(component.image.build_spec, autogen_models.PythonBuild)
|
|
197
|
-
and not component.image.build_spec.cuda_version
|
|
198
|
-
)
|
|
199
|
-
uses_gpu = (
|
|
200
|
-
hasattr(component, "resources")
|
|
201
|
-
and isinstance(component, autogen_models.Resources)
|
|
202
|
-
and component.resources.gpu_count > 0
|
|
203
|
-
)
|
|
204
|
-
if is_python_build_without_cuda and uses_gpu:
|
|
205
|
-
logger.warning(
|
|
206
|
-
"Warning: `gpu_count` is greater than 0 in `Resources` (i.e. `resources.gpu_count`) "
|
|
207
|
-
"but no `cuda_version` was passed to `PythonBuild` "
|
|
208
|
-
"(i.e. `image.build_spec.cuda_version`). "
|
|
209
|
-
"Your application might optionally need CUDA toolkit installed "
|
|
210
|
-
"to utilize the GPU. You can choose one by passing one of "
|
|
211
|
-
"`truefoundry.deploy.CUDAVersion` in `PythonBuild` instance. "
|
|
212
|
-
"\n\nE.g.\n```\nPythonBuild(..., cuda_version=CUDAVersion.CUDA_11_3_CUDNN8)\n```"
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
|
|
216
192
|
def _resolve_workspace_fqn(
|
|
217
193
|
component: Component, workspace_fqn: Optional[str] = None
|
|
218
194
|
) -> str:
|
|
@@ -266,7 +242,6 @@ def deploy_component(
|
|
|
266
242
|
wait: bool = True,
|
|
267
243
|
force: bool = False,
|
|
268
244
|
) -> Deployment:
|
|
269
|
-
_warn_when_gpu_selected_without_cuda(component=component)
|
|
270
245
|
workspace_fqn = _resolve_workspace_fqn(
|
|
271
246
|
component=component, workspace_fqn=workspace_fqn
|
|
272
247
|
)
|
|
@@ -274,10 +249,11 @@ def deploy_component(
|
|
|
274
249
|
workspace_id = get_workspace_by_fqn(workspace_fqn).id
|
|
275
250
|
if isinstance(component, autogen_models.ApplicationSet):
|
|
276
251
|
updated_component = component.copy(deep=True)
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
252
|
+
if updated_component.components:
|
|
253
|
+
for i, subcomponent in enumerate(updated_component.components):
|
|
254
|
+
updated_component.components[i] = _handle_if_local_source(
|
|
255
|
+
component=subcomponent, workspace_fqn=workspace_fqn
|
|
256
|
+
)
|
|
281
257
|
else:
|
|
282
258
|
updated_component = _handle_if_local_source(
|
|
283
259
|
component=component, workspace_fqn=workspace_fqn
|
|
@@ -2,7 +2,6 @@ import enum
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
import warnings
|
|
5
|
-
from collections.abc import Mapping
|
|
6
5
|
from typing import Literal, Optional, Union
|
|
7
6
|
|
|
8
7
|
from truefoundry.common.warnings import TrueFoundryDeprecationWarning
|
|
@@ -15,63 +14,6 @@ from truefoundry.pydantic_v1 import (
|
|
|
15
14
|
validator,
|
|
16
15
|
)
|
|
17
16
|
|
|
18
|
-
LEGACY_GPU_TYPE_COUNT_WARNING_MESSAGE_TEMPLATE = """
|
|
19
|
-
---------
|
|
20
|
-
The `gpu_count` and `gpu_type` fields are deprecated. Please remove these fields
|
|
21
|
-
from your deployment Python script or YAML Spec.
|
|
22
|
-
|
|
23
|
-
If you are using Python SDK, add GPUs in the following way:
|
|
24
|
-
|
|
25
|
-
```
|
|
26
|
-
from truefoundry.deploy import NvidiaGPU, Resources
|
|
27
|
-
...
|
|
28
|
-
|
|
29
|
-
resources=Resources(
|
|
30
|
-
...
|
|
31
|
-
devices=[NvidiaGPU(name="{gpu_type}", count={gpu_count})],
|
|
32
|
-
)
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
If you are using YAML Spec to deploy, add GPUs in the following way:
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
resources:
|
|
39
|
-
devices:
|
|
40
|
-
- type: nvidia_gpu
|
|
41
|
-
name: {gpu_type}
|
|
42
|
-
count: {gpu_count}
|
|
43
|
-
```
|
|
44
|
-
---------
|
|
45
|
-
"""
|
|
46
|
-
|
|
47
|
-
LEGACY_GPU_COUNT_WARNING_MESSAGE_TEMPLATE = """
|
|
48
|
-
---------
|
|
49
|
-
The `gpu_count` field is deprecated. Please remove this field from your
|
|
50
|
-
deployment Python script or YAML Spec.
|
|
51
|
-
|
|
52
|
-
If you are using Python SDK, add GPUs in the following way:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
from truefoundry.deploy import NvidiaGPU, Resources
|
|
56
|
-
...
|
|
57
|
-
|
|
58
|
-
resources=Resources(
|
|
59
|
-
...
|
|
60
|
-
devices=[NvidiaGPU(count={gpu_count})],
|
|
61
|
-
)
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
If you are using YAML Spec to deploy, add GPUs in the following way:
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
resources:
|
|
68
|
-
devices:
|
|
69
|
-
- type: nvidia_gpu
|
|
70
|
-
count: {gpu_count}
|
|
71
|
-
```
|
|
72
|
-
---------
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
17
|
AUTO_DISCOVERED_REQUIREMENTS_TXT_WARNING_MESSAGE_TEMPLATE = """\
|
|
76
18
|
Using automatically discovered {requirements_txt_path} as the requirements file.
|
|
77
19
|
This auto discovery behavior is deprecated and will be removed in a future release.
|
|
@@ -304,35 +246,7 @@ class Port(models.Port, PatchedModelBase):
|
|
|
304
246
|
|
|
305
247
|
|
|
306
248
|
class Resources(models.Resources, PatchedModelBase):
|
|
307
|
-
|
|
308
|
-
def warn_gpu_count_type_depreciation(cls, values):
|
|
309
|
-
gpu_count = values.get("gpu_count")
|
|
310
|
-
gpu_type = None
|
|
311
|
-
node = values.get("node")
|
|
312
|
-
if node:
|
|
313
|
-
if isinstance(node, NodeSelector):
|
|
314
|
-
gpu_type = node.gpu_type
|
|
315
|
-
elif isinstance(node, Mapping):
|
|
316
|
-
gpu_count = node.get("gpu_type")
|
|
317
|
-
|
|
318
|
-
if gpu_count and gpu_type:
|
|
319
|
-
warnings.warn(
|
|
320
|
-
LEGACY_GPU_TYPE_COUNT_WARNING_MESSAGE_TEMPLATE.format(
|
|
321
|
-
gpu_type=gpu_type,
|
|
322
|
-
gpu_count=gpu_count,
|
|
323
|
-
),
|
|
324
|
-
category=TrueFoundryDeprecationWarning,
|
|
325
|
-
stacklevel=2,
|
|
326
|
-
)
|
|
327
|
-
elif gpu_count:
|
|
328
|
-
warnings.warn(
|
|
329
|
-
LEGACY_GPU_COUNT_WARNING_MESSAGE_TEMPLATE.format(
|
|
330
|
-
gpu_count=gpu_count,
|
|
331
|
-
),
|
|
332
|
-
category=TrueFoundryDeprecationWarning,
|
|
333
|
-
stacklevel=2,
|
|
334
|
-
)
|
|
335
|
-
return values
|
|
249
|
+
pass
|
|
336
250
|
|
|
337
251
|
|
|
338
252
|
class Param(models.Param, PatchedModelBase):
|
|
@@ -397,7 +311,6 @@ class VolumeMount(models.VolumeMount, PatchedModelBase):
|
|
|
397
311
|
|
|
398
312
|
class NodeSelector(models.NodeSelector, PatchedModelBase):
|
|
399
313
|
type: Literal["node_selector"] = "node_selector"
|
|
400
|
-
gpu_type: Optional[Union[GPUType, str]] = None
|
|
401
314
|
|
|
402
315
|
|
|
403
316
|
class NodepoolSelector(models.NodepoolSelector, PatchedModelBase):
|
|
@@ -573,6 +486,10 @@ class SlackWebhook(models.SlackWebhook, PatchedModelBase):
|
|
|
573
486
|
type: Literal["slack-webhook"] = "slack-webhook"
|
|
574
487
|
|
|
575
488
|
|
|
489
|
+
class SlackBot(models.SlackBot, PatchedModelBase):
|
|
490
|
+
type: Literal["slack-bot"] = "slack-bot"
|
|
491
|
+
|
|
492
|
+
|
|
576
493
|
class SparkJobScalaEntrypoint(models.SparkJobScalaEntrypoint, PatchedModelBase):
|
|
577
494
|
type: Literal["scala"] = "scala"
|
|
578
495
|
|
truefoundry/ml/prompt_utils.py
CHANGED
|
@@ -49,17 +49,22 @@ def render_prompt(
|
|
|
49
49
|
|
|
50
50
|
# Merge parameters from model_configuration and extra_parameters
|
|
51
51
|
model_configuration = prompt_template.model_configuration
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
if model_configuration:
|
|
53
|
+
model = model_configuration.model
|
|
54
|
+
parameters = {
|
|
55
|
+
**(
|
|
56
|
+
model_configuration.parameters.dict()
|
|
57
|
+
if model_configuration.parameters
|
|
58
|
+
else {}
|
|
59
|
+
),
|
|
60
|
+
**(model_configuration.extra_parameters or {}),
|
|
61
|
+
}
|
|
62
|
+
else:
|
|
63
|
+
model = None
|
|
64
|
+
parameters = {}
|
|
60
65
|
|
|
61
66
|
return {
|
|
62
67
|
"messages": rendered_messages,
|
|
63
|
-
"model":
|
|
68
|
+
"model": model,
|
|
64
69
|
"parameters": {k: v for k, v in parameters.items() if v is not None},
|
|
65
70
|
}
|
truefoundry/workflow/__init__.py
CHANGED
|
@@ -2,7 +2,10 @@ try:
|
|
|
2
2
|
import fsspec
|
|
3
3
|
from flytekit import task as _
|
|
4
4
|
except ImportError:
|
|
5
|
-
print(
|
|
5
|
+
print(
|
|
6
|
+
"To use workflows, please run 'pip install truefoundry[workflow]'. "
|
|
7
|
+
"Note: The `workflow` feature is only available for Python 3.9 to 3.12"
|
|
8
|
+
)
|
|
6
9
|
|
|
7
10
|
from flytekit import conditional
|
|
8
11
|
from flytekit.types.directory import FlyteDirectory
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: truefoundry
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: TrueFoundry CLI
|
|
5
5
|
Author-email: TrueFoundry Team <abhishek@truefoundry.com>
|
|
6
6
|
Requires-Python: <3.14,>=3.8.1
|
|
@@ -30,12 +30,12 @@ Requires-Dist: requirements-parser<0.12.0,>=0.11.0
|
|
|
30
30
|
Requires-Dist: rich-click<2.0.0,>=1.2.1
|
|
31
31
|
Requires-Dist: rich<14.0.0,>=13.7.1
|
|
32
32
|
Requires-Dist: tqdm<5.0.0,>=4.0.0
|
|
33
|
-
Requires-Dist: truefoundry-sdk==0.0.
|
|
33
|
+
Requires-Dist: truefoundry-sdk==0.0.15
|
|
34
34
|
Requires-Dist: typing-extensions>=4.0
|
|
35
35
|
Requires-Dist: urllib3<3,>=1.26.18
|
|
36
36
|
Requires-Dist: yq<4.0.0,>=3.1.0
|
|
37
37
|
Provides-Extra: workflow
|
|
38
|
-
Requires-Dist: flytekit==1.
|
|
38
|
+
Requires-Dist: flytekit==1.15.3; (python_version >= '3.9' and python_version < '3.13') and extra == 'workflow'
|
|
39
39
|
Description-Content-Type: text/markdown
|
|
40
40
|
|
|
41
41
|
# TrueFoundry
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
truefoundry/__init__.py,sha256=VVpO-Awh1v93VOURe7hank8QpeSPc0dCykwr14GOFsw,967
|
|
2
|
-
truefoundry/_client.py,sha256=
|
|
2
|
+
truefoundry/_client.py,sha256=VQEfRvPE7nuqq--q28cpmnIYPG3RH52RSifIFOzzvTg,1420
|
|
3
3
|
truefoundry/logger.py,sha256=u-YCNjg5HBwE70uQcpjIG64Ghos-K2ulTWaxC03BSj4,714
|
|
4
4
|
truefoundry/pydantic_v1.py,sha256=jSuhGtz0Mbk1qYu8jJ1AcnIDK4oxUsdhALc4spqstmM,345
|
|
5
5
|
truefoundry/version.py,sha256=bqiT4Q-VWrTC6P4qfK43mez-Ppf-smWfrl6DcwV7mrw,137
|
|
@@ -28,7 +28,7 @@ truefoundry/autodeploy/utils/client.py,sha256=PvbSkfgAjAogGjisinqmh4mP4svowxAC0I
|
|
|
28
28
|
truefoundry/autodeploy/utils/diff.py,sha256=Ef8Y-VffDKel_-q-GxRam6gqiv8qTLMcqVg6iifXfcA,5358
|
|
29
29
|
truefoundry/autodeploy/utils/pydantic_compat.py,sha256=hEAUy5kLjhPdzw7yGZ2iXGMXbbMVXVlGzIofmyHafXQ,412
|
|
30
30
|
truefoundry/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
truefoundry/cli/__main__.py,sha256=
|
|
31
|
+
truefoundry/cli/__main__.py,sha256=oJgbKUQbT5SDhqdtvhGw-WG6s0Kg3jiMiO0oz7bgOrc,3525
|
|
32
32
|
truefoundry/cli/config.py,sha256=tf8w4UfVzcC6eYkENvuuCPYt_V3sqVpO1bclORV9tAk,206
|
|
33
33
|
truefoundry/cli/console.py,sha256=9-dMy4YPisCJQziRKTg8Qa0UJnOGl1soiUnJjsnLDvE,242
|
|
34
34
|
truefoundry/cli/const.py,sha256=dVHPo1uAiDSSMXwXoT2mR5kNQjExT98QNVRz98Hz_Ts,510
|
|
@@ -39,18 +39,18 @@ truefoundry/common/auth_service_client.py,sha256=N3YxKlx63r6cPZqbgb2lqBOPI69ShB7
|
|
|
39
39
|
truefoundry/common/constants.py,sha256=rXWVNhy2j8UVmkPxpjaftL6mWRYILLzKLyCAAXKJy6Q,3247
|
|
40
40
|
truefoundry/common/credential_file_manager.py,sha256=1yEk1Zm2xS4G0VDFwKSZ4w0VUrcPWQ1nJnoBaz9xyKA,4251
|
|
41
41
|
truefoundry/common/credential_provider.py,sha256=_OhJ2XFlDaVsrUO-FyywxctcGGqDdC2pgcvwEKqQD0Q,4071
|
|
42
|
-
truefoundry/common/entities.py,sha256=
|
|
42
|
+
truefoundry/common/entities.py,sha256=ko33kesGy3vI9NJ5Ganq8HpnaURTOHictr6h75764no,3893
|
|
43
43
|
truefoundry/common/exceptions.py,sha256=jkU0N7hV_P-EhXeud4I5vuB9glXXZSWPf8LcH04mSbw,459
|
|
44
44
|
truefoundry/common/request_utils.py,sha256=e9qrAQ1MutU7JALDKcucmNd0KQEVBqgW3yx0w1zeHIU,5700
|
|
45
45
|
truefoundry/common/servicefoundry_client.py,sha256=2fYhdVPSvLXz5C5tosOq86JD8WM3IRUIy1VO9deDxZI,3340
|
|
46
|
-
truefoundry/common/session.py,sha256=
|
|
46
|
+
truefoundry/common/session.py,sha256=d9l3TEBpqVP4mr4mTGY1qVxc815skzMlNNdw14otg34,2923
|
|
47
47
|
truefoundry/common/storage_provider_utils.py,sha256=yURhMw8k0FLFvaviRHDiifhvc6GnuQwGMC9Qd2uM440,10934
|
|
48
48
|
truefoundry/common/types.py,sha256=BMJFCsR1lPJAw66IQBSvLyV4I6o_x5oj78gVsUa9si8,188
|
|
49
49
|
truefoundry/common/utils.py,sha256=yEQtJW2fT9xbNpRhfRoD9hvhGw-FgGS3agh1oZptmjg,6379
|
|
50
50
|
truefoundry/common/warnings.py,sha256=rs6BHwk7imQYedo07iwh3TWEOywAR3Lqhj0AY4khByg,504
|
|
51
|
-
truefoundry/deploy/__init__.py,sha256=
|
|
51
|
+
truefoundry/deploy/__init__.py,sha256=6D22iiCgd5xlzBaG34q9Cx4rGgwf5qIAKQrOCgaCXYY,2746
|
|
52
52
|
truefoundry/deploy/python_deploy_codegen.py,sha256=AainOFR20XvhNeztJkLPWGZ40lAT_nwc-ZmG77Kum4o,6525
|
|
53
|
-
truefoundry/deploy/_autogen/models.py,sha256=
|
|
53
|
+
truefoundry/deploy/_autogen/models.py,sha256=yIGmwEptVUIM1R5DOL8JYp2-8T-YDDumADS4ZAFoEcI,71727
|
|
54
54
|
truefoundry/deploy/builder/__init__.py,sha256=nGQiR3r16iumRy7xbVQ6q-k0EApmijspsfVpXDE-9po,4953
|
|
55
55
|
truefoundry/deploy/builder/constants.py,sha256=amUkHoHvVKzGv0v_knfiioRuKiJM0V0xW0diERgWiI0,508
|
|
56
56
|
truefoundry/deploy/builder/docker_service.py,sha256=sm7GWeIqyrKaZpxskdLejZlsxcZnM3BTDJr6orvPN4E,3948
|
|
@@ -69,7 +69,9 @@ truefoundry/deploy/cli/commands/create_command.py,sha256=rCajvQvAfZU10nDZOYpRACb
|
|
|
69
69
|
truefoundry/deploy/cli/commands/delete_command.py,sha256=8SriRwg5mEHL4zP0mdDjJ0cKWJTU_1Pjq3450zMr9tk,3889
|
|
70
70
|
truefoundry/deploy/cli/commands/deploy_command.py,sha256=8aTBvzPaT9xg6KPmpcpqJlmdj4yXzWUfAy6slcoPN74,4123
|
|
71
71
|
truefoundry/deploy/cli/commands/deploy_init_command.py,sha256=g-jBfrEmhZ0TDWsyqPDn4K6q33EqJSGmBTt1eMYig-w,600
|
|
72
|
-
truefoundry/deploy/cli/commands/get_command.py,sha256=
|
|
72
|
+
truefoundry/deploy/cli/commands/get_command.py,sha256=bR8tAjQQhimzaTQ57L6BPJwcxQ_SGWCF5CqHDpxgG90,837
|
|
73
|
+
truefoundry/deploy/cli/commands/k8s_exec_credential_command.py,sha256=EknpdufMAEnjSGMG7a-Jj7tkoiS5zmbJRREafb14Alw,2160
|
|
74
|
+
truefoundry/deploy/cli/commands/kubeconfig_command.py,sha256=3u3A_scsdd1UnqxKQgGg8ivZArZKQYVV0Z-ATotbPPc,4176
|
|
73
75
|
truefoundry/deploy/cli/commands/list_command.py,sha256=zKu_JWY35eMIzgSNFYtmwi2uezZ4k-8yk3C1Vqsvshc,4470
|
|
74
76
|
truefoundry/deploy/cli/commands/login_command.py,sha256=EHZH3cDQdbVWMkuFxd7JfB0B0Dr89sUmt214ICBmspY,1024
|
|
75
77
|
truefoundry/deploy/cli/commands/logout_command.py,sha256=u3kfrEp0ETbrz40KjD4GCC3XEZ5YRAlrca_Df4U_mk0,536
|
|
@@ -79,21 +81,23 @@ truefoundry/deploy/cli/commands/patch_command.py,sha256=wA95khMO9uVz8SaJlgYMUwaX
|
|
|
79
81
|
truefoundry/deploy/cli/commands/redeploy_command.py,sha256=JNeSK3itsirrKYJh3q-Cc10pCtYR9bojsvI8up6_0wI,1018
|
|
80
82
|
truefoundry/deploy/cli/commands/terminate_comand.py,sha256=UKhOdbAej8ubX3q44vpLrOotAcvH4vHpRZJQrRf_AfM,1077
|
|
81
83
|
truefoundry/deploy/cli/commands/trigger_command.py,sha256=_qSl-AShepZpbGUGTfLfJGd74VJJ_wd3eXYt2DfxIFo,4716
|
|
84
|
+
truefoundry/deploy/cli/commands/utils.py,sha256=Fftlayq5PbLV7lqFVIoMy_uBoIKe2_XpX4BEyQnliC4,3670
|
|
82
85
|
truefoundry/deploy/core/__init__.py,sha256=j61bMWj4BkWihdssKMSFhieo7afJDtpc7qO7zk1rDB4,140
|
|
83
86
|
truefoundry/deploy/core/login.py,sha256=N2VrW3nlBzoyoYulkipxwQvCpjBhi3sfsmhxK1ktWhg,236
|
|
84
87
|
truefoundry/deploy/core/logout.py,sha256=TpWLq4_DsxYS5GX2OJQGDhekNOfiOLb-vO5khQueHXw,80
|
|
85
88
|
truefoundry/deploy/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
|
+
truefoundry/deploy/io/no_output_callback.py,sha256=aXzVwDb-LrSA-2ApsQb5ZVvS6kC0phzqmXrnVfu-xoU,613
|
|
86
90
|
truefoundry/deploy/io/output_callback.py,sha256=_q79-dpFxnU762VPM9Ryy2gnuJnIotZ2_dylgv__ti8,777
|
|
87
91
|
truefoundry/deploy/io/rich_output_callback.py,sha256=m99RodkILXCgy_LJujEcojbpW1tL0H5Fjb0lqe6X_PQ,958
|
|
88
92
|
truefoundry/deploy/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
93
|
truefoundry/deploy/lib/const.py,sha256=Wg0GDnfFu-g1fJr4lU80NH2ULw0R0dYjV7LnW-PbOeM,173
|
|
90
94
|
truefoundry/deploy/lib/logs_utils.py,sha256=SQxRv3jDDmgHdOUMhlMaAPGYskybnBUMpst7QU_i_sc,1469
|
|
91
95
|
truefoundry/deploy/lib/messages.py,sha256=8424kj3kqCyDCX5Nr2WJZZ_UEutPoaSs_y2f9-O4yy8,1001
|
|
92
|
-
truefoundry/deploy/lib/session.py,sha256
|
|
96
|
+
truefoundry/deploy/lib/session.py,sha256=fLdgR6ZDp8-hFl5NTON4ngnWLsMzGxvKtfpDOOw_7lo,4963
|
|
93
97
|
truefoundry/deploy/lib/util.py,sha256=J7r8San2wKo48A7-BlH2-OKTlBO67zlPjLEhMsL8os0,1059
|
|
94
98
|
truefoundry/deploy/lib/win32.py,sha256=1RcvPTdlOAJ48rt8rCbE2Ufha2ztRqBAE9dueNXArrY,5009
|
|
95
99
|
truefoundry/deploy/lib/clients/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
|
-
truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=
|
|
100
|
+
truefoundry/deploy/lib/clients/servicefoundry_client.py,sha256=fmRlPYCimk1ZLbMgdzfJVCbcKRCVnFYL5T3j2uJA0Tc,27037
|
|
97
101
|
truefoundry/deploy/lib/dao/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
102
|
truefoundry/deploy/lib/dao/application.py,sha256=oMszpueXPUfTUuN_XdKwoRjQyqAgWHhZ-10cbprCVdM,9226
|
|
99
103
|
truefoundry/deploy/lib/dao/apply.py,sha256=5IFERe5sLmZGlavaKTIxL4xPHAme4ZS2Ww0a2rKTyT0,3029
|
|
@@ -101,14 +105,14 @@ truefoundry/deploy/lib/dao/delete.py,sha256=uPL2psqWNw2O0oDikXJOlVxmG8n5d3Z0Ia9q
|
|
|
101
105
|
truefoundry/deploy/lib/dao/version.py,sha256=AtdW_4O1DPUKdfv2qy6iUJsZ_95vM6z0AqeEy3WDKs8,1130
|
|
102
106
|
truefoundry/deploy/lib/dao/workspace.py,sha256=6YvfCgWDzAULI3Q6JswyZmP1CwJ5rM-ANsIFkbQia0Q,2349
|
|
103
107
|
truefoundry/deploy/lib/model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
|
-
truefoundry/deploy/lib/model/entity.py,sha256=
|
|
108
|
+
truefoundry/deploy/lib/model/entity.py,sha256=Bp9sLB-M5INCpw5lPmFdygHWS1zvnLicnSiSCi2iqhQ,8591
|
|
105
109
|
truefoundry/deploy/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
106
110
|
truefoundry/deploy/v2/lib/__init__.py,sha256=WEiVMZXOVljzEE3tpGJil14liIn_PCDoACJ6b3tZ6sI,188
|
|
107
|
-
truefoundry/deploy/v2/lib/deploy.py,sha256=
|
|
111
|
+
truefoundry/deploy/v2/lib/deploy.py,sha256=EOqVQTCMrvs_Kwp8Ft0FZYlRErPuFq4eDBDR1qEZ7_E,11767
|
|
108
112
|
truefoundry/deploy/v2/lib/deploy_workflow.py,sha256=G5BzMIbap8pgDX1eY-TITruUxQdkKhYtBmRwLL6lDeY,14342
|
|
109
113
|
truefoundry/deploy/v2/lib/deployable_patched_models.py,sha256=xbHFD3pURflvCm8EODPvjfvRrv67mlSrjPUknY8SMB8,4060
|
|
110
114
|
truefoundry/deploy/v2/lib/models.py,sha256=ogc1UYs1Z2nBdGSKCrde9sk8d0GxFKMkem99uqO5CmM,1148
|
|
111
|
-
truefoundry/deploy/v2/lib/patched_models.py,sha256=
|
|
115
|
+
truefoundry/deploy/v2/lib/patched_models.py,sha256=8ib9Y7b4-DoEml2zCv3V7QIqh4tLJUjzPj1AWomwvag,14775
|
|
112
116
|
truefoundry/deploy/v2/lib/source.py,sha256=d6-8_6Zn5koBglqrBrY6ZLG_7yyPuLdyEmK4iZTw6xY,9405
|
|
113
117
|
truefoundry/ml/__init__.py,sha256=EEEHV7w58Krpo_W9Chd8Y3TdItfFO3LI6j6Izqc4-P8,2219
|
|
114
118
|
truefoundry/ml/constants.py,sha256=vDq72d4C9FSWqr9MMdjgTF4TuyNFApvo_6RVsSeAjB4,2837
|
|
@@ -121,7 +125,7 @@ truefoundry/ml/logger.py,sha256=VT-BF3BnBYTWVq87O58F0c8uXMu94gYzsiFlGY3_7Ao,458
|
|
|
121
125
|
truefoundry/ml/mlfoundry_api.py,sha256=WiIVpJRylBn8NrcAmXClQnavffqI9fhY5h-8Vwx4RKo,59872
|
|
122
126
|
truefoundry/ml/mlfoundry_run.py,sha256=34yyQqgpG6EfrAJd37vkbCjrFoHkhvbOAxxSQcSWPtY,44320
|
|
123
127
|
truefoundry/ml/model_framework.py,sha256=nVbKTtKDRBdLzt7Wrg5_vJKZ-awHbISGvL73s-V9egU,18975
|
|
124
|
-
truefoundry/ml/prompt_utils.py,sha256=
|
|
128
|
+
truefoundry/ml/prompt_utils.py,sha256=8FueyElVTXLnLtC3O6hKsW_snocArr_B8KG3Qv6eFIQ,2651
|
|
125
129
|
truefoundry/ml/run_utils.py,sha256=0W208wSLUrbdfk2pjNcZlkUi9bNxG2JORqoe-5rVqHI,2423
|
|
126
130
|
truefoundry/ml/session.py,sha256=2v8JjFyrcKzKQ8qsTaZaQTqlxl-in9odIMyWWMEqWNY,4436
|
|
127
131
|
truefoundry/ml/validation_utils.py,sha256=rmIBNcpmnwq2jeje6uFg8uY-rL4JG_RV6mc6BlujVWg,12150
|
|
@@ -362,7 +366,7 @@ truefoundry/ml/log_types/image/constants.py,sha256=wLtGEOA4T5fZHSlOXPuNDLX3lpbCt
|
|
|
362
366
|
truefoundry/ml/log_types/image/image.py,sha256=sa0tBHdyluC8bELXY16E0HgFrUDnDBxHrteix4BFXcs,12479
|
|
363
367
|
truefoundry/ml/log_types/image/image_normalizer.py,sha256=vrzfuSpVGgIxw_Q2sbFe7kQ_JpAndX0bMwC7wtfi41g,3104
|
|
364
368
|
truefoundry/ml/log_types/image/types.py,sha256=inFQlyAyDvZtfliFpENirNCm1XO9beyZ8DNn97DoDKs,1568
|
|
365
|
-
truefoundry/workflow/__init__.py,sha256=
|
|
369
|
+
truefoundry/workflow/__init__.py,sha256=MNxnOh5fzAmDaK-cJy9qwtA-zH2CdOEP2h0q9lEiLgY,1588
|
|
366
370
|
truefoundry/workflow/container_task.py,sha256=8arieePsX4__OnG337hOtCiNgJwtKJJCsZcmFmCBJtk,402
|
|
367
371
|
truefoundry/workflow/map_task.py,sha256=f9vcAPRQy0Ttw6bvdZBKUVJMSm4eGQrbE1GHWhepHIU,1864
|
|
368
372
|
truefoundry/workflow/python_task.py,sha256=SRXRLC4vdBqGjhkwuaY39LEWN6iPCpJAuW17URRdWTY,1128
|
|
@@ -372,7 +376,7 @@ truefoundry/workflow/remote_filesystem/__init__.py,sha256=LQ95ViEjJ7Ts4JcCGOxMPs
|
|
|
372
376
|
truefoundry/workflow/remote_filesystem/logger.py,sha256=em2l7D6sw7xTLDP0kQSLpgfRRCLpN14Qw85TN7ujQcE,1022
|
|
373
377
|
truefoundry/workflow/remote_filesystem/tfy_signed_url_client.py,sha256=xcT0wQmQlgzcj0nP3tJopyFSVWT1uv3nhiTIuwfXYeg,12342
|
|
374
378
|
truefoundry/workflow/remote_filesystem/tfy_signed_url_fs.py,sha256=nSGPZu0Gyd_jz0KsEE-7w_BmnTD8CVF1S8cUJoxaCbc,13305
|
|
375
|
-
truefoundry-0.
|
|
376
|
-
truefoundry-0.
|
|
377
|
-
truefoundry-0.
|
|
378
|
-
truefoundry-0.
|
|
379
|
+
truefoundry-0.7.0.dist-info/METADATA,sha256=pn3-G0IdxJF6mi-kH-SL8UsdgoGnKo6oOVvgFJJDRao,2407
|
|
380
|
+
truefoundry-0.7.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
381
|
+
truefoundry-0.7.0.dist-info/entry_points.txt,sha256=xVjn7RMN-MW2-9f7YU-bBdlZSvvrwzhpX1zmmRmsNPU,98
|
|
382
|
+
truefoundry-0.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|