truefoundry 0.2.10__py3-none-any.whl → 0.3.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/__init__.py +1 -0
- truefoundry/autodeploy/cli.py +31 -18
- truefoundry/deploy/__init__.py +112 -1
- truefoundry/deploy/auto_gen/models.py +1714 -0
- truefoundry/deploy/builder/__init__.py +134 -0
- truefoundry/deploy/builder/builders/__init__.py +22 -0
- truefoundry/deploy/builder/builders/dockerfile.py +57 -0
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +46 -0
- truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py +66 -0
- truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +44 -0
- truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +158 -0
- truefoundry/deploy/builder/docker_service.py +168 -0
- truefoundry/deploy/cli/cli.py +21 -26
- truefoundry/deploy/cli/commands/__init__.py +18 -0
- truefoundry/deploy/cli/commands/apply_command.py +52 -0
- truefoundry/deploy/cli/commands/build_command.py +45 -0
- truefoundry/deploy/cli/commands/build_logs_command.py +89 -0
- truefoundry/deploy/cli/commands/create_command.py +75 -0
- truefoundry/deploy/cli/commands/delete_command.py +77 -0
- truefoundry/deploy/cli/commands/deploy_command.py +102 -0
- truefoundry/deploy/cli/commands/get_command.py +216 -0
- truefoundry/deploy/cli/commands/list_command.py +171 -0
- truefoundry/deploy/cli/commands/login_command.py +33 -0
- truefoundry/deploy/cli/commands/logout_command.py +20 -0
- truefoundry/deploy/cli/commands/logs_command.py +134 -0
- truefoundry/deploy/cli/commands/patch_application_command.py +81 -0
- truefoundry/deploy/cli/commands/patch_command.py +70 -0
- truefoundry/deploy/cli/commands/redeploy_command.py +41 -0
- truefoundry/deploy/cli/commands/terminate_comand.py +44 -0
- truefoundry/deploy/cli/commands/trigger_command.py +145 -0
- truefoundry/deploy/cli/config.py +10 -0
- truefoundry/deploy/cli/console.py +5 -0
- truefoundry/deploy/cli/const.py +12 -0
- truefoundry/deploy/cli/display_util.py +118 -0
- truefoundry/deploy/cli/util.py +129 -0
- truefoundry/deploy/core/__init__.py +7 -0
- truefoundry/deploy/core/login.py +9 -0
- truefoundry/deploy/core/logout.py +5 -0
- truefoundry/deploy/function_service/__init__.py +3 -0
- truefoundry/deploy/function_service/__main__.py +27 -0
- truefoundry/deploy/function_service/app.py +92 -0
- truefoundry/deploy/function_service/build.py +45 -0
- truefoundry/deploy/function_service/remote/__init__.py +6 -0
- truefoundry/deploy/function_service/remote/context.py +3 -0
- truefoundry/deploy/function_service/remote/method.py +67 -0
- truefoundry/deploy/function_service/remote/remote.py +144 -0
- truefoundry/deploy/function_service/route.py +137 -0
- truefoundry/deploy/function_service/service.py +113 -0
- truefoundry/deploy/function_service/utils.py +53 -0
- truefoundry/deploy/io/__init__.py +0 -0
- truefoundry/deploy/io/output_callback.py +23 -0
- truefoundry/deploy/io/rich_output_callback.py +27 -0
- truefoundry/deploy/json_util.py +7 -0
- truefoundry/deploy/lib/__init__.py +0 -0
- truefoundry/deploy/lib/auth/auth_service_client.py +181 -0
- truefoundry/deploy/lib/auth/credential_file_manager.py +115 -0
- truefoundry/deploy/lib/auth/credential_provider.py +131 -0
- truefoundry/deploy/lib/auth/servicefoundry_session.py +59 -0
- truefoundry/deploy/lib/clients/__init__.py +0 -0
- truefoundry/deploy/lib/clients/servicefoundry_client.py +746 -0
- truefoundry/deploy/lib/clients/shell_client.py +13 -0
- truefoundry/deploy/lib/clients/utils.py +41 -0
- truefoundry/deploy/lib/const.py +43 -0
- truefoundry/deploy/lib/dao/__init__.py +0 -0
- truefoundry/deploy/lib/dao/application.py +263 -0
- truefoundry/deploy/lib/dao/apply.py +80 -0
- truefoundry/deploy/lib/dao/version.py +33 -0
- truefoundry/deploy/lib/dao/workspace.py +71 -0
- truefoundry/deploy/lib/exceptions.py +26 -0
- truefoundry/deploy/lib/logs_utils.py +43 -0
- truefoundry/deploy/lib/messages.py +12 -0
- truefoundry/deploy/lib/model/__init__.py +0 -0
- truefoundry/deploy/lib/model/entity.py +400 -0
- truefoundry/deploy/lib/session.py +158 -0
- truefoundry/deploy/lib/util.py +90 -0
- truefoundry/deploy/lib/win32.py +129 -0
- truefoundry/deploy/v2/__init__.py +0 -0
- truefoundry/deploy/v2/lib/__init__.py +3 -0
- truefoundry/deploy/v2/lib/deploy.py +283 -0
- truefoundry/deploy/v2/lib/deploy_workflow.py +295 -0
- truefoundry/deploy/v2/lib/deployable_patched_models.py +86 -0
- truefoundry/deploy/v2/lib/models.py +53 -0
- truefoundry/deploy/v2/lib/patched_models.py +479 -0
- truefoundry/deploy/v2/lib/source.py +267 -0
- truefoundry/langchain/__init__.py +12 -1
- truefoundry/langchain/deprecated.py +302 -0
- truefoundry/langchain/truefoundry_chat.py +130 -0
- truefoundry/langchain/truefoundry_embeddings.py +171 -0
- truefoundry/langchain/truefoundry_llm.py +106 -0
- truefoundry/langchain/utils.py +85 -0
- truefoundry/logger.py +17 -0
- truefoundry/pydantic_v1.py +5 -0
- truefoundry/python_deploy_codegen.py +132 -0
- truefoundry/version.py +6 -0
- truefoundry/workflow/__init__.py +19 -0
- truefoundry/workflow/container_task.py +12 -0
- truefoundry/workflow/example/deploy.sh +1 -0
- truefoundry/workflow/example/hello_world_package/workflow.py +20 -0
- truefoundry/workflow/example/package/test_workflow.py +152 -0
- truefoundry/workflow/example/truefoundry.yaml +9 -0
- truefoundry/workflow/example/workflow.yaml +116 -0
- truefoundry/workflow/map_task.py +45 -0
- truefoundry/workflow/python_task.py +32 -0
- truefoundry/workflow/task.py +50 -0
- truefoundry/workflow/workflow.py +114 -0
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/METADATA +27 -7
- truefoundry-0.3.0.dist-info/RECORD +136 -0
- truefoundry/deploy/cli/deploy.py +0 -165
- truefoundry/deploy/cli/version.py +0 -6
- truefoundry-0.2.10.dist-info/RECORD +0 -38
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/WHEEL +0 -0
- {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tfy deploy --workspace_fqn tfy-ctl-euwe1-devtest:nikhil-ws
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from truefoundry.workflow import PythonTaskConfig, TaskPythonBuild, task, workflow
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@task(
|
|
5
|
+
task_config=PythonTaskConfig(
|
|
6
|
+
image=TaskPythonBuild(
|
|
7
|
+
python_version="3.9",
|
|
8
|
+
pip_packages=["truefoundry[workflow]==0.3.0rc7"],
|
|
9
|
+
),
|
|
10
|
+
service_account="tfy-flyte-dataplane-devtest-s3",
|
|
11
|
+
)
|
|
12
|
+
)
|
|
13
|
+
def say_hello() -> str:
|
|
14
|
+
return "Hello, World!"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@workflow
|
|
18
|
+
def hello_world_wf() -> str:
|
|
19
|
+
res = say_hello()
|
|
20
|
+
return res
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from functools import partial
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from truefoundry.deploy import Image, NvidiaGPU, Resources
|
|
7
|
+
from truefoundry.workflow import (
|
|
8
|
+
ContainerTask,
|
|
9
|
+
ContainerTaskConfig,
|
|
10
|
+
ExecutionConfig,
|
|
11
|
+
FlyteDirectory,
|
|
12
|
+
PythonTaskConfig,
|
|
13
|
+
TaskPythonBuild,
|
|
14
|
+
conditional,
|
|
15
|
+
map_task,
|
|
16
|
+
task,
|
|
17
|
+
workflow,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
cpu_task_config = PythonTaskConfig(
|
|
21
|
+
image=TaskPythonBuild(
|
|
22
|
+
python_version="3.9",
|
|
23
|
+
pip_packages=["truefoundry[workflow]==0.3.0rc7"],
|
|
24
|
+
),
|
|
25
|
+
resources=Resources(cpu_request=0.45),
|
|
26
|
+
service_account="tfy-flyte-dataplane-devtest-s3",
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# figure out naming
|
|
31
|
+
@task(task_config=cpu_task_config)
|
|
32
|
+
def should_train_tokenizer(tokenizer: str) -> bool:
|
|
33
|
+
print("Should train tokenizer")
|
|
34
|
+
return not bool(tokenizer)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@task(
|
|
38
|
+
task_config=cpu_task_config,
|
|
39
|
+
# currently we will not support the caching, but keeping here for now
|
|
40
|
+
# cache=True,
|
|
41
|
+
# cache_version="1.0",
|
|
42
|
+
)
|
|
43
|
+
def train_tokenizer() -> str:
|
|
44
|
+
print("Training tokenizer")
|
|
45
|
+
return "trained_tokenizer"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@task(
|
|
49
|
+
task_config=PythonTaskConfig(
|
|
50
|
+
image=TaskPythonBuild(
|
|
51
|
+
python_version="3.9",
|
|
52
|
+
pip_packages=["truefoundry[workflow]==0.3.0rc7", "pynvml==11.5.0"],
|
|
53
|
+
cuda_version="11.5-cudnn8",
|
|
54
|
+
),
|
|
55
|
+
env={
|
|
56
|
+
"NVIDIA_DRIVER_CAPABILITIES": "compute,utility",
|
|
57
|
+
"NVIDIA_VISIBLE_DEVICES": "all",
|
|
58
|
+
},
|
|
59
|
+
resources=Resources(cpu_request=0.45, devices=[NvidiaGPU(name="T4", count=1)]),
|
|
60
|
+
service_account="tfy-flyte-dataplane-devtest-s3",
|
|
61
|
+
),
|
|
62
|
+
)
|
|
63
|
+
def train_model(tokenizer: str) -> Tuple[FlyteDirectory, str]:
|
|
64
|
+
print("Training model")
|
|
65
|
+
import flytekit
|
|
66
|
+
from pynvml import nvmlDeviceGetCount, nvmlInit
|
|
67
|
+
|
|
68
|
+
nvmlInit()
|
|
69
|
+
assert nvmlDeviceGetCount() > 0
|
|
70
|
+
|
|
71
|
+
working_dir = flytekit.current_context().working_directory
|
|
72
|
+
local_dir = Path(os.path.join(working_dir, "csv_files"))
|
|
73
|
+
local_dir.mkdir(exist_ok=True)
|
|
74
|
+
|
|
75
|
+
with open(os.path.join(local_dir, "model"), "w", encoding="utf-8") as f:
|
|
76
|
+
f.write(tokenizer)
|
|
77
|
+
|
|
78
|
+
return FlyteDirectory(path=str(local_dir)), "hello"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@task(task_config=cpu_task_config)
|
|
82
|
+
def get_validation_data() -> List[str]:
|
|
83
|
+
print("Getting validation data")
|
|
84
|
+
return ["foo", "bar", "baz"]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@task(task_config=cpu_task_config)
|
|
88
|
+
def validate_model(model: FlyteDirectory, tokenizer: str, validation_data: str) -> bool:
|
|
89
|
+
print(validation_data)
|
|
90
|
+
model_path = os.path.join(model, "model")
|
|
91
|
+
with open(model_path, "r", encoding="utf-8") as f:
|
|
92
|
+
return f.read() == tokenizer
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@task(task_config=cpu_task_config)
|
|
96
|
+
def all_good(validations: List[bool]) -> bool:
|
|
97
|
+
print("Validations", validations)
|
|
98
|
+
return all(validations)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
echo = ContainerTask(
|
|
102
|
+
name="echo",
|
|
103
|
+
task_config=ContainerTaskConfig(
|
|
104
|
+
image=Image(
|
|
105
|
+
image_uri="bash:4.1",
|
|
106
|
+
command=["echo", "hello"],
|
|
107
|
+
),
|
|
108
|
+
service_account="tfy-flyte-dataplane-devtest-s3",
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@task(task_config=cpu_task_config)
|
|
114
|
+
def random(tokenizer: str) -> Tuple[FlyteDirectory, str]:
|
|
115
|
+
print(tokenizer)
|
|
116
|
+
return FlyteDirectory(path=""), "random"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@workflow(
|
|
120
|
+
execution_configs=[
|
|
121
|
+
ExecutionConfig(
|
|
122
|
+
schedule="*/10 * * * *",
|
|
123
|
+
)
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
def train(
|
|
127
|
+
tokenizer: str = "",
|
|
128
|
+
test_v2: str = "",
|
|
129
|
+
optional_var: Optional[str] = "",
|
|
130
|
+
default_v: Optional[str] = "hello1",
|
|
131
|
+
default_v2: str = "hello2",
|
|
132
|
+
) -> bool:
|
|
133
|
+
stt = should_train_tokenizer(tokenizer=tokenizer)
|
|
134
|
+
model, t = (
|
|
135
|
+
conditional("train_tokenizer")
|
|
136
|
+
.if_(stt.is_true())
|
|
137
|
+
.then(train_model(tokenizer=tokenizer))
|
|
138
|
+
.else_()
|
|
139
|
+
.then(random(tokenizer=tokenizer))
|
|
140
|
+
)
|
|
141
|
+
validation_task = partial(validate_model, model=model, tokenizer=t)
|
|
142
|
+
validation_data = get_validation_data()
|
|
143
|
+
validations = map_task(
|
|
144
|
+
validation_task,
|
|
145
|
+
concurrency=2,
|
|
146
|
+
)(validation_data=validation_data)
|
|
147
|
+
echo()
|
|
148
|
+
return all_good(validations=validations)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
if __name__ == "__main__":
|
|
152
|
+
train()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
type: workflow
|
|
2
|
+
name: my-test-workflow5
|
|
3
|
+
source:
|
|
4
|
+
type: remote
|
|
5
|
+
remote_uri: s3://tfy-mlfoundry-tfy-ctl-euwe1-devtest20221012173221749100000002/tfy-ctl-euwe1-devtest:nikhil-ws/my-test-workflow5/clyhb79w4013l01oyfq1iawd5/package.tar.gz
|
|
6
|
+
workflow_file_path: hello_world_package/workflow.py
|
|
7
|
+
docker_registry: docker.io/nikp-test
|
|
8
|
+
flyte_entities:
|
|
9
|
+
- template:
|
|
10
|
+
id:
|
|
11
|
+
resourceType: TASK
|
|
12
|
+
name: hello_world_package.workflow.say_hello
|
|
13
|
+
type: python-task
|
|
14
|
+
metadata:
|
|
15
|
+
runtime:
|
|
16
|
+
type: FLYTE_SDK
|
|
17
|
+
version: 1.12.2
|
|
18
|
+
flavor: python
|
|
19
|
+
retries: {}
|
|
20
|
+
interface:
|
|
21
|
+
inputs: {}
|
|
22
|
+
outputs:
|
|
23
|
+
variables:
|
|
24
|
+
o0:
|
|
25
|
+
type:
|
|
26
|
+
simple: STRING
|
|
27
|
+
description: o0
|
|
28
|
+
custom:
|
|
29
|
+
truefoundry:
|
|
30
|
+
type: python-task-config
|
|
31
|
+
image:
|
|
32
|
+
type: task-python-build
|
|
33
|
+
python_version: '3.9'
|
|
34
|
+
pip_packages:
|
|
35
|
+
- flytekit==1.10.3
|
|
36
|
+
resources:
|
|
37
|
+
cpu_request: 0.2
|
|
38
|
+
cpu_limit: 0.5
|
|
39
|
+
memory_request: 200
|
|
40
|
+
memory_limit: 500
|
|
41
|
+
ephemeral_storage_request: 1000
|
|
42
|
+
ephemeral_storage_limit: 2000
|
|
43
|
+
container:
|
|
44
|
+
image: ':'
|
|
45
|
+
args:
|
|
46
|
+
- pyflyte-execute
|
|
47
|
+
- --inputs
|
|
48
|
+
- '{{.input}}'
|
|
49
|
+
- --output-prefix
|
|
50
|
+
- '{{.outputPrefix}}'
|
|
51
|
+
- --raw-output-data-prefix
|
|
52
|
+
- '{{.rawOutputDataPrefix}}'
|
|
53
|
+
- --checkpoint-path
|
|
54
|
+
- '{{.checkpointOutputPrefix}}'
|
|
55
|
+
- --prev-checkpoint
|
|
56
|
+
- '{{.prevCheckpointPrefix}}'
|
|
57
|
+
- --resolver
|
|
58
|
+
- flytekit.core.python_auto_container.default_task_resolver
|
|
59
|
+
- --
|
|
60
|
+
- task-module
|
|
61
|
+
- hello_world_package.workflow
|
|
62
|
+
- task-name
|
|
63
|
+
- say_hello
|
|
64
|
+
resources: {}
|
|
65
|
+
description:
|
|
66
|
+
longDescription:
|
|
67
|
+
format: DESCRIPTION_FORMAT_RST
|
|
68
|
+
- template:
|
|
69
|
+
id:
|
|
70
|
+
resourceType: WORKFLOW
|
|
71
|
+
name: hello_world_package.workflow.hello_world_wf
|
|
72
|
+
metadata: {}
|
|
73
|
+
interface:
|
|
74
|
+
inputs: {}
|
|
75
|
+
outputs:
|
|
76
|
+
variables:
|
|
77
|
+
o0:
|
|
78
|
+
type:
|
|
79
|
+
simple: STRING
|
|
80
|
+
description: o0
|
|
81
|
+
nodes:
|
|
82
|
+
- id: n0
|
|
83
|
+
metadata:
|
|
84
|
+
name: say_hello
|
|
85
|
+
retries: {}
|
|
86
|
+
taskNode:
|
|
87
|
+
referenceId:
|
|
88
|
+
resourceType: TASK
|
|
89
|
+
name: hello_world_package.workflow.say_hello
|
|
90
|
+
overrides: {}
|
|
91
|
+
outputs:
|
|
92
|
+
- var: o0
|
|
93
|
+
binding:
|
|
94
|
+
promise:
|
|
95
|
+
nodeId: n0
|
|
96
|
+
var: o0
|
|
97
|
+
metadataDefaults: {}
|
|
98
|
+
description:
|
|
99
|
+
longDescription:
|
|
100
|
+
format: DESCRIPTION_FORMAT_RST
|
|
101
|
+
- id:
|
|
102
|
+
resourceType: LAUNCH_PLAN
|
|
103
|
+
name: hello_world_package.workflow.hello_world_wf
|
|
104
|
+
spec:
|
|
105
|
+
workflowId:
|
|
106
|
+
resourceType: WORKFLOW
|
|
107
|
+
name: hello_world_package.workflow.hello_world_wf
|
|
108
|
+
entityMetadata: {}
|
|
109
|
+
defaultInputs: {}
|
|
110
|
+
fixedInputs: {}
|
|
111
|
+
labels: {}
|
|
112
|
+
annotations: {}
|
|
113
|
+
rawOutputDataConfig: {}
|
|
114
|
+
closure:
|
|
115
|
+
expectedInputs: {}
|
|
116
|
+
expectedOutputs: {}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from flytekit import PythonFunctionTask
|
|
4
|
+
from flytekit.core.array_node_map_task import ArrayNodeMapTask
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TrueFoundryArrayNodeMapTask(ArrayNodeMapTask):
|
|
8
|
+
def __init__(self, *args, **kwargs):
|
|
9
|
+
super().__init__(*args, **kwargs)
|
|
10
|
+
|
|
11
|
+
def get_custom(self, settings) -> dict:
|
|
12
|
+
arraynode_custom_params = super().get_custom(settings)
|
|
13
|
+
return {
|
|
14
|
+
"truefoundry": self._partial.func.task_config.dict(),
|
|
15
|
+
**arraynode_custom_params,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def map_task(
|
|
20
|
+
task_function: PythonFunctionTask,
|
|
21
|
+
concurrency: Optional[int] = None,
|
|
22
|
+
# TODO why no min_successes?
|
|
23
|
+
min_success_ratio: float = 1.0,
|
|
24
|
+
**kwargs,
|
|
25
|
+
):
|
|
26
|
+
"""Map task that uses the ``ArrayNode`` construct..
|
|
27
|
+
|
|
28
|
+
.. important::
|
|
29
|
+
|
|
30
|
+
This is an experimental drop-in replacement for :py:func:`~flytekit.map_task`.
|
|
31
|
+
|
|
32
|
+
:param task_function: This argument is implicitly passed and represents the repeatable function
|
|
33
|
+
:param concurrency: If specified, this limits the number of mapped tasks than can run in parallel to the given batch
|
|
34
|
+
size. If the size of the input exceeds the concurrency value, then multiple batches will be run serially until
|
|
35
|
+
all inputs are processed. If set to 0, this means unbounded concurrency. If left unspecified, this means the
|
|
36
|
+
array node will inherit parallelism from the workflow
|
|
37
|
+
:param min_success_ratio: If specified, this determines the minimum fraction of total jobs which can complete
|
|
38
|
+
successfully before terminating this task and marking it successful.
|
|
39
|
+
"""
|
|
40
|
+
return TrueFoundryArrayNodeMapTask(
|
|
41
|
+
task_function,
|
|
42
|
+
concurrency=concurrency,
|
|
43
|
+
min_success_ratio=min_success_ratio,
|
|
44
|
+
**kwargs,
|
|
45
|
+
)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Callable, Tuple, Union
|
|
2
|
+
|
|
3
|
+
from flytekit import FlyteContext, PythonFunctionTask
|
|
4
|
+
from flytekit.extend import Promise, TaskPlugins
|
|
5
|
+
|
|
6
|
+
from truefoundry.logger import logger
|
|
7
|
+
from truefoundry.workflow import PythonTaskConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TrueFoundryFunctionTask(PythonFunctionTask[PythonTaskConfig]):
|
|
11
|
+
# Note(nikp1172) We are not creating our task type for now, we just re-use their task type for now
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
task_config: PythonTaskConfig,
|
|
15
|
+
task_function: Callable,
|
|
16
|
+
**kwargs,
|
|
17
|
+
):
|
|
18
|
+
super().__init__(task_config=task_config, task_function=task_function, **kwargs)
|
|
19
|
+
|
|
20
|
+
def local_execute(
|
|
21
|
+
self, ctx: FlyteContext, **kwargs
|
|
22
|
+
) -> Union[Tuple[Promise], Promise, None]:
|
|
23
|
+
logger.warning(
|
|
24
|
+
"Running pod task locally. Local environment may not match pod environment which may cause issues."
|
|
25
|
+
)
|
|
26
|
+
return super().local_execute(ctx=ctx, **kwargs)
|
|
27
|
+
|
|
28
|
+
def get_custom(self, settings) -> dict:
|
|
29
|
+
return {"truefoundry": self._task_config.dict()}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
TaskPlugins.register_pythontask_plugin(PythonTaskConfig, TrueFoundryFunctionTask)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from typing import Any, Callable, Optional, Union
|
|
2
|
+
|
|
3
|
+
from flytekit import PythonFunctionTask
|
|
4
|
+
from flytekit.core.task import FuncOut, T
|
|
5
|
+
from flytekit.core.task import task as flytekit_task
|
|
6
|
+
|
|
7
|
+
from truefoundry.workflow import PythonTaskConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def task(
|
|
11
|
+
_task_function: Optional[Callable[..., Any]] = None,
|
|
12
|
+
task_config: Optional[PythonTaskConfig] = None,
|
|
13
|
+
retries: Optional[int] = 0,
|
|
14
|
+
) -> Union[
|
|
15
|
+
Callable[[Callable[..., FuncOut]], PythonFunctionTask[T]],
|
|
16
|
+
PythonFunctionTask[T],
|
|
17
|
+
Callable[..., FuncOut],
|
|
18
|
+
]:
|
|
19
|
+
"""
|
|
20
|
+
This is the decorator used to run flyte tasks in Truefoundry.
|
|
21
|
+
|
|
22
|
+
Tasks are the building blocks of workflow. They represent users code. Tasks have the following properties
|
|
23
|
+
|
|
24
|
+
* Versioned (usually tied to the git revision SHA1)
|
|
25
|
+
* Strong interfaces (specified inputs and outputs)
|
|
26
|
+
* Declarative
|
|
27
|
+
* Independently executable
|
|
28
|
+
* Unit testable
|
|
29
|
+
|
|
30
|
+
For a simple python task,
|
|
31
|
+
|
|
32
|
+
.. code-block:: python
|
|
33
|
+
from truefoundry.workflow import task
|
|
34
|
+
@task
|
|
35
|
+
def my_task(x: int, y: typing.Dict[str, str]) -> str:
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
For specific task types
|
|
39
|
+
|
|
40
|
+
.. code-block:: python
|
|
41
|
+
from truefoundry.workflow import task, PythonTaskConfig
|
|
42
|
+
@task(task_config=PythonTaskConfig(), retries=2)
|
|
43
|
+
def my_task(x: int, y: typing.Dict[str, str]) -> str:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
:param _task_function: The function to be wrapped as a task. This is optional and can be used as a decorator.
|
|
47
|
+
:param task_config: The configuration for the task of class PythonTaskConfig. This is optional and defaults to an empty configuration.
|
|
48
|
+
:param retries: The number of retries for the task. This is optional and defaults to 0.
|
|
49
|
+
"""
|
|
50
|
+
return flytekit_task(_task_function, task_config=task_config, retries=retries)
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
|
2
|
+
|
|
3
|
+
from flytekit.core.base_task import Task
|
|
4
|
+
from flytekit.core.launch_plan import LaunchPlan
|
|
5
|
+
from flytekit.core.schedule import CronSchedule
|
|
6
|
+
from flytekit.core.workflow import (
|
|
7
|
+
FuncOut,
|
|
8
|
+
PythonFunctionWorkflow,
|
|
9
|
+
WorkflowFailurePolicy,
|
|
10
|
+
)
|
|
11
|
+
from flytekit.core.workflow import workflow as flytekit_workflow
|
|
12
|
+
|
|
13
|
+
from truefoundry.pydantic_v1 import BaseModel
|
|
14
|
+
|
|
15
|
+
TRUEFOUNDRY_LAUNCH_PLAN_NAME = "default"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ExecutionConfig(BaseModel):
|
|
19
|
+
# I am not using job's Schedule here because:
|
|
20
|
+
# 1. flyte accepts schedule only as a string
|
|
21
|
+
# 2. flyte doesn't support setting concurrency policy
|
|
22
|
+
schedule: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class ExecutionConfigStore(BaseModel):
|
|
26
|
+
execution_config_map: Dict[str, ExecutionConfig] = {}
|
|
27
|
+
|
|
28
|
+
def reset(self):
|
|
29
|
+
self.execution_config_map = {}
|
|
30
|
+
|
|
31
|
+
def get(self, key: str) -> Optional[ExecutionConfig]:
|
|
32
|
+
return self.execution_config_map.get(key)
|
|
33
|
+
|
|
34
|
+
def set(self, key: str, value: ExecutionConfig):
|
|
35
|
+
self.execution_config_map[key] = value
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
execution_config_store = ExecutionConfigStore()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def workflow(
|
|
42
|
+
_workflow_function: Optional[Callable[..., Any]] = None,
|
|
43
|
+
failure_policy: Optional[WorkflowFailurePolicy] = None,
|
|
44
|
+
on_failure: Optional[Task] = None,
|
|
45
|
+
execution_configs: Optional[List[ExecutionConfig]] = None,
|
|
46
|
+
) -> Union[
|
|
47
|
+
Callable[[Callable[..., FuncOut]], PythonFunctionWorkflow],
|
|
48
|
+
PythonFunctionWorkflow,
|
|
49
|
+
Callable[..., FuncOut],
|
|
50
|
+
]:
|
|
51
|
+
"""
|
|
52
|
+
This decorator declares a function to be a Flyte workflow. Workflows are declarative entities that construct a DAG
|
|
53
|
+
of tasks using the data flow between tasks.
|
|
54
|
+
|
|
55
|
+
Unlike a task, the function body of a workflow is evaluated at serialization-time (aka compile-time). This is
|
|
56
|
+
because while we can determine the entire structure of a task by looking at the function's signature, workflows need
|
|
57
|
+
to run through the function itself because the body of the function is what expresses the workflow structure. It's
|
|
58
|
+
also important to note that, local execution notwithstanding, it is not evaluated again when the workflow runs on
|
|
59
|
+
Flyte.
|
|
60
|
+
That is, workflows should not call non-Flyte entities since they are only run once (again, this is with respect to
|
|
61
|
+
the platform, local runs notwithstanding).
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
|
|
65
|
+
.. literalinclude:: ../../../tests/flytekit/unit/core/test_workflows.py
|
|
66
|
+
:pyobject: my_wf_example
|
|
67
|
+
|
|
68
|
+
Again, users should keep in mind that even though the body of the function looks like regular Python, it is
|
|
69
|
+
actually not. When flytekit scans the workflow function, the objects being passed around between the tasks are not
|
|
70
|
+
your typical Python values. So even though you may have a task ``t1() -> int``, when ``a = t1()`` is called, ``a``
|
|
71
|
+
will not be an integer so if you try to ``range(a)`` you'll get an error.
|
|
72
|
+
|
|
73
|
+
Please see the :ref:`user guide <cookbook:workflow>` for more usage examples.
|
|
74
|
+
|
|
75
|
+
:param _workflow_function: This argument is implicitly passed and represents the decorated function.
|
|
76
|
+
:param failure_policy: Use the options in flytekit.WorkflowFailurePolicy
|
|
77
|
+
:param on_failure: Invoke this workflow or task on failure. The Workflow / task has to match the signature of
|
|
78
|
+
the current workflow, with an additional parameter called `error` Error
|
|
79
|
+
"""
|
|
80
|
+
if _workflow_function is None:
|
|
81
|
+
|
|
82
|
+
def wrapper(func: Callable[..., Any]) -> Any:
|
|
83
|
+
return workflow(
|
|
84
|
+
_workflow_function=func,
|
|
85
|
+
failure_policy=failure_policy,
|
|
86
|
+
on_failure=on_failure,
|
|
87
|
+
execution_configs=execution_configs,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
return wrapper
|
|
91
|
+
|
|
92
|
+
workflow_object = flytekit_workflow(
|
|
93
|
+
_workflow_function,
|
|
94
|
+
failure_policy=failure_policy,
|
|
95
|
+
on_failure=on_failure,
|
|
96
|
+
)
|
|
97
|
+
execution_configs = execution_configs or []
|
|
98
|
+
if len(execution_configs) > 1:
|
|
99
|
+
raise ValueError("Only one cron execution config is allowed per workflow")
|
|
100
|
+
|
|
101
|
+
if execution_configs:
|
|
102
|
+
execution_config = execution_configs[0]
|
|
103
|
+
# flyte maintains this in-memory and uses it while serialization
|
|
104
|
+
LaunchPlan.get_or_create(
|
|
105
|
+
workflow=workflow_object,
|
|
106
|
+
name=TRUEFOUNDRY_LAUNCH_PLAN_NAME,
|
|
107
|
+
schedule=CronSchedule(schedule=execution_config.schedule),
|
|
108
|
+
)
|
|
109
|
+
function_module = _workflow_function.__module__
|
|
110
|
+
function_name = _workflow_function.__name__
|
|
111
|
+
function_path = f"{function_module}.{function_name}"
|
|
112
|
+
execution_config_store.set(function_path, execution_config)
|
|
113
|
+
|
|
114
|
+
return workflow_object
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: truefoundry
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Truefoundry CLI
|
|
5
5
|
Author: Abhishek Choudhary
|
|
6
6
|
Author-email: abhishek@truefoundry.com
|
|
7
|
-
Requires-Python: >=3.8,<3.13
|
|
7
|
+
Requires-Python: >=3.8.1,<3.13
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
10
9
|
Classifier: Programming Language :: Python :: 3.9
|
|
11
10
|
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
14
13
|
Provides-Extra: ml
|
|
15
|
-
|
|
14
|
+
Provides-Extra: workflow
|
|
15
|
+
Requires-Dist: GitPython (>=3.1.43,<3.2.0)
|
|
16
|
+
Requires-Dist: Mako (>=1.1.6,<2.0.0)
|
|
17
|
+
Requires-Dist: PyJWT (>=2.0.0,<3.0.0)
|
|
18
|
+
Requires-Dist: PyYAML (>=6.0.0,<7.0.0)
|
|
19
|
+
Requires-Dist: click (>=7.0.0,<9.0.0)
|
|
20
|
+
Requires-Dist: docker (>=6.1.2,<8.0.0)
|
|
21
|
+
Requires-Dist: fastapi (>=0.56.0,<0.200.0)
|
|
22
|
+
Requires-Dist: filelock (>=3.8.0,<4.0.0)
|
|
23
|
+
Requires-Dist: flytekit (==1.12.2) ; extra == "workflow"
|
|
16
24
|
Requires-Dist: gitignorefile (>=1.1.2,<1.2.0)
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
25
|
+
Requires-Dist: importlib-metadata (>=6.0.1,<8.0.0)
|
|
26
|
+
Requires-Dist: importlib-resources (>=5.2.0,<6.0.0)
|
|
27
|
+
Requires-Dist: mlfoundry (==0.11.5) ; extra == "ml"
|
|
19
28
|
Requires-Dist: openai (>=1.16.2,<2.0.0)
|
|
29
|
+
Requires-Dist: packaging (>=20.0,<25.0)
|
|
20
30
|
Requires-Dist: pydantic (>=1.10.0,<3)
|
|
31
|
+
Requires-Dist: pygments (>=2.12.0,<3.0.0)
|
|
32
|
+
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
21
33
|
Requires-Dist: python-dotenv (>=1.0.1,<1.1.0)
|
|
34
|
+
Requires-Dist: python-socketio[client] (>=5.5.2,<6.0.0)
|
|
35
|
+
Requires-Dist: questionary (>=1.10.0,<2.0.0)
|
|
22
36
|
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
37
|
+
Requires-Dist: requirements-parser (>=0.10.2,<0.11.0)
|
|
23
38
|
Requires-Dist: rich (>=13.7.1,<14.0.0)
|
|
24
|
-
Requires-Dist:
|
|
39
|
+
Requires-Dist: rich-click (>=1.2.1,<2.0.0)
|
|
40
|
+
Requires-Dist: tqdm (>=4.0.0,<5.0.0)
|
|
41
|
+
Requires-Dist: typing-extensions (>=3.10.0)
|
|
42
|
+
Requires-Dist: urllib3 (>=1.26.18,<3)
|
|
43
|
+
Requires-Dist: uvicorn (>=0.13.0,<0.40.0)
|
|
44
|
+
Requires-Dist: yq (>=3.1.0,<4.0.0)
|
|
25
45
|
Description-Content-Type: text/markdown
|
|
26
46
|
|
|
27
47
|
# TrueFoundry
|