truefoundry 0.3.0rc9__tar.gz → 0.3.0rc11__tar.gz

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 (136) hide show
  1. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/PKG-INFO +2 -2
  2. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/pyproject.toml +2 -2
  3. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/__init__.py +1 -0
  4. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/cli.py +2 -0
  5. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/trigger_command.py +58 -0
  6. truefoundry-0.3.0rc11/truefoundry/deploy/lib/auth/auth_service_client.py +181 -0
  7. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/auth/credential_provider.py +2 -2
  8. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/clients/servicefoundry_client.py +36 -11
  9. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/dao/application.py +20 -0
  10. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/model/entity.py +17 -0
  11. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/session.py +20 -8
  12. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/util.py +20 -0
  13. truefoundry-0.3.0rc9/truefoundry/deploy/lib/auth/auth_service_client.py +0 -81
  14. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/README.md +0 -0
  15. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/__init__.py +0 -0
  16. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/__init__.py +0 -0
  17. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/agents/__init__.py +0 -0
  18. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/agents/base.py +0 -0
  19. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/agents/developer.py +0 -0
  20. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/agents/project_identifier.py +0 -0
  21. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/agents/tester.py +0 -0
  22. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/cli.py +0 -0
  23. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/constants.py +0 -0
  24. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/exception.py +0 -0
  25. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/logger.py +0 -0
  26. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/__init__.py +0 -0
  27. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/ask.py +0 -0
  28. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/base.py +0 -0
  29. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/commit.py +0 -0
  30. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/docker_build.py +0 -0
  31. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/docker_run.py +0 -0
  32. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/file_type_counts.py +0 -0
  33. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/list_files.py +0 -0
  34. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/read_file.py +0 -0
  35. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/send_request.py +0 -0
  36. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/tools/write_file.py +0 -0
  37. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/utils/client.py +0 -0
  38. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/utils/diff.py +0 -0
  39. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/autodeploy/utils/pydantic_compat.py +0 -0
  40. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/cli/__init__.py +0 -0
  41. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/cli/__main__.py +0 -0
  42. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/auto_gen/models.py +0 -0
  43. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/__init__.py +0 -0
  44. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/__init__.py +0 -0
  45. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/dockerfile.py +0 -0
  46. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/tfy_notebook_buildpack/__init__.py +0 -0
  47. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/tfy_notebook_buildpack/dockerfile_template.py +0 -0
  48. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/tfy_python_buildpack/__init__.py +0 -0
  49. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/builders/tfy_python_buildpack/dockerfile_template.py +0 -0
  50. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/builder/docker_service.py +0 -0
  51. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/__init__.py +0 -0
  52. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/__init__.py +0 -0
  53. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/apply_command.py +0 -0
  54. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/build_command.py +0 -0
  55. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/build_logs_command.py +0 -0
  56. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/create_command.py +0 -0
  57. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/delete_command.py +0 -0
  58. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/deploy_command.py +0 -0
  59. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/get_command.py +0 -0
  60. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/list_command.py +0 -0
  61. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/login_command.py +0 -0
  62. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/logout_command.py +0 -0
  63. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/logs_command.py +0 -0
  64. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/patch_application_command.py +0 -0
  65. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/patch_command.py +0 -0
  66. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/redeploy_command.py +0 -0
  67. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/commands/terminate_comand.py +0 -0
  68. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/config.py +0 -0
  69. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/console.py +0 -0
  70. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/const.py +0 -0
  71. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/display_util.py +0 -0
  72. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/cli/util.py +0 -0
  73. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/core/__init__.py +0 -0
  74. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/core/login.py +0 -0
  75. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/core/logout.py +0 -0
  76. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/__init__.py +0 -0
  77. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/__main__.py +0 -0
  78. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/app.py +0 -0
  79. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/build.py +0 -0
  80. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/remote/__init__.py +0 -0
  81. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/remote/context.py +0 -0
  82. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/remote/method.py +0 -0
  83. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/remote/remote.py +0 -0
  84. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/route.py +0 -0
  85. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/service.py +0 -0
  86. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/function_service/utils.py +0 -0
  87. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/io/__init__.py +0 -0
  88. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/io/output_callback.py +0 -0
  89. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/io/rich_output_callback.py +0 -0
  90. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/json_util.py +0 -0
  91. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/__init__.py +0 -0
  92. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/auth/credential_file_manager.py +0 -0
  93. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/auth/servicefoundry_session.py +0 -0
  94. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/clients/__init__.py +0 -0
  95. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/clients/shell_client.py +0 -0
  96. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/clients/utils.py +0 -0
  97. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/const.py +0 -0
  98. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/dao/__init__.py +0 -0
  99. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/dao/apply.py +0 -0
  100. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/dao/version.py +0 -0
  101. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/dao/workspace.py +0 -0
  102. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/exceptions.py +0 -0
  103. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/logs_utils.py +0 -0
  104. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/messages.py +0 -0
  105. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/model/__init__.py +0 -0
  106. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/lib/win32.py +0 -0
  107. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/__init__.py +0 -0
  108. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/__init__.py +0 -0
  109. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/deploy.py +0 -0
  110. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/deploy_workflow.py +0 -0
  111. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/deployable_patched_models.py +0 -0
  112. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/models.py +0 -0
  113. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/patched_models.py +0 -0
  114. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/deploy/v2/lib/source.py +0 -0
  115. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/__init__.py +0 -0
  116. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/deprecated.py +0 -0
  117. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/truefoundry_chat.py +0 -0
  118. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/truefoundry_embeddings.py +0 -0
  119. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/truefoundry_llm.py +0 -0
  120. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/langchain/utils.py +0 -0
  121. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/logger.py +0 -0
  122. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/ml/__init__.py +0 -0
  123. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/pydantic_v1.py +0 -0
  124. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/python_deploy_codegen.py +0 -0
  125. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/version.py +0 -0
  126. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/__init__.py +0 -0
  127. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/container_task.py +0 -0
  128. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/example/deploy.sh +0 -0
  129. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/example/hello_world_package/workflow.py +0 -0
  130. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/example/package/test_workflow.py +0 -0
  131. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/example/truefoundry.yaml +0 -0
  132. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/example/workflow.yaml +0 -0
  133. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/map_task.py +0 -0
  134. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/python_task.py +0 -0
  135. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/task.py +0 -0
  136. {truefoundry-0.3.0rc9 → truefoundry-0.3.0rc11}/truefoundry/workflow/workflow.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: truefoundry
