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.

Files changed (112) hide show
  1. truefoundry/__init__.py +1 -0
  2. truefoundry/autodeploy/cli.py +31 -18
  3. truefoundry/deploy/__init__.py +112 -1
  4. truefoundry/deploy/auto_gen/models.py +1714 -0
  5. truefoundry/deploy/builder/__init__.py +134 -0
  6. truefoundry/deploy/builder/builders/__init__.py +22 -0
  7. truefoundry/deploy/builder/builders/dockerfile.py +57 -0
  8. truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +46 -0
  9. truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py +66 -0
  10. truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +44 -0
  11. truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +158 -0
  12. truefoundry/deploy/builder/docker_service.py +168 -0
  13. truefoundry/deploy/cli/cli.py +21 -26
  14. truefoundry/deploy/cli/commands/__init__.py +18 -0
  15. truefoundry/deploy/cli/commands/apply_command.py +52 -0
  16. truefoundry/deploy/cli/commands/build_command.py +45 -0
  17. truefoundry/deploy/cli/commands/build_logs_command.py +89 -0
  18. truefoundry/deploy/cli/commands/create_command.py +75 -0
  19. truefoundry/deploy/cli/commands/delete_command.py +77 -0
  20. truefoundry/deploy/cli/commands/deploy_command.py +102 -0
  21. truefoundry/deploy/cli/commands/get_command.py +216 -0
  22. truefoundry/deploy/cli/commands/list_command.py +171 -0
  23. truefoundry/deploy/cli/commands/login_command.py +33 -0
  24. truefoundry/deploy/cli/commands/logout_command.py +20 -0
  25. truefoundry/deploy/cli/commands/logs_command.py +134 -0
  26. truefoundry/deploy/cli/commands/patch_application_command.py +81 -0
  27. truefoundry/deploy/cli/commands/patch_command.py +70 -0
  28. truefoundry/deploy/cli/commands/redeploy_command.py +41 -0
  29. truefoundry/deploy/cli/commands/terminate_comand.py +44 -0
  30. truefoundry/deploy/cli/commands/trigger_command.py +145 -0
  31. truefoundry/deploy/cli/config.py +10 -0
  32. truefoundry/deploy/cli/console.py +5 -0
  33. truefoundry/deploy/cli/const.py +12 -0
  34. truefoundry/deploy/cli/display_util.py +118 -0
  35. truefoundry/deploy/cli/util.py +129 -0
  36. truefoundry/deploy/core/__init__.py +7 -0
  37. truefoundry/deploy/core/login.py +9 -0
  38. truefoundry/deploy/core/logout.py +5 -0
  39. truefoundry/deploy/function_service/__init__.py +3 -0
  40. truefoundry/deploy/function_service/__main__.py +27 -0
  41. truefoundry/deploy/function_service/app.py +92 -0
  42. truefoundry/deploy/function_service/build.py +45 -0
  43. truefoundry/deploy/function_service/remote/__init__.py +6 -0
  44. truefoundry/deploy/function_service/remote/context.py +3 -0
  45. truefoundry/deploy/function_service/remote/method.py +67 -0
  46. truefoundry/deploy/function_service/remote/remote.py +144 -0
  47. truefoundry/deploy/function_service/route.py +137 -0
  48. truefoundry/deploy/function_service/service.py +113 -0
  49. truefoundry/deploy/function_service/utils.py +53 -0
  50. truefoundry/deploy/io/__init__.py +0 -0
  51. truefoundry/deploy/io/output_callback.py +23 -0
  52. truefoundry/deploy/io/rich_output_callback.py +27 -0
  53. truefoundry/deploy/json_util.py +7 -0
  54. truefoundry/deploy/lib/__init__.py +0 -0
  55. truefoundry/deploy/lib/auth/auth_service_client.py +181 -0
  56. truefoundry/deploy/lib/auth/credential_file_manager.py +115 -0
  57. truefoundry/deploy/lib/auth/credential_provider.py +131 -0
  58. truefoundry/deploy/lib/auth/servicefoundry_session.py +59 -0
  59. truefoundry/deploy/lib/clients/__init__.py +0 -0
  60. truefoundry/deploy/lib/clients/servicefoundry_client.py +746 -0
  61. truefoundry/deploy/lib/clients/shell_client.py +13 -0
  62. truefoundry/deploy/lib/clients/utils.py +41 -0
  63. truefoundry/deploy/lib/const.py +43 -0
  64. truefoundry/deploy/lib/dao/__init__.py +0 -0
  65. truefoundry/deploy/lib/dao/application.py +263 -0
  66. truefoundry/deploy/lib/dao/apply.py +80 -0
  67. truefoundry/deploy/lib/dao/version.py +33 -0
  68. truefoundry/deploy/lib/dao/workspace.py +71 -0
  69. truefoundry/deploy/lib/exceptions.py +26 -0
  70. truefoundry/deploy/lib/logs_utils.py +43 -0
  71. truefoundry/deploy/lib/messages.py +12 -0
  72. truefoundry/deploy/lib/model/__init__.py +0 -0
  73. truefoundry/deploy/lib/model/entity.py +400 -0
  74. truefoundry/deploy/lib/session.py +158 -0
  75. truefoundry/deploy/lib/util.py +90 -0
  76. truefoundry/deploy/lib/win32.py +129 -0
  77. truefoundry/deploy/v2/__init__.py +0 -0
  78. truefoundry/deploy/v2/lib/__init__.py +3 -0
  79. truefoundry/deploy/v2/lib/deploy.py +283 -0
  80. truefoundry/deploy/v2/lib/deploy_workflow.py +295 -0
  81. truefoundry/deploy/v2/lib/deployable_patched_models.py +86 -0
  82. truefoundry/deploy/v2/lib/models.py +53 -0
  83. truefoundry/deploy/v2/lib/patched_models.py +479 -0
  84. truefoundry/deploy/v2/lib/source.py +267 -0
  85. truefoundry/langchain/__init__.py +12 -1
  86. truefoundry/langchain/deprecated.py +302 -0
  87. truefoundry/langchain/truefoundry_chat.py +130 -0
  88. truefoundry/langchain/truefoundry_embeddings.py +171 -0
  89. truefoundry/langchain/truefoundry_llm.py +106 -0
  90. truefoundry/langchain/utils.py +85 -0
  91. truefoundry/logger.py +17 -0
  92. truefoundry/pydantic_v1.py +5 -0
  93. truefoundry/python_deploy_codegen.py +132 -0
  94. truefoundry/version.py +6 -0
  95. truefoundry/workflow/__init__.py +19 -0
  96. truefoundry/workflow/container_task.py +12 -0
  97. truefoundry/workflow/example/deploy.sh +1 -0
  98. truefoundry/workflow/example/hello_world_package/workflow.py +20 -0
  99. truefoundry/workflow/example/package/test_workflow.py +152 -0
  100. truefoundry/workflow/example/truefoundry.yaml +9 -0
  101. truefoundry/workflow/example/workflow.yaml +116 -0
  102. truefoundry/workflow/map_task.py +45 -0
  103. truefoundry/workflow/python_task.py +32 -0
  104. truefoundry/workflow/task.py +50 -0
  105. truefoundry/workflow/workflow.py +114 -0
  106. {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/METADATA +27 -7
  107. truefoundry-0.3.0.dist-info/RECORD +136 -0
  108. truefoundry/deploy/cli/deploy.py +0 -165
  109. truefoundry/deploy/cli/version.py +0 -6
  110. truefoundry-0.2.10.dist-info/RECORD +0 -38
  111. {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/WHEEL +0 -0
  112. {truefoundry-0.2.10.dist-info → truefoundry-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,295 @@
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+ from typing import Dict, List, Optional, Union
5
+
6
+ import requirements
7
+ from flytekit.configuration import (
8
+ SERIALIZED_CONTEXT_ENV_VAR,
9
+ ImageConfig,
10
+ SerializationSettings,
11
+ )
12
+ from flytekit.configuration import Image as FlytekitImage
13
+ from flytekit.models.launch_plan import LaunchPlan as FlyteLaunchPlan
14
+ from flytekit.tools.repo import serialize as serialize_workflow
15
+ from flytekit.tools.translator import TaskSpec as FlyteTaskSpec
16
+ from flytekit.tools.translator import WorkflowSpec as FlyteWorkflowSpec
17
+ from google.protobuf.json_format import MessageToDict
18
+
19
+ from truefoundry.deploy.auto_gen import models as auto_gen_models
20
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
21
+ ServiceFoundryServiceClient,
22
+ )
23
+ from truefoundry.deploy.lib.dao.workspace import get_workspace_by_fqn
24
+ from truefoundry.deploy.lib.model.entity import Deployment
25
+ from truefoundry.deploy.v2.lib.source import (
26
+ local_source_to_remote_source,
27
+ )
28
+ from truefoundry.logger import logger
29
+ from truefoundry.pydantic_v1 import ValidationError
30
+ from truefoundry.workflow.workflow import (
31
+ TRUEFOUNDRY_LAUNCH_PLAN_NAME,
32
+ execution_config_store,
33
+ )
34
+
35
+
36
+ def _handle_code_upload_for_workflow(
37
+ workflow: auto_gen_models.Workflow, workspace_fqn: str
38
+ ) -> auto_gen_models.Workflow:
39
+ new_workflow = workflow.copy(deep=True)
40
+ new_workflow.source = local_source_to_remote_source(
41
+ local_source=workflow.source,
42
+ workspace_fqn=workspace_fqn,
43
+ component_name=workflow.name,
44
+ )
45
+ return new_workflow
46
+
47
+
48
+ def _is_tfy_workflow_package(package: requirements.parser.Requirement) -> bool:
49
+ return package.name == "truefoundry" and "workflow" in package.extras
50
+
51
+
52
+ def _is_tfy_wf_present_in_pip_packages_or_requirements_file(
53
+ pip_packages: List[str],
54
+ project_root_path: str,
55
+ requirements_path: Optional[str] = None,
56
+ ) -> bool:
57
+ for package in pip_packages:
58
+ parsed_package = requirements.parser.Requirement.parse(package)
59
+ if _is_tfy_workflow_package(parsed_package):
60
+ return True
61
+ if requirements_path:
62
+ requirements_file_absolute_path = os.path.join(
63
+ project_root_path, requirements_path
64
+ )
65
+ if not os.path.exists(requirements_file_absolute_path):
66
+ raise FileNotFoundError(
67
+ f"requirements file not found at {requirements_file_absolute_path}. requirements file path should be relative to project root path."
68
+ )
69
+ with open(requirements_file_absolute_path, "r") as file:
70
+ for package in requirements.parse(file):
71
+ if _is_tfy_workflow_package(package):
72
+ return True
73
+ return False
74
+
75
+
76
+ def _is_tfy_wf_present_in_task_python_build(
77
+ task_image_spec: Dict, project_root_path: str
78
+ ) -> bool:
79
+ pip_packages = task_image_spec["pip_packages"] or []
80
+ requirements_path = task_image_spec.get("requirements_path")
81
+ return _is_tfy_wf_present_in_pip_packages_or_requirements_file(
82
+ pip_packages=pip_packages,
83
+ project_root_path=project_root_path,
84
+ requirements_path=requirements_path,
85
+ )
86
+
87
+
88
+ def _is_dynamic_task(flyte_task: FlyteTaskSpec) -> bool:
89
+ envs: Dict[str:str] = flyte_task.template.container.env or {}
90
+ return SERIALIZED_CONTEXT_ENV_VAR in envs.keys()
91
+
92
+
93
+ # this function does validation that num_workflows = 1, this also validates task_config is passed correctly.
94
+ # This is verified by pydantic but doing it here also as error messages are not clear in pydantic
95
+ def _validate_workflow_entities( # noqa: C901
96
+ workflow_entities: List[Union[FlyteWorkflowSpec, FlyteLaunchPlan, FlyteTaskSpec]],
97
+ project_root_path: str,
98
+ ):
99
+ workflow_objs: List[FlyteWorkflowSpec] = []
100
+ launch_plans: List[FlyteLaunchPlan] = []
101
+ tasks: List[FlyteTaskSpec] = []
102
+ for entity in workflow_entities:
103
+ if isinstance(entity, FlyteWorkflowSpec):
104
+ workflow_objs.append(entity)
105
+ elif isinstance(entity, FlyteLaunchPlan):
106
+ launch_plans.append(entity)
107
+ elif isinstance(entity, FlyteTaskSpec):
108
+ tasks.append(entity)
109
+ else:
110
+ raise ValueError(f"Invalid entity found in workflow: {entity}")
111
+ if len(workflow_objs) != 1:
112
+ raise ValueError(
113
+ f"Workflow file must have exactly one workflow object. Found {len(workflow_objs)}"
114
+ )
115
+ if len(launch_plans) > 2:
116
+ raise ValueError(
117
+ f"Workflow file must have exactly one launch plan. Found {len(launch_plans) - 1}"
118
+ )
119
+
120
+ error_message_to_use_truefoundry_decorators = """Invalid task definition for task: {}, Please use valid truefoundry decorator/class and pass task_config for tasks.
121
+ You can import truefoundry task decorators using:
122
+ `from truefoundry.workflow import task, ContainerTask, map_task`
123
+ You can pass task config using `task_config` parameter in the task definition. Task config should be one of the following:
124
+ `PythonTaskConfig`, or `ContainerTaskConfig`. You can import these using:
125
+ `from truefoundry.workflow import PythonTaskConfig, ContainerTaskConfig`
126
+ """
127
+ tasks_without_truefoundry_worflow_package = []
128
+ for task in tasks:
129
+ if _is_dynamic_task(task):
130
+ raise ValueError("Dynamic workflows are not supported yet.")
131
+ if not task.template.custom:
132
+ raise ValueError(
133
+ error_message_to_use_truefoundry_decorators.format(
134
+ task.template.id.name
135
+ )
136
+ )
137
+ task_image_spec = task.template.custom["truefoundry"]["image"]
138
+ if task_image_spec["type"] == "task-python-build":
139
+ is_tfy_wf_present_in_task_python_build = (
140
+ _is_tfy_wf_present_in_task_python_build(
141
+ task_image_spec=task_image_spec, project_root_path=project_root_path
142
+ )
143
+ )
144
+ if not is_tfy_wf_present_in_task_python_build:
145
+ tasks_without_truefoundry_worflow_package.append(task.template.id.name)
146
+ try:
147
+ auto_gen_models.FlyteTaskCustom.validate(task.template.custom)
148
+ except ValidationError:
149
+ raise ValueError(
150
+ error_message_to_use_truefoundry_decorators.format(
151
+ task.template.id.name
152
+ )
153
+ ) from None
154
+ if len(tasks_without_truefoundry_worflow_package) > 0:
155
+ raise ValueError(
156
+ rf"truefoundry\[workflow] package is required dependency to run workflows, add it in pip_packages for tasks: {', '.join(tasks_without_truefoundry_worflow_package)}"
157
+ )
158
+
159
+ # validate that all inputs have default values for cron workflows
160
+ for launch_plan in launch_plans:
161
+ if (
162
+ execution_config_store.get(launch_plan.spec.workflow_id.name)
163
+ and launch_plan.id.name == TRUEFOUNDRY_LAUNCH_PLAN_NAME
164
+ ):
165
+ workflow_inputs = launch_plan.spec.default_inputs.parameters
166
+ for input in workflow_inputs:
167
+ if workflow_inputs[input].required:
168
+ raise ValueError(
169
+ f"All inputs must have default for a cron workflow. Input {input} is required in workflow but default value is not provided"
170
+ )
171
+
172
+
173
+ def _get_relative_package_path_from_filepath(
174
+ project_root_path: str, filepath: str
175
+ ) -> str:
176
+ """
177
+ This function returns the relative package path from the project root path for a given file path.
178
+ e.g. if project_root_path = /home/user/project and filepath = /home/user/project/src/module.py
179
+ then the function will return src.module
180
+ """
181
+ relative_file_path = os.path.relpath(filepath, project_root_path)
182
+ path = Path(relative_file_path)
183
+ package_path = str(path.with_suffix("")).replace(os.path.sep, ".")
184
+
185
+ return package_path
186
+
187
+
188
+ def _generate_manifest_for_workflow(
189
+ workflow: auto_gen_models.Workflow,
190
+ ):
191
+ settings = SerializationSettings(
192
+ # We are adding these defaults to avoid validation errors in flyte objects
193
+ image_config=ImageConfig(default_image=FlytekitImage(name="", tag="", fqn="")),
194
+ python_interpreter=sys.executable,
195
+ )
196
+ source_absolute_path = os.path.abspath(workflow.source.project_root_path)
197
+
198
+ workflow_file_absolute_path = os.path.join(
199
+ source_absolute_path, workflow.workflow_file_path
200
+ )
201
+ if not os.path.exists(workflow_file_absolute_path):
202
+ raise FileNotFoundError(
203
+ f"Workflow file not found at {workflow_file_absolute_path}. Workflow file path should be relative to project root path."
204
+ )
205
+
206
+ package_path = _get_relative_package_path_from_filepath(
207
+ project_root_path=source_absolute_path, filepath=workflow_file_absolute_path
208
+ )
209
+
210
+ workflow_entities = serialize_workflow(
211
+ pkgs=[package_path], settings=settings, local_source_root=source_absolute_path
212
+ )
213
+ _validate_workflow_entities(workflow_entities, source_absolute_path)
214
+
215
+ workflow.flyte_entities = []
216
+ for entity in workflow_entities:
217
+ if isinstance(entity, FlyteLaunchPlan):
218
+ workflow_name = entity.spec.workflow_id.name
219
+
220
+ # this is the case when someone has a cron schedule. and this line is for handling default launch plan in this case.
221
+ if (
222
+ execution_config_store.get(workflow_name)
223
+ and workflow_name == entity.id.name
224
+ ):
225
+ continue
226
+ # this is the case when someone does not have a cron schedule. and this line is for handling default launch plan in this case.
227
+ elif entity.id.name == workflow_name:
228
+ entity._id._name = TRUEFOUNDRY_LAUNCH_PLAN_NAME
229
+ # this the case when some workflow doesn't have cron schedule, neither it is default launch plan
230
+ elif entity.id.name != TRUEFOUNDRY_LAUNCH_PLAN_NAME:
231
+ raise ValueError(
232
+ f"Creating launch plans is not allowed. Found launch plan with name {entity.id.name}"
233
+ )
234
+ message_dict = MessageToDict(entity.to_flyte_idl())
235
+ # proto message to dict conversion converts all int to float. so we need this hack
236
+ if (
237
+ message_dict.get("template")
238
+ and message_dict["template"].get("custom")
239
+ and message_dict["template"]["custom"].get("truefoundry")
240
+ ):
241
+ parsed_model = auto_gen_models.FlyteTaskCustom.parse_obj(
242
+ message_dict["template"]["custom"]
243
+ )
244
+ message_dict["template"]["custom"]["truefoundry"] = parsed_model.truefoundry
245
+
246
+ workflow.flyte_entities.append(message_dict)
247
+
248
+ # this step is just to verify if pydantic model is still valid after adding flyte_entities
249
+ auto_gen_models.Workflow.validate({**workflow.dict()})
250
+
251
+
252
+ def _validate_workspace_fqn(
253
+ workflow: auto_gen_models.Workflow,
254
+ workspace_fqn: Optional[str] = None,
255
+ ):
256
+ if not workspace_fqn:
257
+ raise ValueError(
258
+ "No Workspace FQN was provided. "
259
+ "Pass it explicitly using `--workspace-fqn` argument on CLI or `workspace_fqn` argument of `deploy_workflow`."
260
+ )
261
+
262
+
263
+ def deploy_workflow(
264
+ workflow: auto_gen_models.Workflow,
265
+ workspace_fqn: str,
266
+ wait: bool = True,
267
+ ) -> Deployment:
268
+ _generate_manifest_for_workflow(workflow)
269
+ _validate_workspace_fqn(workflow, workspace_fqn)
270
+
271
+ # we need to rest the execution config store as it is a global variable and we don't want to keep the cron execution config for next workflow
272
+ # this is only needed for notebook environment
273
+ execution_config_store.reset()
274
+
275
+ workspace_id = get_workspace_by_fqn(workspace_fqn).id
276
+
277
+ logger.info(
278
+ f"Deploying workflow {workflow.name} to workspace {workspace_fqn} ({workspace_id})"
279
+ )
280
+
281
+ workflow = _handle_code_upload_for_workflow(
282
+ workflow=workflow, workspace_fqn=workspace_fqn
283
+ )
284
+
285
+ client = ServiceFoundryServiceClient()
286
+ deployment = client.deploy_application(
287
+ workspace_id=workspace_id, application=workflow
288
+ )
289
+ logger.info(
290
+ "🚀 Deployment started for application '%s'. Deployment FQN is '%s'.",
291
+ workflow.name,
292
+ deployment.fqn,
293
+ )
294
+ deployment_url = f"{client.base_url.strip('/')}/applications/{deployment.applicationId}?tab=deployments"
295
+ logger.info("You can find the application on the dashboard:- '%s'", deployment_url)
@@ -0,0 +1,86 @@
1
+ from typing import Literal, Union
2
+
3
+ from truefoundry.deploy.auto_gen import models
4
+ from truefoundry.deploy.lib.model.entity import Deployment
5
+ from truefoundry.deploy.v2.lib.deploy import deploy_component
6
+ from truefoundry.pydantic_v1 import BaseModel, Field, conint
7
+
8
+
9
+ class DeployablePatchedModelBase(BaseModel):
10
+ class Config:
11
+ extra = "forbid"
12
+
13
+ def deploy(self, workspace_fqn: str, wait: bool = True) -> Deployment:
14
+ return deploy_component(component=self, workspace_fqn=workspace_fqn, wait=wait)
15
+
16
+
17
+ class Service(models.Service, DeployablePatchedModelBase):
18
+ type: Literal["service"] = "service"
19
+ resources: models.Resources = Field(default_factory=models.Resources)
20
+ # This is being patched because cue export marks this as a "number"
21
+ replicas: Union[conint(ge=0, le=100), models.ServiceAutoscaling] = Field(
22
+ 1,
23
+ description="+label=Replicas\n+usage=Replicas of service you want to run\n+icon=fa-clone\n+sort=3",
24
+ )
25
+
26
+
27
+ class Job(models.Job, DeployablePatchedModelBase):
28
+ type: Literal["job"] = "job"
29
+ resources: models.Resources = Field(default_factory=models.Resources)
30
+
31
+
32
+ class Notebook(models.Notebook, DeployablePatchedModelBase):
33
+ type: Literal["notebook"] = "notebook"
34
+ resources: models.Resources = Field(default_factory=models.Resources)
35
+
36
+
37
+ class Codeserver(models.Codeserver, DeployablePatchedModelBase):
38
+ type: Literal["codeserver"] = "codeserver"
39
+
40
+
41
+ class Helm(models.Helm, DeployablePatchedModelBase):
42
+ type: Literal["helm"] = "helm"
43
+
44
+
45
+ class Volume(models.Volume, DeployablePatchedModelBase):
46
+ type: Literal["volume"] = "volume"
47
+
48
+
49
+ class ApplicationSet(models.ApplicationSet, DeployablePatchedModelBase):
50
+ type: Literal["application-set"] = "application-set"
51
+
52
+
53
+ class AsyncService(models.AsyncService, DeployablePatchedModelBase):
54
+ type: Literal["async-service"] = "async-service"
55
+ replicas: Union[conint(ge=0, le=100), models.AsyncServiceAutoscaling] = 1
56
+ resources: models.Resources = Field(default_factory=models.Resources)
57
+
58
+
59
+ class SSHServer(models.SSHServer, DeployablePatchedModelBase):
60
+ type: Literal["ssh-server"] = "ssh-server"
61
+ resources: models.Resources = Field(default_factory=models.Resources)
62
+
63
+
64
+ class Application(models.Application, DeployablePatchedModelBase):
65
+ def deploy(self, workspace_fqn: str, wait: bool = True) -> Deployment:
66
+ if isinstance(self.__root__, models.Workflow):
67
+ from truefoundry.deploy.v2.lib.deploy_workflow import deploy_workflow
68
+
69
+ return deploy_workflow(
70
+ workflow=self.__root__,
71
+ workspace_fqn=workspace_fqn,
72
+ wait=wait,
73
+ )
74
+ else:
75
+ return deploy_component(
76
+ component=self.__root__, workspace_fqn=workspace_fqn, wait=wait
77
+ )
78
+
79
+
80
+ class Workflow(models.Workflow, DeployablePatchedModelBase):
81
+ type: Literal["workflow"] = "workflow"
82
+
83
+ def deploy(self, workspace_fqn: str, wait: bool = True) -> Deployment:
84
+ from truefoundry.deploy.v2.lib.deploy_workflow import deploy_workflow
85
+
86
+ return deploy_workflow(workflow=self, workspace_fqn=workspace_fqn, wait=wait)
@@ -0,0 +1,53 @@
1
+ import datetime
2
+ from typing import List, Optional
3
+
4
+ from truefoundry.pydantic_v1 import BaseModel, Extra, create_model
5
+
6
+
7
+ class BuildResponse(BaseModel):
8
+ id: str
9
+ name: str
10
+ # TODO: make status an enum
11
+ status: str
12
+ # TODO: should we just make these fields
13
+ # snake-case and add camelCase aliases?
14
+ deploymentId: str
15
+ componentName: str
16
+ createdAt: datetime.datetime
17
+ updatedAt: datetime.datetime
18
+ imageUri: Optional[str]
19
+ failureReason: Optional[str]
20
+ getLogsUrl: str
21
+ tailLogsUrl: str
22
+ logsStartTs: int
23
+
24
+ class Config:
25
+ extra = Extra.allow
26
+
27
+
28
+ class AppDeploymentStatusResponse(BaseModel):
29
+ state: create_model(
30
+ "State",
31
+ isTerminalState=(bool, ...),
32
+ type=(str, ...),
33
+ transitions=(List[str], ...),
34
+ )
35
+
36
+ id: str
37
+ status: str
38
+ message: Optional[str]
39
+ transition: Optional[str]
40
+
41
+ class Config:
42
+ extra = Extra.allow
43
+
44
+
45
+ class DeploymentFqnResponse(BaseModel):
46
+ deploymentId: str
47
+ applicationId: str
48
+ workspaceId: str
49
+
50
+
51
+ class ApplicationFqnResponse(BaseModel):
52
+ applicationId: str
53
+ workspaceId: str