3
- Version: 0.3.0rc9
3
+ Version: 0.3.0rc11
4
4
  Summary: Truefoundry CLI
5
5
  Author: Abhishek Choudhary
6
6
  Author-email: abhishek@truefoundry.com
@@ -24,7 +24,7 @@ Requires-Dist: flytekit (==1.12.2) ; extra == "workflow"
24
24
  Requires-Dist: gitignorefile (>=1.1.2,<1.2.0)
25
25
  Requires-Dist: importlib-metadata (>=6.0.1,<8.0.0)
26
26
  Requires-Dist: importlib-resources (>=5.2.0,<6.0.0)
27
- Requires-Dist: mlfoundry (==0.11.2) ; extra == "ml"
27
+ Requires-Dist: mlfoundry (==0.11.4) ; extra == "ml"
28
28
  Requires-Dist: openai (>=1.16.2,<2.0.0)
29
29
  Requires-Dist: packaging (>=20.0,<25.0)
30
30
  Requires-Dist: pydantic (>=1.10.0,<3)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "truefoundry"
3
- version = "0.3.0rc9"
3
+ version = "0.3.0rc11"
4
4
  description = "Truefoundry CLI"
5
5
  authors = ["Abhishek Choudhary <abhishek@truefoundry.com>"]
6
6
  readme = "README.md"
@@ -17,7 +17,7 @@ GitPython = ">=3.1.43,<3.2.0"
17
17
  importlib-metadata = ">=6.0.1,<8.0.0"
18
18
  importlib-resources = "^5.2.0"
19
19
  Mako = "^1.1.6"
20
- mlfoundry = { version = "0.11.2", optional = true }
20
+ mlfoundry = { version = "0.11.4", optional = true }
21
21
  openai = ">=1.16.2,<2.0.0"
22
22
  packaging = ">=20.0,<25.0"
23
23
  pydantic = ">=1.10.0,<3"
@@ -16,6 +16,7 @@ from truefoundry.deploy.lib.dao.application import (
16
16
  list_job_runs,
17
17
  terminate_job_run,
18
18
  trigger_job,
19
+ trigger_workflow,
19
20
  )
20
21
  from truefoundry.deploy.lib.dao.version import (
21
22
  get_version as get_application_version,
@@ -13,6 +13,7 @@ from truefoundry.deploy.cli.commands import (
13
13
  get_logout_command,
14
14
  get_patch_application_command,
15
15
  get_patch_command,
16
+ get_trigger_command,
16
17
  )
17
18
  from truefoundry.deploy.cli.config import CliConfig
18
19
  from truefoundry.deploy.cli.const import GROUP_CLS
@@ -36,6 +37,7 @@ def create_truefoundry_cli():
36
37
  cli.add_command(get_deploy_command())
37
38
  cli.add_command(get_patch_application_command())
38
39
  cli.add_command(get_delete_command())
40
+ cli.add_command(get_trigger_command())
39
41
 
40
42
  if not (sys.platform.startswith("win32") or sys.platform.startswith("cygwin")):
41
43
  cli.add_command(get_patch_command())
@@ -82,6 +82,64 @@ def trigger_job(application_fqn: str, command: Optional[Sequence[str]], params):
82
82
  )
83
83
 
84
84
 
85
+ @click.command(
86
+ name="workflow",
87
+ cls=COMMAND_CLS,
88
+ context_settings={"ignore_unknown_options": True, "allow_extra_args": True},
89
+ )
90
+ @click.option(
91
+ "--application-fqn",
92
+ "--application_fqn",
93
+ type=click.STRING,
94
+ required=True,
95
+ help="FQN of the workflow application",
96
+ )
97
+ @click.argument(
98
+ "inputs",
99
+ type=click.STRING,
100
+ nargs=-1,
101
+ required=False,
102
+ )
103
+ @handle_exception_wrapper
104
+ def trigger_workflow(application_fqn: str, inputs):
105
+ """
106
+ Trigger a Workflow on TrueFoundry
107
+
108
+ [b]tfy trigger workflow --application-fqn "my-cluster:my-workspace:my-workflow"[/]
109
+
110
+ \n
111
+ Additionally, you can pass inputs (if defined in the workflow)\n\n
112
+
113
+ Passing inputs:
114
+
115
+ [b]tfy trigger workflow --application-fqn "my-cluster:my-workspace:my-workflow" -- --input1_name input1_value --input2_name input2_value ...[/]
116
+ """
117
+ if inputs:
118
+ inputs_dict = {}
119
+ if len(inputs) % 2 != 0:
120
+ raise ClickException(
121
+ f"Found odd number of argument pairs: {inputs}. "
122
+ "Perhaps you forgot to pass a value for one of the inputs? "
123
+ "inputs for workflow should be passed in the "
124
+ "format `--input1_name input1_value --input2_name input2_value ...`"
125
+ )
126
+ for i in range(0, len(inputs), 2):
127
+ key = inputs[i]
128
+ value = inputs[i + 1]
129
+ if not key.startswith("--"):
130
+ raise ClickException(
131
+ f"Got ambiguous argument {key!r} in inputs: {inputs}. "
132
+ f"input names should be prefixed with '--' i.e. "
133
+ "inputs for workflow should be passed in the "
134
+ "format `--input1_name input1_value --input2_name input2_value ...`"
135
+ )
136
+ key = key.lstrip("-")
137
+ inputs_dict[key] = value
138
+
139
+ application.trigger_workflow(application_fqn=application_fqn, inputs=inputs)
140
+
141
+
85
142
  def get_trigger_command():
86
143
  trigger_command.add_command(trigger_job)
144
+ trigger_command.add_command(trigger_workflow)
87
145
  return trigger_command
@@ -0,0 +1,181 @@
1
+ import time
2
+ from abc import ABC, abstractmethod
3
+
4
+ import requests
5
+
6
+ from truefoundry.deploy.lib.clients.utils import poll_for_function, request_handling
7
+ from truefoundry.deploy.lib.const import VERSION_PREFIX
8
+ from truefoundry.deploy.lib.exceptions import BadRequestException
9
+ from truefoundry.deploy.lib.model.entity import DeviceCode, Token
10
+ from truefoundry.logger import logger
11
+
12
+
13
+ class AuthServiceClient(ABC):
14
+ def __init__(self, base_url):
15
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
16
+ ServiceFoundryServiceClient,
17
+ )
18
+
19
+ client = ServiceFoundryServiceClient(init_session=False, base_url=base_url)
20
+
21
+ self._api_server_url = client._api_server_url
22
+ self._auth_server_url = client.tenant_info.auth_server_url
23
+ self._tenant_name = client.tenant_info.tenant_name
24
+
25
+ @classmethod
26
+ def from_base_url(cls, base_url: str) -> "AuthServiceClient":
27
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
28
+ ServiceFoundryServiceClient,
29
+ )
30
+
31
+ client = ServiceFoundryServiceClient(init_session=False, base_url=base_url)
32
+ if client.python_sdk_config.use_sfy_server_auth_apis:
33
+ return ServiceFoundryServerAuthServiceClient(base_url)
34
+ return AuthServerServiceClient(base_url)
35
+
36
+ @abstractmethod
37
+ def refresh_token(self, token: Token, host: str = None) -> Token: ...
38
+
39
+ @abstractmethod
40
+ def get_device_code(self) -> DeviceCode: ...
41
+
42
+ @abstractmethod
43
+ def get_token_from_device_code(
44
+ self, device_code: str, timeout: float = 60, poll_interval_seconds: int = 1
45
+ ) -> Token: ...
46
+
47
+
48
+ class ServiceFoundryServerAuthServiceClient(AuthServiceClient):
49
+ def __init__(self, base_url):
50
+ super().__init__(base_url)
51
+
52
+ def refresh_token(self, token: Token, host: str = None) -> Token:
53
+ host_arg_str = f"--host {host}" if host else "--host HOST"
54
+ if not token.refresh_token:
55
+ # TODO: Add a way to propagate error messages without traceback to the output interface side
56
+ raise Exception(
57
+ f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
58
+ )
59
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/oauth2/token"
60
+ data = {
61
+ "tenantName": token.tenant_name,
62
+ "refreshToken": token.refresh_token,
63
+ "grantType": "refresh_token",
64
+ "returnJWT": True,
65
+ }
66
+ res = requests.post(url, json=data)
67
+ try:
68
+ res = request_handling(res)
69
+ return Token.parse_obj(res)
70
+ except BadRequestException as ex:
71
+ raise Exception(
72
+ f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
73
+ ) from ex
74
+
75
+ def get_device_code(self) -> DeviceCode:
76
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/oauth2/device-authorize"
77
+ data = {"tenantName": self._tenant_name}
78
+ res = requests.post(url, json=data)
79
+ res = request_handling(res)
80
+ # TODO: temporary cleanup of incorrect attributes
81
+ res = {"userCode": res.get("userCode"), "deviceCode": res.get("deviceCode")}
82
+ return DeviceCode.parse_obj(res)
83
+
84
+ def get_token_from_device_code(
85
+ self, device_code: str, timeout: float = 60, poll_interval_seconds: int = 1
86
+ ) -> Token:
87
+ timeout = timeout or 60
88
+ poll_interval_seconds = poll_interval_seconds or 1
89
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/oauth2/token"
90
+ data = {
91
+ "tenantName": self._tenant_name,
92
+ "deviceCode": device_code,
93
+ "grantType": "device_code",
94
+ "returnJWT": True,
95
+ }
96
+ response = requests.post(url=url, json=data)
97
+ start_time = time.monotonic()
98
+
99
+ for response in poll_for_function(
100
+ requests.post, poll_after_secs=poll_interval_seconds, url=url, json=data
101
+ ):
102
+ if response.status_code == 201:
103
+ response = response.json()
104
+ return Token.parse_obj(response)
105
+ elif response.status_code == 202:
106
+ logger.debug("User has not authorized yet. Checking again.")
107
+ else:
108
+ raise Exception(
109
+ "Failed to get token using device code. "
110
+ f"status_code {response.status_code},\n {response.text}"
111
+ )
112
+ time_elapsed = time.monotonic() - start_time
113
+ if time_elapsed > timeout:
114
+ logger.warning("Polled server for %s secs.", int(time_elapsed))
115
+ break
116
+
117
+ raise Exception(f"Did not get authorized within {timeout} seconds.")
118
+
119
+
120
+ class AuthServerServiceClient(AuthServiceClient):
121
+ def __init__(self, base_url):
122
+ super().__init__(base_url)
123
+
124
+ def refresh_token(self, token: Token, host: str = None) -> Token:
125
+ host_arg_str = f"--host {host}" if host else "--host HOST"
126
+ if not token.refresh_token:
127
+ # TODO: Add a way to propagate error messages without traceback to the output interface side
128
+ raise Exception(
129
+ f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
130
+ )
131
+ url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/token/refresh"
132
+ data = {
133
+ "tenantName": token.tenant_name,
134
+ "refreshToken": token.refresh_token,
135
+ }
136
+ res = requests.post(url, json=data)
137
+ try:
138
+ res = request_handling(res)
139
+ return Token.parse_obj(res)
140
+ except BadRequestException as ex:
141
+ raise Exception(
142
+ f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
143
+ ) from ex
144
+
145
+ def get_device_code(self) -> DeviceCode:
146
+ url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/device"
147
+ data = {"tenantName": self._tenant_name}
148
+ res = requests.post(url, json=data)
149
+ res = request_handling(res)
150
+ return DeviceCode.parse_obj(res)
151
+
152
+ def get_token_from_device_code(
153
+ self, device_code: str, timeout: float = 60, poll_interval_seconds: int = 1
154
+ ) -> Token:
155
+ url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/device/token"
156
+ data = {
157
+ "tenantName": self._tenant_name,
158
+ "deviceCode": device_code,
159
+ }
160
+ start_time = time.monotonic()
161
+ poll_interval_seconds = 1
162
+
163
+ for response in poll_for_function(
164
+ requests.post, poll_after_secs=poll_interval_seconds, url=url, json=data
165
+ ):
166
+ if response.status_code == 201:
167
+ response = response.json()
168
+ return Token.parse_obj(response)
169
+ elif response.status_code == 202:
170
+ logger.debug("User has not authorized yet. Checking again.")
171
+ else:
172
+ raise Exception(
173
+ "Failed to get token using device code. "
174
+ f"status_code {response.status_code},\n {response.text}"
175
+ )
176
+ time_elapsed = time.monotonic() - start_time
177
+ if time_elapsed > timeout:
178
+ logger.warning("Polled server for %s secs.", int(time_elapsed))
179
+ break
180
+
181
+ raise Exception(f"Did not get authorized within {timeout} seconds.")
@@ -44,7 +44,7 @@ class EnvCredentialProvider(CredentialProvider):
44
44
  # TODO: Read host from cred file as well.
45
45
  base_url = resolve_base_url().strip("/")
46
46
  self._host = base_url
47
- self._auth_service = AuthServiceClient(base_url=base_url)
47
+ self._auth_service = AuthServiceClient.from_base_url(base_url=base_url)
48
48
 
49
49
  servicefoundry_client = ServiceFoundryServiceClient(
50
50
  init_session=False, base_url=base_url
@@ -82,7 +82,7 @@ class FileCredentialProvider(CredentialProvider):
82
82
  self._token = self._last_cred_file_content.to_token()
83
83
  self._host = self._last_cred_file_content.host
84
84
 
85
- self._auth_service = AuthServiceClient(base_url=self._host)
85
+ self._auth_service = AuthServiceClient.from_base_url(base_url=self._host)
86
86
 
87
87
  @staticmethod
88
88
  def can_provide() -> bool:
@@ -26,12 +26,14 @@ from truefoundry.deploy.lib.model.entity import (
26
26
  Deployment,
27
27
  DockerRegistryCredentials,
28
28
  JobRun,
29
+ PythonSDKConfig,
29
30
  TenantInfo,
30
31
  Token,
31
32
  TriggerJobResult,
32
33
  Workspace,
33
34
  WorkspaceResources,
34
35
  )
36
+ from truefoundry.deploy.lib.util import timed_lru_cache
35
37
  from truefoundry.deploy.lib.win32 import allow_interrupt
36
38
  from truefoundry.deploy.v2.lib.models import (
37
39
  AppDeploymentStatusResponse,
@@ -93,6 +95,24 @@ def check_min_cli_version(fn):
93
95
  return inner
94
96
 
95
97
 
98
+ @timed_lru_cache(seconds=30 * 60)
99
+ def _cached_get_tenant_info(api_server_url: str) -> TenantInfo:
100
+ res = requests.get(
101
+ url=f"{api_server_url}/v1/tenant-id",
102
+ params={"hostName": urlparse(api_server_url).netloc},
103
+ )
104
+ res = request_handling(res)
105
+ return TenantInfo.parse_obj(res)
106
+
107
+
108
+ @timed_lru_cache(seconds=30 * 60)
109
+ def _cached_get_python_sdk_config(api_server_url: str) -> PythonSDKConfig:
110
+ url = f"{api_server_url}/v1/min-cli-version"
111
+ res = requests.get(url)
112
+ res = request_handling(res)
113
+ return PythonSDKConfig.parse_obj(res)
114
+
115
+
96
116
  class ServiceFoundryServiceClient:
97
117
  def __init__(self, init_session: bool = True, base_url: Optional[str] = None):
98
118
  self._session: Optional[ServiceFoundrySession] = None
@@ -111,20 +131,19 @@ class ServiceFoundryServiceClient:
111
131
  def base_url(self) -> str:
112
132
  return self._base_url
113
133
 
114
- def get_tenant_info(self) -> TenantInfo:
115
- res = requests.get(
116
- url=f"{self._api_server_url}/v1/tenant-id",
117
- params={"hostName": urlparse(self._api_server_url).netloc},
118
- )
119
- res = request_handling(res)
120
- return TenantInfo.parse_obj(res)
134
+ @property
135
+ def tenant_info(self) -> TenantInfo:
136
+ return _cached_get_tenant_info(self._api_server_url)
137
+
138
+ @property
139
+ def python_sdk_config(self) -> PythonSDKConfig:
140
+ return _cached_get_python_sdk_config(self._api_server_url)
121
141
 
122
142
  @functools.cached_property
123
143
  def _min_cli_version_required(self) -> str:
124
- url = f"{self._api_server_url}/v1/min-cli-version"
125
- res = requests.get(url)
126
- res = request_handling(res)
127
- return res["truefoundryCliMinVersion"]
144
+ return _cached_get_python_sdk_config(
145
+ self._api_server_url
146
+ ).truefoundry_cli_min_version
128
147
 
129
148
  def _get_header(self):
130
149
  if not self._session:
@@ -616,6 +635,12 @@ class ServiceFoundryServiceClient:
616
635
  response = request_handling(res)
617
636
  return TriggerJobResult.parse_obj(response)
618
637
 
638
+ def trigger_workflow(self, application_id: str, inputs: Dict[str, Any]):
639
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/workflow/{application_id}/executions"
640
+ res = requests.post(url, json=inputs, headers=self._get_header())
641
+ response = request_handling(res)
642
+ return response
643
+
619
644
  @check_min_cli_version
620
645
  def get_docker_registry_creds(
621
646
  self, docker_registry_fqn: str, cluster_id: str
@@ -241,3 +241,23 @@ def terminate_job_run(
241
241
  job_run_name=job_run_name,
242
242
  )
243
243
  return response
244
+
245
+
246
+ def trigger_workflow(application_fqn: str, inputs: Optional[Dict[str, Any]] = None):
247
+ inputs = inputs or {}
248
+ client = ServiceFoundryServiceClient()
249
+ _application_info = client.get_application_info_by_fqn(
250
+ application_fqn=application_fqn
251
+ )
252
+ application_info = client.get_application_info(
253
+ application_id=_application_info.applicationId
254
+ )
255
+ client.trigger_workflow(
256
+ application_id=application_info.id,
257
+ inputs=inputs,
258
+ )
259
+ logger.info(f"Started Execution for Workflow: {application_info.name}")
260
+ executions_page = (
261
+ f"{client.base_url.strip('/')}/deployments/{application_info.id}?tab=executions"
262
+ )
263
+ logger.info(f"You can check the executions at {executions_page}")
@@ -297,6 +297,11 @@ class CredentialsFileContent(BaseModel):
297
297
  class DeviceCode(BaseModel):
298
298
  user_code: str = Field(alias="userCode")
299
299
  device_code: str = Field(alias="deviceCode")
300
+ verification_url: Optional[str] = Field(alias="verificationURI")
301
+ complete_verification_url: Optional[str] = Field(alias="verificationURIComplete")
302
+ expires_in_seconds: int = Field(alias="expiresInSeconds", default=60)
303
+ interval_in_seconds: int = Field(alias="intervalInSeconds", default=1)
304
+ message: Optional[str] = Field(alias="message")
300
305
 
301
306
  class Config:
302
307
  allow_population_by_field_name = True
@@ -306,6 +311,18 @@ class DeviceCode(BaseModel):
306
311
  return f"{auth_host}/authorize/device?userCode={self.user_code}"
307
312
 
308
313
 
314
+ class PythonSDKConfig(BaseModel):
315
+ min_version: str = Field(alias="minVersion")
316
+ truefoundry_cli_min_version: str = Field(alias="truefoundryCliMinVersion")
317
+ use_sfy_server_auth_apis: Optional[bool] = Field(
318
+ alias="useSFYServerAuthAPIs", default=False
319
+ )
320
+
321
+ class Config:
322
+ allow_population_by_field_name = True
323
+ allow_mutation = False
324
+
325
+
309
326
  class JobRun(Base):
310
327
  name: str
311
328
  applicationName: str
@@ -92,7 +92,7 @@ def login(
92
92
  api_key=api_key, servicefoundry_client=servicefoundry_client
93
93
  )
94
94
  else:
95
- auth_service = AuthServiceClient(base_url=host)
95
+ auth_service = AuthServiceClient.from_base_url(base_url=host)
96
96
  # interactive login
97
97
  token = _login_with_device_code(base_url=host, auth_service=auth_service)
98
98
 
@@ -136,11 +136,23 @@ def _login_with_device_code(
136
136
  ) -> Token:
137
137
  logger.debug("Logging in with device code")
138
138
  device_code = auth_service.get_device_code()
139
- url_to_go = device_code.get_user_clickable_url(auth_host=base_url)
140
- output_hook.print_line(f"Opening:- {url_to_go}")
141
- output_hook.print(
142
- "Please click on the above link if it is not "
143
- "automatically opened in a browser window."
139
+ auto_open_url = None
140
+ message = "Please click on the above link if it is not automatically opened in a browser window."
141
+ if device_code.complete_verification_url:
142
+ auto_open_url = device_code.complete_verification_url
143
+ elif device_code.verification_url:
144
+ if device_code.message:
145
+ message = device_code.message
146
+ else:
147
+ message = f"Please open the following URL in a browser and enter the code {device_code.user_code} when prompted: {device_code.verification_url}"
148
+ else:
149
+ auto_open_url = device_code.get_user_clickable_url(auth_host=base_url)
150
+ output_hook.print_line(message)
151
+ if auto_open_url:
152
+ output_hook.print_line(f"Opening:- {auto_open_url}")
153
+ click.launch(auto_open_url)
154
+ return auth_service.get_token_from_device_code(
155
+ device_code=device_code.device_code,
156
+ timeout=device_code.expires_in_seconds,
157
+ poll_interval_seconds=device_code.interval_in_seconds,
144
158
  )
145
- click.launch(url_to_go)
146
- return auth_service.get_token_from_device_code(device_code=device_code.device_code)
@@ -1,5 +1,7 @@
1
1
  import os
2
2
  import re
3
+ from functools import lru_cache, wraps
4
+ from time import monotonic_ns
3
5
  from typing import Union
4
6
 
5
7
  from truefoundry.deploy.lib.const import (
@@ -68,3 +70,21 @@ def find_list_paths(data, parent_key="", sep="."):
68
70
  new_key = f"{parent_key}[{i}]"
69
71
  list_paths.extend(find_list_paths(value, new_key, sep))
70
72
  return list_paths
73
+
74
+
75
+ def timed_lru_cache(seconds: int = 300, maxsize: int = None):
76
+ def wrapper_cache(func):
77
+ func = lru_cache(maxsize=maxsize)(func)
78
+ func.delta = seconds * 10**9
79
+ func.expiration = monotonic_ns() + func.delta
80
+
81
+ @wraps(func)
82
+ def wrapped_func(*args, **kwargs):
83
+ if monotonic_ns() >= func.expiration:
84
+ func.cache_clear()
85
+ func.expiration = monotonic_ns() + func.delta
86
+ return func(*args, **kwargs)
87
+
88
+ return wrapped_func
89
+
90
+ return wrapper_cache
@@ -1,81 +0,0 @@
1
- import time
2
-
3
- import requests
4
-
5
- from truefoundry.deploy.lib.clients.utils import poll_for_function, request_handling
6
- from truefoundry.deploy.lib.const import VERSION_PREFIX
7
- from truefoundry.deploy.lib.exceptions import BadRequestException
8
- from truefoundry.deploy.lib.model.entity import DeviceCode, Token
9
- from truefoundry.logger import logger
10
-
11
-
12
- class AuthServiceClient:
13
- def __init__(self, base_url):
14
- from truefoundry.deploy.lib.clients.servicefoundry_client import (
15
- ServiceFoundryServiceClient,
16
- )
17
-
18
- client = ServiceFoundryServiceClient(init_session=False, base_url=base_url)
19
- tenant_info = client.get_tenant_info()
20
-
21
- self._auth_server_url = tenant_info.auth_server_url
22
- self._tenant_name = tenant_info.tenant_name
23
-
24
- def refresh_token(self, token: Token, host: str = None) -> Token:
25
- host_arg_str = f"--host {host}" if host else "--host HOST"
26
- if not token.refresh_token:
27
- # TODO: Add a way to propagate error messages without traceback to the output interface side
28
- raise Exception(
29
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
30
- )
31
- url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/token/refresh"
32
- data = {
33
- "tenantName": token.tenant_name,
34
- "refreshToken": token.refresh_token,
35
- }
36
- res = requests.post(url, data=data)
37
- try:
38
- res = request_handling(res)
39
- return Token.parse_obj(res)
40
- except BadRequestException as ex:
41
- raise Exception(
42
- f"Unable to resume login session. Please log in again using `tfy login {host_arg_str} --relogin`"
43
- ) from ex
44
-
45
- def get_device_code(self) -> DeviceCode:
46
- url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/device"
47
- data = {"tenantName": self._tenant_name}
48
- res = requests.post(url, data=data)
49
- res = request_handling(res)
50
- return DeviceCode.parse_obj(res)
51
-
52
- def get_token_from_device_code(
53
- self, device_code: str, timeout: float = 60
54
- ) -> Token:
55
- url = f"{self._auth_server_url}/api/{VERSION_PREFIX}/oauth/device/token"
56
- data = {
57
- "tenantName": self._tenant_name,
58
- "deviceCode": device_code,
59
- }
60
- start_time = time.monotonic()
61
- poll_interval_seconds = 1
62
-
63
- for response in poll_for_function(
64
- requests.post, poll_after_secs=poll_interval_seconds, url=url, data=data
65
- ):
66
- if response.status_code == 201:
67
- response = response.json()
68
- return Token.parse_obj(response)
69
- elif response.status_code == 202:
70
- logger.debug("User has not authorized yet. Checking again.")
71
- else:
72
- raise Exception(
73
- "Failed to get token using device code. "
74
- f"status_code {response.status_code},\n {response.text}"
75
- )
76
- time_elapsed = time.monotonic() - start_time
77
- if time_elapsed > timeout:
78
- logger.warning("Polled server for %s secs.", int(time_elapsed))
79
- break
80
-
81
- raise Exception(f"Did not get authorized within {timeout} seconds.")